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>