blob: 5f544110b0c6f2296d0d553e00fb5adb7475816c [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// dmccann - Mar 2/2009 - 2.0 - Initial implementation
package org.eclipse.persistence.internal.oxm.schema;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Vector;
import javax.xml.namespace.QName;
import org.eclipse.persistence.core.descriptors.CoreInheritancePolicy;
import org.eclipse.persistence.core.mappings.CoreMapping;
import org.eclipse.persistence.core.mappings.converters.CoreConverter;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.oxm.Constants;
import org.eclipse.persistence.internal.oxm.ConversionManager;
import org.eclipse.persistence.internal.oxm.Namespace;
import org.eclipse.persistence.internal.oxm.NamespaceResolver;
import org.eclipse.persistence.internal.oxm.XMLChoiceFieldToClassAssociation;
import org.eclipse.persistence.internal.oxm.XPathFragment;
import org.eclipse.persistence.internal.oxm.mappings.AnyAttributeMapping;
import org.eclipse.persistence.internal.oxm.mappings.AnyCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.AnyObjectMapping;
import org.eclipse.persistence.internal.oxm.mappings.BinaryDataCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.BinaryDataMapping;
import org.eclipse.persistence.internal.oxm.mappings.ChoiceCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.ChoiceObjectMapping;
import org.eclipse.persistence.internal.oxm.mappings.CollectionReferenceMapping;
import org.eclipse.persistence.internal.oxm.mappings.CompositeCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.CompositeObjectMapping;
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
import org.eclipse.persistence.internal.oxm.mappings.DirectCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.DirectMapping;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.mappings.Mapping;
import org.eclipse.persistence.internal.oxm.mappings.ObjectReferenceMapping;
import org.eclipse.persistence.internal.oxm.schema.model.Any;
import org.eclipse.persistence.internal.oxm.schema.model.AnyAttribute;
import org.eclipse.persistence.internal.oxm.schema.model.Attribute;
import org.eclipse.persistence.internal.oxm.schema.model.Choice;
import org.eclipse.persistence.internal.oxm.schema.model.ComplexContent;
import org.eclipse.persistence.internal.oxm.schema.model.ComplexType;
import org.eclipse.persistence.internal.oxm.schema.model.Element;
import org.eclipse.persistence.internal.oxm.schema.model.Extension;
import org.eclipse.persistence.internal.oxm.schema.model.Import;
import org.eclipse.persistence.internal.oxm.schema.model.Occurs;
import org.eclipse.persistence.internal.oxm.schema.model.Restriction;
import org.eclipse.persistence.internal.oxm.schema.model.Schema;
import org.eclipse.persistence.internal.oxm.schema.model.Sequence;
import org.eclipse.persistence.internal.oxm.schema.model.SimpleContent;
import org.eclipse.persistence.internal.oxm.schema.model.SimpleType;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.EnumTypeConverter;
import org.eclipse.persistence.oxm.XMLContext;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.XMLMarshaller;
import org.eclipse.persistence.oxm.schema.XMLSchemaReference;
import org.eclipse.persistence.sessions.Project;
/**
* INTERNAL:
* <p><b>Purpose:</b>Generate one or more EclipseLink Schema model objects based
* on a given list of XMLDescriptors.
* <p><b>Responsibilities:</b><ul>
* <li>Return a Map of generated EclipseLink Schema objects based on a given list of XMLDescriptors</li>
* </ul>
* <p> This class will create and populate one or more EclipseLink schema model Schema objects for a
* given list of EclipseLink XMLDescriptors.
*
* @see Schema
* @see XMLDescriptor
*/
public class SchemaModelGenerator {
protected static final String SCHEMA_FILE_NAME = "schema";
protected static final String SCHEMA_FILE_EXT = ".xsd";
protected static final String TEXT = "text()";
protected static final String ID = "ID";
protected static final String IDREF = "IDREF";
protected static String SWAREF_LOCATION;
private ConversionManager conversionManager;
/**
* The default constructor.
*/
public SchemaModelGenerator(ConversionManager conversionManager) {
this(conversionManager, false);
}
/**
* This constructor should be used with a value of 'true' when the schemaLocation
* attribute of the import for swaRef should be swaref.xsd. This is useful when
* the user has a local copy of the swaRef schema and wants to use it when access
* to external URLs is not available. The default value is:
* "http://ws-i.org/profiles/basic/1.1/swaref.xsd".
*/
public SchemaModelGenerator(ConversionManager conversionManager, boolean customSwaRefSchema) {
this.conversionManager = conversionManager;
if (customSwaRefSchema) {
SWAREF_LOCATION = Constants.SWA_REF.toLowerCase() + SCHEMA_FILE_EXT;
} else {
SWAREF_LOCATION = Constants.SWAREF_XSD;
}
}
/**
* Generates a Map of EclipseLink schema model Schema objects for a given list of XMLDescriptors.
* The descriptors are assumed to have been initialized. One Schema object will be generated
* per namespace.
*
* @param descriptorsToProcess list of XMLDescriptors which will be used to generate Schema objects
* @param properties holds a namespace to Properties map containing schema settings, such as elementFormDefault
* @param additionalGlobalElements a map of QName-Type entries identifying additional global elements to be added
* @return a map of namespaces to EclipseLink schema model Schema objects
* @throws DescriptorException if the reference descriptor for a composite mapping is not in the list of descriptors
* @see Schema
*/
public Map<String, Schema> generateSchemas(List<Descriptor> descriptorsToProcess, SchemaModelGeneratorProperties properties, Map<QName, Type> additionalGlobalElements) throws DescriptorException {
Map<String, Schema> schemaForNamespace = generateSchemas(descriptorsToProcess, properties);
// process any additional global elements
if (additionalGlobalElements != null) {
for(Entry<QName, Type> entry : additionalGlobalElements.entrySet()) {
QName qname = entry.getKey();
Type type = entry.getValue();
if (type instanceof Class) {
Class tClass = (Class) type;
String nsKey = qname.getNamespaceURI();
Schema schema = schemaForNamespace.get(nsKey);
QName typeAsQName = conversionManager.schemaType(tClass);
if (typeAsQName == null) {
// not a built in type - need to get the type via schema reference
Descriptor desc = getDescriptorByClass(tClass, descriptorsToProcess);
if (desc == null) {
// at this point we can't determine the element type, so don't add anything
continue;
}
// if the schema is null generate a new one and create an import
if (schema == null) {
schema = buildNewSchema(nsKey, new org.eclipse.persistence.oxm.NamespaceResolver(), schemaForNamespace.size(), properties);
schemaForNamespace.put(nsKey, schema);
typeAsQName = desc.getSchemaReference().getSchemaContextAsQName();
Schema schemaForUri = schemaForNamespace.get(typeAsQName.getNamespaceURI());
if (!importExists(schema, schemaForUri.getTargetNamespace())) {
Import newImport = new Import();
newImport.setNamespace(schemaForUri.getTargetNamespace());
newImport.setSchemaLocation(schemaForUri.getName());
schema.getImports().add(newImport);
}
} else {
typeAsQName = desc.getSchemaReference().getSchemaContextAsQName(schema.getNamespaceResolver());
}
}
if (schema == null) {
schema = buildNewSchema(nsKey, new org.eclipse.persistence.oxm.NamespaceResolver(), schemaForNamespace.size(), properties);
schemaForNamespace.put(nsKey, schema);
}
Element element = new Element();
element.setName(qname.getLocalPart());
element.setType(getSchemaTypeString(typeAsQName, schema));
schema.addTopLevelElement(element);
}
}
}
return schemaForNamespace;
}
/**
* Generates a Map of EclipseLink schema model Schema objects for a given list of XMLDescriptors.
* The descriptors are assumed to have been initialized. One Schema object will be generated
* per namespace.
*
* @param descriptorsToProcess list of XMLDescriptors which will be used to generate Schema objects
* @param properties holds a namespace to Properties map containing schema settings, such as elementFormDefault
* @return a map of namespaces to EclipseLink schema model Schema objects
* @throws DescriptorException if the reference descriptor for a composite mapping is not in the list of descriptors
* @see Schema
*/
public Map<String, Schema> generateSchemas(List<Descriptor> descriptorsToProcess, SchemaModelGeneratorProperties properties) throws DescriptorException {
HashMap<String, Schema> schemaForNamespace = new HashMap<>();
Schema workingSchema = null;
if (properties == null) {
properties = new SchemaModelGeneratorProperties();
}
// set up schemas for the descriptors
for (Descriptor desc : descriptorsToProcess) {
String namespace;
XMLSchemaReference schemaRef = desc.getSchemaReference();
if (schemaRef != null) {
namespace = schemaRef.getSchemaContextAsQName(desc.getNamespaceResolver()).getNamespaceURI();
workingSchema = getSchema(namespace, desc.getNamespaceResolver(), schemaForNamespace, properties);
addNamespacesToWorkingSchema(desc.getNamespaceResolver(), workingSchema);
} else {
// at this point there is no schema reference set, but if a descriptor has a
// default root element set we will need to generate a global element for it
for (DatabaseTable table : (Vector<DatabaseTable>)desc.getTables()) {
namespace = getDefaultRootElementAsQName(desc, table.getName()).getNamespaceURI();
workingSchema = getSchema(namespace, desc.getNamespaceResolver(), schemaForNamespace, properties);
addNamespacesToWorkingSchema(desc.getNamespaceResolver(), workingSchema);
}
}
}
// process the descriptors
for (Descriptor xdesc : descriptorsToProcess) {
processDescriptor(xdesc, schemaForNamespace, workingSchema, properties, descriptorsToProcess);
}
// return the generated schema(s)
return schemaForNamespace;
}
/**
* Generates a Map of EclipseLink schema model Schema objects for a given list of XMLDescriptors.
* The descriptors are assumed to have been initialized. One Schema object will be generated
* per namespace.
*
* @param descriptorsToProcess list of XMLDescriptors which will be used to generate Schema objects
* @param properties holds a namespace to Properties map containing schema settings, such as elementFormDefault
* @param additionalGlobalElements a map of QName-Type entries identifying additional global elements to be added
* @return a map of namespaces to EclipseLink schema model Schema objects
* @throws DescriptorException if the reference descriptor for a composite mapping is not in the list of descriptors
* @see Schema
*/
public Map<String, Schema> generateSchemas(List<Descriptor> descriptorsToProcess, SchemaModelGeneratorProperties properties, SchemaModelOutputResolver outputResolver, Map<QName, Type> additionalGlobalElements) throws DescriptorException {
Map<String, Schema> schemas = generateSchemas(descriptorsToProcess, properties, additionalGlobalElements);
// write out the generates schema(s) via the given output resolver
Project proj = new SchemaModelProject();
XMLContext context = new XMLContext(proj);
XMLMarshaller marshaller = context.createMarshaller();
Descriptor schemaDescriptor = (Descriptor)proj.getDescriptor(Schema.class);
int schemaCount = 0;
for (Entry<String, Schema> entry : schemas.entrySet()) {
Schema schema = entry.getValue();
try {
NamespaceResolver schemaNamespaces = schema.getNamespaceResolver();
schemaNamespaces.put(Constants.SCHEMA_PREFIX, "http://www.w3.org/2001/XMLSchema");
schemaDescriptor.setNamespaceResolver(schemaNamespaces);
javax.xml.transform.Result target = outputResolver.createOutput(schema.getTargetNamespace(), schema.getName());
marshaller.marshal(schema, target);
schemaCount++;
} catch (IOException ex) {
ex.printStackTrace();
}
}
return schemas;
}
/**
* Generates a Map of EclipseLink schema model Schema objects for a given list of XMLDescriptors.
* The descriptors are assumed to have been initialized. One Schema object will be generated
* per namespace.
*
* @param descriptorsToProcess list of XMLDescriptors which will be used to generate Schema objects
* @param properties holds a namespace to Properties map containing schema settings, such as elementFormDefault
* @return a map of namespaces to EclipseLink schema model Schema objects
* @throws DescriptorException if the reference descriptor for a composite mapping is not in the list of descriptors
* @see Schema
*/
public Map<String, Schema> generateSchemas(List<Descriptor> descriptorsToProcess, SchemaModelGeneratorProperties properties, SchemaModelOutputResolver outputResolver) throws DescriptorException {
return generateSchemas(descriptorsToProcess, properties, outputResolver, null);
}
/**
* Process a given descriptor. Global complex types will be generated for based on
* schema context, and global elements based on default root element.
*
*/
protected void processDescriptor(Descriptor desc, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors) {
// determine if a simple type (or complex type with simple content) or complex type is required
boolean simple = isSimple(desc);
XMLSchemaReference schemaRef = desc.getSchemaReference();
if (schemaRef != null) {
if (schemaRef.getType() == org.eclipse.persistence.platform.xml.XMLSchemaReference.COMPLEX_TYPE) {
workingSchema.addTopLevelComplexTypes(buildComplexType(false, desc, schemaForNamespace, workingSchema, properties, descriptors));
} else if (schemaRef.getType() == org.eclipse.persistence.platform.xml.XMLSchemaReference.SIMPLE_TYPE) {
workingSchema.addTopLevelSimpleTypes(buildSimpleType(desc, workingSchema, true));
} else if (schemaRef.getType() == org.eclipse.persistence.platform.xml.XMLSchemaReference.ELEMENT) {
workingSchema.addTopLevelElement(buildElement(desc, schemaForNamespace, workingSchema, properties, descriptors, simple));
}
for (DatabaseTable table : (Vector<DatabaseTable>)desc.getTables()) {
String localName = getDefaultRootElementAsQName(desc, table.getName()).getLocalPart();
// don't overwrite existing top level elements
if (workingSchema.getTopLevelElements().get(localName) != null) {
continue;
}
Element topLevelElement = new Element();
topLevelElement.setName(localName);
QName qname = schemaRef.getSchemaContextAsQName(workingSchema.getNamespaceResolver());
String elementType = qname.getLocalPart();
String elementTypeUri = qname.getNamespaceURI();
String elementTypePrefix = workingSchema.getNamespaceResolver().resolveNamespaceURI(elementTypeUri);
if (elementTypePrefix != null) {
elementType = elementTypePrefix + Constants.COLON + elementType;
}
topLevelElement.setType(elementType);
workingSchema.addTopLevelElement(topLevelElement);
}
} else {
// here we have a descriptor that does not have a schema reference set, but since
// there is a default root element set we need to generate a global element
for (DatabaseTable table : (Vector<DatabaseTable>)desc.getTables()) {
String localName = getDefaultRootElementAsQName(desc, table.getName()).getLocalPart();
// a global element may have been created while generating an element ref
if (workingSchema.getTopLevelElements().get(localName) == null) {
Element topLevelElement = new Element();
topLevelElement.setName(localName);
if (simple) {
if (isComplexTypeWithSimpleContentRequired(desc)) {
topLevelElement.setComplexType(buildComplexTypeWithSimpleContent(desc, schemaForNamespace, workingSchema, properties, descriptors));
} else {
topLevelElement.setSimpleType(buildSimpleType(desc, workingSchema, false));
}
} else {
topLevelElement.setComplexType(buildComplexType(true, desc, schemaForNamespace, workingSchema, properties, descriptors));
}
workingSchema.addTopLevelElement(topLevelElement);
}
}
}
}
/**
* Return the Schema for a given namespace. If one doesn't exist, a new one will
* be created and returned.
*
* @see Schema
*/
protected Schema getSchema(String uri, NamespaceResolver nr, HashMap<String, Schema> schemaForNamespace, SchemaModelGeneratorProperties properties) {
Schema schema = schemaForNamespace.get(uri);
if (schema == null) {
schema = buildNewSchema(uri, nr, schemaForNamespace.size(), properties);
schemaForNamespace.put(uri, schema);
}
return schema;
}
/**
* Create and return an Element for a given XMLDescriptor.
*
*/
protected Element buildElement(Descriptor desc, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors, boolean simple) {
Element element = new Element();
element.setName(desc.getSchemaReference().getSchemaContextAsQName(workingSchema.getNamespaceResolver()).getLocalPart());
if (simple) {
if (isComplexTypeWithSimpleContentRequired(desc)) {
element.setComplexType(buildComplexTypeWithSimpleContent(desc, schemaForNamespace, workingSchema, properties, descriptors));
} else {
element.setSimpleType(buildSimpleType(desc, workingSchema, false));
}
} else {
element.setComplexType(buildComplexType(true, desc, schemaForNamespace, workingSchema, properties, descriptors));
}
return element;
}
/**
* Create and return a SimpleType for a given XMLDescriptor.
*
*/
protected SimpleType buildSimpleType(Descriptor desc, Schema workingSchema, boolean global) {
SimpleType st;
if (global) {
st = buildNewSimpleType(desc.getSchemaReference().getSchemaContextAsQName(workingSchema.getNamespaceResolver()).getLocalPart());
} else {
st = new SimpleType();
}
CoreMapping mapping = (CoreMapping)desc.getMappings().get(0);
QName qname = conversionManager.schemaType(mapping.getAttributeClassification());
String baseType = qname.getLocalPart();
if (qname.getNamespaceURI() != null) {
String prefix = workingSchema.getNamespaceResolver().resolveNamespaceURI(qname.getNamespaceURI());
if (prefix == null) {
prefix = workingSchema.getNamespaceResolver().generatePrefix();
workingSchema.getNamespaceResolver().put(prefix, qname.getNamespaceURI());
}
baseType = prefix + Constants.COLON + baseType;
}
Restriction restriction = new Restriction();
restriction.setBaseType(baseType);
st.setRestriction(restriction);
return st;
}
/**
* Create and return a SimpleType with name set to the given name.
*
*/
protected SimpleType buildNewSimpleType(String name) {
SimpleType st = new SimpleType();
st.setName(name);
return st;
}
/**
* Create and return a ComplexType for a given XMLDescriptor. Assumes that the descriptor has a schema context
* set.
*
*/
protected ComplexType buildComplexType(boolean anonymous, Descriptor desc, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors) {
ComplexType ct = new ComplexType();
if (!anonymous) {
ct.setName(desc.getSchemaReference().getSchemaContextAsQName(workingSchema.getNamespaceResolver()).getLocalPart());
}
CoreInheritancePolicy inheritancePolicy = desc.getInheritancePolicyOrNull();
Extension extension = null;
if (inheritancePolicy != null && inheritancePolicy.getParentClass() != null) {
extension = new Extension();
extension.setBaseType(desc.getSchemaReference().getSchemaContextAsQName(workingSchema.getNamespaceResolver()).getLocalPart());
ComplexContent complexContent = new ComplexContent();
complexContent.setExtension(extension);
ct.setComplexContent(complexContent);
}
Sequence seq = new Sequence();
for (CoreMapping mapping : (Vector<CoreMapping>)desc.getMappings()) {
processMapping(mapping, seq, ct, schemaForNamespace, workingSchema, properties, descriptors);
}
if (extension != null) {
extension.setSequence(seq);
} else {
ct.setSequence(seq);
}
return ct;
}
/**
* Create and return a ComplexType containing simple content for a given XMLDescriptor. Assumes
* that the descriptor has a schema context set.
*
*/
private ComplexType buildComplexTypeWithSimpleContent(Descriptor desc, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors) {
ComplexType ct = new ComplexType();
SimpleContent sc = new SimpleContent();
Extension extension = new Extension();
sc.setExtension(extension);
ct.setSimpleContent(sc);
for (CoreMapping mapping : (Vector<CoreMapping>)desc.getMappings()) {
Field xFld = (Field) mapping.getField();
if (xFld.getXPath().equals(TEXT)) {
extension.setBaseType(getSchemaTypeForDirectMapping((DirectMapping) mapping, workingSchema));
} else if (xFld.getXPathFragment().isAttribute()) {
String schemaTypeString = getSchemaTypeForDirectMapping((DirectMapping) mapping, workingSchema);
Attribute attr = buildAttribute((DirectMapping) mapping, schemaTypeString);
extension.getOrderedAttributes().add(attr);
}
}
return ct;
}
/**
* Return the schema type for a given mapping's xmlfield. If the field does not have a schema type
* set, the attribute classification will be used if non-null. Otherwise, ClassConstants.STRING
* will be returned.
*
*/
protected String getSchemaTypeForDirectMapping(DirectMapping mapping, Schema workingSchema) {
return getSchemaTypeForElement((Field) mapping.getField(), mapping.getAttributeClassification(), workingSchema);
}
/**
* Return the schema type for a given xmlfield. If the field does not have a schema type set,
* the attribute classification will be used if non-null. Otherwise, ClassConstants.STRING
* will be returned.
*
*/
protected String getSchemaTypeForElement(Field xmlField, Class attrClass, Schema workingSchema) {
String schemaTypeString = null;
QName schemaType = xmlField.getSchemaType();
if (schemaType != null) {
schemaTypeString = getSchemaTypeString(schemaType, workingSchema);
} else {
if (attrClass != null && !attrClass.equals(CoreClassConstants.STRING)) {
QName qName = conversionManager.schemaType(attrClass);
if (qName != null) {
schemaTypeString = getSchemaTypeString(qName, workingSchema);
}
} else {
// default to string
schemaTypeString = getSchemaTypeString(Constants.STRING_QNAME, workingSchema);
}
}
return schemaTypeString;
}
/**
* Return the descriptor from the list whose java class name matches
* javaClassName. If none exists null will be returned.
*
*/
protected Descriptor getDescriptorByName(String javaClassName, List<Descriptor> descriptors) {
for (Descriptor xDesc : descriptors) {
if (xDesc.getJavaClassName().equals(javaClassName)) {
return xDesc;
}
}
return null;
}
/**
* Return the descriptor from the list whose java class matches
* javaClass. If none exists null will be returned.
*
*/
protected Descriptor getDescriptorByClass(Class javaClass, List<Descriptor> descriptors) {
for (Descriptor xDesc : descriptors) {
if (xDesc.getJavaClass() != null && xDesc.getJavaClass() == javaClass) {
return xDesc;
}
}
return null;
}
/**
* Adds an Any to a given sequence. If isCollection is true, maxOccurs will
* be set to unbounded.
*
* @see Any
* @see Occurs#UNBOUNDED
*/
protected void processAnyMapping(Sequence seq, boolean isCollection) {
Any any = new Any();
any.setProcessContents(Any.LAX);
any.setMinOccurs(Occurs.ZERO);
if (isCollection) {
any.setMaxOccurs(Occurs.UNBOUNDED);
}
seq.addAny(any);
}
/**
* Process a given XMLBinaryDataMapping.
*
*/
protected void processXMLBinaryDataMapping(BinaryDataMapping mapping, Sequence seq, ComplexType ct, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties) {
Field xmlField = (Field) mapping.getField();
XPathFragment frag = xmlField.getXPathFragment();
String schemaTypeString;
if (mapping.isSwaRef()) {
schemaTypeString = getSchemaTypeString(Constants.SWA_REF_QNAME, workingSchema);
Import newImport = new Import();
newImport.setNamespace(Constants.REF_URL);
newImport.setSchemaLocation(SWAREF_LOCATION);
workingSchema.getImports().add(newImport);
} else {
schemaTypeString = getSchemaTypeString(Constants.BASE_64_BINARY_QNAME, workingSchema);
}
seq = buildSchemaComponentsForXPath(frag, seq, schemaForNamespace, workingSchema, properties);
frag = getTargetXPathFragment(frag);
Element elem = elementExistsInSequence(frag.getLocalName(), frag.getShortName(), seq);
if (elem == null) {
if (frag.getNamespaceURI() != null) {
elem = handleFragNamespace(frag, schemaForNamespace, workingSchema, properties, null, schemaTypeString);
} else {
elem = buildElement(frag, schemaTypeString, Occurs.ZERO, null);
}
if (mapping.getNullPolicy().isNullRepresentedByXsiNil()) {
elem.setNillable(true);
}
if (xmlField.isRequired()) {
elem.setMinOccurs("1");
}
if (mapping.getMimeType() != null) {
elem.getAttributesMap().put(Constants.EXPECTED_CONTENT_TYPES_QNAME, mapping.getMimeType());
}
seq.addElement(elem);
}
}
/**
* Process a given XMLBinaryDataCollectionMapping.
*
*/
protected void processXMLBinaryDataCollectionMapping(BinaryDataCollectionMapping mapping, Sequence seq, ComplexType ct, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties) {
Field xmlField = (Field) mapping.getField();
XPathFragment frag = xmlField.getXPathFragment();
String schemaTypeString;
if (mapping.isSwaRef()) {
schemaTypeString = getSchemaTypeString(Constants.SWA_REF_QNAME, workingSchema);
} else {
schemaTypeString = getSchemaTypeString(Constants.BASE_64_BINARY_QNAME, workingSchema);
}
seq = buildSchemaComponentsForXPath(frag, seq, schemaForNamespace, workingSchema, properties);
frag = getTargetXPathFragment(frag);
Element elem = elementExistsInSequence(frag.getLocalName(), frag.getShortName(), seq);
if (elem == null) {
if (frag.getNamespaceURI() != null) {
elem = handleFragNamespace(frag, schemaForNamespace, workingSchema, properties, null, schemaTypeString);
elem.setMaxOccurs(Occurs.UNBOUNDED);
} else {
elem = buildElement(frag, schemaTypeString, Occurs.ZERO, Occurs.UNBOUNDED);
}
if (mapping.getNullPolicy().isNullRepresentedByXsiNil()) {
elem.setNillable(true);
}
if (xmlField.isRequired()) {
elem.setMinOccurs("1");
}
if (mapping.getMimeType() != null) {
elem.getAttributesMap().put(Constants.EXPECTED_CONTENT_TYPES_QNAME, mapping.getMimeType());
}
seq.addElement(elem);
}
}
/**
* Process a given XMLDirectMapping.
*
*/
protected void processXMLDirectMapping(DirectMapping mapping, Sequence seq, ComplexType ct, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties) {
Field xmlField = (Field) mapping.getField();
XPathFragment frag = xmlField.getXPathFragment();
if (frag.isSelfFragment()) {
// do nothing;
return;
}
// Handle ID
boolean isPk = isFragPrimaryKey(frag, mapping);
String schemaTypeString = null;
if (isPk) {
schemaTypeString = Constants.SCHEMA_PREFIX + Constants.COLON + ID;
} else {
schemaTypeString = getSchemaTypeForDirectMapping(mapping, workingSchema);
}
// Handle enumerations
Class attributeClassification = mapping.getAttributeClassification();
if (attributeClassification != null && Enum.class.isAssignableFrom(attributeClassification)) {
CoreConverter converter = mapping.getConverter();
if (converter != null && converter instanceof EnumTypeConverter) {
processEnumeration(schemaTypeString, frag, mapping, seq, ct, workingSchema, converter);
return;
}
}
if (frag.isAttribute()) {
Attribute attr = buildAttribute(mapping, schemaTypeString);
if (xmlField.isRequired()) {
attr.setUse(Attribute.REQUIRED);
}
ct.getOrderedAttributes().add(attr);
} else {
seq = buildSchemaComponentsForXPath(frag, seq, schemaForNamespace, workingSchema, properties);
frag = getTargetXPathFragment(frag);
Element elem = elementExistsInSequence(frag.getLocalName(), frag.getShortName(), seq);
if (elem == null) {
if (frag.getNamespaceURI() != null) {
elem = handleFragNamespace(frag, schemaForNamespace, workingSchema, properties, null, schemaTypeString);
} else {
elem = buildElement(frag, schemaTypeString, Occurs.ZERO, null);
}
if (mapping.getNullPolicy().isNullRepresentedByXsiNil()) {
elem.setNillable(true);
}
if (xmlField.isRequired()) {
elem.setMinOccurs("1");
}
seq.addElement(elem);
}
}
}
/**
* Process a given XMLCompositeDirectCollectionMapping.
*
*/
protected void processXMLCompositeDirectCollectionMapping(DirectCollectionMapping mapping, Sequence seq, ComplexType ct, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties) {
Field xmlField = ((Field) (mapping).getField());
XPathFragment frag = xmlField.getXPathFragment();
seq = buildSchemaComponentsForXPath(frag, seq, schemaForNamespace, workingSchema, properties);
frag = getTargetXPathFragment(frag);
String schemaTypeString = getSchemaTypeForElement(xmlField, mapping.getAttributeElementClass(), workingSchema);
Element element = null;
if (xmlField.usesSingleNode()) {
SimpleType st = new SimpleType();
org.eclipse.persistence.internal.oxm.schema.model.List list = new org.eclipse.persistence.internal.oxm.schema.model.List();
if (schemaTypeString == null) {
schemaTypeString = getSchemaTypeString(Constants.ANY_SIMPLE_TYPE_QNAME, workingSchema);
}
list.setItemType(schemaTypeString);
st.setList(list);
element = buildElement(xmlField.getXPathFragment(), null, Occurs.ZERO, null);
element.setSimpleType(st);
} else {
if (frag.getNamespaceURI() != null) {
element = handleFragNamespace(frag, schemaForNamespace, workingSchema, properties, element, schemaTypeString);
element.setMaxOccurs(Occurs.UNBOUNDED);
} else {
element = buildElement(frag, schemaTypeString, Occurs.ZERO, Occurs.UNBOUNDED);
}
}
if (mapping.getNullPolicy().isNullRepresentedByXsiNil()) {
element.setNillable(true);
}
if (xmlField.isRequired()) {
element.setMinOccurs("1");
}
seq.addElement(element);
}
/**
* Process a given XML composite mapping - either an XMLCompositeObjectMapping, or an
* XMLCompositeCollectionMapping. For XMLCompositeDirectCollectionMappings the
* processXMLCompositeDirectCollectionMapping method should be used.
*
*/
protected void processXMLCompositeMapping(CompositeObjectMapping mapping, Sequence seq, ComplexType ct, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors, boolean collection) {
Field xmlField = (Field) mapping.getField();
String refClassName = mapping.getReferenceClassName();
Descriptor refDesc = getDescriptorByName(refClassName, descriptors);
if (refDesc == null) {
throw DescriptorException.descriptorIsMissing(refClassName, (DatabaseMapping)mapping);
}
XPathFragment frag = xmlField.getXPathFragment();
seq = buildSchemaComponentsForXPath(frag, seq, schemaForNamespace, workingSchema, properties);
frag = getTargetXPathFragment(frag);
Element element = buildElement(frag, null, Occurs.ZERO, (collection ? Occurs.UNBOUNDED : null));
ComplexType ctype = null;
// if the reference descriptor's schema context is null we need to generate an anonymous complex type
if (refDesc.getSchemaReference() == null) {
ctype = buildComplexType(true, refDesc, schemaForNamespace, workingSchema, properties, descriptors);
} else {
element.setType(getSchemaTypeString(refDesc.getSchemaReference().getSchemaContextAsQName(workingSchema.getNamespaceResolver()), workingSchema));
}
if (frag.getNamespaceURI() != null) {
// may need to add a global element
element = handleFragNamespace(frag, schemaForNamespace, workingSchema, properties, element, ctype, refDesc);
} else if (ctype != null) {
// set an anonymous complex type
element.setComplexType(ctype);
}
boolean isNillable = false;
if (!collection) {
isNillable = mapping.getNullPolicy().isNullRepresentedByXsiNil();
} else {
isNillable = mapping.getNullPolicy().isNullRepresentedByXsiNil();
}
element.setNillable(isNillable);
if (xmlField.isRequired()) {
element.setMinOccurs("1");
}
seq.addElement(element);
}
/**
* Process a given XMLChoiceCollectionMapping.
*
*/
protected void processXMLChoiceCollectionMapping(ChoiceCollectionMapping mapping, Sequence seq, ComplexType ct, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors) {
Map<Field, Class> fieldToClassMap = mapping.getFieldToClassMappings();
List<XMLChoiceFieldToClassAssociation> choiceFieldToClassList = mapping.getChoiceFieldToClassAssociations();
processChoiceMapping(fieldToClassMap, choiceFieldToClassList, seq, ct, schemaForNamespace, workingSchema, properties, descriptors, true);
}
/**
*
*/
protected void processXMLChoiceObjectMapping(ChoiceObjectMapping mapping, Sequence seq, ComplexType ct, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors) {
Map<Field, Class> fieldToClassMap =mapping.getFieldToClassMappings();
List<XMLChoiceFieldToClassAssociation> choiceFieldToClassList = mapping.getChoiceFieldToClassAssociations();
processChoiceMapping(fieldToClassMap, choiceFieldToClassList, seq, ct, schemaForNamespace, workingSchema, properties, descriptors, false);
}
/**
* Process a given XMLChoiceMapping.
*
*/
protected void processChoiceMapping(Map<Field, Class> fieldToClassMap, List<XMLChoiceFieldToClassAssociation> choiceFieldToClassList, Sequence seq, ComplexType ct, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors, boolean isCollection) {
Choice theChoice = new Choice();
if (isCollection) {
theChoice.setMaxOccurs(Occurs.UNBOUNDED);
}
for (XMLChoiceFieldToClassAssociation next : choiceFieldToClassList) {
Field field = next.getXmlField();
Element element = buildElement(field.getXPathFragment().getShortName(), Occurs.ZERO, null);
QName schemaTypeQName = field.getSchemaType();
if (schemaTypeQName != null) {
element.setType(getSchemaTypeString(schemaTypeQName, workingSchema));
} else {
element = processReferenceDescriptor(element, getDescriptorByClass(fieldToClassMap.get(field), descriptors), schemaForNamespace, workingSchema, properties, descriptors, field, false);
}
theChoice.addElement(element);
}
seq.addChoice(theChoice);
}
/**
* Process a given XMLObjectReferenceMapping. In the case of an XMLCollectionReferenceMapping,
* i.e. the isCollection flag is set to true, maxOccurs will be set to 'unbounded' on any
* source elements
*
*/
protected void processXMLObjectReferenceMapping(ObjectReferenceMapping mapping, Sequence seq, ComplexType ct, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors, boolean isCollection) {
String tgtClassName = mapping.getReferenceClassName();
Descriptor tgtDesc = getDescriptorByName(tgtClassName, descriptors);
if (tgtDesc == null) {
throw DescriptorException.descriptorIsMissing(tgtClassName, (DatabaseMapping)mapping);
}
// get the target mapping(s) to determine the appropriate type(s)
String schemaTypeString = null;
Map<Field, Field> associations = mapping.getSourceToTargetKeyFieldAssociations();
for (Entry<Field, Field> entry : associations.entrySet()) {
Field tgtField = entry.getValue();
Vector mappings = tgtDesc.getMappings();
// Until IDREF support is added, we want the source type to be that of the target
//schemaTypeString = Constants.SCHEMA_PREFIX + COLON + IDREF;
for (Enumeration mappingsNum = mappings.elements(); mappingsNum.hasMoreElements();) {
Mapping nextMapping = (Mapping)mappingsNum.nextElement();
if (nextMapping.getField() != null && nextMapping.getField() instanceof Field) {
Field xFld = (Field) nextMapping.getField();
if (xFld == tgtField) {
schemaTypeString = getSchemaTypeForElement(tgtField, nextMapping.getAttributeClassification(), workingSchema);
}
}
}
if (schemaTypeString == null) {
schemaTypeString = getSchemaTypeString(Constants.STRING_QNAME, workingSchema);
}
XPathFragment frag = entry.getKey().getXPathFragment();
if (frag.isAttribute()) {
Attribute attr = buildAttribute(frag, schemaTypeString);
ct.getOrderedAttributes().add(attr);
} else {
Element elem = buildElement(frag, schemaTypeString, Occurs.ZERO, null);
if (isCollection) {
elem.setMaxOccurs(Occurs.UNBOUNDED);
}
seq.addElement(elem);
}
}
}
/**
* Process a given mapping.
*
*/
protected void processMapping(CoreMapping mapping, Sequence seq, ComplexType ct, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors) {
if (mapping instanceof BinaryDataMapping) {
processXMLBinaryDataMapping((BinaryDataMapping) mapping, seq, ct, schemaForNamespace, workingSchema, properties);
} else if (mapping instanceof BinaryDataCollectionMapping) {
processXMLBinaryDataCollectionMapping((BinaryDataCollectionMapping) mapping, seq, ct, schemaForNamespace, workingSchema, properties);
} else if (mapping instanceof DirectMapping) {
processXMLDirectMapping((DirectMapping) mapping, seq, ct, schemaForNamespace, workingSchema, properties);
} else if (mapping instanceof DirectCollectionMapping) {
processXMLCompositeDirectCollectionMapping((DirectCollectionMapping) mapping, seq, ct, schemaForNamespace, workingSchema, properties);
} else if (mapping instanceof CompositeCollectionMapping) {
processXMLCompositeMapping((CompositeCollectionMapping) mapping, seq, ct, schemaForNamespace, workingSchema, properties, descriptors, true);
} else if (mapping instanceof CompositeObjectMapping) {
processXMLCompositeMapping((CompositeObjectMapping) mapping, seq, ct, schemaForNamespace, workingSchema, properties, descriptors, false);
} else if (mapping instanceof AnyAttributeMapping) {
AnyAttribute anyAttribute = new AnyAttribute();
anyAttribute.setProcessContents(AnyAttribute.LAX);
ct.setAnyAttribute(anyAttribute);
} else if (mapping instanceof AnyObjectMapping) {
processAnyMapping(seq, false);
} else if (mapping instanceof AnyCollectionMapping) {
processAnyMapping(seq, true);
} else if (mapping instanceof ChoiceObjectMapping) {
processXMLChoiceObjectMapping((ChoiceObjectMapping) mapping, seq, ct, schemaForNamespace, workingSchema, properties, descriptors);
} else if (mapping instanceof ChoiceCollectionMapping) {
processXMLChoiceCollectionMapping((ChoiceCollectionMapping) mapping, seq, ct, schemaForNamespace, workingSchema, properties, descriptors);
} else if (mapping instanceof CollectionReferenceMapping) {
processXMLObjectReferenceMapping((CollectionReferenceMapping) mapping, seq, ct, schemaForNamespace, workingSchema, properties, descriptors, true);
} else if (mapping instanceof ObjectReferenceMapping) {
processXMLObjectReferenceMapping((ObjectReferenceMapping) mapping, seq, ct, schemaForNamespace, workingSchema, properties, descriptors, false);
}
}
/**
* This method will generate a global element if required (based in URI and elementFormDefault) and
* set a reference to it on a given element accordingly. This method will typically be used for
* direct mappings.
*
*/
protected Element handleFragNamespace(XPathFragment frag, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, Element element, String schemaTypeString) {
String fragUri = frag.getNamespaceURI();
// may need to add a global element
Schema s = getSchema(fragUri, null, schemaForNamespace, properties);
String targetNS = workingSchema.getTargetNamespace();
if ((s.isElementFormDefault() && !fragUri.equals(targetNS)) || (!s.isElementFormDefault() && fragUri.length() > 0)) {
if (s.getTopLevelElements().get(frag.getLocalName()) == null) {
Element globalElement = new Element();
globalElement.setName(frag.getLocalName());
globalElement.setType(schemaTypeString);
s.addTopLevelElement(globalElement);
}
element = new Element();
element.setRef(frag.getShortName());
} else {
element = buildElement(frag, schemaTypeString, Occurs.ZERO, null);
}
return element;
}
/**
* This method will generate a global element if required (based in URI and elementFormDefault) and
* set a reference to it on a given element accordingly, or set an anonymous complex type on a given
* element. This method will typically be used by composite mappings.
*
*/
protected Element handleFragNamespace(XPathFragment frag, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, Element element, ComplexType ctype, Descriptor refDesc) {
String fragUri = frag.getNamespaceURI();
// may need to add a global element
Element globalElement = null;
Schema s = getSchema(fragUri, null, schemaForNamespace, properties);
String targetNS = workingSchema.getTargetNamespace();
if ((s.isElementFormDefault() && !fragUri.equals(targetNS)) || (!s.isElementFormDefault() && fragUri.length() > 0)) {
globalElement = (Element) s.getTopLevelElements().get(frag.getLocalName());
if (globalElement == null) {
globalElement = new Element();
globalElement.setName(frag.getLocalName());
if (ctype != null) {
globalElement.setComplexType(ctype);
} else {
globalElement.setType(getSchemaTypeString(refDesc.getSchemaReference().getSchemaContextAsQName(workingSchema.getNamespaceResolver()), workingSchema));
}
s.addTopLevelElement(globalElement);
}
element = new Element();
element.setMaxOccurs(Occurs.UNBOUNDED);
element.setRef(frag.getShortName());
}
if (globalElement == null && ctype != null) {
element.setComplexType(ctype);
}
return element;
}
/**
* Return the last fragment before text() in the XPath that a given XPathFragment
* is part of.
*
*/
protected XPathFragment getTargetXPathFragment(XPathFragment frag) {
if (frag.isAttribute() || frag.isSelfFragment()) {
return frag;
}
while (frag.getNextFragment() != null && !frag.getNextFragment().nameIsText()) {
frag = frag.getNextFragment();
if (frag.getNextFragment() == null || frag.getNextFragment().nameIsText()) {
break;
}
}
return frag;
}
/**
* This method will build element/complexType/sequence components for a given XPath,
* and return the sequence that the target element of the mapping should be added
* to. For example, if the XPath was "contact-info/address/street/text()", street
* would be the target. This method defers processing of the target path element
* to the calling method, allowing for differences in handling, such as direct
* mappings versus composite mappings, etc.
*
*/
protected Sequence buildSchemaComponentsForXPath(XPathFragment frag, Sequence seq, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties) {
// the mapping will handle processing of the target fragment; return the sequence it will be added to
if (frag.getNextFragment() == null || frag.getNextFragment().nameIsText()) {
return seq;
}
Sequence currentSequence = seq;
// if the current element exists, use it; otherwise create a new one
Element currentElement = elementExistsInSequence(frag.getLocalName(), frag.getShortName(), currentSequence);
boolean currentElementExists = (currentElement != null);
if (currentElement == null) {
currentElement = new Element();
// don't set the element name yet, as it may end up being a ref
ComplexType cType = new ComplexType();
Sequence sequence = new Sequence();
cType.setSequence(sequence);
currentElement.setComplexType(cType);
}
Element globalElement = null;
String fragUri = frag.getNamespaceURI();
if (fragUri != null) {
Schema s = getSchema(fragUri, null, schemaForNamespace, properties);
String targetNS = workingSchema.getTargetNamespace();
if ((s.isElementFormDefault() && !fragUri.equals(targetNS)) || (!s.isElementFormDefault() && fragUri.length() > 0)) {
// must generate a global element are create a reference to it
// if the global element exists, use it; otherwise create a new one
globalElement = (Element) s.getTopLevelElements().get(frag.getLocalName());
if (globalElement == null) {
globalElement = new Element();
globalElement.setName(frag.getLocalName());
ComplexType gCType = new ComplexType();
Sequence gSequence = new Sequence();
gCType.setSequence(gSequence);
globalElement.setComplexType(gCType);
s.addTopLevelElement(globalElement);
}
// if the current element doesn't exist set a ref and add it to the sequence
if (!currentElementExists) {
// ref won't have a complex type
currentElement.setComplexType(null);
currentElement.setRef(frag.getShortName());
currentSequence.addElement(currentElement);
currentElementExists = true;
}
// make the global element current
currentElement = globalElement;
}
}
// if we didn't process a global element, and the current element isn't already in the sequence, add it
if (!currentElementExists && globalElement == null) {
currentElement.setName(frag.getLocalName());
currentSequence.addElement(currentElement);
}
// set the correct sequence to use/return
currentSequence = currentElement.getComplexType().getSequence();
// don't process the last element in the path - let the calling mapping process it
frag = frag.getNextFragment();
if (frag.getNextFragment() != null && !frag.getNextFragment().nameIsText()) {
Element childElt = buildElement(frag, null, Occurs.ZERO, null);
currentSequence.addElement(childElt);
}
// call back into this method to process the next path element
return buildSchemaComponentsForXPath(frag, currentSequence, schemaForNamespace, workingSchema, properties);
}
/**
* Build and return an Attribute for a given XMLDirectMapping.
*
*/
protected Attribute buildAttribute(DirectMapping mapping, String schemaType) {
XPathFragment frag = ((Field) mapping.getField()).getXPathFragment();
Attribute attr = new Attribute();
attr.setName(frag.getShortName());
attr.setType(schemaType);
return attr;
}
/**
* Build and return an Attribute for a given XPathFragment.
*
*/
protected Attribute buildAttribute(XPathFragment frag, String schemaType) {
Attribute attr = new Attribute();
attr.setName(frag.getShortName());
attr.setType(schemaType);
return attr;
}
/**
* Build and return an Element for a given XPathFragment.
*
*/
protected Element buildElement(XPathFragment frag, String schemaType, String minOccurs, String maxOccurs) {
Element element = new Element();
element.setName(frag.getLocalName());
element.setMinOccurs(minOccurs);
element.setMaxOccurs(frag.containsIndex() ? Occurs.UNBOUNDED : maxOccurs);
if (schemaType != null) {
element.setType(schemaType);
}
return element;
}
/**
* Build and return an Element based on a given name, minOccurs and
* maxOccurs.
*
*/
protected Element buildElement(String name, String minOccurs, String maxOccurs) {
Element element = new Element();
element.setName(name);
element.setMinOccurs(minOccurs);
element.setMaxOccurs(maxOccurs);
return element;
}
/**
* Indicates if two namespaces are equal. The result is TRUE if the two URI strings are
* equal according to the equals() method, if one is null and the other is "", or
* both are null.
*
*/
public boolean areNamespacesEqual(String ns1, String ns2) {
if (ns1 == null) {
return (ns2 == null || ns2.equals(Constants.EMPTY_STRING));
}
return ((ns1.equals(ns2)) || (ns1.equals(Constants.EMPTY_STRING) && ns2 == null));
}
/**
* Return the schema type as a string for a given QName and Schema. The schema's
* namespace resolver will be used to determine the prefix (if any) to use.
*
*/
protected String getSchemaTypeString(QName schemaType, Schema workingSchema) {
String schemaTypeString = schemaType.getLocalPart();
String uri = schemaType.getNamespaceURI();
String prefix = workingSchema.getNamespaceResolver().resolveNamespaceURI(uri);
if (prefix == null && !areNamespacesEqual(uri, workingSchema.getDefaultNamespace())) {
if (uri.equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
prefix = workingSchema.getNamespaceResolver().generatePrefix(Constants.SCHEMA_PREFIX);
} else if (uri.equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)) {
prefix = workingSchema.getNamespaceResolver().generatePrefix(Constants.SCHEMA_INSTANCE_PREFIX);
} else if (uri.equals(Constants.REF_URL)) {
prefix = workingSchema.getNamespaceResolver().generatePrefix(Constants.REF_PREFIX);
} else {
prefix = workingSchema.getNamespaceResolver().generatePrefix();
}
workingSchema.getNamespaceResolver().put(prefix, uri);
}
if (prefix != null) {
schemaTypeString = prefix + Constants.COLON + schemaTypeString;
}
return schemaTypeString;
}
/**
* Adds each namespace in the given resolver to the schema.
*
*/
protected void addNamespacesToWorkingSchema(NamespaceResolver nr, Schema workingSchema) {
if (nr != null) {
Vector<Namespace> descNamespaces = nr.getNamespaces();
for (Namespace nextNamespace : descNamespaces) {
workingSchema.getNamespaceResolver().put(nextNamespace.getPrefix(), nextNamespace.getNamespaceURI());
}
}
}
/**
* Create and return a new schema for the given namespace. ElementFormDefault and
* AttributeFormDefault can be set via SchemaModelGeneratorProperties object. The
* namespace resolver's default namespace will be set if non-null.
*
*/
protected Schema buildNewSchema(String uri, NamespaceResolver nr, int schemaCount, SchemaModelGeneratorProperties properties) {
Schema schema = new Schema();
schema.setName(SCHEMA_FILE_NAME + schemaCount + SCHEMA_FILE_EXT);
String defaultNamespace = null;
if (nr != null) {
defaultNamespace = nr.getDefaultNamespaceURI();
if (defaultNamespace != null) {
schema.setDefaultNamespace(defaultNamespace);
schema.getNamespaceResolver().setDefaultNamespaceURI(defaultNamespace);
}
}
if (!uri.equals(Constants.EMPTY_STRING)) {
schema.setTargetNamespace(uri);
String prefix = null;
if (nr != null) {
prefix = nr.resolveNamespaceURI(uri);
}
if (prefix == null && !uri.equals(defaultNamespace)) {
prefix = schema.getNamespaceResolver().generatePrefix();
schema.getNamespaceResolver().put(prefix, uri);
}
}
if (properties != null) {
// set elementFormDefault and attributeFormDefault to qualified if necessary
Properties props = properties.getProperties(uri);
if (props != null) {
if (props.containsKey(SchemaModelGeneratorProperties.ELEMENT_FORM_QUALIFIED_KEY)) {
schema.setElementFormDefault((Boolean) props.get(SchemaModelGeneratorProperties.ELEMENT_FORM_QUALIFIED_KEY));
}
if (props.containsKey(SchemaModelGeneratorProperties.ATTRIBUTE_FORM_QUALIFIED_KEY)) {
schema.setAttributeFormDefault((Boolean) props.get(SchemaModelGeneratorProperties.ATTRIBUTE_FORM_QUALIFIED_KEY));
}
}
}
return schema;
}
/**
* Determines if a given schema contains an import for a given schema name.
*
*/
protected boolean importExists(Schema schema, String schemaName) {
java.util.List<Import> imports = schema.getImports();
for (Import nextImport : imports) {
if (nextImport.getSchemaLocation() != null && nextImport.getSchemaLocation().equals(schemaName)) {
return true;
}
}
return false;
}
/**
* Return a QName representation of a qualified table name (aka default root element). The
* given descriptor's namespace resolver will be used to determine the correct prefix - if
* any - to be used.
*
*/
protected QName getDefaultRootElementAsQName(Descriptor desc, String qualifiedTableName) {
QName qName = null;
int idx = qualifiedTableName.indexOf(Constants.COLON);
String localName = qualifiedTableName.substring(idx + 1);
NamespaceResolver nsResolver = desc.getNamespaceResolver();
if (nsResolver == null) {
qName = new QName(localName);
} else if (idx > -1) {
String prefix = qualifiedTableName.substring(0, idx);
String uri = nsResolver.resolveNamespacePrefix(prefix);
qName = new QName(uri, localName);
} else {
if (nsResolver.getDefaultNamespaceURI() != null) {
qName = new QName(nsResolver.getDefaultNamespaceURI(), localName);
} else {
qName = new QName(localName);
}
}
return qName;
}
/**
*
*/
protected Element processReferenceDescriptor(Element element, Descriptor refDesc, HashMap<String, Schema> schemaForNamespace, Schema workingSchema, SchemaModelGeneratorProperties properties, List<Descriptor> descriptors, Field field, boolean isCollection) {
ComplexType ctype = null;
if (refDesc.getSchemaReference() == null) {
ctype = buildComplexType(true, refDesc, schemaForNamespace, workingSchema, properties, descriptors);
} else {
element.setType(getSchemaTypeString(refDesc.getSchemaReference().getSchemaContextAsQName(workingSchema.getNamespaceResolver()), workingSchema));
}
XPathFragment frag = field.getXPathFragment();
String fragUri = frag.getNamespaceURI();
if (fragUri != null) {
// may need to add a global element
Schema s = getSchema(fragUri, null, schemaForNamespace, properties);
String targetNS = workingSchema.getTargetNamespace();
if ((s.isElementFormDefault() && !fragUri.equals(targetNS)) || (!s.isElementFormDefault() && fragUri.length() > 0)) {
if (s.getTopLevelElements().get(frag.getShortName()) == null) {
Element globalElement = new Element();
globalElement.setName(frag.getLocalName());
if (ctype != null) {
globalElement.setComplexType(ctype);
} else {
globalElement.setType(getSchemaTypeString(refDesc.getSchemaReference().getSchemaContextAsQName(workingSchema.getNamespaceResolver()), workingSchema));
}
s.getTopLevelElements().put(frag.getShortName(), globalElement);
}
element = new Element();
element.setMinOccurs(Occurs.ZERO);
if (isCollection) {
element.setMaxOccurs(Occurs.UNBOUNDED);
}
element.setRef(frag.getShortName());
} else {
element.setComplexType(ctype);
}
} else if (ctype != null) {
element.setComplexType(ctype);
}
return element;
}
/**
* Convenience method for determining if an element already exists in a given
* sequence. If an element exists whose name is equal to 'elementName' true
* is returned. False otherwise.
*
*/
protected Element elementExistsInSequence(String elementName, String refString, Sequence seq) {
if (seq.isEmpty()) {
return null;
}
List existingElements = seq.getOrderedElements();
if (existingElements != null) {
Iterator elementIt = existingElements.iterator();
while (elementIt.hasNext()) {
Element element;
// could be other components in the list, like Any, but we only care about Element
try {
element = (Element) elementIt.next();
} catch (ClassCastException cce) {
continue;
}
if ((element.getRef() != null && element.getRef().equals(refString)) || (element.getName() != null && element.getName().equals(elementName))) {
return element;
}
}
}
return null;
}
/**
* Determines if a given descriptor contains a direct mapping to "text()" indicating a
* simple mapping. In this case, a simple type or complex type with simple content
* will be generated
*
*/
protected boolean isSimple(Descriptor desc) {
boolean isSimple = false;
for (CoreMapping mapping : (Vector<CoreMapping>)desc.getMappings()) {
if (mapping.isDirectToFieldMapping()) {
Field xFld = (Field) mapping.getField();
if (xFld.getXPath().equals(TEXT)) {
isSimple = true;
break;
}
} else {
break;
}
}
return isSimple;
}
/**
* Indicates if a complex type with simple content is to be generated. This is true when
* the given descriptor has more than one mapping. It is assumed that isSimple(desc)
* will return true for the given descriptor, and that the descriptor will contain at most
* one direct with a 'text()' xpath, and any additional mappings are attribute mappings.
*
*/
protected boolean isComplexTypeWithSimpleContentRequired(Descriptor desc) {
return desc.getMappings().size() > 1 ? true : false;
}
/**
* Process information contained within an EnumTypeConverter. This will generate a simple
* type containing enumeration info.
*
*/
protected void processEnumeration(String schemaTypeString, XPathFragment frag, DirectMapping mapping, Sequence seq, ComplexType ct, Schema workingSchema, CoreConverter converter) {
Element elem = null;
Attribute attr = null;
if (frag.isAttribute()) {
attr = buildAttribute(mapping, schemaTypeString);
} else {
elem = buildElement(frag, schemaTypeString, Occurs.ZERO, null);
}
Collection<String> fieldValues = ((EnumTypeConverter) converter).getAttributeToFieldValues().values();
ArrayList facets = new ArrayList();
for (String field : fieldValues) {
facets.add(field);
}
Restriction restriction = new Restriction();
restriction.setEnumerationFacets(facets);
SimpleType st = new SimpleType();
st.setRestriction(restriction);
if (frag.isAttribute()) {
attr.setSimpleType(st);
ct.getOrderedAttributes().add(attr);
} else {
elem.setSimpleType(st);
seq.addElement(elem);
}
}
/**
* Indicates if a given fragment is a primary key.
*
*/
protected boolean isFragPrimaryKey(XPathFragment frag, DirectMapping mapping) {
/* Uncomment the following when ID support is needed
Vector<String> pkFieldNames = mapping.getDescriptor().getPrimaryKeyFieldNames();
if (pkFieldNames != null) {
if (frag.isAttribute()) {
return pkFieldNames.contains(XMLConstants.ATTRIBUTE + frag.getLocalName());
}
return pkFieldNames.contains(frag.getLocalName() + '/' + TEXT);
}
*/
return false;
}
}