Add nullability

The nullability is added. If someone wants to implement an element or
attribute as nullability, add nonnull or nullable annotation.

Bug: 151155782
Test: m -j && make update-api  && make checkapi
Change-Id: I3e5e4adf3f8a42d41e3fd99caf5e9c9981ef0bca
diff --git a/src/com/android/xsdc/XsdHandler.java b/src/com/android/xsdc/XsdHandler.java
index db78a09..5a7c0d4 100644
--- a/src/com/android/xsdc/XsdHandler.java
+++ b/src/com/android/xsdc/XsdHandler.java
@@ -41,6 +41,7 @@
         final List<XsdTag> tags;
         boolean deprecated;
         boolean finalValue;
+        Nullability nullability;
 
         State(String name, Map<String, String> attributeMap) {
             this.name = name;
@@ -48,6 +49,7 @@
             tags = new ArrayList<>();
             deprecated = false;
             finalValue = false;
+            nullability = Nullability.UNKNOWN;
         }
     }
 
@@ -204,6 +206,8 @@
                 case "annotation":
                     stateStack.peek().deprecated = isDeprecated(state.attributeMap, state.tags);
                     stateStack.peek().finalValue = isFinalValue(state.attributeMap, state.tags);
+                    stateStack.peek().nullability = getNullability(state.attributeMap, state.tags,
+                            stateStack.peek().nullability);
                     break;
                 case "appinfo":
                     // They function like comments, so are ignored.
@@ -293,7 +297,7 @@
         }
 
         return setDeprecatedAndFinal(new XsdElement(name, ref, type, multiple), state.deprecated,
-                state.finalValue);
+                state.finalValue, state.nullability);
     }
 
     private XsdAttribute makeAttribute(State state) throws XsdParserException {
@@ -317,7 +321,7 @@
         }
 
         return setDeprecatedAndFinal(new XsdAttribute(name, ref, type), state.deprecated,
-                state.finalValue);
+                state.finalValue, state.nullability);
     }
 
     private XsdAttributeGroup makeAttributeGroup(State state) throws XsdParserException {
@@ -337,7 +341,7 @@
         }
 
         return setDeprecatedAndFinal(new XsdAttributeGroup(name, ref, attributes, attributeGroups),
-                state.deprecated, state.finalValue);
+                state.deprecated, state.finalValue, state.nullability);
     }
 
     private XsdGroup makeGroup(State state) throws XsdParserException {
@@ -354,7 +358,7 @@
         }
 
         return setDeprecatedAndFinal(new XsdGroup(name, ref, elements), state.deprecated,
-                state.finalValue);
+                state.finalValue, state.nullability);
     }
 
     private XsdComplexType makeComplexType(State state) throws XsdParserException {
@@ -389,16 +393,19 @@
                 XsdComplexContent child = (XsdComplexContent) tag;
                 type = setDeprecatedAndFinal(new XsdComplexContent(name, child.getBase(),
                         child.getAttributes(), child.getAttributeGroups(),
-                        child.getElements(), child.getGroup()), state.deprecated, state.finalValue);
+                        child.getElements(), child.getGroup()), state.deprecated, state.finalValue,
+                        state.nullability);
             } else if (tag instanceof XsdSimpleContent) {
                 XsdSimpleContent child = (XsdSimpleContent) tag;
                 type = setDeprecatedAndFinal(new XsdSimpleContent(name, child.getBase(),
-                        child.getAttributes()), state.deprecated, state.finalValue);
+                        child.getAttributes()), state.deprecated, state.finalValue,
+                        state.nullability);
             }
         }
 
-        return (type != null) ? type : setDeprecatedAndFinal(new XsdComplexContent(name, null, attributes,
-                attributeGroups, elements, group), state.deprecated, state.finalValue);
+        return (type != null) ? type : setDeprecatedAndFinal(new XsdComplexContent(name, null,
+                attributes, attributeGroups, elements, group), state.deprecated, state.finalValue,
+                state.nullability);
     }
 
     private XsdComplexContent makeComplexContent(State state) throws XsdParserException {
@@ -431,7 +438,8 @@
             }
         }
 
-        return setDeprecatedAndFinal(content, state.deprecated, state.finalValue);
+        return setDeprecatedAndFinal(content, state.deprecated, state.finalValue,
+                state.nullability);
     }
 
     private XsdSimpleContent makeSimpleContent(State state) {
@@ -449,7 +457,8 @@
             }
         }
 
-        return setDeprecatedAndFinal(content, state.deprecated, state.finalValue);
+        return setDeprecatedAndFinal(content, state.deprecated, state.finalValue,
+                state.nullability);
     }
 
     private XsdGeneralRestriction makeGeneralRestriction(State state) throws XsdParserException {
@@ -477,7 +486,7 @@
         }
 
         return setDeprecatedAndFinal(new XsdGeneralRestriction(type, attributes, attributeGroups,
-                elements, group), state.deprecated, state.finalValue);
+                elements, group), state.deprecated, state.finalValue, state.nullability);
     }
 
     private XsdGeneralExtension makeGeneralExtension(State state) throws XsdParserException {
@@ -500,7 +509,8 @@
             }
         }
         return setDeprecatedAndFinal(new XsdGeneralExtension(new XsdType(null, base), attributes,
-                attributeGroups, elements, group), state.deprecated, state.finalValue);
+                attributeGroups, elements, group), state.deprecated, state.finalValue,
+                state.nullability);
     }
 
     private XsdSimpleType makeSimpleType(State state) throws XsdParserException {
@@ -524,7 +534,7 @@
                 type = new XsdUnion(name, ((XsdUnion) tag).getMemberTypes());
             }
         }
-        return setDeprecatedAndFinal(type, state.deprecated, state.finalValue);
+        return setDeprecatedAndFinal(type, state.deprecated, state.finalValue, state.nullability);
     }
 
     private XsdList makeSimpleTypeList(State state) throws XsdParserException {
@@ -541,7 +551,7 @@
             }
         }
         return setDeprecatedAndFinal(new XsdList(null, itemType), state.deprecated,
-                state.finalValue);
+                state.finalValue, state.nullability);
     }
 
     private XsdUnion makeSimpleTypeUnion(State state) throws XsdParserException {
@@ -557,7 +567,7 @@
         }
 
         return setDeprecatedAndFinal(new XsdUnion(null, memberTypes), state.deprecated,
-                state.finalValue);
+                state.finalValue, state.nullability);
     }
 
     private static List<XsdTag> makeSequence(State state) throws XsdParserException {
@@ -599,7 +609,7 @@
                 XsdElement element = (XsdElement)tag;
                 elementsAndGroup.add((XsdTag) setDeprecatedAndFinal(new XsdChoice(element.getName(),
                         element.getRef(), element.getType(), element.isMultiple()),
-                        element.isDeprecated(), element.isFinalValue()));
+                        element.isDeprecated(), element.isFinalValue(), element.getNullability()));
             } else if (tag instanceof XsdGroup) {
                 elementsAndGroup.add(tag);
             }
@@ -615,7 +625,7 @@
                 XsdElement element = (XsdElement)tag;
                 elements.add(setDeprecatedAndFinal(new XsdAll(element.getName(), element.getRef(),
                         element.getType(), element.isMultiple()), element.isDeprecated(),
-                        element.isFinalValue()));
+                        element.isFinalValue(), element.getNullability()));
             }
         }
         return elements;
@@ -624,7 +634,7 @@
     private XsdEnumeration makeEnumeration(State state) throws XsdParserException {
         String value = state.attributeMap.get("value");
         return setDeprecatedAndFinal(new XsdEnumeration(value), state.deprecated,
-                state.finalValue);
+                state.finalValue, state.nullability);
     }
 
     private XsdEnumRestriction makeEnumRestriction(State state) throws XsdParserException {
@@ -643,7 +653,7 @@
         }
 
         return setDeprecatedAndFinal(new XsdEnumRestriction(type, enums), state.deprecated,
-                state.finalValue);
+                state.finalValue, state.nullability);
     }
 
     private boolean isDeprecated(Map<String, String> attributeMap,List<XsdTag> tags)
@@ -664,11 +674,23 @@
         return false;
     }
 
+    private Nullability getNullability(Map<String, String> attributeMap,List<XsdTag> tags,
+            Nullability nullability) throws XsdParserException {
+        String name = attributeMap.get("name");
+        if ("nullable".equals(name)) {
+            return Nullability.NULLABLE;
+        } else if ("nonnull".equals(name)) {
+            return Nullability.NON_NULL;
+        }
+        return nullability;
+    }
+
     private static <T extends XsdTag> T setDeprecatedAndFinal(T tag, boolean deprecated,
-            boolean finalValue) {
+            boolean finalValue, Nullability nullability) {
         if (tag != null) {
             tag.setDeprecated(deprecated);
             tag.setFinalValue(finalValue);
+            tag.setNullability(nullability);
         }
         return tag;
     }
diff --git a/src/com/android/xsdc/java/JavaCodeGenerator.java b/src/com/android/xsdc/java/JavaCodeGenerator.java
index f6c7bb5..23ce51d 100644
--- a/src/com/android/xsdc/java/JavaCodeGenerator.java
+++ b/src/com/android/xsdc/java/JavaCodeGenerator.java
@@ -87,8 +87,7 @@
                 XsdComplexType complexType = (XsdComplexType) type;
                 try (CodeWriter out = new CodeWriter(fs.getPrintWriter(name + ".java"))) {
                     out.printf("package %s;\n\n", packageName);
-                    printClass(out, name, complexType, "", type.isDeprecated(),
-                            type.isFinalValue());
+                    printClass(out, name, complexType, "");
                 }
             } else if (type instanceof XsdRestriction &&
                     ((XsdRestriction)type).getEnums() != null) {
@@ -96,7 +95,7 @@
                 XsdRestriction restrictionType = (XsdRestriction) type;
                 try (CodeWriter out = new CodeWriter(fs.getPrintWriter(name + ".java"))) {
                     out.printf("package %s;\n\n", packageName);
-                    printEnumClass(out, name, restrictionType, type.isDeprecated());
+                    printEnumClass(out, name, restrictionType);
                 }
             }
         }
@@ -107,8 +106,7 @@
                 XsdComplexType complexType = (XsdComplexType) type;
                 try (CodeWriter out = new CodeWriter(fs.getPrintWriter(name + ".java"))) {
                     out.printf("package %s;\n\n", packageName);
-                    printClass(out, name, complexType, "", type.isDeprecated(),
-                            type.isFinalValue());
+                    printClass(out, name, complexType, "");
                 }
             }
         }
@@ -117,9 +115,9 @@
         }
     }
 
-    private void printEnumClass(CodeWriter out, String name, XsdRestriction restrictionType,
-            boolean deprecated) throws JavaCodeGeneratorException {
-        if (deprecated) {
+    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);
@@ -147,8 +145,7 @@
     }
 
     private void printClass(CodeWriter out, String name, XsdComplexType complexType,
-            String nameScope, boolean deprecated, boolean finalValue)
-            throws JavaCodeGeneratorException {
+            String nameScope) throws JavaCodeGeneratorException {
         assert name != null;
         // need element, attribute name duplicate validation?
 
@@ -156,8 +153,8 @@
         JavaSimpleType valueType = (complexType instanceof XsdSimpleContent) ?
                 getValueType((XsdSimpleContent) complexType, false) : null;
 
-        String finalString = getFinalString(finalValue);
-        if (deprecated) {
+        String finalString = getFinalString(complexType.isFinalValue());
+        if (complexType.isDeprecated()) {
             out.printf("@java.lang.Deprecated\n");
         }
         if (nameScope.isEmpty()) {
@@ -186,8 +183,7 @@
                 String innerName = Utils.toClassName(getElementName(element));
                 XsdComplexType innerType = (XsdComplexType) element.getType();
                 String innerNameScope = nameScope + name + ".";
-                printClass(out, innerName, innerType, innerNameScope, innerType.isDeprecated(),
-                        innerType.isFinalValue());
+                printClass(out, innerName, innerType, innerNameScope);
                 out.println();
                 javaType = new JavaComplexType(innerNameScope + innerName);
             } else {
@@ -214,14 +210,14 @@
             XsdElement elementValue = resolveElement(element);
             String typeName = element.isMultiple() ? String.format("java.util.List<%s>",
                     type.getNullableName()) : type.getName();
-            out.printf("private %s %s;\n", typeName,
-                    Utils.toVariableName(getElementName(elementValue)));
+            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("private %s %s;\n", type.getName(),
-                    Utils.toVariableName(attribute.getName()));
+            out.printf("%sprivate %s %s;\n", getNullabilityString(attribute.getNullability()),
+                    type.getName(), Utils.toVariableName(attribute.getName()));
         }
         if (valueType != null) {
             out.printf("private %s value;\n", valueType.getName());
@@ -233,16 +229,16 @@
             XsdElement element = elements.get(i);
             XsdElement elementValue = resolveElement(element);
             printGetterAndSetter(out, type, Utils.toVariableName(getElementName(elementValue)),
-                    element.isMultiple(), element.isDeprecated(), element.isFinalValue());
+                    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.isDeprecated(), attribute.isFinalValue());
+                    attribute);
         }
         if (valueType != null) {
-            printGetterAndSetter(out, valueType, "value", false, false, false);
+            printGetterAndSetter(out, valueType, "value", false, null);
         }
 
         out.println();
@@ -337,15 +333,18 @@
     }
 
     private void printGetterAndSetter(CodeWriter out, JavaType type, String variableName,
-            boolean isMultiple, boolean deprecated, boolean finalValue) {
+            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 get%s() {\n", getFinalString(finalValue), typeName,
-                Utils.capitalize(variableName));
+        out.printf("public%s %s%s get%s() {\n", getFinalString(finalValue),
+                getNullabilityString(nullability), typeName, Utils.capitalize(variableName));
         if (isMultiple) {
             out.printf("if (%s == null) {\n"
                     + "%s = new java.util.ArrayList<>();\n"
@@ -359,11 +358,12 @@
         if (deprecated) {
             out.printf("@java.lang.Deprecated\n");
         }
-        out.printf("public%s void set%s(%s %s) {\n"
+        out.printf("public%s void set%s(%s%s %s) {\n"
                         + "this.%s = %s;\n"
                         + "}\n",
-                getFinalString(finalValue),
-                Utils.capitalize(variableName), typeName, variableName, variableName, variableName);
+                getFinalString(finalValue), Utils.capitalize(variableName),
+                getNullabilityString(nullability), typeName, variableName,
+                variableName, variableName);
     }
 
     private void printXmlParser(CodeWriter out) throws JavaCodeGeneratorException {
@@ -448,6 +448,15 @@
         return "";
     }
 
+    private String getNullabilityString(Nullability nullability) {
+        if (nullability == Nullability.NON_NULL) {
+            return "@android.annotation.NonNull ";
+        } else if (nullability == Nullability.NULLABLE) {
+            return "@android.annotation.Nullable ";
+        }
+        return "";
+    }
+
     private void stackComponents(XsdComplexType complexType, List<XsdElement> elements,
             List<XsdAttribute> attributes) throws JavaCodeGeneratorException {
         if (complexType.getBase() != null) {
diff --git a/src/com/android/xsdc/tag/Nullability.java b/src/com/android/xsdc/tag/Nullability.java
new file mode 100644
index 0000000..9e81635
--- /dev/null
+++ b/src/com/android/xsdc/tag/Nullability.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 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.tag;
+
+public enum Nullability {
+    UNKNOWN,
+    NON_NULL,
+    NULLABLE
+}
diff --git a/src/com/android/xsdc/tag/XsdTag.java b/src/com/android/xsdc/tag/XsdTag.java
index bfe600e..3397267 100644
--- a/src/com/android/xsdc/tag/XsdTag.java
+++ b/src/com/android/xsdc/tag/XsdTag.java
@@ -23,12 +23,14 @@
     final private QName ref;
     private boolean deprecated;
     private boolean finalValue;
+    private Nullability nullability;
 
     XsdTag(String name, QName ref) {
         this.name = name;
         this.ref = ref;
         this.deprecated = false;
         this.finalValue = false;
+        this.nullability = Nullability.UNKNOWN;
     }
 
     public String getName() {
@@ -54,4 +56,12 @@
     public void setDeprecated(boolean deprecated) {
         this.deprecated = deprecated;
     }
+
+    public Nullability getNullability() {
+        return nullability;
+    }
+
+    public void setNullability(Nullability nullability) {
+        this.nullability = nullability;
+    }
 }
diff --git a/tests/resources/simple_complex_content/api/current.txt b/tests/resources/simple_complex_content/api/current.txt
index a8c4e07..601a57e 100644
--- a/tests/resources/simple_complex_content/api/current.txt
+++ b/tests/resources/simple_complex_content/api/current.txt
@@ -31,20 +31,20 @@
     ctor public Person2();
     method public simple.complex.content.KRAddress getKRAddress();
     method public String getName();
-    method public simple.complex.content.SubAddress getSubAddress();
+    method @NonNull public simple.complex.content.SubAddress getSubAddress();
     method public simple.complex.content.USAddressP getUSAddressP();
     method public void setKRAddress(simple.complex.content.KRAddress);
     method public void setName(String);
-    method public void setSubAddress(simple.complex.content.SubAddress);
+    method public void setSubAddress(@NonNull simple.complex.content.SubAddress);
     method public void setUSAddressP(simple.complex.content.USAddressP);
   }
 
   public class SubAddress {
     ctor public SubAddress();
-    method public String getChoice1_optional();
-    method public String getChoice2_optional();
-    method public void setChoice1_optional(String);
-    method public void setChoice2_optional(String);
+    method @Nullable public String getChoice1_optional();
+    method @NonNull public String getChoice2_optional();
+    method public void setChoice1_optional(@Nullable String);
+    method public void setChoice2_optional(@NonNull String);
   }
 
   public final class USAddressP extends simple.complex.content.Address {
diff --git a/tests/resources/simple_complex_content/simple_complex_content.xsd b/tests/resources/simple_complex_content/simple_complex_content.xsd
index b6053a7..dc66cd3 100644
--- a/tests/resources/simple_complex_content/simple_complex_content.xsd
+++ b/tests/resources/simple_complex_content/simple_complex_content.xsd
@@ -33,8 +33,12 @@
     </xs:complexType>
     <xs:complexType name="subAddress">
         <xs:choice>
-            <xs:element name="choice1" type="xs:string"/>
-            <xs:element name="choice2" type="xs:string"/>
+            <xs:element name="choice1" type="xs:string">
+                <xs:annotation name="nullable"/>
+            </xs:element>
+            <xs:element name="choice2" type="xs:string">
+                <xs:annotation name="nonnull"/>
+            </xs:element>
         </xs:choice>
     </xs:complexType>
     <xs:element name="person">
@@ -53,7 +57,9 @@
                 <xs:element name="name" type="xs:string"/>
                 <xs:element name="USAddressP" type="USAddressP"/>
                 <xs:element name="KRAddress" type="KRAddress"/>
-                <xs:element name="subAddress" type="subAddress"/>
+                <xs:element name="subAddress" type="subAddress">
+                    <xs:annotation name="nonnull"/>
+                </xs:element>
             </xs:sequence>
         </xs:complexType>
     </xs:element>