Support base64Binary and hexBinary

The xml generator is not supported the base64Binary and hexBinary. So,
the base64Binary and hexBinary were treated specially unlike other types.

Bug: 178068675
Test: m -j && atest xsdc-java-tests && atest xsdc-cpp-tests
Change-Id: I0261dec0548cc6e2b886d2187c721655fe568e99
diff --git a/src/com/android/xsdc/java/JavaCodeGenerator.java b/src/com/android/xsdc/java/JavaCodeGenerator.java
index b578148..d55c230 100644
--- a/src/com/android/xsdc/java/JavaCodeGenerator.java
+++ b/src/com/android/xsdc/java/JavaCodeGenerator.java
@@ -39,6 +39,7 @@
     private boolean writer;
     private boolean showNullability;
     private boolean generateHasMethod;
+    private boolean useHexBinary;
 
     public JavaCodeGenerator(XmlSchema xmlSchema, String packageName, boolean writer,
             boolean showNullability, boolean generateHasMethod) throws JavaCodeGeneratorException {
@@ -47,6 +48,7 @@
         this.writer = writer;
         this.showNullability = showNullability;
         this.generateHasMethod = generateHasMethod;
+        useHexBinary = false;
 
         // class naming validation
         {
@@ -124,6 +126,11 @@
                 printXmlWriter(out);
             }
         }
+        if (useHexBinary) {
+            try (CodeWriter out = new CodeWriter(fs.getPrintWriter("HexBinaryHelper.java"))) {
+                printHexBinaryHelper(out);
+            }
+        }
     }
 
     private void printEnumClass(CodeWriter out, String name, XsdRestriction restrictionType)
@@ -621,6 +628,33 @@
         out.printf("}\n");
     }
 
+    private void printHexBinaryHelper(CodeWriter out) throws JavaCodeGeneratorException {
+        out.printf("package %s;\n", packageName);
+        out.println();
+        out.println("public class HexBinaryHelper {");
+        out.print("public static byte[] hexStringToByteArray(String hexString) {\n"
+                + "if (hexString.length() % 2 != 0) {\n"
+                + "throw new IllegalArgumentException(\"length must be multiple of 2\");\n"
+                + "}\n"
+                + "byte[] outputBytes = new byte[hexString.length() / 2];\n"
+                + "for (int i = 0; i < hexString.length(); i += 2) {\n"
+                + "char c1 = hexString.charAt(i);\n"
+                + "char c2 = hexString.charAt(i + 1);\n"
+                + "outputBytes[i / 2] = (byte) ((Character.digit(c1, 16) << 4)"
+                + " + Character.digit(c2, 16));\n"
+                + "}\n"
+                + "return outputBytes;"
+                + "}\n\n"
+                + "public static String byteArrayToHexString(byte[] b) {\n"
+                + "StringBuffer s = new StringBuffer();\n"
+                + "for (int i = 0; i < b.length; i++) {\n"
+                + "s.append(Integer.toHexString(0x100 + (b[i] & 0xff)).substring(1));\n"
+                + "}\n"
+                + "return s.toString();\n"
+                + "}\n"
+                + "}\n");
+    }
+
     private String getElementName(XsdElement element) {
         if (element instanceof XsdChoice) {
             return element.getName() + "_optional";
@@ -925,11 +959,16 @@
                 return new JavaSimpleType("float", "java.lang.Float", "Float.parseFloat(%s)",
                         false);
             case "base64Binary":
-                return new JavaSimpleType("byte[]", "java.util.Base64.getDecoder().decode(%s)",
+                return new JavaSimpleType("byte[]", "byte[]",
+                        "java.util.Base64.getDecoder().decode(%s)",
+                        "java.util.Base64.getEncoder().encodeToString(%s)",
                         false);
             case "hexBinary":
-                return new JavaSimpleType("java.math.BigInteger",
-                        "new java.math.BigInteger(%s, 16)", false);
+                useHexBinary = true;
+                return new JavaSimpleType("byte[]", "byte[]",
+                        "HexBinaryHelper.hexStringToByteArray(%s)",
+                        "HexBinaryHelper.byteArrayToHexString(%s)",
+                        false);
         }
         throw new JavaCodeGeneratorException("unknown xsd predefined type : " + name);
     }
diff --git a/src/com/android/xsdc/java/JavaSimpleType.java b/src/com/android/xsdc/java/JavaSimpleType.java
index 83c39b8..3535e89 100644
--- a/src/com/android/xsdc/java/JavaSimpleType.java
+++ b/src/com/android/xsdc/java/JavaSimpleType.java
@@ -20,19 +20,26 @@
     final private String name;
     final private String nullableName;
     final private String rawParsingExpression;
+    final private String rawWritingExpression;
     final private boolean list;
     final private String fullName;
     final private String nullableFullName;
 
-    JavaSimpleType(String name, String nullableName, String rawParsingExpression, boolean list) {
+    JavaSimpleType(String name, String nullableName, String rawParsingExpression,
+            String rawWritingExpression, boolean list) {
         this.name = name;
         this.nullableName = nullableName;
         this.rawParsingExpression = rawParsingExpression;
+        this.rawWritingExpression = rawWritingExpression;
         this.list = list;
         fullName = list ? String.format("java.util.List<%s>", nullableName) : name;
         nullableFullName = list ? String.format("java.util.List<%s>", nullableName) : nullableName;
     }
 
+    JavaSimpleType(String name, String nullableName, String rawParsingExpression, boolean list) {
+        this(name, nullableName, rawParsingExpression, "%s", list);
+    }
+
     JavaSimpleType(String name, String rawParsingExpression, boolean list) {
         this(name, name, rawParsingExpression, list);
     }
@@ -91,7 +98,8 @@
             expression.append("out.printf(\"%s\", v);\n}\n");
             expression.append("}\n");
         } else {
-            expression.append(String.format("out.printf(\"%%s\", %s);\n", getValue));
+            expression.append(String.format("out.printf(\"%%s\", %s);\n",
+                    String.format(rawWritingExpression, getValue)));
         }
         return expression.toString();
     }
diff --git a/tests/main.cpp b/tests/main.cpp
index 02a8891..4dbf2d0 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -130,7 +130,7 @@
   EXPECT_EQ(miscTypes.getAnyURI(), "https://www.google.com");
   EXPECT_EQ(miscTypes.getBase64Binary(), "Z29vZ2xl");
   EXPECT_TRUE(miscTypes.getBoolean());
-  EXPECT_EQ(miscTypes.getHexBinary(), "516a75cb56d7e7");
+  EXPECT_EQ(miscTypes.getHexBinary(), "016a75cb56d7e7");
   EXPECT_EQ(miscTypes.getQName(), "abcd");
   EXPECT_EQ(miscTypes.getIDREF(), "abcd");
   EXPECT_EQ(miscTypes.getIDREFS()[0], "abcd");
diff --git a/tests/resources/predefined_types.xml b/tests/resources/predefined_types.xml
index 7c4cf1b..9c24542 100644
--- a/tests/resources/predefined_types.xml
+++ b/tests/resources/predefined_types.xml
@@ -46,7 +46,7 @@
         <anyURI>https://www.google.com</anyURI>
         <base64Binary>Z29vZ2xl</base64Binary>
         <boolean>true</boolean>
-        <hexBinary>516a75cb56d7e7</hexBinary>
+        <hexBinary>016a75cb56d7e7</hexBinary>
         <QName>abcd</QName>
         <IDREF>abcd</IDREF>
         <IDREFS>abcd abcd</IDREFS>
diff --git a/tests/resources/predefined_types/api/current.txt b/tests/resources/predefined_types/api/current.txt
index 6a5ab13..e7141d3 100644
--- a/tests/resources/predefined_types/api/current.txt
+++ b/tests/resources/predefined_types/api/current.txt
@@ -23,6 +23,12 @@
     method public void setTime(javax.xml.datatype.XMLGregorianCalendar);
   }
 
+  public class HexBinaryHelper {
+    ctor public HexBinaryHelper();
+    method public static String byteArrayToHexString(byte[]);
+    method public static byte[] hexStringToByteArray(String);
+  }
+
   public class ListPrimitiveTypes {
     ctor public ListPrimitiveTypes();
     method public java.util.List<java.lang.Boolean> getListBoolean();
@@ -46,7 +52,7 @@
     method public String getAnyType();
     method public String getAnyURI();
     method public byte[] getBase64Binary();
-    method public java.math.BigInteger getHexBinary();
+    method public byte[] getHexBinary();
     method public String getIDREF();
     method public java.util.List<java.lang.String> getIDREFS();
     method public String getQName();
@@ -56,7 +62,7 @@
     method public void setAnyType(String);
     method public void setAnyURI(String);
     method public void setBase64Binary(byte[]);
-    method public void setHexBinary(java.math.BigInteger);
+    method public void setHexBinary(byte[]);
     method public void setIDREF(String);
     method public void setIDREFS(java.util.List<java.lang.String>);
     method public void setQName(String);
diff --git a/tests/src/com/android/xsdc/tests/XmlParserTest.java b/tests/src/com/android/xsdc/tests/XmlParserTest.java
index ed9aa08..f3b5eaf 100644
--- a/tests/src/com/android/xsdc/tests/XmlParserTest.java
+++ b/tests/src/com/android/xsdc/tests/XmlParserTest.java
@@ -180,7 +180,8 @@
             assertThat(miscTypes.getAnyURI(), is("https://www.google.com"));
             assertThat(miscTypes.getBase64Binary(), is(Base64.getDecoder().decode("Z29vZ2xl")));
             assertThat(miscTypes.get_boolean(), is(true));
-            assertThat(miscTypes.getHexBinary(), is(new BigInteger("516a75cb56d7e7", 16)));
+            assertThat(miscTypes.getHexBinary(),
+                    is(predefined.types.HexBinaryHelper.hexStringToByteArray("016a75cb56d7e7")));
             assertThat(miscTypes.getQName(), is("abcd"));
             assertThat(miscTypes.getIDREF(), is("abcd"));
             assertThat(miscTypes.getIDREFS(), is(Arrays.asList("abcd", "abcd")));
@@ -203,6 +204,21 @@
             assertThat(listPrimitiveTypes.getListFloat(), is(Arrays.asList(123.4f, 456.1f)));
             assertThat(listPrimitiveTypes.getListBoolean(), is(Arrays.asList(true, false)));
         }
+
+        String actualStr, expectedStr;
+        try (InputStream str = this.getClass().getClassLoader().getResourceAsStream(
+            "predefined_types.xml")) {
+            expectedStr = new String(str.readAllBytes());
+        }
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            try(predefined.types.XmlWriter writer =
+                    new predefined.types.XmlWriter(new PrintWriter(baos))) {
+                predefined.types.XmlWriter.write(writer, type);
+            }
+            actualStr = new String(baos.toByteArray());
+        }
+
+        assertThat(new String(actualStr), is(expectedStr));
     }
 
     @Test