Mark ab/6881855 as merged

Bug: 172690556
Change-Id: Ibb890234981cfc920718ebd65541faa22a085d85
diff --git a/build/xsdc.go b/build/xsdc.go
index 1f91a78..22c0fc5 100644
--- a/build/xsdc.go
+++ b/build/xsdc.go
@@ -64,6 +64,12 @@
 	Package_name *string
 	Api_dir      *string
 	Gen_writer   *bool
+	Nullability  *bool
+
+	// Whether has{element or atrribute} methods are set to public.
+	// It is not applied to C++, because these methods are always
+	// generated to public for C++.
+	Gen_has      *bool
 }
 
 type xsdConfig struct {
@@ -165,6 +171,14 @@
 		args = "-w"
 	}
 
+	if proptools.Bool(module.properties.Nullability) {
+		args = args + " -n "
+	}
+
+	if proptools.Bool(module.properties.Gen_has) {
+		args = args + " -g "
+	}
+
 	module.genOutputs_j = android.PathForModuleGen(ctx, "java", filenameStem+"_xsdcgen.srcjar")
 
 	ctx.Build(pctx, android.BuildParams{
diff --git a/src/com/android/xsdc/Main.java b/src/com/android/xsdc/Main.java
index 7e5767d..5f786f3 100644
--- a/src/com/android/xsdc/Main.java
+++ b/src/com/android/xsdc/Main.java
@@ -65,6 +65,16 @@
                 .hasArgs(0)
                 .withDescription("Generate Writer code.")
                 .create("w"));
+        options.addOption(OptionBuilder
+                .withLongOpt("nullability")
+                .hasArgs(0)
+                .withDescription("Add @NonNull or @Nullable annotation to generated java code.")
+                .create("n"));
+        options.addOption(OptionBuilder
+                .withLongOpt("genHas")
+                .hasArgs(0)
+                .withDescription("Generate public hasX() method")
+                .create("g"));
 
         CommandLineParser CommandParser = new GnuParser();
         CommandLine cmd;
@@ -81,6 +91,8 @@
         String packageName = cmd.getOptionValue('p', null);
         String outDir = cmd.getOptionValue('o', null);
         boolean writer = cmd.hasOption('w');
+        boolean nullability = cmd.hasOption('n');
+        boolean genHas = cmd.hasOption('g');
 
         if (xsdFile.length != 1 || packageName == null) {
             System.err.println("Error: no xsd files or pacakge name");
@@ -91,22 +103,14 @@
             outDir = ".";
         }
 
-        XmlSchema xmlSchema;
-        try (FileInputStream in = new FileInputStream(xsdFile[0])) {
-            SAXParserFactory factory = SAXParserFactory.newInstance();
-            factory.setNamespaceAware(true);
-            SAXParser parser = factory.newSAXParser();
-            XsdHandler xsdHandler = new XsdHandler();
-            parser.parse(in, xsdHandler);
-            xmlSchema = xsdHandler.getSchema();
-        }
+        XmlSchema xmlSchema = parse(xsdFile[0]);
 
         if (cmd.hasOption('j')) {
             File packageDir = new File(Paths.get(outDir, packageName.replace(".", "/")).toString());
             packageDir.mkdirs();
             FileSystem fs = new FileSystem(packageDir);
             JavaCodeGenerator javaCodeGenerator =
-                    new JavaCodeGenerator(xmlSchema, packageName, writer);
+                    new JavaCodeGenerator(xmlSchema, packageName, writer, nullability, genHas);
             javaCodeGenerator.print(fs);
         } else if (cmd.hasOption('c')) {
             File includeDir = new File(Paths.get(outDir, "include").toString());
@@ -118,6 +122,23 @@
         }
     }
 
+    private static XmlSchema parse(String xsdFile) throws Exception {
+        XmlSchema xmlSchema;
+        try (FileInputStream in = new FileInputStream(xsdFile)) {
+            SAXParserFactory factory = SAXParserFactory.newInstance();
+            factory.setNamespaceAware(true);
+            SAXParser parser = factory.newSAXParser();
+            XsdHandler xsdHandler = new XsdHandler();
+            parser.parse(in, xsdHandler);
+            xmlSchema = xsdHandler.getSchema();
+        }
+        for (String file : xmlSchema.getIncludeList()) {
+            XmlSchema temp = parse(Paths.get(xsdFile).resolveSibling(file).toString());
+            xmlSchema.include(temp);
+        }
+        return xmlSchema;
+    }
+
     private static void help(Options options) {
         new HelpFormatter().printHelp(
                 "xsdc path/to/xsd_file.xsd","", options, null, true);
diff --git a/src/com/android/xsdc/XmlSchema.java b/src/com/android/xsdc/XmlSchema.java
index a941791..4bfa3fd 100644
--- a/src/com/android/xsdc/XmlSchema.java
+++ b/src/com/android/xsdc/XmlSchema.java
@@ -19,6 +19,7 @@
 import com.android.xsdc.tag.*;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 public class XmlSchema {
@@ -27,16 +28,18 @@
     final private Map<String, XsdAttribute> attributeMap;
     final private Map<String, XsdAttributeGroup> attributeGroupMap;
     final private Map<String, XsdGroup> groupMap;
+    final private List<String> includeList;
 
     XmlSchema(Map<String, XsdElement> elementMap, Map<String, XsdType> typeMap,
             Map<String, XsdAttribute> attributeMap,
             Map<String, XsdAttributeGroup> attributeGroupMap,
-            Map<String, XsdGroup> groupMap) {
-        this.elementMap = Collections.unmodifiableMap(elementMap);
-        this.typeMap = Collections.unmodifiableMap(typeMap);
-        this.attributeMap = Collections.unmodifiableMap(attributeMap);
-        this.attributeGroupMap = Collections.unmodifiableMap(attributeGroupMap);
-        this.groupMap = Collections.unmodifiableMap(groupMap);
+            Map<String, XsdGroup> groupMap, List<String> includeList) {
+        this.elementMap = elementMap;
+        this.typeMap = typeMap;
+        this.attributeMap = attributeMap;
+        this.attributeGroupMap = attributeGroupMap;
+        this.groupMap = groupMap;
+        this.includeList = includeList;
     }
 
     public Map<String, XsdElement> getElementMap() {
@@ -58,4 +61,16 @@
     public Map<String, XsdGroup> getGroupMap() {
         return groupMap;
     }
+
+    public List<String> getIncludeList() {
+        return includeList;
+    }
+
+    public void include(XmlSchema schema) {
+        elementMap.putAll(schema.getElementMap());
+        typeMap.putAll(schema.getTypeMap());
+        attributeMap.putAll(schema.getAttributeMap());
+        attributeGroupMap.putAll(schema.getAttributeGroupMap());
+        groupMap.putAll(schema.getGroupMap());
+    }
 }
diff --git a/src/com/android/xsdc/XsdHandler.java b/src/com/android/xsdc/XsdHandler.java
index 1fa0b88..f6a9492 100644
--- a/src/com/android/xsdc/XsdHandler.java
+++ b/src/com/android/xsdc/XsdHandler.java
@@ -60,6 +60,7 @@
     private boolean documentationFlag;
     private boolean enumerationFlag;
     private List<XsdTag> enumTags;
+    private List<String> includeList;
 
     public XsdHandler() {
         stateStack = new Stack<>();
@@ -67,6 +68,7 @@
         documentationFlag = false;
         enumerationFlag = false;
         enumTags = new ArrayList<>();
+        includeList = new ArrayList<>();
     }
 
     public XmlSchema getSchema() {
@@ -226,6 +228,9 @@
                     // They are using when validating xml files via xsd file.
                     // So they are ignored.
                     break;
+                case "include":
+                    addInclude(state);
+                    break;
                 default:
                     throw new XsdParserException(String.format("unsupported tag : %s", state.name));
             }
@@ -259,7 +264,7 @@
             }
         }
 
-        return new XmlSchema(elementMap, typeMap, attrMap, attrGroupMap, groupMap);
+        return new XmlSchema(elementMap, typeMap, attrMap, attrGroupMap, groupMap, includeList);
     }
 
     private XsdElement makeElement(State state) throws XsdParserException {
@@ -663,6 +668,11 @@
                 state.finalValue, state.nullability);
     }
 
+    private void addInclude(State state) throws XsdParserException {
+        String fileName = state.attributeMap.get("schemaLocation");
+        includeList.add(fileName);
+    }
+
     private boolean isDeprecated(Map<String, String> attributeMap,List<XsdTag> tags,
             boolean deprecated) throws XsdParserException {
         String name = attributeMap.get("name");
diff --git a/src/com/android/xsdc/cpp/CppCodeGenerator.java b/src/com/android/xsdc/cpp/CppCodeGenerator.java
index 9365b9c..489dc39 100644
--- a/src/com/android/xsdc/cpp/CppCodeGenerator.java
+++ b/src/com/android/xsdc/cpp/CppCodeGenerator.java
@@ -119,20 +119,23 @@
         headerFile.printf("#include <optional>\n");
         headerFile.printf("#include <string>\n");
         headerFile.printf("#include <vector>\n");
+        if (writer) {
+            headerFile.printf("#include <iostream>\n");
+        }
         headerFile.printf("\n");
+        headerFile.printf("#if __has_include(<libxml/parser.h>)\n");
         headerFile.printf("#include <libxml/parser.h>\n");
         headerFile.printf("#include <libxml/xinclude.h>\n");
+        headerFile.printf("#else\n");
+        headerFile.printf("#error Require libxml2 library. ");
+        headerFile.printf("Please add libxml2 to shared_libs or static_libs\n");
+        headerFile.printf("#endif\n");
         if (hasEnums) {
             headerFile.printf("#include <xsdc/XsdcSupport.h>\n");
         }
         headerFile.printf("\n");
 
         cppFile.printf("#define LOG_TAG \"%s\"\n", pkgName);
-        cppFile.printf("#include <android/log.h>\n");
-        cppFile.printf("#include <android-base/strings.h>\n");
-        cppFile.printf("#include <libxml/parser.h>\n");
-        cppFile.printf("#include <libxml/xinclude.h>\n");
-        cppFile.printf("\n");
         cppFile.printf("#include \"%s\"\n\n", hFileName);
 
         List<String> namespace = new java.util.ArrayList<>();
@@ -421,15 +424,11 @@
             String variableName = Utils.toVariableName(attribute.getName());
             cppFile.printf("raw = getXmlAttribute(root, \"%s\");\n", attribute.getName());
             if (attribute.isRequired()) {
-                if (type.getName().equals("bool")) {
-                    cppFile.printf("%s %s = false;\n", type.getName(), variableName);
-                } else if (type.getName().equals("std::string")) {
-                    cppFile.printf("%s %s;\n", type.getName(), variableName);
-                } else if (type.isEnum()) {
+                if (type.isEnum()) {
                     cppFile.printf("%s %s = %s::%s;\n",
                             type.getName(), variableName, type.getName(), UNKNOWN_ENUM);
                 } else {
-                    cppFile.printf("%s %s = 0;\n", type.getName(), variableName);
+                    cppFile.printf("%s %s{};\n", type.getName(), variableName);
                 }
             } else {
                 cppFile.printf("std::optional<%s> %s = std::nullopt;\n", type.getName(),
diff --git a/src/com/android/xsdc/cpp/CppSimpleType.java b/src/com/android/xsdc/cpp/CppSimpleType.java
index 375111a..282855f 100644
--- a/src/com/android/xsdc/cpp/CppSimpleType.java
+++ b/src/com/android/xsdc/cpp/CppSimpleType.java
@@ -63,9 +63,15 @@
         if (list) {
             expression.append(
                     String.format("%s value;\n", getName()));
-            expression.append("for (auto& token : android::base::Split(raw, \" \")) {\n");
-            expression.append(String.format("value.push_back(std::move(%s));\n",
-                    String.format(rawParsingExpression, "token")));
+            expression.append(String.format("{\nint base = 0;\n"
+                    + "int found;\n"
+                    + "while(true) {\n"
+                    + "found = raw.find_first_of(\" \", base);\n"
+                    + "value.push_back(%s);\n"
+                    + "if (found == raw.npos) break;\n"
+                    + "base = found + 1;\n"
+                    + "}\n",
+                    String.format(rawParsingExpression, "raw.substr(base, found - base)")));
             expression.append("}\n");
         } else {
             expression.append(
diff --git a/src/com/android/xsdc/java/JavaCodeGenerator.java b/src/com/android/xsdc/java/JavaCodeGenerator.java
index 6392240..52d6427 100644
--- a/src/com/android/xsdc/java/JavaCodeGenerator.java
+++ b/src/com/android/xsdc/java/JavaCodeGenerator.java
@@ -37,12 +37,16 @@
     private String packageName;
     private Map<String, JavaSimpleType> javaSimpleTypeMap;
     private boolean writer;
+    private boolean showNullability;
+    private boolean generateHasMethod;
 
-    public JavaCodeGenerator(XmlSchema xmlSchema, String packageName, boolean writer)
-            throws JavaCodeGeneratorException {
+    public JavaCodeGenerator(XmlSchema xmlSchema, String packageName, boolean writer,
+            boolean showNullability, boolean generateHasMethod) throws JavaCodeGeneratorException {
         this.xmlSchema = xmlSchema;
         this.packageName = packageName;
         this.writer = writer;
+        this.showNullability = showNullability;
+        this.generateHasMethod = generateHasMethod;
 
         // class naming validation
         {
@@ -139,27 +143,28 @@
         }
         out.printf(";\n\n");
         out.printf("private final String rawName;\n\n");
-        out.printf("%s(String rawName) {\n"
+        out.printf("%s(%sString rawName) {\n"
                 + "this.rawName = rawName;\n"
-                + "}\n\n", name);
-        out.printf("public String getRawName() {\n"
+                + "}\n\n", name, getDefaultNullability(Nullability.NON_NULL));
+        out.printf("public %sString getRawName() {\n"
                 + "return rawName;\n"
-                + "}\n\n");
+                + "}\n\n", getDefaultNullability(Nullability.NON_NULL));
 
-        out.printf("static %s fromString(String rawString) {\n"
+        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", name, name);
+                + "}\n\n", getDefaultNullability(Nullability.NULLABLE), name,
+                getDefaultNullability(Nullability.NON_NULL), name);
 
         if (writer) {
             out.printf("@Override\n"
-                    + "public String toString() {\n"
+                    + "public %sString toString() {\n"
                     + "return rawName;\n"
-                    + "}\n");
+                    + "}\n", getDefaultNullability(Nullability.NON_NULL));
         }
         out.println("}");
     }
@@ -291,9 +296,11 @@
             allAttributeTypes.add(parseSimpleType(type, false));
         }
 
-        out.printf("static %s read(org.xmlpull.v1.XmlPullParser parser) " +
+        out.printf("static %s%s read(%sorg.xmlpull.v1.XmlPullParser parser) " +
                 "throws org.xmlpull.v1.XmlPullParserException, java.io.IOException, " +
-                "javax.xml.datatype.DatatypeConfigurationException {\n", name);
+                "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);
@@ -376,8 +383,9 @@
             allAttributeTypes.add(parseSimpleType(type, false));
         }
 
-        out.printf("\nvoid write(XmlWriter out, String name) " +
-                "throws java.io.IOException {\n");
+        out.printf("\nvoid write(%sXmlWriter out, %sString name) " +
+                "throws java.io.IOException {\n", getDefaultNullability(Nullability.NON_NULL),
+                getDefaultNullability(Nullability.NON_NULL));
 
         out.printf("out.printf(\"<%s\");\n", name);
         for (int i = 0; i < allAttributes.size(); ++i) {
@@ -462,11 +470,12 @@
 
         if (isMultiple) return;
         out.println();
-        out.printf("boolean has%s() {\n"
+        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");
@@ -487,7 +496,7 @@
         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 read%s(java.io.InputStream in)"
+            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"
@@ -497,8 +506,9 @@
                 + "parser.setInput(in, null);\n"
                 + "parser.nextTag();\n"
                 + "String tagName = parser.getName();\n"
-                + "String raw = null;\n", javaType.getName(),
-                isMultiRootElement ? Utils.capitalize(javaType.getName()) : "");
+                + "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");
@@ -511,8 +521,8 @@
             out.println();
         }
 
-        out.print(
-                "public static java.lang.String readText(org.xmlpull.v1.XmlPullParser parser)"
+        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"
@@ -520,11 +530,12 @@
                         + "    parser.nextTag();\n"
                         + "}\n"
                         + "return result;\n"
-                        + "}\n");
+                        + "}\n", getDefaultNullability(Nullability.NULLABLE),
+                        getDefaultNullability(Nullability.NON_NULL));
         out.println();
 
-        out.print(
-                "public static void skip(org.xmlpull.v1.XmlPullParser parser)"
+        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"
@@ -540,7 +551,7 @@
                         + "            break;\n"
                         + "    }\n"
                         + "}\n"
-                        + "}\n");
+                        + "}\n", getDefaultNullability(Nullability.NON_NULL));
 
         out.println("}");
     }
@@ -550,10 +561,10 @@
         out.println();
         out.println("public class XmlWriter implements java.io.Closeable {");
 
-        out.print("private java.io.PrintWriter out;\n"
+        out.printf("private java.io.PrintWriter out;\n"
                 + "private int indent;\n"
                 + "private boolean startLine;\n\n"
-                + "public XmlWriter(java.io.PrintWriter printWriter) {\n"
+                + "public XmlWriter(%sjava.io.PrintWriter printWriter) {\n"
                 + "    out = printWriter;\n"
                 + "    indent = 0;\n"
                 + "    startLine = true;\n"
@@ -592,7 +603,7 @@
                 + "    if (out != null) {\n"
                 + "        out.close();\n"
                 + "    }\n"
-                + "}\n\n");
+                + "}\n\n", getDefaultNullability(Nullability.NON_NULL));
 
 
         for (XsdElement element : xmlSchema.getElementMap().values()) {
@@ -601,7 +612,9 @@
             String VariableName = Utils.toVariableName(elementName);
             String typeName = javaType instanceof JavaSimpleType ? javaType.getName() :
                     Utils.toClassName(javaType.getName());
-            out.printf("public static void write(XmlWriter out, %s %s) throws java.io.IOException {", typeName, VariableName);
+            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}\n\n", VariableName, elementName);
@@ -625,11 +638,20 @@
         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 "";
     }
diff --git a/tests/main.cpp b/tests/main.cpp
index e187577..02a8891 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -300,6 +300,9 @@
   EXPECT_EQ(student.getCity(), "Mountain View");
   EXPECT_EQ(student.getState(), "CA");
   EXPECT_EQ(student.getRoad(), "Street 101");
+  EXPECT_EQ(student.getList()[0], 1);
+  EXPECT_EQ(student.getList()[1], 2);
+  EXPECT_EQ(student.getList()[2], 3);
 
   ofstream out("old_attr_group_simple.xml");
   write(out, student);
diff --git a/tests/resources/attr_group_simple.xml b/tests/resources/attr_group_simple.xml
index fb3bfc8..0a14222 100644
--- a/tests/resources/attr_group_simple.xml
+++ b/tests/resources/attr_group_simple.xml
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Student State="CA" city="Mountain View" road="Street 101">
+<Student State="CA" city="Mountain View" road="Street 101" list="1 2 3">
     <Name>Jun</Name>
 </Student>
diff --git a/tests/resources/attr_group_simple/api/current.txt b/tests/resources/attr_group_simple/api/current.txt
index 62fc25f..9203633 100644
--- a/tests/resources/attr_group_simple/api/current.txt
+++ b/tests/resources/attr_group_simple/api/current.txt
@@ -4,10 +4,12 @@
   public class Student {
     ctor public Student();
     method public String getCity();
+    method public java.util.List<java.lang.Integer> getList();
     method public String getName();
     method public String getRoad();
     method public String getState();
     method public void setCity(String);
+    method public void setList(java.util.List<java.lang.Integer>);
     method public void setName(String);
     method public void setRoad(String);
     method public void setState(String);
diff --git a/tests/resources/attr_group_simple/attr_group_simple.xsd b/tests/resources/attr_group_simple/attr_group_simple.xsd
index 831f8ed..0853848 100644
--- a/tests/resources/attr_group_simple/attr_group_simple.xsd
+++ b/tests/resources/attr_group_simple/attr_group_simple.xsd
@@ -7,12 +7,16 @@
         <xs:attributeGroup ref="address"/>
         <xs:attribute name="road" type="xs:string"/>
     </xs:attributeGroup>
+    <xs:simpleType name="listInt">
+        <xs:list itemType="xs:int" />
+    </xs:simpleType>
     <xs:element name="Student">
         <xs:complexType>
             <xs:sequence>
                 <xs:element name="Name" type="xs:string"/>
             </xs:sequence>
             <xs:attributeGroup ref="homeAddress"/>
+            <xs:attribute name="list" type="listInt" use="required"/>
         </xs:complexType>
     </xs:element>
 </xs:schema>
diff --git a/tests/resources/group/address.xsd b/tests/resources/group/address.xsd
new file mode 100644
index 0000000..6fc075c
--- /dev/null
+++ b/tests/resources/group/address.xsd
@@ -0,0 +1,8 @@
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="attr_group_simple" elementFormDefault="qualified">
+  <xs:group name="address1">
+    <xs:sequence>
+      <xs:element name="State" type="xs:string"/>
+      <xs:element name="city" type="xs:string"/>
+    </xs:sequence>
+  </xs:group>
+</xs:schema>
diff --git a/tests/resources/group/group.xsd b/tests/resources/group/group.xsd
index 2bef1a3..3e96834 100644
--- a/tests/resources/group/group.xsd
+++ b/tests/resources/group/group.xsd
@@ -1,10 +1,5 @@
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="attr_group_simple" elementFormDefault="qualified">
-  <xs:group name="address1">
-    <xs:sequence>
-      <xs:element name="State" type="xs:string"/>
-      <xs:element name="city" type="xs:string"/>
-    </xs:sequence>
-  </xs:group>
+  <xs:include schemaLocation="address.xsd" />
   <xs:group name="address2" ref="address1">
     <xs:sequence>
       <xs:element name="road" type="xs:string"/>
diff --git a/tests/resources/reference/Android.bp b/tests/resources/reference/Android.bp
index ced8d0f..b3132ba 100644
--- a/tests/resources/reference/Android.bp
+++ b/tests/resources/reference/Android.bp
@@ -4,5 +4,6 @@
     srcs: ["reference.xsd"],
     package_name: "reference",
     gen_writer: true,
+    nullability: true,
 }
 
diff --git a/tests/resources/reference/api/current.txt b/tests/resources/reference/api/current.txt
index 6ae4cf1..e33fb20 100644
--- a/tests/resources/reference/api/current.txt
+++ b/tests/resources/reference/api/current.txt
@@ -3,22 +3,22 @@
 
   public class Class {
     ctor public Class();
-    method public String getName();
-    method public java.util.List<java.lang.String> getStudent();
-    method public void setName(String);
+    method @Nullable public String getName();
+    method @Nullable public java.util.List<java.lang.String> getStudent();
+    method public void setName(@Nullable String);
   }
 
   public class XmlParser {
     ctor public XmlParser();
-    method public static reference.Class read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @Nullable public static reference.Class read(@NonNull java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @Nullable public static String readText(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
   }
 
   public class XmlWriter implements java.io.Closeable {
-    ctor public XmlWriter(java.io.PrintWriter);
+    ctor public XmlWriter(@NonNull java.io.PrintWriter);
     method public void close();
-    method public static void write(reference.XmlWriter, reference.Class) throws java.io.IOException;
+    method public static void write(@NonNull reference.XmlWriter, @NonNull reference.Class) throws java.io.IOException;
   }
 
 }
diff --git a/tests/resources/simple_complex_content/Android.bp b/tests/resources/simple_complex_content/Android.bp
index 31966cf..36077db 100644
--- a/tests/resources/simple_complex_content/Android.bp
+++ b/tests/resources/simple_complex_content/Android.bp
@@ -4,5 +4,6 @@
     srcs: ["simple_complex_content.xsd"],
     package_name: "simple.complex.content",
     gen_writer: true,
+    gen_has: true,
 }
 
diff --git a/tests/resources/simple_complex_content/api/current.txt b/tests/resources/simple_complex_content/api/current.txt
index 34f5d7b..043fb50 100644
--- a/tests/resources/simple_complex_content/api/current.txt
+++ b/tests/resources/simple_complex_content/api/current.txt
@@ -6,6 +6,9 @@
     method @Deprecated public String getCity();
     method @Deprecated public final String getName();
     method @Deprecated public String getStreet();
+    method @Deprecated public boolean hasCity();
+    method @Deprecated public boolean hasName();
+    method @Deprecated public boolean hasStreet();
     method @Deprecated public void setCity(String);
     method @Deprecated public final void setName(String);
     method @Deprecated public void setStreet(String);
@@ -21,6 +24,10 @@
     method public String getName();
     method public simple.complex.content.SubAddress getSubAddress();
     method public simple.complex.content.USAddressP getUSAddressP();
+    method public boolean hasKRAddress();
+    method public boolean hasName();
+    method public boolean hasSubAddress();
+    method public boolean hasUSAddressP();
     method public void setKRAddress(simple.complex.content.KRAddress);
     method public void setName(String);
     method public void setSubAddress(simple.complex.content.SubAddress);
@@ -33,6 +40,10 @@
     method public String getName();
     method @NonNull public simple.complex.content.SubAddress getSubAddress();
     method public simple.complex.content.USAddressP getUSAddressP();
+    method public boolean hasKRAddress();
+    method public boolean hasName();
+    method public boolean hasSubAddress();
+    method public boolean hasUSAddressP();
     method public void setKRAddress(simple.complex.content.KRAddress);
     method public void setName(String);
     method public void setSubAddress(@NonNull simple.complex.content.SubAddress);
@@ -43,6 +54,8 @@
     ctor public SubAddress();
     method @Nullable public final String getChoice1_optional();
     method @NonNull public final String getChoice2_optional();
+    method public boolean hasChoice1_optional();
+    method public boolean hasChoice2_optional();
     method public final void setChoice1_optional(@Nullable String);
     method public final void setChoice2_optional(@NonNull String);
   }
@@ -51,6 +64,8 @@
     ctor public USAddressP();
     method public String getState();
     method public java.math.BigInteger getZipcode();
+    method public boolean hasState();
+    method public boolean hasZipcode();
     method public void setState(String);
     method public void setZipcode(java.math.BigInteger);
   }