| /* |
| * Copyright (c) 1998, 2019 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. |
| * |
| * @param desc |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| */ |
| 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. |
| * |
| * @param uri |
| * @param nr |
| * @param schemaForNamespace |
| * @param properties |
| * @return |
| * @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. |
| * |
| * @param desc |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| * @param simple |
| * @return |
| */ |
| 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. |
| * |
| * @param desc |
| * @param workingSchema |
| * @return |
| */ |
| 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. |
| * |
| * @param name |
| * @return |
| */ |
| 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. |
| * |
| * @param anonymous |
| * @param desc |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| * @return |
| */ |
| 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. |
| * |
| * @param desc |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| * @return |
| */ |
| 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. |
| * |
| * @param mapping |
| * @param workingSchema |
| * @return |
| */ |
| 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. |
| * |
| * @param xmlField |
| * @param attrClass |
| * @param workingSchema |
| * @return |
| */ |
| 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. |
| * |
| * @param javaClassName |
| * @param descriptors |
| * @return |
| */ |
| 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. |
| * |
| * @param javaClass |
| * @param descriptors |
| * @return |
| */ |
| 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. |
| * |
| * @param seq |
| * @param isCollection |
| * @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. |
| * |
| * @param mapping |
| * @param seq |
| * @param ct |
| * @param workingSchema |
| */ |
| 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. |
| * |
| * @param mapping |
| * @param seq |
| * @param ct |
| * @param workingSchema |
| */ |
| 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. |
| * |
| * @param mapping |
| * @param seq |
| * @param ct |
| * @param workingSchema |
| */ |
| 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. |
| * |
| * @param mapping |
| * @param seq |
| * @param ct |
| * @param workingSchema |
| */ |
| 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. |
| * |
| * @param mapping |
| * @param seq |
| * @param ct |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| * @param collection |
| */ |
| 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. |
| * |
| * @param mapping |
| * @param seq |
| * @param ct |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| */ |
| 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); |
| } |
| |
| /** |
| * |
| * @param mapping |
| * @param seq |
| * @param ct |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| */ |
| 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. |
| * |
| * @param fieldToClassMap |
| * @param choiceFieldToClassList |
| * @param seq |
| * @param ct |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| * @param isCollection |
| */ |
| 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 |
| * |
| * @param mapping |
| * @param seq |
| * @param ct |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| * @param isCollection |
| */ |
| 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. |
| * |
| * @param mapping |
| * @param seq |
| * @param ct |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| */ |
| 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. |
| * |
| * @param frag |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param element |
| * @param schemaTypeString |
| * @return |
| */ |
| 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. |
| * |
| * @param frag |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param element |
| * @param ctype |
| * @param refDesc |
| */ |
| 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. |
| * |
| * @param frag |
| * @return |
| */ |
| 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. |
| * |
| * @param frag |
| * @param seq |
| * @return |
| */ |
| 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. |
| * |
| * @param mapping |
| * @param schemaType |
| * @return |
| */ |
| 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. |
| * |
| * @param frag |
| * @param schemaType |
| * @return |
| */ |
| 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. |
| * |
| * @param frag |
| * @param schemaType |
| * @param minOccurs |
| * @param maxOccurs |
| * @return |
| */ |
| 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. |
| * |
| * @param name |
| * @param minOccurs |
| * @param maxOccurs |
| * @return |
| */ |
| 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. |
| * |
| * @param ns1 |
| * @param ns2 |
| * @return |
| */ |
| 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. |
| * |
| * @param schemaType |
| * @param workingSchema |
| * @return |
| */ |
| 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. |
| * |
| * @param nr |
| * @param workingSchema |
| */ |
| 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. |
| * |
| * @param uri |
| * @param nr |
| * @param schemaCount |
| * @param properties |
| * @return |
| */ |
| 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. |
| * |
| * @param schema |
| * @param schemaName |
| * @return |
| */ |
| 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. |
| * |
| * @param desc |
| * @param qualifiedTableName |
| * @return |
| */ |
| 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; |
| } |
| |
| /** |
| * |
| * @param element |
| * @param refDesc |
| * @param schemaForNamespace |
| * @param workingSchema |
| * @param properties |
| * @param descriptors |
| * @param field |
| * @param isCollection |
| * @return |
| */ |
| 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. |
| * |
| * @param elementName |
| * @param seq |
| * @return |
| */ |
| 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 |
| * |
| * @param desc |
| * @return |
| */ |
| 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. |
| * |
| * @param desc |
| * @return |
| */ |
| 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. |
| * |
| * @param schemaTypeString |
| * @param frag |
| * @param mapping |
| * @param seq |
| * @param ct |
| * @param workingSchema |
| * @param converter |
| */ |
| 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. |
| * |
| * @param frag |
| * @param mapping |
| * @return |
| */ |
| 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; |
| } |
| |
| } |