• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.xsdc.cpp;
18 
19 import com.android.xsdc.CodeWriter;
20 import com.android.xsdc.FileSystem;
21 import com.android.xsdc.XmlSchema;
22 import com.android.xsdc.XsdConstants;
23 import com.android.xsdc.tag.*;
24 
25 import java.io.IOException;
26 import java.util.Arrays;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 
36 import javax.xml.namespace.QName;
37 
38 public class CppCodeGenerator {
39     public static final int GENERATE_ENUMS = 1 << 0;
40     public static final int GENERATE_PARSER = 1 << 1;
41 
42     private XmlSchema xmlSchema;
43     private String pkgName;
44     private Map<String, CppSimpleType> cppSimpleTypeMap;
45     private CodeWriter enumsCppFile;
46     private CodeWriter enumsHeaderFile;
47     private CodeWriter parserCppFile;
48     private CodeWriter parserHeaderFile;
49     private boolean hasAttr;
50     private boolean writer;
51     private int generators;
52     private boolean booleanGetter;
53     private boolean useTinyXml;
54     private String[] rootElements;
55 
56     private static final String UNKNOWN_ENUM = "UNKNOWN";
57 
CppCodeGenerator(XmlSchema xmlSchema, String pkgName, boolean writer, int generators, boolean booleanGetter, boolean useTinyXml, String[] rootElements)58     public CppCodeGenerator(XmlSchema xmlSchema, String pkgName, boolean writer, int generators,
59             boolean booleanGetter, boolean useTinyXml, String[] rootElements)
60             throws CppCodeGeneratorException {
61         this.xmlSchema = xmlSchema;
62         this.pkgName = pkgName;
63         this.writer = writer;
64         this.generators = generators;
65         this.booleanGetter = booleanGetter;
66         this.useTinyXml = useTinyXml;
67         this.rootElements = rootElements;
68 
69         // class naming validation
70         {
71             Set<String> nameSet = new HashSet<>();
72             nameSet.add("XmlParser");
73             for (XsdType type : xmlSchema.getTypeMap().values()) {
74                 if ((type instanceof XsdComplexType) || (type instanceof XsdRestriction &&
75                         ((XsdRestriction)type).getEnums() != null)) {
76                     String name = Utils.toClassName(type.getName());
77                     if (nameSet.contains(name)) {
78                         throw new CppCodeGeneratorException(
79                                 String.format("duplicate class name : %s", name));
80                     }
81                     nameSet.add(name);
82                     if (type instanceof XsdComplexType && !hasAttr) {
83                         hasAttr = hasAttribute((XsdComplexType)type);
84                     }
85                 }
86             }
87             for (XsdElement element : xmlSchema.getElementMap().values()) {
88                 XsdType type = element.getType();
89                 if (type.getRef() == null && type instanceof XsdComplexType) {
90                     String name = Utils.toClassName(element.getName());
91                     if (nameSet.contains(name)) {
92                         throw new CppCodeGeneratorException(
93                                 String.format("duplicate class name : %s", name));
94                     }
95                     nameSet.add(name);
96                     if (!hasAttr) {
97                         hasAttr = hasAttribute((XsdComplexType)type);
98                     }
99                 }
100             }
101         }
102 
103         cppSimpleTypeMap = new HashMap<>();
104         for (XsdType type : xmlSchema.getTypeMap().values()) {
105             if (type instanceof XsdSimpleType) {
106                 XsdType refType = new XsdType(null, new QName(type.getName()));
107                 parseSimpleType(refType, true);
108             }
109         }
110     }
111 
print(FileSystem fs)112     public void print(FileSystem fs)
113             throws CppCodeGeneratorException, IOException {
114         // cpp file, header file init
115         String fileNameStem = pkgName.replace('.', '_');
116         String enumsCppFileName = fileNameStem + "_enums.cpp";
117         String enumsHeaderFileName = fileNameStem + "_enums.h";
118         String parserCppFileName = fileNameStem + ".cpp";
119         String parserHeaderFileName = fileNameStem + ".h";
120         if ((this.generators & GENERATE_ENUMS) == GENERATE_ENUMS) {
121             enumsCppFile = new CodeWriter(fs.getPrintWriter(enumsCppFileName));
122             enumsHeaderFile = new CodeWriter(fs.getPrintWriter("include/" + enumsHeaderFileName));
123         } else {
124             enumsCppFile = new CodeWriter();
125             enumsHeaderFile = new CodeWriter();
126         }
127         if ((this.generators & GENERATE_PARSER) == GENERATE_PARSER) {
128             parserCppFile = new CodeWriter(fs.getPrintWriter(parserCppFileName));
129             parserHeaderFile = new CodeWriter(fs.getPrintWriter("include/" + parserHeaderFileName));
130         } else {
131             parserCppFile = new CodeWriter();
132             parserHeaderFile = new CodeWriter();
133         }
134 
135         boolean hasEnums = false;
136         for (XsdType type : xmlSchema.getTypeMap().values()) {
137             if (type instanceof XsdRestriction &&
138                   ((XsdRestriction)type).getEnums() != null) {
139                 hasEnums = true;
140                 break;
141             }
142         }
143 
144         String enumsHeaderMacro = enumsHeaderFileName.toUpperCase().replace('.', '_');
145         String parserHeaderMacro = parserHeaderFileName.toUpperCase().replace('.', '_');
146         enumsHeaderFile.printf("#ifndef %s\n", enumsHeaderMacro);
147         enumsHeaderFile.printf("#define %s\n", enumsHeaderMacro);
148         enumsHeaderFile.printf("\n");
149         enumsHeaderFile.printf("#include <array>\n");
150         enumsHeaderFile.printf("#include <string>\n");
151         enumsHeaderFile.printf("\n");
152         parserHeaderFile.printf("#ifndef %s\n", parserHeaderMacro);
153         parserHeaderFile.printf("#define %s\n", parserHeaderMacro);
154         parserHeaderFile.printf("\n");
155         parserHeaderFile.printf("#include <array>\n");
156         parserHeaderFile.printf("#include <map>\n");
157         parserHeaderFile.printf("#include <optional>\n");
158         parserHeaderFile.printf("#include <string>\n");
159         parserHeaderFile.printf("#include <vector>\n");
160         parserHeaderFile.printf("#include <sstream>\n");
161         if (writer) {
162             parserHeaderFile.printf("#include <iostream>\n");
163         }
164         parserHeaderFile.printf("\n");
165         if (useTinyXml) {
166             printGuardedIncludes("libtinyxml2", "tinyxml2.h");
167         } else {
168             printGuardedIncludes("libxml2", "libxml/parser.h",
169                     Arrays.asList("libxml/xinclude.h"));
170         }
171         if (hasEnums) {
172             enumsHeaderFile.printf("#include <xsdc/XsdcSupport.h>\n");
173             enumsHeaderFile.printf("\n");
174         }
175         parserHeaderFile.printf("\n");
176         parserHeaderFile.printf("#include \"%s\"\n", enumsHeaderFileName);
177         parserHeaderFile.printf("\n");
178 
179         enumsCppFile.printf("#include <map>\n");
180         enumsCppFile.printf("\n");
181         enumsCppFile.printf("#include \"%s\"\n\n", enumsHeaderFileName);
182         parserCppFile.printf("#define LOG_TAG \"%s\"\n", pkgName);
183         parserCppFile.printf("#include \"%s\"\n\n", parserHeaderFileName);
184         // define _xsdc_assert to abort with message regardless of NDEBUG
185         parserCppFile.println("#include <assert.h>\n"
186                 + "#ifndef __BIONIC__\n"
187                 + "#define __assert2(f,n,fun,e) do { fprintf(stderr, \"%s:%d: %s: Assertion `%s'"
188                 + " failed\", (f), (n), (fun), (e)); abort(); } while (false)\n"
189                 + "#endif\n"
190                 + "#define _xsdc_assert(e) do if (!(e)) __assert2(__FILE__, __LINE__,"
191                 + " __PRETTY_FUNCTION__, #e); while (false)\n");
192 
193         List<String> namespace = new java.util.ArrayList<>();
194         for (String token : pkgName.split("\\.")) {
195             if (token.isEmpty()) {
196                 continue;
197             }
198             if (Character.isDigit(token.charAt(0))) {
199                 token = "_" + token;
200             }
201             namespace.add(token);
202             enumsHeaderFile.printf("namespace %s {\n", token);
203             enumsCppFile.printf("namespace %s {\n", token);
204             parserHeaderFile.printf("namespace %s {\n", token);
205             parserCppFile.printf("namespace %s {\n", token);
206         }
207 
208         printPrototype();
209         printXmlParser();
210         if (writer) {
211             printXmlWriter();
212         }
213 
214         for (XsdType type : xmlSchema.getTypeMap().values()) {
215             if (type instanceof XsdRestriction &&
216                   ((XsdRestriction)type).getEnums() != null) {
217                 String name = Utils.toClassName(type.getName());
218                 XsdRestriction restrictionType = (XsdRestriction) type;
219                 printEnum(name, restrictionType);
220             }
221         }
222         for (XsdType type : xmlSchema.getTypeMap().values()) {
223             if (type instanceof XsdComplexType) {
224                 String name = Utils.toClassName(type.getName());
225                 XsdComplexType complexType = (XsdComplexType) type;
226                 printClass(name, "", complexType);
227             }
228         }
229         for (XsdElement element : xmlSchema.getElementMap().values()) {
230             XsdType type = element.getType();
231             if (type.getRef() == null && type instanceof XsdComplexType) {
232                 String name = Utils.toClassName(element.getName());
233                 XsdComplexType complexType = (XsdComplexType) type;
234                 printClass(name, "", complexType);
235             }
236         }
237 
238         Collections.reverse(namespace);
239         for (String token : namespace) {
240             enumsHeaderFile.printf("} // %s\n", token);
241             enumsCppFile.printf("} // %s\n", token);
242             parserHeaderFile.printf("} // %s\n", token);
243             parserCppFile.printf("} // %s\n", token);
244         }
245 
246         if (hasEnums) {
247             enumsHeaderFile.printf("\n//\n// global type declarations for package\n//\n\n");
248             enumsHeaderFile.printf("namespace android {\nnamespace details {\n");
249             Collections.reverse(namespace);
250             for (XsdType type : xmlSchema.getTypeMap().values()) {
251                 if (type instanceof XsdRestriction &&
252                         ((XsdRestriction)type).getEnums() != null) {
253                     String name = Utils.toClassName(type.getName());
254                     XsdRestriction restrictionType = (XsdRestriction) type;
255                     printEnumValues(namespace, name, restrictionType);
256                 }
257             }
258             enumsHeaderFile.printf("}  // namespace details\n}  // namespace android\n\n");
259         }
260 
261         parserHeaderFile.printf("#endif // %s\n", parserHeaderMacro);
262         enumsHeaderFile.printf("#endif // %s\n", enumsHeaderMacro);
263         parserCppFile.close();
264         parserHeaderFile.close();
265         enumsCppFile.close();
266         enumsHeaderFile.close();
267     }
268 
printGuardedIncludes(String libName, String mainHeader)269     private void printGuardedIncludes(String libName, String mainHeader) {
270         printGuardedIncludes(libName, mainHeader, Collections.emptyList());
271     }
272 
printGuardedIncludes(String libName, String mainHeader, Collection<String> additionalHeaders)273     private void printGuardedIncludes(String libName, String mainHeader,
274             Collection<String> additionalHeaders) {
275         parserHeaderFile.printf("#if __has_include(<%s>)\n", mainHeader);
276         parserHeaderFile.printf("#include <%s>\n", mainHeader);
277         for (String header : additionalHeaders) {
278             parserHeaderFile.printf("#include <%s>\n", header);
279         }
280         parserHeaderFile.printf("#else\n");
281         parserHeaderFile.printf("#error Require %s library. ", libName);
282         parserHeaderFile.printf("Please add %s to shared_libs or static_libs\n", libName);
283         parserHeaderFile.printf("#endif\n");
284     }
285 
printEnum(String name, XsdRestriction restrictionType)286     private void printEnum(String name, XsdRestriction restrictionType)
287             throws CppCodeGeneratorException {
288         enumsHeaderFile.printf("enum class %s {\n", name);
289         enumsCppFile.printf("const std::map<std::string, %s> %sString {\n", name, name);
290         List<XsdEnumeration> enums = restrictionType.getEnums();
291 
292         enumsHeaderFile.printf("%s = %d,\n", UNKNOWN_ENUM, -1);
293         for (XsdEnumeration tag : enums) {
294             String value = tag.getValue();
295             enumsHeaderFile.printf("%s,\n", Utils.toEnumName(value));
296             enumsCppFile.printf("{ \"%s\", %s::%s },\n", tag.getValue(), name,
297                     Utils.toEnumName(value));
298         }
299         enumsHeaderFile.printf("};\n");
300         enumsCppFile.printf("};\n\n");
301 
302         enumsHeaderFile.printf("%s stringTo%s(const std::string& value);\n",
303                 name, name);
304         enumsCppFile.printf("%s stringTo%s(const std::string& value) {\n"
305                 + "auto enumValue = %sString.find(value);\n"
306                 + "return enumValue != %sString.end() ? enumValue->second : %s::%s;\n"
307                 + "}\n\n", name, name, name, name, name, UNKNOWN_ENUM);
308 
309         enumsHeaderFile.printf("std::string toString(%s o);\n\n", name);
310         enumsCppFile.printf("std::string toString(%s o) {\n", name);
311         enumsCppFile.printf("switch (o) {\n");
312         for (XsdEnumeration tag : enums) {
313             String value = tag.getValue();
314             enumsCppFile.printf("case %s::%s: return \"%s\";\n",
315                     name, Utils.toEnumName(value), tag.getValue());
316         }
317         enumsCppFile.printf("default: return std::to_string(static_cast<int>(o));\n}\n");
318         enumsCppFile.printf("}\n\n");
319     }
320 
printEnumValues(List<String> namespace, String name, XsdRestriction restrictionType)321     private void printEnumValues(List<String> namespace, String name,
322             XsdRestriction restrictionType) throws CppCodeGeneratorException {
323         List<XsdEnumeration> enums = restrictionType.getEnums();
324         String absoluteNamespace = "::" + String.join("::", namespace);
325         enumsHeaderFile.printf("template<> inline constexpr std::array<%s::%s, %d> "
326                 + "xsdc_enum_values<%s::%s> = {\n",
327                 absoluteNamespace, name, enums.size(), absoluteNamespace, name);
328         for (XsdEnumeration tag : enums) {
329             String value = tag.getValue();
330             enumsHeaderFile.printf("%s::%s::%s,\n",
331                     absoluteNamespace, name, Utils.toEnumName(value));
332         }
333         enumsHeaderFile.printf("};\n");
334     }
335 
336     /**
337      * Prints forward declarations for complex types.
338      *
339      * Foo.h:
340      *
341      *     class Foo;
342      *
343      */
printPrototype()344     private void printPrototype() throws CppCodeGeneratorException {
345         for (XsdType type : xmlSchema.getTypeMap().values()) {
346             if (type instanceof XsdComplexType) {
347                 String name = Utils.toClassName(type.getName());
348                 parserHeaderFile.printf("class %s;\n", name);
349             }
350         }
351         for (XsdElement element : xmlSchema.getElementMap().values()) {
352             XsdType type = element.getType();
353             if (type.getRef() == null && type instanceof XsdComplexType) {
354                 String name = Utils.toClassName(element.getName());
355                 parserHeaderFile.printf("class %s;\n", name);
356             }
357         }
358     }
359 
360     /**
361      * Prints class definitions for complex types.
362      *
363      * Foo.h:
364      *
365      *     class Foo {
366      *       printClass(<inner types>)
367      *       <members>
368      *       const optional<> value_;
369      *       args = printConstructor()
370      *       printGetter(<members>)
371      *       printParser( args )
372      *       printWriter()
373      *     };
374      *
375      */
printClass(String name, String nameScope, XsdComplexType complexType)376     private void printClass(String name, String nameScope, XsdComplexType complexType)
377             throws CppCodeGeneratorException {
378         assert name != null;
379         // need element, attribute name duplicate validation?
380 
381         String baseName = getBaseName(complexType);
382         CppSimpleType valueType = (complexType instanceof XsdSimpleContent) ?
383                 getValueType((XsdSimpleContent) complexType, false) : null;
384 
385         parserHeaderFile.printf("class %s ", name);
386 
387         if (baseName != null) {
388             parserHeaderFile.printf(": public %s {\n", baseName);
389         } else {
390             parserHeaderFile.println("{");
391         }
392 
393         // parse types for elements and attributes
394         List<CppType> elementTypes = new ArrayList<>();
395         List<XsdElement> elements = new ArrayList<>();
396         elements.addAll(getAllElements(complexType.getGroup()));
397         elements.addAll(complexType.getElements());
398 
399         for (XsdElement element : elements) {
400             CppType cppType;
401             XsdElement elementValue = resolveElement(element);
402             if (element.getRef() == null && element.getType().getRef() == null
403                     && element.getType() instanceof XsdComplexType) {
404                 // print inner class for anonymous types
405                 parserHeaderFile.printf("public:\n");
406                 String innerName = Utils.toClassName(getElementName(element));
407                 XsdComplexType innerType = (XsdComplexType) element.getType();
408                 printClass(innerName, nameScope + name + "::", innerType);
409                 parserHeaderFile.println();
410                 cppType = new CppComplexType(nameScope + name + "::"+ innerName);
411             } else {
412                 cppType = parseType(elementValue.getType(), getElementName(elementValue));
413             }
414             elementTypes.add(cppType);
415         }
416         List<CppSimpleType> attributeTypes = new ArrayList<>();
417         List<XsdAttribute> attributes = new ArrayList<>();
418         for (XsdAttributeGroup attributeGroup : complexType.getAttributeGroups()) {
419             attributes.addAll(getAllAttributes(resolveAttributeGroup(attributeGroup)));
420         }
421         attributes.addAll(complexType.getAttributes());
422 
423         for (XsdAttribute attribute : attributes) {
424             XsdType type = resolveAttribute(attribute).getType();
425             attributeTypes.add(parseSimpleType(type, false));
426         }
427 
428         // print member variables
429 
430         parserHeaderFile.printf("private:\n");
431         for (int i = 0; i < elementTypes.size(); ++i) {
432             CppType type = elementTypes.get(i);
433             XsdElement element = elements.get(i);
434             XsdElement elementValue = resolveElement(element);
435             String typeName = Utils.elementTypeName(type.getName(),
436                     element.isMultiple() || type instanceof CppComplexType);
437             parserHeaderFile.printf("const %s %s_;\n", typeName,
438                     Utils.toVariableName(getElementName(elementValue)));
439         }
440         for (int i = 0; i < attributeTypes.size(); ++i) {
441             CppType type = attributeTypes.get(i);
442             XsdAttribute attribute = resolveAttribute(attributes.get(i));
443             String variableName = Utils.toVariableName(attribute.getName());
444             if (attribute.isRequired()) {
445                 parserHeaderFile.printf("const %s %s_;\n", type.getName(), variableName);
446             } else {
447                 parserHeaderFile.printf("const std::optional<%s> %s_;\n",
448                         type.getName(), variableName);
449             }
450         }
451         if (valueType != null) {
452             parserHeaderFile.printf("const std::optional<%s> value_;\n", valueType.getName());
453         }
454 
455         parserHeaderFile.printf("public:\n");
456         String constructorArgs = printConstructor(name, nameScope, complexType, elements,
457                 attributes, baseName);
458 
459         // print getters and setters
460         for (int i = 0; i < elementTypes.size(); ++i) {
461             CppType type = elementTypes.get(i);
462             XsdElement element = elements.get(i);
463             XsdElement elementValue = resolveElement(element);
464             printGetter(nameScope + name, type,
465                     Utils.toVariableName(getElementName(elementValue)),
466                     type instanceof CppComplexType ? true : element.isMultiple(),
467                     type instanceof CppComplexType ? false : ((CppSimpleType)type).isList(),
468                     false);
469         }
470         for (int i = 0; i < attributeTypes.size(); ++i) {
471             CppType type = attributeTypes.get(i);
472             XsdAttribute attribute = resolveAttribute(attributes.get(i));
473             printGetter(nameScope + name, type, Utils.toVariableName(attribute.getName()),
474                     false, false, attribute.isRequired());
475         }
476         if (valueType != null) {
477             printGetter(nameScope + name, valueType, "value", false, false, false);
478         }
479 
480         printParser(name, nameScope, complexType, constructorArgs);
481 
482         if (writer) {
483             printWriter(name, nameScope, complexType);
484         }
485 
486         parserHeaderFile.println("};\n");
487     }
488 
489     /**
490      * Prints read() static member function for complex types.
491      * Note that read() is a non-validating parser.
492      *
493      * Foo.h:
494      *
495      *     static Foo read(XmlNode* root);
496      *
497      * Foo.cpp:
498      *
499      *     Foo Foo::read(XmlNode* root) {
500      *        string _raw;
501      *        for each member m
502      *           _raw = ..
503      *           parsing expressions for each member (read _raw, set _value)
504      *           m = _value;
505      *        Foo instance(args...);
506      *        return instance;
507      *     }
508      *
509      */
printParser(String name, String nameScope, XsdComplexType complexType, String args)510     private void printParser(String name, String nameScope, XsdComplexType complexType, String args)
511             throws CppCodeGeneratorException {
512         CppSimpleType baseValueType = (complexType instanceof XsdSimpleContent) ?
513                 getValueType((XsdSimpleContent) complexType, true) : null;
514         List<XsdElement> allElements = new ArrayList<>();
515         List<XsdAttribute> allAttributes = new ArrayList<>();
516         stackComponents(complexType, allElements, allAttributes);
517 
518         // parse types for elements and attributes
519         List<CppType> allElementTypes = new ArrayList<>();
520         for (XsdElement element : allElements) {
521             XsdElement elementValue = resolveElement(element);
522             CppType cppType = parseType(elementValue.getType(), elementValue.getName());
523             allElementTypes.add(cppType);
524         }
525         List<CppSimpleType> allAttributeTypes = new ArrayList<>();
526         for (XsdAttribute attribute : allAttributes) {
527             XsdType type = resolveAttribute(attribute).getType();
528             allAttributeTypes.add(parseSimpleType(type, false));
529         }
530 
531         String fullName = nameScope + name;
532         String nodeType = getXmlNodeType();
533         parserHeaderFile.printf("static %s read(%s *root);\n", fullName, nodeType);
534         parserCppFile.printf("\n%s %s::read(%s *root) {\n", fullName, fullName, nodeType);
535 
536         parserCppFile.print("std::string _raw;\n");
537 
538         for (int i = 0; i < allAttributes.size(); ++i) {
539             CppSimpleType type = allAttributeTypes.get(i);
540             XsdAttribute attribute = resolveAttribute(allAttributes.get(i));
541             String variableName = Utils.toVariableName(attribute.getName());
542             parserCppFile.printf("_raw = getXmlAttribute(root, \"%s\");\n", attribute.getName());
543             if (attribute.isRequired()) {
544                 if (type.isEnum()) {
545                     parserCppFile.printf("%s %s = %s::%s;\n",
546                             type.getName(), variableName, type.getName(), UNKNOWN_ENUM);
547                 } else {
548                     parserCppFile.printf("%s %s{};\n", type.getName(), variableName);
549                 }
550             } else {
551                 parserCppFile.printf("std::optional<%s> %s = std::nullopt;\n", type.getName(),
552                         variableName);
553             }
554             parserCppFile.printf("if (_raw != \"\") {\n");
555             parserCppFile.print(type.getParsingExpression());
556             parserCppFile.printf("%s = _value;\n}\n", variableName);
557         }
558 
559         if (!allElements.isEmpty()) {
560             for (int i = 0; i < allElements.size(); ++i) {
561                 CppType type = allElementTypes.get(i);
562                 XsdElement element = allElements.get(i);
563                 XsdElement elementValue = resolveElement(element);
564                 String variableName = Utils.toVariableName(getElementName(elementValue));
565                 parserCppFile.printf("%s %s;\n", Utils.elementTypeName(type.getName(),
566                         element.isMultiple() || type instanceof CppComplexType), variableName);
567             }
568             if (useTinyXml) {
569                 parserCppFile.print("for (auto *_child = root->FirstChildElement();"
570                         + " _child != nullptr;"
571                         + " _child = _child->NextSiblingElement()) {\n");
572             } else {
573                 parserCppFile.print("for (auto *_child = root->xmlChildrenNode;"
574                         + " _child != nullptr;"
575                         + " _child = _child->next) {\n");
576             }
577             for (int i = 0; i < allElements.size(); ++i) {
578                 CppType type = allElementTypes.get(i);
579                 XsdElement element = allElements.get(i);
580                 XsdElement elementValue = resolveElement(element);
581                 String variableName = Utils.toVariableName(getElementName(elementValue));
582 
583                 if (i != 0) parserCppFile.printf("} else ");
584                 if (useTinyXml) {
585                     parserCppFile.printf("if (!strcmp(_child->Name(), \"%s\")) {\n",
586                             elementValue.getName());
587                 } else {
588                     parserCppFile.printf("if (!xmlStrcmp(_child->name,"
589                             + " reinterpret_cast<const xmlChar*>(\"%s\"))) {\n",
590                             elementValue.getName());
591                 }
592 
593                 if (type instanceof CppSimpleType) {
594                     printSetRawWithElementText("_child");
595                 }
596 
597                 parserCppFile.print(type.getParsingExpression());
598 
599                 if (element.isMultiple() || type instanceof CppComplexType) {
600                     parserCppFile.printf("%s.push_back(std::move(_value));\n", variableName);
601                 } else {
602                     parserCppFile.printf("%s = std::move(_value);\n", variableName);
603                 }
604             }
605             parserCppFile.printf("}\n}\n");
606         }
607         if (baseValueType != null) {
608             printSetRawWithElementText("root");
609             parserCppFile.print(baseValueType.getParsingExpression());
610         }
611         parserCppFile.printf("%s instance%s;\n",
612                 fullName, args.length() > 0 ? "(" + args + ")" : "");
613         parserCppFile.print("return instance;\n}\n");
614     }
615 
getXmlNodeType()616     private String getXmlNodeType() {
617         return (useTinyXml ? "tinyxml2::XMLElement" : "xmlNode");
618     }
619 
printSetRawWithElementText(String varName)620     private void printSetRawWithElementText(String varName) {
621         if (useTinyXml) {
622             // The tinyxml version, in contrast to xmlNodeListGetString does not deal
623             // with ENTITY_REF nodes
624             parserCppFile.printf("_raw = \"\";\n");
625             parserCppFile.printf("for (auto *textNode = %s->FirstChild();"
626                     + " textNode != nullptr;"
627                     + " textNode = textNode->NextSibling()) {\n", varName);
628             parserCppFile.printf("if (textNode->ToText() != nullptr) {\n");
629             parserCppFile.printf("_raw.append(textNode->Value());\n");
630             parserCppFile.printf("}\n");
631             parserCppFile.printf("}\n");
632         } else {
633             parserCppFile.printf("auto xmlValue = make_xmlUnique(xmlNodeListGetString(");
634             parserCppFile.printf("%s->doc, %s->xmlChildrenNode, 1));\n", varName, varName);
635             parserCppFile.printf("if (xmlValue == nullptr) {\n_raw = \"\";\n} else {\n");
636             parserCppFile.printf("_raw = reinterpret_cast<const char*>(xmlValue.get());\n}");
637             parserCppFile.printf("\n");
638         }
639     }
640 
641     /**
642      * Prints write() member function for complex types.
643      *
644      * Foo.h:
645      *
646      *     void write(ostream& _out, string name) const;
647      *
648      * Foo.cpp:
649      *
650      *     void Foo::write(ostream& _out, string name) const {
651      *        <FooElement attrs....>
652      *          value_
653      *        </Fooelement>
654      *     }
655      *
656      */
printWriter(String name, String nameScope, XsdComplexType complexType)657     private void printWriter(String name, String nameScope, XsdComplexType complexType)
658             throws CppCodeGeneratorException {
659         List<XsdElement> allElements = new ArrayList<>();
660         List<XsdAttribute> allAttributes = new ArrayList<>();
661         stackComponents(complexType, allElements, allAttributes);
662 
663         // parse types for elements and attributes
664         List<CppType> allElementTypes = new ArrayList<>();
665         for (XsdElement element : allElements) {
666             XsdElement elementValue = resolveElement(element);
667             CppType cppType = parseType(elementValue.getType(), elementValue.getName());
668             allElementTypes.add(cppType);
669         }
670         List<CppSimpleType> allAttributeTypes = new ArrayList<>();
671         for (XsdAttribute attribute : allAttributes) {
672             XsdType type = resolveAttribute(attribute).getType();
673             allAttributeTypes.add(parseSimpleType(type, false));
674         }
675 
676         String fullName = nameScope + name;
677         parserHeaderFile.printf("void write(std::ostream& _out, const std::string& _name) const;\n");
678         parserCppFile.printf(
679                 "\nvoid %s::write(std::ostream& _out, const std::string& _name) const {\n",
680                 fullName);
681 
682         parserCppFile.printf("_out << printIndent() << \"<\" << _name;\n");
683         for (int i = 0; i < allAttributes.size(); ++i) {
684             CppType type = allAttributeTypes.get(i);
685             XsdAttribute attribute = resolveAttribute(allAttributes.get(i));
686             String variableName = Utils.toVariableName(attribute.getName());
687             parserCppFile.printf("if (has%s()) {\n", Utils.capitalize(variableName));
688             parserCppFile.printf("_out << \" %s=\\\"\";\n", attribute.getName());
689             parserCppFile.print(type.getWritingExpression(String.format("%s%s()",
690                     getterName(type.getName()), Utils.capitalize(variableName)),
691                     attribute.getName()));
692             parserCppFile.printf("_out << \"\\\"\";\n}\n");
693         }
694         parserCppFile.print("_out << \">\" << std::endl;\n");
695         parserCppFile.print("++indentIndex;\n");
696 
697         if (!allElements.isEmpty()) {
698             for (int i = 0; i < allElements.size(); ++i) {
699                 CppType type = allElementTypes.get(i);
700                 XsdElement element = allElements.get(i);
701                 XsdElement elementValue = resolveElement(element);
702                 String elementName = getElementName(elementValue);
703                 String variableName = Utils.toVariableName(elementName);
704 
705                 if (type instanceof CppComplexType || element.isMultiple()) {
706                     parserCppFile.printf("for (auto& _value : get%s()) {\n",
707                             Utils.capitalize(variableName));
708                     if (type instanceof CppSimpleType) {
709                         parserCppFile.printf("_out << printIndent() << \"<%s>\";\n",
710                                 elementValue.getName());
711                     }
712                     parserCppFile.printf(
713                             type.getWritingExpression("_value", elementValue.getName()));
714                     if (type instanceof CppSimpleType) {
715                         parserCppFile.printf("_out << \"</%s>\" << std::endl;\n",
716                                 elementValue.getName());
717                     }
718                     parserCppFile.printf("}\n");
719                 } else {
720                     parserCppFile.printf("if (has%s()) {\n", Utils.capitalize(variableName));
721                     if (type instanceof CppSimpleType) {
722                         parserCppFile.printf("_out << printIndent() << \"<%s>\";\n",
723                                 elementValue.getName());
724                     }
725                     parserCppFile.print(type.getWritingExpression(String.format("%s%s()",
726                               getterName(type.getName()), Utils.capitalize(variableName)),
727                               elementValue.getName()));
728                     if (type instanceof CppSimpleType) {
729                         parserCppFile.printf("_out << \"</%s>\" << std::endl;\n",
730                                 elementValue.getName());
731                     }
732                     parserCppFile.print("}\n");
733                 }
734             }
735         }
736         parserCppFile.print("--indentIndex;\n");
737         parserCppFile.printf("_out << printIndent() << \"</\" << _name << \">\" << std::endl;\n");
738         parserCppFile.printf("}\n");
739     }
740 
741     /**
742      * Prints hasAttr() and getAttr() member functions for each member field.
743      *
744      * Foo.h:
745      *
746      *     const Attr& getAttr() const;
747      *     bool hasAttr() const;
748      *     const Item* getFirstItem() const; // for multi-value member
749      *
750      * Foo.cpp:
751      *
752      *     const Attr& Foo::getAttr() const {
753      *       return attr_;
754      *     }
755      *     bool Foo::hasAttr() const {
756      *       return true;
757      *     }
758      *     const Item* Foo::getFirstItem() const {
759      *       if (item_.empty()) {
760      *          return nullptr;
761      *       }
762      *       return &item_[0];
763      *     }
764      *
765      */
printGetter(String name, CppType type, String variableName, boolean isMultiple, boolean isMultipleType, boolean isRequired)766     private void printGetter(String name, CppType type, String variableName,
767             boolean isMultiple, boolean isMultipleType, boolean isRequired) {
768         String typeName = isMultiple ? String.format("std::vector<%s>",
769                 type.getName()) : type.getName();
770         String assertHasValue = String.format("_xsdc_assert(has%s());\n",
771                 Utils.capitalize(variableName));
772 
773         parserHeaderFile.printf("const %s& %s%s() const;\n", typeName, getterName(typeName),
774                 Utils.capitalize(variableName));
775 
776         parserCppFile.println();
777         parserCppFile.printf("const %s& %s::%s%s() const {\n", typeName, name,
778                 getterName(typeName), Utils.capitalize(variableName));
779         if (isMultiple || isRequired) {
780             parserCppFile.printf("return %s_;\n", variableName);
781         } else {
782             // Before accessing an optional::value(), we need to ensure that it has a value.
783             parserCppFile.print(assertHasValue);
784             parserCppFile.printf("return %s_.value();\n", variableName);
785         }
786         parserCppFile.printf("}\n\n");
787 
788         parserHeaderFile.printf("bool has%s() const;\n", Utils.capitalize(variableName));
789         parserCppFile.printf("bool %s::has%s() const {\n", name, Utils.capitalize(variableName));
790         if (isMultiple) {
791             parserCppFile.printf("return !(%s_.empty());\n}\n", variableName);
792         } else if (isRequired){
793             parserCppFile.print("return true;\n}\n");
794         } else {
795             parserCppFile.printf("return %s_.has_value();\n}\n", variableName);
796         }
797 
798         // For elements that may occur multiple types or have a list of simple types
799         if (isMultiple || isMultipleType) {
800             String elementTypeName = type instanceof CppComplexType ? type.getName() :
801                     ((CppSimpleType)type).getTypeName();
802             if (elementTypeName.equals("bool")) {
803                 parserHeaderFile.printf("%s getFirst%s() const;\n",
804                         elementTypeName, Utils.capitalize(variableName));
805                 parserCppFile.println();
806                 parserCppFile.printf("%s %s::getFirst%s() const {\n"
807                         + "%s"
808                         + "if (%s_%sempty()) {\n"
809                         + "return false;\n"
810                         + "}\n"
811                         + "return %s;\n"
812                         + "}\n",
813                         elementTypeName, name, Utils.capitalize(variableName),
814                         isMultiple ? "" : assertHasValue,
815                         variableName,
816                         isMultiple ? "." : "->",
817                         isMultiple ? String.format("%s_[0]", variableName) :
818                         String.format("%s_.value()[0]", variableName));
819             } else {
820                 parserHeaderFile.printf("const %s* getFirst%s() const;\n",
821                         elementTypeName, Utils.capitalize(variableName));
822                 parserCppFile.println();
823                 parserCppFile.printf("const %s* %s::getFirst%s() const {\n"
824                         + "%s"
825                         + "if (%s_%sempty()) {\n"
826                         + "return nullptr;\n"
827                         + "}\n"
828                         + "return &%s;\n"
829                         + "}\n",
830                         elementTypeName, name, Utils.capitalize(variableName),
831                         isMultiple ? "" : assertHasValue,
832                         variableName,
833                         isMultiple ? "." : "->",
834                         isMultiple ? String.format("%s_[0]", variableName) :
835                         String.format("%s_.value()[0]", variableName));
836             }
837         }
838     }
839 
840     /**
841      * Prints constructor for complex types
842      *
843      * Foo.h:
844      *
845      *     Foo(args...);
846      *
847      * Foo.cpp:
848      *
849      *     Foo::Foo(args...): initializer... {}
850      *
851      */
printConstructor(String name, String nameScope, XsdComplexType complexType, List<XsdElement> elements, List<XsdAttribute> attributes, String baseName)852     private String printConstructor(String name, String nameScope, XsdComplexType complexType,
853             List<XsdElement> elements, List<XsdAttribute> attributes, String baseName)
854             throws CppCodeGeneratorException {
855         String fullName = nameScope + name;
856         StringBuilder constructorArgs = new StringBuilder();
857         StringBuilder parentArgs = new StringBuilder();
858         StringBuilder constructor = new StringBuilder();
859         StringBuilder args = new StringBuilder();
860 
861         List<XsdElement> allElements = new ArrayList<>();
862         List<XsdAttribute> allAttributes = new ArrayList<>();
863         stackComponents(complexType, allElements, allAttributes);
864 
865         for (XsdElement element : allElements) {
866             XsdElement elementValue = resolveElement(element);
867             CppType type = parseType(elementValue.getType(), elementValue.getName());
868             String variableName = Utils.toVariableName(getElementName(elementValue));
869             constructorArgs.append(String.format(", %s %s", Utils.elementTypeName(type.getName(),
870                     element.isMultiple() || type instanceof CppComplexType), variableName));
871             args.append(String.format(", %s", variableName));
872             boolean isMultipleType;
873             if (type instanceof CppComplexType) {
874                 isMultipleType = true;
875             } else if (((CppSimpleType)type).isList()) {
876                 isMultipleType = true;
877             } else {
878                 isMultipleType = false;
879             }
880 
881             if (elements.contains(element)) {
882                 constructor.append(String.format(", %s_(%s)", variableName,
883                         Utils.toAssignmentName(type.getName(), variableName, isMultipleType)));
884             } else {
885                 parentArgs.append(String.format(", %s", variableName));
886             }
887         }
888         for (XsdAttribute attribute : allAttributes) {
889             CppType type = parseSimpleType(resolveAttribute(attribute).getType(), false);
890             String variableName = Utils.toVariableName(resolveAttribute(attribute).getName());
891             if (attribute.isRequired()) {
892                 constructorArgs.append(String.format(", %s %s", type.getName(), variableName));
893             } else {
894                 constructorArgs.append(String.format(", std::optional<%s> %s", type.getName(),
895                         variableName));
896             }
897             args.append(String.format(", %s", variableName));
898             boolean isMultipleType = ((CppSimpleType)type).isList() ? true : false;
899             if (attributes.contains(attribute)) {
900                 constructor.append(String.format(", %s_(%s)", variableName,
901                         Utils.toAssignmentName(type.getName(), variableName, isMultipleType)));
902             } else {
903                 parentArgs.append(String.format(", %s", variableName));
904             }
905         }
906 
907         CppSimpleType valueType = (complexType instanceof XsdSimpleContent) ?
908                 getValueType((XsdSimpleContent) complexType, false) : null;
909         if (valueType != null) {
910             constructorArgs.append(String.format(", %s %s", valueType.getName(), "value"));
911             constructor.append(String.format(", %s_(%s)", "value", "value"));
912             // getParsingExpression prepends with underscore, so set args for instantiation
913             args.append(String.format(", %s", "_value"));
914         }
915 
916         String constructorArgsString = constructorArgs.toString();
917         String constructorString = constructor.toString();
918         if (constructorArgsString.length() > 0) {
919             constructorArgsString = constructorArgsString.substring(2);
920         }
921 
922         boolean useExplicit =
923                 !(constructorArgsString.isEmpty() || constructorArgsString.contains(","));
924         if (useExplicit) {
925             parserHeaderFile.printf("explicit %s(%s);\n", name, constructorArgsString);
926         } else {
927             parserHeaderFile.printf("%s(%s);\n", name, constructorArgsString);
928         }
929         parserCppFile.printf("\n%s::%s(%s) : ", fullName, name, constructorArgsString);
930 
931         String parentArgsString = parentArgs.toString();
932         if (parentArgsString.length() > 0) {
933             parentArgsString = parentArgsString.substring(2);
934             parserCppFile.printf("%s(%s)", baseName, parentArgsString);
935         } else {
936             constructorString = constructorString.substring(2);
937         }
938         parserCppFile.printf("%s {\n}\n", constructorString);
939 
940         String argsString = args.toString();
941         if (argsString.length() > 0) {
942             argsString = argsString.substring(2);
943         }
944         return argsString;
945     }
946 
947     /**
948      * Prints reader functions for each top-level types.
949      *
950      * Foo.h:
951      *
952      *     optional<Foo> readFoo(const char* filename);
953      *
954      * Foo.cpp:
955      *
956      *     std::optional<Foo> readFoo(const char* filename) {
957      *        ...
958      *        Foo _value = Foo::read(root);
959      *        return _value;
960      *     }
961      *
962      */
printXmlParser()963     private void printXmlParser() throws CppCodeGeneratorException {
964         if (useTinyXml) {
965             // Nothing to do for libtinyxml2
966         } else {
967             parserCppFile.printf("template <class T>\n"
968                     + "constexpr void (*xmlDeleter)(T* t);\n"
969                     + "template <>\nconstexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;\n"
970                     + "template <>\nauto xmlDeleter<xmlChar> = [](xmlChar *s) { xmlFree(s); };\n\n"
971                     + "template <class T>\n"
972                     + "constexpr auto make_xmlUnique(T *t) {\n"
973                     + "auto deleter = [](T *t) { xmlDeleter<T>(t); };\n"
974                     + "return std::unique_ptr<T, decltype(deleter)>{t, deleter};\n"
975                     + "}\n\n");
976         }
977 
978         if (hasAttr) {
979             parserCppFile.printf("static std::string getXmlAttribute"
980                     + "(const %s *cur, const char *attribute) {\n", getXmlNodeType());
981             if (useTinyXml) {
982                 parserCppFile.printf("auto attrValue = cur->Attribute(attribute);\n"
983                         + "if(attrValue == nullptr) {\n"
984                         + "return \"\";\n"
985                         + "}\n"
986                         + "return std::string(attrValue);\n");
987             } else {
988                 parserCppFile.printf("auto xmlValue = make_xmlUnique(xmlGetProp(cur, "
989                         + "reinterpret_cast<const xmlChar*>(attribute)));\n"
990                         + "if (xmlValue == nullptr) {\n"
991                         + "return \"\";\n"
992                         + "}\n"
993                         + "std::string value(reinterpret_cast<const char*>(xmlValue.get()));\n"
994                         + "return value;\n");
995             }
996             parserCppFile.printf("}\n\n");
997         }
998 
999         boolean isMultiRootElement = xmlSchema.getElementMap().values().size() > 1;
1000         for (XsdElement element : xmlSchema.getElementMap().values()) {
1001             // Skip parser if not specified as root.
1002             if (rootElements != null
1003                     && Arrays.asList(rootElements).indexOf(element.getName()) == -1) continue;
1004             printXmlParserFor(element, /*loadFile=*/true, isMultiRootElement);
1005             printXmlParserFor(element, /*loadFile=*/false, isMultiRootElement);
1006         }
1007     }
1008 
1009     /**
1010      * Prints readType(const char* configFile) if loadFile is true.
1011      * Otherwise, prints parseType(const char* xml).
1012      */
printXmlParserFor(XsdElement element, boolean loadFile, boolean isMultiRootElement)1013     private void printXmlParserFor(XsdElement element, boolean loadFile, boolean isMultiRootElement)
1014             throws CppCodeGeneratorException {
1015         CppType cppType = parseType(element.getType(), element.getName());
1016         String elementName = element.getName();
1017         String typeName = cppType.getName();
1018         String readerName =
1019                 cppType instanceof CppSimpleType ? Utils.toClassName(elementName) : typeName;
1020         String methodName = loadFile ? "read" : "parse";
1021         String argName = loadFile ? "configFile" : "xml";
1022         parserHeaderFile.printf("std::optional<%s> %s%s(const char* %s);\n\n",
1023                 typeName,
1024                 methodName,
1025                 isMultiRootElement ? readerName : "",
1026                 argName);
1027         parserCppFile.printf("std::optional<%s> %s%s(const char* %s) {\n",
1028                 typeName,
1029                 methodName,
1030                 isMultiRootElement ? readerName : "",
1031                 argName);
1032         if (useTinyXml) {
1033             String innerParser = loadFile ? "LoadFile(configFile)" : "Parse(xml)";
1034             parserCppFile.printf("tinyxml2::XMLDocument doc;\n"
1035                     + "if (doc.%s != tinyxml2::XML_SUCCESS) {\n"
1036                     + "return std::nullopt;\n"
1037                     + "}\n"
1038                     + "auto _child = doc.FirstChildElement();\n"
1039                     + "if (_child == nullptr) {\n"
1040                     + "return std::nullopt;\n"
1041                     + "}\n\n"
1042                     + "if (strcmp(_child->Name(), \"%s\") == 0) {\n",
1043                     innerParser,
1044                     elementName);
1045         } else {
1046             String innerParser = loadFile
1047                     ? "xmlParseFile(configFile)"
1048                     : "xmlParseDoc(reinterpret_cast<const xmlChar*>(xml))";
1049             parserCppFile.printf("auto doc = make_xmlUnique(%s);\n"
1050                     + "if (doc == nullptr) {\n"
1051                     + "return std::nullopt;\n"
1052                     + "}\n"
1053                     + "xmlNodePtr _child = xmlDocGetRootElement(doc.get());\n"
1054                     + "if (_child == nullptr) {\n"
1055                     + "return std::nullopt;\n"
1056                     + "}\n"
1057                     + "if (xmlXIncludeProcess(doc.get()) < 0) {\n"
1058                     + "return std::nullopt;\n"
1059                     + "}\n\n"
1060                     + "if (!xmlStrcmp(_child->name, reinterpret_cast<const xmlChar*>"
1061                     + "(\"%s\"))) {\n",
1062                     innerParser,
1063                     elementName);
1064         }
1065 
1066         if (cppType instanceof CppSimpleType) {
1067             parserCppFile.print("std::string _raw;\n");
1068             printSetRawWithElementText("_child");
1069         }
1070         parserCppFile.printf(cppType.getParsingExpression());
1071         parserCppFile.printf("return _value;\n}\n");
1072         parserCppFile.printf("return std::nullopt;\n");
1073         parserCppFile.printf("}\n\n");
1074     }
1075 
1076     /**
1077      * Prints writer functions for each top-level types
1078      *
1079      * Foo.h:
1080      *
1081      *     void write(ostream&, const Foo& foo);
1082      *
1083      * Foo.cpp:
1084      *
1085      *     void write(ostream& _out, const Foo& foo) {
1086      *        ... <?xml ... ?>
1087      *        foo.write(_out, "FooElementName");
1088      *     }
1089      *
1090      */
printXmlWriter()1091     private void printXmlWriter() throws CppCodeGeneratorException {
1092         for (XsdElement element : xmlSchema.getElementMap().values()) {
1093             // Skip writer if not specified as root.
1094             if (rootElements != null
1095                     && Arrays.asList(rootElements).indexOf(element.getName()) == -1) continue;
1096             CppType cppType = parseType(element.getType(), element.getName());
1097             String elementName = element.getName();
1098             String variableName = Utils.toVariableName(elementName);
1099             String typeName = cppType.getName();
1100             String writerName =
1101                     cppType instanceof CppSimpleType ? Utils.toClassName(elementName) : "";
1102             parserHeaderFile.printf("void write%s(std::ostream& _out, const %s& %s);\n\n",
1103                     writerName, typeName, variableName);
1104             parserCppFile.printf("void write%s(std::ostream& _out, const %s& %s) {\n",
1105                     writerName, typeName, variableName);
1106             parserCppFile.print(
1107                     "_out << \"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\";\n");
1108             if (cppType instanceof CppSimpleType) {
1109                 parserCppFile.printf("_out << \"<%s>\";\n", elementName);
1110                 parserCppFile.print(cppType.getWritingExpression(variableName, ""));
1111                 parserCppFile.printf("_out << \"</%s>\" << std::endl;\n", elementName);
1112             } else {
1113                 parserCppFile.printf("%s.write(_out, \"%s\");\n", variableName, elementName);
1114             }
1115             parserCppFile.printf("}\n\n");
1116         }
1117 
1118         parserCppFile.print("static int indentIndex = 0;\n"
1119                 + "std::string printIndent() {\n"
1120                 + "std::string s = \"\";\n"
1121                 + "for (int index = 0; index < indentIndex; ++index) {\n"
1122                 + "s += \"    \";\n"
1123                 + "}\nreturn s;\n}\n\n");
1124     }
1125 
getElementName(XsdElement element)1126     private String getElementName(XsdElement element) {
1127         if (element instanceof XsdChoice) {
1128             return element.getName() + "_optional";
1129         } else if (element instanceof XsdAll) {
1130             return element.getName() + "_all";
1131         }
1132         return element.getName();
1133     }
1134 
getterName(String type)1135     private String getterName(String type) {
1136         if (type.equals("bool") && booleanGetter) {
1137             return "is";
1138         }
1139         return "get";
1140     }
1141 
stackComponents(XsdComplexType complexType, List<XsdElement> elements, List<XsdAttribute> attributes)1142     private void stackComponents(XsdComplexType complexType, List<XsdElement> elements,
1143             List<XsdAttribute> attributes) throws CppCodeGeneratorException {
1144         if (complexType.getBase() != null) {
1145             QName baseRef = complexType.getBase().getRef();
1146             if (baseRef != null && !baseRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) {
1147                 XsdType parent = getType(baseRef.getLocalPart());
1148                 if (parent instanceof XsdComplexType) {
1149                     stackComponents((XsdComplexType) parent, elements, attributes);
1150                 }
1151             }
1152         }
1153         elements.addAll(getAllElements(complexType.getGroup()));
1154         elements.addAll(complexType.getElements());
1155         for (XsdAttributeGroup attributeGroup : complexType.getAttributeGroups()) {
1156             attributes.addAll(getAllAttributes(resolveAttributeGroup(attributeGroup)));
1157         }
1158         attributes.addAll(complexType.getAttributes());
1159     }
1160 
getAllAttributes(XsdAttributeGroup attributeGroup)1161     private List<XsdAttribute> getAllAttributes(XsdAttributeGroup attributeGroup)
1162             throws CppCodeGeneratorException{
1163         List<XsdAttribute> attributes = new ArrayList<>();
1164         for (XsdAttributeGroup attrGroup : attributeGroup.getAttributeGroups()) {
1165             attributes.addAll(getAllAttributes(resolveAttributeGroup(attrGroup)));
1166         }
1167         attributes.addAll(attributeGroup.getAttributes());
1168         return attributes;
1169     }
1170 
getAllElements(XsdGroup group)1171     private List<XsdElement> getAllElements(XsdGroup group) throws CppCodeGeneratorException {
1172         List<XsdElement> elements = new ArrayList<>();
1173         if (group == null) {
1174             return elements;
1175         }
1176         elements.addAll(getAllElements(resolveGroup(group)));
1177         elements.addAll(group.getElements());
1178         return elements;
1179     }
1180 
1181 
getBaseName(XsdComplexType complexType)1182     private String getBaseName(XsdComplexType complexType) throws CppCodeGeneratorException {
1183         if (complexType.getBase() == null) return null;
1184         if (complexType.getBase().getRef().getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) {
1185             return null;
1186         }
1187         XsdType base = getType(complexType.getBase().getRef().getLocalPart());
1188         if (base instanceof XsdComplexType) {
1189             return Utils.toClassName(base.getName());
1190         }
1191         return null;
1192     }
1193 
getValueType(XsdSimpleContent simpleContent, boolean traverse)1194     private CppSimpleType getValueType(XsdSimpleContent simpleContent, boolean traverse)
1195             throws CppCodeGeneratorException {
1196         assert simpleContent.getBase() != null;
1197         QName baseRef = simpleContent.getBase().getRef();
1198         assert baseRef != null;
1199         if (baseRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) {
1200             return predefinedType(baseRef.getLocalPart());
1201         } else {
1202             XsdType parent = getType(baseRef.getLocalPart());
1203             if (parent instanceof XsdSimpleType) {
1204                 return parseSimpleTypeReference(baseRef, false);
1205             }
1206             if (!traverse) return null;
1207             if (parent instanceof XsdSimpleContent) {
1208                 return getValueType((XsdSimpleContent) parent, true);
1209             } else {
1210                 throw new CppCodeGeneratorException(
1211                         String.format("base not simple : %s", baseRef.getLocalPart()));
1212             }
1213         }
1214     }
1215 
parseType(XsdType type, String defaultName)1216     private CppType parseType(XsdType type, String defaultName) throws CppCodeGeneratorException {
1217         if (type.getRef() != null) {
1218             String name = type.getRef().getLocalPart();
1219             if (type.getRef().getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) {
1220                 return predefinedType(name);
1221             } else {
1222                 XsdType typeValue = getType(name);
1223                 if (typeValue instanceof XsdSimpleType) {
1224                     return parseSimpleTypeReference(type.getRef(), false);
1225                 }
1226                 return parseType(typeValue, name);
1227             }
1228         }
1229         if (type instanceof XsdComplexType) {
1230             return new CppComplexType(Utils.toClassName(defaultName));
1231         } else if (type instanceof XsdSimpleType) {
1232             return parseSimpleTypeValue((XsdSimpleType) type, false);
1233         } else {
1234             throw new CppCodeGeneratorException(
1235                     String.format("unknown type name : %s", defaultName));
1236         }
1237     }
1238 
parseSimpleType(XsdType type, boolean traverse)1239     private CppSimpleType parseSimpleType(XsdType type, boolean traverse)
1240             throws CppCodeGeneratorException {
1241         if (type.getRef() != null) {
1242             return parseSimpleTypeReference(type.getRef(), traverse);
1243         } else {
1244             return parseSimpleTypeValue((XsdSimpleType) type, traverse);
1245         }
1246     }
1247 
parseSimpleTypeReference(QName typeRef, boolean traverse)1248     private CppSimpleType parseSimpleTypeReference(QName typeRef, boolean traverse)
1249             throws CppCodeGeneratorException {
1250         assert typeRef != null;
1251         String typeName = typeRef.getLocalPart();
1252         if (typeRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) {
1253             return predefinedType(typeName);
1254         }
1255         if (cppSimpleTypeMap.containsKey(typeName)) {
1256             return cppSimpleTypeMap.get(typeName);
1257         } else if (traverse) {
1258             XsdSimpleType simpleType = getSimpleType(typeName);
1259             CppSimpleType ret = parseSimpleTypeValue(simpleType, true);
1260             cppSimpleTypeMap.put(typeName, ret);
1261             return ret;
1262         } else {
1263             throw new CppCodeGeneratorException(String.format("unknown type name : %s", typeName));
1264         }
1265     }
1266 
parseSimpleTypeValue(XsdSimpleType simpleType, boolean traverse)1267     private CppSimpleType parseSimpleTypeValue(XsdSimpleType simpleType, boolean traverse)
1268             throws CppCodeGeneratorException {
1269         if (simpleType instanceof XsdList) {
1270             XsdList list = (XsdList) simpleType;
1271             return parseSimpleType(list.getItemType(), traverse).newListType();
1272         } else if (simpleType instanceof XsdRestriction) {
1273             // we don't consider any restrictions.
1274             XsdRestriction restriction = (XsdRestriction) simpleType;
1275             if (restriction.getEnums() != null) {
1276                 String name = Utils.toClassName(restriction.getName());
1277                 return new CppSimpleType(name, "stringTo" + name + "(%s)", false, true);
1278             }
1279             return parseSimpleType(restriction.getBase(), traverse);
1280         } else if (simpleType instanceof XsdUnion) {
1281             // unions are almost always interpreted as java.lang.String
1282             // Exceptionally, if any of member types of union are 'list', then we interpret it as
1283             // List<String>
1284             XsdUnion union = (XsdUnion) simpleType;
1285             for (XsdType memberType : union.getMemberTypes()) {
1286                 if (parseSimpleType(memberType, traverse).isList()) {
1287                     return new CppSimpleType("std::string", "%s", true);
1288                 }
1289             }
1290             return new CppSimpleType("std::string", "%s", false);
1291         } else {
1292             // unreachable
1293             throw new IllegalStateException("unknown simple type");
1294         }
1295     }
1296 
resolveElement(XsdElement element)1297     private XsdElement resolveElement(XsdElement element) throws CppCodeGeneratorException {
1298         if (element.getRef() == null) return element;
1299         String name = element.getRef().getLocalPart();
1300         XsdElement ret = xmlSchema.getElementMap().get(name);
1301         if (ret != null) return ret;
1302         throw new CppCodeGeneratorException(String.format("no element named : %s", name));
1303     }
1304 
resolveGroup(XsdGroup group)1305     private XsdGroup resolveGroup(XsdGroup group) throws CppCodeGeneratorException {
1306         if (group.getRef() == null) return null;
1307         String name = group.getRef().getLocalPart();
1308         XsdGroup ret = xmlSchema.getGroupMap().get(name);
1309         if (ret != null) return ret;
1310         throw new CppCodeGeneratorException(String.format("no group named : %s", name));
1311     }
1312 
resolveAttribute(XsdAttribute attribute)1313     private XsdAttribute resolveAttribute(XsdAttribute attribute)
1314             throws CppCodeGeneratorException {
1315         if (attribute.getRef() == null) return attribute;
1316         String name = attribute.getRef().getLocalPart();
1317         XsdAttribute ret = xmlSchema.getAttributeMap().get(name);
1318         if (ret != null) return ret;
1319         throw new CppCodeGeneratorException(String.format("no attribute named : %s", name));
1320     }
1321 
resolveAttributeGroup(XsdAttributeGroup attributeGroup)1322     private XsdAttributeGroup resolveAttributeGroup(XsdAttributeGroup attributeGroup)
1323             throws CppCodeGeneratorException {
1324         if (attributeGroup.getRef() == null) return attributeGroup;
1325         String name = attributeGroup.getRef().getLocalPart();
1326         XsdAttributeGroup ret = xmlSchema.getAttributeGroupMap().get(name);
1327         if (ret != null) return ret;
1328         throw new CppCodeGeneratorException(String.format("no attribute group named : %s", name));
1329     }
1330 
getType(String name)1331     private XsdType getType(String name) throws CppCodeGeneratorException {
1332         XsdType type = xmlSchema.getTypeMap().get(name);
1333         if (type != null) return type;
1334         throw new CppCodeGeneratorException(String.format("no type named : %s", name));
1335     }
1336 
getSimpleType(String name)1337     private XsdSimpleType getSimpleType(String name) throws CppCodeGeneratorException {
1338         XsdType type = getType(name);
1339         if (type instanceof XsdSimpleType) return (XsdSimpleType) type;
1340         throw new CppCodeGeneratorException(String.format("not a simple type : %s", name));
1341     }
1342 
hasAttribute(XsdComplexType complexType)1343     private boolean hasAttribute(XsdComplexType complexType) throws CppCodeGeneratorException {
1344         if (complexType.getAttributes().size() > 0 ||
1345                 complexType.getAttributeGroups().size() > 0) {
1346             return true;
1347         }
1348         boolean results = false;
1349         for (XsdElement element : complexType.getElements()) {
1350             if (element.getRef() == null && element.getType().getRef() == null
1351                     && element.getType() instanceof XsdComplexType) {
1352                 results = hasAttribute((XsdComplexType) element.getType());
1353                 if (results) {
1354                     return results;
1355                 }
1356             }
1357         }
1358         return results;
1359     }
1360 
predefinedType(String name)1361     private static CppSimpleType predefinedType(String name) throws CppCodeGeneratorException {
1362         switch (name) {
1363             case "string":
1364             case "token":
1365             case "normalizedString":
1366             case "language":
1367             case "ENTITY":
1368             case "ID":
1369             case "Name":
1370             case "NCName":
1371             case "NMTOKEN":
1372             case "anyURI":
1373             case "anyType":
1374             case "QName":
1375             case "NOTATION":
1376             case "IDREF":
1377                 return new CppSimpleType("std::string", "%s", false);
1378             case "ENTITIES":
1379             case "NMTOKENS":
1380             case "IDREFS":
1381                 return new CppSimpleType("std::string", "%s", true);
1382             case "date":
1383             case "dateTime":
1384             case "time":
1385             case "gDay":
1386             case "gMonth":
1387             case "gYear":
1388             case "gMonthDay":
1389             case "gYearMonth":
1390             case "duration":
1391                 return new CppSimpleType("std::string", "%s", false);
1392             case "decimal":
1393                 return new CppSimpleType("double", "std::stod(%s)", false);
1394             case "integer":
1395             case "negativeInteger":
1396             case "nonNegativeInteger":
1397             case "positiveInteger":
1398             case "nonPositiveInteger":
1399                 return new CppSimpleType("int64_t", "std::stoll(%s)", false);
1400             case "unsignedLong":
1401                 return new CppSimpleType("uint64_t", "std::stoull(%s)", false);
1402             case "long":
1403                 return new CppSimpleType("int64_t", "std::stoll(%s)", false);
1404             case "unsignedInt":
1405                 return new CppSimpleType("unsigned int",
1406                         "static_cast<unsigned int>(stoul(%s))", false);
1407             case "int":
1408                 return new CppSimpleType("int", "std::stoi(%s)", false);
1409             case "unsignedShort":
1410                 return new CppSimpleType("unsigned short",
1411                         "static_cast<unsigned short>(std::stoi(%s))", false);
1412             case "short":
1413                 return new CppSimpleType("short", "static_cast<short>(std::stoi(%s))", false);
1414             case "unsignedByte":
1415                 return new CppSimpleType("unsigned char",
1416                         "static_cast<unsigned char>(std::stoi(%s))", false);
1417             case "byte":
1418                 return new CppSimpleType("char", "static_cast<char>(std::stoi(%s))", false);
1419             case "boolean":
1420                 return new CppSimpleType("bool", "%s == \"true\"", false);
1421             case "double":
1422                 return new CppSimpleType("double", "std::stod(%s)", false);
1423             case "float":
1424                 return new CppSimpleType("float", "std::stof(%s)", false);
1425             case "base64Binary":
1426             case "hexBinary":
1427                 return new CppSimpleType("std::string", "%s", false);
1428         }
1429         throw new CppCodeGeneratorException("unknown xsd predefined type : " + name);
1430     }
1431 }
1432