• 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(parserHeaderFile, "libtinyxml2", "tinyxml2.h");
167         } else {
168             printGuardedIncludes(parserHeaderFile, "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(CodeWriter file, String libName, String mainHeader)269     private void printGuardedIncludes(CodeWriter file, String libName, String mainHeader) {
270         printGuardedIncludes(file, libName, mainHeader, Collections.emptyList());
271     }
272 
printGuardedIncludes(CodeWriter file, String libName, String mainHeader, Collection<String> additionalHeaders)273     private void printGuardedIncludes(CodeWriter file, 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 (baseValueType != null) {
560             printSetRawWithElementText("root");
561             parserCppFile.print(baseValueType.getParsingExpression());
562             parserCppFile.printf("instance.setValue(_value);\n");
563             parserCppFile.printf("}\n");
564         } else if (!allElements.isEmpty()) {
565             for (int i = 0; i < allElements.size(); ++i) {
566                 CppType type = allElementTypes.get(i);
567                 XsdElement element = allElements.get(i);
568                 XsdElement elementValue = resolveElement(element);
569                 String variableName = Utils.toVariableName(getElementName(elementValue));
570                 parserCppFile.printf("%s %s;\n", Utils.elementTypeName(type.getName(),
571                         element.isMultiple() || type instanceof CppComplexType), variableName);
572             }
573             if (useTinyXml) {
574                 parserCppFile.print("for (auto *_child = root->FirstChildElement();"
575                         + " _child != nullptr;"
576                         + " _child = _child->NextSiblingElement()) {\n");
577             } else {
578                 parserCppFile.print("for (auto *_child = root->xmlChildrenNode;"
579                         + " _child != nullptr;"
580                         + " _child = _child->next) {\n");
581             }
582             for (int i = 0; i < allElements.size(); ++i) {
583                 CppType type = allElementTypes.get(i);
584                 XsdElement element = allElements.get(i);
585                 XsdElement elementValue = resolveElement(element);
586                 String variableName = Utils.toVariableName(getElementName(elementValue));
587 
588                 if (i != 0) parserCppFile.printf("} else ");
589                 if (useTinyXml) {
590                     parserCppFile.printf("if (!strcmp(_child->Name(), \"%s\")) {\n",
591                             elementValue.getName());
592                 } else {
593                     parserCppFile.printf("if (!xmlStrcmp(_child->name,"
594                             + " reinterpret_cast<const xmlChar*>(\"%s\"))) {\n",
595                             elementValue.getName());
596                 }
597 
598                 if (type instanceof CppSimpleType) {
599                     printSetRawWithElementText("_child");
600                 }
601 
602                 parserCppFile.print(type.getParsingExpression());
603 
604                 if (element.isMultiple() || type instanceof CppComplexType) {
605                     parserCppFile.printf("%s.push_back(std::move(_value));\n", variableName);
606                 } else {
607                     parserCppFile.printf("%s = std::move(_value);\n", variableName);
608                 }
609             }
610             parserCppFile.printf("}\n}\n");
611         }
612         parserCppFile.printf("%s instance%s;\n",
613                 fullName, args.length() > 0 ? "(" + args + ")" : "");
614         parserCppFile.print("return instance;\n}\n");
615     }
616 
getXmlNodeType()617     private String getXmlNodeType() {
618         return (useTinyXml ? "tinyxml2::XMLElement" : "xmlNode");
619     }
620 
printSetRawWithElementText(String varName)621     private void printSetRawWithElementText(String varName) {
622         if (useTinyXml) {
623             // The tinyxml version, in contrast to xmlNodeListGetString does not deal
624             // with ENTITY_REF nodes
625             parserCppFile.printf("_raw = \"\";\n");
626             parserCppFile.printf("for (auto *textNode = %s->FirstChild();"
627                     + " textNode != nullptr;"
628                     + " textNode = textNode->NextSibling()) {\n", varName);
629             parserCppFile.printf("if (textNode->ToText() != nullptr) {\n");
630             parserCppFile.printf("_raw.append(textNode->Value());\n");
631             parserCppFile.printf("}\n");
632             parserCppFile.printf("}\n");
633         } else {
634             parserCppFile.printf("auto xmlValue = make_xmlUnique(xmlNodeListGetString(");
635             parserCppFile.printf("%s->doc, %s->xmlChildrenNode, 1));\n", varName, varName);
636             parserCppFile.printf("if (xmlValue == nullptr) {\n_raw = \"\";\n} else {\n");
637             parserCppFile.printf("_raw = reinterpret_cast<const char*>(xmlValue.get());\n}");
638             parserCppFile.printf("\n");
639         }
640     }
641 
642     /**
643      * Prints write() member function for complex types.
644      *
645      * Foo.h:
646      *
647      *     void write(ostream& _out, string name) const;
648      *
649      * Foo.cpp:
650      *
651      *     void Foo::write(ostream& _out, string name) const {
652      *        <FooElement attrs....>
653      *          value_
654      *        </Fooelement>
655      *     }
656      *
657      */
printWriter(String name, String nameScope, XsdComplexType complexType)658     private void printWriter(String name, String nameScope, XsdComplexType complexType)
659             throws CppCodeGeneratorException {
660         List<XsdElement> allElements = new ArrayList<>();
661         List<XsdAttribute> allAttributes = new ArrayList<>();
662         stackComponents(complexType, allElements, allAttributes);
663 
664         // parse types for elements and attributes
665         List<CppType> allElementTypes = new ArrayList<>();
666         for (XsdElement element : allElements) {
667             XsdElement elementValue = resolveElement(element);
668             CppType cppType = parseType(elementValue.getType(), elementValue.getName());
669             allElementTypes.add(cppType);
670         }
671         List<CppSimpleType> allAttributeTypes = new ArrayList<>();
672         for (XsdAttribute attribute : allAttributes) {
673             XsdType type = resolveAttribute(attribute).getType();
674             allAttributeTypes.add(parseSimpleType(type, false));
675         }
676 
677         String fullName = nameScope + name;
678         parserHeaderFile.printf("void write(std::ostream& _out, const std::string& _name) const;\n");
679         parserCppFile.printf(
680                 "\nvoid %s::write(std::ostream& _out, const std::string& _name) const {\n",
681                 fullName);
682 
683         parserCppFile.printf("_out << printIndent() << \"<\" << _name;\n");
684         for (int i = 0; i < allAttributes.size(); ++i) {
685             CppType type = allAttributeTypes.get(i);
686             XsdAttribute attribute = resolveAttribute(allAttributes.get(i));
687             String variableName = Utils.toVariableName(attribute.getName());
688             parserCppFile.printf("if (has%s()) {\n", Utils.capitalize(variableName));
689             parserCppFile.printf("_out << \" %s=\\\"\";\n", attribute.getName());
690             parserCppFile.print(type.getWritingExpression(String.format("%s%s()",
691                     getterName(type.getName()), Utils.capitalize(variableName)),
692                     attribute.getName()));
693             parserCppFile.printf("_out << \"\\\"\";\n}\n");
694         }
695         parserCppFile.print("_out << \">\" << std::endl;\n");
696         parserCppFile.print("++indentIndex;\n");
697 
698         if (!allElements.isEmpty()) {
699             for (int i = 0; i < allElements.size(); ++i) {
700                 CppType type = allElementTypes.get(i);
701                 XsdElement element = allElements.get(i);
702                 XsdElement elementValue = resolveElement(element);
703                 String elementName = getElementName(elementValue);
704                 String variableName = Utils.toVariableName(elementName);
705 
706                 if (type instanceof CppComplexType || element.isMultiple()) {
707                     parserCppFile.printf("for (auto& _value : get%s()) {\n",
708                             Utils.capitalize(variableName));
709                     if (type instanceof CppSimpleType) {
710                         parserCppFile.printf("_out << printIndent() << \"<%s>\";\n",
711                                 elementValue.getName());
712                     }
713                     parserCppFile.printf(
714                             type.getWritingExpression("_value", elementValue.getName()));
715                     if (type instanceof CppSimpleType) {
716                         parserCppFile.printf("_out << \"</%s>\" << std::endl;\n",
717                                 elementValue.getName());
718                     }
719                     parserCppFile.printf("}\n");
720                 } else {
721                     parserCppFile.printf("if (has%s()) {\n", Utils.capitalize(variableName));
722                     if (type instanceof CppSimpleType) {
723                         parserCppFile.printf("_out << printIndent() << \"<%s>\";\n",
724                                 elementValue.getName());
725                     }
726                     parserCppFile.print(type.getWritingExpression(String.format("%s%s()",
727                               getterName(type.getName()), Utils.capitalize(variableName)),
728                               elementValue.getName()));
729                     if (type instanceof CppSimpleType) {
730                         parserCppFile.printf("_out << \"</%s>\" << std::endl;\n",
731                                 elementValue.getName());
732                     }
733                     parserCppFile.print("}\n");
734                 }
735             }
736         }
737         parserCppFile.print("--indentIndex;\n");
738         parserCppFile.printf("_out << printIndent() << \"</\" << _name << \">\" << std::endl;\n");
739         parserCppFile.printf("}\n");
740     }
741 
742     /**
743      * Prints hasAttr() and getAttr() member functions for each member field.
744      *
745      * Foo.h:
746      *
747      *     const Attr& getAttr() const;
748      *     bool hasAttr() const;
749      *     const Item* getFirstItem() const; // for multi-value member
750      *
751      * Foo.cpp:
752      *
753      *     const Attr& Foo::getAttr() const {
754      *       return attr_;
755      *     }
756      *     bool Foo::hasAttr() const {
757      *       return true;
758      *     }
759      *     const Item* Foo::getFirstItem() const {
760      *       if (item_.empty()) {
761      *          return nullptr;
762      *       }
763      *       return &item_[0];
764      *     }
765      *
766      */
printGetter(String name, CppType type, String variableName, boolean isMultiple, boolean isMultipleType, boolean isRequired)767     private void printGetter(String name, CppType type, String variableName,
768             boolean isMultiple, boolean isMultipleType, boolean isRequired) {
769         String typeName = isMultiple ? String.format("std::vector<%s>",
770                 type.getName()) : type.getName();
771         String assertHasValue = String.format("_xsdc_assert(has%s());\n",
772                 Utils.capitalize(variableName));
773 
774         parserHeaderFile.printf("const %s& %s%s() const;\n", typeName, getterName(typeName),
775                 Utils.capitalize(variableName));
776 
777         parserCppFile.println();
778         parserCppFile.printf("const %s& %s::%s%s() const {\n", typeName, name,
779                 getterName(typeName), Utils.capitalize(variableName));
780         if (isMultiple || isRequired) {
781             parserCppFile.printf("return %s_;\n", variableName);
782         } else {
783             // Before accessing an optional::value(), we need to ensure that it has a value.
784             parserCppFile.print(assertHasValue);
785             parserCppFile.printf("return %s_.value();\n", variableName);
786         }
787         parserCppFile.printf("}\n\n");
788 
789         parserHeaderFile.printf("bool has%s() const;\n", Utils.capitalize(variableName));
790         parserCppFile.printf("bool %s::has%s() const {\n", name, Utils.capitalize(variableName));
791         if (isMultiple) {
792             parserCppFile.printf("return !(%s_.empty());\n}\n", variableName);
793         } else if (isRequired){
794             parserCppFile.print("return true;\n}\n");
795         } else {
796             parserCppFile.printf("return %s_.has_value();\n}\n", variableName);
797         }
798 
799         // For elements that may occur multiple types or have a list of simple types
800         if (isMultiple || isMultipleType) {
801             String elementTypeName = type instanceof CppComplexType ? type.getName() :
802                     ((CppSimpleType)type).getTypeName();
803             if (elementTypeName.equals("bool")) {
804                 parserHeaderFile.printf("%s getFirst%s() const;\n",
805                         elementTypeName, Utils.capitalize(variableName));
806                 parserCppFile.println();
807                 parserCppFile.printf("%s %s::getFirst%s() const {\n"
808                         + "%s"
809                         + "if (%s_%sempty()) {\n"
810                         + "return false;\n"
811                         + "}\n"
812                         + "return %s;\n"
813                         + "}\n",
814                         elementTypeName, name, Utils.capitalize(variableName),
815                         isMultiple ? "" : assertHasValue,
816                         variableName,
817                         isMultiple ? "." : "->",
818                         isMultiple ? String.format("%s_[0]", variableName) :
819                         String.format("%s_.value()[0]", variableName));
820             } else {
821                 parserHeaderFile.printf("const %s* getFirst%s() const;\n",
822                         elementTypeName, Utils.capitalize(variableName));
823                 parserCppFile.println();
824                 parserCppFile.printf("const %s* %s::getFirst%s() const {\n"
825                         + "%s"
826                         + "if (%s_%sempty()) {\n"
827                         + "return nullptr;\n"
828                         + "}\n"
829                         + "return &%s;\n"
830                         + "}\n",
831                         elementTypeName, name, Utils.capitalize(variableName),
832                         isMultiple ? "" : assertHasValue,
833                         variableName,
834                         isMultiple ? "." : "->",
835                         isMultiple ? String.format("%s_[0]", variableName) :
836                         String.format("%s_.value()[0]", variableName));
837             }
838         }
839     }
840 
841     /**
842      * Prints constructor for complex types
843      *
844      * Foo.h:
845      *
846      *     Foo(args...);
847      *
848      * Foo.cpp:
849      *
850      *     Foo::Foo(args...): initializer... {}
851      *
852      */
printConstructor(String name, String nameScope, XsdComplexType complexType, List<XsdElement> elements, List<XsdAttribute> attributes, String baseName)853     private String printConstructor(String name, String nameScope, XsdComplexType complexType,
854             List<XsdElement> elements, List<XsdAttribute> attributes, String baseName)
855             throws CppCodeGeneratorException {
856         String fullName = nameScope + name;
857         StringBuilder constructorArgs = new StringBuilder();
858         StringBuilder parentArgs = new StringBuilder();
859         StringBuilder constructor = new StringBuilder();
860         StringBuilder args = new StringBuilder();
861 
862         List<XsdElement> allElements = new ArrayList<>();
863         List<XsdAttribute> allAttributes = new ArrayList<>();
864         stackComponents(complexType, allElements, allAttributes);
865 
866         for (XsdElement element : allElements) {
867             XsdElement elementValue = resolveElement(element);
868             CppType type = parseType(elementValue.getType(), elementValue.getName());
869             String variableName = Utils.toVariableName(getElementName(elementValue));
870             constructorArgs.append(String.format(", %s %s", Utils.elementTypeName(type.getName(),
871                     element.isMultiple() || type instanceof CppComplexType), variableName));
872             args.append(String.format(", %s", variableName));
873             boolean isMultipleType;
874             if (type instanceof CppComplexType) {
875                 isMultipleType = true;
876             } else if (((CppSimpleType)type).isList()) {
877                 isMultipleType = true;
878             } else {
879                 isMultipleType = false;
880             }
881 
882             if (elements.contains(element)) {
883                 constructor.append(String.format(", %s_(%s)", variableName,
884                         Utils.toAssignmentName(type.getName(), variableName, isMultipleType)));
885             } else {
886                 parentArgs.append(String.format(", %s", variableName));
887             }
888         }
889         for (XsdAttribute attribute : allAttributes) {
890             CppType type = parseSimpleType(resolveAttribute(attribute).getType(), false);
891             String variableName = Utils.toVariableName(resolveAttribute(attribute).getName());
892             if (attribute.isRequired()) {
893                 constructorArgs.append(String.format(", %s %s", type.getName(), variableName));
894             } else {
895                 constructorArgs.append(String.format(", std::optional<%s> %s", type.getName(),
896                         variableName));
897             }
898             args.append(String.format(", %s", variableName));
899             boolean isMultipleType = ((CppSimpleType)type).isList() ? true : false;
900             if (attributes.contains(attribute)) {
901                 constructor.append(String.format(", %s_(%s)", variableName,
902                         Utils.toAssignmentName(type.getName(), variableName, isMultipleType)));
903             } else {
904                 parentArgs.append(String.format(", %s", variableName));
905             }
906         }
907 
908         String constructorArgsString = constructorArgs.toString();
909         String constructorString = constructor.toString();
910         if (constructorArgsString.length() > 0) {
911             constructorArgsString = constructorArgsString.substring(2);
912         }
913 
914         boolean useExplicit =
915                 !(constructorArgsString.isEmpty() || constructorArgsString.contains(","));
916         if (useExplicit) {
917             parserHeaderFile.printf("explicit %s(%s);\n", name, constructorArgsString);
918         } else {
919             parserHeaderFile.printf("%s(%s);\n", name, constructorArgsString);
920         }
921         parserCppFile.printf("\n%s::%s(%s) : ", fullName, name, constructorArgsString);
922 
923         String parentArgsString = parentArgs.toString();
924         if (parentArgsString.length() > 0) {
925             parentArgsString = parentArgsString.substring(2);
926             parserCppFile.printf("%s(%s)", baseName, parentArgsString);
927         } else {
928             constructorString = constructorString.substring(2);
929         }
930         parserCppFile.printf("%s {\n}\n", constructorString);
931 
932         String argsString = args.toString();
933         if (argsString.length() > 0) {
934             argsString = argsString.substring(2);
935         }
936         return argsString;
937     }
938 
939     /**
940      * Prints reader functions for each top-level types.
941      *
942      * Foo.h:
943      *
944      *     optional<Foo> readFoo(const char* filename);
945      *
946      * Foo.cpp:
947      *
948      *     std::optional<Foo> readFoo(const char* filename) {
949      *        ...
950      *        Foo _value = Foo::read(root);
951      *        return _value;
952      *     }
953      *
954      */
printXmlParser()955     private void printXmlParser() throws CppCodeGeneratorException {
956         if (useTinyXml) {
957             // Nothing to do for libtinyxml2
958         } else {
959             parserCppFile.printf("template <class T>\n"
960                     + "constexpr void (*xmlDeleter)(T* t);\n"
961                     + "template <>\nconstexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;\n"
962                     + "template <>\nauto xmlDeleter<xmlChar> = [](xmlChar *s) { xmlFree(s); };\n\n"
963                     + "template <class T>\n"
964                     + "constexpr auto make_xmlUnique(T *t) {\n"
965                     + "auto deleter = [](T *t) { xmlDeleter<T>(t); };\n"
966                     + "return std::unique_ptr<T, decltype(deleter)>{t, deleter};\n"
967                     + "}\n\n");
968         }
969 
970         if (hasAttr) {
971             parserCppFile.printf("static std::string getXmlAttribute"
972                     + "(const %s *cur, const char *attribute) {\n", getXmlNodeType());
973             if (useTinyXml) {
974                 parserCppFile.printf("auto attrValue = cur->Attribute(attribute);\n"
975                         + "if(attrValue == nullptr) {\n"
976                         + "return \"\";\n"
977                         + "}\n"
978                         + "return std::string(attrValue);\n");
979             } else {
980                 parserCppFile.printf("auto xmlValue = make_xmlUnique(xmlGetProp(cur, "
981                         + "reinterpret_cast<const xmlChar*>(attribute)));\n"
982                         + "if (xmlValue == nullptr) {\n"
983                         + "return \"\";\n"
984                         + "}\n"
985                         + "std::string value(reinterpret_cast<const char*>(xmlValue.get()));\n"
986                         + "return value;\n");
987             }
988             parserCppFile.printf("}\n\n");
989         }
990 
991         boolean isMultiRootElement = xmlSchema.getElementMap().values().size() > 1;
992         for (XsdElement element : xmlSchema.getElementMap().values()) {
993             // Skip parser if not specified as root.
994             if (rootElements != null
995                     && Arrays.asList(rootElements).indexOf(element.getName()) == -1) continue;
996             printXmlParserFor(element, /*loadFile=*/true, isMultiRootElement);
997             printXmlParserFor(element, /*loadFile=*/false, isMultiRootElement);
998         }
999     }
1000 
1001     /**
1002      * Prints readType(const char* configFile) if loadFile is true.
1003      * Otherwise, prints parseType(const char* xml).
1004      */
printXmlParserFor(XsdElement element, boolean loadFile, boolean isMultiRootElement)1005     private void printXmlParserFor(XsdElement element, boolean loadFile, boolean isMultiRootElement)
1006             throws CppCodeGeneratorException {
1007         CppType cppType = parseType(element.getType(), element.getName());
1008         String elementName = element.getName();
1009         String typeName = cppType.getName();
1010         String readerName =
1011                 cppType instanceof CppSimpleType ? Utils.toClassName(elementName) : typeName;
1012         String methodName = loadFile ? "read" : "parse";
1013         String argName = loadFile ? "configFile" : "xml";
1014         parserHeaderFile.printf("std::optional<%s> %s%s(const char* %s);\n\n",
1015                 typeName,
1016                 methodName,
1017                 isMultiRootElement ? readerName : "",
1018                 argName);
1019         parserCppFile.printf("std::optional<%s> %s%s(const char* %s) {\n",
1020                 typeName,
1021                 methodName,
1022                 isMultiRootElement ? readerName : "",
1023                 argName);
1024         if (useTinyXml) {
1025             String innerParser = loadFile ? "LoadFile(configFile)" : "Parse(xml)";
1026             parserCppFile.printf("tinyxml2::XMLDocument doc;\n"
1027                     + "if (doc.%s != tinyxml2::XML_SUCCESS) {\n"
1028                     + "return std::nullopt;\n"
1029                     + "}\n"
1030                     + "auto _child = doc.FirstChildElement();\n"
1031                     + "if (_child == nullptr) {\n"
1032                     + "return std::nullopt;\n"
1033                     + "}\n\n"
1034                     + "if (strcmp(_child->Name(), \"%s\") == 0) {\n",
1035                     innerParser,
1036                     elementName);
1037         } else {
1038             String innerParser = loadFile
1039                     ? "xmlParseFile(configFile)"
1040                     : "xmlParseDoc(reinterpret_cast<const xmlChar*>(xml))";
1041             parserCppFile.printf("auto doc = make_xmlUnique(%s);\n"
1042                     + "if (doc == nullptr) {\n"
1043                     + "return std::nullopt;\n"
1044                     + "}\n"
1045                     + "xmlNodePtr _child = xmlDocGetRootElement(doc.get());\n"
1046                     + "if (_child == nullptr) {\n"
1047                     + "return std::nullopt;\n"
1048                     + "}\n"
1049                     + "if (xmlXIncludeProcess(doc.get()) < 0) {\n"
1050                     + "return std::nullopt;\n"
1051                     + "}\n\n"
1052                     + "if (!xmlStrcmp(_child->name, reinterpret_cast<const xmlChar*>"
1053                     + "(\"%s\"))) {\n",
1054                     innerParser,
1055                     elementName);
1056         }
1057 
1058         if (cppType instanceof CppSimpleType) {
1059             parserCppFile.print("std::string _raw;\n");
1060             printSetRawWithElementText("_child");
1061         }
1062         parserCppFile.printf(cppType.getParsingExpression());
1063         parserCppFile.printf("return _value;\n}\n");
1064         parserCppFile.printf("return std::nullopt;\n");
1065         parserCppFile.printf("}\n\n");
1066     }
1067 
1068     /**
1069      * Prints writer functions for each top-level types
1070      *
1071      * Foo.h:
1072      *
1073      *     void write(ostream&, const Foo& foo);
1074      *
1075      * Foo.cpp:
1076      *
1077      *     void write(ostream& _out, const Foo& foo) {
1078      *        ... <?xml ... ?>
1079      *        foo.write(_out, "FooElementName");
1080      *     }
1081      *
1082      */
printXmlWriter()1083     private void printXmlWriter() throws CppCodeGeneratorException {
1084         for (XsdElement element : xmlSchema.getElementMap().values()) {
1085             // Skip writer if not specified as root.
1086             if (rootElements != null
1087                     && Arrays.asList(rootElements).indexOf(element.getName()) == -1) continue;
1088             CppType cppType = parseType(element.getType(), element.getName());
1089             String elementName = element.getName();
1090             String variableName = Utils.toVariableName(elementName);
1091             String typeName = cppType.getName();
1092             String writerName =
1093                     cppType instanceof CppSimpleType ? Utils.toClassName(elementName) : "";
1094             parserHeaderFile.printf("void write%s(std::ostream& _out, const %s& %s);\n\n",
1095                     writerName, typeName, variableName);
1096             parserCppFile.printf("void write%s(std::ostream& _out, const %s& %s) {\n",
1097                     writerName, typeName, variableName);
1098             parserCppFile.print(
1099                     "_out << \"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\";\n");
1100             if (cppType instanceof CppSimpleType) {
1101                 parserCppFile.printf("_out << \"<%s>\";\n", elementName);
1102                 parserCppFile.print(cppType.getWritingExpression(variableName, ""));
1103                 parserCppFile.printf("_out << \"</%s>\" << std::endl;\n", elementName);
1104             } else {
1105                 parserCppFile.printf("%s.write(_out, \"%s\");\n", variableName, elementName);
1106             }
1107             parserCppFile.printf("}\n\n");
1108         }
1109 
1110         parserCppFile.print("static int indentIndex = 0;\n"
1111                 + "std::string printIndent() {\n"
1112                 + "std::string s = \"\";\n"
1113                 + "for (int index = 0; index < indentIndex; ++index) {\n"
1114                 + "s += \"    \";\n"
1115                 + "}\nreturn s;\n}\n\n");
1116     }
1117 
getElementName(XsdElement element)1118     private String getElementName(XsdElement element) {
1119         if (element instanceof XsdChoice) {
1120             return element.getName() + "_optional";
1121         } else if (element instanceof XsdAll) {
1122             return element.getName() + "_all";
1123         }
1124         return element.getName();
1125     }
1126 
getterName(String type)1127     private String getterName(String type) {
1128         if (type.equals("bool") && booleanGetter) {
1129             return "is";
1130         }
1131         return "get";
1132     }
1133 
stackComponents(XsdComplexType complexType, List<XsdElement> elements, List<XsdAttribute> attributes)1134     private void stackComponents(XsdComplexType complexType, List<XsdElement> elements,
1135             List<XsdAttribute> attributes) throws CppCodeGeneratorException {
1136         if (complexType.getBase() != null) {
1137             QName baseRef = complexType.getBase().getRef();
1138             if (baseRef != null && !baseRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) {
1139                 XsdType parent = getType(baseRef.getLocalPart());
1140                 if (parent instanceof XsdComplexType) {
1141                     stackComponents((XsdComplexType) parent, elements, attributes);
1142                 }
1143             }
1144         }
1145         elements.addAll(getAllElements(complexType.getGroup()));
1146         elements.addAll(complexType.getElements());
1147         for (XsdAttributeGroup attributeGroup : complexType.getAttributeGroups()) {
1148             attributes.addAll(getAllAttributes(resolveAttributeGroup(attributeGroup)));
1149         }
1150         attributes.addAll(complexType.getAttributes());
1151     }
1152 
getAllAttributes(XsdAttributeGroup attributeGroup)1153     private List<XsdAttribute> getAllAttributes(XsdAttributeGroup attributeGroup)
1154             throws CppCodeGeneratorException{
1155         List<XsdAttribute> attributes = new ArrayList<>();
1156         for (XsdAttributeGroup attrGroup : attributeGroup.getAttributeGroups()) {
1157             attributes.addAll(getAllAttributes(resolveAttributeGroup(attrGroup)));
1158         }
1159         attributes.addAll(attributeGroup.getAttributes());
1160         return attributes;
1161     }
1162 
getAllElements(XsdGroup group)1163     private List<XsdElement> getAllElements(XsdGroup group) throws CppCodeGeneratorException {
1164         List<XsdElement> elements = new ArrayList<>();
1165         if (group == null) {
1166             return elements;
1167         }
1168         elements.addAll(getAllElements(resolveGroup(group)));
1169         elements.addAll(group.getElements());
1170         return elements;
1171     }
1172 
1173 
getBaseName(XsdComplexType complexType)1174     private String getBaseName(XsdComplexType complexType) throws CppCodeGeneratorException {
1175         if (complexType.getBase() == null) return null;
1176         if (complexType.getBase().getRef().getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) {
1177             return null;
1178         }
1179         XsdType base = getType(complexType.getBase().getRef().getLocalPart());
1180         if (base instanceof XsdComplexType) {
1181             return Utils.toClassName(base.getName());
1182         }
1183         return null;
1184     }
1185 
getValueType(XsdSimpleContent simpleContent, boolean traverse)1186     private CppSimpleType getValueType(XsdSimpleContent simpleContent, boolean traverse)
1187             throws CppCodeGeneratorException {
1188         assert simpleContent.getBase() != null;
1189         QName baseRef = simpleContent.getBase().getRef();
1190         assert baseRef != null;
1191         if (baseRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) {
1192             return predefinedType(baseRef.getLocalPart());
1193         } else {
1194             XsdType parent = getType(baseRef.getLocalPart());
1195             if (parent instanceof XsdSimpleType) {
1196                 return parseSimpleTypeReference(baseRef, false);
1197             }
1198             if (!traverse) return null;
1199             if (parent instanceof XsdSimpleContent) {
1200                 return getValueType((XsdSimpleContent) parent, true);
1201             } else {
1202                 throw new CppCodeGeneratorException(
1203                         String.format("base not simple : %s", baseRef.getLocalPart()));
1204             }
1205         }
1206     }
1207 
parseType(XsdType type, String defaultName)1208     private CppType parseType(XsdType type, String defaultName) throws CppCodeGeneratorException {
1209         if (type.getRef() != null) {
1210             String name = type.getRef().getLocalPart();
1211             if (type.getRef().getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) {
1212                 return predefinedType(name);
1213             } else {
1214                 XsdType typeValue = getType(name);
1215                 if (typeValue instanceof XsdSimpleType) {
1216                     return parseSimpleTypeReference(type.getRef(), false);
1217                 }
1218                 return parseType(typeValue, name);
1219             }
1220         }
1221         if (type instanceof XsdComplexType) {
1222             return new CppComplexType(Utils.toClassName(defaultName));
1223         } else if (type instanceof XsdSimpleType) {
1224             return parseSimpleTypeValue((XsdSimpleType) type, false);
1225         } else {
1226             throw new CppCodeGeneratorException(
1227                     String.format("unknown type name : %s", defaultName));
1228         }
1229     }
1230 
parseSimpleType(XsdType type, boolean traverse)1231     private CppSimpleType parseSimpleType(XsdType type, boolean traverse)
1232             throws CppCodeGeneratorException {
1233         if (type.getRef() != null) {
1234             return parseSimpleTypeReference(type.getRef(), traverse);
1235         } else {
1236             return parseSimpleTypeValue((XsdSimpleType) type, traverse);
1237         }
1238     }
1239 
parseSimpleTypeReference(QName typeRef, boolean traverse)1240     private CppSimpleType parseSimpleTypeReference(QName typeRef, boolean traverse)
1241             throws CppCodeGeneratorException {
1242         assert typeRef != null;
1243         String typeName = typeRef.getLocalPart();
1244         if (typeRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) {
1245             return predefinedType(typeName);
1246         }
1247         if (cppSimpleTypeMap.containsKey(typeName)) {
1248             return cppSimpleTypeMap.get(typeName);
1249         } else if (traverse) {
1250             XsdSimpleType simpleType = getSimpleType(typeName);
1251             CppSimpleType ret = parseSimpleTypeValue(simpleType, true);
1252             cppSimpleTypeMap.put(typeName, ret);
1253             return ret;
1254         } else {
1255             throw new CppCodeGeneratorException(String.format("unknown type name : %s", typeName));
1256         }
1257     }
1258 
parseSimpleTypeValue(XsdSimpleType simpleType, boolean traverse)1259     private CppSimpleType parseSimpleTypeValue(XsdSimpleType simpleType, boolean traverse)
1260             throws CppCodeGeneratorException {
1261         if (simpleType instanceof XsdList) {
1262             XsdList list = (XsdList) simpleType;
1263             return parseSimpleType(list.getItemType(), traverse).newListType();
1264         } else if (simpleType instanceof XsdRestriction) {
1265             // we don't consider any restrictions.
1266             XsdRestriction restriction = (XsdRestriction) simpleType;
1267             if (restriction.getEnums() != null) {
1268                 String name = Utils.toClassName(restriction.getName());
1269                 return new CppSimpleType(name, "stringTo" + name + "(%s)", false, true);
1270             }
1271             return parseSimpleType(restriction.getBase(), traverse);
1272         } else if (simpleType instanceof XsdUnion) {
1273             // unions are almost always interpreted as java.lang.String
1274             // Exceptionally, if any of member types of union are 'list', then we interpret it as
1275             // List<String>
1276             XsdUnion union = (XsdUnion) simpleType;
1277             for (XsdType memberType : union.getMemberTypes()) {
1278                 if (parseSimpleType(memberType, traverse).isList()) {
1279                     return new CppSimpleType("std::string", "%s", true);
1280                 }
1281             }
1282             return new CppSimpleType("std::string", "%s", false);
1283         } else {
1284             // unreachable
1285             throw new IllegalStateException("unknown simple type");
1286         }
1287     }
1288 
resolveElement(XsdElement element)1289     private XsdElement resolveElement(XsdElement element) throws CppCodeGeneratorException {
1290         if (element.getRef() == null) return element;
1291         String name = element.getRef().getLocalPart();
1292         XsdElement ret = xmlSchema.getElementMap().get(name);
1293         if (ret != null) return ret;
1294         throw new CppCodeGeneratorException(String.format("no element named : %s", name));
1295     }
1296 
resolveGroup(XsdGroup group)1297     private XsdGroup resolveGroup(XsdGroup group) throws CppCodeGeneratorException {
1298         if (group.getRef() == null) return null;
1299         String name = group.getRef().getLocalPart();
1300         XsdGroup ret = xmlSchema.getGroupMap().get(name);
1301         if (ret != null) return ret;
1302         throw new CppCodeGeneratorException(String.format("no group named : %s", name));
1303     }
1304 
resolveAttribute(XsdAttribute attribute)1305     private XsdAttribute resolveAttribute(XsdAttribute attribute)
1306             throws CppCodeGeneratorException {
1307         if (attribute.getRef() == null) return attribute;
1308         String name = attribute.getRef().getLocalPart();
1309         XsdAttribute ret = xmlSchema.getAttributeMap().get(name);
1310         if (ret != null) return ret;
1311         throw new CppCodeGeneratorException(String.format("no attribute named : %s", name));
1312     }
1313 
resolveAttributeGroup(XsdAttributeGroup attributeGroup)1314     private XsdAttributeGroup resolveAttributeGroup(XsdAttributeGroup attributeGroup)
1315             throws CppCodeGeneratorException {
1316         if (attributeGroup.getRef() == null) return attributeGroup;
1317         String name = attributeGroup.getRef().getLocalPart();
1318         XsdAttributeGroup ret = xmlSchema.getAttributeGroupMap().get(name);
1319         if (ret != null) return ret;
1320         throw new CppCodeGeneratorException(String.format("no attribute group named : %s", name));
1321     }
1322 
getType(String name)1323     private XsdType getType(String name) throws CppCodeGeneratorException {
1324         XsdType type = xmlSchema.getTypeMap().get(name);
1325         if (type != null) return type;
1326         throw new CppCodeGeneratorException(String.format("no type named : %s", name));
1327     }
1328 
getSimpleType(String name)1329     private XsdSimpleType getSimpleType(String name) throws CppCodeGeneratorException {
1330         XsdType type = getType(name);
1331         if (type instanceof XsdSimpleType) return (XsdSimpleType) type;
1332         throw new CppCodeGeneratorException(String.format("not a simple type : %s", name));
1333     }
1334 
hasAttribute(XsdComplexType complexType)1335     private boolean hasAttribute(XsdComplexType complexType) throws CppCodeGeneratorException {
1336         if (complexType.getAttributes().size() > 0 ||
1337                 complexType.getAttributeGroups().size() > 0) {
1338             return true;
1339         }
1340         boolean results = false;
1341         for (XsdElement element : complexType.getElements()) {
1342             if (element.getRef() == null && element.getType().getRef() == null
1343                     && element.getType() instanceof XsdComplexType) {
1344                 results = hasAttribute((XsdComplexType) element.getType());
1345                 if (results) {
1346                     return results;
1347                 }
1348             }
1349         }
1350         return results;
1351     }
1352 
predefinedType(String name)1353     private static CppSimpleType predefinedType(String name) throws CppCodeGeneratorException {
1354         switch (name) {
1355             case "string":
1356             case "token":
1357             case "normalizedString":
1358             case "language":
1359             case "ENTITY":
1360             case "ID":
1361             case "Name":
1362             case "NCName":
1363             case "NMTOKEN":
1364             case "anyURI":
1365             case "anyType":
1366             case "QName":
1367             case "NOTATION":
1368             case "IDREF":
1369                 return new CppSimpleType("std::string", "%s", false);
1370             case "ENTITIES":
1371             case "NMTOKENS":
1372             case "IDREFS":
1373                 return new CppSimpleType("std::string", "%s", true);
1374             case "date":
1375             case "dateTime":
1376             case "time":
1377             case "gDay":
1378             case "gMonth":
1379             case "gYear":
1380             case "gMonthDay":
1381             case "gYearMonth":
1382             case "duration":
1383                 return new CppSimpleType("std::string", "%s", false);
1384             case "decimal":
1385                 return new CppSimpleType("double", "std::stod(%s)", false);
1386             case "integer":
1387             case "negativeInteger":
1388             case "nonNegativeInteger":
1389             case "positiveInteger":
1390             case "nonPositiveInteger":
1391                 return new CppSimpleType("int64_t", "std::stoll(%s)", false);
1392             case "unsignedLong":
1393                 return new CppSimpleType("uint64_t", "std::stoull(%s)", false);
1394             case "long":
1395                 return new CppSimpleType("int64_t", "std::stoll(%s)", false);
1396             case "unsignedInt":
1397                 return new CppSimpleType("unsigned int",
1398                         "static_cast<unsigned int>(stoul(%s))", false);
1399             case "int":
1400                 return new CppSimpleType("int", "std::stoi(%s)", false);
1401             case "unsignedShort":
1402                 return new CppSimpleType("unsigned short",
1403                         "static_cast<unsigned short>(std::stoi(%s))", false);
1404             case "short":
1405                 return new CppSimpleType("short", "static_cast<short>(std::stoi(%s))", false);
1406             case "unsignedByte":
1407                 return new CppSimpleType("unsigned char",
1408                         "static_cast<unsigned char>(std::stoi(%s))", false);
1409             case "byte":
1410                 return new CppSimpleType("char", "static_cast<char>(std::stoi(%s))", false);
1411             case "boolean":
1412                 return new CppSimpleType("bool", "%s == \"true\"", false);
1413             case "double":
1414                 return new CppSimpleType("double", "std::stod(%s)", false);
1415             case "float":
1416                 return new CppSimpleType("float", "std::stof(%s)", false);
1417             case "base64Binary":
1418             case "hexBinary":
1419                 return new CppSimpleType("std::string", "%s", false);
1420         }
1421         throw new CppCodeGeneratorException("unknown xsd predefined type : " + name);
1422     }
1423 }
1424