blob: 6c2e9ad15045d747bd3c4b346fb849cf04b77189 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache
* XMLBeans", nor may "Apache" appear in their name, without prior
* written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2003 BEA Systems
* Inc., <http://www.bea.com/>. For more information on the Apache Software
* Foundation, please see <http://www.apache.org/>.
*/
package org.apache.xmlbeans.impl.binding.compile;
import org.apache.xmlbeans.impl.binding.bts.BindingFile;
import org.apache.xmlbeans.impl.binding.bts.BindingType;
import org.apache.xmlbeans.impl.binding.bts.BindingLoader;
import org.apache.xmlbeans.impl.binding.bts.XmlName;
import org.apache.xmlbeans.impl.binding.bts.JavaName;
import org.apache.xmlbeans.impl.binding.bts.ByNameBean;
import org.apache.xmlbeans.impl.binding.bts.QNameProperty;
import org.apache.xmlbeans.impl.binding.bts.SimpleBindingType;
import org.apache.xmlbeans.impl.binding.bts.BindingTypeName;
import org.apache.xmlbeans.impl.binding.compile.BindingFileGenerator;
import org.apache.xmlbeans.impl.binding.compile.JavaCodeGenerator;
import org.apache.xmlbeans.impl.binding.compile.JavaCodePrinter;
import org.apache.xmlbeans.impl.common.NameUtil;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.SchemaTypeSystem;
import org.apache.xmlbeans.SchemaProperty;
import org.apache.xmlbeans.SchemaLocalAttribute;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.soap.SOAPArrayType;
import org.apache.xmlbeans.soap.SchemaWSDLArrayType;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.Collections;
import java.util.AbstractCollection;
import java.util.List;
import java.math.BigInteger;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.io.IOException;
/**
* This is the "JAXRPC-style" Schema-to-bts compiler.
*/
public class JAXRPCSchemaBinder implements JavaCodeGenerator, BindingFileGenerator, SchemaToJavaResult
{
private Set usedNames = new HashSet();
private SchemaTypeSystem sts;
private Map scratchFromXmlName = new LinkedHashMap();
private Map scratchFromSchemaType = new HashMap(); // for convenience
private Map scratchFromJavaNameString = new HashMap(); // for printing
private BindingLoader path;
private int structureCount;
private BindingFile bindingFile = new BindingFile();
private JAXRPCSchemaBinder(SchemaTypeSystem sts, BindingLoader path)
{
this.sts = sts;
this.path = path;
}
/**
* Generates a binding for the given set of schema types.
*
* Defer to any previously defined bindings on the BindingLoader path
* supplied.
*/
public static SchemaToJavaResult bind(SchemaTypeSystem sts, BindingLoader path)
{
JAXRPCSchemaBinder binder = new JAXRPCSchemaBinder(sts, path);
binder.bind();
return binder;
}
/**
* Computes the binding.
*/
private void bind()
{
// Every type or global element or global attribute is dropped into
// one of a number of categories. (See Scratch constants.)
//
// Based on the category, a java class is either defined or found
// that maches with the schema type.
//
// The phases work as follows:
//
// 1. categorize and allocate Scratches
// 2. write or find java names for each xml type
// 3. allocate binding types for each scratch (done in the same pass as 2)
// 4. fill in the java getter/setter structure of any complex types
// 1. categorize and allocate Scratches
createScratchArea();
// 2. write or find java names for each xml type
// 3. allocate binding types for each scratch (done in the same pass as 2)
for (Iterator i = scratchIterator(); i.hasNext(); )
{
Scratch scratch = (Scratch)i.next();
resolveJavaName(scratch);
createBindingType(scratch);
}
// 4. fill in the java getter/setter structure of any complex types
for (Iterator i = scratchIterator(); i.hasNext(); )
{
resolveJavaStructure((Scratch)i.next());
}
}
/**
* This function goes through all relevant schema types, plus soap
* array types, and creates a scratch area for each. Each
* scratch area is also marked at this time with an XmlName,
* a schema type, and a category.
*/
private void createScratchArea()
{
for (Iterator i = allTypeIterator(); i.hasNext(); )
{
SchemaType sType = (SchemaType)i.next();
XmlName xmlName = XmlName.forSchemaType(sType);
Scratch scratch;
if (sType.isSimpleType())
{
// simple types are atomic
// todo: what about simple content, custom codecs, etc?
scratch = new Scratch(sType, xmlName, Scratch.ATOMIC_TYPE);
}
else if (sType.isDocumentType())
{
scratch = new Scratch(sType, XmlName.forGlobalName(XmlName.ELEMENT, sType.getDocumentElementName()), Scratch.ELEMENT);
}
else if (sType.isAttributeType())
{
scratch = new Scratch(sType, XmlName.forGlobalName(XmlName.ATTRIBUTE, sType.getDocumentElementName()), Scratch.ATTRIBUTE);
}
else if (isSoapArray(sType))
{
scratch = new Scratch(sType, xmlName, Scratch.SOAPARRAY_REF);
xmlName = soapArrayTypeName(sType);
scratch.setAsIf(xmlName);
// soap arrays unroll like this
while (xmlName.getComponentType() == XmlName.SOAP_ARRAY)
{
scratch = new Scratch(null, xmlName, Scratch.SOAPARRAY);
scratchFromXmlName.put(xmlName, scratch);
xmlName = xmlName.getOuterComponent();
}
}
else if (isLiteralArray(sType))
{
scratch = new Scratch(sType, xmlName, Scratch.LITERALARRAY_TYPE);
}
else
{
scratch = new Scratch(sType, xmlName, Scratch.STRUCT_TYPE);
}
scratchFromXmlName.put(xmlName, scratch);
scratchFromSchemaType.put(sType, scratch);
}
}
/**
* Computes a JavaName for each scratch. Notice that structures and
* atoms can be computed directly, but arrays, elements, etc, need
* to defer to other scratch areas, so this is a resolution
* process that occurs in dependency order.
*/
private void resolveJavaName(Scratch scratch)
{
// already resolved (we recurse to do in dependency order)
if (scratch.getJavaName() != null)
return;
switch (scratch.getCategory())
{
case Scratch.ATOMIC_TYPE:
{
resolveSimpleScratch(scratch);
return;
}
case Scratch.STRUCT_TYPE:
{
structureCount += 1;
JavaName javaName = pickUniqueJavaName(scratch.getSchemaType());
scratch.setJavaName(javaName);
scratchFromJavaNameString.put(javaName.toString(), scratch);
return;
}
case Scratch.LITERALARRAY_TYPE:
{
SchemaType itemType = getLiteralArrayItemType(scratch.getSchemaType());
Scratch itemScratch = scratchForSchemaType(itemType);
resolveJavaName(itemScratch);
scratch.setJavaName(JavaName.forArray(itemScratch.getJavaName(), 1));
return;
}
case Scratch.SOAPARRAY_REF:
{
XmlName soapArrayName = scratch.getAsIf();
Scratch arrayScratch = scratchForXmlName(soapArrayName);
resolveJavaName(arrayScratch);
scratch.setJavaName(arrayScratch.getJavaName());
scratch.setAsIf(arrayScratch.getXmlName());
return;
}
case Scratch.SOAPARRAY:
{
XmlName arrayName = scratch.getXmlName();
XmlName itemName = arrayName.getOuterComponent();
Scratch itemScratch = scratchForXmlName(itemName);
resolveJavaName(itemScratch);
scratch.setJavaName(JavaName.forArray(itemScratch.getJavaName(), arrayName.getNumber()));
return;
}
case Scratch.ELEMENT:
case Scratch.ATTRIBUTE:
{
SchemaType contentType = scratch.getSchemaType().getProperties()[0].getType();
Scratch contentScratch = scratchForSchemaType(contentType);
resolveJavaName(contentScratch);
scratch.setJavaName(contentScratch.getJavaName());
scratch.setAsIf(contentScratch.getXmlName());
return;
}
default:
throw new IllegalStateException("Unrecognized category");
}
}
/**
* Computes a BindingType for a scratch.
*/
private void createBindingType(Scratch scratch)
{
assert(scratch.getBindingType() == null);
BindingTypeName btName = BindingTypeName.forPair(scratch.getJavaName(), scratch.getXmlName());
switch (scratch.getCategory())
{
case Scratch.ATOMIC_TYPE:
case Scratch.ELEMENT:
case Scratch.SOAPARRAY_REF:
case Scratch.ATTRIBUTE:
SimpleBindingType simpleResult = new SimpleBindingType(btName);
simpleResult.setAsIfXmlType(scratch.getAsIf());
scratch.setBindingType(simpleResult);
bindingFile.addBindingType(simpleResult, shouldBeFromJavaDefault(btName), true);
break;
case Scratch.STRUCT_TYPE:
ByNameBean byNameResult = new ByNameBean(btName);
scratch.setBindingType(byNameResult);
bindingFile.addBindingType(byNameResult, true, true);
break;
case Scratch.LITERALARRAY_TYPE:
throw new UnsupportedOperationException();
case Scratch.SOAPARRAY:
throw new UnsupportedOperationException();
default:
throw new IllegalStateException("Unrecognized category");
}
}
/**
* Returns true if the given btName would be the first (unique)
* default java->xml binding to be entered in the java-to-type or
* java-to-element tables.
*/
private boolean shouldBeFromJavaDefault(BindingTypeName btName)
{
JavaName jName = btName.getJavaName();
XmlName xName = btName.getXmlName();
if (xName.isSchemaType())
{
return (bindingFile.lookupTypeFor(jName) == null &&
path.lookupTypeFor(jName) == null);
}
if (xName.getComponentType() == XmlName.ELEMENT)
{
return (bindingFile.lookupElementFor(jName) == null &&
path.lookupElementFor(jName) == null);
}
return false;
}
/**
* Now we resolve the structural aspects (property names) for each
* scratch.
*
* todo: understand how we want inheritance to work
*/
private void resolveJavaStructure(Scratch scratch)
{
if (scratch.getCategory() != Scratch.STRUCT_TYPE)
return;
if (scratch.isStructureResolved())
return;
scratch.setStructureResolved(true);
SchemaType baseType = scratch.getSchemaType().getBaseType();
Collection baseProperties = null;
if (baseType != null)
baseProperties = extractProperties(baseType);
if (baseProperties == null)
baseProperties = Collections.EMPTY_LIST;
// sort properties based on QName attr/elt
Map seenAttrProps = new HashMap();
Map seenEltProps = new HashMap();
Set seenMethodNames = new HashSet();
seenMethodNames.add("getClass");
for (Iterator i = baseProperties.iterator(); i.hasNext(); )
{
QNameProperty prop = (QNameProperty)i.next();
if (prop.isAttribute())
seenAttrProps.put(prop.getQName(), prop);
else
seenEltProps.put(prop.getQName(), prop);
// todo: probably this collision avoidance should be using Java introspection instead
if (prop.getGetterName() != null)
seenMethodNames.add(prop.getGetterName());
if (prop.getSetterName() != null)
seenMethodNames.add(prop.getSetterName());
}
// now deal with remaining props
SchemaProperty[] props = scratch.getSchemaType().getProperties();
for (int i = 0; i < props.length; i++)
{
QNameProperty prop = (QNameProperty)(props[i].isAttribute() ? seenAttrProps : seenEltProps).get(props[i].getName());
if (prop != null)
{
// already seen property: verify multiplicity looks cool
if (prop.isMultiple() != isMultiple(props[i]))
{
// todo: signal nicer error
throw new IllegalStateException("Can't change multiplicity");
}
// todo: think about optionality and nillability too
}
else
{
SchemaType sType = props[i].getType();
BindingType bType = bindingTypeForSchemaType(sType);
String propName = pickUniquePropertyName(props[i].getName(), seenMethodNames);
String getter = "get" + propName;
String setter = "set" + propName;
boolean isMultiple = isMultiple(props[i]);
JavaName collection = null;
if (isMultiple)
collection = JavaName.forArray(bType.getName().getJavaName(), 1);
prop = new QNameProperty();
prop.setQName(props[i].getName());
prop.setAttribute(props[i].isAttribute());
prop.setSetterName(setter);
prop.setGetterName(getter);
prop.setCollectionClass(collection);
prop.setBindingType(bType);
prop.setNillable(props[i].hasNillable() != SchemaProperty.NEVER);
prop.setOptional(isOptional(props[i]));
prop.setMultiple(isMultiple);
}
scratch.addQNameProperty(prop);
}
}
/**
* Picks a property name without colliding with names of
* previously picked getters and setters.
*/
private String pickUniquePropertyName(QName name, Set seenMethodNames)
{
String baseName = NameUtil.upperCamelCase(name.getLocalPart());
String propName = baseName;
for (int i = 1; ; i += 1)
{
String getter = "get" + propName;
String setter = "set" + propName;
if (!seenMethodNames.contains(getter) &&
!seenMethodNames.contains(setter))
{
seenMethodNames.add(getter);
seenMethodNames.add(setter);
return propName;
}
propName = baseName + i;
}
}
/**
* True if the given SchemaProperty has maxOccurs > 1
*/
private static boolean isMultiple(SchemaProperty prop)
{
return (prop.getMaxOccurs() == null || prop.getMaxOccurs().compareTo(BigInteger.ONE) > 0);
}
/**
* True if the given SchemaProperty has minOccurs < 1
*/
private static boolean isOptional(SchemaProperty prop)
{
return (prop.getMinOccurs().signum() == 0);
}
/**
* Returns a collection of QNameProperties for a given schema type that
* may either be on the path or the current scratch area.
*/
private Collection extractProperties(SchemaType sType)
{
// case 1: it's in the current area
Scratch scratch = scratchForSchemaType(sType);
if (scratch != null)
{
resolveJavaStructure(scratch);
return scratch.getQNameProperties();
}
// case 2: it's in the path
BindingType bType = path.getBindingType(path.lookupPojoFor(XmlName.forSchemaType(sType)));
if (!(bType instanceof ByNameBean))
{
return null;
}
Collection result = new ArrayList();
ByNameBean bnb = (ByNameBean)bType;
for (Iterator i = bnb.getProperties().iterator(); i.hasNext(); )
{
result.add(i.next());
}
return result;
}
/**
* True for a schema type that is a SOAP array.
*/
private static boolean isSoapArray(SchemaType sType)
{
// SOAP Array definition must be put on the compiletime classpath
while (sType != null)
{
String signature = XmlName.forSchemaType(sType).toString();
// captures both SOAP 1.1 and SOAP 1.2+
if (signature.equals("t=Array@http://schemas.xmlsoap.org/soap/encoding/") ||
signature.startsWith("t=Array@http://www.w3.org/")
&& signature.endsWith("/soap-encoding"))
return true;
sType = sType.getBaseType();
}
return false;
}
private static final QName arrayType = new QName("http://schemas.xmlsoap.org/soap/encoding/", "arrayType");
/**
* Returns an XmlName describing a SOAP array.
*/
private static XmlName soapArrayTypeName(SchemaType sType)
{
// first, look for wsdl:arrayType default - this will help us with multidimensional arrays
SOAPArrayType defaultArrayType = null;
SchemaLocalAttribute attr = sType.getAttributeModel().getAttribute(arrayType);
if (attr != null)
defaultArrayType = ((SchemaWSDLArrayType)attr).getWSDLArrayType();
// method 1: trust wsdl:arrayType
if (defaultArrayType != null)
return XmlName.forSoapArrayType(defaultArrayType);
// method 2: SOAP 1.2 equivalent?
// todo: track what do WSDLs do in the world of SOAP 1.2.
// method 3: look at the type of a unique element.
SchemaType itemType = XmlObject.type;
SchemaProperty[] props = sType.getElementProperties();
if (props.length == 1)
itemType = props[0].getType();
return XmlName.forNestedNumber(XmlName.SOAP_ARRAY, 1, XmlName.forSchemaType(itemType));
}
/**
* Climbs the structure of a schema type to find the namespace within
* which it was defined.
*/
private String findContainingNamespace(SchemaType sType)
{
for (;;)
{
if (sType.isDocumentType())
return sType.getDocumentElementName().getNamespaceURI();
else if (sType.isAttributeType())
return sType.getAttributeTypeAttributeName().getNamespaceURI();
else if (sType.getName() != null)
return sType.getName().getNamespaceURI();
sType = sType.getOuterType();
}
}
/**
* Picks a unique fully-qualified Java class name for the given schema
* type. Uses and updates the "usedNames" set.
*/
private JavaName pickUniqueJavaName(SchemaType sType)
{
QName qname = null;
while (qname == null)
{
if (sType.isDocumentType())
qname = sType.getDocumentElementName();
else if (sType.isAttributeType())
qname = sType.getAttributeTypeAttributeName();
else if (sType.getName() != null)
qname = sType.getName();
else if (sType.getContainerField() != null)
{
qname = sType.getContainerField().getName();
if (qname.getNamespaceURI().length() == 0)
qname = new QName(findContainingNamespace(sType), qname.getLocalPart());
}
sType = sType.getOuterType();
}
String baseName = NameUtil.getClassNameFromQName(qname);
String pickedName = baseName;
for (int i = 1; usedNames.contains(pickedName); i += 1)
pickedName = baseName + i;
usedNames.add(pickedName);
return JavaName.forString(pickedName);
}
/**
* Resolves an atomic scratch all at once, including its
* JavaName and basedOn fields.
*
* This resolution method sets up a scratch so that is
* is "based on" another binding type. It finds the
* underlying binding type by climing the base type
* chain, and grabbing the first hit.
*/
private void resolveSimpleScratch(Scratch scratch)
{
assert(scratch.getCategory() == Scratch.ATOMIC_TYPE);
if (scratch.getJavaName() != null)
return;
SchemaType baseType = scratch.getSchemaType().getBaseType();
while (baseType != null)
{
// find a base type within this type system
Scratch basedOnScratch = scratchForSchemaType(baseType);
if (basedOnScratch != null)
{
if (basedOnScratch.getCategory() != Scratch.ATOMIC_TYPE)
throw new IllegalStateException("Atomic types should only inherit from atomic types");
resolveSimpleScratch(basedOnScratch);
scratch.setJavaName(basedOnScratch.getJavaName());
scratch.setAsIf(basedOnScratch.getXmlName());
return;
}
// or if not within this type system, find the base type on the path
XmlName treatAs = XmlName.forSchemaType(baseType);
BindingType basedOnBinding = path.getBindingType(path.lookupPojoFor(treatAs));
if (basedOnBinding != null)
{
scratch.setJavaName(basedOnBinding.getName().getJavaName());
scratch.setAsIf(treatAs);
return;
}
// or go to the next base type up
baseType = baseType.getBaseType();
}
// builtin at least should give us xs:anyType
throw new IllegalStateException("Builtin binding type loader is not on path.");
}
/**
* Looks on both the path and in the current scratch area for
* the binding type corresponding to the given schema type. Must
* be called after all the binding types have been created.
*/
private BindingType bindingTypeForSchemaType(SchemaType sType)
{
Scratch scratch = scratchForSchemaType(sType);
if (scratch != null)
return scratch.getBindingType();
return path.getBindingType(path.lookupPojoFor(XmlName.forSchemaType(sType)));
}
/**
* Returns the scratch area for a given schema type. Notice that
* SOAP arrays have an XmlName but not a schema type.
*/
private Scratch scratchForSchemaType(SchemaType sType)
{
return (Scratch)scratchFromSchemaType.get(sType);
}
/**
* Returns the scratch area for a given XmlName.
*/
private Scratch scratchForXmlName(XmlName xmlName)
{
return (Scratch)scratchFromXmlName.get(xmlName);
}
/**
* Returns the scratch area for a given JavaName. Notice that only
* structures generate a java class, so not non-strucuture scratch areas
* cannot be referenced this way.
*/
private Scratch scratchForJavaNameString(String javaName)
{
return (Scratch)scratchFromJavaNameString.get(javaName);
}
/**
* Extracts the schema type for the array items for a literal array.
*/
private static SchemaType getLiteralArrayItemType(SchemaType sType)
{
// consider: must the type be named "ArrayOf..."?
if (sType.isSimpleType() || sType.getContentType() == SchemaType.SIMPLE_CONTENT)
return null;
SchemaProperty[] prop = sType.getProperties();
if (prop.length != 1 || prop[0].isAttribute())
return null;
BigInteger max = prop[0].getMaxOccurs();
if (max != null && max.compareTo(BigInteger.ONE) <= 0)
return null;
return prop[0].getType();
}
/**
* True if the given schema type is interpreted as a .NET-style
* array.
*/
private static boolean isLiteralArray(SchemaType sType)
{
return getLiteralArrayItemType(sType) != null;
}
/**
* Scratch area corresponding to a schema type, used for the binding
* computation.
*/
private static class Scratch
{
Scratch(SchemaType schemaType, XmlName xmlName, int category)
{
this.schemaType = schemaType;
this.xmlName = xmlName;
this.category = category;
}
private BindingType bindingType;
private SchemaType schemaType; // may be null
private JavaName javaName;
private XmlName xmlName;
private int category;
// atomic types get a treatAs
private XmlName asIf;
private boolean isStructureResolved;
// categories of Scratch, established at ctor time
public static final int ATOMIC_TYPE = 1;
public static final int STRUCT_TYPE = 2;
public static final int LITERALARRAY_TYPE = 3;
public static final int SOAPARRAY_REF = 4;
public static final int SOAPARRAY = 5;
public static final int ELEMENT = 6;
public static final int ATTRIBUTE = 7;
public int getCategory()
{
return category;
}
public JavaName getJavaName()
{
return javaName;
}
public void setJavaName(JavaName javaName)
{
this.javaName = javaName;
}
public BindingType getBindingType()
{
return bindingType;
}
public void setBindingType(BindingType bindingType)
{
this.bindingType = bindingType;
}
public SchemaType getSchemaType()
{
return schemaType;
}
public XmlName getXmlName()
{
return xmlName;
}
public void setXmlName(XmlName xmlName)
{
this.xmlName = xmlName;
}
public XmlName getAsIf()
{
return asIf;
}
public void setAsIf(XmlName xmlName)
{
this.asIf = xmlName;
}
public void addQNameProperty(QNameProperty prop)
{
if (!(bindingType instanceof ByNameBean))
throw new IllegalStateException();
((ByNameBean)bindingType).addProperty(prop);
}
public Collection getQNameProperties()
{
if (!(bindingType instanceof ByNameBean))
throw new IllegalStateException();
return ((ByNameBean)bindingType).getProperties();
}
public boolean isStructureResolved()
{
return this.isStructureResolved;
}
public void setStructureResolved(boolean isStructureResolved)
{
this.isStructureResolved = isStructureResolved;
}
}
/**
* Returns an iterator for all the Scratch's
*/
private Iterator scratchIterator()
{
return scratchFromXmlName.values().iterator();
}
/**
* Returns an iterator for all the schema types
*/
private Iterator allTypeIterator()
{
class AllTypeIterator implements Iterator
{
int index;
List allSeenTypes;
AllTypeIterator(SchemaTypeSystem sts)
{
allSeenTypes = new ArrayList();
allSeenTypes.addAll(Arrays.asList(sts.documentTypes()));
allSeenTypes.addAll(Arrays.asList(sts.attributeTypes()));
allSeenTypes.addAll(Arrays.asList(sts.globalTypes()));
index = 0;
}
public boolean hasNext()
{
return index < allSeenTypes.size();
}
public Object next()
{
SchemaType next = (SchemaType)allSeenTypes.get(index);
allSeenTypes.addAll(Arrays.asList(next.getAnonymousTypes()));
index += 1;
return next;
}
public void remove()
{
throw new UnsupportedOperationException();
}
}
return new AllTypeIterator(sts);
}
/**
* Iterates over all the top-level Java class names generated
* by this binding. Used by getToplevelClasses.
*/
private class TopLevelClassNameIterator implements Iterator
{
private final Iterator si = scratchIterator();
private Scratch next = nextStructure();
public boolean hasNext()
{
return next != null;
}
public Object next()
{
// todo: need to strip off x= from xmlobject name
String result = next.getJavaName().toString();
next = nextStructure();
return result;
}
private Scratch nextStructure()
{
while (si.hasNext())
{
Scratch scratch = (Scratch)si.next();
if (scratch.getCategory() == Scratch.STRUCT_TYPE)
return scratch;
}
return null;
}
public void remove()
{
throw new UnsupportedOperationException();
}
}
/**
* Returns a collection of fully-qualified Java class name strings
* generated by this binding.
*/
public Collection getToplevelClasses()
{
return new AbstractCollection()
{
public Iterator iterator()
{
return new TopLevelClassNameIterator();
}
public int size()
{
return structureCount;
}
};
}
/**
* Prints the Java source code for the given generated java class name.
*/
public void printSourceCode(String topLevelClassName, OutputStream output) throws IOException
{
Scratch scratch = scratchForJavaNameString(topLevelClassName);
if (scratch == null)
throw new IllegalArgumentException();
Writer writer = new OutputStreamWriter(output);
ScratchCodePrinter printer = new ScratchCodePrinter(scratch, writer);
printer.print();
writer.flush();
}
/**
* Handles the printing for one generated java class.
*/
private class ScratchCodePrinter extends JavaCodePrinter
{
private Scratch scratch;
ScratchCodePrinter(Scratch scratch, Writer writer)
{
super(writer);
assert(scratch.getCategory() == Scratch.STRUCT_TYPE);
this.scratch = scratch;
}
private void print() throws IOException
{
JavaName javaName = scratch.getJavaName();
String packageName = javaName.getPackage();
String shortClassName = javaName.getShortClassName();
BindingType baseType = bindingTypeForSchemaType(scratch.getSchemaType().getBaseType());
String baseJavaname = null;
if (baseType != null)
{
baseJavaname = baseType.getName().getJavaName().toString();
if (baseJavaname.equals("java.lang.Object"))
baseJavaname = null;
}
line("package " + packageName);
line();
javadoc("Generated from schema type " + scratch.getXmlName());
line("class " + shortClassName + (baseJavaname != null ? " extends " + baseJavaname : ""));
startBlock();
Collection props = scratch.getQNameProperties();
Map fieldNames = new HashMap();
Set seenFieldNames = new HashSet();
// pick field names
for (Iterator i = props.iterator(); i.hasNext(); )
{
QNameProperty prop = (QNameProperty)i.next();
fieldNames.put(prop, pickUniqueFieldName(prop.getGetterName(), seenFieldNames));
}
// print private fields
for (Iterator i = props.iterator(); i.hasNext(); )
{
QNameProperty prop = (QNameProperty)i.next();
JavaName jType = prop.getTypeName().getJavaName();
if (prop.getCollectionClass() != null)
jType = prop.getCollectionClass();
line("private " + jType.toString() + " " + fieldNames.get(prop) + ";");
}
// print getters and setters
for (Iterator i = props.iterator(); i.hasNext(); )
{
QNameProperty prop = (QNameProperty)i.next();
JavaName jType = prop.getTypeName().getJavaName();
if (prop.getCollectionClass() != null)
jType = prop.getCollectionClass();
String fieldName = (String)fieldNames.get(prop);
line();
line("public " + jType.toString() + " " + prop.getGetterName() + "()");
startBlock();
line("return " + fieldName + ";");
endBlock();
line();
line("public void " + prop.getSetterName() + "(" + jType.toString() + " " + fieldName + ")");
startBlock();
line("this." + fieldName + " = " + fieldName + ";");
endBlock();
}
endBlock();
}
private String pickUniqueFieldName(String getter, Set seenNames)
{
String baseName;
if (getter.length() > 3 && getter.startsWith("get"))
baseName = Character.toLowerCase(getter.charAt(3)) + getter.substring(4);
else
baseName = "field";
String fieldName = baseName;
for (int i = 1; seenNames.contains(fieldName); i += 1)
fieldName = baseName + i;
seenNames.add(fieldName);
return fieldName;
}
}
/**
* Prints the binding file generated by this binding.
*/
public void printBindingFile(OutputStream output) throws IOException
{
bindingFile.write().save(output, new XmlOptions().setSavePrettyPrint());
}
/**
* Returns a BindingFileGenerator for this SchemaToJavaResult.
*/
public BindingFileGenerator getBindingFileGenerator()
{
return this;
}
/**
* Returns the JavaCodeGenerator for this SchemaToJavaResult.
*/
public JavaCodeGenerator getJavaCodeGenerator()
{
return this;
}
}