1 /***************************************************************************
2   datareader.cpp
3   -------------------
4   XML stream reader for QBrew data
5   -------------------
6   Copyright 2008, David Johnson
7   Please see the header file for copyright and license information
8  ***************************************************************************/
9 
10 #include "data.h"
11 #include "resource.h"
12 
13 #include "datareader.h"
14 
15 using namespace Resource;
16 
17 // DataReader ///////////////////////////////////////////////////////////////
18 
19 /////////////////////////////////////////////////////////////////////////////
20 // DataReader()
21 // ------------
22 // Constructor
23 
DataReader(QIODevice * device)24 DataReader::DataReader(QIODevice *device)
25     : QXmlStreamReader(device)
26 {
27 }
28 
29 /////////////////////////////////////////////////////////////////////////////
30 // readData()
31 // ----------
32 // Read in data
33 
readData(Data * data)34 bool DataReader::readData(Data *data)
35 {
36     QString buf;
37     bool grainsflag=false, hopsflag=false, miscsflag=false;
38     bool stylesflag=false, utilflag=false;
39 
40     data->grainmap_.clear();
41     data->hopmap_.clear();
42     data->miscmap_.clear();
43     data->stylemap_.clear();
44     data->utable_.clear();
45 
46     // parse file
47     while (!atEnd()) {
48         readNext();
49         if (isStartElement()) {
50             // qbrewdata tag
51             if (name() == tagDoc) {
52                 // check version
53                 buf = attributes().value(attrVersion).toString();
54                 if (buf < DATA_PREVIOUS) {
55                     // too old of a version
56                     qWarning() << "Error: Unsupported data version";
57                     raiseError("Unsupported data version");
58                     return false;
59                 }
60             }
61 
62             // styles
63             else if (name() == tagStyles) {
64                 stylesflag = true;
65             }
66             // style
67             else if (name() == tagStyle) {
68                 if (!stylesflag) qWarning("Warning: mislocated style tag");
69                 Style style;
70                 buf = attributes().value(attrOGLow).toString();
71                 style.setOGLow(buf.toDouble());
72                 buf = attributes().value(attrOGHigh).toString();
73                 style.setOGHi(buf.toDouble());
74                 buf = attributes().value(attrFGLow).toString();
75                 style.setFGLow(buf.toDouble());
76                 buf = attributes().value(attrFGHigh).toString();
77                 style.setFGHi(buf.toDouble());
78                 buf = attributes().value(attrIBULow).toString();
79                 style.setIBULow(buf.toInt());
80                 buf = attributes().value(attrIBUHigh).toString();
81                 style.setIBUHi(buf.toInt());
82                 buf = attributes().value(attrSRMLow).toString();
83                 style.setSRMLow(buf.toInt());
84                 buf = attributes().value(attrSRMHigh).toString();
85                 style.setSRMHi(buf.toInt());
86                 style.setName(readElementText());
87                 data->insertStyle(style);
88             }
89 
90             // grains
91             else if (name() == tagGrains) {
92                 grainsflag = true;
93             }
94             // grain
95             else if (name() == tagGrain) {
96                 if (!grainsflag) qWarning("Warning: mislocated grain tag");
97                 Grain grain;
98                 grain.setWeight(Weight(1.0, data->defaultGrainUnit()));
99                 buf = attributes().value(attrExtract).toString();
100                 grain.setExtract(buf.toDouble());
101                 buf = attributes().value(attrColor).toString();
102                 grain.setColor(buf.toDouble());
103                 // TODO: grain type deprecates data file format (0.4.0)
104                 grain.setType(attributes().value(attrType).toString());
105                 grain.setUse(attributes().value(attrUse).toString());
106                 grain.setName(readElementText());
107                 data->insertGrain(grain);
108             }
109 
110             // hops
111             else if (name() == tagHops) {
112                 hopsflag = true;
113             }
114             // hop
115             else if (name() == tagHop) {
116                 // TODO: attrForm is deprecated 0.4.0
117                 if (!hopsflag) qWarning("Warning: mislocated hop tag");
118                 Hop hop;
119                 buf = attributes().value(attrQuantity).toString();
120                 hop.setWeight(Weight(1.0, data->defaultHopUnit()));
121                 buf = attributes().value(attrAlpha).toString();
122                 hop.setAlpha(buf.toDouble());
123                 hop.setName(readElementText());
124                 data->insertHop(hop);
125             }
126 
127             // miscs
128             else if (name() == tagMiscs) {
129                 miscsflag = true;
130             }
131             // misc
132             else if (name() == tagMisc) {
133                 if (!miscsflag) qWarning("Warning: mislocated misc tag");
134                 Misc misc;
135                 misc.setQuantity(Quantity(1.0, data->defaultMiscUnit()));
136                 misc.setType(attributes().value(attrType).toString());
137                 misc.setNotes(attributes().value(attrNotes).toString());
138                 misc.setName(readElementText());
139                 data->insertMisc(misc);
140             }
141 
142             // utilization
143             else if (name() == tagUtilization) {
144                 utilflag = true;
145             }
146             // entry
147             else if (name() == tagEntry) {
148                 if (!utilflag) qWarning("Warning: mislocated entry tag");
149                 UEntry entry;
150                 buf = attributes().value(attrTime).toString();
151                 entry.time = buf.toUInt();
152                 buf = attributes().value(attrUtil).toString();
153                 entry.utilization = buf.toUInt();
154                 data->addUEntry(entry);
155             }
156 
157             // unknown tag, skip
158             else {
159                 qWarning() << "Warning: Unknown tag" << name().toString();
160                 skipElement();
161             }
162         }
163 
164         else if (isEndElement()) {
165             // unset flags
166             if (name() == tagStyles) stylesflag = false;
167             else if (name() == tagGrains) grainsflag = false;
168             else if (name() == tagHops) hopsflag = false;
169             else if (name() == tagMiscs) miscsflag = false;
170             else if (name() == tagUtilization) utilflag = false;
171         }
172     }
173 
174     return true;
175 }
176 
177 // skip over xml element
skipElement()178 void DataReader::skipElement()
179 {
180     if (isStartElement()) {
181         int level = 0;
182         while (!atEnd()) {
183             readNext();
184 
185             if (isStartElement()) level++;
186             if (isEndElement()) level--;
187 
188             if (level < 0) break;
189         }
190     }
191 }
192 
193 // DataWriter /////////////////////////////////////////////////////////////////
194 
195 ///////////////////////////////////////////////////////////////////////////////
196 // DataWriter()
197 // ------------
198 // Constructor
199 
DataWriter(QIODevice * device)200 DataWriter::DataWriter(QIODevice *device)
201     : QXmlStreamWriter(device)
202 {
203 #if (QT_VERSION >= QT_VERSION_CHECK(4, 4, 0))
204     setAutoFormatting(true);
205     setAutoFormattingIndent(2);
206 #endif
207 }
208 
209 ///////////////////////////////////////////////////////////////////////////////
210 // writeData()
211 // -----------
212 // Write out data
213 
writeData(Data * data)214 bool DataWriter::writeData(Data *data)
215 {
216     writeStartDocument();
217     writeDTD(QString("<!DOCTYPE %1>").arg(tagDoc));
218 
219     // write root element
220     writeStartElement(tagDoc);
221     writeAttribute(attrVersion, VERSION);
222 
223     // write styles
224     writeStartElement(tagStyles);
225     foreach(Style style, data->stylemap_) {
226         // iterate through grain list
227         writeStartElement(tagStyle);
228         writeAttribute(attrOGLow, QString::number(style.OGLow()));
229         writeAttribute(attrOGHigh, QString::number(style.OGHi()));
230         writeAttribute(attrFGLow, QString::number(style.FGLow()));
231         writeAttribute(attrFGHigh, QString::number(style.FGHi()));
232         writeAttribute(attrIBULow, QString::number(style.IBULow()));
233         writeAttribute(attrIBUHigh, QString::number(style.IBUHi()));
234         writeAttribute(attrSRMLow, QString::number(style.SRMLow()));
235         writeAttribute(attrSRMHigh, QString::number(style.SRMHi()));
236         writeCharacters(style.name());
237         writeEndElement();
238     }
239     writeEndElement(); // tagStyles
240 
241     // write grains
242     writeStartElement(tagGrains);
243     foreach(Grain grain, data->grainmap_) {
244         // iterate through grain list
245         writeStartElement(tagGrain);
246         writeAttribute(attrExtract, QString::number(grain.extract()));
247         writeAttribute(attrColor, QString::number(grain.color()));
248         writeAttribute(attrType, grain.type());
249         writeAttribute(attrUse, grain.use());
250         writeCharacters(grain.name());
251         writeEndElement();
252     }
253     writeEndElement(); // tagGrains
254 
255     // write hops
256     writeStartElement(tagHops);
257     foreach(Hop hop, data->hopmap_) {
258         // iterate through hop list
259         writeStartElement(tagHop);
260         writeAttribute(attrAlpha, QString::number(hop.alpha()));
261         writeCharacters(hop.name());
262         writeEndElement();
263     }
264     writeEndElement(); // tagHops
265 
266 
267     // write misc ingredients
268     writeStartElement(tagMiscs);
269     foreach(Misc misc, data->miscmap_) {
270         // iterate through misc list
271         writeStartElement(tagMisc);
272         writeAttribute(attrType, misc.type());
273         writeAttribute(attrNotes, misc.notes());
274         writeCharacters(misc.name());
275         writeEndElement();
276     }
277     writeEndElement(); // tagMiscs
278 
279     // write utilization table
280     writeStartElement(tagUtilization);
281     foreach(UEntry uentry, data->utable_) {
282         // iterate through entries
283         writeStartElement(tagEntry);
284         writeAttribute(attrTime, QString::number(uentry.time));
285         writeAttribute(attrUtil, QString::number(uentry.utilization));
286         writeEndElement();
287     }
288     writeEndElement(); // tagMiscs
289 
290     writeEndElement(); // tagDoc
291     writeEndDocument();
292 
293     return true;
294 }
295