Support "choice" tag

The "choice" tag is added for paring. This tag is similar to a
"sequence" tag.
The difference between the two tags are that "choice" tag allow the
maxOccurs and minOccurs and add the suffix to name as "_optional",
because to distinguish it from the sequence tag.

Test: m -j && atest xsdc-cpp-tests
Bug: 80453829

Change-Id: I73df54ff655208f99aeea242d915a1a4e9f506f2
diff --git a/src/com/android/xsdc/XsdHandler.java b/src/com/android/xsdc/XsdHandler.java
index 6589c1c..6494c1c 100644
--- a/src/com/android/xsdc/XsdHandler.java
+++ b/src/com/android/xsdc/XsdHandler.java
@@ -169,6 +169,9 @@
                 case "sequence":
                     stateStack.peek().tags.addAll(makeSequence(state.attributeMap, state.tags));
                     break;
+                case "choice":
+                    stateStack.peek().tags.addAll(makeChoice(state.attributeMap, state.tags));
+                    break;
                 case "enumeration":
                     stateStack.peek().tags.add(makeEnumeration(state.attributeMap));
                     enumerationFlag = true;
@@ -495,6 +498,20 @@
         return elements;
     }
 
+    private static List<XsdElement> makeChoice(Map<String, String> attributeMap,
+            List<XsdTag> tags) throws XsdParserException {
+        List<XsdElement> elements = new ArrayList<>();
+        for (XsdTag tag : tags) {
+            if (tag == null) continue;
+            if (tag instanceof XsdElement) {
+                XsdElement element = (XsdElement)tag;
+                elements.add(new XsdChoice(element.getName(), element.getRef(), element.getType(),
+                        element.isMultiple()));
+            }
+        }
+        return elements;
+    }
+
     private XsdEnumeration makeEnumeration(Map<String, String> attributeMap)
             throws XsdParserException {
         String value = attributeMap.get("value");
diff --git a/src/com/android/xsdc/cpp/CppCodeGenerator.java b/src/com/android/xsdc/cpp/CppCodeGenerator.java
index df4e2f8..121ac48 100644
--- a/src/com/android/xsdc/cpp/CppCodeGenerator.java
+++ b/src/com/android/xsdc/cpp/CppCodeGenerator.java
@@ -215,13 +215,13 @@
                     && element.getType() instanceof XsdComplexType) {
                 // print inner class for anonymous types
                 headerFile.printf("public:\n");
-                String innerName = Utils.toClassName(element.getName());
+                String innerName = Utils.toClassName(getElementName(element));
                 XsdComplexType innerType = (XsdComplexType) element.getType();
                 printClass(innerName, nameScope + name + "::", innerType);
                 headerFile.println();
                 cppType = new CppComplexType(nameScope + name + "::"+ innerName);
             } else {
-                cppType = parseType(elementValue.getType(), elementValue.getName());
+                cppType = parseType(elementValue.getType(), getElementName(elementValue));
             }
             elementTypes.add(cppType);
         }
@@ -241,7 +241,8 @@
             //String typeName = String.format("std::vector<%s>", type.getName());
             String typeName = element.isMultiple() || type instanceof CppComplexType ?
                     String.format("std::vector<%s>", type.getName()) : type.getName();
-            headerFile.printf("%s %s;\n", typeName, Utils.toVariableName(elementValue.getName()));
+            headerFile.printf("%s %s;\n", typeName,
+                    Utils.toVariableName(getElementName(elementValue)));
         }
         for (int i = 0; i < attributeTypes.size(); ++i) {
             CppType type = attributeTypes.get(i);
@@ -261,8 +262,8 @@
             XsdElement element = complexType.getElements().get(i);
             XsdElement elementValue = resolveElement(element);
             printGetterAndSetter(nameScope + name, type,
-                    Utils.toVariableName(elementValue.getName()), type instanceof CppComplexType ?
-                    true : element.isMultiple());
+                    Utils.toVariableName(getElementName(elementValue)),
+                    type instanceof CppComplexType ? true : element.isMultiple());
         }
         for (int i = 0; i < attributeTypes.size(); ++i) {
             CppType type = attributeTypes.get(i);
@@ -331,7 +332,7 @@
                 CppType type = allElementTypes.get(i);
                 XsdElement element = allElements.get(i);
                 XsdElement elementValue = resolveElement(element);
-                String variableName = Utils.toVariableName(elementValue.getName());
+                String variableName = Utils.toVariableName(getElementName(elementValue));
                 if (i != 0) cppFile.printf("} else ");
                 cppFile.printf("if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>");
                 cppFile.printf("(\"%s\"))) {\n", elementValue.getName());
@@ -444,6 +445,13 @@
         cppFile.printf("}\n\n");
     }
 
+    private String getElementName(XsdElement element) {
+        if (element instanceof XsdChoice) {
+            return element.getName() + "_optional";
+        }
+        return element.getName();
+    }
+
     private void stackComponents(XsdComplexType complexType, List<XsdElement> elements,
             List<XsdAttribute> attributes) throws CppCodeGeneratorException {
         if (complexType.getBase() != null) {
diff --git a/src/com/android/xsdc/java/JavaCodeGenerator.java b/src/com/android/xsdc/java/JavaCodeGenerator.java
index a4b36b0..63e2a66 100644
--- a/src/com/android/xsdc/java/JavaCodeGenerator.java
+++ b/src/com/android/xsdc/java/JavaCodeGenerator.java
@@ -162,14 +162,14 @@
             if (element.getRef() == null && element.getType().getRef() == null
                     && element.getType() instanceof XsdComplexType) {
                 // print inner class for anonymous types
-                String innerName = Utils.toClassName(element.getName());
+                String innerName = Utils.toClassName(getElementName(element));
                 XsdComplexType innerType = (XsdComplexType) element.getType();
                 String innerNameScope = nameScope + name + ".";
                 printClass(out, innerName, innerType, innerNameScope);
                 out.println();
                 javaType = new JavaComplexType(innerNameScope + innerName);
             } else {
-                javaType = parseType(elementValue.getType(), elementValue.getName());
+                javaType = parseType(elementValue.getType(), getElementName(elementValue));
             }
             elementTypes.add(javaType);
         }
@@ -186,7 +186,8 @@
             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(elementValue.getName()));
+            out.printf("private %s %s;\n", typeName,
+                    Utils.toVariableName(getElementName(elementValue)));
         }
         for (int i = 0; i < attributeTypes.size(); ++i) {
             JavaType type = attributeTypes.get(i);
@@ -203,7 +204,7 @@
             JavaType type = elementTypes.get(i);
             XsdElement element = complexType.getElements().get(i);
             XsdElement elementValue = resolveElement(element);
-            printGetterAndSetter(out, type, Utils.toVariableName(elementValue.getName()),
+            printGetterAndSetter(out, type, Utils.toVariableName(getElementName(elementValue)),
                     element.isMultiple());
         }
         for (int i = 0; i < attributeTypes.size(); ++i) {
@@ -274,7 +275,7 @@
                 JavaType type = allElementTypes.get(i);
                 XsdElement element = allElements.get(i);
                 XsdElement elementValue = resolveElement(element);
-                String variableName = Utils.toVariableName(elementValue.getName());
+                String variableName = Utils.toVariableName(getElementName(elementValue));
                 out.printf("if (tagName.equals(\"%s\")) {\n", elementValue.getName());
                 if (type instanceof JavaSimpleType) {
                     out.print("raw = XmlParser.readText(parser);\n");
@@ -387,6 +388,13 @@
         out.println("}");
     }
 
+    private String getElementName(XsdElement element) {
+        if (element instanceof XsdChoice) {
+            return element.getName() + "_optional";
+        }
+        return element.getName();
+    }
+
     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/XsdChoice.java b/src/com/android/xsdc/tag/XsdChoice.java
new file mode 100644
index 0000000..223bb44
--- /dev/null
+++ b/src/com/android/xsdc/tag/XsdChoice.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.xsdc.tag;
+
+import com.android.xsdc.XsdParserException;
+
+import javax.xml.namespace.QName;
+
+public class XsdChoice extends XsdElement {
+    public XsdChoice(String name, QName ref, XsdType type, boolean multiple)
+            throws XsdParserException {
+        super(name, ref, type, multiple);
+    }
+}
diff --git a/tests/main.cpp b/tests/main.cpp
index f3f302b..cd07467 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -182,6 +182,7 @@
   Person person = read("resources/simple_complex_content.xml")[0];
   USAddressP uSAddressP = person.getUSAddressP()[0];
   KRAddress kRAddress = person.getKRAddress()[0];
+  SubAddress subAddress = person.getSubAddress()[0];
 
   EXPECT_EQ(person.getName(), "Petr");
 
@@ -194,6 +195,8 @@
   EXPECT_EQ(kRAddress.getName(), "Donga Studio");
   EXPECT_EQ(kRAddress.getStreet(), "Nokdu Street");
   EXPECT_EQ(kRAddress.getCity(), "Seoul");
+
+  EXPECT_EQ(subAddress.getChoice1_optional(), "Temp");
 }
 
 int main(int argc, char **argv) {
diff --git a/tests/resources/simple_complex_content.xml b/tests/resources/simple_complex_content.xml
index 505dd35..06c590f 100644
--- a/tests/resources/simple_complex_content.xml
+++ b/tests/resources/simple_complex_content.xml
@@ -15,4 +15,7 @@
         <street>Nokdu Street</street>
         <city>Seoul</city>
     </KRAddress>
+    <subAddress>
+        <choice1>Temp</choice1>
+    </subAddress>
 </person>
diff --git a/tests/resources/simple_complex_content/api/current.txt b/tests/resources/simple_complex_content/api/current.txt
index 5681c5b..d987dbc 100644
--- a/tests/resources/simple_complex_content/api/current.txt
+++ b/tests/resources/simple_complex_content/api/current.txt
@@ -18,12 +18,22 @@
     ctor public Person();
     method public simple.complex.content.KRAddress getKRAddress();
     method public java.lang.String getName();
+    method 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(java.lang.String);
+    method public void setSubAddress(simple.complex.content.SubAddress);
     method public void setUSAddressP(simple.complex.content.USAddressP);
   }
 
+  public class SubAddress {
+    ctor public SubAddress();
+    method public java.lang.String getChoice1_optional();
+    method public java.lang.String getChoice2_optional();
+    method public void setChoice1_optional(java.lang.String);
+    method public void setChoice2_optional(java.lang.String);
+  }
+
   public class USAddressP extends simple.complex.content.Address {
     ctor public USAddressP();
     method public java.lang.String getState();
diff --git a/tests/resources/simple_complex_content/simple_complex_content.xsd b/tests/resources/simple_complex_content/simple_complex_content.xsd
index 34ac508..e6be1eb 100644
--- a/tests/resources/simple_complex_content/simple_complex_content.xsd
+++ b/tests/resources/simple_complex_content/simple_complex_content.xsd
@@ -27,12 +27,19 @@
             </xs:restriction>
         </xs:complexContent>
     </xs:complexType>
+    <xs:complexType name="subAddress">
+        <xs:choice>
+            <xs:element name="choice1" type="xs:string"/>
+            <xs:element name="choice2" type="xs:string"/>
+        </xs:choice>
+    </xs:complexType>
     <xs:element name="person">
         <xs:complexType>
             <xs:sequence>
                 <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:sequence>
         </xs:complexType>
     </xs:element>