| /* |
| * 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: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.jaxb.compiler; |
| |
| import java.awt.Image; |
| import java.beans.Introspector; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import jakarta.xml.bind.SchemaOutputResolver; |
| import jakarta.xml.bind.annotation.XmlElementDecl.GLOBAL; |
| import jakarta.xml.bind.annotation.XmlSchemaType; |
| import javax.xml.namespace.QName; |
| import javax.xml.transform.Result; |
| import javax.xml.transform.Source; |
| |
| import org.eclipse.persistence.exceptions.BeanValidationException; |
| import org.eclipse.persistence.internal.core.helper.CoreClassConstants; |
| import org.eclipse.persistence.internal.jaxb.many.MapValue; |
| import org.eclipse.persistence.internal.oxm.Constants; |
| import org.eclipse.persistence.internal.oxm.Namespace; |
| import org.eclipse.persistence.internal.oxm.XPathFragment; |
| import org.eclipse.persistence.internal.oxm.mappings.Field; |
| import org.eclipse.persistence.internal.oxm.schema.model.All; |
| 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.SimpleComponent; |
| import org.eclipse.persistence.internal.oxm.schema.model.SimpleContent; |
| import org.eclipse.persistence.internal.oxm.schema.model.SimpleType; |
| import org.eclipse.persistence.internal.oxm.schema.model.TypeDefParticle; |
| import org.eclipse.persistence.internal.oxm.schema.model.TypeDefParticleOwner; |
| import org.eclipse.persistence.jaxb.compiler.builder.TransformerPropertyBuilder; |
| import org.eclipse.persistence.jaxb.compiler.facets.DecimalMaxFacet; |
| import org.eclipse.persistence.jaxb.compiler.facets.DecimalMinFacet; |
| import org.eclipse.persistence.jaxb.compiler.facets.DigitsFacet; |
| import org.eclipse.persistence.jaxb.compiler.facets.Facet; |
| import org.eclipse.persistence.jaxb.compiler.facets.FacetVisitor; |
| import org.eclipse.persistence.jaxb.compiler.facets.MaxFacet; |
| import org.eclipse.persistence.jaxb.compiler.facets.MinFacet; |
| import org.eclipse.persistence.jaxb.compiler.facets.PatternFacet; |
| import org.eclipse.persistence.jaxb.compiler.facets.PatternListFacet; |
| import org.eclipse.persistence.jaxb.compiler.facets.SizeFacet; |
| import org.eclipse.persistence.jaxb.javamodel.Helper; |
| import org.eclipse.persistence.jaxb.javamodel.JavaClass; |
| import org.eclipse.persistence.jaxb.javamodel.JavaClassCompareByNamespace; |
| import org.eclipse.persistence.jaxb.xmlmodel.XmlElementWrapper; |
| import org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes.XmlJoinNode; |
| import org.eclipse.persistence.jaxb.xmlmodel.XmlVirtualAccessMethodsSchema; |
| import org.eclipse.persistence.oxm.NamespaceResolver; |
| import org.eclipse.persistence.oxm.XMLField; |
| |
| /** |
| * INTERNAL: |
| * <p><b>Purpose:</b>To generate Schema objects based on a map of TypeInfo objects, and some |
| * additional information gathered by the AnnotationsProcessing phase. |
| * <p><b>Responsibilities:</b><ul> |
| * <li>Create and maintain a collection of Schema objects based on the provided TypeInfo objects</li> |
| * <li>Add additional global elements to the schema based on an optional map (for WS integration)</li> |
| * <li>Should create a schema for each namespace encountered during generation.</li> |
| * </ul> |
| * <p>This class is used by the Generator to handle the generation of Schemas. The |
| * Generator passes in a map of TypeInfo objects, generated by the Annotations processor. |
| * The generated Schemas are stored in a map of keyed on Target Namespace. |
| * @see org.eclipse.persistence.jaxb.compiler.TypeInfo |
| * @see org.eclipse.persistence.jaxb.compiler.AnnotationsProcessor |
| * @see org.eclipse.persistence.jaxb.compiler.Generator |
| * @since Oracle TopLink 11.1.1.0.0 |
| * @author mmacivor |
| */ |
| public class SchemaGenerator { |
| private Map<String, Schema> schemaForNamespace; |
| private List<Schema> allSchemas; |
| private int schemaCount; |
| private Helper helper; |
| private Map<String, TypeInfo> typeInfo; |
| private Map<String, PackageInfo> packageToPackageInfoMappings; |
| private Map<String, SchemaTypeInfo> schemaTypeInfo; |
| private Map<String, QName> userDefinedSchemaTypes; |
| private Map<String, Class> arrayClassesToGeneratedClasses; |
| |
| private static final String JAVAX_ACTIVATION_DATAHANDLER = "jakarta.activation.DataHandler"; |
| private static final String JAVAX_MAIL_INTERNET_MIMEMULTIPART = "jakarta.mail.internet.MimeMultipart"; |
| private static final String SWA_REF_IMPORT = "http://ws-i.org/profiles/basic/1.1/swaref.xsd"; |
| |
| private static final String COLON = ":"; |
| private static final String ATT = "@"; |
| private static final String EMPTY_STRING = ""; |
| private static final String DOT = "."; |
| private static final String SKIP = "skip"; |
| private static final String ENTRY = "entry"; |
| private static final String GENERATE = "##generate"; |
| private static final String SCHEMA = "schema"; |
| private static final String SCHEMA_EXT = ".xsd"; |
| private static final String OBJECT_CLASSNAME = "java.lang.Object"; |
| private static final String ID = "ID"; |
| private static final String IDREF = "IDREF"; |
| private static final Character DOT_CHAR = '.'; |
| private static final Character SLASH = '/'; |
| private static final Character SLASHES = '\\'; |
| |
| private SchemaOutputResolver outputResolver; |
| private boolean facets; |
| |
| public SchemaGenerator(Helper helper) { |
| this.helper = helper; |
| this.facets = helper.isFacets(); |
| } |
| |
| public void generateSchema(List<JavaClass> typeInfoClasses, Map<String, TypeInfo> typeInfo, Map<String, QName> userDefinedSchemaTypes, Map<String, PackageInfo> packageToPackageInfoMappings, Map<QName, ElementDeclaration> additionalGlobalElements, Map<String, Class> arrayClassesToGeneratedClasses, SchemaOutputResolver outputResolver) { |
| this.outputResolver = outputResolver; |
| generateSchema(typeInfoClasses, typeInfo, userDefinedSchemaTypes, packageToPackageInfoMappings, additionalGlobalElements, arrayClassesToGeneratedClasses); |
| } |
| |
| public void generateSchema(List<JavaClass> typeInfoClasses, Map<String, TypeInfo> typeInfo, Map<String, QName> userDefinedSchemaTypes, Map<String, PackageInfo> packageToPackageInfoMappings, Map<QName, ElementDeclaration> additionalGlobalElements, Map<String, Class> arrayClassesToGeneratedClasses) { |
| this.typeInfo = typeInfo; |
| this.userDefinedSchemaTypes = userDefinedSchemaTypes; |
| this.packageToPackageInfoMappings = packageToPackageInfoMappings; |
| this.schemaCount = 1; |
| this.schemaTypeInfo = new HashMap<String, SchemaTypeInfo>(typeInfo.size()); |
| this.arrayClassesToGeneratedClasses = arrayClassesToGeneratedClasses; |
| |
| //sort input classes before schema name (like schema1.xsd, schema2.xsd....) is generated and assigned |
| typeInfoClasses.sort(new JavaClassCompareByNamespace(typeInfo)); |
| for (JavaClass javaClass : typeInfoClasses) { |
| addSchemaComponents(javaClass); |
| } |
| populateSchemaTypes(); |
| if (additionalGlobalElements != null) { |
| addGlobalElements(additionalGlobalElements); |
| } |
| } |
| |
| public void addSchemaComponents(JavaClass myClass) { |
| // first check for type |
| String myClassName = myClass.getQualifiedName(); |
| Element rootElement = null; |
| TypeInfo info = typeInfo.get(myClassName); |
| if (info.isTransient() || info.getClassNamespace().equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)) { |
| return; |
| } |
| SchemaTypeInfo schemaTypeInfo = new SchemaTypeInfo(); |
| schemaTypeInfo.setSchemaTypeName(new QName(info.getClassNamespace(), info.getSchemaTypeName())); |
| this.schemaTypeInfo.put(myClass.getQualifiedName(), schemaTypeInfo); |
| NamespaceInfo namespaceInfo = this.packageToPackageInfoMappings.get(myClass.getPackageName()).getNamespaceInfo(); |
| if (namespaceInfo.getLocation() != null && !namespaceInfo.getLocation().equals(GENERATE)) { |
| return; |
| } |
| Schema schema = getSchemaForNamespace(info.getClassNamespace(), myClass.getPackageName()); |
| info.setSchema(schema); |
| |
| String typeName = info.getSchemaTypeName(); |
| String pfx = EMPTY_STRING; |
| |
| Property valueField = null; |
| if (info.isSetXmlRootElement()) { |
| //Create the root element and add it to the schema |
| org.eclipse.persistence.jaxb.xmlmodel.XmlRootElement xmlRE = info.getXmlRootElement(); |
| rootElement = new Element(); |
| String elementName = xmlRE.getName(); |
| if (elementName.equals(XMLProcessor.DEFAULT) || elementName.equals(EMPTY_STRING)) { |
| try{ |
| elementName = info.getXmlNameTransformer().transformRootElementName(myClassName); |
| }catch (Exception ex){ |
| throw org.eclipse.persistence.exceptions.JAXBException.exceptionDuringNameTransformation(myClassName, info.getXmlNameTransformer().getClass().getName(), ex); |
| } |
| } |
| rootElement.setName(elementName); |
| String rootNamespace = xmlRE.getNamespace(); |
| if (rootNamespace.equals(XMLProcessor.DEFAULT)) { |
| Schema rootElementSchema = getSchemaForNamespace(namespaceInfo.getNamespace()); |
| if (rootElementSchema != null) { |
| rootElementSchema.addTopLevelElement(rootElement); |
| } |
| schemaTypeInfo.getGlobalElementDeclarations().add(new QName(namespaceInfo.getNamespace(), rootNamespace)); |
| rootNamespace = namespaceInfo.getNamespace(); |
| } else { |
| Schema rootElementSchema = getSchemaForNamespace(rootNamespace); |
| if (rootElementSchema != null) { |
| rootElementSchema.addTopLevelElement(rootElement); |
| } |
| schemaTypeInfo.getGlobalElementDeclarations().add(new QName(rootNamespace, elementName)); |
| } |
| |
| // handle root-level imports/includes [schema = the type's schema] |
| Schema rootSchema = getSchemaForNamespace(rootNamespace); |
| addImportIfRequired(rootSchema, schema, schema.getTargetNamespace()); |
| |
| // setup a prefix, if necessary |
| if (rootSchema != null && !info.getClassNamespace().equals(EMPTY_STRING)) { |
| pfx = getOrGeneratePrefixForNamespace(info.getClassNamespace(), rootSchema); |
| pfx += COLON; |
| } |
| } |
| |
| |
| |
| if (CompilerHelper.isSimpleType(info)){ |
| |
| SimpleType type = new SimpleType(); |
| //simple type case, we just need the name and namespace info |
| if (typeName.equals(EMPTY_STRING)) { |
| //In this case, it should be a type under |
| //A root elem or locally defined whenever used |
| if (rootElement != null) { |
| rootElement.setSimpleType(type); |
| } |
| } else { |
| type.setName(typeName); |
| schema.addTopLevelSimpleTypes(type); |
| if (rootElement != null) { |
| rootElement.setType(pfx + type.getName()); |
| } |
| } |
| //Figure out schema type and set it as Restriction |
| QName restrictionType = null; |
| Restriction restriction = new Restriction(); |
| if (info.isEnumerationType()) { |
| restrictionType = ((EnumTypeInfo) info).getRestrictionBase(); |
| restriction.setEnumerationFacets(this.getEnumerationFacetsFor((EnumTypeInfo) info)); |
| |
| String prefix = null; |
| if (restrictionType.getNamespaceURI() != null && !EMPTY_STRING.equals(restrictionType.getNamespaceURI())) { |
| if (javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(restrictionType.getNamespaceURI())) { |
| prefix = Constants.SCHEMA_PREFIX; |
| } else { |
| prefix = getPrefixForNamespace(schema, restrictionType.getNamespaceURI()); |
| } |
| } |
| String extensionTypeName = restrictionType.getLocalPart(); |
| if (prefix != null) { |
| extensionTypeName = prefix + COLON + extensionTypeName; |
| } |
| restriction.setBaseType(extensionTypeName); |
| |
| type.setRestriction(restriction); |
| } else { |
| valueField= info.getXmlValueProperty(); |
| JavaClass javaType = valueField.getActualType(); |
| QName baseType = getSchemaTypeFor(javaType); |
| String prefix = null; |
| if (baseType.getNamespaceURI() != null && !baseType.getNamespaceURI().equals(EMPTY_STRING)) { |
| if (baseType.getNamespaceURI().equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)) { |
| prefix = Constants.SCHEMA_PREFIX; |
| } else { |
| prefix = getPrefixForNamespace(schema, baseType.getNamespaceURI()); |
| } |
| } |
| String baseTypeName = baseType.getLocalPart(); |
| if (prefix != null) { |
| baseTypeName = prefix + COLON + baseTypeName; |
| } |
| if (valueField.isXmlList() || (valueField.getGenericType() != null)) { |
| //generate a list instead of a restriction |
| org.eclipse.persistence.internal.oxm.schema.model.List list = new org.eclipse.persistence.internal.oxm.schema.model.List(); |
| list.setItemType(baseTypeName); |
| type.setList(list); |
| } else { |
| if (helper.isAnnotationPresent(valueField.getElement(), XmlSchemaType.class)) { |
| XmlSchemaType schemaType = (XmlSchemaType) helper.getAnnotation(valueField.getElement(), XmlSchemaType.class); |
| baseType = new QName(schemaType.namespace(), schemaType.name()); // TODO: This assignment seems like a bug, probably this should be "baseTypeName" ? |
| } |
| restriction.setBaseType(baseTypeName); |
| type.setRestriction(restriction); |
| } |
| } |
| info.setSimpleType(type); |
| } else if ((valueField = this.getXmlValueFieldForSimpleContent(info)) != null) { |
| ComplexType type = new ComplexType(); |
| SimpleContent content = new SimpleContent(); |
| if (EMPTY_STRING.equals(typeName)) { |
| if (rootElement != null) { |
| rootElement.setComplexType(type); |
| } |
| info.setComplexType(type); |
| } else { |
| type.setName(typeName); |
| schema.addTopLevelComplexTypes(type); |
| if (rootElement != null) { |
| rootElement.setType(pfx + type.getName()); |
| } |
| } |
| QName extensionType = getSchemaTypeFor(valueField.getType()); |
| if (helper.isAnnotationPresent(valueField.getElement(), XmlSchemaType.class)) { |
| XmlSchemaType schemaType = (XmlSchemaType) helper.getAnnotation(valueField.getElement(), XmlSchemaType.class); |
| extensionType = new QName(schemaType.namespace(), schemaType.name()); |
| } |
| String prefix = null; |
| if (extensionType.getNamespaceURI() != null && !extensionType.getNamespaceURI().equals(EMPTY_STRING)) { |
| if (extensionType.getNamespaceURI().equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)) { |
| prefix = Constants.SCHEMA_PREFIX; |
| } else { |
| prefix = getPrefixForNamespace(schema, extensionType.getNamespaceURI()); |
| } |
| } |
| String extensionTypeName = extensionType.getLocalPart(); |
| if (prefix != null) { |
| extensionTypeName = prefix + COLON + extensionTypeName; |
| } |
| Extension extension = new Extension(); |
| extension.setBaseType(extensionTypeName); |
| content.setExtension(extension); |
| type.setSimpleContent(content); |
| info.setComplexType(type); |
| } else { |
| ComplexType type = createComplexTypeForClass(myClass, info); |
| TypeDefParticle compositor = null; |
| if(type.getComplexContent() != null && type.getComplexContent().getExtension() != null) { |
| compositor = type.getComplexContent().getExtension().getTypeDefParticle(); |
| } else { |
| compositor = type.getTypeDefParticle(); |
| } |
| if (EMPTY_STRING.equals(typeName)) { |
| if (rootElement != null) { |
| rootElement.setComplexType(type); |
| } |
| info.setComplexType(type); |
| info.setCompositor(compositor); |
| } else { |
| type.setName(typeName); |
| if (rootElement != null) { |
| rootElement.setType(pfx + type.getName()); |
| } |
| schema.addTopLevelComplexTypes(type); |
| info.setComplexType(type); |
| info.setCompositor(compositor); |
| } |
| } |
| } |
| |
| private ComplexType createComplexTypeForClass(JavaClass myClass, TypeInfo info) { |
| |
| Schema schema = getSchemaForNamespace(info.getClassNamespace()); |
| |
| ComplexType type = new ComplexType(); |
| JavaClass superClass = CompilerHelper.getNextMappedSuperClass(myClass, this.typeInfo, this.helper); |
| // Handle abstract class |
| if (myClass.isAbstract()) { |
| type.setAbstractValue(true); |
| } |
| |
| Extension extension = null; |
| if (superClass != null) { |
| TypeInfo parentTypeInfo = this.typeInfo.get(superClass.getQualifiedName()); |
| if (parentTypeInfo != null) { |
| extension = new Extension(); |
| // may need to qualify the type |
| String parentPrefix = getPrefixForNamespace(schema, parentTypeInfo.getClassNamespace()); |
| if (parentPrefix != null) { |
| extension.setBaseType(parentPrefix + COLON + parentTypeInfo.getSchemaTypeName()); |
| } else { |
| extension.setBaseType(parentTypeInfo.getSchemaTypeName()); |
| } |
| |
| if(parentTypeInfo.getXmlValueProperty() != null){ |
| SimpleContent content = new SimpleContent(); |
| content.setExtension(extension); |
| type.setSimpleContent(content); |
| return type; |
| }else{ |
| ComplexContent content = new ComplexContent(); |
| content.setExtension(extension); |
| type.setComplexContent(content); |
| } |
| |
| } |
| } |
| |
| TypeDefParticle compositor = null; |
| String[] propOrder = null; |
| if (info.isSetPropOrder()) { |
| propOrder = info.getPropOrder(); |
| } |
| |
| if (propOrder != null && propOrder.length == 0) { |
| // Note that the spec requires an 'all' to be generated |
| // in cases where propOrder == 0, however, the TCK |
| // requires the extension case to use sequences |
| if (info.hasElementRefs()) { |
| // generate a sequence to satisfy TCK |
| compositor = new Sequence(); |
| if (extension != null) { |
| extension.setSequence((Sequence) compositor); |
| } else { |
| type.setSequence((Sequence) compositor); |
| } |
| } else if (extension != null) { |
| compositor = new All(); |
| extension.setAll((All) compositor); |
| } else { |
| compositor = new All(); |
| type.setAll((All) compositor); |
| } |
| } else { |
| // generate a sequence to satisfy TCK |
| compositor = new Sequence(); |
| if (extension != null) { |
| extension.setSequence((Sequence) compositor); |
| } else { |
| type.setSequence((Sequence) compositor); |
| } |
| } |
| return type; |
| } |
| public void addToSchemaType(TypeInfo ownerTypeInfo, java.util.List<Property> properties, TypeDefParticle compositor, ComplexType type, Schema workingSchema) { |
| //If there are no properties we don't want a sequence/choice or all tag written out |
| if (properties.size() == 0) { |
| type.setAll(null); |
| type.setSequence(null); |
| type.setChoice(null); |
| ownerTypeInfo.setCompositor(null); |
| return; |
| } |
| |
| boolean extAnyAdded = false; |
| |
| // generate schema components for each property |
| for (Property next : properties) { |
| if (next == null) { continue; } |
| Schema currentSchema = workingSchema; |
| TypeDefParticle parentCompositor = compositor; |
| boolean isChoice = (parentCompositor instanceof Choice); |
| ComplexType parentType = type; |
| // ignore transient and inverse reference/non-writeable properties |
| if(!next.isTransient() && !(next.isInverseReference() && !next.isWriteableInverseReference())){ |
| // handle xml extensions |
| if (next.isVirtual()) { |
| boolean extSchemaAny = false; |
| if (ownerTypeInfo.getXmlVirtualAccessMethods().getSchema() != null) { |
| extSchemaAny = ownerTypeInfo.getXmlVirtualAccessMethods().getSchema().equals(XmlVirtualAccessMethodsSchema.ANY); |
| } |
| if (extSchemaAny && !next.isAttribute()) { |
| if (!extAnyAdded) { |
| addAnyToSchema(next, compositor, true, Constants.ANY_NAMESPACE_ANY); |
| extAnyAdded = true; |
| continue; |
| } else { |
| // any already added |
| continue; |
| } |
| } else { |
| // proceed, adding the schema element as usual |
| } |
| } |
| // handle transformers |
| if (next.isSetXmlTransformation() && next.getXmlTransformation().isSetXmlWriteTransformers()) { |
| addTransformerToSchema(next, ownerTypeInfo, compositor, type, workingSchema); |
| continue; |
| } |
| // handle XmlJoinNodes |
| if (next.isSetXmlJoinNodes()) { |
| addXmlJoinNodesToSchema(next, parentCompositor, currentSchema, parentType); |
| continue; |
| } |
| // deal with xml-path case |
| if (next.getXmlPath() != null) { |
| // create schema components based on the XmlPath |
| AddToSchemaResult xpr = addXPathToSchema(next, parentCompositor, currentSchema, isChoice, type); |
| // if the returned object or schema component is null there is nothing to do |
| if (xpr == null || ((parentCompositor = xpr.particle) == null && xpr.simpleContentType == null)) { |
| continue; |
| } |
| // now process the property as per usual, adding to the created schema component |
| if(xpr.schema == null) { |
| //if there's no schema, this may be a ref to an attribute in an ungenerated schema |
| //no need to generate the attribute in the target schema |
| continue; |
| } |
| currentSchema = xpr.schema; |
| if(parentCompositor == null) { |
| parentType = xpr.simpleContentType; |
| } else if (parentCompositor.getOwner() instanceof ComplexType) { |
| parentType = ((ComplexType)parentCompositor.getOwner()); |
| } |
| // deal with the XmlElementWrapper case |
| } else if (!isChoice && !next.isMap() && next.isSetXmlElementWrapper()) { |
| AddToSchemaResult asr = addXmlElementWrapperToSchema(next, currentSchema, compositor); |
| // if returned object is null there is nothing to do |
| if (asr == null) { |
| continue; |
| } |
| // the returned object contains ComplexType and TypeDefParticles to use |
| parentType = asr.type; |
| parentCompositor = asr.particle; |
| } |
| // handle mixed content |
| if (next.isMixedContent()) { |
| parentType.setMixed(true); |
| } |
| // handle attribute |
| if (next.isAttribute() && !next.isAnyAttribute()) { |
| addAttributeToSchema(buildAttribute(next, currentSchema), next.getSchemaName(), currentSchema, parentType); |
| // handle any attribute |
| } else if (next.isAnyAttribute()) { |
| addAnyAttributeToSchema(parentType); |
| // handle choice |
| } else if (next.isChoice()) { |
| addChoiceToSchema(next, ownerTypeInfo, parentType, parentCompositor, currentSchema); |
| // handle reference |
| } else if (next.isReference()) { |
| addReferenceToSchema(next, currentSchema, parentCompositor); |
| // handle any |
| } else if (next.isAny() || next.getVariableAttributeName() !=null) { |
| addAnyToSchema(next, parentCompositor); |
| // add an element |
| } else if (!(ownerTypeInfo.getXmlValueProperty() != null && ownerTypeInfo.getXmlValueProperty() == next)) { |
| Element element = buildElement(next, parentCompositor instanceof All, currentSchema, ownerTypeInfo); |
| addElementToSchema(element, next.getSchemaName().getNamespaceURI(), next.isPositional(), parentCompositor, currentSchema, ownerTypeInfo.getJavaClass().getPackageName()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Return the schema type (as QName) based on a given JavaClass. |
| * |
| * @param javaClass |
| * @return |
| */ |
| public QName getSchemaTypeFor(JavaClass javaClass) { |
| String className; |
| if (javaClass.isArray()) { |
| Class wrapperClass = arrayClassesToGeneratedClasses.get(javaClass.getName()); |
| if (null == wrapperClass) { |
| className = javaClass.getQualifiedName(); |
| } else { |
| className = wrapperClass.getName(); |
| } |
| } else { |
| className = javaClass.getQualifiedName(); |
| } |
| // check user defined types first |
| QName schemaType = userDefinedSchemaTypes.get(className); |
| if (schemaType == null) { |
| schemaType = (QName) helper.getXMLToJavaTypeMap().get(javaClass.getRawName()); |
| } |
| if (schemaType == null) { |
| TypeInfo targetInfo = this.typeInfo.get(className); |
| if (targetInfo != null) { |
| schemaType = new QName(targetInfo.getClassNamespace(), targetInfo.getSchemaTypeName()); |
| } |
| } |
| if (schemaType == null) { |
| if (javaClass.getQualifiedName().equals(OBJECT_CLASSNAME)) { |
| return Constants.ANY_TYPE_QNAME; |
| } |
| return Constants.ANY_SIMPLE_TYPE_QNAME; |
| } |
| return schemaType; |
| } |
| |
| public void populateSchemaTypes() { |
| for (TypeInfo info : typeInfo.values()) { |
| if (info.isComplexType()) { |
| if (info.getSchema() != null) { |
| List<Property> props = info.getNonTransientPropertiesInPropOrder(); |
| // handle class indicator field name |
| if (info.isSetXmlDiscriminatorNode()) { |
| String xpath = info.getXmlDiscriminatorNode(); |
| String pname = XMLProcessor.getNameFromXPath(xpath, EMPTY_STRING, true); |
| if (!pname.equals(EMPTY_STRING)) { |
| // since there is no property for the indicator field name, we'll need to make one |
| Property prop = new Property(helper); |
| prop.setPropertyName(pname); |
| prop.setXmlPath(xpath); |
| prop.setSchemaName(new QName(pname)); |
| prop.setType(helper.getJavaClass(String.class)); |
| prop.setIsAttribute(true); |
| props.add(prop); |
| } |
| } |
| addToSchemaType(info, props, info.getCompositor(), info.getComplexType(), info.getSchema()); |
| if (info.hasPredicateProperties()) { |
| addToSchemaType(info, info.getPredicateProperties(), info.getCompositor(), info.getComplexType(), info.getSchema()); |
| } |
| } |
| } |
| } |
| } |
| |
| public String getSchemaTypeNameForClassName(String className) { |
| return Introspector.decapitalize(className.substring(className.lastIndexOf(DOT_CHAR) + 1)); |
| } |
| |
| public ArrayList<Object> getEnumerationFacetsFor(EnumTypeInfo info) { |
| return (ArrayList<Object>) info.getXmlEnumValues(); |
| } |
| |
| public Property getXmlValueFieldForSimpleContent(TypeInfo info) { |
| ArrayList<Property> properties = info.getPropertyList(); |
| Property xmlValueProperty = info.getXmlValueProperty(); |
| boolean foundValue = false; |
| boolean foundNonAttribute = false; |
| Property valueField = null; |
| |
| for (Property prop : properties) { |
| if (xmlValueProperty != null && xmlValueProperty == prop) { |
| foundValue = true; |
| valueField = prop; |
| } else if (!prop.isAttribute() && !prop.isTransient() && !prop.isAnyAttribute()) { |
| foundNonAttribute = true; |
| } |
| } |
| if (foundValue && !foundNonAttribute) { |
| return valueField; |
| } |
| return null; |
| } |
| |
| /** |
| * Indicates if a given Property is a collection type. |
| * |
| * @param field |
| * @return |
| */ |
| public boolean isCollectionType(Property field) { |
| JavaClass type = field.getType(); |
| return (helper.getJavaClass(java.util.Collection.class).isAssignableFrom(type) |
| || helper.getJavaClass(java.util.List.class).isAssignableFrom(type) |
| || helper.getJavaClass(java.util.Set.class).isAssignableFrom(type)); |
| } |
| |
| private Schema getSchemaForNamespace(String namespace) { |
| return getSchemaForNamespace(namespace, null); |
| } |
| |
| /** |
| * Return the Schema for a given namespace. If no schema exists for the |
| * given namespace, one will be created. |
| * |
| * @param namespace |
| * @return |
| */ |
| private Schema getSchemaForNamespace(String namespace, String packageName) { |
| if (schemaForNamespace == null) { |
| schemaForNamespace = new HashMap<String, Schema>(); |
| allSchemas = new ArrayList<Schema>(); |
| } |
| Schema schema = schemaForNamespace.get(namespace); |
| if (schema == null && !javax.xml.XMLConstants.XML_NS_URI.equals(namespace)) { |
| schema = new Schema(); |
| String schemaName = SCHEMA + schemaCount + SCHEMA_EXT; |
| |
| NamespaceInfo namespaceInfo = getNamespaceInfoForNamespace(namespace, packageName); |
| if (namespaceInfo != null) { |
| if (namespaceInfo.getLocation() != null && !namespaceInfo.getLocation().equals(GENERATE)) { |
| return null; |
| } |
| java.util.Vector namespaces = namespaceInfo.getNamespaceResolver().getNamespaces(); |
| for (int i = 0; i < namespaces.size(); i++) { |
| Namespace nextNamespace = (Namespace) namespaces.get(i); |
| schema.getNamespaceResolver().put(nextNamespace.getPrefix(), nextNamespace.getNamespaceURI()); |
| } |
| } |
| |
| if (outputResolver != null) { |
| try { |
| Result res = outputResolver.createOutput(namespace, schemaName); |
| // if the resolver returns null, schema generation for this namespace URI will be skipped |
| if (res == null) { |
| return null; |
| } |
| schema.setResult(res); |
| if (res.getSystemId() != null) { |
| schemaName = res.getSystemId(); |
| // may use schema name to create a URI, which expects '/' as file separator |
| schemaName = schemaName.replace(SLASHES, SLASH); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| schema.setName(schemaName); |
| schemaCount++; |
| |
| if (!namespace.equals(EMPTY_STRING)) { |
| schema.setTargetNamespace(namespace); |
| String prefix = null; |
| if (namespaceInfo != null) { |
| prefix = namespaceInfo.getNamespaceResolver().resolveNamespaceURI(namespace); |
| } |
| if (prefix == null) { |
| prefix = schema.getNamespaceResolver().generatePrefix(); |
| } |
| schema.getNamespaceResolver().put(prefix, namespace); |
| } |
| |
| if (namespaceInfo != null) { |
| schema.setAttributeFormDefault(namespaceInfo.isAttributeFormQualified()); |
| schema.setElementFormDefault(namespaceInfo.isElementFormQualified()); |
| } |
| schemaForNamespace.put(namespace, schema); |
| allSchemas.add(schema); |
| } |
| return schema; |
| } |
| |
| public Collection<Schema> getAllSchemas() { |
| if (allSchemas == null) { |
| allSchemas = new ArrayList<Schema>(); |
| } |
| return allSchemas; |
| } |
| |
| public NamespaceInfo getNamespaceInfoForNamespace(String namespace) { |
| |
| return getNamespaceInfoForNamespace(namespace, null); |
| } |
| |
| public NamespaceInfo getNamespaceInfoForNamespace(String namespace, String packageName) { |
| |
| if (null != packageName) { |
| if (packageToPackageInfoMappings.containsKey(packageName)) { |
| PackageInfo packageInfo = packageToPackageInfoMappings.get(packageName); |
| if (packageInfo.getNamespace().equals(namespace)) { |
| return packageInfo.getNamespaceInfo(); |
| } |
| } |
| } |
| |
| Collection<PackageInfo> packageInfo = packageToPackageInfoMappings.values(); |
| for (PackageInfo info : packageInfo) { |
| if (info.getNamespace().equals(namespace)) { |
| return info.getNamespaceInfo(); |
| } |
| } |
| return null; |
| } |
| |
| public String getPrefixForNamespace(Schema schema, String URI) { |
| //add Import if necessary |
| Schema referencedSchema = this.getSchemaForNamespace(URI); |
| addImportIfRequired(schema, referencedSchema, URI); |
| |
| NamespaceResolver namespaceResolver = schema.getNamespaceResolver(); |
| Enumeration keys = namespaceResolver.getPrefixes(); |
| while (keys.hasMoreElements()) { |
| String next = (String) keys.nextElement(); |
| String nextUri = namespaceResolver.resolveNamespacePrefix(next); |
| if (nextUri.equals(URI)) { |
| return next; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Attempt to resolve the given URI to a prefix. If this is unsuccessful, one |
| * will be generated and added to the resolver. |
| * |
| * @param URI |
| * @param schema |
| * @return |
| */ |
| public String getOrGeneratePrefixForNamespace(String URI, Schema schema) { |
| String prefix = schema.getNamespaceResolver().resolveNamespaceURI(URI); |
| if (prefix == null) { |
| if (URI.equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)) { |
| prefix = schema.getNamespaceResolver().generatePrefix(Constants.SCHEMA_PREFIX); |
| } else if (URI.equals(Constants.REF_URL)) { |
| prefix = schema.getNamespaceResolver().generatePrefix(Constants.REF_PREFIX); |
| |
| if(!importExists(schema, URI, SWA_REF_IMPORT)){ |
| Import schemaImport = new Import(); |
| schemaImport.setSchemaLocation(SWA_REF_IMPORT); |
| schemaImport.setNamespace(URI); |
| schema.getImports().add(schemaImport); |
| } |
| } else { |
| prefix = schema.getNamespaceResolver().generatePrefix(); |
| } |
| schema.getNamespaceResolver().put(prefix, URI); |
| } |
| return prefix; |
| } |
| |
| public void addGlobalElements(Map<QName, ElementDeclaration> additionalElements) { |
| for (Entry<QName, ElementDeclaration> entry : additionalElements.entrySet()) { |
| QName next = entry.getKey(); |
| if(next != null) { |
| ElementDeclaration nextElement = entry.getValue(); |
| if (nextElement.getScopeClass() == GLOBAL.class) { |
| |
| String namespaceURI = next.getNamespaceURI(); |
| Schema targetSchema = getSchemaForNamespace(namespaceURI); |
| if (targetSchema == null) { |
| continue; |
| } |
| |
| if (targetSchema.getTopLevelElements().get(next.getLocalPart()) == null) { |
| Element element = new Element(); |
| element.setName(next.getLocalPart()); |
| element.setNillable(nextElement.isNillable()); |
| JavaClass javaClass = nextElement.getJavaType(); |
| |
| //First check for built in type |
| QName schemaType = (QName) helper.getXMLToJavaTypeMap().get(javaClass.getRawName()); |
| if (schemaType != null) { |
| element.setType(Constants.SCHEMA_PREFIX + COLON + schemaType.getLocalPart()); |
| } else if (areEquals(javaClass, JAVAX_ACTIVATION_DATAHANDLER) || areEquals(javaClass, byte[].class) || areEquals(javaClass, Byte[].class) || areEquals(javaClass, Image.class) || areEquals(javaClass, Source.class) || areEquals(javaClass, JAVAX_MAIL_INTERNET_MIMEMULTIPART)) { |
| schemaType = Constants.BASE_64_BINARY_QNAME; |
| if(nextElement.getTypeMappingInfo() != null) { |
| if(nextElement.isXmlAttachmentRef()) { |
| schemaType = Constants.SWA_REF_QNAME; |
| } |
| if (nextElement.getXmlMimeType() != null) { |
| element.getAttributesMap().put(Constants.EXPECTED_CONTENT_TYPES_QNAME, nextElement.getXmlMimeType()); |
| } |
| } |
| String prefix = getOrGeneratePrefixForNamespace(schemaType.getNamespaceURI(), targetSchema); |
| element.setType(prefix + COLON + schemaType.getLocalPart()); |
| } else if (areEquals(javaClass, CoreClassConstants.XML_GREGORIAN_CALENDAR)) { |
| schemaType = Constants.ANY_SIMPLE_TYPE_QNAME; |
| element.setType(Constants.SCHEMA_PREFIX + COLON + schemaType.getLocalPart()); |
| } else { |
| |
| TypeInfo type = this.typeInfo.get(javaClass.getQualifiedName()); |
| if (type != null) { |
| String typeName = null; |
| if (type.isComplexType()) { |
| typeName = type.getComplexType().getName(); |
| } else { |
| typeName = type.getSimpleType().getName(); |
| } |
| // may need an anonymous complex type |
| if (typeName == null) { |
| Schema schema = getSchemaForNamespace(next.getNamespaceURI()); |
| ComplexType cType = createComplexTypeForClass(javaClass, type); |
| TypeDefParticle particle = null; |
| if(cType.getComplexContent() != null && cType.getComplexContent().getExtension() != null) { |
| particle = cType.getComplexContent().getExtension().getTypeDefParticle(); |
| } else { |
| particle = cType.getTypeDefParticle(); |
| } |
| addToSchemaType(type, type.getPropertyList(), particle, cType, schema); |
| targetSchema = schema; |
| element.setComplexType(cType); |
| } else { |
| // check namespace of schemaType |
| if (type.getClassNamespace().equals(namespaceURI)) { |
| //no need to prefix here |
| String prefix = targetSchema.getNamespaceResolver().resolveNamespaceURI(namespaceURI); |
| if(prefix != null && !(prefix.equals(EMPTY_STRING))) { |
| element.setType(prefix + COLON + typeName); |
| } else { |
| element.setType(typeName); |
| } |
| } else { |
| Schema complexTypeSchema = getSchemaForNamespace(type.getClassNamespace()); |
| String complexTypeSchemaNS = type.getClassNamespace(); |
| if (complexTypeSchemaNS == null) { |
| complexTypeSchemaNS = EMPTY_STRING; |
| } |
| addImportIfRequired(targetSchema, complexTypeSchema, type.getClassNamespace()); |
| |
| String prefix = targetSchema.getNamespaceResolver().resolveNamespaceURI(complexTypeSchemaNS); |
| if (prefix != null) { |
| element.setType(prefix + COLON + typeName); |
| } else { |
| element.setType(typeName); |
| } |
| } |
| } |
| } |
| } |
| if (nextElement.getSubstitutionHead() != null) { |
| String subLocal = nextElement.getSubstitutionHead().getLocalPart(); |
| String subNamespace = nextElement.getSubstitutionHead().getNamespaceURI(); |
| String prefix = getPrefixForNamespace(targetSchema, subNamespace); |
| if (prefix == null || prefix.equals(EMPTY_STRING)) { |
| element.setSubstitutionGroup(subLocal); |
| } else { |
| element.setSubstitutionGroup(prefix + COLON + subLocal); |
| } |
| } |
| targetSchema.addTopLevelElement(element); |
| SchemaTypeInfo info = this.schemaTypeInfo.get(javaClass.getQualifiedName()); |
| if (info == null) { |
| // probably for a simple type, not generated |
| info = new SchemaTypeInfo(); |
| info.setSchemaTypeName(schemaType); |
| schemaTypeInfo.put(javaClass.getQualifiedName(), info); |
| } |
| info.getGlobalElementDeclarations().add(next); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Return the Map of SchemaTypeInfo instances. The Map is keyed on JavaClass |
| * qualified name. |
| * |
| * @return |
| */ |
| public Map<String, SchemaTypeInfo> getSchemaTypeInfo() { |
| return this.schemaTypeInfo; |
| } |
| |
| private boolean importExists(Schema schema, String importNsURI, String schemaLocation) { |
| java.util.List imports = schema.getImports(); |
| for (int i = 0; i < imports.size(); i++) { |
| Import nextImport = (Import) imports.get(i); |
| if (nextImport.getSchemaLocation() != null && nextImport.getSchemaLocation().equals(schemaLocation)) { |
| if ("".equals(importNsURI)) { |
| if (nextImport.getNamespace() == null) { |
| return true; |
| } |
| } else { |
| if (importNsURI.equals(nextImport.getNamespace())) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| private boolean addImportIfRequired(Schema sourceSchema, Schema importSchema, String importNamespace) { |
| if (importSchema != sourceSchema && !(importNamespace != null && importNamespace.equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI))) { |
| String schemaName = null; |
| if(javax.xml.XMLConstants.XML_NS_URI.equals(importNamespace)){ |
| schemaName = Constants.XML_NAMESPACE_SCHEMA_LOCATION; |
| }else{ |
| if (importSchema != null) { |
| schemaName = importSchema.getName(); |
| } else if (importNamespace != null) { |
| NamespaceInfo nInfo = getNamespaceInfoForNamespace(importNamespace); |
| schemaName = nInfo.getLocation(); |
| } |
| } |
| // may need to relativize the schema name |
| if (schemaName != null && importSchema != null) { |
| URI relativizedURI = null; |
| try { |
| // need to strip off the last slash and the file name that follows |
| String schemaPath = sourceSchema.getName(); |
| int idx; |
| if ((idx = schemaPath.lastIndexOf(SLASH)) != -1) { |
| schemaPath = schemaPath.substring(0, idx); |
| } |
| URI baseURI = new URI(schemaPath); |
| URI importURI = new URI(schemaName); |
| relativizedURI = baseURI.relativize(importURI); |
| } catch (Exception e) { |
| // at this point we will leave schemaName as is |
| relativizedURI = null; |
| } |
| if (relativizedURI != null) { |
| schemaName = relativizedURI.getPath(); |
| } |
| } |
| |
| if (schemaName != null && !importExists(sourceSchema, importNamespace, schemaName)) { |
| Import schemaImport = new Import(); |
| schemaImport.setSchemaLocation(schemaName); |
| if (importNamespace != null && !importNamespace.equals(EMPTY_STRING)) { |
| schemaImport.setNamespace(importNamespace); |
| } |
| sourceSchema.getImports().add(schemaImport); |
| if (schemaImport.getNamespace() != null) { |
| if(schemaImport.getNamespace().equals(javax.xml.XMLConstants.XML_NS_URI)) { |
| //make sure xml namespace is in the resolver so it gets declared |
| sourceSchema.getNamespaceResolver().put(javax.xml.XMLConstants.XML_NS_PREFIX, javax.xml.XMLConstants.XML_NS_URI); |
| } |
| String prefix = sourceSchema.getNamespaceResolver().resolveNamespaceURI(importNamespace); |
| //Don't need to generate prefix for default namespace |
| if (prefix == null && !importNamespace.equals(EMPTY_STRING)) { |
| sourceSchema.getNamespaceResolver().put(sourceSchema.getNamespaceResolver().generatePrefix(), importNamespace); |
| } |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Compares a JavaModel JavaClass to a Class. Equality is based on |
| * the raw name of the JavaClass compared to the canonical |
| * name of the Class. |
| * |
| * @param src |
| * @param tgtCanonicalName |
| * @return |
| */ |
| protected boolean areEquals(JavaClass src, String tgtCanonicalName) { |
| if (src == null || tgtCanonicalName == null) { |
| return false; |
| } |
| return src.getRawName().equals(tgtCanonicalName); |
| } |
| |
| /** |
| * Compares a JavaModel JavaClass to a Class. Equality is based on |
| * the raw name of the JavaClass compared to the canonical |
| * name of the Class. |
| * |
| * @param src |
| * @param tgt |
| * @return |
| */ |
| protected boolean areEquals(JavaClass src, Class tgt) { |
| if (src == null || tgt == null) { |
| return false; |
| } |
| return src.getRawName().equals(tgt.getCanonicalName()); |
| } |
| |
| /** |
| * Return the type name for a given Property. |
| * |
| * @param next |
| * @param javaType |
| * @param theSchema |
| * @return |
| */ |
| private String getTypeName(Property next, JavaClass javaType, Schema theSchema){ |
| QName schemaType = next.getSchemaType(); |
| if (schemaType == null) { |
| schemaType = getSchemaTypeFor(javaType); |
| } |
| if (schemaType != null) { |
| if (schemaType.getNamespaceURI() == null) { |
| return schemaType.getLocalPart(); |
| } |
| return getOrGeneratePrefixForNamespace(schemaType.getNamespaceURI(), theSchema) + COLON + schemaType.getLocalPart(); |
| } |
| return Constants.SCHEMA_PREFIX + Constants.COLON + Constants.ANY_SIMPLE_TYPE; |
| } |
| |
| /** |
| * Return the qualified (if necessary) type name for a given Property. |
| * |
| * @param prop |
| * @param schema |
| * @return |
| */ |
| private String getQualifiedTypeName(Property prop, Schema schema) { |
| JavaClass javaType = prop.getActualType(); |
| String typeName = getTypeName(prop, javaType, schema); |
| // may need to qualify the type |
| if (typeName != null && !typeName.contains(COLON)) { |
| String prefix = getPrefixForNamespace(schema, schema.getTargetNamespace()); |
| if (prefix != null) { |
| typeName = prefix + COLON + typeName; |
| } |
| } |
| return typeName; |
| } |
| |
| /** |
| * This method will build element/complexType/typedefparticle components for a given xml-path, |
| * and return an XmlPathResult instance containg the sequence that the target should be added |
| * to, as well as the current schema - which could be different than the working schema used |
| * before calling this method in the case of a prefixed path element from a different namespace. |
| * Regarding the path 'target', if the xml-path was "contact-info/address/street", "street" |
| * would be the target. In this case the sequence containing the "address" element would be |
| * set in the XmlPathResult to be returned. |
| * |
| * The exception case is an 'any', where we want to process the last path element before |
| * returning - this is necessary due to the fact that an Any will be added to the sequence |
| * in place of the last path element by the calling method. |
| * |
| * @param frag |
| * @param xpr |
| * @param isChoice |
| * @param next |
| * @return |
| */ |
| private AddToSchemaResult buildSchemaComponentsForXPath(XPathFragment frag, AddToSchemaResult xpr, boolean isChoice, Property next) { |
| boolean isAny = next.isAny() || next.isAnyAttribute(); |
| TypeDefParticle currentParticle = xpr.particle; |
| Schema workingSchema = xpr.schema; |
| |
| // each nested choice on a collection will be unbounded |
| boolean isUnbounded = false; |
| if(currentParticle != null) { |
| isUnbounded = (currentParticle.getMaxOccurs() != null && currentParticle.getMaxOccurs()==Occurs.UNBOUNDED); |
| } |
| |
| // don't process the last frag; that will be handled by the calling method if necessary |
| // note that we may need to process the last frag if it has a namespace or is an 'any' |
| boolean lastFrag = (frag.getNextFragment() == null || frag.getNextFragment().nameIsText()); |
| //boolean isNextAttribute = (frag.getNextFragment() != null) && (frag.getNextFragment().isAttribute()); |
| // if the element is already in the sequence we don't want the calling method to add a second one |
| if (lastFrag && (elementExistsInParticle(frag.getLocalName(), frag.getShortName(), currentParticle) != null)) { |
| xpr.particle = null; |
| return xpr; |
| } |
| |
| |
| // if the current element exists, use it; otherwise create a new one |
| Element currentElement = elementExistsInParticle(frag.getLocalName(), frag.getShortName(), currentParticle); |
| boolean currentElementExists = (currentElement != null); |
| |
| if (!currentElementExists) { |
| currentElement = new Element(); |
| // don't set the element name yet, as it may end up being a ref |
| ComplexType cType = new ComplexType(); |
| TypeDefParticle particle = null; |
| if (isChoice) { |
| particle = new Choice(); |
| if (isUnbounded) { |
| particle.setMaxOccurs(Occurs.UNBOUNDED); |
| } |
| } else { |
| XPathFragment nextFragment = frag.getNextFragment(); |
| if (frag.containsIndex() || frag.getPredicate() != null || (!next.isXmlList() && null != nextFragment && nextFragment.isAttribute() && helper.isCollectionType(next.getType()))) { |
| currentElement.setMaxOccurs(Occurs.UNBOUNDED); |
| } |
| particle = new Sequence(); |
| } |
| cType.setTypeDefParticle(particle); |
| currentElement.setComplexType(cType); |
| } else { |
| //if the current element already exists, we may need to change it's type |
| XPathFragment nextFrag = frag.getNextFragment(); |
| if(nextFrag != null && nextFrag.isAttribute()) { |
| if(currentElement.getType() != null && currentElement.getComplexType() == null) { |
| //there's already a text mapping to this element, so |
| //attributes can be added by making it complex with |
| //simple content. |
| SimpleType type = currentElement.getSimpleType(); |
| if(type != null) { |
| ComplexType cType = new ComplexType(); |
| cType.setSimpleContent(new SimpleContent()); |
| Extension extension = new Extension(); |
| extension.setBaseType(type.getRestriction().getBaseType()); |
| cType.getSimpleContent().setExtension(extension); |
| currentElement.setSimpleType(null); |
| currentElement.setComplexType(cType); |
| } else { |
| String eType = currentElement.getType(); |
| ComplexType cType = new ComplexType(); |
| SimpleContent sContent = new SimpleContent(); |
| Extension extension = new Extension(); |
| extension.setBaseType(eType); |
| sContent.setExtension(extension); |
| cType.setSimpleContent(sContent); |
| currentElement.setType(null); |
| currentElement.setComplexType(cType); |
| } |
| } |
| } |
| } |
| // may need to create a ref, depending on the namespace |
| Element globalElement = null; |
| |
| String fragUri = frag.getNamespaceURI(); |
| if (fragUri != null) { |
| Schema fragSchema = getSchemaForNamespace(fragUri); |
| String targetNS = workingSchema.getTargetNamespace(); |
| |
| // handle Attribute case |
| if (frag.isAttribute()) { |
| if (fragSchema == null || (fragSchema.isAttributeFormDefault() && !fragUri.equals(targetNS)) || (!fragSchema.isAttributeFormDefault() && fragUri.length() > 0)) { |
| // must generate a global attribute and create a reference to it |
| // if the global attribute exists, use it; otherwise create a new one |
| // if fragSchema is null, just generate the ref |
| if(fragSchema != null) { |
| Attribute globalAttribute = null; |
| globalAttribute = (Attribute) fragSchema.getTopLevelAttributes().get(frag.getLocalName()); |
| if (globalAttribute == null) { |
| globalAttribute = createGlobalAttribute(frag, workingSchema, fragSchema, next); |
| } |
| } else { |
| //may need to add an import |
| addImportIfRequired(workingSchema, null, fragUri); |
| } |
| // add the attribute ref to the current element |
| String attributeRefName; |
| if (fragUri.equals(targetNS)) { |
| String prefix = fragSchema.getNamespaceResolver().resolveNamespaceURI(fragUri); |
| attributeRefName = prefix + COLON + frag.getLocalName(); |
| } else { |
| attributeRefName = frag.getShortName(); |
| } |
| TypeDefParticleOwner type; |
| if(currentParticle != null) { |
| type = currentParticle.getOwner(); |
| } else { |
| type = xpr.simpleContentType; |
| } |
| if (type instanceof ComplexType) { |
| createRefAttribute(attributeRefName, (ComplexType)type); |
| } |
| // set the frag's schema as it may be different than the current schema |
| xpr.schema = fragSchema; |
| // ref case - indicate to the calling method that there's nothing to do |
| xpr.particle = null; |
| } |
| // since we are dealing with an attribute, we are on the last fragment; return |
| return xpr; |
| } |
| |
| // here we are dealing with an Element |
| if ((fragSchema.isElementFormDefault() && !fragUri.equals(targetNS)) || (!fragSchema.isElementFormDefault() && fragUri.length() > 0)) { |
| // must generate a global element and create a reference to it |
| // if the global element exists, use it; otherwise create a new one |
| globalElement = (Element) fragSchema.getTopLevelElements().get(frag.getLocalName()); |
| if (globalElement == null) { |
| globalElement = createGlobalElement(frag, workingSchema, fragSchema, isChoice, isUnbounded, next, (lastFrag && !isAny)); |
| } |
| // if the current element doesn't exist set a ref and add it to the sequence |
| if (!currentElementExists) { |
| // use prefix from the working schema's resolver - add prefix/uri pair if necessary |
| String fragPrefix = workingSchema.getNamespaceResolver().resolveNamespaceURI(fragUri); |
| if (fragPrefix == null) { |
| fragPrefix = workingSchema.getNamespaceResolver().generatePrefix(frag.getPrefix()); |
| workingSchema.getNamespaceResolver().put(fragPrefix, fragUri); |
| } |
| currentElement = createRefElement(fragPrefix + COLON + frag.getLocalName(), currentParticle); |
| if (frag.containsIndex() || frag.getPredicate() != null || helper.isCollectionType(next.getType())) { |
| currentElement.setMaxOccurs(Occurs.UNBOUNDED); |
| } |
| currentElementExists = true; |
| } |
| // set the frag's schema as it may be different than the current schema |
| xpr.schema = fragSchema; |
| // at this point, if we are dealing with the last fragment we will need to return |
| if (lastFrag) { |
| // since we processed the last frag, return null so the calling method doesn't |
| // add a second one...unless we're dealing with an 'any' |
| if (isAny) { |
| // set the particle that the 'any' will be added to by the calling method |
| xpr.particle = globalElement.getComplexType().getTypeDefParticle(); |
| return xpr; |
| } |
| // ref case - indicate to the calling method that there's nothing to do |
| xpr.particle = null; |
| return xpr; |
| } |
| // make the global element current |
| currentElement = globalElement; |
| } |
| } |
| if (!lastFrag || (lastFrag && isAny)) { |
| // 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()); |
| Integer minOccurs = next.getMinOccurs(); |
| if (minOccurs != null) currentElement.setMinOccurs(String.valueOf(minOccurs)); |
| else currentElement.setMinOccurs(Occurs.ZERO); |
| currentParticle.addElement(currentElement); |
| } |
| // set the correct particle to use/return |
| if(currentElement.getComplexType() != null) { |
| if(currentElement.getComplexType().getTypeDefParticle() == null) { |
| //complexType with simple-content |
| xpr.simpleContentType = currentElement.getComplexType(); |
| xpr.particle = null; |
| } else { |
| xpr.particle = currentElement.getComplexType().getTypeDefParticle(); |
| } |
| } else { |
| //If there's no complex type, we're building the path through an element with |
| //a simple type. In order to build the path through this |
| //element, switch to a complex type with simple content. |
| SimpleType type = currentElement.getSimpleType(); |
| if(type != null) { |
| ComplexType cType = new ComplexType(); |
| cType.setSimpleContent(new SimpleContent()); |
| Extension extension = new Extension(); |
| extension.setBaseType(type.getRestriction().getBaseType()); |
| cType.getSimpleContent().setExtension(extension); |
| currentElement.setSimpleType(null); |
| currentElement.setComplexType(cType); |
| xpr.particle = null; |
| xpr.simpleContentType = cType; |
| } else { |
| String eType = currentElement.getType(); |
| ComplexType cType = new ComplexType(); |
| SimpleContent sContent = new SimpleContent(); |
| Extension extension = new Extension(); |
| extension.setBaseType(eType); |
| sContent.setExtension(extension); |
| cType.setSimpleContent(sContent); |
| currentElement.setType(null); |
| currentElement.setComplexType(cType); |
| xpr.particle = null; |
| xpr.simpleContentType = cType; |
| } |
| } |
| } |
| // if we're on the last fragment, we're done |
| if (lastFrag) { |
| return xpr; |
| } |
| // call back into this method to process the next path element |
| return buildSchemaComponentsForXPath(frag.getNextFragment(), xpr, isChoice, next); |
| } |
| |
| /** |
| * Convenience method for determining if an element already exists in a given |
| * typedefparticle. If an element exists whose ref is equal to 'refString' |
| * or its name is equal to 'elementName', it is returned. Null otherwise. |
| * |
| * Note that ref takes precidence, so if either has a ref set name equality |
| * will not be performed. |
| * |
| * @param elementName the non-null element name to look for |
| * @param refString if the element is a ref, this will be the prefix qualified element name |
| * @param particle the sequence/choice/all to search for an existing element |
| * @return |
| */ |
| protected Element elementExistsInParticle(String elementName, String refString, TypeDefParticle particle) { |
| if (particle == null || particle.getElements() == null || particle.getElements().size() == 0) { |
| return null; |
| } |
| java.util.List existingElements = particle.getElements(); |
| 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; |
| } |
| // case #1 - refString is null |
| if (refString == null) { |
| // if element has a null ref value and the element names are equal, or element has a non-null |
| // ref that is not equal to its name, and the element names are equal, we found a match |
| if ((element.getRef() == null || (element.getRef() != null && element.getRef().equals(element.getName()))) && elementName.equals(element.getName())) { |
| return element; |
| } |
| } |
| // case #2 - refString equals elementName |
| else if (refString.equals(elementName)) { |
| // if element has a ref equal to refString, or no ref but element names are equal, we found a match |
| if ((element.getRef() != null && element.getRef().equals(refString)) || (element.getRef() == null && elementName.equals(element.getName()))) { |
| return element; |
| } |
| } |
| // case #3 - refString is different than elementName |
| else if (element.getRef() != null && element.getRef().equals(refString)) { |
| return element; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Create a global attribute. An import is added if necessary. This method |
| * will typically be called when processing an XPath and a prefixed path |
| * element is encountered tha requires an attribute ref. |
| * |
| * @param frag |
| * @param workingSchema |
| * @param fragSchema |
| * @param prop |
| * @return |
| */ |
| public Attribute createGlobalAttribute(XPathFragment frag, Schema workingSchema, Schema fragSchema, Property prop) { |
| Attribute gAttribute = new Attribute(); |
| gAttribute.setName(frag.getLocalName()); |
| gAttribute.setType(getQualifiedTypeName(prop, fragSchema)); |
| fragSchema.getTopLevelAttributes().put(gAttribute.getName(), gAttribute); |
| addImportIfRequired(workingSchema, fragSchema, frag.getNamespaceURI()); |
| return gAttribute; |
| } |
| |
| /** |
| * Create a global element. An import is added if necessary. This method |
| * will typically be called when processing an XPath and a prefixed path |
| * element is encountered the requires an element ref. |
| * |
| * @param frag XPathFragment which wil lbe used to create the global element |
| * @param workingSchema current schema |
| * @param fragSchema frag's schema |
| * @param isChoice indicates if we need to construct a choice |
| * @param isUnbounded maxOccurs setting for choice |
| * @param prop property which owns the xml-path |
| * @param shouldSetType if this is the last fragment in the xml-path and not an 'any', we should set the type |
| * @return |
| */ |
| public Element createGlobalElement(XPathFragment frag, Schema workingSchema, Schema fragSchema, boolean isChoice, boolean isUnbounded, Property prop, boolean shouldSetType) { |
| Element gElement = new Element(); |
| gElement.setName(frag.getLocalName()); |
| if (shouldSetType) { |
| gElement.setType(getQualifiedTypeName(prop, fragSchema)); |
| } else { |
| ComplexType gCType = new ComplexType(); |
| TypeDefParticle particle; |
| if (isChoice) { |
| particle = new Choice(); |
| if (isUnbounded) { |
| particle.setMaxOccurs(Occurs.UNBOUNDED); |
| } |
| } else { |
| particle = new Sequence(); |
| } |
| gCType.setTypeDefParticle(particle); |
| gElement.setComplexType(gCType); |
| } |
| fragSchema.addTopLevelElement(gElement); |
| addImportIfRequired(workingSchema, fragSchema, frag.getNamespaceURI()); |
| return gElement; |
| } |
| |
| /** |
| * Create an element reference and add it to a given particle. This |
| * method will typically be called when processing an XPath and a |
| * prefixed path element is encountered that requires an element ref. |
| * |
| * @param elementRefName |
| * @param particle |
| * @return |
| */ |
| public Element createRefElement(String elementRefName, TypeDefParticle particle) { |
| Element refElement = new Element(); |
| // ref won't have a complex type |
| refElement.setComplexType(null); |
| refElement.setMinOccurs(Occurs.ZERO); |
| refElement.setMaxOccurs(Occurs.ONE); |
| refElement.setRef(elementRefName); |
| particle.addElement(refElement); |
| return refElement; |
| } |
| |
| /** |
| * Create an attribute reference and add it to a given complex type. |
| * This method will typically be called when processing an XPath |
| * and a prefixed path element is encountered that requires an |
| * attribute ref. |
| * |
| * @param attributeRefName |
| * @param owningComplexType |
| * @return |
| */ |
| public Attribute createRefAttribute(String attributeRefName, ComplexType owningComplexType) { |
| Attribute refAttribute = new Attribute(); |
| refAttribute.setRef(attributeRefName); |
| if (owningComplexType.getSimpleContent() != null) { |
| owningComplexType.getSimpleContent().getExtension().getOrderedAttributes().add(refAttribute); |
| } else { |
| owningComplexType.getOrderedAttributes().add(refAttribute); |
| } |
| return refAttribute; |
| } |
| |
| /** |
| * This class will typically be used when building schema components. It will hold the |
| * TypeDefParticle (all, sequence, choice), ComplexType, and/or Schema that are to be |
| * used by the method that is processing the given property. |
| * |
| */ |
| private static final class AddToSchemaResult { |
| ComplexType type; |
| TypeDefParticle particle; |
| Schema schema; |
| ComplexType simpleContentType; |
| |
| AddToSchemaResult(TypeDefParticle particle, Schema schema) { |
| this.particle = particle; |
| this.schema = schema; |
| } |
| |
| AddToSchemaResult(TypeDefParticle particle, ComplexType type) { |
| this.particle = particle; |
| this.type = type; |
| } |
| } |
| |
| /** |
| * Convenience method for processing XmlWriteTransformer(s) for a given property. |
| * Required schema components will be generated and set accordingly. |
| * |
| * @param property the property containing one or more XmlWriteTransformers. |
| * @param typeInfo the TypeInfo that owns the property |
| * @param compositor sequence/choice/all to modify |
| * @param type ComplexType which compositor(s) should be added to |
| * @param schema current schema which ComplextType will be added to |
| */ |
| private void addTransformerToSchema(Property property, TypeInfo typeInfo, TypeDefParticle compositor, ComplexType type, Schema schema) { |
| java.util.List<Property> props = getTransformerPropertyBuilder(property, typeInfo).buildProperties(); |
| addToSchemaType(typeInfo, props, compositor, type, schema); |
| } |
| |
| /** |
| * Returns TransformerPropertyBuilder which builds properties from xml transformers. |
| * |
| * @param property property holding xml transformers |
| * @param typeInfo typeInfo with transformer class |
| * @return transformer property builder |
| */ |
| protected TransformerPropertyBuilder getTransformerPropertyBuilder(Property property, TypeInfo typeInfo) { |
| return new TransformerPropertyBuilder(property, typeInfo, helper, ATT); |
| } |
| |
| /** |
| * Process a given XmlPath, and create the required schema components. The last |
| * fragment may not be processed; in this case the returned XmlPathResult is |
| * used to set the current schema and parent complex type, then the owning |
| * property is processed as per usual. Note the last fragment may be processed |
| * if it has a namespace or is an 'any'. |
| * |
| * @param property the property containing the XmlPath for which schema components are to be built |
| * @param compositor the sequence/choice/all to modify |
| * @param schema the schema being built when this method is called - this could |
| * if the XmlPath contains an entry that references a different namespace |
| * @param isChoice indicates if the given property is a choice property |
| * @param type the ComplexType which compositor(s) should be added to |
| * @return AddToSchemaResult containing current schema and sequence/all/choice build |
| * based on the given XmlPath |
| */ |
| private AddToSchemaResult addXPathToSchema(Property property, TypeDefParticle compositor, Schema schema, boolean isChoice, ComplexType type) { |
| // '.' xml-path requires special handling |
| if (property.getXmlPath().equals(DOT)) { |
| TypeInfo info = typeInfo.get(property.getActualType().getQualifiedName()); |
| JavaClass infoClass = property.getActualType(); |
| addSelfProperties(infoClass, info, compositor, type); |
| return null; |
| } |
| // create the XPathFragment(s) for the path |
| Field xfld = new XMLField(property.getXmlPath()); |
| xfld.setNamespaceResolver(schema.getNamespaceResolver()); |
| xfld.initialize(); |
| // build the schema components for the xml-path |
| return buildSchemaComponentsForXPath(xfld.getXPathFragment(), new AddToSchemaResult(compositor, schema), isChoice, property); |
| } |
| |
| private void addSelfProperties(JavaClass aJavaClass, TypeInfo info, TypeDefParticle compositor, ComplexType type) { |
| // Recursively add all properties from aJavaClass and its superclasses, adding superclass properties first. |
| if (aJavaClass.getSuperclass() != null) { |
| TypeInfo superInfo = typeInfo.get(aJavaClass.getSuperclass().getQualifiedName()); |
| if (superInfo != null) { |
| addSelfProperties(aJavaClass.getSuperclass(), superInfo, compositor, type); |
| } |
| } |
| addToSchemaType(info, info.getPropertyList(), compositor, type, info.getSchema()); |
| } |
| |
| /** |
| * Convenience method for processing an XmlElementWrapper for a given property. Required schema |
| * components will be generated and set accordingly. |
| * |
| * @param property the property containing an XmlElementWrapper |
| * @param schema the schema currently being generated |
| * @param compositor sequence/choice/all that the generated wrapper Element will be added to |
| * @return AddToSchemaResult containing current ComplexType and TypeDefParticle |
| */ |
| private AddToSchemaResult addXmlElementWrapperToSchema(Property property, Schema schema, TypeDefParticle compositor) { |
| XmlElementWrapper wrapper = property.getXmlElementWrapper(); |
| Element wrapperElement = new Element(); |
| String name = wrapper.getName(); |
| // handle nillable |
| wrapperElement.setNillable(wrapper.isNillable()); |
| |
| // namespace in not the target or ##default, create a ref with min/max = 1 |
| String wrapperNS = wrapper.getNamespace(); |
| if (!wrapperNS.equals(XMLProcessor.DEFAULT) && !wrapperNS.equals(schema.getTargetNamespace())) { |
| wrapperElement.setMinOccurs(Occurs.ONE); |
| wrapperElement.setMaxOccurs(Occurs.ONE); |
| |
| String prefix = getOrGeneratePrefixForNamespace(wrapperNS, schema); |
| wrapperElement.setRef(prefix + COLON + name); |
| compositor.addElement(wrapperElement); |
| // assume that the element exists and does not need to be created |
| return null; |
| } |
| wrapperElement.setName(name); |
| if (wrapper.isRequired()) { |
| wrapperElement.setMinOccurs(Occurs.ONE); |
| } else { |
| wrapperElement.setMinOccurs(Occurs.ZERO); |
| } |
| if (!wrapperNS.equals(XMLProcessor.DEFAULT)) { |
| String lookupNamespace = schema.getTargetNamespace(); |
| if (lookupNamespace == null) { |
| lookupNamespace = EMPTY_STRING; |
| } |
| NamespaceInfo namespaceInfo = getNamespaceInfoForNamespace(lookupNamespace); |
| boolean isElementFormQualified = false; |
| if (namespaceInfo != null) { |
| isElementFormQualified = namespaceInfo.isElementFormQualified(); |
| } |
| shouldAddRefAndSetForm(wrapperElement, wrapperNS, lookupNamespace, |
| isElementFormQualified, true); |
| } |
| compositor.addElement(wrapperElement); |
| ComplexType wrapperType = new ComplexType(); |
| Sequence wrapperSequence = new Sequence(); |
| wrapperType.setSequence(wrapperSequence); |
| wrapperElement.setComplexType(wrapperType); |
| return new AddToSchemaResult(wrapperSequence, wrapperType); |
| } |
| |
| /** |
| * Build an Attribute with name and type set. This method will typically be |
| * called when processing an XPath that has no associated Property that can |
| * be used to build an Attribute, such as in the case of XmlJoinNodes. |
| * |
| * @param attributeName name of the Attribute |
| * @param typeName type of the Attribute |
| * @return |
| */ |
| private Attribute buildAttribute(QName attributeName, String typeName) { |
| Attribute attribute = new Attribute(); |
| attribute.setName(attributeName.getLocalPart()); |
| attribute.setType(typeName); |
| return attribute; |
| } |
| |
| /** |
| * Build an Attribute based on a given Property. |
| * |
| * @param property the Property used to build the Attribute |
| * @param schema the schema currently being generated |
| * @return |
| */ |
| private Attribute buildAttribute(Property property, Schema schema) { |
| Attribute attribute = new Attribute(); |
| |
| QName attributeName = property.getSchemaName(); |
| attribute.setName(attributeName.getLocalPart()); |
| if (property.isRequired()) { |
| attribute.setUse(Attribute.REQUIRED); |
| } |
| String fixedValue = property.getFixedValue(); |
| if (fixedValue != null){ |
| attribute.setFixed(fixedValue); |
| } |
| // Check to see if it's a collection |
| TypeInfo info = typeInfo.get(property.getActualType().getQualifiedName()); |
| String typeName = getTypeNameForComponent(property, schema, property.getActualType(), attribute, false); |
| if (isCollectionType(property)) { |
| if(!property.isXmlList() && null != property.getXmlPath() && property.getXmlPath().contains("/")) { |
| attribute.setType(typeName); |
| } else { |
| // assume XmlList for an attribute collection |
| SimpleType localType = new SimpleType(); |
| org.eclipse.persistence.internal.oxm.schema.model.List list = new org.eclipse.persistence.internal.oxm.schema.model.List(); |
| list.setItemType(typeName); |
| localType.setList(list); |
| attribute.setSimpleType(localType); |
| } |
| } else { |
| // may need to qualify the type |
| if (typeName != null && !typeName.contains(COLON)) { |
| if (info.getSchema() == schema) { |
| String uri = schema.getTargetNamespace(); |
| if(uri == null) { |
| uri = EMPTY_STRING; |
| } |
| String prefix = getPrefixForNamespace(schema, uri); |
| if (prefix != null) { |
| typeName = prefix + COLON + typeName; |
| } |
| } |
| } |
| attribute.setType(typeName); |
| } |
| return attribute; |
| } |
| |
| /** |
| * Convenience method for processing an attribute property. Required schema |
| * components will be generated and set accordingly. |
| * |
| * @param attribute the attribute property to be processed |
| * @param schema the schema currently being generated |
| * @param type the ComplexType which compositor(s) should be added to |
| */ |
| private void addAttributeToSchema(Attribute attribute, QName attributeName, Schema schema, ComplexType type) { |
| String lookupNamespace = schema.getTargetNamespace(); |
| if (lookupNamespace == null) { |
| lookupNamespace = EMPTY_STRING; |
| } |
| NamespaceInfo namespaceInfo = getNamespaceInfoForNamespace(lookupNamespace); |
| boolean isAttributeFormQualified = false; |
| if (namespaceInfo != null) { |
| isAttributeFormQualified = namespaceInfo.isAttributeFormQualified(); |
| } |
| |
| boolean addRef = shouldAddRefAndSetForm(attribute, attributeName.getNamespaceURI(), lookupNamespace, isAttributeFormQualified, false); |
| if(addRef){ |
| Schema attributeSchema = this.getSchemaForNamespace(attributeName.getNamespaceURI()); |
| if (attributeSchema != null && attributeSchema.getTopLevelAttributes().get(attribute.getName()) == null) { |
| //don't overwrite existing global elements and attributes. |
| attributeSchema.getTopLevelAttributes().put(attribute.getName(), attribute); |
| } |
| Attribute reference = new Attribute(); |
| String prefix = getPrefixForNamespace(schema, attributeName.getNamespaceURI()); |
| if (prefix == null) { |
| reference.setRef(attribute.getName()); |
| } else { |
| reference.setRef(prefix + COLON + attribute.getName()); |
| } |
| if (type.getSimpleContent() != null) { |
| type.getSimpleContent().getExtension().getOrderedAttributes().add(reference); |
| } else { |
| type.getOrderedAttributes().add(reference); |
| } |
| } else { |
| if (type.getSimpleContent() != null) { |
| type.getSimpleContent().getExtension().getOrderedAttributes().add(attribute); |
| } else if (type.getComplexContent() != null) { |
| type.getComplexContent().getExtension().getOrderedAttributes().add(attribute); |
| } else { |
| type.getOrderedAttributes().add(attribute); |
| } |
| } |
| } |
| |
| private boolean shouldAddRefAndSetForm(SimpleComponent sc, String simpleComponentNamespace, String lookupNamespace, boolean formQualified, boolean isElement){ |
| if(sc.getRef() != null){ |
| return true; |
| } |
| boolean addRef = false; |
| boolean sameNamespace = simpleComponentNamespace.equals(lookupNamespace); |
| |
| if (formQualified && !sameNamespace){ |
| if(simpleComponentNamespace.equals(EMPTY_STRING)){ |
| sc.setForm(Constants.UNQUALIFIED); |
| }else{ |
| addRef = true; |
| } |
| } else if(!formQualified && !simpleComponentNamespace.equals(EMPTY_STRING)){ |
| if(sameNamespace && isElement){ |
| sc.setForm(Constants.QUALIFIED); |
| }else{ |
| addRef = true; |
| } |
| } |
| return addRef; |
| } |
| |
| /** |
| * Convenience method for processing an any attribute property. Required |
| * schema components will be generated and set accordingly. |
| * |
| * @param type the ComplexType which compositor(s) should be added to |
| */ |
| private void addAnyAttributeToSchema(ComplexType type) { |
| AnyAttribute anyAttribute = new AnyAttribute(); |
| anyAttribute.setProcessContents(SKIP); |
| anyAttribute.setNamespace(Constants.ANY_NAMESPACE_OTHER); |
| if (type.getSimpleContent() != null) { |
| SimpleContent content = type.getSimpleContent(); |
| if(content.getExtension() != null){ |
| content.getExtension().setAnyAttribute(anyAttribute); |
| }else if(content.getRestriction() != null){ |
| content.getRestriction().setAnyAttribute(anyAttribute); |
| } |
| } else { |
| type.setAnyAttribute(anyAttribute); |
| } |
| } |
| |
| /** |
| * Convenience method for processing an any property. Required |
| * schema components will be generated and set accordingly. |
| * |
| * @param property the choice property to be processed |
| * @param compositor the sequence/choice/all to modify |
| */ |
| private void addAnyToSchema(Property property, TypeDefParticle compositor) { |
| addAnyToSchema(property, compositor, isCollectionType(property)|| property.getType().isArray(), Constants.ANY_NAMESPACE_OTHER); |
| } |
| |
| /** |
| * Convenience method for processing an any property. Required |
| * schema components will be generated and set accordingly. |
| * |
| * @param property the choice property to be processed |
| * @param compositor the sequence/choice/all to modify |
| * @param isCollection if true will be unbounded |
| */ |
| private void addAnyToSchema(Property property, TypeDefParticle compositor, boolean isCollection) { |
| addAnyToSchema(property, compositor, isCollection, Constants.ANY_NAMESPACE_OTHER); |
| } |
| |
| /** |
| * Convenience method for processing an any property. Required |
| * schema components will be generated and set accordingly. |
| * |
| * @param property the choice property to be processed |
| * @param compositor the sequence/choice/all to modify |
| * @param isCollection if true will be unbounded |
| * @param anyNamespace value for the Any's namespace attribute |
| */ |
| private void addAnyToSchema(Property property, TypeDefParticle compositor, boolean isCollection, String anyNamespace) { |
| Any any = new Any(); |
| any.setNamespace(anyNamespace); |
| if (property.isLax()) { |
| any.setProcessContents(Any.LAX); |
| } else { |
| any.setProcessContents(SKIP); |
| } |
| if (isCollection) { |
| any.setMinOccurs(Occurs.ZERO); |
| any.setMaxOccurs(Occurs.UNBOUNDED); |
| } |
| if (compositor instanceof Sequence) { |
| ((Sequence) compositor).addAny(any); |
| } else if (compositor instanceof Choice) { |
| ((Choice) compositor).addAny(any); |
| } |
| } |
| |
| /** |
| * Convenience method for processing a choice property. Required |
| * schema components will be generated and set accordingly. |
| * |
| * @param property the choice property to be processed |
| * @param typeInfo the TypeInfo that the given property belongs to |
| * @param type the ComplexType which compositor(s) should be added to |
| * @param compositor the sequence/choice/all to modify |
| * @param schema the schema being built |
| */ |
| private void addChoiceToSchema(Property property, TypeInfo typeInfo, ComplexType type, TypeDefParticle compositor, Schema schema) { |
| Choice choice = new Choice(); |
| if (property.getGenericType() != null) { |
| choice.setMaxOccurs(Occurs.UNBOUNDED); |
| } |
| ArrayList<Property> choiceProperties = (ArrayList<Property>) property.getChoiceProperties(); |
| addToSchemaType(typeInfo, choiceProperties, choice, type, schema); |
| if (compositor instanceof Sequence) { |
| ((Sequence) compositor).addChoice(choice); |
| } else if (compositor instanceof Choice) { |
| ((Choice) compositor).addChoice(choice); |
| } |
| } |
| |
| /** |
| * Convenience method for processing a reference property. Required |
| * schema components will be generated and set accordingly. |
| * |
| * @param property the choice property to be processed |
| * @param compositor the sequence/choice/all to modify |
| * @param schema the schema being built |
| */ |
| private void addReferenceToSchema(Property property, Schema schema, TypeDefParticle compositor) { |
| java.util.List<ElementDeclaration> referencedElements = property.getReferencedElements(); |
| if (referencedElements.size() == 1 && !property.isAny()) { |
| // if only a single reference, just add the element. |
| Element element = new Element(); |
| ElementDeclaration decl = referencedElements.get(0); |
| String localName = decl.getElementName().getLocalPart(); |
| |
| String prefix = getPrefixForNamespace(schema, decl.getElementName().getNamespaceURI()); |
| if (decl.getScopeClass() == GLOBAL.class){ |
| if (prefix == null || prefix.equals(EMPTY_STRING)) { |
| element.setRef(localName); |
| } else { |
| element.setRef(prefix + COLON + localName); |
| } |
| } else { |
| element.setType(getTypeName(property, decl.getJavaType(), schema)); |
| element.setName(localName); |
| } |
| if (property.getGenericType() != null) { |
| element.setMinOccurs(Occurs.ZERO); |
| element.setMaxOccurs(Occurs.UNBOUNDED); |
| }else if(!property.isRequired()){ |
| element.setMinOccurs(Occurs.ZERO); |
| } |
| compositor.addElement(element); |
| } else { |
| // otherwise, add a choice of referenced elements. |
| Choice choice = new Choice(); |
| if (property.getGenericType() != null) { |
| choice.setMaxOccurs(Occurs.UNBOUNDED); |
| } |
| if (!property.isRequired()){ |
| choice.setMinOccurs(Occurs.ZERO); |
| } |
| for (ElementDeclaration elementDecl : referencedElements) { |
| Element element = new Element(); |
| String localName = elementDecl.getElementName().getLocalPart(); |
| |
| String prefix = getPrefixForNamespace(schema, elementDecl.getElementName().getNamespaceURI()); |
| if (elementDecl.getScopeClass() == GLOBAL.class){ |
| if (prefix == null || prefix.equals(EMPTY_STRING)) { |
| element.setRef(localName); |
| } else { |
| element.setRef(prefix + COLON + localName); |
| } |
| } else { |
| Schema referencedSchema = getSchemaForNamespace(elementDecl.getElementName().getNamespaceURI()); |
| element.setType(getTypeName(property, elementDecl.getJavaType(), referencedSchema)); |
| element.setName(localName); |
| } |
| choice.addElement(element); |
| } |
| // handle XmlAnyElement case |
| if (property.isAny()) { |
| addAnyToSchema(property, choice, false); |
| } |
| if (compositor instanceof Sequence) { |
| ((Sequence) compositor).addChoice(choice); |
| } else if (compositor instanceof Choice) { |
| ((Choice) compositor).addChoice(choice); |
| } |
| } |
| } |
| |
| /** |
| * Convenience method for processing a reference property. Required |
| * schema components will be generated and set accordingly. |
| * |
| * @param property the map property to be processed |
| * @param element schema Element a new complex type will be added to |
| * @param schema the schema being built |
| * @param typeInfo the TypeInfo that the given property belongs to |
| */ |
| private void addMapToSchema(Property property, Element element, Schema schema, TypeInfo typeInfo) { |
| ComplexType entryComplexType = new ComplexType(); |
| Sequence entrySequence = new Sequence(); |
| |
| Element keyElement = new Element(); |
| keyElement.setName(Property.DEFAULT_KEY_NAME); |
| keyElement.setMinOccurs(Occurs.ZERO); |
| |
| JavaClass keyType = property.getKeyType(); |
| JavaClass valueType = property.getActualValueType(); |
| |
| if (keyType == null) { |
| keyType = helper.getJavaClass(Object.class); |
| } |
| |
| if (valueType == null) { |
| valueType = helper.getJavaClass(Object.class); |
| } |
| |
| String typeName; |
| QName keySchemaType = getSchemaTypeFor(keyType); |
| if (keySchemaType != null) { |
| TypeInfo targetInfo = this.typeInfo.get(keyType.getQualifiedName()); |
| if (targetInfo != null) { |
| Schema keyElementSchema = this.getSchemaForNamespace(keySchemaType.getNamespaceURI()); |
| //add an import here |
| addImportIfRequired(schema, keyElementSchema, keySchemaType.getNamespaceURI()); |
| } |
| String prefix; |
| if (keySchemaType.getNamespaceURI().equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)) { |
| prefix = Constants.SCHEMA_PREFIX; |
| } else { |
| prefix = getPrefixForNamespace(schema, keySchemaType.getNamespaceURI()); |
| } |
| if (prefix != null && !prefix.equals(EMPTY_STRING)) { |
| typeName = prefix + COLON + keySchemaType.getLocalPart(); |
| } else { |
| typeName = keySchemaType.getLocalPart(); |
| } |
| keyElement.setType(typeName); |
| } |
| |
| entrySequence.addElement(keyElement); |
| |
| Element valueElement = new Element(); |
| valueElement.setName(Property.DEFAULT_VALUE_NAME); |
| valueElement.setMinOccurs(Occurs.ZERO); |
| QName valueSchemaType = getSchemaTypeFor(valueType); |
| if (valueSchemaType != null) { |
| TypeInfo targetInfo = this.typeInfo.get(valueType.getQualifiedName()); |
| if (targetInfo != null) { |
| Schema valueElementSchema = this.getSchemaForNamespace(valueSchemaType.getNamespaceURI()); |
| //add an import here |
| addImportIfRequired(schema, valueElementSchema, valueSchemaType.getNamespaceURI()); |
| } |
| String prefix; |
| if (valueSchemaType.getNamespaceURI().equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)) { |
| prefix = Constants.SCHEMA_PREFIX; |
| } else { |
| prefix = getPrefixForNamespace(schema, valueSchemaType.getNamespaceURI()); |
| } |
| if (prefix != null && !prefix.equals(EMPTY_STRING)) { |
| typeName = prefix + COLON + valueSchemaType.getLocalPart(); |
| } else { |
| typeName = valueSchemaType.getLocalPart(); |
| } |
| if (property.getValueGenericType() != null) { |
| valueElement.setMaxOccurs(Occurs.UNBOUNDED); |
| } |
| valueElement.setType(typeName); |
| } |
| |
| entrySequence.addElement(valueElement); |
| entryComplexType.setSequence(entrySequence); |
| |
| JavaClass descriptorClass = null; |
| if (null != typeInfo.getDescriptor()) { |
| descriptorClass = helper.getJavaClass(typeInfo.getDescriptor().getJavaClassName()); |
| } |
| |
| JavaClass mapValueClass = helper.getJavaClass(MapValue.class); |
| |
| if (null != descriptorClass && mapValueClass.isAssignableFrom(descriptorClass)) { |
| element.setComplexType(entryComplexType); |
| element.setMaxOccurs(Occurs.UNBOUNDED); |
| } else { |
| ComplexType complexType = new ComplexType(); |
| Sequence sequence = new Sequence(); |
| complexType.setSequence(sequence); |
| |
| Element entryElement = new Element(); |
| entryElement.setName(ENTRY); |
| entryElement.setMinOccurs(Occurs.ZERO); |
| entryElement.setMaxOccurs(Occurs.UNBOUNDED); |
| sequence.addElement(entryElement); |
| entryElement.setComplexType(entryComplexType); |
| element.setComplexType(complexType); |
| } |
| } |
| |
| /** |
| * Convenience method that adds an element ref to a given schema. |
| * |
| * @param schema the schema being built |
| * @param compositor the sequence/choice/all the new reference will be added to |
| * @param referencedElement the element being referenced |
| * @param referencedElementURI the URI of the element being referenced |
| */ |
| private void addElementRefToSchema(Schema schema, TypeDefParticle compositor, Element referencedElement, String referencedElementURI) { |
| Element reference = new Element(); |
| reference.setMinOccurs(referencedElement.getMinOccurs()); |
| reference.setMaxOccurs(referencedElement.getMaxOccurs()); |
| Schema attributeSchema = this.getSchemaForNamespace(referencedElementURI); |
| if (attributeSchema != null && attributeSchema.getTopLevelElements().get(referencedElement.getName()) == null) { |
| // reset min/max occurs as they aren't applicable for global elements |
| referencedElement.setMinOccurs(null); |
| referencedElement.setMaxOccurs(null); |
| // don't overwrite global elements; may have been defined by a type |
| attributeSchema.getTopLevelElements().put(referencedElement.getName(), referencedElement); |
| } |
| String prefix = getPrefixForNamespace(schema, referencedElementURI); |
| if (prefix == null) { |
| reference.setRef(referencedElement.getName()); |
| } else { |
| reference.setRef(prefix + COLON + referencedElement.getName()); |
| } |
| // make sure a ref doesn't already exist before adding this one |
| if (elementExistsInParticle(reference.getName(), reference.getRef(), compositor) == null) { |
| compositor.addElement(reference); |
| } |
| } |
| |
| /** |
| * Build an Element with name, type and possibly minOccurs set. This method will |
| * typically be called when processing an XPath that has no associated Property |
| * that can be used to build an Element, such as in the case of XmlJoinNodes. |
| * |
| * @param elementName name of the Element |
| * @param elementType type of the Element |
| * @param isAll indicates if the Element will be added to an All structure |
| * @return |
| */ |
| private Element buildElement(String elementName, String elementType, boolean isAll) { |
| Element element = new Element(); |
| // Set minOccurs based on the 'required' flag |
| if (!(isAll)) { |
| element.setMinOccurs(Occurs.ZERO); |
| } |
| element.setName(elementName); |
| element.setType(elementType); |
| return element; |
| } |
| |
| /** |
| * Build an Element based on a given Property. |
| * |
| * @param property the Property used to build the Element |
| * @param isAll true if the Element will be added to an All structure |
| * @param schema the schema currently being built |
| * @param typeInfo the TypeInfo that owns the given Property |
| * @return |
| */ |
| private Element buildElement(Property property, boolean isAll, Schema schema, TypeInfo typeInfo) { |
| Element element = new Element(); |
| |
| // handle nillable |
| if (property.shouldSetNillable()) { |
| if (property.isNotNullAnnotated()) throw BeanValidationException.notNullAndNillable(property.getPropertyName()); |
| element.setNillable(true); |
| } |
| // handle defaultValue |
| if (property.isSetDefaultValue()) { |
| element.setDefaultValue(property.getDefaultValue()); |
| } |
| // handle mime-type |
| if (property.getMimeType() != null) { |
| element.getAttributesMap().put(Constants.EXPECTED_CONTENT_TYPES_QNAME, property.getMimeType()); |
| } |
| |
| QName elementName = property.getSchemaName(); |
| String elementNamespace = elementName.getNamespaceURI(); |
| String lookupNamespace = schema.getTargetNamespace(); |
| if (lookupNamespace == null) { |
| lookupNamespace = EMPTY_STRING; |
| } |
| NamespaceInfo namespaceInfo = getNamespaceInfoForNamespace(lookupNamespace, getPackageName(typeInfo)); |
| boolean isElementFormQualified = false; |
| if (namespaceInfo != null) { |
| isElementFormQualified = namespaceInfo.isElementFormQualified(); |
| } |
| // handle element reference |
| |
| boolean addRef = shouldAddRefAndSetForm(element, elementNamespace, lookupNamespace, isElementFormQualified, true); |
| |
| if(addRef){ |
| schema = this.getSchemaForNamespace(elementNamespace); |
| } |
| |
| JavaClass javaType = property.getActualType(); |
| element.setName(elementName.getLocalPart()); |
| String typeName = getTypeNameForComponent(property, schema, javaType, element, true); |
| |
| if (property.getGenericType() != null) { |
| if (property.isXmlList()) { |
| SimpleType localSimpleType = new SimpleType(); |
| org.eclipse.persistence.internal.oxm.schema.model.List list = new org.eclipse.persistence.internal.oxm.schema.model.List(); |
| list.setItemType(typeName); |
| localSimpleType.setList(list); |
| element.setSimpleType(localSimpleType); |
| } else { |
| element.setMaxOccurs(Occurs.UNBOUNDED); |
| element.setType(typeName); |
| } |
| // handle map property |
| } else if (property.isMap()) { |
| addMapToSchema(property, element, schema, typeInfo); |
| } else { |
| element.setType(typeName); |
| } |
| |
| // Set minOccurs based on the 'required' flag |
| Integer minOccurs = property.getMinOccurs(); |
| if (minOccurs != null) { |
| element.setMinOccurs(String.valueOf(minOccurs)); |
| } else { |
| element.setMinOccurs(property.isRequired() ? Occurs.ONE : Occurs.ZERO); |
| } |
| // Overwrite maxOccurs if it has been explicitly set on property. |
| Integer maxOccurs = property.getMaxOccurs(); |
| if (maxOccurs != null) element.setMaxOccurs(String.valueOf(maxOccurs)); |
| |
| if (facets) { |
| for (Facet facet : property.getFacets()) { |
| processFacet(element, facet); |
| } |
| } |
| return element; |
| } |
| |
| private String getPackageName(TypeInfo typeInfo) { |
| if (null != typeInfo && null != typeInfo.getDescriptor() && null != typeInfo.getDescriptor().getJavaClass() && null != typeInfo.getDescriptor().getJavaClass().getPackage()) { |
| return typeInfo.getDescriptor().getJavaClass().getPackage().getName(); |
| } |
| return null; |
| } |
| |
| private void processFacet(Element element, Facet facet) { |
| if (element.getSimpleType() == null) element.setSimpleType(new SimpleType()); |
| Restriction restriction = element.getSimpleType().getRestriction(); |
| if (restriction == null) { |
| restriction = new Restriction(element.getType()); |
| element.getSimpleType().setRestriction(restriction); |
| } |
| element.setType(null); // Prevent error: "Cannot have both a 'type' attribute and an 'anonymous type' child". |
| facet.accept(FacetVisitorHolder.VISITOR, restriction); |
| } |
| |
| |
| /** |
| * @since 2.6 |
| * @author Marcel Valovy |
| */ |
| private static final class FacetVisitorHolder { |
| private static final FacetVisitor<Void, Restriction> VISITOR = new FacetVisitor<Void, Restriction>() { |
| @Override |
| public Void visit(DecimalMinFacet t, Restriction restriction) { |
| if (t.isInclusive()) restriction.setMinInclusive(t.getValue()); |
| else restriction.setMinExclusive(t.getValue()); |
| return null; |
| } |
| |
| @Override |
| public Void visit(DecimalMaxFacet t, Restriction restriction) { |
| if (t.isInclusive()) restriction.setMaxInclusive(t.getValue()); |
| else restriction.setMaxExclusive(t.getValue()); |
| return null; |
| } |
| |
| @Override |
| public Void visit(DigitsFacet t, Restriction restriction) { |
| int fraction = t.getFraction(); |
| if (fraction > 0) { |
| restriction.setFractionDigits(fraction); |
| restriction.setTotalDigits(fraction + t.getInteger()); |
| } else if (fraction == 0) { |
| restriction.setTotalDigits(t.getInteger()); |
| } |
| return null; |
| } |
| |
| @Override |
| public Void visit(MaxFacet t, Restriction restriction) { |
| restriction.setMaxInclusive(String.valueOf(t.getValue())); |
| return null; |
| } |
| |
| @Override |
| public Void visit(MinFacet t, Restriction restriction) { |
| restriction.setMinInclusive(String.valueOf(t.getValue())); |
| return null; |
| } |
| |
| @Override |
| public Void visit(PatternFacet t, Restriction restriction) { |
| String regex = t.getRegexp(); |
| regex = introduceShorthands(regex); |
| restriction.addPattern(regex); |
| return null; |
| } |
| |
| @Override |
| public Void visit(PatternListFacet t, Restriction restriction) { |
| for (PatternFacet pf : t.getPatterns()) { |
| String regex = pf.getRegexp(); |
| regex = introduceShorthands(regex); |
| restriction.addPattern(regex); |
| } |
| return null; |
| } |
| |
| @Override |
| public Void visit(SizeFacet t, Restriction restriction) { |
| int minLength = t.getMin(); |
| int maxLength = t.getMax(); |
| if (minLength == maxLength) { |
| restriction.setLength(minLength); |
| } else { |
| // 0 is the default minBoundary. |
| if (minLength > 0) restriction.setMinLength(minLength); |
| // 2^31 is the default maxBoundary. |
| if (maxLength < Integer.MAX_VALUE) restriction.setMaxLength(maxLength); |
| } |
| return null; |
| } |
| }; |
| } |
| |
| /** |
| * Convenience method that adds an element to a given schema. |
| * |
| * @param element the Property that the Element will be based on |
| * @param compositor the sequence/choice/all that the Element will be added to |
| * @param schema the schema currently being built |
| */ |
| private void addElementToSchema(Element element, String elementURI, boolean isPositional, TypeDefParticle compositor, Schema schema, String packageName) { |
| String lookupNamespace = schema.getTargetNamespace(); |
| if (lookupNamespace == null) { |
| lookupNamespace = EMPTY_STRING; |
| } |
| NamespaceInfo namespaceInfo = getNamespaceInfoForNamespace(lookupNamespace, packageName); |
| boolean isElementFormQualified = false; |
| if (namespaceInfo != null) { |
| isElementFormQualified = namespaceInfo.isElementFormQualified(); |
| } |
| boolean addRef = shouldAddRefAndSetForm(element, elementURI, lookupNamespace, isElementFormQualified, true); |
| if(addRef){ |
| addElementRefToSchema(schema, compositor, element, elementURI); |
| } else { |
| // for positional mappings we could have multiple elements with same name; check before adding |
| if (elementExistsInParticle(element.getName(), element.getRef(), compositor) == null) { |
| if (isPositional) { |
| element.setMaxOccurs(Occurs.UNBOUNDED); |
| } |
| compositor.addElement(element); |
| } |
| } |
| } |
| |
| /** |
| * Convenience method that processes the XmlJoinNodes for a given Property and adds the |
| * appropriate components to the schema. |
| * |
| * @param property the Property contianing one or more XmlJoinNode entries |
| * @param compositor the sequence/choice/all that will be added to |
| * @param schema the schema currently being built |
| * @param type the complex type currently being built |
| */ |
| private void addXmlJoinNodesToSchema(Property property, TypeDefParticle compositor, Schema schema, ComplexType type) { |
| for (XmlJoinNode xmlJoinNode : property.getXmlJoinNodes().getXmlJoinNode()) { |
| // create the XPathFragment(s) for the path |
| Field xfld = new XMLField(xmlJoinNode.getXmlPath()); |
| xfld.setNamespaceResolver(schema.getNamespaceResolver()); |
| xfld.initialize(); |
| |
| // build the schema components for the xml-path |
| AddToSchemaResult asr = buildSchemaComponentsForXPath(xfld.getXPathFragment(), new AddToSchemaResult(compositor, schema), false, property); |
| |
| // process the last fragment |
| TypeDefParticle currentParticle = asr.particle; |
| Schema currentSchema = asr.schema; |
| if (currentParticle.getOwner() instanceof ComplexType) { |
| type = ((ComplexType) currentParticle.getOwner()); |
| } |
| // get a QName for the last part of the xpath - this will be used as the |
| // attribute/element name, and also to figure out if a ref is required |
| QName schemaName; |
| XPathFragment frag = xfld.getLastXPathFragment(); |
| boolean isAttribute = xmlJoinNode.getXmlPath().contains(ATT); |
| // for non-attributes, the last fragment may be 'text()' |
| if (!isAttribute) { |
| if (frag.nameIsText()) { |
| frag = xfld.getXPathFragment(); |
| while (frag.getNextFragment() != null && !frag.getNextFragment().nameIsText()) { |
| frag = frag.getNextFragment(); |
| } |
| } |
| } |
| schemaName = new QName(frag.getNamespaceURI(), frag.getLocalName()); |
| |
| // handle Element/Attribute |
| if (isAttribute) { |
| addAttributeToSchema(buildAttribute(schemaName, Constants.SCHEMA_PREFIX + COLON + Constants.ANY_SIMPLE_TYPE), schemaName, currentSchema, type); |
| } else { |
| addElementToSchema(buildElement(schemaName.getLocalPart(), Constants.SCHEMA_PREFIX + COLON + Constants.ANY_SIMPLE_TYPE, currentParticle instanceof All), schemaName.getNamespaceURI(), false, currentParticle, currentSchema, null); |
| } |
| } |
| } |
| |
| /** |
| * Return the type name for an Element based on a given property. |
| * |
| * @param property the Property that the type name will be based on |
| * @param schema the schema currently being built |
| * @param javaClass the given Property's 'actual' type |
| * @param sc the element being generated for the given Property |
| * @return a type name based on the given Property, or null if not obtainable |
| */ |
| private String getTypeNameForComponent(Property property, Schema schema, JavaClass javaClass, SimpleComponent sc, boolean isElement) { |
| String typeName = null; |
| if (property.isXmlId()) { |
| // handle user-set schema-type |
| if (property.getSchemaType() != null) { |
| typeName = getTypeName(property, property.getActualType(), schema); |
| } else { |
| // default to xsd:ID |
| typeName = Constants.SCHEMA_PREFIX + COLON + ID; |
| } |
| } else if (property.isXmlIdRef()) { |
| typeName = Constants.SCHEMA_PREFIX + COLON + IDREF; |
| } else { |
| TypeInfo info = typeInfo.get(javaClass.getQualifiedName()); |
| if (info != null) { |
| if (info.isComplexType()) { |
| typeName = info.getComplexType().getName(); |
| } else if (info.getSimpleType() != null) { |
| typeName = info.getSimpleType().getName(); |
| } else { |
| typeName = info.getSchemaTypeName(); |
| } |
| if (typeName == null) { |
| // need to add complex-type locally, or reference global element |
| if(isElement && info.hasRootElement() && info.getXmlRootElement().getName().equals(sc.getName())){ |
| String refName = info.getXmlRootElement().getName(); |
| (sc).setRef(refName); |
| }else{ |
| if (isElement && info.isComplexType()) { |
| ((Element)sc).setComplexType(info.getComplexType()); |
| } else { |
| sc.setSimpleType(info.getSimpleType()); |
| } |
| } |
| } else { |
| // check to see if we need to add an import |
| if (addImportIfRequired(schema, info.getSchema(), info.getClassNamespace())) { |
| String prefix = schema.getNamespaceResolver().resolveNamespaceURI(info.getClassNamespace()); |
| if (prefix != null && (!typeName.equals(EMPTY_STRING))) { |
| typeName = prefix + COLON + typeName; |
| } |
| } |
| } |
| } else if (!property.isMap()) { |
| typeName = getTypeName(property, javaClass, schema); |
| } |
| // may need to qualify the type |
| if (typeName != null && !typeName.contains(COLON)) { |
| String prefix; |
| if (info.getClassNamespace().equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)) { |
| prefix = Constants.SCHEMA_PREFIX; |
| } else { |
| prefix = getPrefixForNamespace(schema, info.getClassNamespace()); |
| } |
| if (prefix != null) { |
| typeName = prefix + COLON + typeName; |
| } |
| } |
| } |
| return typeName; |
| } |
| private static String introduceShorthands(String regex) { |
| return RegexMutator.mutate(regex); |
| } |
| |
| /** |
| * Maintains compatibility between Java Pattern Regex and XML Schema regex. |
| * Replaces Java regexes with their respective XML Regex Shorthands, where applicable. |
| * <p> |
| * Recognized are Java regexes for the following XML Shorthands, and their negations: |
| * <blockquote><pre>{@code |
| * \i - Matches any character that may be the first character of an XML name. |
| * "[_:A-Za-z]" |
| * \c - Matches any character that may occur after the first character in an XML name. |
| * "[-.0-9:A-Z_a-z]" |
| * \d - All digits. |
| * "\\p{Nd}" |
| * \w - Word character. |
| * "[\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]" |
| * \s - Whitespace character. |
| * "[\\u0009-\\u000D\\u0020\\u0085\\u00A0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]" |
| * \b, \B - Boundary definitions. |
| * "(?:(?<=[\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]) |
| * (?![\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]])| |
| * (?<![\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]) |
| * (?=[\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]))" |
| * "(?:(?<=[\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]) |
| * (?=[\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]])| |
| * (?<![\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]) |
| * (?![\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]))" |
| * \h - Horizontal whitespace character - Java does not support, changed in Java 8 though. |
| * "[\\u0009\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]" |
| * \v - Vertical whitespace character - Java translates the shorthand to \cK only, meaning changed in Java 8 though. |
| * "[\\u000A-\\u000D\\u0085\\u2028\\u2029]" |
| * \X - Extended grapheme cluster. |
| * "(?:(?:\\u000D\\u000A)|(?:[\\u0E40\\u0E41\\u0E42\\u0E43\\u0E44\\u0EC0\\u0EC1\\u0EC2\\u0EC3\\u0EC4\\uAAB5 |
| * \\uAAB6\\uAAB9\\uAABB\\uAABC]*(?:[\\u1100-\\u115F\\uA960-\\uA97C]+|([\\u1100-\\u115F\\uA960-\\uA97C]*( |
| * (?:[[\\u1160-\\u11A2\\uD7B0-\\uD7C6][\\uAC00\\uAC1C\\uAC38]][\\u1160-\\u11A2\\uD7B0-\\uD7C6]*|[\\uAC01 |
| * \\uAC02\\uAC03\\uAC04])[\\u11A8-\\u11F9\\uD7CB-\\uD7FB]*))|[\\u11A8-\\u11F9\\uD7CB-\\uD7FB]+|[^[\\p{Zl} |
| * \\p{Zp}\\p{Cc}\\p{Cf}&&[^\\u000D\\u000A\\u200C\\u200D]]\\u000D\\u000A])[[\\p{Mn}\\p{Me}\\u200C\\u200D\\ |
| * u0488\\u0489\\u20DD\\u20DE\\u20DF\\u20E0\\u20E2\\u20E3\\u20E4\\uA670\\uA671\\uA672\\uFF9E\\uFF9F][\\p{Mc |
| * }\\u0E30\\u0E32\\u0E33\\u0E45\\u0EB0\\u0EB2\\u0EB3]]*)|(?s:.))" |
| * \R - Carriage return. |
| * "(?:(?>\\u000D\\u000A)|[\\u000A\\u000B\\u000C\\u000D\\u0085\\u2028\\u2029])" |
| * }</pre></blockquote> |
| * |
| * CAUTION - ORDER SENSITIVE: Longer patterns should come first, because they may contain one of the shorter pattern. |
| * <p> |
| * Changes to this class should also be reflected in the opposite {@link org.eclipse.persistence.jaxb.plugins.BeanValidationPlugin.RegexMutator RegexMutator} class within XJC BeanValidation Plugin. |
| * |
| * @see <a href="http://stackoverflow.com/questions/4304928/unicode-equivalents-for-w-and-b-in-java-regular-expressions">tchrist's work</a> |
| * @see <a href="http://www.regular-expressions.info/shorthand.html#xml">Special shorthands in XML Schema.</a> |
| */ |
| private static final class RegexMutator { |
| private static final Map<Pattern, String> shorthandReplacements = new LinkedHashMap<Pattern, String>(32) {{ |
| put(Pattern.compile("[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD]" , Pattern.LITERAL),"\\\\i"); |
| put(Pattern.compile("[^:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD]" , Pattern.LITERAL),"\\\\I"); |
| put(Pattern.compile("[-.0-9:A-Z_a-z\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F\\u2040\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD]" , Pattern.LITERAL),"\\\\c"); |
| put(Pattern.compile("[^-.0-9:A-Z_a-z\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F\\u2040\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD]" , Pattern.LITERAL),"\\\\C"); |
| put(Pattern.compile("[\\u0009-\\u000D\\u0020\\u0085\\u00A0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]" , Pattern.LITERAL),"\\\\s"); |
| put(Pattern.compile("[^\\u0009-\\u000D\\u0020\\u0085\\u00A0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]" , Pattern.LITERAL),"\\\\S"); |
| put(Pattern.compile("[\\u000A-\\u000D\\u0085\\u2028\\u2029]" , Pattern.LITERAL),"\\\\v"); |
| put(Pattern.compile("[^\\u000A-\\u000D\\u0085\\u2028\\u2029]" , Pattern.LITERAL),"\\\\V"); |
| put(Pattern.compile("[\\u0009\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]" , Pattern.LITERAL),"\\\\h"); |
| put(Pattern.compile("[^\\u0009\\u0020\\u00A0\\u1680\\u180E\\u2000\\u2001-\\u200A\\u202F\\u205F\\u3000]" , Pattern.LITERAL),"\\\\H"); |
| put(Pattern.compile("[\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]" , Pattern.LITERAL),"\\\\w"); |
| put(Pattern.compile("[^\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]" , Pattern.LITERAL),"\\\\W"); |
| put(Pattern.compile("(?:(?<=[\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]])(?![\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]])|(?<![\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]])(?=[\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]))", Pattern.LITERAL),"\\\\b"); |
| put(Pattern.compile("(?:(?<=[\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]])(?=[\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]])|(?<![\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]])(?![\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]]))", Pattern.LITERAL),"\\\\B"); |
| put(Pattern.compile("\\p{Nd}" , Pattern.LITERAL),"\\\\d"); |
| put(Pattern.compile("\\P{Nd}" , Pattern.LITERAL),"\\\\D"); |
| put(Pattern.compile("(?:(?>\\u000D\\u000A)|[\\u000A\\u000B\\u000C\\u000D\\u0085\\u2028\\u2029])" , Pattern.LITERAL),"\\\\R"); |
| put(Pattern.compile("(?:(?:\\u000D\\u000A)|(?:[\\u0E40\\u0E41\\u0E42\\u0E43\\u0E44\\u0EC0\\u0EC1\\u0EC2\\u0EC3\\u0EC4\\uAAB5\\uAAB6\\uAAB9\\uAABB\\uAABC]*(?:[\\u1100-\\u115F\\uA960-\\uA97C]+|([\\u1100-\\u115F\\uA960-\\uA97C]*((?:[[\\u1160-\\u11A2\\uD7B0-\\uD7C6][\\uAC00\\uAC1C\\uAC38]][\\u1160-\\u11A2\\uD7B0-\\uD7C6]*|[\\uAC01\\uAC02\\uAC03\\uAC04])[\\u11A8-\\u11F9\\uD7CB-\\uD7FB]*))|[\\u11A8-\\u11F9\\uD7CB-\\uD7FB]+|[^[\\p{Zl}\\p{Zp}\\p{Cc}\\p{Cf}&&[^\\u000D\\u000A\\u200C\\u200D]]\\u000D\\u000A])[[\\p{Mn}\\p{Me}\\u200C\\u200D\\u0488\\u0489\\u20DD\\u20DE\\u20DF\\u20E0\\u20E2\\u20E3\\u20E4\\uA670\\uA671\\uA672\\uFF9E\\uFF9F][\\p{Mc}\\u0E30\\u0E32\\u0E33\\u0E45\\u0EB0\\u0EB2\\u0EB3]]*)|(?s:.))" , Pattern.LITERAL),"\\\\X"); |
| put(Pattern.compile("[_:A-Za-z]" , Pattern.LITERAL),"\\\\i"); // ascii only |
| put(Pattern.compile("[^:A-Z_a-z]" , Pattern.LITERAL),"\\\\I"); // ascii only |
| put(Pattern.compile("[-.0-9:A-Z_a-z]" , Pattern.LITERAL),"\\\\c"); // ascii only |
| put(Pattern.compile("[^-.0-9:A-Z_a-z]" , Pattern.LITERAL),"\\\\C"); // ascii only |
| }}; |
| |
| private RegexMutator() { |
| } |
| |
| /** |
| * @param input Java regex |
| * @return XML regex |
| */ |
| private static String mutate(String input){ |
| for (Map.Entry<Pattern, String> entry : shorthandReplacements.entrySet()) { |
| Matcher m = entry.getKey().matcher(input); |
| input = m.replaceAll(entry.getValue()); |
| } |
| return input; |
| } |
| } |
| } |