blob: 78164fedf6bfb40a8e437fc2a253fecd5c0cb918 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// 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.XMLConversionManager;
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<>(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.
*
*/
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 = 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.
*
*/
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.
*
*/
private Schema getSchemaForNamespace(String namespace, String packageName) {
if (schemaForNamespace == null) {
schemaForNamespace = new HashMap<>();
allSchemas = new ArrayList<>();
}
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<Namespace> namespaces = namespaceInfo.getNamespaceResolver().getNamespaces();
for (int i = 0; i < namespaces.size(); i++) {
Namespace nextNamespace = 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<>();
}
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<String> keys = namespaceResolver.getPrefixes();
while (keys.hasMoreElements()) {
String next = 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.
*
*/
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 = 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.
*
*/
public Map<String, SchemaTypeInfo> getSchemaTypeInfo() {
return this.schemaTypeInfo;
}
private boolean importExists(Schema schema, String importNsURI, String schemaLocation) {
java.util.List<Import> imports = schema.getImports();
for (int i = 0; i < imports.size(); i++) {
Import nextImport = 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.
*
*/
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.
*
*/
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.
*
*/
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.
*
*/
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.
*
*/
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 = 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 = 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
*/
protected Element elementExistsInParticle(String elementName, String refString, TypeDefParticle particle) {
if (particle == null || particle.getElements() == null || particle.getElements().size() == 0) {
return null;
}
java.util.List<Element> existingElements = particle.getElements();
if (existingElements != null) {
Iterator<Element> 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 = 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.
*
*/
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
*/
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.
*
*/
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.
*
*/
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<XMLConversionManager, NamespaceResolver> 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
*/
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
*/
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
*/
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
*/
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<>() {
@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<XMLConversionManager, NamespaceResolver> 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<>(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;
}
}
}