blob: 7f7a5fa2e70b9b18151372ce03535468836176e2 [file] [log] [blame]
package org.apache.axis2.schema;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axis2.namespace.Constants;
import org.apache.axis2.schema.i18n.SchemaCompilerMessages;
import org.apache.axis2.schema.util.SchemaPropertyLoader;
import org.apache.axis2.schema.writer.BeanWriter;
import org.apache.axis2.util.URLProcessor;
import org.apache.axis2.util.SchemaUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaAll;
import org.apache.ws.commons.schema.XmlSchemaAny;
import org.apache.ws.commons.schema.XmlSchemaAnyAttribute;
import org.apache.ws.commons.schema.XmlSchemaAttribute;
import org.apache.ws.commons.schema.XmlSchemaChoice;
import org.apache.ws.commons.schema.XmlSchemaComplexContent;
import org.apache.ws.commons.schema.XmlSchemaComplexContentExtension;
import org.apache.ws.commons.schema.XmlSchemaComplexContentRestriction;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaContent;
import org.apache.ws.commons.schema.XmlSchemaContentModel;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaEnumerationFacet;
import org.apache.ws.commons.schema.XmlSchemaImport;
import org.apache.ws.commons.schema.XmlSchemaInclude;
import org.apache.ws.commons.schema.XmlSchemaLengthFacet;
import org.apache.ws.commons.schema.XmlSchemaMaxExclusiveFacet;
import org.apache.ws.commons.schema.XmlSchemaMaxInclusiveFacet;
import org.apache.ws.commons.schema.XmlSchemaMaxLengthFacet;
import org.apache.ws.commons.schema.XmlSchemaMinExclusiveFacet;
import org.apache.ws.commons.schema.XmlSchemaMinInclusiveFacet;
import org.apache.ws.commons.schema.XmlSchemaMinLengthFacet;
import org.apache.ws.commons.schema.XmlSchemaObject;
import org.apache.ws.commons.schema.XmlSchemaObjectCollection;
import org.apache.ws.commons.schema.XmlSchemaObjectTable;
import org.apache.ws.commons.schema.XmlSchemaParticle;
import org.apache.ws.commons.schema.XmlSchemaPatternFacet;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.apache.ws.commons.schema.XmlSchemaSimpleContent;
import org.apache.ws.commons.schema.XmlSchemaSimpleContentExtension;
import org.apache.ws.commons.schema.XmlSchemaSimpleContentRestriction;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeContent;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeList;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeUnion;
import org.apache.ws.commons.schema.XmlSchemaType;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/*
* Copyright 2004,2005 The Apache Software Foundation.
*
* 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.
*/
/**
* Schema compiler for ADB. Based on WS-Commons schema object model.
*/
public class SchemaCompiler {
private static final Log log = LogFactory.getLog(SchemaCompiler .class);
private CompilerOptions options;
private HashMap processedTypemap;
//the list of processedElements for the outer elements
private HashMap processedElementMap;
private HashMap processedAnonymousComplexTypesMap;
//we need this map to keep the referenced elements. these elements need to be kept seperate
//to avoid conflicts
private HashMap processedElementRefMap;
private HashMap simpleTypesMap;
private HashMap changedTypeMap;
// this map is necessary to retain the metainformation of types. The reason why these
// meta info 'bags' would be useful later is to cater for the extensions and restrictions
// of types
private HashMap processedTypeMetaInfoMap;
//
private ArrayList processedElementList;
//a list of nillable elements - used to generate code
//for nillable elements
private List nillableElementList;
// writee reference
private BeanWriter writer = null;
private Map baseSchemaTypeMap = null;
//a map for keeping the already loaded schemas
//the key is the targetnamespace and the value is the schema object
private Map loadedSchemaMap = new HashMap();
// A map keeping the available schemas
//the key is the targetnamespace and the value is the schema object
//this map will be populated when multiple schemas
//are fed to the schema compiler!
private Map availableSchemaMap = new HashMap();
private Map loadedSourceURI = new HashMap();
// a list of externally identified QNames to be processed. This becomes
// useful when only a list of external elements need to be processed
public static final String ANY_ELEMENT_FIELD_NAME = "extraElement";
public static final String EXTRA_ATTRIBUTE_FIELD_NAME = "extraAttributes";
public static final String DEFAULT_CLASS_NAME = OMElement.class.getName();
public static final String DEFAULT_CLASS_ARRAY_NAME = "org.apache.axiom.om.OMElement[]";
public static final String DEFAULT_ATTRIB_CLASS_NAME = OMAttribute.class.getName();
public static final String DEFAULT_ATTRIB_ARRAY_CLASS_NAME = "org.apache.axiom.om.OMAttribute[]";
private static int typeCounter = 0;
/**
* @return the processes element map
* includes the Qname of the element as the key and a
* String representing the fully qualified class name
*/
public HashMap getProcessedElementMap() {
return processedElementMap;
}
/**
* @return a map of Qname vs models. A model can be anything,
* ranging from a DOM document to a stream. This is taken from the
* writer and the schema compiler has no control over it
*/
public Map getProcessedModelMap() {
return writer.getModelMap();
}
/**
* Constructor - Accepts a options bean
*
* @param options
*/
public SchemaCompiler(CompilerOptions options) throws SchemaCompilationException {
if (options == null) {
//create an empty options object
this.options = new CompilerOptions();
} else {
this.options = options;
}
//instantiate the maps
processedTypemap = new HashMap();
processedElementMap = new HashMap();
simpleTypesMap = new HashMap();
processedElementList = new ArrayList();
processedAnonymousComplexTypesMap = new HashMap();
changedTypeMap = new HashMap();
processedTypeMetaInfoMap = new HashMap();
processedElementRefMap = new HashMap();
nillableElementList = new ArrayList();
//load the writer and initiliaze the base types
writer = SchemaPropertyLoader.getBeanWriterInstance();
writer.init(this.options);
//load the base types
baseSchemaTypeMap = SchemaPropertyLoader.getTypeMapperInstance().getTypeMap();
}
/**
* Compile a list of schemas
* This actually calls the compile (XmlSchema s) method repeatedly
*
* @param schemalist
* @throws SchemaCompilationException
* @see #compile(org.apache.ws.commons.schema.XmlSchema)
*/
public void compile(List schemalist) throws SchemaCompilationException {
try {
if (schemalist.isEmpty()){
return;
}
//clear the loaded and available maps
loadedSchemaMap.clear();
availableSchemaMap.clear();
XmlSchema schema;
// first round - populate the avaialble map
for (int i = 0; i < schemalist.size(); i++) {
schema = (XmlSchema) schemalist.get(i);
availableSchemaMap.put(
schema.getTargetNamespace(),
schema
);
}
//set a mapper package if not avaialable
if (writer.getExtensionMapperPackageName()==null){
String nsp = null;
//get the first schema from the list and take that namespace as the
//mapper namespace
for (int i = 0; nsp == null && i < schemalist.size(); i++) {
nsp = ((XmlSchema) schemalist.get(i)).getTargetNamespace();
if (nsp != null)
break;
XmlSchema[] schemas = SchemaUtil.getAllSchemas((XmlSchema) schemalist.get(i));
for (int j = 0; schemas != null && j < schemas.length; j++) {
nsp = schemas[j].getTargetNamespace();
if (nsp != null)
break;
}
}
if(nsp == null) {
nsp = URLProcessor.DEFAULT_PACKAGE;
}
writer.registerExtensionMapperPackageName(URLProcessor.makePackageName(nsp));
}
// second round - call the schema compiler one by one
for (int i = 0; i < schemalist.size(); i++) {
compile((XmlSchema) schemalist.get(i),true);
}
//finish up
finalizeSchemaCompilation();
} catch (SchemaCompilationException e) {
throw e;
} catch (Exception e) {
throw new SchemaCompilationException(e);
}
}
/**
* Compile (rather codegen) a single schema element
* @param schema
* @throws SchemaCompilationException
*/
public void compile(XmlSchema schema) throws SchemaCompilationException {
compile(schema,false);
}
/**
* Compile (rather codegen) a single schema element
* @param schema
* @param isPartofGroup
* @throws SchemaCompilationException
*/
private void compile(XmlSchema schema,boolean isPartofGroup) throws SchemaCompilationException {
// some documents explicitly imports the schema of built in types. We don't actually need to compile
// the built-in types. So check the target namespace here and ignore it.
if (Constants.URI_2001_SCHEMA_XSD.equals(schema.getTargetNamespace())) {
return;
}
//register the package from this namespace as the mapper classes package
if (!isPartofGroup){
//set a mapper package if not avaialable
if (writer.getExtensionMapperPackageName()==null){
writer.registerExtensionMapperPackageName(
URLProcessor.makePackageName(schema.getTargetNamespace()));
}
}
//First look for the schemas that are imported and process them
//Note that these are processed recursively!
//add the schema to the loaded schema list
if (!loadedSchemaMap.containsKey(schema.getTargetNamespace())) {
loadedSchemaMap.put(schema.getTargetNamespace(), schema);
}
// If we have/are loading a schema with a specific targetnamespace from a certain URI,
// then just return back to the caller to avoid recursion.
if (schema.getSourceURI() != null){
String key = schema.getTargetNamespace() + ":" + schema.getSourceURI();
if(loadedSourceURI.containsKey(key)){
return;
}
loadedSourceURI.put(key, key);
}
XmlSchemaObjectCollection includes = schema.getIncludes();
if (includes != null) {
Iterator tempIterator = includes.getIterator();
while (tempIterator.hasNext()) {
Object o = tempIterator.next();
if (o instanceof XmlSchemaImport) {
XmlSchema schema1 = ((XmlSchemaImport) o).getSchema();
if (schema1 != null) compile(schema1,isPartofGroup);
}
if (o instanceof XmlSchemaInclude) {
XmlSchema schema1 = ((XmlSchemaInclude) o).getSchema();
if (schema1 != null) compile(schema1,isPartofGroup);
}
}
}
//select all the elements. We generate the code for types
//only if the elements refer them!!! regardless of the fact that
//we have a list of elementnames, we'll need to process all the elements
XmlSchemaObjectTable elements = schema.getElements();
Iterator xmlSchemaElement1Iterator = elements.getValues();
while (xmlSchemaElement1Iterator.hasNext()) {
//this is the set of outer elements so we need to generate classes
//The outermost elements do not contain occurence counts (!) so we do not need
//to check for arraytypes
processElement((XmlSchemaElement) xmlSchemaElement1Iterator.next(), schema);
}
Iterator xmlSchemaElement2Iterator = elements.getValues();
// re-iterate through the elements and write them one by one
// if the mode is unpack this process will not really write the
// classes but will accumilate the models for a final single shot
// write
while (xmlSchemaElement2Iterator.hasNext()) {
//this is the set of outer elements so we need to generate classes
writeElement((XmlSchemaElement) xmlSchemaElement2Iterator.next());
}
if(options.isGenerateAll()) {
Iterator xmlSchemaTypes2Iterator = schema.getSchemaTypes().getValues();
while (xmlSchemaTypes2Iterator.hasNext()) {
XmlSchemaType schemaType = (XmlSchemaType) xmlSchemaTypes2Iterator.next();
if(this.isAlreadyProcessed(schemaType.getQName())) {
continue;
}
if (schemaType instanceof XmlSchemaComplexType) {
//write classes for complex types
XmlSchemaComplexType complexType = (XmlSchemaComplexType) schemaType;
if (complexType.getName() != null) {
processNamedComplexSchemaType(complexType, schema);
}
} else if (schemaType instanceof XmlSchemaSimpleType) {
//process simple type
processSimpleSchemaType((XmlSchemaSimpleType) schemaType,
null,
schema);
}
}
}
if (!isPartofGroup){
//complete the compilation
finalizeSchemaCompilation();
}
}
/**
* Completes the schema compilation process by writing the
* mappers and the classes in a batch if needed
* @throws SchemaCompilationException
*/
private void finalizeSchemaCompilation() throws SchemaCompilationException {
//write the extension mapping class
writer.writeExtensionMapper(
(BeanWriterMetaInfoHolder[])
processedTypeMetaInfoMap.values().toArray(
new BeanWriterMetaInfoHolder[processedTypeMetaInfoMap.size()]));
if (options.isWrapClasses()) {
writer.writeBatch();
}
}
/**
* @return the property map of the schemacompiler.
* In this case it would be the property map loaded from
* the configuration file
*/
public Properties getCompilerProperties(){
return SchemaPropertyLoader.getPropertyMap() ;
}
/**
* Writes the element
*
* @param xsElt
* @throws SchemaCompilationException
*/
private void writeElement(XmlSchemaElement xsElt) throws SchemaCompilationException {
if (this.processedElementMap.containsKey(xsElt.getQName())) {
return;
}
XmlSchemaType schemaType = xsElt.getSchemaType();
BeanWriterMetaInfoHolder metainf = new BeanWriterMetaInfoHolder();
if (schemaType != null && schemaType.getName() != null) {
//this is a named type
QName qName = schemaType.getQName();
//find the class name
String className = findClassName(qName, isArray(xsElt));
//this means the schema type actually returns a different QName
if (changedTypeMap.containsKey(qName)) {
metainf.registerMapping(xsElt.getQName(),
(QName) changedTypeMap.get(qName),
className);
} else {
metainf.registerMapping(xsElt.getQName(),
qName,
className);
}
}else if (xsElt.getRefName()!= null){
// Since top level elements would not have references
// and we only write toplevel elements, this should
// not be a problem , atleast should not occur in a legal schema
}else if (xsElt.getSchemaTypeName()!= null) {
QName qName = xsElt.getSchemaTypeName();
String className = findClassName(qName, isArray(xsElt));
metainf.registerMapping(xsElt.getQName(),
qName,
className);
}else if (schemaType != null){ //the named type should have been handled already
//we are going to special case the anonymous complex type. Our algorithm for dealing
//with it is to generate a single object that has the complex content inside. Really the
//intent of the user when he declares the complexType anonymously is to use it privately
//First copy the schema types content into the metainf holder
metainf = (BeanWriterMetaInfoHolder) this.processedAnonymousComplexTypesMap.get(xsElt);
metainf.setAnonymous(true);
}else{
//this means we did not find any schema type associated with the particular element.
log.warn(SchemaCompilerMessages.getMessage("schema.elementWithNoType", xsElt.getQName().toString()));
metainf.registerMapping(xsElt.getQName(),
null,
DEFAULT_CLASS_NAME,
SchemaConstants.ANY_TYPE);
}
if (nillableElementList.contains(xsElt.getQName())){
metainf.registerNillableQName(xsElt.getQName());
}
String writtenClassName = writer.write(xsElt, processedTypemap, metainf);
//register the class name
xsElt.addMetaInfo(SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY,
writtenClassName);
processedElementMap.put(xsElt.getQName(), writtenClassName);
}
/**
* For inner elements
* @param xsElt
* @param innerElementMap
* @param parentSchema
* @throws SchemaCompilationException
*/
private void processElement(XmlSchemaElement xsElt,Map innerElementMap,List localNillableList,XmlSchema parentSchema) throws SchemaCompilationException {
processElement(xsElt,false,innerElementMap,localNillableList,parentSchema);
}
/**
* For outer elements
* @param xsElt
* @param parentSchema
* @throws SchemaCompilationException
*/
private void processElement(XmlSchemaElement xsElt,XmlSchema parentSchema) throws SchemaCompilationException {
processElement(xsElt,true,null,null,parentSchema);
}
/**
* Process and Element
*
* @param xsElt
* @param isOuter We need to know this since the treatment of outer elements is different that
* inner elements
* @throws SchemaCompilationException
*/
private void processElement(XmlSchemaElement xsElt, boolean isOuter,Map innerElementMap,List localNillableList, XmlSchema parentSchema) throws SchemaCompilationException {
//if the element is null, which usually happens when the qname is not
//proper, throw an exceptions
if (xsElt==null){
throw new SchemaCompilationException(
SchemaCompilerMessages.getMessage("schema.elementNull"));
}
//The processing element logic seems to be quite simple. Look at the relevant schema type
//for each and every element and process that accordingly.
//this means that any unused type definitions would not be generated!
if (isOuter && processedElementList.contains(xsElt.getQName())) {
return;
}
XmlSchemaType schemaType = xsElt.getSchemaType();
if (schemaType != null) {
processSchema(xsElt, schemaType,parentSchema);
//at this time it is not wise to directly write the class for the element
//so we push the complete element to an arraylist and let the process
//pass through. We'll be iterating through the elements writing them
//later
if (!isOuter) {
if (schemaType.getName()!=null){
// this element already has a name. Which means we can directly
// register it
String className = findClassName(schemaType.getQName(),
isArray(xsElt));
innerElementMap.put(xsElt.getQName(), className);
//store in the schema map
schemaType.addMetaInfo(
SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY,
className);
if (baseSchemaTypeMap.containsValue(className)){
schemaType.addMetaInfo(
SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_PRIMITVE_KEY,
Boolean.TRUE);
}
//since this is a inner element we should add it to the inner element map
}else{
//this is an anon type. This should have been already processed and registered at
//the anon map. we've to write it just like we treat a referenced type(giving due
//care that this is meant to be an attribute in some class)
QName generatedTypeName = generateTypeQName(xsElt.getQName(), parentSchema);
if (schemaType instanceof XmlSchemaComplexType){
//set a name
schemaType.setName(generatedTypeName.getLocalPart());
// Must do this up front to support recursive types
String fullyQualifiedClassName = writer.makeFullyQualifiedClassName(schemaType.getQName());
processedTypemap.put(schemaType.getQName(), fullyQualifiedClassName);
BeanWriterMetaInfoHolder metaInfHolder = (BeanWriterMetaInfoHolder) processedAnonymousComplexTypesMap.get(xsElt);
metaInfHolder.setOwnQname(schemaType.getQName());
metaInfHolder.setOwnClassName(fullyQualifiedClassName);
writeComplexType((XmlSchemaComplexType)schemaType,
metaInfHolder);
//remove the reference from the anon list since we named the type
processedAnonymousComplexTypesMap.remove(xsElt);
String className = findClassName(schemaType.getQName(), isArray(xsElt));
innerElementMap.put(
xsElt.getQName(),
className);
//store in the schema map
xsElt.addMetaInfo(
SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY,
className);
} else if (schemaType instanceof XmlSchemaSimpleType){
//set a name
schemaType.setName(generatedTypeName.getLocalPart());
// Must do this up front to support recursive types
String fullyQualifiedClassName = writer.makeFullyQualifiedClassName(schemaType.getQName());
processedTypemap.put(schemaType.getQName(), fullyQualifiedClassName);
BeanWriterMetaInfoHolder metaInfHolder = (BeanWriterMetaInfoHolder) processedAnonymousComplexTypesMap.get(xsElt);
metaInfHolder.setOwnQname(schemaType.getQName());
metaInfHolder.setOwnClassName(fullyQualifiedClassName);
writeSimpleType((XmlSchemaSimpleType)schemaType,
metaInfHolder);
//remove the reference from the anon list since we named the type
processedAnonymousComplexTypesMap.remove(xsElt);
String className = findClassName(schemaType.getQName(), isArray(xsElt));
innerElementMap.put(
xsElt.getQName(),
className);
//store in the schema map
xsElt.addMetaInfo(
SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY,
className);
}
}
}else{
this.processedElementList.add(xsElt.getQName());
}
//referenced name
}else if (xsElt.getRefName()!=null){
if(xsElt.getRefName().equals(SchemaConstants.XSD_SCHEMA)){
innerElementMap.put(xsElt.getQName(), SchemaCompiler.DEFAULT_CLASS_NAME);
return;
}
//process the referenced type. It could be thought that the referenced element replaces this
//element
XmlSchemaElement referencedElement = getReferencedElement(parentSchema, xsElt.getRefName());
if (referencedElement==null){
throw new SchemaCompilationException(
SchemaCompilerMessages.getMessage("schema.referencedElementNotFound", xsElt.getRefName().toString()));
}
//if the element is referenced, then it should be one of the outer (global) ones
processElement(referencedElement, parentSchema);
//no outer check required here. If the element is having a ref, then it is definitely
//not an outer element since the top level elements are not supposed to have refs
//Also we are sure that it should have a type reference
QName referenceEltQName = referencedElement.getQName();
if (referencedElement.getSchemaTypeName()!=null){
String className = findClassName(referencedElement.getSchemaTypeName(), isArray(xsElt));
//if this element is referenced, there's no QName for this element
this.processedElementRefMap.put(referenceEltQName, className);
referencedElement.addMetaInfo(SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY,
className);
}else{
//this referenced element has an anon type and that anon type has been already
//processed. But in this case we need it to be a seperate class since this
//complextype has to be added as an attribute in a class.
//generate a name for this type
QName generatedTypeName = generateTypeQName(referenceEltQName, parentSchema);
XmlSchemaType referenceSchemaType = referencedElement.getSchemaType();
if (referenceSchemaType instanceof XmlSchemaComplexType){
//set a name
referenceSchemaType.setName(generatedTypeName.getLocalPart());
writeComplexType((XmlSchemaComplexType)referenceSchemaType,
(BeanWriterMetaInfoHolder)processedAnonymousComplexTypesMap.get(referencedElement)
);
//remove the reference from the anon list since we named the type
// DEEPAL :- We can not remove the entry from the hashtable ,
// this will fail if there are two reference for the same type
//processedAnonymousComplexTypesMap.remove(referencedElement);
//add this to the processed ref type map
String fullyQualifiedClassName = writer.makeFullyQualifiedClassName(generatedTypeName);
processedTypemap.put(generatedTypeName, fullyQualifiedClassName);
this.processedElementRefMap.put(referenceEltQName, fullyQualifiedClassName);
}
}
// schema type name is present but not the schema type object
}else if (xsElt.getSchemaTypeName()!=null){
//There can be instances where the SchemaType is null but the schemaTypeName is not!
//this specifically happens with xsd:anyType.
QName schemaTypeName = xsElt.getSchemaTypeName();
XmlSchema currentParentSchema = resolveParentSchema(schemaTypeName,parentSchema);
XmlSchemaType typeByName = getType(currentParentSchema, schemaTypeName);
if (typeByName!=null){
//this type is found in the schema so we can process it
processSchema(xsElt, typeByName,currentParentSchema);
if (!isOuter) {
String className = findClassName(schemaTypeName, isArray(xsElt));
//since this is a inner element we should add it to the inner element map
innerElementMap.put(xsElt.getQName(), className);
}else{
this.processedElementList.add(xsElt.getQName());
}
}else{
//this type is not found at all. we'll just register it with whatever the class name we can comeup with
if (!isOuter) {
String className = findClassName(schemaTypeName, isArray(xsElt));
innerElementMap.put(xsElt.getQName(), className);
}else{
this.processedElementList.add(xsElt.getQName());
}
}
}
//add this elements QName to the nillable group if it has the nillable attribute
if (xsElt.isNillable()){
if (isOuter){
this.nillableElementList.add(xsElt.getQName());
}else{
localNillableList.add(xsElt.getQName());
}
}
}
/**
* resolve the parent schema for the given schema type name
*
* @param schemaTypeName
* @param currentSchema
*/
private XmlSchema resolveParentSchema(QName schemaTypeName,XmlSchema currentSchema)
throws SchemaCompilationException{
String targetNamespace = schemaTypeName.getNamespaceURI();
Object loadedSchema = loadedSchemaMap.get(targetNamespace);
if (loadedSchema!=null){
return (XmlSchema)loadedSchema;
}else if (availableSchemaMap.containsKey(targetNamespace)) {
//compile the referenced Schema first and then pass it
XmlSchema schema = (XmlSchema) availableSchemaMap.get(targetNamespace);
compile(schema);
return schema;
}else{
return currentSchema;
}
}
/**
* Generate a unique type Qname using an element name
* @param referenceEltQName
* @param parentSchema
*/
private QName generateTypeQName(QName referenceEltQName, XmlSchema parentSchema) {
QName generatedTypeName = new QName(referenceEltQName.getNamespaceURI(),
referenceEltQName.getLocalPart() + getNextTypeSuffix());
while (parentSchema.getTypeByName(generatedTypeName)!= null){
generatedTypeName = new QName(referenceEltQName.getNamespaceURI(),
referenceEltQName.getLocalPart() + getNextTypeSuffix());
}
return generatedTypeName;
}
/**
* Generate a unique attribute Qname using the ref name
* @param attrRefName
* @param parentSchema
* @return Returns the generated attribute name
*/
private QName generateAttributeQName(QName attrRefName, XmlSchema parentSchema) {
if (typeCounter==Integer.MAX_VALUE){
typeCounter = 0;
}
QName generatedAttrName = new QName(attrRefName.getNamespaceURI(),
attrRefName.getLocalPart() + typeCounter++);
while (parentSchema.getTypeByName(generatedAttrName)!= null){
generatedAttrName = new QName(attrRefName.getNamespaceURI(),
attrRefName.getLocalPart() + typeCounter++);
}
return generatedAttrName;
}
/**
* Finds whether a given class is already made
* @param qName
*/
private boolean isAlreadyProcessed(QName qName){
return processedTypemap.containsKey(qName)||
simpleTypesMap.containsKey(qName) ||
baseSchemaTypeMap.containsKey(qName);
}
/**
* A method to pick the ref class name
* @param name
* @param isArray
*/
private String findRefClassName(QName name,boolean isArray){
String className = null;
if (processedElementRefMap.get(name)!=null){
className =(String)processedElementRefMap.get(name);
//if (isArray) {
//append the square braces that say this is an array
//hope this works for all cases!!!!!!!
//todo this however is a thing that needs to be
//todo fixed to get complete language support
// className = className + "[]";
//}
}
return className;
}
/**
* Finds a class name from the given Qname
*
* @param qName
* @param isArray
* @return FQCN
*/
private String findClassName(QName qName, boolean isArray) throws SchemaCompilationException {
//find the class name
String className;
if (processedTypemap.containsKey(qName)) {
className = (String) processedTypemap.get(qName);
} else if (simpleTypesMap.containsKey(qName)) {
className = (String) simpleTypesMap.get(qName);
} else if (baseSchemaTypeMap.containsKey(qName)) {
className = (String) baseSchemaTypeMap.get(qName);
} else {
if(isSOAP_ENC(qName.getNamespaceURI())) {
throw new SchemaCompilationException(SchemaCompilerMessages.getMessage("schema.soapencoding.error", qName.toString()));
}
// We seem to have failed in finding a class name for the
//contained schema type. We better set the default then
//however it's better if the default can be set through the
//property file
className = DEFAULT_CLASS_NAME;
log.warn(SchemaCompilerMessages
.getMessage("schema.typeMissing", qName.toString()));
}
if (isArray) {
//append the square braces that say this is an array
//hope this works for all cases!!!!!!!
//todo this however is a thing that needs to be
//todo fixed to get complete language support
className = className + "[]";
}
return className;
}
/**
* Returns true if SOAP_ENC Namespace.
*
* @param s a string representing the URI to check
* @return true if <code>s</code> matches a SOAP ENCODING namespace URI,
* false otherwise
*/
public static boolean isSOAP_ENC(String s) {
if (s.equals(Constants.URI_SOAP11_ENC))
return true;
return s.equals(Constants.URI_SOAP12_ENC);
}
/**
* Process a schema element which has been refered to by an element
* @param schemaType
* @throws SchemaCompilationException
*/
private void processSchema(XmlSchemaElement xsElt, XmlSchemaType schemaType,XmlSchema parentSchema) throws SchemaCompilationException {
if (schemaType instanceof XmlSchemaComplexType) {
//write classes for complex types
XmlSchemaComplexType complexType = (XmlSchemaComplexType) schemaType;
if (complexType.getName() != null) {
processNamedComplexSchemaType(complexType,parentSchema);
} else {
processAnonymousComplexSchemaType(xsElt, complexType,parentSchema);
}
} else if (schemaType instanceof XmlSchemaSimpleType) {
//process simple type
processSimpleSchemaType((XmlSchemaSimpleType) schemaType,
xsElt,
parentSchema);
}
}
/**
* @param complexType
* @throws SchemaCompilationException
*/
private void processAnonymousComplexSchemaType(XmlSchemaElement elt, XmlSchemaComplexType complexType,XmlSchema parentSchema)
throws SchemaCompilationException {
BeanWriterMetaInfoHolder metaInfHolder = processComplexType(complexType,parentSchema);
//since this is a special case (an unnamed complex type) we'll put the already processed
//metainf holder in a special map to be used later
this.processedAnonymousComplexTypesMap.put(elt, metaInfHolder);
}
/**
* handle the complex types which are named
* @param complexType
*/
private void processNamedComplexSchemaType(XmlSchemaComplexType complexType,XmlSchema parentSchema) throws SchemaCompilationException {
if (processedTypemap.containsKey(complexType.getQName())
|| baseSchemaTypeMap.containsKey(complexType.getQName())) {
return;
}
// Must do this up front to support recursive types
String fullyQualifiedClassName = writer.makeFullyQualifiedClassName(complexType.getQName());
processedTypemap.put(complexType.getQName(), fullyQualifiedClassName);
//register that in the schema metainfo bag
complexType.addMetaInfo(SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY,
fullyQualifiedClassName);
BeanWriterMetaInfoHolder metaInfHolder = processComplexType(complexType,parentSchema);
//add this information to the metainfo holder
metaInfHolder.setOwnQname(complexType.getQName());
metaInfHolder.setOwnClassName(fullyQualifiedClassName);
//write the class. This type mapping would have been populated right now
//Note - We always write classes for named complex types
writeComplexType(complexType, metaInfHolder);
}
/**
* Writes a complex type
* @param complexType
* @param metaInfHolder
* @param fullyQualifiedClassName the name returned by makeFullyQualifiedClassName() or null if it wasn't called
* @throws SchemaCompilationException
*/
private void writeComplexType(XmlSchemaComplexType complexType, BeanWriterMetaInfoHolder metaInfHolder)
throws SchemaCompilationException {
writer.write(complexType, processedTypemap, metaInfHolder);
processedTypeMetaInfoMap.put(complexType.getQName(),metaInfHolder);
}
/**
* Writes a complex type
* @param simpleType
* @param metaInfHolder
* @throws SchemaCompilationException
*/
private void writeSimpleType(XmlSchemaSimpleType simpleType, BeanWriterMetaInfoHolder metaInfHolder)
throws SchemaCompilationException {
writer.write(simpleType, processedTypemap, metaInfHolder);
processedTypeMetaInfoMap.put(simpleType.getQName(),metaInfHolder);
}
private BeanWriterMetaInfoHolder processComplexType(XmlSchemaComplexType complexType,XmlSchema parentSchema) throws SchemaCompilationException {
XmlSchemaParticle particle = complexType.getParticle();
BeanWriterMetaInfoHolder metaInfHolder = new BeanWriterMetaInfoHolder();
if (particle != null) {
//Process the particle
processParticle(particle, metaInfHolder,parentSchema);
}
//process attributes - first look for the explicit attributes
XmlSchemaObjectCollection attribs = complexType.getAttributes();
Iterator attribIterator = attribs.getIterator();
while (attribIterator.hasNext()) {
Object o = attribIterator.next();
if (o instanceof XmlSchemaAttribute) {
processAttribute((XmlSchemaAttribute) o, metaInfHolder,parentSchema);
}
}
//process any attribute
//somehow the xml schema parser does not seem to pickup the any attribute!!
XmlSchemaAnyAttribute anyAtt = complexType.getAnyAttribute();
if (anyAtt != null) {
processAnyAttribute(metaInfHolder,anyAtt);
}
//process content ,either complex or simple
if (complexType.getContentModel()!=null){
processContentModel(complexType.getContentModel(),
metaInfHolder,
parentSchema);
}
return metaInfHolder;
}
/**
* Process the content models. A content model is either simple type or a complex type
* and included inside a complex content
*/
private void processContentModel(XmlSchemaContentModel content,
BeanWriterMetaInfoHolder metaInfHolder,
XmlSchema parentSchema)
throws SchemaCompilationException{
if (content instanceof XmlSchemaComplexContent){
processComplexContent((XmlSchemaComplexContent)content,metaInfHolder,parentSchema);
}else if (content instanceof XmlSchemaSimpleContent){
processSimpleContent((XmlSchemaSimpleContent)content,metaInfHolder,parentSchema);
metaInfHolder.setSimple(true);
}
}
/**
* Prcess the complex content
*/
private void processComplexContent(XmlSchemaComplexContent complexContent,
BeanWriterMetaInfoHolder metaInfHolder,
XmlSchema parentSchema)
throws SchemaCompilationException{
XmlSchemaContent content = complexContent.getContent();
if (content instanceof XmlSchemaComplexContentExtension ){
// to handle extension we need to attach the extended items to the base type
// and create a new type
XmlSchemaComplexContentExtension extension = (XmlSchemaComplexContentExtension)
content;
//process the base type if it has not been processed yet
if (!isAlreadyProcessed(extension.getBaseTypeName())){
//pick the relevant basetype from the schema and process it
XmlSchemaType type = getType(parentSchema, extension.getBaseTypeName());
if (type instanceof XmlSchemaComplexType) {
XmlSchemaComplexType complexType = (XmlSchemaComplexType) type;
if (complexType.getName() != null) {
processNamedComplexSchemaType(complexType,parentSchema);
} else {
//this is not possible. The extension should always
//have a name
throw new SchemaCompilationException("Unnamed complex type used in extension");//Internationlize this
}
} else if (type instanceof XmlSchemaSimpleType) {
//process simple type
processSimpleSchemaType((XmlSchemaSimpleType)type,null,parentSchema);
}
}
// before actually processing this node, we need to recurse through the base types and add their
// children (sometimes even preserving the order) to the metainfo holder of this type
// the reason is that for extensions, the prefered way is to have the sequences of the base class
//* before * the sequence of the child element.
copyMetaInfoHierarchy(metaInfHolder,extension.getBaseTypeName(),parentSchema);
//process the particle of this node
processParticle(extension.getParticle(),metaInfHolder,parentSchema);
String className = findClassName(extension.getBaseTypeName(), false);
if (!SchemaCompiler.DEFAULT_CLASS_NAME.equals(className)) {
//the particle has been processed, However since this is an extension we need to
//add the basetype as an extension to the complex type class.
// The basetype has been processed already
metaInfHolder.setExtension(true);
metaInfHolder.setExtensionClassName(className);
//Note - this is no array! so the array boolean is false
}
}else if (content instanceof XmlSchemaComplexContentRestriction){
XmlSchemaComplexContentRestriction restriction = (XmlSchemaComplexContentRestriction) content;
//process the base type if it has not been processed yet
if (!isAlreadyProcessed(restriction.getBaseTypeName())){
//pick the relevant basetype from the schema and process it
XmlSchemaType type = getType(parentSchema, restriction.getBaseTypeName());
if (type instanceof XmlSchemaComplexType) {
XmlSchemaComplexType complexType = (XmlSchemaComplexType) type;
if (complexType.getName() != null) {
processNamedComplexSchemaType(complexType,parentSchema);
} else {
//this is not possible. The restriction should always
//have a name
throw new SchemaCompilationException("Unnamed complex type used in restriction");//Internationlize this
}
} else if (type instanceof XmlSchemaSimpleType) {
throw new SchemaCompilationException("Not a valid restriction, complex content restriction base type cannot be a simple type.");
}
}
copyMetaInfoHierarchy(metaInfHolder,restriction.getBaseTypeName(),parentSchema);
//process the particle of this node
processParticle(restriction.getParticle(),metaInfHolder,parentSchema);
String className = findClassName(restriction.getBaseTypeName(), false);
if (!SchemaCompiler.DEFAULT_CLASS_NAME.equals(className)) {
metaInfHolder.setRestriction(true);
metaInfHolder.setRestrictionClassName(findClassName(restriction.getBaseTypeName(), false));
//Note - this is no array! so the array boolean is false
}
}
}
/**
* Recursive method to populate the metainfo holders with info from the base types
* @param metaInfHolder
* @param baseTypeName
* @param parentSchema
*/
private void copyMetaInfoHierarchy(BeanWriterMetaInfoHolder metaInfHolder,
QName baseTypeName,
XmlSchema parentSchema)
throws SchemaCompilationException {
XmlSchemaType type = parentSchema.getTypeByName(baseTypeName);
BeanWriterMetaInfoHolder baseMetaInfoHolder = (BeanWriterMetaInfoHolder)
processedTypeMetaInfoMap.get(baseTypeName);
if (baseMetaInfoHolder!= null){
// see whether this type is also extended from some other type first
// if so proceed to set their parents as well.
if (type instanceof XmlSchemaComplexType){
XmlSchemaComplexType complexType = (XmlSchemaComplexType)type;
if (complexType.getContentModel()!= null){
XmlSchemaContentModel content = complexType.getContentModel();
if (content instanceof XmlSchemaComplexContent){
XmlSchemaComplexContent complexContent =
(XmlSchemaComplexContent)content;
if (complexContent.getContent() instanceof XmlSchemaComplexContentExtension){
XmlSchemaComplexContentExtension extension =
(XmlSchemaComplexContentExtension)complexContent.getContent();
//recursively call the copyMetaInfoHierarchy method
copyMetaInfoHierarchy(baseMetaInfoHolder,
extension.getBaseTypeName(),
parentSchema);
}else if (complexContent.getContent() instanceof XmlSchemaComplexContentRestriction){
XmlSchemaComplexContentRestriction restriction =
(XmlSchemaComplexContentRestriction)complexContent.getContent();
//recursively call the copyMetaInfoHierarchy method
copyMetaInfoHierarchy(baseMetaInfoHolder,
restriction.getBaseTypeName(),
parentSchema);
}else{
throw new SchemaCompilationException(
SchemaCompilerMessages.getMessage("schema.unknowncontenterror"));
}
}else if (content instanceof XmlSchemaSimpleContent){
throw new SchemaCompilationException(
SchemaCompilerMessages.getMessage("schema.unsupportedcontenterror","Simple Content"));
}else{
throw new SchemaCompilationException(
SchemaCompilerMessages.getMessage("schema.unknowncontenterror"));
}
}
//Do the actual parent setting
metaInfHolder.setAsParent(baseMetaInfoHolder);
}
}
}
/**
*
* @param simpleContent
* @param metaInfHolder
* @throws SchemaCompilationException
*/
private void processSimpleContent(XmlSchemaSimpleContent simpleContent,BeanWriterMetaInfoHolder metaInfHolder,XmlSchema parentSchema)
throws SchemaCompilationException{
XmlSchemaContent content;
content = simpleContent.getContent();
if (content instanceof XmlSchemaSimpleContentExtension){
XmlSchemaSimpleContentExtension extension = (XmlSchemaSimpleContentExtension)content;
//process the base type if it has not been processed yet
if (!isAlreadyProcessed(extension.getBaseTypeName())){
//pick the relevant basetype from the schema and process it
XmlSchemaType type = getType(parentSchema,extension.getBaseTypeName());
if (type instanceof XmlSchemaComplexType) {
XmlSchemaComplexType complexType = (XmlSchemaComplexType) type;
if (complexType.getName() != null) {
processNamedComplexSchemaType(complexType,parentSchema);
} else {
//this is not possible. The extension should always
//have a name
throw new SchemaCompilationException("Unnamed complex type used in extension");//Internationlize this
}
} else if (type instanceof XmlSchemaSimpleType) {
//process simple type
processSimpleSchemaType((XmlSchemaSimpleType)type,null, parentSchema);
}
}
//process extension base type
processSimpleExtensionBaseType(extension.getBaseTypeName(),metaInfHolder);
//process attributes
XmlSchemaObjectCollection attribs = extension.getAttributes();
Iterator attribIterator = attribs.getIterator();
while (attribIterator.hasNext()) {
Object attr = attribIterator.next();
if (attr instanceof XmlSchemaAttribute) {
processAttribute((XmlSchemaAttribute) attr, metaInfHolder,parentSchema);
}
}
//process any attribute
XmlSchemaAnyAttribute anyAtt = extension.getAnyAttribute();
if (anyAtt != null) {
processAnyAttribute(metaInfHolder,anyAtt);
}
}else if (content instanceof XmlSchemaSimpleContentRestriction){
XmlSchemaSimpleContentRestriction restriction = (XmlSchemaSimpleContentRestriction) content;
//process the base type if it has not been processed yet
if (!isAlreadyProcessed(restriction.getBaseTypeName())){
//pick the relevant basetype from the schema and process it
XmlSchemaType type = getType(parentSchema,restriction.getBaseTypeName());
if (type instanceof XmlSchemaComplexType) {
XmlSchemaComplexType complexType = (XmlSchemaComplexType) type;
if (complexType.getName() != null) {
processNamedComplexSchemaType(complexType,parentSchema);
} else {
//this is not possible. The extension should always
//have a name
throw new SchemaCompilationException("Unnamed complex type used in restriction");//Internationlize this
}
} else if (type instanceof XmlSchemaSimpleType) {
//process simple type
processSimpleSchemaType((XmlSchemaSimpleType)type,null, parentSchema);
}
}
//process restriction base type
processSimpleRestrictionBaseType(restriction.getBaseTypeName(), restriction.getBaseTypeName(),metaInfHolder);
}
}
/**
* Process Simple Extension Base Type.
*
* @param extBaseType
* @param metaInfHolder
*/
public void processSimpleExtensionBaseType(QName extBaseType,BeanWriterMetaInfoHolder metaInfHolder) throws SchemaCompilationException {
//find the class name
String className = findClassName(extBaseType, false);
//this means the schema type actually returns a different QName
if (changedTypeMap.containsKey(extBaseType)) {
metaInfHolder.registerMapping(extBaseType,
(QName) changedTypeMap.get(extBaseType),
className,SchemaConstants.ELEMENT_TYPE);
} else {
metaInfHolder.registerMapping(extBaseType,
extBaseType,
className,SchemaConstants.ELEMENT_TYPE);
}
//get the binary state and add that to the status map
if (isBinary(extBaseType)){
metaInfHolder.addtStatus(extBaseType,
SchemaConstants.BINARY_TYPE);
}
}
/**
* Process Simple Restriction Base Type.
*
* @param resBaseType
* @param metaInfHolder
*/
public void processSimpleRestrictionBaseType(QName qName, QName resBaseType,BeanWriterMetaInfoHolder metaInfHolder) throws SchemaCompilationException {
//find the class name
String className = findClassName(resBaseType, false);
//this means the schema type actually returns a different QName
if (changedTypeMap.containsKey(resBaseType)) {
metaInfHolder.registerMapping(qName,
(QName) changedTypeMap.get(resBaseType),
className,SchemaConstants.ELEMENT_TYPE);
} else {
metaInfHolder.registerMapping(qName,
resBaseType,
className,SchemaConstants.ELEMENT_TYPE);
}
metaInfHolder.setRestrictionBaseType(resBaseType);
}
/**
* Process Facets.
*
* @param facets
* @param metaInfHolder
*/
private void processFacets(XmlSchemaObjectCollection facets,BeanWriterMetaInfoHolder metaInfHolder) {
Iterator facetIterator = facets.getIterator();
while (facetIterator.hasNext()) {
Object obj = facetIterator.next();
if ( obj instanceof XmlSchemaPatternFacet ) {
XmlSchemaPatternFacet pattern = (XmlSchemaPatternFacet) obj;
metaInfHolder.setPatternFacet(pattern.getValue().toString());
}
else if ( obj instanceof XmlSchemaEnumerationFacet ) {
XmlSchemaEnumerationFacet enumeration = (XmlSchemaEnumerationFacet) obj;
metaInfHolder.addEnumFacet(enumeration.getValue().toString());
}
else if ( obj instanceof XmlSchemaLengthFacet ) {
XmlSchemaLengthFacet length = (XmlSchemaLengthFacet) obj;
metaInfHolder.setLengthFacet(Integer.parseInt(length.getValue().toString()));
}
else if ( obj instanceof XmlSchemaMaxExclusiveFacet ) {
XmlSchemaMaxExclusiveFacet maxEx = (XmlSchemaMaxExclusiveFacet) obj;
metaInfHolder.setMaxExclusiveFacet(maxEx.getValue().toString());
}
else if ( obj instanceof XmlSchemaMinExclusiveFacet ) {
XmlSchemaMinExclusiveFacet minEx = (XmlSchemaMinExclusiveFacet) obj;
metaInfHolder.setMinExclusiveFacet(minEx.getValue().toString());
}
else if ( obj instanceof XmlSchemaMaxInclusiveFacet ) {
XmlSchemaMaxInclusiveFacet maxIn = (XmlSchemaMaxInclusiveFacet) obj;
metaInfHolder.setMaxInclusiveFacet(maxIn.getValue().toString());
}
else if ( obj instanceof XmlSchemaMinInclusiveFacet ) {
XmlSchemaMinInclusiveFacet minIn = (XmlSchemaMinInclusiveFacet) obj;
metaInfHolder.setMinInclusiveFacet(minIn.getValue().toString());
}
else if ( obj instanceof XmlSchemaMaxLengthFacet ) {
XmlSchemaMaxLengthFacet maxLen = (XmlSchemaMaxLengthFacet) obj;
metaInfHolder.setMaxLengthFacet(Integer.parseInt(maxLen.getValue().toString()));
}
else if ( obj instanceof XmlSchemaMinLengthFacet ) {
XmlSchemaMinLengthFacet minLen = (XmlSchemaMinLengthFacet) obj;
metaInfHolder.setMinLengthFacet(Integer.parseInt(minLen.getValue().toString()));
}
}
}
/**
* Handle any attribute
* @param metainf
*/
private void processAnyAttribute(BeanWriterMetaInfoHolder metainf,XmlSchemaAnyAttribute anyAtt) {
//The best thing we can do here is to add a set of OMAttributes
//since attributes do not have the notion of minoccurs/maxoccurs the
//safest option here is to have an OMAttribute array
QName qName = new QName(EXTRA_ATTRIBUTE_FIELD_NAME);
metainf.registerMapping(qName,
null,
DEFAULT_ATTRIB_ARRAY_CLASS_NAME,//always generate an array of
//OMAttributes
SchemaConstants.ANY_TYPE);
metainf.addtStatus(qName, SchemaConstants.ATTRIBUTE_TYPE);
metainf.addtStatus(qName, SchemaConstants.ARRAY_TYPE);
}
/**
* Process the attribute
*
* @param att
* @param metainf
*/
public void processAttribute(XmlSchemaAttribute att, BeanWriterMetaInfoHolder metainf, XmlSchema parentSchema)
throws SchemaCompilationException {
//for now we assume (!!!) that attributes refer to standard types only
QName schemaTypeName = att.getSchemaTypeName();
if (schemaTypeName != null) {
if (baseSchemaTypeMap.containsKey(schemaTypeName)) {
if (att.getQName() != null) {
metainf.registerMapping(att.getQName(),schemaTypeName,
baseSchemaTypeMap.get(schemaTypeName).toString(), SchemaConstants.ATTRIBUTE_TYPE);
// add optional attribute status if set
String use = att.getUse().getValue();
if (use.indexOf("optional") != -1) {
metainf.addtStatus(att.getQName(), SchemaConstants.OPTIONAL_TYPE);
}
}
}
} else if (att.getRefName() != null) {
XmlSchema currentParentSchema = resolveParentSchema(att.getRefName(),parentSchema);
QName attrQname = generateAttributeQName(att.getRefName(),parentSchema);
XmlSchemaObjectCollection items = currentParentSchema.getItems();
Iterator itemIterator = items.getIterator();
while (itemIterator.hasNext()) {
Object attr = itemIterator.next();
if (attr instanceof XmlSchemaAttribute) {
XmlSchemaAttribute attribute = (XmlSchemaAttribute) attr;
if (attribute.getName().equals(att.getRefName().getLocalPart())) {
QName attrTypeName = attribute.getSchemaTypeName();
Object type = baseSchemaTypeMap.get(attrTypeName);
if (type == null) {
XmlSchemaSimpleType simpleType = attribute.getSchemaType();
if(simpleType != null && simpleType.getContent() instanceof XmlSchemaSimpleTypeRestriction) {
XmlSchemaSimpleTypeRestriction restriction = (XmlSchemaSimpleTypeRestriction) simpleType.getContent();
QName baseTypeName = restriction.getBaseTypeName();
type = baseSchemaTypeMap.get(baseTypeName);
attrQname = att.getRefName();
}
//TODO: Handle XmlSchemaSimpleTypeUnion and XmlSchemaSimpleTypeList
}
if (type != null) {
metainf.registerMapping(attrQname,attrQname,
type.toString(), SchemaConstants.ATTRIBUTE_TYPE);
// add optional attribute status if set
String use = att.getUse().getValue();
if (use.indexOf("optional") != -1) {
metainf.addtStatus(att.getQName(), SchemaConstants.OPTIONAL_TYPE);
}
}
}
}
}
} else {
//todo his attribute refers to a custom type, probably one of the extended simple types.
//todo handle it here
}
}
/**
* Process a particle- A particle may be a sequence,all or a choice
*
* @param particle
* @param metainfHolder
* @throws SchemaCompilationException
*/
private void processParticle(XmlSchemaParticle particle, //particle being processed
BeanWriterMetaInfoHolder metainfHolder // metainf holder
,XmlSchema parentSchema) throws SchemaCompilationException {
if (particle instanceof XmlSchemaSequence) {
XmlSchemaObjectCollection items = ((XmlSchemaSequence) particle).getItems();
if (options.isBackwordCompatibilityMode()) {
process(items, metainfHolder, false, parentSchema);
} else {
process(items, metainfHolder, true, parentSchema);
}
} else if (particle instanceof XmlSchemaAll) {
XmlSchemaObjectCollection items = ((XmlSchemaAll) particle).getItems();
process(items, metainfHolder, false,parentSchema);
} else if (particle instanceof XmlSchemaChoice) {
XmlSchemaObjectCollection items = ((XmlSchemaChoice) particle).getItems();
metainfHolder.setChoice(true);
process(items, metainfHolder, false,parentSchema);
}
}
/**
* @param items
* @param metainfHolder
* @param order
* @throws SchemaCompilationException
*/
private void process(XmlSchemaObjectCollection items,
BeanWriterMetaInfoHolder metainfHolder,
boolean order,
XmlSchema parentSchema) throws SchemaCompilationException {
int count = items.getCount();
Map processedElementArrayStatusMap = new LinkedHashMap();
Map processedElementTypeMap = new LinkedHashMap();
List localNillableList = new ArrayList();
Map elementOrderMap = new HashMap();
for (int i = 0; i < count; i++) {
XmlSchemaObject item = items.getItem(i);
if (item instanceof XmlSchemaElement) {
//recursively process the element
XmlSchemaElement xsElt = (XmlSchemaElement) item;
boolean isArray = isArray(xsElt);
processElement(xsElt, processedElementTypeMap,localNillableList,parentSchema); //we know for sure this is not an outer type
processedElementArrayStatusMap.put(xsElt, (isArray) ? Boolean.TRUE : Boolean.FALSE);
if (order) {
//we need to keep the order of the elements. So push the elements to another
//hashmap with the order number
elementOrderMap.put(xsElt, new Integer(i));
}
//handle xsd:any ! We place an OMElement (or an array of OMElements) in the generated class
} else if (item instanceof XmlSchemaAny) {
XmlSchemaAny any = (XmlSchemaAny) item;
processedElementTypeMap.put(new QName(ANY_ELEMENT_FIELD_NAME),any);
//any can also be inside a sequence
if (order) {
elementOrderMap.put(any, new Integer(i));
}
//we do not register the array status for the any type
processedElementArrayStatusMap.put(any,isArray(any) ? Boolean.TRUE : Boolean.FALSE);
} else {
//there may be other types to be handled here. Add them
//when we are ready
}
}
// loop through the processed items and add them to the matainf object
Iterator processedElementsIterator = processedElementArrayStatusMap.keySet().iterator();
int startingItemNumberOrder = metainfHolder.getOrderStartPoint();
while (processedElementsIterator.hasNext()) {
Object child = processedElementsIterator.next();
// process the XmlSchemaElement
if (child instanceof XmlSchemaElement){
XmlSchemaElement elt = (XmlSchemaElement) child;
QName referencedQName = null;
if (elt.getQName()!=null){
referencedQName = elt.getQName();
QName schemaTypeQName = elt.getSchemaType()!=null?elt.getSchemaType().getQName():elt.getSchemaTypeName();
if(schemaTypeQName != null) {
String clazzName = (String) processedElementTypeMap.get(elt.getQName());
metainfHolder.registerMapping(referencedQName,
schemaTypeQName,
clazzName,
((Boolean) processedElementArrayStatusMap.get(elt)).booleanValue() ?
SchemaConstants.ARRAY_TYPE :
SchemaConstants.ELEMENT_TYPE);
}
}
if (elt.getRefName()!=null) { //probably this is referenced
referencedQName = elt.getRefName();
boolean arrayStatus = ((Boolean) processedElementArrayStatusMap.get(elt)).booleanValue();
String clazzName = findRefClassName(referencedQName,arrayStatus);
if(clazzName == null) {
clazzName = findClassName(referencedQName,arrayStatus);
}
XmlSchemaElement refElement = getReferencedElement(parentSchema, referencedQName);
// register the mapping if we found the referenced element
// else throw an exception
if (refElement != null) {
metainfHolder.registerMapping(referencedQName,
refElement.getSchemaTypeName()
, clazzName,
arrayStatus ?
SchemaConstants.ARRAY_TYPE :
SchemaConstants.ELEMENT_TYPE);
} else {
if(referencedQName.equals(SchemaConstants.XSD_SCHEMA)) {
metainfHolder.registerMapping(referencedQName,
null,
DEFAULT_CLASS_NAME,
SchemaConstants.ANY_TYPE);
} else {
throw new SchemaCompilationException(SchemaCompilerMessages.getMessage("schema.referencedElementNotFound",referencedQName.toString()));
}
}
}
if(referencedQName == null) {
throw new SchemaCompilationException(SchemaCompilerMessages.getMessage("schema.emptyName"));
}
//register the occurence counts
metainfHolder.addMaxOccurs(referencedQName, elt.getMaxOccurs());
metainfHolder.addMinOccurs(referencedQName, elt.getMinOccurs());
//we need the order to be preserved. So record the order also
if (order) {
//record the order in the metainf holder
Integer integer = (Integer) elementOrderMap.get(elt);
metainfHolder.registerQNameIndex(referencedQName,
startingItemNumberOrder + integer.intValue());
}
//get the nillable state and register that on the metainf holder
if (localNillableList.contains(elt.getQName())){
metainfHolder.registerNillableQName(elt.getQName());
}
//get the binary state and add that to the status map
if (isBinary(elt)){
metainfHolder.addtStatus(elt.getQName(),
SchemaConstants.BINARY_TYPE);
}
// process the XMLSchemaAny
}else if (child instanceof XmlSchemaAny){
XmlSchemaAny any = (XmlSchemaAny)child;
//since there is only one element here it does not matter
//for the constant. However the problem occurs if the users
//uses the same name for an element decalration
QName anyElementFieldName = new QName(ANY_ELEMENT_FIELD_NAME);
//this can be an array or a single element
boolean isArray = ((Boolean) processedElementArrayStatusMap.get(any)).booleanValue();
metainfHolder.registerMapping(anyElementFieldName,
null,
isArray?DEFAULT_CLASS_ARRAY_NAME:DEFAULT_CLASS_NAME,
SchemaConstants.ANY_TYPE);
//if it's an array register an extra status flag with the system
if (isArray){
metainfHolder.addtStatus(anyElementFieldName,
SchemaConstants.ARRAY_TYPE);
}
metainfHolder.addMaxOccurs(anyElementFieldName,any.getMaxOccurs());
metainfHolder.addMinOccurs(anyElementFieldName,any.getMinOccurs());
if (order) {
//record the order in the metainf holder for the any
Integer integer = (Integer) elementOrderMap.get(any);
metainfHolder.registerQNameIndex(anyElementFieldName,
startingItemNumberOrder + integer.intValue());
}
}
}
//set the ordered flag in the metainf holder
metainfHolder.setOrdered(order);
}
private XmlSchemaType getType(XmlSchema schema, QName schemaTypeName) throws SchemaCompilationException {
schema = resolveParentSchema(schemaTypeName, schema);
XmlSchemaType typeByName = schema.getTypeByName(schemaTypeName);
if (typeByName == null) {
// The referenced element seems to come from an imported
// schema.
XmlSchemaObjectCollection includes = schema.getIncludes();
if (includes != null) {
Iterator tempIterator = includes.getIterator();
while (tempIterator.hasNext()) {
Object o = tempIterator.next();
XmlSchema inclSchema = null;
if (o instanceof XmlSchemaImport) {
inclSchema = ((XmlSchemaImport) o).getSchema();
if(inclSchema == null) {
inclSchema = (XmlSchema) loadedSchemaMap.get(((XmlSchemaImport) o).getNamespace());
}
}
if (o instanceof XmlSchemaInclude) {
inclSchema = ((XmlSchemaInclude) o).getSchema();
}
// get the element from the included schema
if (inclSchema != null) {
typeByName = inclSchema.getTypeByName(schemaTypeName);
}
if (typeByName != null) {
// we found the referenced element an can break the loop
break;
}
}
}
}
return typeByName;
}
private XmlSchemaElement getReferencedElement(XmlSchema parentSchema, QName referencedQName) {
XmlSchemaElement refElement = parentSchema.getElementByName(referencedQName);
if (refElement == null) {
// The referenced element seems to come from an imported
// schema.
XmlSchemaObjectCollection includes = parentSchema.getIncludes();
if (includes != null) {
Iterator tempIterator = includes.getIterator();
while (tempIterator.hasNext()) {
Object o = tempIterator.next();
XmlSchema inclSchema = null;
if (o instanceof XmlSchemaImport) {
inclSchema = ((XmlSchemaImport) o).getSchema();
if(inclSchema == null) {
inclSchema = (XmlSchema) loadedSchemaMap.get(((XmlSchemaImport) o).getNamespace());
}
}
if (o instanceof XmlSchemaInclude) {
inclSchema = ((XmlSchemaInclude) o).getSchema();
}
// get the element from the included schema
if (inclSchema != null) {
refElement = inclSchema.getElementByName(referencedQName);
}
if (refElement != null) {
// we found the referenced element an can break the loop
break;
}
}
}
}
return refElement;
}
/**
* Checks whether a given element is a binary element
* @param elt
*/
private boolean isBinary(XmlSchemaElement elt) {
return elt.getSchemaType()!=null &&
SchemaConstants.XSD_BASE64.equals(elt.getSchemaType().getQName());
}
/**
* Checks whether a given qname is a binary
* @param qName
*/
private boolean isBinary(QName qName) {
return qName!=null &&
SchemaConstants.XSD_BASE64.equals(qName);
}
/**
* Handle the simple content
*
* @param simpleType
*/
private void processSimpleSchemaType(XmlSchemaSimpleType simpleType,
XmlSchemaElement xsElt,
XmlSchema parentSchema) throws SchemaCompilationException{
if (processedTypemap.containsKey(simpleType.getQName())
|| baseSchemaTypeMap.containsKey(simpleType.getQName())) {
return;
}
String fullyQualifiedClassName = null;
if(simpleType.getQName() != null) {
// Must do this up front to support recursive types
fullyQualifiedClassName = writer.makeFullyQualifiedClassName(simpleType.getQName());
} else {
fullyQualifiedClassName = writer.makeFullyQualifiedClassName(xsElt.getQName());
simpleType.addMetaInfo(SchemaConstants.SchemaCompilerInfoHolder.FAKE_QNAME,
new QName(xsElt.getQName().getNamespaceURI(), xsElt.getQName().getLocalPart()));
}
processedTypemap.put(simpleType.getQName(), fullyQualifiedClassName);
//register that in the schema metainfo bag
simpleType.addMetaInfo(SchemaConstants.SchemaCompilerInfoHolder.CLASSNAME_KEY,
fullyQualifiedClassName);
BeanWriterMetaInfoHolder metaInfHolder = processSimpleType(simpleType, parentSchema);
metaInfHolder.setSimple(true);
if(simpleType.getQName() == null) {
this.processedAnonymousComplexTypesMap.put(xsElt, metaInfHolder);
simpleTypesMap.put(new QName(xsElt.getQName().getNamespaceURI(), xsElt.getQName().getLocalPart()), fullyQualifiedClassName);
}
//add this information to the metainfo holder
metaInfHolder.setOwnQname(simpleType.getQName());
if(fullyQualifiedClassName != null) {
metaInfHolder.setOwnClassName(fullyQualifiedClassName);
}
//write the class. This type mapping would have been populated right now
//Note - We always write classes for named complex types
writeSimpleType(simpleType, metaInfHolder);
}
private BeanWriterMetaInfoHolder processSimpleType(XmlSchemaSimpleType simpleType,XmlSchema parentSchema) throws SchemaCompilationException {
BeanWriterMetaInfoHolder metaInfHolder = new BeanWriterMetaInfoHolder();
// handle the restriction
XmlSchemaSimpleTypeContent content = simpleType.getContent();
if (content != null) {
if (content instanceof XmlSchemaSimpleTypeRestriction) {
XmlSchemaSimpleTypeRestriction restriction = (XmlSchemaSimpleTypeRestriction) content;
QName baseTypeName = restriction.getBaseTypeName();
//check whether the base type is one of the base schema types
if (baseSchemaTypeMap.containsKey(baseTypeName)) {
//process restriction base type
QName qName = simpleType.getQName();
if(qName == null) {
qName = (QName) simpleType.getMetaInfoMap().get(SchemaConstants.SchemaCompilerInfoHolder.FAKE_QNAME);
}
processSimpleRestrictionBaseType(qName, restriction.getBaseTypeName(),metaInfHolder);
//process facets
XmlSchemaObjectCollection facets = restriction.getFacets();
processFacets(facets,metaInfHolder);
} else {
//recurse
if (restriction.getBaseType() != null) {
processSimpleSchemaType(restriction.getBaseType(), null, parentSchema);
}
}
}else if (content instanceof XmlSchemaSimpleTypeUnion) {
//Todo - Handle unions here
throw new SchemaCompilationException(
SchemaCompilerMessages.getMessage("schema.unsupportedcontenterror","Simple Type Union in " + simpleType.getQName()));
}else if (content instanceof XmlSchemaSimpleTypeList){
//todo - Handle lists here
throw new SchemaCompilationException(
SchemaCompilerMessages.getMessage("schema.unsupportedcontenterror","Simple Type List in " + simpleType.getQName()));
}
}
return metaInfHolder;
}
/**
* Find whether a given particle is an array. The logic for deciding
* whether a given particle is an array is depending on their minOccurs
* and maxOccurs counts. If Maxoccurs is greater than one (1) then the
* content is an array.
* Also no higher level element will have the maxOccurs greater than one
*
* @param particle
* @throws SchemaCompilationException
*/
private boolean isArray(XmlSchemaParticle particle) throws SchemaCompilationException {
long minOccurs = particle.getMinOccurs();
long maxOccurs = particle.getMaxOccurs();
if (maxOccurs < minOccurs) {
throw new SchemaCompilationException();
} else {
return (maxOccurs > 1);
}
}
private String getNextTypeSuffix(){
if (typeCounter==Integer.MAX_VALUE){
typeCounter = 0;
}
return ("_type" +typeCounter++);
}
}