/*
* Copyright (c) 2010-2016 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.prism.schema;
import static com.evolveum.midpoint.prism.PrismConstants.*;
import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import com.evolveum.midpoint.prism.*;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import com.evolveum.midpoint.prism.xml.DynamicNamespacePrefixMapper;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.prism.xml.XsdTypeMapper;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.sun.xml.xsom.XSParticle;
/**
* Takes a midPoint Schema definition and produces a XSD schema (in a DOM form).
*
* Great pains were taken to make sure that the output XML is "nice" and human readable.
* E.g. the namespace prefixes are unified using the definitions in SchemaRegistry.
* Please do not ruin this if you would update this class.
*
* Single use class. Not thread safe. Create new instance for each run.
*
* @author lazyman
* @author Radovan Semancik
*/
public class SchemaToDomProcessor {
private static final Trace LOGGER = TraceManager.getTrace(SchemaToDomProcessor.class);
private static final String MAX_OCCURS_UNBOUNDED = "unbounded";
private boolean attributeQualified = false;
private PrismContext prismContext;
private DynamicNamespacePrefixMapper namespacePrefixMapper;
private PrismSchema schema;
private Element rootXsdElement;
private Set<String> importNamespaces;
private Document document;
SchemaToDomProcessor() {
importNamespaces = new HashSet<String>();
}
public PrismContext getPrismContext() {
return prismContext;
}
public void setPrismContext(PrismContext prismContext) {
this.prismContext = prismContext;
}
void setAttributeQualified(boolean attributeQualified) {
this.attributeQualified = attributeQualified;
}
public DynamicNamespacePrefixMapper getNamespacePrefixMapper() {
return namespacePrefixMapper;
}
public void setNamespacePrefixMapper(DynamicNamespacePrefixMapper namespacePrefixMapper) {
this.namespacePrefixMapper = namespacePrefixMapper;
}
private SchemaDefinitionFactory getDefinitionFactory() {
return ((PrismContextImpl) prismContext).getDefinitionFactory();
}
private String getNamespace() {
return schema.getNamespace();
}
private boolean isMyNamespace(QName qname) {
return getNamespace().equals(qname.getNamespaceURI());
}
/**
* Main entry point.
*
* @param schema midPoint schema
* @return XSD schema in DOM form
* @throws SchemaException error parsing the midPoint schema or converting values
*/
@NotNull
Document parseSchema(PrismSchema schema) throws SchemaException {
if (schema == null) {
throw new IllegalArgumentException("Schema can't be null.");
}
this.schema = schema;
try {
init(); // here the document is initialized
// Process complex types first.
Collection<ComplexTypeDefinition> complexTypes = schema.getDefinitions(ComplexTypeDefinition.class);
for (ComplexTypeDefinition complexTypeDefinition: complexTypes) {
addComplexTypeDefinition(complexTypeDefinition, document.getDocumentElement());
}
Collection<Definition> definitions = schema.getDefinitions();
for (Definition definition : definitions) {
if (definition instanceof PrismContainerDefinition) {
// Add property container definition. This will add <complexType> and <element> definitions to XSD
addContainerDefinition((PrismContainerDefinition) definition,
document.getDocumentElement(), document.getDocumentElement());
} else if (definition instanceof PrismPropertyDefinition) {
// Add top-level property definition. It will create <element> XSD definition
addPropertyDefinition((PrismPropertyDefinition) definition,
document.getDocumentElement());
} else if (definition instanceof ComplexTypeDefinition){
// Skip this. Already processed above.
} else {
throw new IllegalArgumentException("Encountered unsupported definition in schema: "
+ definition);
}
// TODO: process unprocessed ComplexTypeDefinitions
}
// Add import definition. These were accumulated during previous processing.
addImports();
} catch (Exception ex) {
throw new SchemaException("Couldn't parse schema, reason: " + ex.getMessage(), ex);
}
return document;
}
/**
* Adds XSD definitions from PropertyContainerDefinition. This is complexType and element.
* If the property container is an ResourceObjectDefinition, it will add only annotated
* complexType definition.
*
* @param definition PropertyContainerDefinition to process
* @param parent element under which the XSD definition will be added
*/
private void addContainerDefinition(PrismContainerDefinition definition,
Element elementParent, Element complexTypeParent) {
ComplexTypeDefinition complexTypeDefinition = definition.getComplexTypeDefinition();
if (complexTypeDefinition != null &&
// Check if the complex type is a top-level complex type. If it is then it was already processed and we can skip it
schema.findComplexTypeDefinition(complexTypeDefinition.getTypeName()) == null &&
// If the definition is not in this schema namespace then skip it. It is only a "ref"
getNamespace().equals(complexTypeDefinition.getTypeName().getNamespaceURI())
) {
addComplexTypeDefinition(complexTypeDefinition,complexTypeParent);
}
Element elementElement = addElementDefinition(definition.getName(), definition.getTypeName(), definition.getMinOccurs(), definition.getMaxOccurs(),
elementParent);
if (complexTypeDefinition == null || !complexTypeDefinition.isContainerMarker()) {
// Need to add a:container annotation to the element as the complex type does not have it
addAnnotationToDefinition(elementElement, A_PROPERTY_CONTAINER);
}
}
/**
* Adds XSD element definition created from the midPoint PropertyDefinition.
* @param definition midPoint PropertyDefinition
* @param parent element under which the definition will be added
*/
private void addPropertyDefinition(PrismPropertyDefinition definition, Element parent) {
Element property = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "element"));
// Add to document first, so following methods will be able to resolve namespaces
parent.appendChild(property);
String attrNamespace = definition.getName().getNamespaceURI();
if (attrNamespace != null && attrNamespace.equals(getNamespace())) {
setAttribute(property, "name", definition.getName().getLocalPart());
setQNameAttribute(property, "type", definition.getTypeName());
} else {
setQNameAttribute(property, "ref", definition.getName());
}
if (definition.getMinOccurs() != 1) {
setAttribute(property, "minOccurs", Integer.toString(definition.getMinOccurs()));
}
if (definition.getMaxOccurs() != 1) {
String maxOccurs = definition.getMaxOccurs() == XSParticle.UNBOUNDED ? MAX_OCCURS_UNBOUNDED
: Integer.toString(definition.getMaxOccurs());
setAttribute(property, "maxOccurs", maxOccurs);
}
Element annotation = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "annotation"));
property.appendChild(annotation);
Element appinfo = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "appinfo"));
annotation.appendChild(appinfo);
addCommonDefinitionAnnotations(definition, appinfo);
if (!definition.canAdd() || !definition.canRead() || !definition.canModify()) {
// read-write-create attribute is the default. If any of this flags is missing, we must
// add appropriate annotations.
if (definition.canAdd()) {
addAnnotation(A_ACCESS, A_ACCESS_CREATE, appinfo);
}
if (definition.canRead()) {
addAnnotation(A_ACCESS, A_ACCESS_READ, appinfo);
}
if (definition.canModify()) {
addAnnotation(A_ACCESS, A_ACCESS_UPDATE, appinfo);
}
}
if (definition.isIndexed() != null) {
addAnnotation(A_INDEXED, XmlTypeConverter.toXmlTextContent(definition.isIndexed(), A_INDEXED), appinfo);
}
if (definition.getMatchingRuleQName() != null) {
addAnnotation(A_MATCHING_RULE, definition.getMatchingRuleQName(), appinfo);
}
if (definition.getValueEnumerationRef() != null) {
addAnnotation(A_VALUE_ENUMERATION_REF, definition.getValueEnumerationRef(), appinfo);
}
SchemaDefinitionFactory definitionFactory = getDefinitionFactory();
definitionFactory.addExtraPropertyAnnotations(definition, appinfo, this);
if (!appinfo.hasChildNodes()) {
// remove unneeded <annotation> element
property.removeChild(annotation);
}
}
/**
* Adds XSD element definition created from the PrismReferenceDefinition.
* TODO: need to finish
*/
private void addReferenceDefinition(PrismReferenceDefinition definition, Element parent) {
Element property = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "element"));
// Add to document first, so following methods will be able to resolve namespaces
parent.appendChild(property);
String attrNamespace = definition.getName().getNamespaceURI();
if (attrNamespace != null && attrNamespace.equals(getNamespace())) {
setAttribute(property, "name", definition.getName().getLocalPart());
setQNameAttribute(property, "type", definition.getTypeName());
} else {
setQNameAttribute(property, "ref", definition.getName());
}
if (definition.getCompositeObjectElementName() == null) {
setMultiplicityAttribute(property, "minOccurs", 0);
}
setMultiplicityAttribute(property, "maxOccurs", definition.getMaxOccurs());
// Add annotations
Element annotation = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "annotation"));
property.appendChild(annotation);
Element appinfo = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "appinfo"));
annotation.appendChild(appinfo);
addAnnotation(A_OBJECT_REFERENCE, appinfo);
if (definition.getTargetTypeName() != null) {
addAnnotation(A_OBJECT_REFERENCE_TARGET_TYPE, definition.getTargetTypeName(), appinfo);
}
if (definition.getCompositeObjectElementName() == null) {
return;
}
property = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "element"));
// Add to document first, so following methods will be able to resolve namespaces
parent.appendChild(property);
QName elementName = definition.getCompositeObjectElementName();
attrNamespace = elementName.getNamespaceURI();
if (attrNamespace != null && attrNamespace.equals(getNamespace())) {
setAttribute(property, "name", elementName.getLocalPart());
setQNameAttribute(property, "type", definition.getTargetTypeName());
} else {
setQNameAttribute(property, "ref", elementName);
}
setMultiplicityAttribute(property, "minOccurs", 0);
setMultiplicityAttribute(property, "maxOccurs", definition.getMaxOccurs());
// Add annotations
annotation = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "annotation"));
property.appendChild(annotation);
appinfo = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "appinfo"));
annotation.appendChild(appinfo);
addAnnotation(A_OBJECT_REFERENCE, definition.getName(), appinfo);
if (definition.isComposite()) {
addAnnotation(A_COMPOSITE, definition.isComposite(), appinfo);
}
SchemaDefinitionFactory definitionFactory = getDefinitionFactory();
definitionFactory.addExtraReferenceAnnotations(definition, appinfo, this);
}
/**
* Adds XSD element definition.
* @param name element QName
* @param typeName element type QName
* @param parent element under which the definition will be added
*/
private Element addElementDefinition(QName name, QName typeName, int minOccurs, int maxOccurs, Element parent) {
Element elementDef = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "element"));
parent.appendChild(elementDef);
if (isMyNamespace(name)) {
setAttribute(elementDef, "name", name.getLocalPart());
if (typeName.equals(DOMUtil.XSD_ANY)) {
addSequenceXsdAnyDefinition(elementDef);
} else {
setQNameAttribute(elementDef, "type", typeName);
}
} else {
// Need to create "ref" instead of "name"
setAttribute(elementDef, "ref", name);
if (typeName != null) {
// Type cannot be stored directly, XSD does not allow it with "ref"s.
addAnnotationToDefinition(elementDef, A_TYPE, typeName);
}
}
setMultiplicityAttribute(elementDef, "minOccurs", minOccurs);
setMultiplicityAttribute(elementDef, "maxOccurs", maxOccurs);
return elementDef;
}
private void addSequenceXsdAnyDefinition(Element elementDef) {
Element complexContextElement = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "complexType"));
elementDef.appendChild(complexContextElement);
Element sequenceElement = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "sequence"));
complexContextElement.appendChild(sequenceElement);
addXsdAnyDefinition(sequenceElement);
}
private void addXsdAnyDefinition(Element elementDef) {
Element anyElement = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "any"));
elementDef.appendChild(anyElement);
setAttribute(anyElement, "namespace", "##other");
setAttribute(anyElement, "minOccurs", "0");
setAttribute(anyElement, "maxOccurs", "unbounded");
setAttribute(anyElement, "processContents", "lax");
}
/**
* Adds XSD complexType definition from the midPoint Schema ComplexTypeDefinion object
* @param definition midPoint Schema ComplexTypeDefinion object
* @param parent element under which the definition will be added
* @return created (and added) XSD complexType definition
*/
private Element addComplexTypeDefinition(ComplexTypeDefinition definition,
Element parent) {
if (definition == null) {
// Nothing to do
return null;
}
if (definition.getTypeName() == null) {
throw new UnsupportedOperationException("Anonymous complex types as containers are not supported yet");
}
Element complexType = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "complexType"));
parent.appendChild(complexType);
// "typeName" should be used instead of "name" when defining a XSD type
setAttribute(complexType, "name", definition.getTypeName().getLocalPart());
Element annotation = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "annotation"));
complexType.appendChild(annotation);
Element containingElement = complexType;
if (definition.getSuperType() != null) {
Element complexContent = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "complexContent"));
complexType.appendChild(complexContent);
Element extension = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "extension"));
complexContent.appendChild(extension);
setQNameAttribute(extension, "base", definition.getSuperType());
containingElement = extension;
}
Element sequence = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "sequence"));
containingElement.appendChild(sequence);
Collection<? extends ItemDefinition> definitions = definition.getDefinitions();
for (ItemDefinition def : definitions) {
if (def instanceof PrismPropertyDefinition) {
addPropertyDefinition((PrismPropertyDefinition) def, sequence);
} else if (def instanceof PrismContainerDefinition) {
PrismContainerDefinition contDef = (PrismContainerDefinition)def;
addContainerDefinition(contDef, sequence, parent);
} else if (def instanceof PrismReferenceDefinition) {
addReferenceDefinition((PrismReferenceDefinition) def, sequence);
} else {
throw new IllegalArgumentException("Uknown definition "+def+"("+def.getClass().getName()+") in complex type definition "+def);
}
}
if (definition.isXsdAnyMarker()) {
addXsdAnyDefinition(sequence);
}
Element appinfo = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "appinfo"));
annotation.appendChild(appinfo);
if (definition.isObjectMarker()) {
// annotation: propertyContainer
addAnnotation(A_OBJECT, definition.getDisplayName(), appinfo);
} else if (definition.isContainerMarker()) {
// annotation: propertyContainer
addAnnotation(A_PROPERTY_CONTAINER, definition.getDisplayName(), appinfo);
}
addCommonDefinitionAnnotations(definition, appinfo);
SchemaDefinitionFactory definitionFactory = getDefinitionFactory();
definitionFactory.addExtraComplexTypeAnnotations(definition, appinfo, this);
if (!appinfo.hasChildNodes()) {
// remove unneeded <annotation> element
complexType.removeChild(annotation);
}
return complexType;
}
private void addCommonDefinitionAnnotations(Definition definition, Element appinfoElement) {
if (definition.isIgnored()) {
addAnnotation(A_IGNORE, "true", appinfoElement);
}
if ((definition instanceof ItemDefinition) && ((ItemDefinition)definition).isOperational()) {
addAnnotation(A_OPERATIONAL, "true", appinfoElement);
}
if (definition.getDisplayName() != null) {
addAnnotation(A_DISPLAY_NAME, definition.getDisplayName(), appinfoElement);
}
if (definition.getDisplayOrder() != null) {
addAnnotation(A_DISPLAY_ORDER, definition.getDisplayOrder().toString(), appinfoElement);
}
if (definition.getHelp() != null) {
addAnnotation(A_HELP, definition.getHelp(), appinfoElement);
}
if (definition.isEmphasized()) {
addAnnotation(A_EMPHASIZED, "true", appinfoElement);
}
}
/**
* Add generic annotation element.
* @param qname QName of the element
* @param value string value of the element
* @param parent element under which the definition will be added
* @return created XSD element
*/
public Element addAnnotation(QName qname, String value, Element parent) {
Element annotation = createElement(qname);
parent.appendChild(annotation);
if (value != null) {
annotation.setTextContent(value);
}
return annotation;
}
public Element addAnnotation(QName qname, boolean value, Element parent) {
Element annotation = createElement(qname);
parent.appendChild(annotation);
annotation.setTextContent(Boolean.toString(value));
return annotation;
}
public Element addAnnotation(QName qname, Element parent) {
Element annotation = createElement(qname);
parent.appendChild(annotation);
return annotation;
}
public Element addAnnotation(QName qname, QName value, Element parent) {
Element annotation = createElement(qname);
parent.appendChild(annotation);
if (value != null) {
DOMUtil.setQNameValue(annotation, value);
}
return annotation;
}
public Element addAnnotation(QName qname, PrismReferenceValue value, Element parent) {
Element annotation = createElement(qname);
parent.appendChild(annotation);
if (value != null) {
annotation.setAttribute(ATTRIBUTE_OID_LOCAL_NAME, value.getOid());
DOMUtil.setQNameAttribute(annotation, ATTRIBUTE_REF_TYPE_LOCAL_NAME, value.getTargetType());
}
return annotation;
}
private void addAnnotationToDefinition(Element definitionElement, QName qname) {
addAnnotationToDefinition(definitionElement, qname, null);
}
private void addAnnotationToDefinition(Element definitionElement, QName qname, QName value) {
Element annotationElement = getOrCreateElement(new QName(W3C_XML_SCHEMA_NS_URI, "annotation"), definitionElement);
Element appinfoElement = getOrCreateElement(new QName(W3C_XML_SCHEMA_NS_URI, "appinfo"), annotationElement);
if (value == null) {
addAnnotation(qname, appinfoElement);
} else {
addAnnotation(qname, value, appinfoElement);
}
}
private Element getOrCreateElement(QName qName, Element parentElement) {
NodeList elements = parentElement.getElementsByTagNameNS(qName.getNamespaceURI(), qName.getLocalPart());
if (elements.getLength() == 0) {
Element element = createElement(qName);
Element refChild = DOMUtil.getFirstChildElement(parentElement);
parentElement.insertBefore(element, refChild);
return element;
}
return (Element)elements.item(0);
}
/**
* Adds annotation that points to another element (ususaly a property).
* @param qname QName of the element
* @param value Qname of the target element (property QName)
* @param parent parent element under which the definition will be added
* @return created XSD element
*/
public Element addRefAnnotation(QName qname, QName value, Element parent) {
Element element = createElement(qname);
parent.appendChild(element);
//old way: setQNameAttribute(access, "ref", value);
DOMUtil.setQNameValue(element, value);
return element;
}
/**
* Create schema XSD DOM document.
*/
private void init() throws ParserConfigurationException {
if (namespacePrefixMapper == null) {
// TODO: clone?
namespacePrefixMapper = ((SchemaRegistryImpl) prismContext.getSchemaRegistry()).getNamespacePrefixMapper();
}
// We don't want the "tns" prefix to be kept in the mapper
namespacePrefixMapper = namespacePrefixMapper.clone();
namespacePrefixMapper.registerPrefixLocal(getNamespace(), "tns");
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Using namespace prefix mapper to serialize schema:\n{}",DebugUtil.dump(namespacePrefixMapper));
}
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(false);
DocumentBuilder db = dbf.newDocumentBuilder();
document = db.newDocument();
Element root = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "schema"));
document.appendChild(root);
rootXsdElement = document.getDocumentElement();
setAttribute(rootXsdElement, "targetNamespace", getNamespace());
setAttribute(rootXsdElement, "elementFormDefault", "qualified");
DOMUtil.setNamespaceDeclaration(rootXsdElement, "tns", getNamespace());
if (attributeQualified) {
setAttribute(rootXsdElement, "attributeFormDefault", "qualified");
}
}
/**
* Create DOM document with a root element.
*/
private Document createDocument(QName name) throws ParserConfigurationException {
return document;
}
/**
* Create XML element with the correct namespace prefix and namespace definition.
* @param qname element QName
* @return created DOM element
*/
public Element createElement(QName qname) {
QName qnameWithPrefix = namespacePrefixMapper.setQNamePrefix(qname);
addToImport(qname.getNamespaceURI());
if (rootXsdElement!=null) {
return DOMUtil.createElement(document, qnameWithPrefix, rootXsdElement, rootXsdElement);
} else {
// This is needed otherwise the root element itself could not be created
return DOMUtil.createElement(document, qnameWithPrefix);
}
}
/**
* Set attribute in the DOM element to a string value.
* @param element element where to set attribute
* @param attrName attribute name (String)
* @param attrValue attribute value (String)
*/
private void setAttribute(Element element, String attrName, String attrValue) {
setAttribute(element, new QName(W3C_XML_SCHEMA_NS_URI, attrName), attrValue);
}
private void setAttribute(Element element, String attrName, QName attrValue) {
setAttribute(element, new QName(W3C_XML_SCHEMA_NS_URI, attrName), attrValue);
}
private void setMultiplicityAttribute(Element element, String attrName, int attrValue) {
if (attrValue == 1) {
return;
}
setAttribute(element, attrName, XsdTypeMapper.multiplicityToString(attrValue));
}
/**
* Set attribute in the DOM element to a string value.
* @param element element element where to set attribute
* @param attr attribute name (QName)
* @param attrValue attribute value (String)
*/
private void setAttribute(Element element, QName attr, String attrValue) {
if (attributeQualified) {
element.setAttributeNS(attr.getNamespaceURI(), attr.getLocalPart(), attrValue);
addToImport(attr.getNamespaceURI());
} else {
element.setAttribute(attr.getLocalPart(), attrValue);
}
}
private void setAttribute(Element element, QName attr, QName attrValue) {
if (attributeQualified) {
DOMUtil.setQNameAttribute(element, attr, attrValue, rootXsdElement);
addToImport(attr.getNamespaceURI());
} else {
DOMUtil.setQNameAttribute(element, attr.getLocalPart(), attrValue, rootXsdElement);
}
}
/**
* Set attribute in the DOM element to a QName value. This will make sure that the
* appropriate namespace definition for the QName exists.
*
* @param element element element element where to set attribute
* @param attrName attribute name (String)
* @param value attribute value (Qname)
*/
private void setQNameAttribute(Element element, String attrName, QName value) {
QName valueWithPrefix = namespacePrefixMapper.setQNamePrefix(value);
DOMUtil.setQNameAttribute(element, attrName, valueWithPrefix, rootXsdElement);
addToImport(value.getNamespaceURI());
}
/**
* Make sure that the namespace will be added to import definitions.
* @param namespace namespace to import
*/
private void addToImport(String namespace) {
if (!importNamespaces.contains(namespace)) {
importNamespaces.add(namespace);
}
}
/**
* Adds import definition to XSD.
* It adds imports of namespaces that accumulated during schema processing in the importNamespaces list.
* @param schema
*/
private void addImports() {
for (String namespace : importNamespaces) {
if (W3C_XML_SCHEMA_NS_URI.equals(namespace)) {
continue;
}
if (getNamespace().equals(namespace)) {
//we don't want to import target namespace
continue;
}
rootXsdElement.insertBefore(createImport(namespace), rootXsdElement.getFirstChild());
}
}
/**
* Create single import XSD element.
*/
private Element createImport(String namespace) {
Element element = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "import"));
setAttribute(element, "namespace", namespace);
return element;
}
}