1
package mso.generator;
2
3
import java.io.File;
4
import java.io.FileWriter;
5
import java.io.IOException;
6
import java.io.PrintWriter;
7
import java.util.regex.Matcher;
8
import java.util.regex.Pattern;
9
10
import mso.generator.utils.Choice;
11
import mso.generator.utils.Limitation;
12
import mso.generator.utils.MSO;
13
import mso.generator.utils.Member;
14
import mso.generator.utils.Option;
15
import mso.generator.utils.Stream;
16
import mso.generator.utils.Struct;
17
import mso.generator.utils.TypeRegistry;
18
19
public class QtParserGenerator {
20
21
	public class QtParserConfiguration {
22
		public String namespace;
23
		public String outputdir;
24
		public String basename;
25
		public boolean createHeader;
26
		public boolean enableXml;
27
		public boolean enableWriting;
28
		public boolean enableIntrospection;
29
		public boolean enableToString;
30
		public boolean enableStyleTextPropAtomFix;
31
	}
32
33
	final public QtParserConfiguration config = new QtParserConfiguration();
34
35
	final private String generatedWarning = "/* This code was generated by msoscheme (http://gitorious.org/msoscheme) */";
36
37
	void generate(MSO mso) throws IOException {
38
		FileWriter fout;
39
		if (config.createHeader) {
40
			fout = new FileWriter(config.outputdir + File.separator
41
					+ config.basename + ".h");
42
		} else {
43
			fout = new FileWriter(config.outputdir + File.separator
44
					+ config.basename + ".cpp");
45
		}
46
		PrintWriter out = new PrintWriter(fout);
47
		out.println(generatedWarning);
48
		if (config.createHeader) {
49
			out.println("#ifndef " + config.basename.toUpperCase() + "_H");
50
			out.println("#define " + config.basename.toUpperCase() + "_H");
51
		}
52
		out.println("#include <QString>");
53
		out.println("#include <QByteArray>");
54
		out.println("#include <QVector>");
55
		out
56
				.println("#include <QSharedPointer>// replace with QScopedPointer when switching to Qt 4.6");
57
		if (config.enableXml) {
58
			out.println("#include <QXmlStreamReader>");
59
		}
60
		if (config.enableWriting) {
61
			out.println("#include \"leoutputstream.h\"");
62
		}
63
		if (config.enableIntrospection) {
64
			out.println("#include \"introspection.h\"");
65
		}
66
		out.println("class LEInputStream;");
67
		out.println("namespace " + config.namespace + "{");
68
69
		if (config.enableXml) {
70
			out.println("void skipToStartElement(QXmlStreamReader& in) {");
71
			out.println("    do {");
72
			out.println("        in.readNext();");
73
			out.println("    } while (!in.atEnd() && !in.isStartElement());");
74
			out.println("}");
75
		}
76
77
		if (!config.enableIntrospection) {
78
			out.println("class StreamOffset {");
79
			out.println("public:");
80
			out.println("    virtual ~StreamOffset() {}");
81
			out.println("    quint32 streamOffset;");
82
			out.println("};");
83
		}
84
85
		for (Struct s : mso.structs) {
86
			out.println("class " + s.name + ";");
87
			out.println("void parse" + s.name + "(LEInputStream& in, " + s.name
88
					+ "& _s);");
89
			if (config.enableXml) {
90
				out.println("void parse" + s.name + "(QXmlStreamReader& in, "
91
						+ s.name + "& _s);");
92
			}
93
			if (config.enableWriting) {
94
				out.println("void write(const " + s.name
95
						+ "& v, LEOutputStream& out);");
96
			}
97
		}
98
99
		for (Struct s : mso.structs) {
100
			printStructureClassDeclaration(out, s);
101
		}
102
103
		if (config.createHeader) {
104
			out.println("} // close namespace");
105
			out.println("#endif");
106
			out.close();
107
			fout = new FileWriter(config.outputdir + File.separator
108
					+ config.basename + ".cpp");
109
			out = new PrintWriter(fout);
110
			out.println(generatedWarning);
111
			out.println("#include \"" + config.basename + ".h\"");
112
			out.println("using namespace " + config.namespace + ";");
113
		}
114
		out.println("#include \"leinputstream.h\"");
115
116
		if (config.enableIntrospection) {
117
			for (Struct s : mso.structs) {
118
				printStructureClassImplementation(out, s);
119
			}
120
		}
121
122
		for (Struct s : mso.structs) {
123
			printStructureParser(out, s);
124
			if (config.enableWriting) {
125
				printStructureWriter(out, s);
126
			}
127
			if (config.enableXml) {
128
				printStructureXmlParser(out, s);
129
			}
130
		}
131
132
		if (!config.createHeader) {
133
			out.println("}");// close namespace
134
		}
135
136
		if (config.enableIntrospection) {
137
			out
138
					.println("const Introspectable* parse(const QString& key, LEInputStream& in) {");
139
			out.println("    const Introspectable* i = 0;");
140
			boolean first = true;
141
			for (Stream s : mso.streams) {
142
				out.print("    ");
143
				if (first) {
144
					first = false;
145
				} else {
146
					out.print("} else ");
147
				}
148
				out.println("if (\"" + s.key + "\" == key) {"); // TODO: fix for
149
				// \001 and \005
150
				// prefix
151
				out.println("        " + s.type + " *_t = new " + s.type
152
						+ "(0);");
153
				out.println("        parse" + s.type + "(in, *_t);");
154
				out.println("        i = _t;");
155
			}
156
			out.println("    } else {");
157
			out.println("        TODOS* _t = new TODOS(0);");
158
			out.println("        parseTODOS(in, *_t);");
159
			out.println("        i = _t;");
160
			out.println("    }");
161
			out.println("    return i;");
162
			out.println("}");
163
		}
164
165
		if (config.enableXml) {
166
			out
167
					.println("const QMap<QString,QSharedPointer<const Introspectable> > parse(QXmlStreamReader& in) {");
168
			out
169
					.println("    QMap<QString,QSharedPointer<const Introspectable> > streams;");
170
			out.println("    // skip until first element");
171
			out.println("    while (!in.atEnd() && !in.isStartElement()) {");
172
			out.println("        in.readNext();");
173
			out.println("    }");
174
			out.println("    if (!in.isStartElement()) {");
175
			out.println("        return streams;");
176
			out.println("    }");
177
			out.println("    do {");
178
			out.println("        in.readNext();");
179
			out.println("    } while (!in.atEnd() && !in.isStartElement());");
180
			out.println("    if (!in.isStartElement()) {");
181
			out.println("        return streams;");
182
			out.println("    }");
183
			out.println("    do {");
184
			out.println("        QString name = in.name().toString();");
185
			out.println("        if (streams.contains(name)) {");
186
			out.println("            streams.clear();");
187
			out.println("            return streams;");
188
			out.println("        }");
189
			boolean first = true;
190
			for (Stream s : mso.streams) {
191
				out.print("        ");
192
				if (first) {
193
					first = false;
194
				} else {
195
					out.print("} else ");
196
				}
197
				out.println("if (\"" + s.key + "\" == name) {");
198
				out
199
						.println("            QSharedPointer<Introspectable> _t(new "
200
								+ s.type + "(0));");
201
				out.println("            parse" + s.type + "(in, *static_cast<"
202
						+ s.type + "*>(_t.data()));");
203
				// out.println("            QSharedPointer<Introspectable> _t(new PowerPointStructs());");
204
				// out.println("            parsePowerPointStructs(in, *static_cast<PowerPointStructs*>(_t.data()));");
205
				out.println("            streams[name] = _t;");
206
			}
207
			out.println("        } else { // unknown stream should be binary");
208
			out
209
					.println("            QSharedPointer<Introspectable> _t(new TODOS(0));");
210
			out
211
					.println("            parseTODOS(in, *static_cast<TODOS*>(_t.data()));");
212
			out.println("            streams[name] = _t;");
213
			out.println("        }");
214
			out.println("        do {");
215
			out.println("            in.readNext();");
216
			out.println("        } while (in.isWhitespace());");
217
			out.println("    } while (in.isStartElement());");
218
			out.println("    qDebug() << in.tokenType();");
219
			out.println("    if (!in.isEndElement()) {");
220
			out
221
					.println("        qDebug() << \"parsing error: not at end of an element\";");
222
			out.println("        streams.clear();");
223
			out.println("    }");
224
			out.println("    in.readNext();");
225
			out.println("    if (!in.isEndDocument()) {");
226
			out
227
					.println("        qDebug() << \"parsing error: not at end of xml\";");
228
			out.println("        streams.clear();");
229
			out.println("    }");
230
			out.println("    return streams;");
231
			out.println("}");
232
233
			out
234
					.println("void serialize(const Introspectable* i, const QString& key, LEOutputStream& out)  {");
235
			first = true;
236
			for (Stream s : mso.streams) {
237
				out.print("    ");
238
				if (first) {
239
					first = false;
240
				} else {
241
					out.print("} else ");
242
				}
243
				out.println("if (\"" + s.key + "\" == key) {"); // TODO: fix for
244
				// \001 and \005
245
				// prefix
246
				out.println("        write(*static_cast<const " + s.type
247
						+ "*>(i), out);");
248
			}
249
			out.println("    } else {");
250
			out.println("        write(*static_cast<const TODOS*>(i), out);");
251
			out.println("    }");
252
			out.println("}");
253
		}
254
		out.close();
255
		fout.close();
256
	}
257
258
	private void printStructureParser(PrintWriter out, Struct s) {
259
		out.print("void ");
260
		if (config.namespace != null && config.namespace.length() > 0) {
261
			out.print(config.namespace + "::");
262
		}
263
		out.println("parse" + s.name + "(LEInputStream& in, " + s.name
264
				+ "& _s) {");
265
		out.println("    _s.streamOffset = in.getPosition();");
266
		if (s.containsKnownLengthArrayMember) {
267
			out.println("    int _c;");
268
		}
269
		if (s.containsArrayMember || s.containsOptionalMember
270
				|| s.containsChoice) {
271
			out.println("    LEInputStream::Mark _m;");
272
		}
273
		if (s.containsOptionalMember) {
274
			out.println("    bool _possiblyPresent;");
275
		}
276
		if (s.containsUnknownLengthArrayMember) {
277
			out.println("    bool _atend;");
278
		}
279
		for (Member m : s.members) {
280
			if (config.enableStyleTextPropAtomFix
281
					&& s.name.equals("StyleTextPropAtom")
282
					&& m.name.equals("todo")) {
283
				break;
284
			}
285
			printStructureMemberParser(out, s.name, m);
286
			if (m.type().name.contains("RecordHeader")) {
287
				// out.println("qDebug() << in.getPosition()<<\" \"<<\"" +
288
				// s.name
289
				// + "\"<<_s.rh.toString();");
290
			}
291
			if (config.enableStyleTextPropAtomFix
292
					&& s.name.equals("TextContainer") && m.name.equals("style")) {
293
				styleTextPropAtomFix2(out);
294
			}
295
		}
296
		out.println("}");
297
	}
298
299
	private void printStructureXmlParser(PrintWriter out, Struct s) {
300
		out.println("void parse" + s.name + "(QXmlStreamReader& in, " + s.name
301
				+ "& _s) {");
302
		out.println("    in.readNext();");
303
		for (Member m : s.members) {
304
			printStructureMemberXmlParser(out, m);
305
		}
306
		/*
307
		 * out.println("    int depth = 0;");
308
		 * out.println("    while (!in.atEnd()) {");
309
		 * out.println("        if (type == QXmlStreamReader::StartElement) {");
310
		 * out.println("            depth++;");
311
		 * out.println("        } else if (type == QXmlStreamReader::EndElement "
312
		 * + "&& --depth < 0) {"); out.println("            return;");
313
		 * out.println("        }");
314
		 * out.println("        type = in.readNext();"); out.println("    }");
315
		 */
316
		out.println("}");
317
318
	}
319
320
	static private String prependStructureToExpression(String expression,
321
			String structureName) {
322
		if (expression.length() > 0) {
323
			Pattern p = Pattern.compile("([^.\\w])([.a-zA-Z])");
324
			Matcher m = p.matcher(expression);
325
			expression = m.replaceAll("$1" + structureName + ".$2");
326
			p = Pattern.compile("^([a-zA-Z])");
327
			m = p.matcher(expression);
328
			expression = m.replaceAll(structureName + ".$1");
329
		}
330
		return expression;
331
	}
332
333
	private void printStructureMemberParser(PrintWriter out, String structure,
334
			Member m) {
335
		String s = "    ";
336
		String index = (m.count == null) ? "" : "[_i]";
337
		if (m.condition != null) {
338
			String condition = prependStructureToExpression(m.condition, "_s");
339
			if (!m.isStruct) {
340
				out.println(s + "_s._has_" + m.name + " = " + condition + ";");
341
				out.println(s + "if (_s._has_" + m.name + ") {");
342
			} else {
343
				out.println(s + "if (" + condition + ") {");
344
			}
345
			s = s + "    ";
346
			if (m.isStruct) {
347
				out.println(s + "_s." + m.name + " = QSharedPointer<"
348
						+ m.type().name + ">(new " + m.type().name + "(&_s));");
349
				index = ".data()";
350
			}
351
		}
352
		String parse;
353
		if (m.isStruct) {
354
			String star = (m.condition == null) ? "" : "*";
355
			parse = "parse" + m.type().name + "(in, " + star + "_s." + m.name
356
					+ index + ");";
357
		} else {
358
			parse = "_s." + m.name + index + " = in.read" + m.type().name
359
					+ "();";
360
		}
361
		if (m.isChoice) {
362
			printChoiceParser(out, s, structure, m);
363
			return;
364
		}
365
		if (m.isArray && m.count == null) {
366
			if (m.size != null) {
367
				printFixedSizeArrayParser(out, s, m);
368
			} else {
369
				// array for which no size is given: parse items until one fails
370
				printVariableArrayParser(out, s, m);
371
			}
372
			return;
373
		}
374
		if (m.isOptional) {
375
			printOptionalMemberParser(out, s, m);
376
			return;
377
		}
378
		if (m.count != null) {
379
			String count = prependStructureToExpression(m.count, "_s");
380
			out.println(s + "_c = " + count + ";");
381
		}
382
		if (m.count != null) {
383
			if (!m.isStruct) {
384
				out.println(s + "_s." + m.name + ".resize(_c);");
385
			}
386
			if (m.type() == m.registry.uint8) { // special case for
387
				// reading bytearrays quickly
388
				out.println(s + "in.readBytes(_s." + m.name + ");");
389
			} else {
390
				out.println(s + "for (int _i=0; _i<_c; ++_i) {");
391
				if (m.isStruct) {
392
					out.println(s + "    _s." + m.name + ".append("
393
							+ m.type().name + "(&_s));");
394
				}
395
				out.println(s + "    " + parse);
396
				printLimitationCheck(out, "        ", "_s." + m.name + "[_i]",
397
						m);
398
				out.println(s + "}");
399
			}
400
		} else {
401
			out.println(s + parse);
402
			printLimitationCheck(out, s, "_s." + m.name, m);
403
		}
404
		if (m.condition != null) {
405
			out.println("    }");
406
		}
407
	}
408
409
	private void printStructureMemberXmlParser(PrintWriter out, Member m) {
410
		String s = "    ";
411
412
		out.println(s + "if (!in.isStartElement()) {");
413
		out.println(s + "    qDebug() << \"not startelement in "
414
				+ m.type().name + " \" << in.lineNumber();");
415
		out.println(s + "    return;");
416
		out.println(s + "}");
417
		if (!m.isOptional && !m.isArray) {
418
			out.println(s + "if (in.name() != \"" + m.name + "\") {");
419
			out.println(s + "    qDebug() << \"not startelement in " + m.name
420
					+ " \" << in.lineNumber();");
421
			out.println(s + "    return;");
422
			out.println(s + "}");
423
		}
424
		if (m.isOptional) {
425
			out.println(s + "if (in.name() == \"" + m.name + "\") {");
426
			s = s + "    ";
427
		}
428
		if (!m.isStruct) {
429
			out.println(s + "in.readElementText();");
430
		} else {
431
			out.println(s + "skipToStartElement(in);");
432
			// out.println(s + "parse" + m.type + "(in, _s." + m.name + ");");
433
		}
434
		// out.println(s + "if (in.name() == \"" + m.name + "\") {");
435
		if (m.isOptional) {
436
			out.println("    }");
437
		}
438
	}
439
440
	private void printStructureWriter(PrintWriter out, Struct s) {
441
		out.println("void write(const " + s.name
442
				+ "& _s, LEOutputStream& out) {");
443
		for (Member m : s.members) {
444
			printStructureMemberWriter(out, m);
445
		}
446
		out.println("}");
447
	}
448
449
	private void printStructureMemberWriter(PrintWriter out, Member m) {
450
		String s = "    ";
451
		if (m.condition != null) {
452
			out.println("    if (" + getExpression("_s", m.condition) + ") {");
453
			s = s + "    ";
454
		}
455
		if (m.isChoice) {
456
			boolean first = true;
457
			for (String t : ((Choice) m.type()).getChoiceNames()) {
458
				out.print(s);
459
				if (!first) {
460
					out.print("} else ");
461
				}
462
				first = false;
463
				out.println("if (_s." + m.name + ".is<" + t + ">()) {");
464
				out.println(s + "    write(*_s." + m.name + ".get<" + t
465
						+ ">(), out);");
466
			}
467
			out.println(s + "}");
468
		} else if (m.isArray) {
469
			if (m.type() == m.registry.uint8) {
470
				out.println(s + "out.writeBytes(_s." + m.name + ");");
471
			} else {
472
				String t = getTypeName(m.type());
473
				out.println(s + "foreach (" + t + " _i, _s." + m.name + ") {");
474
				if (m.isStruct) {
475
					out.println(s + "    write(_i, out);");
476
				} else {
477
					out.println(s + "    out.write" + m.type().name + "(_i);");
478
				}
479
				out.println(s + "}");
480
			}
481
		} else if (m.isStruct) {
482
			out.print(s);
483
			if (m.isOptional || m.condition != null) {
484
				out.print("if (_s." + m.name + ") write(*");
485
			} else {
486
				out.print("write(");
487
			}
488
			out.println("_s." + m.name + ", out);");
489
		} else {
490
			out.println(s + "out.write" + m.type().name + "(_s." + m.name
491
					+ ");");
492
		}
493
		if (m.condition != null) {
494
			out.println("    }");
495
		}
496
	}
497
498
	private String getTypeName(TypeRegistry.Type t) {
499
		TypeRegistry r = t.registry;
500
		if (t instanceof Choice) {
501
			return createChoiceClass(t.name, (Choice) t);
502
		} else if (t == r.bit) {
503
			return "bool";
504
		} else if (t == r.uint2 || t == r.uint3 || t == r.uint4 || t == r.uint5
505
				|| t == r.uint6 || t == r.uint7 || t == r.uint8) {
506
			return "quint8";
507
		} else if (t == r.uint9 || t == r.uint12 || t == r.uint13
508
				|| t == r.uint14 || t == r.uint15
509
				|| t == r.uint16) {
510
			return "quint16";
511
		} else if (t == r.uint20 || t == r.uint30 || t == r.uint32) {
512
			return "quint32";
513
		} else if (t == r.int16) {
514
			return "qint16";
515
		} else if (t == r.int32) {
516
			return "qint32";
517
		}
518
		return t.name;
519
	}
520
521
	private String createChoiceClass(String name, Choice c) {
522
		String base = "StreamOffset";
523
		if (config.enableIntrospection) {
524
			base = "Introspectable";
525
		}
526
		String choice = "class " + name + " : public QSharedPointer<" + base
527
				+ "> {\n";
528
		choice += "    public:\n";
529
		choice += "        " + name + "() {}\n";
530
		for (String s : c.getChoiceNames()) {
531
			choice += "        explicit " + name + "(" + s
532
					+ "* a) :QSharedPointer<" + base + ">(a) {}\n";
533
		}
534
		choice += "        template <typename T> T*get() { return dynamic_cast<T*>(this->data()); }\n";
535
		choice += "        template <typename T> const T*get() const { return dynamic_cast<const T*>(this->data()); }\n";
536
		choice += "        template <typename T> bool is() const { return get<T>(); }\n";
537
		choice += "    };\n";
538
		choice += "    " + name;
539
		return choice;
540
	}
541
542
	private String getMemberDeclaration(Member m) {
543
		String t = getTypeName(m.type());
544
		if (m.isArray) {
545
			if (m.isStruct) {
546
				return "QList<" + m.type().name + "> " + m.name;
547
			} else {
548
				if ("quint8".equals(t)) {
549
					return "QByteArray " + m.name;
550
				} else {
551
					return "QVector<" + t + "> " + m.name;
552
				}
553
			}
554
		} else if (m.isStruct && (m.isOptional || m.condition != null)) {
555
			return "QSharedPointer<" + t + "> " + m.name;
556
		}
557
		return t + " " + m.name;
558
	}
559
560
	private String memberToString(Member m, String prefix) {
561
		String s;
562
		String mn = prefix + m.name;
563
		if (m.isArray) {
564
			s = "\"[array of " + mn + "]\"";
565
		} else {
566
			if (m.isInteger) {
567
				s = "QString::number(" + mn + ") + \"(\" + QString::number("
568
						+ mn + ",16).toUpper() + \")\"";
569
			} else if (m.type() == m.type().registry.bit) {
570
				s = "QString::number(" + mn + ")";
571
			} else if (m.isChoice) {
572
				s = "\"<choice>\"";
573
			} else if (m.isOptional || m.condition != null) {
574
				s = "((" + mn + ")?" + mn + "->toString() :\"null\")";
575
			} else {
576
				s = mn + ".toString()";
577
			}
578
		}
579
		return s;
580
	}
581
582
	private void styleTextPropAtomFix(PrintWriter out) {
583
		out.println("    RecordHeader rh;");
584
		out.println("    QList<TextPFRun> rgTextPFRun;");
585
		out.println("    QList<TextCFRun> rgTextCFRun;");
586
	}
587
588
	private void styleTextPropAtomFix2(PrintWriter out) {
589
		out.println("    if (_s.style) {");
590
		out.println("        quint32 count = 0;");
591
		out.println("        if (_s.text.is<TextCharsAtom>()) {");
592
		out
593
				.println("            count = _s.text.get<TextCharsAtom>()->textChars.size();");
594
		out.println("        }");
595
		out.println("        if (_s.text.is<TextBytesAtom>()) {");
596
		out
597
				.println("            count = _s.text.get<TextBytesAtom>()->textChars.size();");
598
		out.println("        }");
599
		out.println("        quint32 sum = 0;");
600
		out.println("        do {");
601
		out
602
				.println("        _s.style->rgTextPFRun.append(TextPFRun(_s.style.data()));");
603
		out
604
				.println("            parseTextPFRun(in, _s.style->rgTextPFRun.last());");
605
		out.println("            sum += _s.style->rgTextPFRun.last().count;");
606
		out.println("        } while (sum <= count);");
607
		out.println("        sum = 0;");
608
		out.println("        do {");
609
		out
610
				.println("            _s.style->rgTextCFRun.append(TextCFRun(_s.style.data()));");
611
		out
612
				.println("            parseTextCFRun(in, _s.style->rgTextCFRun.last());");
613
		out.println("            sum += _s.style->rgTextCFRun.last().count;");
614
		out.println("        } while (sum <= count);");
615
		out.println("    }");
616
	}
617
618
	private void printStructureClassDeclaration(PrintWriter out, Struct s) {
619
		out.print("class " + s.name);
620
		if (config.enableIntrospection) {
621
			out.println(" : public Introspectable {");
622
			out.println("private:");
623
			out.println("    class _Introspection;");
624
		} else {
625
			out.println(" : public StreamOffset {");
626
627
		}
628
		out.println("public:");
629
		if (config.enableIntrospection) {
630
			out.println("    static const Introspection _introspection;");
631
		}
632
		for (Member m : s.members) {
633
			if (!m.isStruct && m.condition != null) {
634
				out.println("    bool _has_" + m.name + ";");
635
			}
636
		}
637
		if (config.enableStyleTextPropAtomFix
638
				&& s.name.equals("StyleTextPropAtom")) {
639
			styleTextPropAtomFix(out);
640
		} else {
641
			for (Member m : s.members) {
642
				String d = getMemberDeclaration(m);
643
				out.println("    " + d + ";");
644
			}
645
		}
646
		boolean first = true;
647
		if (config.enableIntrospection) {
648
			out.print("    explicit " + s.name
649
					+ "(const Introspectable* parent)");
650
			out.print("\n       :Introspectable(parent)");
651
			first = false;
652
			for (Member m : s.members) {
653
				if (m.isStruct && !m.isArray && !m.isOptional && !(m.isChoice)
654
						&& m.condition == null) {
655
					if (first) {
656
						out.print("\n       :");
657
						first = false;
658
					} else {
659
						out.print(",\n        ");
660
					}
661
					out.print(m.name + "(this)");
662
				}
663
			}
664
			out.println(" {}");
665
		} else {
666
			out.println("    " + s.name + "(void* /*dummy*/ = 0) {}");
667
		}
668
669
		// function toString
670
		if (config.enableToString) {
671
			out.println("    QString toString() {");
672
			out.println("        QString _s = \"" + s.name + ":\";");
673
			for (Member m : s.members) {
674
				out.print("        _s = _s + \"" + m.name + ": \" + ");
675
				out.print(memberToString(m, ""));
676
				out.println(" + \", \";");
677
			}
678
			out.println("        return _s;");
679
			out.println("    }");
680
		}
681
		if (config.enableIntrospection) {
682
			out
683
					.println("    const Introspection* getIntrospection() const { return &_introspection; }");
684
		}
685
		out.println("};");
686
687
	}
688
689
	private void printStructureClassImplementation(PrintWriter out, Struct s) {
690
		final int nm = s.members.size();
691
		final String ns = s.name + "::_Introspection";
692
		out.println("class " + ns + " {");
693
		out.println("public:");
694
		out.println("    static const QString name;");
695
		out.println("    static const int numberOfMembers;");
696
		out.println("    static const QString names[" + nm + "];");
697
		out.println("    static int (* const numberOfInstances[" + nm
698
				+ "])(const Introspectable*);");
699
		out.println("    static QVariant (* const value[" + nm
700
				+ "])(const Introspectable*, int position);");
701
		out.println("    static const Introspectable* (* const introspectable["
702
				+ nm + "])(const Introspectable*, int position);");
703
		for (Member m : s.members) {
704
			if (s.name.equals("StyleTextPropAtom")
705
					&& config.enableStyleTextPropAtomFix
706
					&& m.name.equals("todo")) {
707
				break;
708
			}
709
			if (!m.isStruct && !(m.isChoice)) {
710
				if (m.condition != null) {
711
					out.println("    static int count_" + m.name
712
							+ "(const Introspectable* i) {");
713
					out.println("        return static_cast<const " + s.name
714
							+ "*>(i)->_has_" + m.name + " ?1 :0;");
715
					out.println("    }");
716
				}
717
			} else if (m.isOptional || m.condition != null) {
718
				out.println("    static int count_" + m.name
719
						+ "(const Introspectable* i) {");
720
				out.println("        return get_" + m.name + "(i, 0) ?1 :0;");
721
				out.println("    }");
722
			} else if (m.isArray) {
723
				out.println("    static int count_" + m.name
724
						+ "(const Introspectable* i) {");
725
				out.println("        return static_cast<const " + s.name
726
						+ "*>(i)->" + m.name + ".size();");
727
				out.println("    }");
728
			}
729
			if (m.isStruct || m.isChoice) {
730
				out.println("    static const Introspectable* get_" + m.name
731
						+ "(const Introspectable* i, int j) {");
732
			} else {
733
				out.println("    static QVariant get_" + m.name
734
						+ "(const Introspectable* i, int j) {");
735
			}
736
			String dm = "static_cast<const " + s.name + "*>(i)->" + m.name + "";
737
			if (!(m.isChoice)) {
738
				out.print("        ");
739
				if (!m.isStruct) {
740
					if (m.isArray && m.type() != m.type().registry.uint8) {
741
						out.println("return qVariantFromValue(" + dm + ");");
742
					} else {
743
						out.println("return " + dm + ";");
744
					}
745
				} else if (m.isArray) {
746
					out.println("return &(" + dm + "[j]);");
747
				} else if (m.isOptional || m.condition != null) {
748
					out.println("return " + dm + ".data();");
749
				} else {
750
					out.println("return &(" + dm + ");");
751
				}
752
			} else {
753
				out.println("        return static_cast<const " + s.name
754
						+ "*>(i)->" + m.name + ".data();");
755
			}
756
			out.println("    }");
757
		}
758
		out.println("};");
759
		out.println("const QString " + ns + "::name(\"" + s.name + "\");");
760
		out.println("const int " + ns + "::numberOfMembers(" + nm + ");");
761
		out.println("const QString " + ns + "::names[" + nm + "] = {");
762
		for (Member m : s.members) {
763
			out.println("    \"" + m.name + "\",");
764
		}
765
		out.println("};");
766
		out.println("int (* const " + ns + "::numberOfInstances[" + nm
767
				+ "])(const Introspectable*) = {");
768
		for (Member m : s.members) {
769
			if (m.condition != null
770
					|| ((m.isStruct || m.isChoice) && (m.isOptional || m.isArray))) {
771
				out.println("    _Introspection::count_" + m.name + ",");
772
			} else {
773
				// arrays of simple types count as one instance
774
				out.println("    Introspection::one,");
775
			}
776
		}
777
		out.println("};");
778
		out.println("QVariant (* const " + ns + "::value[" + nm
779
				+ "])(const Introspectable*, int position) = {");
780
		for (Member m : s.members) {
781
			if (s.name.equals("StyleTextPropAtom")
782
					&& config.enableStyleTextPropAtomFix
783
					&& m.name.equals("todo")) {
784
				break;
785
			} else if (m.isStruct || m.isChoice) {
786
				out.println("    Introspection::nullValue,");
787
			} else {
788
				out.println("    _Introspection::get_" + m.name + ",");
789
			}
790
		}
791
		out.println("};");
792
		out.println("const Introspectable* (* const " + ns
793
				+ "::introspectable[" + nm
794
				+ "])(const Introspectable*, int position) = {");
795
		for (Member m : s.members) {
796
			if (m.isStruct || m.isChoice) {
797
				out.println("    _Introspection::get_" + m.name + ",");
798
			} else {
799
				out.println("    Introspection::null,");
800
			}
801
		}
802
		out.println("};");
803
		out.println("const Introspection " + s.name + "::_introspection(");
804
		out
805
				.println("    \""
806
						+ s.name
807
						+ "\", "
808
						+ s.members.size()
809
						+ ", _Introspection::names, _Introspection::numberOfInstances, _Introspection::value, _Introspection::introspectable);");
810
	}
811
812
	private void printChoiceParser(PrintWriter out, String s, String structure,
813
			Member m) {
814
		Choice c = (Choice) m.type();
815
		if (c.commonType == null) {
816
			printUnsureChoiceParser(out, s, structure, m);
817
		} else {
818
			printSureChoiceParser(out, s, structure, m);
819
		}
820
	}
821
822
	String getClause(String name, TypeRegistry.Type t, Option.Lim lim) {
823
		String ls = "";
824
		if (lim.limitations != null && lim.limitations.length > 0) {
825
			for (int i = 0; i < lim.limitations.length; ++i) {
826
				Limitation l = lim.limitations[i];
827
				String condition = l.expression;
828
				String mname = name;
829
				if (t instanceof Struct) {
830
					mname += "." + l.name;
831
				}
832
				if (condition == null) {
833
					condition = getCondition(mname, l);
834
				} else {
835
					condition = getExpression(mname, condition);
836
				}
837
				if (ls.length() > 0) {
838
					ls += "&&";
839
				}
840
				ls += "(" + condition + ")";
841
			}
842
		} else if (lim.lims != null && lim.lims.length > 0) {
843
			for (int i = 0; i < lim.lims.length; ++i) {
844
				Option.Lim l = lim.lims[i];
845
				String condition = getClause(name, t, l);
846
				if (ls.length() > 0) {
847
					ls += "||";
848
				}
849
				ls += "(" + condition + ")";
850
			}
851
		}
852
		return ls.replace("..", ".");
853
	}
854
855
	private void printSureChoiceParser(PrintWriter out, String s,
856
			String structure, Member m) {
857
		out.println(s + "_m = in.setMark();");
858
		Choice c = (Choice) m.type();
859
		String type = getTypeName(c.commonType);
860
		if (c.commonType instanceof Struct) {
861
			out.println(s + type + " _choice(&_s);");
862
			out.println(s + "parse" + type + "(in, _choice);");
863
		} else {
864
			out.println(s + type + " _choice = in.read" + c.commonType.name
865
					+ "();");
866
		}
867
		out.println(s + "in.rewind(_m);");
868
		out.println(s + "qint64 startPos = in.getPosition();");
869
		for (int i = 0; i < c.options.size(); ++i) {
870
			out.print(s);
871
			Option o = c.options.get(i);
872
			String clause = getClause("_choice", o.limitsType, o.lim);
873
			out.print("if (startPos == in.getPosition()");
874
			if (clause == null || (!m.isOptional && i == c.options.size() - 1)) {
875
				out.println(") {");
876
			} else {
877
				out.println(" && (" + clause + ")) {");
878
			}
879
			out.println(s + "    _s." + m.name + " = " + structure + "::"
880
					+ m.type().name + "(new " + o.type.name + "(&_s));");
881
			out.println(s + "    parse" + o.type.name + "(in, *(" + o.type.name
882
					+ "*)_s." + m.name + ".data());");
883
			out.println(s + "}");
884
		}
885
	}
886
887
	private void printUnsureChoiceParser(PrintWriter out, String s,
888
			String structure, Member m) {
889
		String closing = "";
890
		String exception = "_x";
891
		String choice;
892
		out.println(s + "_m = in.setMark();");
893
		Choice c = (Choice) m.type();
894
		String choices[] = c.getChoiceNames();
895
		int length = (m.isOptional) ? choices.length : choices.length - 1;
896
		for (int i = 0; i < length; ++i) {
897
			choice = choices[i];
898
			out.println(s + "try {");
899
			out.println(s + "    _s." + m.name + " = " + structure + "::"
900
					+ c.name + "(new " + choice + "(&_s));");
901
			out.println(s + "    parse" + choice + "(in, *(" + choice + "*)_s."
902
					+ m.name + ".data());");
903
			out.println(s + "} catch (IncorrectValueException " + exception
904
					+ ") {");
905
			out.println(s + "    _s." + m.name + ".clear();");
906
			out.println(s + "    in.rewind(_m);");
907
			exception = exception + "x";
908
			closing = closing + "}";
909
		}
910
		if (!m.isOptional) {
911
			choice = choices[choices.length - 1];
912
			out.println(s + "    _s." + m.name + " = " + structure + "::"
913
					+ c.name + "(new " + choice + "(&_s));");
914
			out.println(s + "    parse" + choice + "(in, *(" + choice + "*)_s."
915
					+ m.name + ".data());");
916
		}
917
		out.println(s + closing);
918
	}
919
920
	private void printFixedSizeArrayParser(PrintWriter out, String s, Member m) {
921
		out.println(s + "qint64 _startPos = in.getPosition();");
922
		/* _totalSize should really be just getExpression("_s", m.size)
923
		   The check for the end of the stream is only a workaround for
924
		   a limitation in the current Excel file parser. In Excel,
925
		   the stream is split up in blocks and the current code parses
926
		   these blocks separately and reassembles them later instead of
927
		   assembling the raw data transparantly in a stream. */
928
		out.println(s + "int _totalSize = qMin(" + getExpression("_s", m.size)
929
				+ ", quint32(in.getSize() - _startPos));");
930
		out.println(s + "_atend = in.getPosition() - _startPos >= _totalSize;");
931
		out.println(s + "while (!_atend) {");
932
		out.println(s + "    _s." + m.name + ".append(" + m.type().name
933
				+ "(&_s));");
934
		out.println(s + "    parse" + m.type().name + "(in, _s." + m.name
935
				+ ".last());");
936
		out.println(s + "    _atend = in.getPosition() - _startPos >= _totalSize;");
937
		out.println(s + "}");
938
	}
939
940
	private void printVariableArrayParser(PrintWriter out, String s, Member m) {
941
		out.println(s + "_atend = false;");
942
		out.println(s + "while (!_atend) {");
943
		out.println(s + "    _m = in.setMark();");
944
		out.println(s + "    try {");
945
		out.println(s + "        _s." + m.name + ".append(" + m.type().name
946
				+ "(&_s));");
947
		out.println(s + "        parse" + m.type().name + "(in, _s." + m.name
948
				+ ".last());");
949
		out.println(s + "    } catch(IncorrectValueException _e) {");
950
		out.println(s + "        _s." + m.name + ".removeLast();");
951
		out.println(s + "        _atend = true;");
952
		out.println(s + "        in.rewind(_m);");
953
		out.println(s + "    } catch(EOFException _e) {");
954
		out.println(s + "        _s." + m.name + ".removeLast();");
955
		out.println(s + "        _atend = true;");
956
		out.println(s + "        in.rewind(_m);");
957
		out.println(s + "    }");
958
		out.println(s + "}");
959
	}
960
961
	private void printOptionalMemberParser(PrintWriter out, String s, Member m) {
962
		out.println(s + "_m = in.setMark();");
963
		Option o = new Option((Struct) m.type(), null);
964
		String type = getTypeName(o.limitsType);
965
		out.println(s + "try {");
966
		out.println(s + "    " + type + " _optionCheck(&_s);");
967
		out.println(s + "    parse" + type + "(in, _optionCheck);");
968
		out.println(s + "    _possiblyPresent = "
969
				+ getClause("_optionCheck", o.limitsType, o.lim) + ";");
970
		out.println(s + "} catch(EOFException _e) {");
971
		out.println(s + "    _possiblyPresent = false;");
972
		out.println(s + "}");
973
974
		out.println(s + "in.rewind(_m);");
975
		out.println(s + "_m = in.setMark();");
976
		out.println(s + "if (_possiblyPresent) {");
977
		out.println(s + "    try {");
978
		out.println(s + "        _s." + m.name + " = QSharedPointer<"
979
				+ m.type().name + ">(new " + m.type().name + "(&_s));");
980
		out.println(s + "        parse" + m.type().name + "(in, *_s." + m.name
981
				+ ".data());");
982
		out.println(s + "    } catch(IncorrectValueException _e) {");
983
		out.println(s + "        _s." + m.name + ".clear();");
984
		out.println(s + "        in.rewind(_m);");
985
		out.println(s + "    } catch(EOFException _e) {");
986
		out.println(s + "        _s." + m.name + ".clear();");
987
		out.println(s + "        in.rewind(_m);");
988
		out.println(s + "    }");
989
		out.println(s + "}");
990
	}
991
992
	private void printLimitationCheck(PrintWriter out, String s, String name,
993
			Member m) {
994
		for (Limitation l : m.limitations) {
995
			String mname = l.name;
996
			if (!"".equals(mname)) {
997
				mname = name + "." + mname;
998
			} else {
999
				mname = name;
1000
			}
1001
			if (!m.isStruct) {
1002
				mname = "((" + getTypeName(m.type()) + ")" + mname + ")";
1003
			}
1004
			String condition = l.expression;
1005
			if (condition == null) {
1006
				condition = getCondition(mname, l);
1007
			} else {
1008
				condition = getExpression(mname, condition);
1009
			}
1010
1011
			out.println(s + "if (!(" + condition + ")) {");
1012
			String exceptionType = "IncorrectValueException";
1013
			out.println(s + "    throw " + exceptionType
1014
					+ "(in.getPosition(), \"" + condition + "\");");
1015
			out.println(s + "}");
1016
		}
1017
	}
1018
1019
	static String getExpression(String structure, String expression) {
1020
		if (Pattern.matches(".*[A-Za-z].*", expression)) {
1021
			return prependStructureToExpression(expression, structure);
1022
		}
1023
		return structure + expression;
1024
	}
1025
1026
	static String getCondition(String name, Limitation l) {
1027
		String value = l.value;
1028
		String cmp = " == ";
1029
		String cmb = " || ";
1030
		if (value.startsWith("!")) {
1031
			value = value.substring(1);
1032
			cmp = " != ";
1033
			cmb = " && ";
1034
		}
1035
		if (value.contains("|")) {
1036
			String values[] = value.split("\\|");
1037
			String c = name + cmp + values[0];
1038
			for (int i = 1; i < values.length; ++i) {
1039
				c = c + cmb + name + cmp + values[i];
1040
			}
1041
			return c;
1042
		}
1043
		if (!"".equals(value)) {
1044
			return name + cmp + value;
1045
		}
1046
		return l.value;
1047
	}
1048
}