| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.xsdc.java; |
| |
| import com.android.xsdc.CodeWriter; |
| import com.android.xsdc.FileSystem; |
| import com.android.xsdc.XmlSchema; |
| import com.android.xsdc.XsdConstants; |
| import com.android.xsdc.tag.*; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.xml.namespace.QName; |
| |
| public class JavaCodeGenerator { |
| private XmlSchema xmlSchema; |
| private String packageName; |
| private Map<String, JavaSimpleType> javaSimpleTypeMap; |
| private boolean writer; |
| private boolean showNullability; |
| private boolean generateHasMethod; |
| private boolean useHexBinary; |
| private boolean booleanGetter; |
| |
| public JavaCodeGenerator(XmlSchema xmlSchema, String packageName, boolean writer, |
| boolean showNullability, boolean generateHasMethod, boolean booleanGetter) |
| throws JavaCodeGeneratorException { |
| this.xmlSchema = xmlSchema; |
| this.packageName = packageName; |
| this.writer = writer; |
| this.showNullability = showNullability; |
| this.generateHasMethod = generateHasMethod; |
| this.booleanGetter = booleanGetter; |
| useHexBinary = false; |
| |
| // class naming validation |
| { |
| Set<String> nameSet = new HashSet<>(); |
| nameSet.add("XmlParser"); |
| for (XsdType type : xmlSchema.getTypeMap().values()) { |
| if ((type instanceof XsdComplexType) || (type instanceof XsdRestriction && |
| ((XsdRestriction)type).getEnums() != null)) { |
| String name = Utils.toClassName(type.getName()); |
| if (nameSet.contains(name)) { |
| throw new JavaCodeGeneratorException( |
| String.format("duplicate class name : %s", name)); |
| } |
| nameSet.add(name); |
| } |
| } |
| for (XsdElement element : xmlSchema.getElementMap().values()) { |
| XsdType type = element.getType(); |
| if (type.getRef() == null && type instanceof XsdComplexType) { |
| String name = Utils.toClassName(element.getName()); |
| if (nameSet.contains(name)) { |
| throw new JavaCodeGeneratorException( |
| String.format("duplicate class name : %s", name)); |
| } |
| nameSet.add(name); |
| } |
| } |
| } |
| |
| javaSimpleTypeMap = new HashMap<>(); |
| for (XsdType type : xmlSchema.getTypeMap().values()) { |
| if (type instanceof XsdSimpleType) { |
| XsdType refType = new XsdType(null, new QName(type.getName())); |
| parseSimpleType(refType, true); |
| } |
| } |
| } |
| |
| public void print(FileSystem fs) |
| throws JavaCodeGeneratorException, IOException { |
| for (XsdType type : xmlSchema.getTypeMap().values()) { |
| if (type instanceof XsdComplexType) { |
| String name = Utils.toClassName(type.getName()); |
| XsdComplexType complexType = (XsdComplexType) type; |
| try (CodeWriter out = new CodeWriter(fs.getPrintWriter(name + ".java"))) { |
| out.printf("package %s;\n\n", packageName); |
| printClass(out, name, complexType, ""); |
| } |
| } else if (type instanceof XsdRestriction && |
| ((XsdRestriction)type).getEnums() != null) { |
| String name = Utils.toClassName(type.getName()); |
| XsdRestriction restrictionType = (XsdRestriction) type; |
| try (CodeWriter out = new CodeWriter(fs.getPrintWriter(name + ".java"))) { |
| out.printf("package %s;\n\n", packageName); |
| printEnumClass(out, name, restrictionType); |
| } |
| } |
| } |
| for (XsdElement element : xmlSchema.getElementMap().values()) { |
| XsdType type = element.getType(); |
| if (type.getRef() == null && type instanceof XsdComplexType) { |
| String name = Utils.toClassName(element.getName()); |
| XsdComplexType complexType = (XsdComplexType) type; |
| try (CodeWriter out = new CodeWriter(fs.getPrintWriter(name + ".java"))) { |
| out.printf("package %s;\n\n", packageName); |
| printClass(out, name, complexType, ""); |
| } |
| } |
| } |
| try (CodeWriter out = new CodeWriter(fs.getPrintWriter("XmlParser.java"))) { |
| printXmlParser(out); |
| } |
| if (writer) { |
| try (CodeWriter out = new CodeWriter(fs.getPrintWriter("XmlWriter.java"))) { |
| printXmlWriter(out); |
| } |
| } |
| if (useHexBinary) { |
| try (CodeWriter out = new CodeWriter(fs.getPrintWriter("HexBinaryHelper.java"))) { |
| printHexBinaryHelper(out); |
| } |
| } |
| } |
| |
| private void printEnumClass(CodeWriter out, String name, XsdRestriction restrictionType) |
| throws JavaCodeGeneratorException { |
| if (restrictionType.isDeprecated()) { |
| out.printf("@java.lang.Deprecated\n"); |
| } |
| out.printf("public enum %s {", name); |
| List<XsdEnumeration> enums = restrictionType.getEnums(); |
| |
| for (XsdEnumeration tag : enums) { |
| if (tag.isDeprecated()) { |
| out.printf("@java.lang.Deprecated\n"); |
| } |
| String value = tag.getValue(); |
| out.printf("\n%s(\"%s\"),", Utils.toEnumName(value), value); |
| } |
| out.printf(";\n\n"); |
| out.printf("private final String rawName;\n\n"); |
| out.printf("%s(%sString rawName) {\n" |
| + "this.rawName = rawName;\n" |
| + "}\n\n", name, getDefaultNullability(Nullability.NON_NULL)); |
| out.printf("public %sString getRawName() {\n" |
| + "return rawName;\n" |
| + "}\n\n", getDefaultNullability(Nullability.NON_NULL)); |
| |
| out.printf("static %s%s fromString(%sString rawString) {\n" |
| + "for (%s _f : values()) {\n" |
| + "if (_f.getRawName().equals(rawString)) {\n" |
| + "return _f;\n" |
| + "}\n" |
| + "}\n" |
| + "throw new IllegalArgumentException(rawString);\n" |
| + "}\n\n", getDefaultNullability(Nullability.NULLABLE), name, |
| getDefaultNullability(Nullability.NON_NULL), name); |
| |
| if (writer) { |
| out.printf("@Override\n" |
| + "public %sString toString() {\n" |
| + "return rawName;\n" |
| + "}\n", getDefaultNullability(Nullability.NON_NULL)); |
| } |
| out.println("}"); |
| } |
| |
| private void printClass(CodeWriter out, String name, XsdComplexType complexType, |
| String nameScope) throws JavaCodeGeneratorException { |
| assert name != null; |
| // need element, attribute name duplicate validation? |
| |
| String baseName = getBaseName(complexType); |
| JavaSimpleType valueType = (complexType instanceof XsdSimpleContent) ? |
| getValueType((XsdSimpleContent) complexType, false) : null; |
| |
| String finalString = getFinalString(complexType.isFinalValue()); |
| if (complexType.isDeprecated()) { |
| out.printf("@java.lang.Deprecated\n"); |
| } |
| if (nameScope.isEmpty()) { |
| out.printf("public%s class %s ", finalString, name); |
| } else { |
| out.printf("public%s static class %s ", finalString, name); |
| } |
| if (baseName != null) { |
| out.printf("extends %s {\n", baseName); |
| } else { |
| out.println("{"); |
| } |
| |
| // parse types for elements and attributes |
| List<JavaType> elementTypes = new ArrayList<>(); |
| List<XsdElement> elements = new ArrayList<>(); |
| elements.addAll(getAllElements(complexType.getGroup())); |
| elements.addAll(complexType.getElements()); |
| |
| for (XsdElement element : elements) { |
| JavaType javaType; |
| XsdElement elementValue = resolveElement(element); |
| if (element.getRef() == null && element.getType().getRef() == null |
| && element.getType() instanceof XsdComplexType) { |
| // print inner class for anonymous types |
| String innerName = Utils.toClassName(getElementName(element)); |
| XsdComplexType innerType = (XsdComplexType) element.getType(); |
| String innerNameScope = nameScope + name + "."; |
| printClass(out, innerName, innerType, innerNameScope); |
| out.println(); |
| javaType = new JavaComplexType(innerNameScope + innerName); |
| } else { |
| javaType = parseType(elementValue.getType(), getElementName(elementValue)); |
| } |
| elementTypes.add(javaType); |
| } |
| List<JavaSimpleType> attributeTypes = new ArrayList<>(); |
| List<XsdAttribute> attributes = new ArrayList<>(); |
| for (XsdAttributeGroup attributeGroup : complexType.getAttributeGroups()) { |
| attributes.addAll(getAllAttributes(resolveAttributeGroup(attributeGroup))); |
| } |
| attributes.addAll(complexType.getAttributes()); |
| |
| for (XsdAttribute attribute : attributes) { |
| XsdType type = resolveAttribute(attribute).getType(); |
| attributeTypes.add(parseSimpleType(type, false)); |
| } |
| |
| // print member variables |
| for (int i = 0; i < elementTypes.size(); ++i) { |
| JavaType type = elementTypes.get(i); |
| XsdElement element = elements.get(i); |
| XsdElement elementValue = resolveElement(element); |
| String typeName = element.isMultiple() ? String.format("java.util.List<%s>", |
| type.getNullableName()) : type.getNullableName(); |
| out.printf("%sprivate %s %s;\n", getNullabilityString(element.getNullability()), |
| typeName, Utils.toVariableName(getElementName(elementValue))); |
| } |
| for (int i = 0; i < attributeTypes.size(); ++i) { |
| JavaType type = attributeTypes.get(i); |
| XsdAttribute attribute = resolveAttribute(attributes.get(i)); |
| out.printf("%sprivate %s %s;\n", getNullabilityString(attribute.getNullability()), |
| type.getNullableName(), Utils.toVariableName(attribute.getName())); |
| } |
| if (valueType != null) { |
| out.printf("private %s value;\n", valueType.getName()); |
| } |
| |
| // print getters and setters |
| for (int i = 0; i < elementTypes.size(); ++i) { |
| JavaType type = elementTypes.get(i); |
| XsdElement element = elements.get(i); |
| XsdElement elementValue = resolveElement(element); |
| printGetterAndSetter(out, type, Utils.toVariableName(getElementName(elementValue)), |
| element.isMultiple(), element); |
| } |
| for (int i = 0; i < attributeTypes.size(); ++i) { |
| JavaType type = attributeTypes.get(i); |
| XsdAttribute attribute = resolveAttribute(attributes.get(i)); |
| printGetterAndSetter(out, type, Utils.toVariableName(attribute.getName()), false, |
| attribute); |
| } |
| if (valueType != null) { |
| printGetterAndSetter(out, valueType, "value", false, null); |
| } |
| |
| out.println(); |
| printParser(out, nameScope + name, complexType); |
| if (writer) { |
| printWriter(out, name, complexType); |
| } |
| |
| out.println("}"); |
| } |
| |
| private void printParser(CodeWriter out, String name, XsdComplexType complexType) |
| throws JavaCodeGeneratorException { |
| JavaSimpleType baseValueType = (complexType instanceof XsdSimpleContent) ? |
| getValueType((XsdSimpleContent) complexType, true) : null; |
| List<XsdElement> allElements = new ArrayList<>(); |
| List<XsdAttribute> allAttributes = new ArrayList<>(); |
| stackComponents(complexType, allElements, allAttributes); |
| |
| // parse types for elements and attributes |
| List<JavaType> allElementTypes = new ArrayList<>(); |
| for (XsdElement element : allElements) { |
| XsdElement elementValue = resolveElement(element); |
| JavaType javaType = parseType(elementValue.getType(), elementValue.getName()); |
| allElementTypes.add(javaType); |
| } |
| List<JavaSimpleType> allAttributeTypes = new ArrayList<>(); |
| for (XsdAttribute attribute : allAttributes) { |
| XsdType type = resolveAttribute(attribute).getType(); |
| allAttributeTypes.add(parseSimpleType(type, false)); |
| } |
| |
| out.printf("static %s%s read(%sorg.xmlpull.v1.XmlPullParser parser) " + |
| "throws org.xmlpull.v1.XmlPullParserException, java.io.IOException, " + |
| "javax.xml.datatype.DatatypeConfigurationException {\n", |
| getDefaultNullability(Nullability.NON_NULL), name, |
| getDefaultNullability(Nullability.NON_NULL)); |
| |
| out.printf("%s instance = new %s();\n" |
| + "String raw = null;\n", name, name); |
| for (int i = 0; i < allAttributes.size(); ++i) { |
| JavaType type = allAttributeTypes.get(i); |
| XsdAttribute attribute = resolveAttribute(allAttributes.get(i)); |
| String variableName = Utils.toVariableName(attribute.getName()); |
| out.printf("raw = parser.getAttributeValue(null, \"%s\");\n" |
| + "if (raw != null) {\n", attribute.getName()); |
| out.print(type.getParsingExpression()); |
| out.printf("instance.set%s(value);\n" |
| + "}\n", Utils.capitalize(variableName)); |
| } |
| |
| if (baseValueType != null) { |
| out.print("raw = XmlParser.readText(parser);\n" |
| + "if (raw != null) {\n"); |
| out.print(baseValueType.getParsingExpression()); |
| out.print("instance.setValue(value);\n" |
| + "}\n"); |
| } else if (!allElements.isEmpty()) { |
| out.print("int outerDepth = parser.getDepth();\n" |
| + "int type;\n" |
| + "while ((type=parser.next()) != org.xmlpull.v1.XmlPullParser.END_DOCUMENT\n" |
| + " && type != org.xmlpull.v1.XmlPullParser.END_TAG) {\n" |
| + "if (parser.getEventType() != org.xmlpull.v1.XmlPullParser.START_TAG) " |
| + "continue;\n" |
| + "String tagName = parser.getName();\n"); |
| for (int i = 0; i < allElements.size(); ++i) { |
| JavaType type = allElementTypes.get(i); |
| XsdElement element = allElements.get(i); |
| XsdElement elementValue = resolveElement(element); |
| String variableName = Utils.toVariableName(getElementName(elementValue)); |
| out.printf("if (tagName.equals(\"%s\")) {\n", elementValue.getName()); |
| if (type instanceof JavaSimpleType) { |
| out.print("raw = XmlParser.readText(parser);\n"); |
| } |
| out.print(type.getParsingExpression()); |
| if (element.isMultiple()) { |
| out.printf("instance.get%s().add(value);\n", |
| Utils.capitalize(variableName)); |
| } else { |
| out.printf("instance.set%s(value);\n", |
| Utils.capitalize(variableName)); |
| } |
| out.printf("} else "); |
| } |
| out.print("{\n" |
| + "XmlParser.skip(parser);\n" |
| + "}\n" |
| + "}\n"); |
| out.printf("if (type != org.xmlpull.v1.XmlPullParser.END_TAG) {\n" |
| + "throw new javax.xml.datatype.DatatypeConfigurationException(\"%s is not closed\");\n" |
| + "}\n", name); |
| } else { |
| out.print("XmlParser.skip(parser);\n"); |
| } |
| out.print("return instance;\n" |
| + "}\n"); |
| } |
| |
| private void printWriter(CodeWriter out, String name, XsdComplexType complexType) |
| throws JavaCodeGeneratorException { |
| JavaSimpleType baseValueType = (complexType instanceof XsdSimpleContent) ? |
| getValueType((XsdSimpleContent) complexType, true) : null; |
| List<XsdElement> allElements = new ArrayList<>(); |
| List<XsdAttribute> allAttributes = new ArrayList<>(); |
| stackComponents(complexType, allElements, allAttributes); |
| |
| // parse types for elements and attributes |
| List<JavaType> allElementTypes = new ArrayList<>(); |
| for (XsdElement element : allElements) { |
| XsdElement elementValue = resolveElement(element); |
| JavaType javaType = parseType(elementValue.getType(), elementValue.getName()); |
| allElementTypes.add(javaType); |
| } |
| List<JavaSimpleType> allAttributeTypes = new ArrayList<>(); |
| for (XsdAttribute attribute : allAttributes) { |
| XsdType type = resolveAttribute(attribute).getType(); |
| allAttributeTypes.add(parseSimpleType(type, false)); |
| } |
| |
| out.printf("\nvoid write(%sXmlWriter out, %sString name) " + |
| "throws java.io.IOException {\n", getDefaultNullability(Nullability.NON_NULL), |
| getDefaultNullability(Nullability.NON_NULL)); |
| |
| out.print("out.print(\"<\" + name);\n"); |
| for (int i = 0; i < allAttributes.size(); ++i) { |
| JavaType type = allAttributeTypes.get(i); |
| boolean isList = allAttributeTypes.get(i).isList(); |
| XsdAttribute attribute = resolveAttribute(allAttributes.get(i)); |
| String variableName = Utils.toVariableName(attribute.getName()); |
| out.printf("if (has%s()) {\n", Utils.capitalize(variableName)); |
| out.printf("out.print(\" %s=\\\"\");\n", attribute.getName()); |
| out.print(type.getWritingExpression(String.format("%s%s()", |
| getterName(type.getName()), Utils.capitalize(variableName)), |
| attribute.getName())); |
| out.printf("out.print(\"\\\"\");\n}\n"); |
| } |
| out.printf("out.print(\">\\n\");\n"); |
| |
| if (!allElements.isEmpty()) { |
| out.printf("out.increaseIndent();\n"); |
| for (int i = 0; i < allElements.size(); ++i) { |
| JavaType type = allElementTypes.get(i); |
| XsdElement element = allElements.get(i); |
| XsdElement elementValue = resolveElement(element); |
| String elementName = getElementName(elementValue); |
| String variableName = Utils.toVariableName(elementName); |
| |
| if (element.isMultiple()) { |
| out.printf("for (%s value : get%s()) {\n", type.getName(), |
| Utils.capitalize(variableName)); |
| if (type instanceof JavaSimpleType) { |
| out.printf("out.print(\"<%s>\");\n", elementValue.getName()); |
| } |
| out.print(type.getWritingExpression("value", elementValue.getName())); |
| if (type instanceof JavaSimpleType) { |
| out.printf("out.print(\"</%s>\\n\");\n", elementValue.getName()); |
| } |
| out.print("}\n"); |
| } else { |
| out.printf("if (has%s()) {\n", Utils.capitalize(variableName)); |
| if (type instanceof JavaSimpleType) { |
| out.printf("out.print(\"<%s>\");\n", elementValue.getName()); |
| } |
| out.print(type.getWritingExpression(String.format("%s%s()", |
| getterName(type.getName()), Utils.capitalize(variableName)), |
| elementValue.getName())); |
| if (type instanceof JavaSimpleType) { |
| out.printf("out.print(\"</%s>\\n\");\n", elementValue.getName()); |
| } |
| out.printf("}\n"); |
| } |
| |
| } |
| out.printf("out.decreaseIndent();\n"); |
| } |
| out.print("out.print(\"</\" + name + \">\\n\");\n"); |
| out.print("}\n"); |
| } |
| |
| private void printGetterAndSetter(CodeWriter out, JavaType type, String variableName, |
| boolean isMultiple, XsdTag tag) { |
| String typeName = isMultiple ? String.format("java.util.List<%s>", type.getNullableName()) |
| : type.getName(); |
| boolean deprecated = tag == null ? false : tag.isDeprecated(); |
| boolean finalValue = tag == null ? false : tag.isFinalValue(); |
| Nullability nullability = tag == null ? Nullability.UNKNOWN : tag.getNullability(); |
| out.println(); |
| if (deprecated) { |
| out.printf("@java.lang.Deprecated\n"); |
| } |
| out.printf("public%s %s%s %s%s() {\n", getFinalString(finalValue), |
| getNullabilityString(nullability), typeName, getterName(typeName), |
| Utils.capitalize(variableName)); |
| if ((type instanceof JavaSimpleType && ((JavaSimpleType)type).isList()) || isMultiple) { |
| out.printf("if (%s == null) {\n" |
| + "%s = new java.util.ArrayList<>();\n" |
| + "}\n", variableName, variableName); |
| } else if (type.isPrimitiveType()) { |
| out.printf("if (%s == null) {\n", variableName); |
| if (typeName.equals("boolean")) { |
| out.printf("return false;\n}\n", variableName); |
| } else { |
| out.printf("return (%s)0;\n}\n", typeName); |
| } |
| } |
| out.printf("return %s;\n" |
| + "}\n", variableName); |
| |
| if (isMultiple) return; |
| out.println(); |
| out.printf("%sboolean has%s() {\n" |
| + "if (%s == null) {\n" |
| + "return false;\n" |
| + "}\n" |
| + "return true;\n}\n\n", |
| generateHasMethod ? "public " : "", |
| Utils.capitalize(variableName), variableName); |
| if (deprecated) { |
| out.printf("@java.lang.Deprecated\n"); |
| } |
| out.printf("public%s void set%s(%s%s %s) {\n" |
| + "this.%s = %s;\n" |
| + "}\n", |
| getFinalString(finalValue), Utils.capitalize(variableName), |
| getNullabilityString(nullability), typeName, variableName, |
| variableName, variableName); |
| } |
| |
| private void printXmlParser(CodeWriter out) throws JavaCodeGeneratorException { |
| out.printf("package %s;\n", packageName); |
| out.println(); |
| out.println("public class XmlParser {"); |
| |
| boolean isMultiRootElement = xmlSchema.getElementMap().values().size() > 1; |
| for (XsdElement element : xmlSchema.getElementMap().values()) { |
| JavaType javaType = parseType(element.getType(), element.getName()); |
| out.printf("public static %s%s read%s(%sjava.io.InputStream in)" |
| + " throws org.xmlpull.v1.XmlPullParserException, java.io.IOException, " |
| + "javax.xml.datatype.DatatypeConfigurationException {\n" |
| + "org.xmlpull.v1.XmlPullParser parser = org.xmlpull.v1.XmlPullParserFactory" |
| + ".newInstance().newPullParser();\n" |
| + "parser.setFeature(org.xmlpull.v1.XmlPullParser.FEATURE_PROCESS_NAMESPACES, " |
| + "true);\n" |
| + "parser.setInput(in, null);\n" |
| + "parser.nextTag();\n" |
| + "String tagName = parser.getName();\n" |
| + "String raw = null;\n", getDefaultNullability(Nullability.NULLABLE), |
| javaType.getName(), isMultiRootElement ? Utils.capitalize(javaType.getName()) : "", |
| getDefaultNullability(Nullability.NON_NULL)); |
| out.printf("if (tagName.equals(\"%s\")) {\n", element.getName()); |
| if (javaType instanceof JavaSimpleType) { |
| out.print("raw = XmlParser.readText(parser);\n"); |
| } |
| out.print(javaType.getParsingExpression()); |
| out.print("return value;\n" |
| + "}\n" |
| + "return null;\n" |
| + "}\n"); |
| out.println(); |
| } |
| |
| out.printf( |
| "public static %sjava.lang.String readText(%sorg.xmlpull.v1.XmlPullParser parser)" |
| + " throws org.xmlpull.v1.XmlPullParserException, java.io.IOException {\n" |
| + "String result = \"\";\n" |
| + "if (parser.next() == org.xmlpull.v1.XmlPullParser.TEXT) {\n" |
| + " result = parser.getText();\n" |
| + " parser.nextTag();\n" |
| + "}\n" |
| + "return result;\n" |
| + "}\n", getDefaultNullability(Nullability.NULLABLE), |
| getDefaultNullability(Nullability.NON_NULL)); |
| out.println(); |
| |
| out.printf( |
| "public static void skip(%sorg.xmlpull.v1.XmlPullParser parser)" |
| + " throws org.xmlpull.v1.XmlPullParserException, java.io.IOException {\n" |
| + "if (parser.getEventType() != org.xmlpull.v1.XmlPullParser.START_TAG) {\n" |
| + " throw new IllegalStateException();\n" |
| + "}\n" |
| + "int depth = 1;\n" |
| + "while (depth != 0) {\n" |
| + " switch (parser.next()) {\n" |
| + " case org.xmlpull.v1.XmlPullParser.END_TAG:\n" |
| + " depth--;\n" |
| + " break;\n" |
| + " case org.xmlpull.v1.XmlPullParser.START_TAG:\n" |
| + " depth++;\n" |
| + " break;\n" |
| + " }\n" |
| + "}\n" |
| + "}\n", getDefaultNullability(Nullability.NON_NULL)); |
| |
| out.println("}"); |
| } |
| |
| private void printXmlWriter(CodeWriter out) throws JavaCodeGeneratorException { |
| out.printf("package %s;\n", packageName); |
| out.println(); |
| out.println("public class XmlWriter implements java.io.Closeable {"); |
| |
| out.printf("private java.io.PrintWriter out;\n" |
| + "private StringBuilder outBuffer;\n" |
| + "private int indent;\n" |
| + "private boolean startLine;\n\n" |
| + "public XmlWriter(%sjava.io.PrintWriter printWriter) {\n" |
| + " out = printWriter;\n" |
| + " outBuffer = new StringBuilder();\n" |
| + " indent = 0;\n" |
| + " startLine = true;\n" |
| + "}\n\n" |
| + "private void printIndent() {\n" |
| + " assert startLine;\n" |
| + " for (int i = 0; i < indent; ++i) {\n" |
| + " outBuffer.append(\" \");\n" |
| + " }\n" |
| + " startLine = false;\n" |
| + "}\n\n" |
| + "void print(String code) {\n" |
| + " String[] lines = code.split(\"\\n\", -1);\n" |
| + " for (int i = 0; i < lines.length; ++i) {\n" |
| + " if (startLine && !lines[i].isEmpty()) {\n" |
| + " printIndent();\n" |
| + " }\n" |
| + " outBuffer.append(lines[i]);\n" |
| + " if (i + 1 < lines.length) {\n" |
| + " outBuffer.append(\"\\n\");\n" |
| + " startLine = true;\n" |
| + " }\n" |
| + " }\n" |
| + "}\n\n" |
| + "void increaseIndent() {\n" |
| + " ++indent;\n}\n\n" |
| + "void decreaseIndent() {\n" |
| + " --indent;\n" |
| + "}\n\n" |
| + "void printXml() {\n" |
| + " out.print(outBuffer.toString());\n" |
| + "}\n\n" |
| + "@Override\n" |
| + "public void close() {\n" |
| + " if (out != null) {\n" |
| + " out.close();\n" |
| + " }\n" |
| + "}\n\n", getDefaultNullability(Nullability.NON_NULL)); |
| |
| |
| for (XsdElement element : xmlSchema.getElementMap().values()) { |
| JavaType javaType = parseType(element.getType(), element.getName()); |
| String elementName = element.getName(); |
| String VariableName = Utils.toVariableName(elementName); |
| String typeName = javaType instanceof JavaSimpleType ? javaType.getName() : |
| Utils.toClassName(javaType.getName()); |
| out.printf("public static void write(%sXmlWriter out, %s%s %s) " |
| + "throws java.io.IOException {", getDefaultNullability(Nullability.NON_NULL), |
| getDefaultNullability(Nullability.NON_NULL), typeName, VariableName); |
| out.print("\nout.print(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\");\n"); |
| out.printf("if (%s != null) {\n", VariableName); |
| out.printf("%s.write(out, \"%s\");\n}\n", VariableName, elementName); |
| out.print("out.printXml();\n}\n\n"); |
| } |
| out.printf("}\n"); |
| } |
| |
| private void printHexBinaryHelper(CodeWriter out) throws JavaCodeGeneratorException { |
| out.printf("package %s;\n", packageName); |
| out.println(); |
| out.println("public class HexBinaryHelper {"); |
| out.print("public static byte[] hexStringToByteArray(String hexString) {\n" |
| + "if (hexString.length() % 2 != 0) {\n" |
| + "throw new IllegalArgumentException(\"length must be multiple of 2\");\n" |
| + "}\n" |
| + "byte[] outputBytes = new byte[hexString.length() / 2];\n" |
| + "for (int i = 0; i < hexString.length(); i += 2) {\n" |
| + "char c1 = hexString.charAt(i);\n" |
| + "char c2 = hexString.charAt(i + 1);\n" |
| + "outputBytes[i / 2] = (byte) ((Character.digit(c1, 16) << 4)" |
| + " + Character.digit(c2, 16));\n" |
| + "}\n" |
| + "return outputBytes;" |
| + "}\n\n" |
| + "public static String byteArrayToHexString(byte[] b) {\n" |
| + "StringBuffer s = new StringBuffer();\n" |
| + "for (int i = 0; i < b.length; i++) {\n" |
| + "s.append(Integer.toHexString(0x100 + (b[i] & 0xff)).substring(1));\n" |
| + "}\n" |
| + "return s.toString();\n" |
| + "}\n" |
| + "}\n"); |
| } |
| |
| private String getElementName(XsdElement element) { |
| if (element instanceof XsdChoice) { |
| return element.getName() + "_optional"; |
| } else if (element instanceof XsdAll) { |
| return element.getName() + "_all"; |
| } |
| return element.getName(); |
| } |
| |
| private String getFinalString(boolean finalValue) { |
| if (finalValue) { |
| return " final"; |
| } |
| return ""; |
| } |
| |
| private String getDefaultNullability(Nullability nullability) { |
| if (showNullability) { |
| return getNullabilityString(nullability); |
| } |
| return ""; |
| } |
| |
| private String getNullabilityString(Nullability nullability) { |
| if (nullability == Nullability.NON_NULL) { |
| return "@android.annotation.NonNull "; |
| } else if (nullability == Nullability.NULLABLE) { |
| return "@android.annotation.Nullable "; |
| } else if (showNullability) { |
| return "@android.annotation.Nullable "; |
| } |
| return ""; |
| } |
| |
| private String getterName(String type) { |
| if (type.equals("boolean") && booleanGetter) { |
| return "is"; |
| } |
| return "get"; |
| } |
| |
| private void stackComponents(XsdComplexType complexType, List<XsdElement> elements, |
| List<XsdAttribute> attributes) throws JavaCodeGeneratorException { |
| if (complexType.getBase() != null) { |
| QName baseRef = complexType.getBase().getRef(); |
| if (baseRef != null && !baseRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) { |
| XsdType parent = getType(baseRef.getLocalPart()); |
| if (parent instanceof XsdComplexType) { |
| stackComponents((XsdComplexType) parent, elements, attributes); |
| } |
| } |
| } |
| elements.addAll(getAllElements(complexType.getGroup())); |
| elements.addAll(complexType.getElements()); |
| for (XsdAttributeGroup attributeGroup : complexType.getAttributeGroups()) { |
| attributes.addAll(getAllAttributes(resolveAttributeGroup(attributeGroup))); |
| } |
| attributes.addAll(complexType.getAttributes()); |
| } |
| |
| private List<XsdAttribute> getAllAttributes(XsdAttributeGroup attributeGroup) |
| throws JavaCodeGeneratorException { |
| List<XsdAttribute> attributes = new ArrayList<>(); |
| for (XsdAttributeGroup attrGroup : attributeGroup.getAttributeGroups()) { |
| attributes.addAll(getAllAttributes(resolveAttributeGroup(attrGroup))); |
| } |
| attributes.addAll(attributeGroup.getAttributes()); |
| return attributes; |
| } |
| |
| private List<XsdElement> getAllElements(XsdGroup group) throws JavaCodeGeneratorException { |
| List<XsdElement> elements = new ArrayList<>(); |
| if (group == null) { |
| return elements; |
| } |
| elements.addAll(getAllElements(resolveGroup(group))); |
| elements.addAll(group.getElements()); |
| return elements; |
| } |
| |
| private String getBaseName(XsdComplexType complexType) throws JavaCodeGeneratorException { |
| if (complexType.getBase() == null) return null; |
| if (complexType.getBase().getRef().getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) { |
| return null; |
| } |
| XsdType base = getType(complexType.getBase().getRef().getLocalPart()); |
| if (base instanceof XsdComplexType) { |
| return Utils.toClassName(base.getName()); |
| } |
| return null; |
| } |
| |
| private JavaSimpleType getValueType(XsdSimpleContent simpleContent, boolean traverse) |
| throws JavaCodeGeneratorException { |
| assert simpleContent.getBase() != null; |
| QName baseRef = simpleContent.getBase().getRef(); |
| assert baseRef != null; |
| if (baseRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) { |
| return predefinedType(baseRef.getLocalPart()); |
| } else { |
| XsdType parent = getType(baseRef.getLocalPart()); |
| if (parent instanceof XsdSimpleType) { |
| return parseSimpleTypeReference(baseRef, false); |
| } |
| if (!traverse) return null; |
| if (parent instanceof XsdSimpleContent) { |
| return getValueType((XsdSimpleContent) parent, true); |
| } else { |
| throw new JavaCodeGeneratorException( |
| String.format("base not simple : %s", baseRef.getLocalPart())); |
| } |
| } |
| } |
| |
| private JavaType parseType(XsdType type, String defaultName) throws JavaCodeGeneratorException { |
| if (type.getRef() != null) { |
| String name = type.getRef().getLocalPart(); |
| if (type.getRef().getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) { |
| return predefinedType(name); |
| } else { |
| XsdType typeValue = getType(name); |
| if (typeValue instanceof XsdSimpleType) { |
| return parseSimpleTypeReference(type.getRef(), false); |
| } |
| return parseType(typeValue, name); |
| } |
| } |
| if (type instanceof XsdComplexType) { |
| return new JavaComplexType(Utils.toClassName(defaultName)); |
| } else if (type instanceof XsdSimpleType) { |
| return parseSimpleTypeValue((XsdSimpleType) type, false); |
| } else { |
| throw new JavaCodeGeneratorException( |
| String.format("unknown type name : %s", defaultName)); |
| } |
| } |
| |
| private JavaSimpleType parseSimpleType(XsdType type, boolean traverse) |
| throws JavaCodeGeneratorException { |
| if (type.getRef() != null) { |
| return parseSimpleTypeReference(type.getRef(), traverse); |
| } else { |
| return parseSimpleTypeValue((XsdSimpleType) type, traverse); |
| } |
| } |
| |
| private JavaSimpleType parseSimpleTypeReference(QName typeRef, boolean traverse) |
| throws JavaCodeGeneratorException { |
| assert typeRef != null; |
| String typeName = typeRef.getLocalPart(); |
| if (typeRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) { |
| return predefinedType(typeName); |
| } |
| if (javaSimpleTypeMap.containsKey(typeName)) { |
| return javaSimpleTypeMap.get(typeName); |
| } else if (traverse) { |
| XsdSimpleType simpleType = getSimpleType(typeName); |
| JavaSimpleType ret = parseSimpleTypeValue(simpleType, true); |
| javaSimpleTypeMap.put(typeName, ret); |
| return ret; |
| } else { |
| throw new JavaCodeGeneratorException(String.format("unknown type name : %s", typeName)); |
| } |
| } |
| |
| private JavaSimpleType parseSimpleTypeValue(XsdSimpleType simpleType, boolean traverse) |
| throws JavaCodeGeneratorException { |
| if (simpleType instanceof XsdList) { |
| XsdList list = (XsdList) simpleType; |
| return parseSimpleType(list.getItemType(), traverse).newListType(); |
| } else if (simpleType instanceof XsdRestriction) { |
| // we don't consider any restrictions. |
| XsdRestriction restriction = (XsdRestriction) simpleType; |
| if (restriction.getEnums() != null) { |
| String name = Utils.toClassName(restriction.getName()); |
| return new JavaSimpleType(name, name, name + ".fromString(%s)", "%s.toString()", |
| false); |
| } |
| return parseSimpleType(restriction.getBase(), traverse); |
| } else if (simpleType instanceof XsdUnion) { |
| // unions are almost always interpreted as java.lang.String |
| // Exceptionally, if any of member types of union are 'list', then we interpret it as |
| // List<String> |
| XsdUnion union = (XsdUnion) simpleType; |
| for (XsdType memberType : union.getMemberTypes()) { |
| if (parseSimpleType(memberType, traverse).isList()) { |
| return new JavaSimpleType("java.lang.String", "%s", true); |
| } |
| } |
| return new JavaSimpleType("java.lang.String", "%s", false); |
| } else { |
| // unreachable |
| throw new IllegalStateException("unknown simple type"); |
| } |
| } |
| |
| private XsdElement resolveElement(XsdElement element) throws JavaCodeGeneratorException { |
| if (element.getRef() == null) return element; |
| String name = element.getRef().getLocalPart(); |
| XsdElement ret = xmlSchema.getElementMap().get(name); |
| if (ret != null) return ret; |
| throw new JavaCodeGeneratorException(String.format("no element named : %s", name)); |
| } |
| |
| private XsdGroup resolveGroup(XsdGroup group) throws JavaCodeGeneratorException { |
| if (group.getRef() == null) return null; |
| String name = group.getRef().getLocalPart(); |
| XsdGroup ret = xmlSchema.getGroupMap().get(name); |
| if (ret != null) return ret; |
| throw new JavaCodeGeneratorException(String.format("no group named : %s", name)); |
| } |
| |
| private XsdAttribute resolveAttribute(XsdAttribute attribute) |
| throws JavaCodeGeneratorException { |
| if (attribute.getRef() == null) return attribute; |
| String name = attribute.getRef().getLocalPart(); |
| XsdAttribute ret = xmlSchema.getAttributeMap().get(name); |
| if (ret != null) return ret; |
| throw new JavaCodeGeneratorException(String.format("no attribute named : %s", name)); |
| } |
| |
| private XsdAttributeGroup resolveAttributeGroup(XsdAttributeGroup attributeGroup) |
| throws JavaCodeGeneratorException { |
| if (attributeGroup.getRef() == null) return attributeGroup; |
| String name = attributeGroup.getRef().getLocalPart(); |
| XsdAttributeGroup ret = xmlSchema.getAttributeGroupMap().get(name); |
| if (ret != null) return ret; |
| throw new JavaCodeGeneratorException(String.format("no attribute group named : %s", name)); |
| } |
| |
| private XsdType getType(String name) throws JavaCodeGeneratorException { |
| XsdType type = xmlSchema.getTypeMap().get(name); |
| if (type != null) return type; |
| throw new JavaCodeGeneratorException(String.format("no type named : %s", name)); |
| } |
| |
| private XsdSimpleType getSimpleType(String name) throws JavaCodeGeneratorException { |
| XsdType type = getType(name); |
| if (type instanceof XsdSimpleType) return (XsdSimpleType) type; |
| throw new JavaCodeGeneratorException(String.format("not a simple type : %s", name)); |
| } |
| |
| private JavaSimpleType predefinedType(String name) throws JavaCodeGeneratorException { |
| switch (name) { |
| case "string": |
| case "token": |
| case "normalizedString": |
| case "language": |
| case "ENTITY": |
| case "ID": |
| case "Name": |
| case "NCName": |
| case "NMTOKEN": |
| case "anyURI": |
| case "anyType": |
| case "QName": |
| case "NOTATION": |
| case "IDREF": |
| return new JavaSimpleType("java.lang.String", "%s", false); |
| case "ENTITIES": |
| case "NMTOKENS": |
| case "IDREFS": |
| return new JavaSimpleType("java.lang.String", "%s", true); |
| case "date": |
| case "dateTime": |
| case "time": |
| case "gDay": |
| case "gMonth": |
| case "gYear": |
| case "gMonthDay": |
| case "gYearMonth": |
| return new JavaSimpleType("javax.xml.datatype.XMLGregorianCalendar", |
| "javax.xml.datatype.XMLGregorianCalendar", |
| "javax.xml.datatype.DatatypeFactory.newInstance()" |
| + ".newXMLGregorianCalendar(%s)", |
| "%s.toString()", false); |
| case "duration": |
| return new JavaSimpleType("javax.xml.datatype.Duration", |
| "javax.xml.datatype.Duration", |
| "javax.xml.datatype.DatatypeFactory.newInstance().newDuration(%s)", |
| "%s.toString()", false); |
| case "decimal": |
| return new JavaSimpleType("java.math.BigDecimal", "java.math.BigDecimal", |
| "new java.math.BigDecimal(%s)", "%s.toString()", false); |
| case "integer": |
| case "negativeInteger": |
| case "nonNegativeInteger": |
| case "positiveInteger": |
| case "nonPositiveInteger": |
| case "unsignedLong": |
| return new JavaSimpleType("java.math.BigInteger", "java.math.BigInteger", |
| "new java.math.BigInteger(%s)", "%s.toString()", false); |
| case "long": |
| case "unsignedInt": |
| return new JavaSimpleType("long", "java.lang.Long", "Long.parseLong(%s)", |
| "Long.toString(%s)", false); |
| case "int": |
| case "unsignedShort": |
| return new JavaSimpleType("int", "java.lang.Integer", "Integer.parseInt(%s)", |
| "Integer.toString(%s)", false); |
| case "short": |
| case "unsignedByte": |
| return new JavaSimpleType("short", "java.lang.Short", "Short.parseShort(%s)", |
| "Short.toString(%s)", false); |
| case "byte": |
| return new JavaSimpleType("byte", "java.lang.Byte", "Byte.parseByte(%s)", |
| "Byte.toString(%s)",false); |
| case "boolean": |
| return new JavaSimpleType("boolean", "java.lang.Boolean", |
| "Boolean.parseBoolean(%s)", "Boolean.toString(%s)", false); |
| case "double": |
| return new JavaSimpleType("double", "java.lang.Double", "Double.parseDouble(%s)", |
| "Double.toString(%s)", false); |
| case "float": |
| return new JavaSimpleType("float", "java.lang.Float", "Float.parseFloat(%s)", |
| "Float.toString(%s)", false); |
| case "base64Binary": |
| return new JavaSimpleType("byte[]", "byte[]", |
| "java.util.Base64.getDecoder().decode(%s)", |
| "java.util.Base64.getEncoder().encodeToString(%s)", |
| false); |
| case "hexBinary": |
| useHexBinary = true; |
| return new JavaSimpleType("byte[]", "byte[]", |
| "HexBinaryHelper.hexStringToByteArray(%s)", |
| "HexBinaryHelper.byteArrayToHexString(%s)", |
| false); |
| } |
| throw new JavaCodeGeneratorException("unknown xsd predefined type : " + name); |
| } |
| } |