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.java; 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.ArrayList; 27 import java.util.HashMap; 28 import java.util.HashSet; 29 import java.util.List; 30 import java.util.Map; 31 import java.util.Set; 32 33 import javax.xml.namespace.QName; 34 35 public class JavaCodeGenerator { 36 private XmlSchema xmlSchema; 37 private String packageName; 38 private Map<String, JavaSimpleType> javaSimpleTypeMap; 39 private boolean writer; 40 private boolean showNullability; 41 private boolean generateHasMethod; 42 private boolean useHexBinary; 43 private boolean booleanGetter; 44 JavaCodeGenerator(XmlSchema xmlSchema, String packageName, boolean writer, boolean showNullability, boolean generateHasMethod, boolean booleanGetter)45 public JavaCodeGenerator(XmlSchema xmlSchema, String packageName, boolean writer, 46 boolean showNullability, boolean generateHasMethod, boolean booleanGetter) 47 throws JavaCodeGeneratorException { 48 this.xmlSchema = xmlSchema; 49 this.packageName = packageName; 50 this.writer = writer; 51 this.showNullability = showNullability; 52 this.generateHasMethod = generateHasMethod; 53 this.booleanGetter = booleanGetter; 54 useHexBinary = false; 55 56 // class naming validation 57 { 58 Set<String> nameSet = new HashSet<>(); 59 nameSet.add("XmlParser"); 60 for (XsdType type : xmlSchema.getTypeMap().values()) { 61 if ((type instanceof XsdComplexType) || (type instanceof XsdRestriction && 62 ((XsdRestriction)type).getEnums() != null)) { 63 String name = Utils.toClassName(type.getName()); 64 if (nameSet.contains(name)) { 65 throw new JavaCodeGeneratorException( 66 String.format("duplicate class name : %s", name)); 67 } 68 nameSet.add(name); 69 } 70 } 71 for (XsdElement element : xmlSchema.getElementMap().values()) { 72 XsdType type = element.getType(); 73 if (type.getRef() == null && type instanceof XsdComplexType) { 74 String name = Utils.toClassName(element.getName()); 75 if (nameSet.contains(name)) { 76 throw new JavaCodeGeneratorException( 77 String.format("duplicate class name : %s", name)); 78 } 79 nameSet.add(name); 80 } 81 } 82 } 83 84 javaSimpleTypeMap = new HashMap<>(); 85 for (XsdType type : xmlSchema.getTypeMap().values()) { 86 if (type instanceof XsdSimpleType) { 87 XsdType refType = new XsdType(null, new QName(type.getName())); 88 parseSimpleType(refType, true); 89 } 90 } 91 } 92 print(FileSystem fs)93 public void print(FileSystem fs) 94 throws JavaCodeGeneratorException, IOException { 95 for (XsdType type : xmlSchema.getTypeMap().values()) { 96 if (type instanceof XsdComplexType) { 97 String name = Utils.toClassName(type.getName()); 98 XsdComplexType complexType = (XsdComplexType) type; 99 try (CodeWriter out = new CodeWriter(fs.getPrintWriter(name + ".java"))) { 100 out.printf("package %s;\n\n", packageName); 101 printClass(out, name, complexType, ""); 102 } 103 } else if (type instanceof XsdRestriction && 104 ((XsdRestriction)type).getEnums() != null) { 105 String name = Utils.toClassName(type.getName()); 106 XsdRestriction restrictionType = (XsdRestriction) type; 107 try (CodeWriter out = new CodeWriter(fs.getPrintWriter(name + ".java"))) { 108 out.printf("package %s;\n\n", packageName); 109 printEnumClass(out, name, restrictionType); 110 } 111 } 112 } 113 for (XsdElement element : xmlSchema.getElementMap().values()) { 114 XsdType type = element.getType(); 115 if (type.getRef() == null && type instanceof XsdComplexType) { 116 String name = Utils.toClassName(element.getName()); 117 XsdComplexType complexType = (XsdComplexType) type; 118 try (CodeWriter out = new CodeWriter(fs.getPrintWriter(name + ".java"))) { 119 out.printf("package %s;\n\n", packageName); 120 printClass(out, name, complexType, ""); 121 } 122 } 123 } 124 try (CodeWriter out = new CodeWriter(fs.getPrintWriter("XmlParser.java"))) { 125 printXmlParser(out); 126 } 127 if (writer) { 128 try (CodeWriter out = new CodeWriter(fs.getPrintWriter("XmlWriter.java"))) { 129 printXmlWriter(out); 130 } 131 } 132 if (useHexBinary) { 133 try (CodeWriter out = new CodeWriter(fs.getPrintWriter("HexBinaryHelper.java"))) { 134 printHexBinaryHelper(out); 135 } 136 } 137 } 138 printEnumClass(CodeWriter out, String name, XsdRestriction restrictionType)139 private void printEnumClass(CodeWriter out, String name, XsdRestriction restrictionType) 140 throws JavaCodeGeneratorException { 141 if (restrictionType.isDeprecated()) { 142 out.printf("@java.lang.Deprecated\n"); 143 } 144 out.printf("public enum %s {", name); 145 List<XsdEnumeration> enums = restrictionType.getEnums(); 146 147 for (XsdEnumeration tag : enums) { 148 if (tag.isDeprecated()) { 149 out.printf("@java.lang.Deprecated\n"); 150 } 151 String value = tag.getValue(); 152 out.printf("\n%s(\"%s\"),", Utils.toEnumName(value), value); 153 } 154 out.printf(";\n\n"); 155 out.printf("private final String rawName;\n\n"); 156 out.printf("%s(%sString rawName) {\n" 157 + "this.rawName = rawName;\n" 158 + "}\n\n", name, getDefaultNullability(Nullability.NON_NULL)); 159 out.printf("public %sString getRawName() {\n" 160 + "return rawName;\n" 161 + "}\n\n", getDefaultNullability(Nullability.NON_NULL)); 162 163 out.printf("static %s%s fromString(%sString rawString) {\n" 164 + "for (%s _f : values()) {\n" 165 + "if (_f.getRawName().equals(rawString)) {\n" 166 + "return _f;\n" 167 + "}\n" 168 + "}\n" 169 + "throw new IllegalArgumentException(rawString);\n" 170 + "}\n\n", getDefaultNullability(Nullability.NULLABLE), name, 171 getDefaultNullability(Nullability.NON_NULL), name); 172 173 if (writer) { 174 out.printf("@Override\n" 175 + "public %sString toString() {\n" 176 + "return rawName;\n" 177 + "}\n", getDefaultNullability(Nullability.NON_NULL)); 178 } 179 out.println("}"); 180 } 181 printClass(CodeWriter out, String name, XsdComplexType complexType, String nameScope)182 private void printClass(CodeWriter out, String name, XsdComplexType complexType, 183 String nameScope) throws JavaCodeGeneratorException { 184 assert name != null; 185 // need element, attribute name duplicate validation? 186 187 String baseName = getBaseName(complexType); 188 JavaSimpleType valueType = (complexType instanceof XsdSimpleContent) ? 189 getValueType((XsdSimpleContent) complexType, false) : null; 190 191 String finalString = getFinalString(complexType.isFinalValue()); 192 if (complexType.isDeprecated()) { 193 out.printf("@java.lang.Deprecated\n"); 194 } 195 if (nameScope.isEmpty()) { 196 out.printf("public%s class %s ", finalString, name); 197 } else { 198 out.printf("public%s static class %s ", finalString, name); 199 } 200 if (baseName != null) { 201 out.printf("extends %s {\n", baseName); 202 } else { 203 out.println("{"); 204 } 205 206 // parse types for elements and attributes 207 List<JavaType> elementTypes = new ArrayList<>(); 208 List<XsdElement> elements = new ArrayList<>(); 209 elements.addAll(getAllElements(complexType.getGroup())); 210 elements.addAll(complexType.getElements()); 211 212 for (XsdElement element : elements) { 213 JavaType javaType; 214 XsdElement elementValue = resolveElement(element); 215 if (element.getRef() == null && element.getType().getRef() == null 216 && element.getType() instanceof XsdComplexType) { 217 // print inner class for anonymous types 218 String innerName = Utils.toClassName(getElementName(element)); 219 XsdComplexType innerType = (XsdComplexType) element.getType(); 220 String innerNameScope = nameScope + name + "."; 221 printClass(out, innerName, innerType, innerNameScope); 222 out.println(); 223 javaType = new JavaComplexType(innerNameScope + innerName); 224 } else { 225 javaType = parseType(elementValue.getType(), getElementName(elementValue)); 226 } 227 elementTypes.add(javaType); 228 } 229 List<JavaSimpleType> attributeTypes = new ArrayList<>(); 230 List<XsdAttribute> attributes = new ArrayList<>(); 231 for (XsdAttributeGroup attributeGroup : complexType.getAttributeGroups()) { 232 attributes.addAll(getAllAttributes(resolveAttributeGroup(attributeGroup))); 233 } 234 attributes.addAll(complexType.getAttributes()); 235 236 for (XsdAttribute attribute : attributes) { 237 XsdType type = resolveAttribute(attribute).getType(); 238 attributeTypes.add(parseSimpleType(type, false)); 239 } 240 241 // print member variables 242 for (int i = 0; i < elementTypes.size(); ++i) { 243 JavaType type = elementTypes.get(i); 244 XsdElement element = elements.get(i); 245 XsdElement elementValue = resolveElement(element); 246 String typeName = element.isMultiple() ? String.format("java.util.List<%s>", 247 type.getNullableName()) : type.getNullableName(); 248 out.printf("%sprivate %s %s;\n", getNullabilityString(element.getNullability()), 249 typeName, Utils.toVariableName(getElementName(elementValue))); 250 } 251 for (int i = 0; i < attributeTypes.size(); ++i) { 252 JavaType type = attributeTypes.get(i); 253 XsdAttribute attribute = resolveAttribute(attributes.get(i)); 254 out.printf("%sprivate %s %s;\n", getNullabilityString(attribute.getNullability()), 255 type.getNullableName(), Utils.toVariableName(attribute.getName())); 256 } 257 if (valueType != null) { 258 out.printf("private %s value;\n", valueType.getName()); 259 } 260 261 // print getters and setters 262 for (int i = 0; i < elementTypes.size(); ++i) { 263 JavaType type = elementTypes.get(i); 264 XsdElement element = elements.get(i); 265 XsdElement elementValue = resolveElement(element); 266 printGetterAndSetter(out, type, Utils.toVariableName(getElementName(elementValue)), 267 element.isMultiple(), element); 268 } 269 for (int i = 0; i < attributeTypes.size(); ++i) { 270 JavaType type = attributeTypes.get(i); 271 XsdAttribute attribute = resolveAttribute(attributes.get(i)); 272 printGetterAndSetter(out, type, Utils.toVariableName(attribute.getName()), false, 273 attribute); 274 } 275 if (valueType != null) { 276 printGetterAndSetter(out, valueType, "value", false, null); 277 } 278 279 out.println(); 280 printParser(out, nameScope + name, complexType); 281 if (writer) { 282 printWriter(out, name, complexType); 283 } 284 285 out.println("}"); 286 } 287 printParser(CodeWriter out, String name, XsdComplexType complexType)288 private void printParser(CodeWriter out, String name, XsdComplexType complexType) 289 throws JavaCodeGeneratorException { 290 JavaSimpleType baseValueType = (complexType instanceof XsdSimpleContent) ? 291 getValueType((XsdSimpleContent) complexType, true) : null; 292 List<XsdElement> allElements = new ArrayList<>(); 293 List<XsdAttribute> allAttributes = new ArrayList<>(); 294 stackComponents(complexType, allElements, allAttributes); 295 296 // parse types for elements and attributes 297 List<JavaType> allElementTypes = new ArrayList<>(); 298 for (XsdElement element : allElements) { 299 XsdElement elementValue = resolveElement(element); 300 JavaType javaType = parseType(elementValue.getType(), elementValue.getName()); 301 allElementTypes.add(javaType); 302 } 303 List<JavaSimpleType> allAttributeTypes = new ArrayList<>(); 304 for (XsdAttribute attribute : allAttributes) { 305 XsdType type = resolveAttribute(attribute).getType(); 306 allAttributeTypes.add(parseSimpleType(type, false)); 307 } 308 309 out.printf("static %s%s read(%sorg.xmlpull.v1.XmlPullParser parser) " + 310 "throws org.xmlpull.v1.XmlPullParserException, java.io.IOException, " + 311 "javax.xml.datatype.DatatypeConfigurationException {\n", 312 getDefaultNullability(Nullability.NON_NULL), name, 313 getDefaultNullability(Nullability.NON_NULL)); 314 315 out.printf("%s instance = new %s();\n" 316 + "String raw = null;\n", name, name); 317 for (int i = 0; i < allAttributes.size(); ++i) { 318 JavaType type = allAttributeTypes.get(i); 319 XsdAttribute attribute = resolveAttribute(allAttributes.get(i)); 320 String variableName = Utils.toVariableName(attribute.getName()); 321 out.printf("raw = parser.getAttributeValue(null, \"%s\");\n" 322 + "if (raw != null) {\n", attribute.getName()); 323 out.print(type.getParsingExpression()); 324 out.printf("instance.set%s(value);\n" 325 + "}\n", Utils.capitalize(variableName)); 326 } 327 328 if (baseValueType != null) { 329 out.print("raw = XmlParser.readText(parser);\n" 330 + "if (raw != null) {\n"); 331 out.print(baseValueType.getParsingExpression()); 332 out.print("instance.setValue(value);\n" 333 + "}\n"); 334 } else if (!allElements.isEmpty()) { 335 out.print("int outerDepth = parser.getDepth();\n" 336 + "int type;\n" 337 + "while ((type=parser.next()) != org.xmlpull.v1.XmlPullParser.END_DOCUMENT\n" 338 + " && type != org.xmlpull.v1.XmlPullParser.END_TAG) {\n" 339 + "if (parser.getEventType() != org.xmlpull.v1.XmlPullParser.START_TAG) " 340 + "continue;\n" 341 + "String tagName = parser.getName();\n"); 342 for (int i = 0; i < allElements.size(); ++i) { 343 JavaType type = allElementTypes.get(i); 344 XsdElement element = allElements.get(i); 345 XsdElement elementValue = resolveElement(element); 346 String variableName = Utils.toVariableName(getElementName(elementValue)); 347 out.printf("if (tagName.equals(\"%s\")) {\n", elementValue.getName()); 348 if (type instanceof JavaSimpleType) { 349 out.print("raw = XmlParser.readText(parser);\n"); 350 } 351 out.print(type.getParsingExpression()); 352 if (element.isMultiple()) { 353 out.printf("instance.get%s().add(value);\n", 354 Utils.capitalize(variableName)); 355 } else { 356 out.printf("instance.set%s(value);\n", 357 Utils.capitalize(variableName)); 358 } 359 out.printf("} else "); 360 } 361 out.print("{\n" 362 + "XmlParser.skip(parser);\n" 363 + "}\n" 364 + "}\n"); 365 out.printf("if (type != org.xmlpull.v1.XmlPullParser.END_TAG) {\n" 366 + "throw new javax.xml.datatype.DatatypeConfigurationException(\"%s is not closed\");\n" 367 + "}\n", name); 368 } else { 369 out.print("XmlParser.skip(parser);\n"); 370 } 371 out.print("return instance;\n" 372 + "}\n"); 373 } 374 printWriter(CodeWriter out, String name, XsdComplexType complexType)375 private void printWriter(CodeWriter out, String name, XsdComplexType complexType) 376 throws JavaCodeGeneratorException { 377 JavaSimpleType baseValueType = (complexType instanceof XsdSimpleContent) ? 378 getValueType((XsdSimpleContent) complexType, true) : null; 379 List<XsdElement> allElements = new ArrayList<>(); 380 List<XsdAttribute> allAttributes = new ArrayList<>(); 381 stackComponents(complexType, allElements, allAttributes); 382 383 // parse types for elements and attributes 384 List<JavaType> allElementTypes = new ArrayList<>(); 385 for (XsdElement element : allElements) { 386 XsdElement elementValue = resolveElement(element); 387 JavaType javaType = parseType(elementValue.getType(), elementValue.getName()); 388 allElementTypes.add(javaType); 389 } 390 List<JavaSimpleType> allAttributeTypes = new ArrayList<>(); 391 for (XsdAttribute attribute : allAttributes) { 392 XsdType type = resolveAttribute(attribute).getType(); 393 allAttributeTypes.add(parseSimpleType(type, false)); 394 } 395 396 out.printf("\nvoid write(%sXmlWriter out, %sString name) " + 397 "throws java.io.IOException {\n", getDefaultNullability(Nullability.NON_NULL), 398 getDefaultNullability(Nullability.NON_NULL)); 399 400 out.print("out.print(\"<\" + name);\n"); 401 for (int i = 0; i < allAttributes.size(); ++i) { 402 JavaType type = allAttributeTypes.get(i); 403 boolean isList = allAttributeTypes.get(i).isList(); 404 XsdAttribute attribute = resolveAttribute(allAttributes.get(i)); 405 String variableName = Utils.toVariableName(attribute.getName()); 406 out.printf("if (has%s()) {\n", Utils.capitalize(variableName)); 407 out.printf("out.print(\" %s=\\\"\");\n", attribute.getName()); 408 out.print(type.getWritingExpression(String.format("%s%s()", 409 getterName(type.getName()), Utils.capitalize(variableName)), 410 attribute.getName())); 411 out.printf("out.print(\"\\\"\");\n}\n"); 412 } 413 out.printf("out.print(\">\\n\");\n"); 414 415 if (!allElements.isEmpty()) { 416 out.printf("out.increaseIndent();\n"); 417 for (int i = 0; i < allElements.size(); ++i) { 418 JavaType type = allElementTypes.get(i); 419 XsdElement element = allElements.get(i); 420 XsdElement elementValue = resolveElement(element); 421 String elementName = getElementName(elementValue); 422 String variableName = Utils.toVariableName(elementName); 423 424 if (element.isMultiple()) { 425 out.printf("for (%s value : get%s()) {\n", type.getName(), 426 Utils.capitalize(variableName)); 427 if (type instanceof JavaSimpleType) { 428 out.printf("out.print(\"<%s>\");\n", elementValue.getName()); 429 } 430 out.print(type.getWritingExpression("value", elementValue.getName())); 431 if (type instanceof JavaSimpleType) { 432 out.printf("out.print(\"</%s>\\n\");\n", elementValue.getName()); 433 } 434 out.print("}\n"); 435 } else { 436 out.printf("if (has%s()) {\n", Utils.capitalize(variableName)); 437 if (type instanceof JavaSimpleType) { 438 out.printf("out.print(\"<%s>\");\n", elementValue.getName()); 439 } 440 out.print(type.getWritingExpression(String.format("%s%s()", 441 getterName(type.getName()), Utils.capitalize(variableName)), 442 elementValue.getName())); 443 if (type instanceof JavaSimpleType) { 444 out.printf("out.print(\"</%s>\\n\");\n", elementValue.getName()); 445 } 446 out.printf("}\n"); 447 } 448 449 } 450 out.printf("out.decreaseIndent();\n"); 451 } 452 out.print("out.print(\"</\" + name + \">\\n\");\n"); 453 out.print("}\n"); 454 } 455 printGetterAndSetter(CodeWriter out, JavaType type, String variableName, boolean isMultiple, XsdTag tag)456 private void printGetterAndSetter(CodeWriter out, JavaType type, String variableName, 457 boolean isMultiple, XsdTag tag) { 458 String typeName = isMultiple ? String.format("java.util.List<%s>", type.getNullableName()) 459 : type.getName(); 460 boolean deprecated = tag == null ? false : tag.isDeprecated(); 461 boolean finalValue = tag == null ? false : tag.isFinalValue(); 462 Nullability nullability = tag == null ? Nullability.UNKNOWN : tag.getNullability(); 463 out.println(); 464 if (deprecated) { 465 out.printf("@java.lang.Deprecated\n"); 466 } 467 out.printf("public%s %s%s %s%s() {\n", getFinalString(finalValue), 468 getNullabilityString(nullability), typeName, getterName(typeName), 469 Utils.capitalize(variableName)); 470 if ((type instanceof JavaSimpleType && ((JavaSimpleType)type).isList()) || isMultiple) { 471 out.printf("if (%s == null) {\n" 472 + "%s = new java.util.ArrayList<>();\n" 473 + "}\n", variableName, variableName); 474 } else if (type.isPrimitiveType()) { 475 out.printf("if (%s == null) {\n", variableName); 476 if (typeName.equals("boolean")) { 477 out.printf("return false;\n}\n", variableName); 478 } else { 479 out.printf("return (%s)0;\n}\n", typeName); 480 } 481 } 482 out.printf("return %s;\n" 483 + "}\n", variableName); 484 485 if (isMultiple) return; 486 out.println(); 487 out.printf("%sboolean has%s() {\n" 488 + "if (%s == null) {\n" 489 + "return false;\n" 490 + "}\n" 491 + "return true;\n}\n\n", 492 generateHasMethod ? "public " : "", 493 Utils.capitalize(variableName), variableName); 494 if (deprecated) { 495 out.printf("@java.lang.Deprecated\n"); 496 } 497 out.printf("public%s void set%s(%s%s %s) {\n" 498 + "this.%s = %s;\n" 499 + "}\n", 500 getFinalString(finalValue), Utils.capitalize(variableName), 501 getNullabilityString(nullability), typeName, variableName, 502 variableName, variableName); 503 } 504 printXmlParser(CodeWriter out)505 private void printXmlParser(CodeWriter out) throws JavaCodeGeneratorException { 506 out.printf("package %s;\n", packageName); 507 out.println(); 508 out.println("public class XmlParser {"); 509 510 boolean isMultiRootElement = xmlSchema.getElementMap().values().size() > 1; 511 for (XsdElement element : xmlSchema.getElementMap().values()) { 512 JavaType javaType = parseType(element.getType(), element.getName()); 513 out.printf("public static %s%s read%s(%sjava.io.InputStream in)" 514 + " throws org.xmlpull.v1.XmlPullParserException, java.io.IOException, " 515 + "javax.xml.datatype.DatatypeConfigurationException {\n" 516 + "org.xmlpull.v1.XmlPullParser parser = org.xmlpull.v1.XmlPullParserFactory" 517 + ".newInstance().newPullParser();\n" 518 + "parser.setFeature(org.xmlpull.v1.XmlPullParser.FEATURE_PROCESS_NAMESPACES, " 519 + "true);\n" 520 + "parser.setInput(in, null);\n" 521 + "parser.nextTag();\n" 522 + "String tagName = parser.getName();\n" 523 + "String raw = null;\n", getDefaultNullability(Nullability.NULLABLE), 524 javaType.getName(), isMultiRootElement ? Utils.capitalize(javaType.getName()) : "", 525 getDefaultNullability(Nullability.NON_NULL)); 526 out.printf("if (tagName.equals(\"%s\")) {\n", element.getName()); 527 if (javaType instanceof JavaSimpleType) { 528 out.print("raw = XmlParser.readText(parser);\n"); 529 } 530 out.print(javaType.getParsingExpression()); 531 out.print("return value;\n" 532 + "}\n" 533 + "return null;\n" 534 + "}\n"); 535 out.println(); 536 } 537 538 out.printf( 539 "public static %sjava.lang.String readText(%sorg.xmlpull.v1.XmlPullParser parser)" 540 + " throws org.xmlpull.v1.XmlPullParserException, java.io.IOException {\n" 541 + "String result = \"\";\n" 542 + "if (parser.next() == org.xmlpull.v1.XmlPullParser.TEXT) {\n" 543 + " result = parser.getText();\n" 544 + " parser.nextTag();\n" 545 + "}\n" 546 + "return result;\n" 547 + "}\n", getDefaultNullability(Nullability.NULLABLE), 548 getDefaultNullability(Nullability.NON_NULL)); 549 out.println(); 550 551 out.printf( 552 "public static void skip(%sorg.xmlpull.v1.XmlPullParser parser)" 553 + " throws org.xmlpull.v1.XmlPullParserException, java.io.IOException {\n" 554 + "if (parser.getEventType() != org.xmlpull.v1.XmlPullParser.START_TAG) {\n" 555 + " throw new IllegalStateException();\n" 556 + "}\n" 557 + "int depth = 1;\n" 558 + "while (depth != 0) {\n" 559 + " switch (parser.next()) {\n" 560 + " case org.xmlpull.v1.XmlPullParser.END_TAG:\n" 561 + " depth--;\n" 562 + " break;\n" 563 + " case org.xmlpull.v1.XmlPullParser.START_TAG:\n" 564 + " depth++;\n" 565 + " break;\n" 566 + " }\n" 567 + "}\n" 568 + "}\n", getDefaultNullability(Nullability.NON_NULL)); 569 570 out.println("}"); 571 } 572 printXmlWriter(CodeWriter out)573 private void printXmlWriter(CodeWriter out) throws JavaCodeGeneratorException { 574 out.printf("package %s;\n", packageName); 575 out.println(); 576 out.println("public class XmlWriter implements java.io.Closeable {"); 577 578 out.printf("private java.io.PrintWriter out;\n" 579 + "private StringBuilder outBuffer;\n" 580 + "private int indent;\n" 581 + "private boolean startLine;\n\n" 582 + "public XmlWriter(%sjava.io.PrintWriter printWriter) {\n" 583 + " out = printWriter;\n" 584 + " outBuffer = new StringBuilder();\n" 585 + " indent = 0;\n" 586 + " startLine = true;\n" 587 + "}\n\n" 588 + "private void printIndent() {\n" 589 + " assert startLine;\n" 590 + " for (int i = 0; i < indent; ++i) {\n" 591 + " outBuffer.append(\" \");\n" 592 + " }\n" 593 + " startLine = false;\n" 594 + "}\n\n" 595 + "void print(String code) {\n" 596 + " String[] lines = code.split(\"\\n\", -1);\n" 597 + " for (int i = 0; i < lines.length; ++i) {\n" 598 + " if (startLine && !lines[i].isEmpty()) {\n" 599 + " printIndent();\n" 600 + " }\n" 601 + " outBuffer.append(lines[i]);\n" 602 + " if (i + 1 < lines.length) {\n" 603 + " outBuffer.append(\"\\n\");\n" 604 + " startLine = true;\n" 605 + " }\n" 606 + " }\n" 607 + "}\n\n" 608 + "void increaseIndent() {\n" 609 + " ++indent;\n}\n\n" 610 + "void decreaseIndent() {\n" 611 + " --indent;\n" 612 + "}\n\n" 613 + "void printXml() {\n" 614 + " out.print(outBuffer.toString());\n" 615 + "}\n\n" 616 + "@Override\n" 617 + "public void close() {\n" 618 + " if (out != null) {\n" 619 + " out.close();\n" 620 + " }\n" 621 + "}\n\n", getDefaultNullability(Nullability.NON_NULL)); 622 623 624 for (XsdElement element : xmlSchema.getElementMap().values()) { 625 JavaType javaType = parseType(element.getType(), element.getName()); 626 String elementName = element.getName(); 627 String VariableName = Utils.toVariableName(elementName); 628 String typeName = javaType instanceof JavaSimpleType ? javaType.getName() : 629 Utils.toClassName(javaType.getName()); 630 out.printf("public static void write(%sXmlWriter out, %s%s %s) " 631 + "throws java.io.IOException {", getDefaultNullability(Nullability.NON_NULL), 632 getDefaultNullability(Nullability.NON_NULL), typeName, VariableName); 633 out.print("\nout.print(\"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?>\\n\");\n"); 634 out.printf("if (%s != null) {\n", VariableName); 635 out.printf("%s.write(out, \"%s\");\n}\n", VariableName, elementName); 636 out.print("out.printXml();\n}\n\n"); 637 } 638 out.printf("}\n"); 639 } 640 printHexBinaryHelper(CodeWriter out)641 private void printHexBinaryHelper(CodeWriter out) throws JavaCodeGeneratorException { 642 out.printf("package %s;\n", packageName); 643 out.println(); 644 out.println("public class HexBinaryHelper {"); 645 out.print("public static byte[] hexStringToByteArray(String hexString) {\n" 646 + "if (hexString.length() % 2 != 0) {\n" 647 + "throw new IllegalArgumentException(\"length must be multiple of 2\");\n" 648 + "}\n" 649 + "byte[] outputBytes = new byte[hexString.length() / 2];\n" 650 + "for (int i = 0; i < hexString.length(); i += 2) {\n" 651 + "char c1 = hexString.charAt(i);\n" 652 + "char c2 = hexString.charAt(i + 1);\n" 653 + "outputBytes[i / 2] = (byte) ((Character.digit(c1, 16) << 4)" 654 + " + Character.digit(c2, 16));\n" 655 + "}\n" 656 + "return outputBytes;" 657 + "}\n\n" 658 + "public static String byteArrayToHexString(byte[] b) {\n" 659 + "StringBuffer s = new StringBuffer();\n" 660 + "for (int i = 0; i < b.length; i++) {\n" 661 + "s.append(Integer.toHexString(0x100 + (b[i] & 0xff)).substring(1));\n" 662 + "}\n" 663 + "return s.toString();\n" 664 + "}\n" 665 + "}\n"); 666 } 667 getElementName(XsdElement element)668 private String getElementName(XsdElement element) { 669 if (element instanceof XsdChoice) { 670 return element.getName() + "_optional"; 671 } else if (element instanceof XsdAll) { 672 return element.getName() + "_all"; 673 } 674 return element.getName(); 675 } 676 getFinalString(boolean finalValue)677 private String getFinalString(boolean finalValue) { 678 if (finalValue) { 679 return " final"; 680 } 681 return ""; 682 } 683 getDefaultNullability(Nullability nullability)684 private String getDefaultNullability(Nullability nullability) { 685 if (showNullability) { 686 return getNullabilityString(nullability); 687 } 688 return ""; 689 } 690 getNullabilityString(Nullability nullability)691 private String getNullabilityString(Nullability nullability) { 692 if (nullability == Nullability.NON_NULL) { 693 return "@android.annotation.NonNull "; 694 } else if (nullability == Nullability.NULLABLE) { 695 return "@android.annotation.Nullable "; 696 } else if (showNullability) { 697 return "@android.annotation.Nullable "; 698 } 699 return ""; 700 } 701 getterName(String type)702 private String getterName(String type) { 703 if (type.equals("boolean") && booleanGetter) { 704 return "is"; 705 } 706 return "get"; 707 } 708 stackComponents(XsdComplexType complexType, List<XsdElement> elements, List<XsdAttribute> attributes)709 private void stackComponents(XsdComplexType complexType, List<XsdElement> elements, 710 List<XsdAttribute> attributes) throws JavaCodeGeneratorException { 711 if (complexType.getBase() != null) { 712 QName baseRef = complexType.getBase().getRef(); 713 if (baseRef != null && !baseRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) { 714 XsdType parent = getType(baseRef.getLocalPart()); 715 if (parent instanceof XsdComplexType) { 716 stackComponents((XsdComplexType) parent, elements, attributes); 717 } 718 } 719 } 720 elements.addAll(getAllElements(complexType.getGroup())); 721 elements.addAll(complexType.getElements()); 722 for (XsdAttributeGroup attributeGroup : complexType.getAttributeGroups()) { 723 attributes.addAll(getAllAttributes(resolveAttributeGroup(attributeGroup))); 724 } 725 attributes.addAll(complexType.getAttributes()); 726 } 727 getAllAttributes(XsdAttributeGroup attributeGroup)728 private List<XsdAttribute> getAllAttributes(XsdAttributeGroup attributeGroup) 729 throws JavaCodeGeneratorException { 730 List<XsdAttribute> attributes = new ArrayList<>(); 731 for (XsdAttributeGroup attrGroup : attributeGroup.getAttributeGroups()) { 732 attributes.addAll(getAllAttributes(resolveAttributeGroup(attrGroup))); 733 } 734 attributes.addAll(attributeGroup.getAttributes()); 735 return attributes; 736 } 737 getAllElements(XsdGroup group)738 private List<XsdElement> getAllElements(XsdGroup group) throws JavaCodeGeneratorException { 739 List<XsdElement> elements = new ArrayList<>(); 740 if (group == null) { 741 return elements; 742 } 743 elements.addAll(getAllElements(resolveGroup(group))); 744 elements.addAll(group.getElements()); 745 return elements; 746 } 747 getBaseName(XsdComplexType complexType)748 private String getBaseName(XsdComplexType complexType) throws JavaCodeGeneratorException { 749 if (complexType.getBase() == null) return null; 750 if (complexType.getBase().getRef().getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) { 751 return null; 752 } 753 XsdType base = getType(complexType.getBase().getRef().getLocalPart()); 754 if (base instanceof XsdComplexType) { 755 return Utils.toClassName(base.getName()); 756 } 757 return null; 758 } 759 getValueType(XsdSimpleContent simpleContent, boolean traverse)760 private JavaSimpleType getValueType(XsdSimpleContent simpleContent, boolean traverse) 761 throws JavaCodeGeneratorException { 762 assert simpleContent.getBase() != null; 763 QName baseRef = simpleContent.getBase().getRef(); 764 assert baseRef != null; 765 if (baseRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) { 766 return predefinedType(baseRef.getLocalPart()); 767 } else { 768 XsdType parent = getType(baseRef.getLocalPart()); 769 if (parent instanceof XsdSimpleType) { 770 return parseSimpleTypeReference(baseRef, false); 771 } 772 if (!traverse) return null; 773 if (parent instanceof XsdSimpleContent) { 774 return getValueType((XsdSimpleContent) parent, true); 775 } else { 776 throw new JavaCodeGeneratorException( 777 String.format("base not simple : %s", baseRef.getLocalPart())); 778 } 779 } 780 } 781 parseType(XsdType type, String defaultName)782 private JavaType parseType(XsdType type, String defaultName) throws JavaCodeGeneratorException { 783 if (type.getRef() != null) { 784 String name = type.getRef().getLocalPart(); 785 if (type.getRef().getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) { 786 return predefinedType(name); 787 } else { 788 XsdType typeValue = getType(name); 789 if (typeValue instanceof XsdSimpleType) { 790 return parseSimpleTypeReference(type.getRef(), false); 791 } 792 return parseType(typeValue, name); 793 } 794 } 795 if (type instanceof XsdComplexType) { 796 return new JavaComplexType(Utils.toClassName(defaultName)); 797 } else if (type instanceof XsdSimpleType) { 798 return parseSimpleTypeValue((XsdSimpleType) type, false); 799 } else { 800 throw new JavaCodeGeneratorException( 801 String.format("unknown type name : %s", defaultName)); 802 } 803 } 804 parseSimpleType(XsdType type, boolean traverse)805 private JavaSimpleType parseSimpleType(XsdType type, boolean traverse) 806 throws JavaCodeGeneratorException { 807 if (type.getRef() != null) { 808 return parseSimpleTypeReference(type.getRef(), traverse); 809 } else { 810 return parseSimpleTypeValue((XsdSimpleType) type, traverse); 811 } 812 } 813 parseSimpleTypeReference(QName typeRef, boolean traverse)814 private JavaSimpleType parseSimpleTypeReference(QName typeRef, boolean traverse) 815 throws JavaCodeGeneratorException { 816 assert typeRef != null; 817 String typeName = typeRef.getLocalPart(); 818 if (typeRef.getNamespaceURI().equals(XsdConstants.XSD_NAMESPACE)) { 819 return predefinedType(typeName); 820 } 821 if (javaSimpleTypeMap.containsKey(typeName)) { 822 return javaSimpleTypeMap.get(typeName); 823 } else if (traverse) { 824 XsdSimpleType simpleType = getSimpleType(typeName); 825 JavaSimpleType ret = parseSimpleTypeValue(simpleType, true); 826 javaSimpleTypeMap.put(typeName, ret); 827 return ret; 828 } else { 829 throw new JavaCodeGeneratorException(String.format("unknown type name : %s", typeName)); 830 } 831 } 832 parseSimpleTypeValue(XsdSimpleType simpleType, boolean traverse)833 private JavaSimpleType parseSimpleTypeValue(XsdSimpleType simpleType, boolean traverse) 834 throws JavaCodeGeneratorException { 835 if (simpleType instanceof XsdList) { 836 XsdList list = (XsdList) simpleType; 837 return parseSimpleType(list.getItemType(), traverse).newListType(); 838 } else if (simpleType instanceof XsdRestriction) { 839 // we don't consider any restrictions. 840 XsdRestriction restriction = (XsdRestriction) simpleType; 841 if (restriction.getEnums() != null) { 842 String name = Utils.toClassName(restriction.getName()); 843 return new JavaSimpleType(name, name, name + ".fromString(%s)", "%s.toString()", 844 false); 845 } 846 return parseSimpleType(restriction.getBase(), traverse); 847 } else if (simpleType instanceof XsdUnion) { 848 // unions are almost always interpreted as java.lang.String 849 // Exceptionally, if any of member types of union are 'list', then we interpret it as 850 // List<String> 851 XsdUnion union = (XsdUnion) simpleType; 852 for (XsdType memberType : union.getMemberTypes()) { 853 if (parseSimpleType(memberType, traverse).isList()) { 854 return new JavaSimpleType("java.lang.String", "%s", true); 855 } 856 } 857 return new JavaSimpleType("java.lang.String", "%s", false); 858 } else { 859 // unreachable 860 throw new IllegalStateException("unknown simple type"); 861 } 862 } 863 resolveElement(XsdElement element)864 private XsdElement resolveElement(XsdElement element) throws JavaCodeGeneratorException { 865 if (element.getRef() == null) return element; 866 String name = element.getRef().getLocalPart(); 867 XsdElement ret = xmlSchema.getElementMap().get(name); 868 if (ret != null) return ret; 869 throw new JavaCodeGeneratorException(String.format("no element named : %s", name)); 870 } 871 resolveGroup(XsdGroup group)872 private XsdGroup resolveGroup(XsdGroup group) throws JavaCodeGeneratorException { 873 if (group.getRef() == null) return null; 874 String name = group.getRef().getLocalPart(); 875 XsdGroup ret = xmlSchema.getGroupMap().get(name); 876 if (ret != null) return ret; 877 throw new JavaCodeGeneratorException(String.format("no group named : %s", name)); 878 } 879 resolveAttribute(XsdAttribute attribute)880 private XsdAttribute resolveAttribute(XsdAttribute attribute) 881 throws JavaCodeGeneratorException { 882 if (attribute.getRef() == null) return attribute; 883 String name = attribute.getRef().getLocalPart(); 884 XsdAttribute ret = xmlSchema.getAttributeMap().get(name); 885 if (ret != null) return ret; 886 throw new JavaCodeGeneratorException(String.format("no attribute named : %s", name)); 887 } 888 resolveAttributeGroup(XsdAttributeGroup attributeGroup)889 private XsdAttributeGroup resolveAttributeGroup(XsdAttributeGroup attributeGroup) 890 throws JavaCodeGeneratorException { 891 if (attributeGroup.getRef() == null) return attributeGroup; 892 String name = attributeGroup.getRef().getLocalPart(); 893 XsdAttributeGroup ret = xmlSchema.getAttributeGroupMap().get(name); 894 if (ret != null) return ret; 895 throw new JavaCodeGeneratorException(String.format("no attribute group named : %s", name)); 896 } 897 getType(String name)898 private XsdType getType(String name) throws JavaCodeGeneratorException { 899 XsdType type = xmlSchema.getTypeMap().get(name); 900 if (type != null) return type; 901 throw new JavaCodeGeneratorException(String.format("no type named : %s", name)); 902 } 903 getSimpleType(String name)904 private XsdSimpleType getSimpleType(String name) throws JavaCodeGeneratorException { 905 XsdType type = getType(name); 906 if (type instanceof XsdSimpleType) return (XsdSimpleType) type; 907 throw new JavaCodeGeneratorException(String.format("not a simple type : %s", name)); 908 } 909 predefinedType(String name)910 private JavaSimpleType predefinedType(String name) throws JavaCodeGeneratorException { 911 switch (name) { 912 case "string": 913 case "token": 914 case "normalizedString": 915 case "language": 916 case "ENTITY": 917 case "ID": 918 case "Name": 919 case "NCName": 920 case "NMTOKEN": 921 case "anyURI": 922 case "anyType": 923 case "QName": 924 case "NOTATION": 925 case "IDREF": 926 return new JavaSimpleType("java.lang.String", "%s", false); 927 case "ENTITIES": 928 case "NMTOKENS": 929 case "IDREFS": 930 return new JavaSimpleType("java.lang.String", "%s", true); 931 case "date": 932 case "dateTime": 933 case "time": 934 case "gDay": 935 case "gMonth": 936 case "gYear": 937 case "gMonthDay": 938 case "gYearMonth": 939 return new JavaSimpleType("javax.xml.datatype.XMLGregorianCalendar", 940 "javax.xml.datatype.XMLGregorianCalendar", 941 "javax.xml.datatype.DatatypeFactory.newInstance()" 942 + ".newXMLGregorianCalendar(%s)", 943 "%s.toString()", false); 944 case "duration": 945 return new JavaSimpleType("javax.xml.datatype.Duration", 946 "javax.xml.datatype.Duration", 947 "javax.xml.datatype.DatatypeFactory.newInstance().newDuration(%s)", 948 "%s.toString()", false); 949 case "decimal": 950 return new JavaSimpleType("java.math.BigDecimal", "java.math.BigDecimal", 951 "new java.math.BigDecimal(%s)", "%s.toString()", false); 952 case "integer": 953 case "negativeInteger": 954 case "nonNegativeInteger": 955 case "positiveInteger": 956 case "nonPositiveInteger": 957 case "unsignedLong": 958 return new JavaSimpleType("java.math.BigInteger", "java.math.BigInteger", 959 "new java.math.BigInteger(%s)", "%s.toString()", false); 960 case "long": 961 case "unsignedInt": 962 return new JavaSimpleType("long", "java.lang.Long", "Long.parseLong(%s)", 963 "Long.toString(%s)", false); 964 case "int": 965 case "unsignedShort": 966 return new JavaSimpleType("int", "java.lang.Integer", "Integer.parseInt(%s)", 967 "Integer.toString(%s)", false); 968 case "short": 969 case "unsignedByte": 970 return new JavaSimpleType("short", "java.lang.Short", "Short.parseShort(%s)", 971 "Short.toString(%s)", false); 972 case "byte": 973 return new JavaSimpleType("byte", "java.lang.Byte", "Byte.parseByte(%s)", 974 "Byte.toString(%s)",false); 975 case "boolean": 976 return new JavaSimpleType("boolean", "java.lang.Boolean", 977 "Boolean.parseBoolean(%s)", "Boolean.toString(%s)", false); 978 case "double": 979 return new JavaSimpleType("double", "java.lang.Double", "Double.parseDouble(%s)", 980 "Double.toString(%s)", false); 981 case "float": 982 return new JavaSimpleType("float", "java.lang.Float", "Float.parseFloat(%s)", 983 "Float.toString(%s)", false); 984 case "base64Binary": 985 return new JavaSimpleType("byte[]", "byte[]", 986 "java.util.Base64.getDecoder().decode(%s)", 987 "java.util.Base64.getEncoder().encodeToString(%s)", 988 false); 989 case "hexBinary": 990 useHexBinary = true; 991 return new JavaSimpleType("byte[]", "byte[]", 992 "HexBinaryHelper.hexStringToByteArray(%s)", 993 "HexBinaryHelper.byteArrayToHexString(%s)", 994 false); 995 } 996 throw new JavaCodeGeneratorException("unknown xsd predefined type : " + name); 997 } 998 } 999