1 /*
2  * Copyright (C) 2010-2015 by Stephen Allewell
3  * steve.allewell@gmail.com
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  */
10 
11 
12 /**
13     @file
14     Implements the scheme parser to read an XML formatted scheme file.
15     */
16 
17 
18 #include "SchemeParser.h"
19 
20 #include <KLocalizedString>
21 
22 #include "FlossScheme.h"
23 #include "Floss.h"
24 
25 
26 /**
27     Valid tags that can exist in the file.
28     */
29 const QStringList validTags = {
30     QStringLiteral("flossScheme"),  /**< The enclosing tags for the file */
31     QStringLiteral("title"),        /**< The title of the scheme */
32     QStringLiteral("floss"),        /**< Enclosing tags for a floss definition */
33     QStringLiteral("name"),         /**< The name of the floss */
34     QStringLiteral("description"),  /**< The descriptive text for the floss */
35     QStringLiteral("color"),        /**< Enclosing tags for a color definition */
36     QStringLiteral("red"),          /**< Red color value */
37     QStringLiteral("green"),        /**< Green color value */
38     QStringLiteral("blue")          /**< Blue color value */
39 };
40 
41 
42 /**
43     Error messages.
44     */
45 const char *errorMessages[] = {
46     I18N_NOOP("No error"),
47     I18N_NOOP("Floss not completely defined"),
48     I18N_NOOP("Unmatched element tags"),
49     I18N_NOOP("Unknown element tag"),
50     I18N_NOOP("Color value is invalid"),
51     I18N_NOOP("Unknown error code")
52 };
53 
54 
55 /**
56     Constructor.  Initialises pointers to 0 and set the m_errorCode value to 0.
57     */
SchemeParser()58 SchemeParser::SchemeParser()
59     :   m_errorCode(0),
60         m_scheme(nullptr)
61 {
62 }
63 
64 
65 /**
66     Get a pointer to the read FlossScheme.
67     @return pointer to FlossScheme class.
68     */
flossScheme()69 FlossScheme *SchemeParser::flossScheme()
70 {
71     return m_scheme;
72 }
73 
74 
75 /**
76     Start a new XML document, creating an instance of a FlossScheme.
77     @return true if the scheme is created, false otherwise.
78     */
startDocument()79 bool SchemeParser::startDocument()
80 {
81     m_scheme = new FlossScheme;
82     return (m_scheme) ? true : false;
83 }
84 
85 
86 /**
87     Start a new element. Reimplemented from QXmlDefaultHandler.
88     @param namespaceURI not used
89     @param localName not used
90     @param qName name of the tag.
91     @param qxmlAttributes not used
92     @return true if the tag is valid, false otherwise.
93     */
startElement(const QString & namespaceURI,const QString & localName,const QString & qName,const QXmlAttributes & qxmlAttributes)94 bool SchemeParser::startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &qxmlAttributes)
95 {
96     Q_UNUSED(namespaceURI);
97     Q_UNUSED(localName);
98     Q_UNUSED(qxmlAttributes);
99 
100     QString name = qName.simplified();
101 
102     if (validTags.contains(name)) {
103         m_elements.push(name);
104         return true;
105     }
106 
107     // invalid tag found
108     m_errorCode = 3;
109     return false;
110 }
111 
112 
113 /**
114     Process a string of characters.  Reimplemented from QXmlDefaultHandler.
115     @param d QString reference of the characters read from the file.
116     @return true if the data is acceptable, false otherwise.
117     */
characters(const QString & d)118 bool SchemeParser::characters(const QString &d)
119 {
120     QString data = d.simplified();
121 
122     if (data.isEmpty()) {
123         return true;
124     }
125 
126     if (m_elements.top() == QLatin1String("title")) {
127         m_scheme->setSchemeName(data);
128     } else {
129         m_flossMap.insert(m_elements.top(), data);
130     }
131 
132     if (m_elements.top() == QLatin1String("red") || m_elements.top() == QLatin1String("green") || m_elements.top() == QLatin1String("blue")) {
133         if (data.toInt() > 255) {
134             // color value is not valid
135             m_errorCode = 4;
136             return false;
137         }
138     }
139 
140     return true;
141 }
142 
143 
144 /**
145     End an element. Reimplemented from QXmlDefaultHandler.
146     @param namespaceURI not used
147     @param localName not used
148     @param qName name of the tag.
149     @return true if the tag is valid, false otherwise.
150     */
endElement(const QString & namespaceURI,const QString & localName,const QString & qName)151 bool SchemeParser::endElement(const QString &namespaceURI, const QString &localName, const QString &qName)
152 {
153     Q_UNUSED(namespaceURI);
154     Q_UNUSED(localName);
155 
156     QString name = qName.simplified();
157     QString s = m_elements.pop();
158 
159     if (s == name) {
160         if (name == QLatin1String("floss")) {
161             if (m_flossMap.contains(QLatin1String("name")) && m_flossMap.contains(QLatin1String("description")) && m_flossMap.contains(QLatin1String("red")) && m_flossMap.contains(QLatin1String("green")) && m_flossMap.contains(QLatin1String("blue"))) {
162                 m_scheme->addFloss(new Floss(m_flossMap[QLatin1String("name")], m_flossMap[QLatin1String("description")], QColor(m_flossMap[QLatin1String("red")].toInt(), m_flossMap[QLatin1String("green")].toInt(), m_flossMap[QLatin1String("blue")].toInt())));
163                 m_flossMap.clear();
164             } else {
165                 // not all elements defined for a floss
166                 m_errorCode = 1;
167             }
168         }
169     } else
170         // mismatched tags
171     {
172         m_errorCode = 2;
173     }
174 
175     if (m_errorCode) {
176         return false;
177     } else {
178         return true;
179     }
180 }
181 
182 
183 /**
184     Return an error string related to the last error code encountered which is stored in m_errorCode.
185     @return QString containing the error message.
186     */
errorString() const187 QString SchemeParser::errorString() const
188 {
189     return i18n(errorMessages[m_errorCode]);
190 }
191