THRIFT-3523 XML Generator
Client: XML
Patch: Benjamin Gould, with minor modifications by Jens Geyer

This closes #774
diff --git a/lib/xml/Makefile.am b/lib/xml/Makefile.am
new file mode 100644
index 0000000..bcad6bd
--- /dev/null
+++ b/lib/xml/Makefile.am
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+SUBDIRS =
+
+if WITH_JAVA
+# Schema validation test depends on java
+SUBDIRS += test
+endif
+
+EXTRA_DIST = \
+  thrift-idl.xsd \
+  test
diff --git a/lib/xml/test/Makefile.am b/lib/xml/test/Makefile.am
new file mode 100644
index 0000000..bb87a52
--- /dev/null
+++ b/lib/xml/test/Makefile.am
@@ -0,0 +1,26 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+check:
+	$(ANT) $(ANT_FLAGS) test
+
+# Make sure this doesn't fail if ant is not configured.
+clean-local:
+	ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \
+	$$ANT $(ANT_FLAGS) clean
diff --git a/lib/xml/test/build.xml b/lib/xml/test/build.xml
new file mode 100644
index 0000000..f0e95cf
--- /dev/null
+++ b/lib/xml/test/build.xml
@@ -0,0 +1,112 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-->
+<project name="XML Schema Test" default="test" basedir=".">
+
+  <description>XML Schema Validation Test</description>
+
+  <property name="xml.dir" location="${basedir}/.." />
+  <property name="gen.xml.dir" location="${basedir}/../gen-xml" />
+  <property name="idl.xml.schema" location="${xml.dir}/thrift-idl.xsd" />
+
+  <property name="thrift.dir" location="../../../" />
+  <property name="thrift.test.dir" location="${thrift.dir}/test" />
+  <property name="thrift.compiler" location="${thrift.dir}/compiler/cpp/thrift" />
+
+  <property file="${basedir}/build.properties" />
+
+  <target name="compiler.check">
+    <fail>
+      <condition>
+        <not>
+          <resourcecount count="1">
+            <fileset id="fs" file="${thrift.compiler}"/>
+          </resourcecount>
+        </not>
+      </condition>
+      Thrift compiler is missing !
+    </fail>
+  </target>
+
+  <target name="init" depends="compiler.check, mkdirs">
+    <tstamp />
+  </target>
+
+  <target name="mkdirs">
+    <mkdir dir="${gen.xml.dir}"/>
+  </target>
+
+  <target name="generate" depends="init">
+    <generate-xml file="${thrift.test.dir}/ThriftTest.thrift"/>
+    <generate-xml file="${thrift.test.dir}/Include.thrift"/>
+    <generate-xml file="${thrift.test.dir}/Recursive.thrift"/>
+    <generate-xml file="${thrift.test.dir}/ManyOptionals.thrift"/>
+    <generate-xml file="${thrift.test.dir}/OptionalRequiredTest.thrift"/>
+    <generate-xml file="${thrift.test.dir}/ConstantsDemo.thrift"/>
+    <generate-xml file="${thrift.test.dir}/TypedefTest.thrift" />
+    <generate-xml file="${thrift.test.dir}/AnnotationTest.thrift" />
+    <generate-xml file="${thrift.test.dir}/DocTest.thrift" />
+    <generate-xml file="${thrift.test.dir}/EnumTest.thrift" />
+    <generate-xml file="${thrift.test.dir}/ManyTypedefs.thrift" />
+  </target>
+
+  <target name="test" description="run schema validation"
+          depends="validate-generated-xml"/>
+    
+  <target name="validate-generated-xml" depends="init, generate">
+    <validate-xml file="${gen.xml.dir}/ThriftTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/Include.xml"/>
+    <validate-xml file="${gen.xml.dir}/Recursive.xml"/>
+    <validate-xml file="${gen.xml.dir}/ManyOptionals.xml"/>
+    <validate-xml file="${gen.xml.dir}/OptionalRequiredTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/ConstantsDemo.xml"/>
+    <validate-xml file="${gen.xml.dir}/TypedefTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/AnnotationTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/DocTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/EnumTest.xml"/>
+    <validate-xml file="${gen.xml.dir}/ManyTypedefs.xml"/>
+  </target>
+
+  <target name="clean">
+    <delete dir="${build.dir}" />
+    <delete dir="${gen.xml.dir}" />
+  </target>
+
+  <macrodef name="generate-xml">
+    <attribute name="file" />
+    <sequential>
+      <exec executable="${thrift.compiler}" failonerror="true">
+        <arg line="-gen xml:merge"/>
+        <arg line="-out ${gen.xml.dir}"/>
+        <arg line="@{file}"/>
+      </exec>
+    </sequential>
+  </macrodef>
+
+  <macrodef name="validate-xml">
+    <attribute name="file" />
+    <sequential>
+      <echo message="validating generated XML: @{file}" />
+      <schemavalidate file="@{file}">
+        <schema namespace="http://thrift.apache.org/xml/idl" 
+                file="${idl.xml.schema}" />
+      </schemavalidate>
+    </sequential>
+  </macrodef>
+
+</project>
diff --git a/lib/xml/thrift-idl.xsd b/lib/xml/thrift-idl.xsd
new file mode 100644
index 0000000..7a5248a
--- /dev/null
+++ b/lib/xml/thrift-idl.xsd
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-->
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+        targetNamespace="http://thrift.apache.org/xml/idl" 
+        xmlns:tns="http://thrift.apache.org/xml/idl" 
+        elementFormDefault="qualified">
+
+  <element name="idl" type="tns:IDL" />
+  <element name="document" type="tns:Document" />
+
+  <complexType name="IDL">
+    <sequence>
+      <element ref="tns:document" minOccurs="1" maxOccurs="unbounded"/>
+    </sequence>
+  </complexType>
+
+  <complexType name="Document">
+    <sequence>
+      <choice minOccurs="0" maxOccurs="unbounded">
+        <element name="include" type="tns:Include" />
+        <element name="namespace" type="tns:Namespace" />
+      </choice>
+      <choice minOccurs="0" maxOccurs="unbounded">
+        <element name="exception" type="tns:Exception" />
+        <element name="typedef" type="tns:Typedef" />
+        <element name="service" type="tns:Service" />
+        <element name="struct" type="tns:Struct" />
+        <element name="const" type="tns:Const" />
+        <element name="union" type="tns:Union" />
+        <element name="enum" type="tns:Enum" />
+      </choice>
+    </sequence>
+    <attribute name="name" type="string" />
+    <attribute name="targetNamespace" type="anyURI" />
+    <attribute name="doc" type="string" />
+  </complexType>
+
+  <complexType name="Include">
+    <attribute name="file" type="string" />
+    <attribute name="name" type="string" />
+  </complexType>
+
+  <complexType name="Namespace">
+    <sequence>
+      <element name="annotation" type="tns:Annotation" 
+               minOccurs="0" maxOccurs="unbounded" />
+    </sequence>
+    <attribute name="name" type="string" />
+    <attribute name="value" type="string" />
+    <attribute name="doc" type="string" />
+  </complexType>
+
+  <group name="AbstractStruct">
+    <sequence>
+      <element name="field" type="tns:Field" 
+               minOccurs="0" maxOccurs="unbounded" />
+      <element name="annotation" type="tns:Annotation" 
+               minOccurs="0" maxOccurs="unbounded" />
+    </sequence>
+  </group>
+
+  <attributeGroup name="StructAttributes">
+    <attribute name="name" type="string" />
+    <attribute name="doc" type="string" />
+  </attributeGroup>
+
+  <complexType name="Exception">
+    <group ref="tns:AbstractStruct" />
+    <attributeGroup ref="tns:StructAttributes" />
+  </complexType>
+
+  <complexType name="Service">
+    <sequence>
+      <element name="method" type="tns:Method" 
+               minOccurs="0" maxOccurs="unbounded" />
+      <element name="annotation" type="tns:Annotation" 
+               minOccurs="0" maxOccurs="unbounded" />
+    </sequence>
+    <attribute name="name" type="string" use="required" />
+    <attribute name="targetNamespace" type="string" use="required" />
+    <attribute name="parent-module" type="string" use="optional" />
+    <attribute name="parent-id" type="string" use="optional" /> 
+    <attribute name="doc" type="string" />
+  </complexType>
+
+  <complexType name="Method">
+    <sequence>
+      <element name="returns" type="tns:ThriftType" />
+      <element name="arg" type="tns:Field" 
+               minOccurs="0" maxOccurs="unbounded" />
+      <element name="throws" type="tns:Field" 
+               minOccurs="0" maxOccurs="unbounded" />
+      <element name="annotation" type="tns:Annotation" 
+               minOccurs="0" maxOccurs="unbounded" />
+    </sequence>
+    <attribute name="name" type="string" use="required" />
+    <attribute name="oneway" type="boolean" />
+    <attribute name="doc" type="string" />
+  </complexType>
+
+  <complexType name="Typedef">
+    <complexContent>
+      <extension base="tns:ThriftType">
+        <sequence>
+          <element name="annotation" type="tns:Annotation" 
+                   minOccurs="0" maxOccurs="unbounded" />
+        </sequence>
+        <attribute name="name" type="string" use="required" />
+        <attribute name="doc" type="string" />
+      </extension>
+    </complexContent>
+  </complexType>
+
+  <complexType name="Struct">
+    <group ref="tns:AbstractStruct" />
+    <attributeGroup ref="tns:StructAttributes" />
+  </complexType>
+
+  <complexType name="Union">
+    <group ref="tns:AbstractStruct" />
+    <attributeGroup ref="tns:StructAttributes" />
+  </complexType>
+
+  <complexType name="Enum">
+    <sequence>
+      <element name="member" minOccurs="1" maxOccurs="unbounded">
+        <complexType>
+          <sequence>
+            <element name="annotation" type="tns:Annotation" 
+                     minOccurs="0" maxOccurs="unbounded" />
+          </sequence>
+          <attribute name="name" type="string" use="required" />
+          <attribute name="value" type="int" />
+          <attribute name="explicit" type="boolean" />
+          <attribute name="doc" type="string" />
+        </complexType>
+      </element>
+      <element name="annotation" type="tns:Annotation" 
+                minOccurs="0" maxOccurs="unbounded" />
+    </sequence>
+    <attribute name="name" type="string" use="required" />
+    <attribute name="doc" type="string" />
+  </complexType>
+
+  <complexType name="Field">
+    <complexContent>
+      <extension base="tns:ThriftType">
+        <sequence>
+          <element name="default" minOccurs="0" maxOccurs="1">
+            <complexType>
+              <group ref="tns:ConstValue" />
+            </complexType>
+          </element>
+          <element name="annotation" type="tns:Annotation" 
+                   minOccurs="0" maxOccurs="unbounded" />
+        </sequence>
+        <attribute name="field-id" type="long" />
+        <attribute name="name" type="string" use="required" />
+        <attribute name="required" type="tns:Requiredness" />
+        <attribute name="doc" type="string" />
+      </extension>
+    </complexContent>
+  </complexType>
+
+  <simpleType name="Requiredness">
+    <restriction base="string">
+      <enumeration value="required" />
+      <enumeration value="optional" />
+    </restriction>
+  </simpleType>
+
+  <complexType name="Annotation">
+    <attribute name="key" type="string" />
+    <attribute name="value" type="string" />
+  </complexType>
+
+  <complexType name="Const">
+    <complexContent>
+      <extension base="tns:ThriftType">
+        <sequence>
+          <group ref="tns:ConstValue" />
+        </sequence>
+        <attribute name="name" type="string" use="required" />
+        <attribute name="doc" type="string" />
+      </extension>
+    </complexContent>
+  </complexType>
+
+  <complexType name="ConstList">
+    <sequence>
+      <element name="entry" minOccurs="0" maxOccurs="unbounded">
+        <complexType>
+          <group ref="tns:ConstValue" />
+        </complexType>
+      </element>
+    </sequence>
+  </complexType>
+
+  <complexType name="ConstMap">
+    <sequence>
+      <element name="entry" minOccurs="0" maxOccurs="unbounded">
+        <complexType>
+          <sequence>
+            <element name="key">
+              <complexType>
+                <group ref="tns:ConstValue" />
+              </complexType>
+            </element>
+            <element name="value">
+              <complexType>
+                <group ref="tns:ConstValue" />
+              </complexType>
+            </element>
+          </sequence>
+        </complexType>
+      </element>
+    </sequence>
+  </complexType>
+
+  <group name="ConstValue">
+    <choice>
+      <element name="string" type="string" />
+      <element name="double" type="double" />
+      <element name="list" type="tns:ConstList" />
+      <element name="map" type="tns:ConstMap" />
+      <element name="int" type="long" />
+    </choice>
+  </group>
+
+  <complexType name="ThriftType">
+    <sequence>
+      <choice minOccurs="0" maxOccurs="1">
+        <element name="elemType" type="tns:ThriftType" />
+        <sequence>
+          <element name="keyType" type="tns:ThriftType" 
+                   minOccurs="1" maxOccurs="1" />
+          <element name="valueType" type="tns:ThriftType" 
+                   minOccurs="1" maxOccurs="1" />
+        </sequence>
+      </choice>
+    </sequence>
+    <attribute name="type" type="tns:TypeIdentifier" use="required" />
+    <attribute name="type-module" type="string" />
+    <attribute name="type-id" type="string" />
+  </complexType>
+
+  <simpleType name="TypeIdentifier">
+    <restriction base="string">
+      <enumeration value="void" />
+      <enumeration value="bool" />
+      <enumeration value="byte" />
+      <enumeration value="i8" />
+      <enumeration value="i16" />
+      <enumeration value="i32" />
+      <enumeration value="i64" />
+      <enumeration value="double" />
+      <enumeration value="binary" />
+      <enumeration value="string" />
+      <enumeration value="id" />
+      <enumeration value="map" />
+      <enumeration value="set" />
+      <enumeration value="list" />
+    </restriction>
+  </simpleType>
+
+</schema>