/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.isis.core.runtime.snapshot; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** * Stateless utility methods relating to the w3.org schema and schema-instance * meta models. */ public final class XsMetaModel { private final Helper helper; /** * URI representing the namespace of the in-built xmlns namespace as defined * by w3.org. * * The NamespaceManager will not allow any namespaces with this URI to be * added. */ public static final String W3_ORG_XMLNS_URI = "http://www.w3.org/2000/xmlns/"; /** * Namespace prefix for {@link W3_ORG_XMLNS_URI}. * * The NamespaceManager will not allow any namespace to use this prefix. */ public static final String W3_ORG_XMLNS_PREFIX = "xmlns"; /** * Namespace prefix for XML schema. */ public static final String W3_ORG_XS_URI = "http://www.w3.org/2001/XMLSchema"; /** * Namespace prefix for {@link W3_ORG_XS_URI}. * * The NamespaceManager will not allow any namespace to use this prefix. */ public static final String W3_ORG_XS_PREFIX = "xs"; /** * Namespace prefix for XML schema instance. */ public static final String W3_ORG_XSI_URI = "http://www.w3.org/2001/XMLSchema-instance"; /** * Namespace prefix for {@link W3_ORG_XSI_URI}. * * The NamespaceManager will not allow any namespace to use this prefix. */ public static final String W3_ORG_XSI_PREFIX = "xsi"; private final IsisSchema isisMeta; public XsMetaModel() { this.helper = new Helper(); this.isisMeta = new IsisSchema(); } /** * Creates an <xs:schema> element for the document to the provided * element, attaching to root of supplied Xsd doc. * * In addition: * <ul> * <li>the elementFormDefault is set * <li>the NOF namespace is set * <li>the <code>xs:import</code> element referencing the NOF namespace is * added as a child * </ul> */ Element createXsSchemaElement(final Document xsdDoc) { if (xsdDoc.getDocumentElement() != null) { throw new IllegalArgumentException("XSD document already has content"); } final Element xsSchemaElement = createXsElement(xsdDoc, "schema"); xsSchemaElement.setAttribute("elementFormDefault", "qualified"); isisMeta.addNamespace(xsSchemaElement); xsdDoc.appendChild(xsSchemaElement); final Element xsImportElement = createXsElement(xsdDoc, "import"); xsImportElement.setAttribute("namespace", IsisSchema.NS_URI); xsImportElement.setAttribute("schemaLocation", IsisSchema.DEFAULT_LOCATION); xsSchemaElement.appendChild(xsImportElement); return xsSchemaElement; } Element createXsElementElement(final Document xsdDoc, final String className) { return createXsElementElement(xsdDoc, className, true); } Element createXsElementElement(final Document xsdDoc, final String className, final boolean includeCardinality) { final Element xsElementElement = createXsElement(xsdDoc, "element"); xsElementElement.setAttribute("name", className); if (includeCardinality) { setXsCardinality(xsElementElement, 0, Integer.MAX_VALUE); } return xsElementElement; } /** * Creates an element in the XS namespace, adding the definition of the * namespace to the root element of the document if required, */ Element createXsElement(final Document xsdDoc, final String localName) { final Element element = xsdDoc.createElementNS(XsMetaModel.W3_ORG_XS_URI, XsMetaModel.W3_ORG_XS_PREFIX + ":" + localName); // xmlns:xs="..." added to root helper.rootElementFor(element).setAttributeNS(XsMetaModel.W3_ORG_XMLNS_URI, XsMetaModel.W3_ORG_XMLNS_PREFIX + ":" + XsMetaModel.W3_ORG_XS_PREFIX, XsMetaModel.W3_ORG_XS_URI); return element; } // private Element addAnyToSequence(final Element xsSequenceElement) { // Element xsAnyElement = createXsElement(docFor(xsSequenceElement), "any"); // xsAnyElement.setAttribute("namespace", "##other"); // xsAnyElement.setAttribute("minOccurs", "0"); // xsAnyElement.setAttribute("maxOccurs", "unbounded"); // xsAnyElement.setAttribute("processContents", "lax"); // xsSequenceElement.appendChild(xsAnyElement); // return xsSequenceElement; // } /** * Creates an xs:attribute ref="isis:xxx" element, and appends to specified * owning element. */ Element addXsIsisAttribute(final Element parentXsElement, final String isisAttributeRef) { return addXsIsisAttribute(parentXsElement, isisAttributeRef, null); } /** * Adds <code>xs:attribute ref="isis:xxx" fixed="yyy"</code> element, and * appends to specified parent XSD element. */ Element addXsIsisAttribute(final Element parentXsElement, final String isisAttributeRef, final String fixedValue) { return addXsIsisAttribute(parentXsElement, isisAttributeRef, fixedValue, true); } /** * Adds <code>xs:attribute ref="isis:xxx" default="yyy"</code> element, and * appends to specified parent XSD element. * * The last parameter determines whether to use <code>fixed="yyy"</code> * rather than <code>default="yyy"</code>. */ Element addXsIsisAttribute(final Element parentXsElement, final String isisAttributeRef, final String value, final boolean useFixed) { final Element xsIsisAttributeElement = createXsElement(helper.docFor(parentXsElement), "attribute"); xsIsisAttributeElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":" + isisAttributeRef); parentXsElement.appendChild(xsIsisAttributeElement); if (value != null) { if (useFixed) { xsIsisAttributeElement.setAttribute("fixed", value); } else { xsIsisAttributeElement.setAttribute("default", value); } } return parentXsElement; } /** * Adds <code>xs:attribute ref="isis:feature" fixed="(feature)"</code> * element as child to supplied XSD element, presumed to be an * <xs:complexType</code>. */ Element addXsIsisFeatureAttributeElements(final Element parentXsElement, final String feature) { final Element xsNofFeatureAttributeElement = createXsElement(helper.docFor(parentXsElement), "attribute"); xsNofFeatureAttributeElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":feature"); xsNofFeatureAttributeElement.setAttribute("fixed", feature); parentXsElement.appendChild(xsNofFeatureAttributeElement); return xsNofFeatureAttributeElement; } /** * returns child <code>xs:complexType</code> element allowing mixed content * for supplied parent XSD element, creating and appending if necessary. * * <p> * The supplied element is presumed to be one for which * <code>xs:complexType</code> is valid as a child (eg * <code>xs:element</code>). */ Element complexTypeFor(final Element parentXsElement) { return complexTypeFor(parentXsElement, true); } /** * returns child <code>xs:complexType</code> element, optionally allowing * mixed content, for supplied parent XSD element, creating and appending if * necessary. * * <p> * The supplied element is presumed to be one for which * <code>xs:complexType</code> is valid as a child (eg * <code>xs:element</code>). */ Element complexTypeFor(final Element parentXsElement, final boolean mixed) { final Element el = childXsElement(parentXsElement, "complexType"); if (mixed) { el.setAttribute("mixed", "true"); } return el; } /** * returns child <code>xs:sequence</code> element for supplied parent XSD * element, creating and appending if necessary. * * The supplied element is presumed to be one for which * <code>xs:simpleContent</code> is valid as a child (eg * <code>xs:complexType</code>). */ Element sequenceFor(final Element parentXsElement) { return childXsElement(parentXsElement, "sequence"); } /** * returns child <code>xs:choice</code> element for supplied parent XSD * element, creating and appending if necessary. * * The supplied element is presumed to be one for which * <code>xs:simpleContent</code> is valid as a child (eg * <code>xs:complexType</code>). */ Element choiceFor(final Element parentXsElement) { return childXsElement(parentXsElement, "choice"); } Element sequenceForComplexTypeFor(final Element parentXsElement) { return sequenceFor(complexTypeFor(parentXsElement)); } Element choiceForComplexTypeFor(final Element parentXsElement) { return choiceFor(complexTypeFor(parentXsElement)); } /** * Returns the <code>xs:choice</code> or <code>xs:sequence</code> element * under the supplied XSD element, or null if neither can be found. */ Element choiceOrSequenceFor(final Element parentXsElement) { final NodeList choiceNodeList = parentXsElement.getElementsByTagNameNS(XsMetaModel.W3_ORG_XS_URI, "choice"); if (choiceNodeList.getLength() > 0) { return (Element) choiceNodeList.item(0); } final NodeList sequenceNodeList = parentXsElement.getElementsByTagNameNS(XsMetaModel.W3_ORG_XS_URI, "sequence"); if (sequenceNodeList.getLength() > 0) { return (Element) sequenceNodeList.item(0); } return null; } /** * returns child <code>xs:simpleContent</code> element for supplied parent * XSD element, creating and appending if necessary. * * The supplied element is presumed to be one for which * <code>xs:simpleContent</code> is valid as a child (eg * <code>xs:complexType</code>). */ Element simpleContentFor(final Element parentXsElement) { return childXsElement(parentXsElement, "simpleContent"); } /** * returns child <code>xs:extension</code> element for supplied parent XSD * element, creating and appending if nec; also sets the <code>base</code> * attribute. * * The supplied element is presumed to be one for which * <code>xs:extension</code> is valid as a child (eg * <code>xs:complexType</code>). */ Element extensionFor(final Element parentXsElement, final String base) { final Element childXsElement = childXsElement(parentXsElement, "extension"); childXsElement.setAttribute("base", XsMetaModel.W3_ORG_XS_PREFIX + ":" + base); return childXsElement; } Element childXsElement(final Element parentXsElement, final String localName) { final NodeList nodeList = parentXsElement.getElementsByTagNameNS(XsMetaModel.W3_ORG_XS_URI, localName); if (nodeList.getLength() > 0) { return (Element) nodeList.item(0); } final Element childXsElement = createXsElement(helper.docFor(parentXsElement), localName); parentXsElement.appendChild(childXsElement); return childXsElement; } /** * @return the <code>xs:schema</code> element (the root element of the * owning XSD Doc). */ Element schemaFor(final Element xsElement) { return xsElement.getOwnerDocument().getDocumentElement(); } /** * Sets the <code>minOccurs</code> and <code>maxOccurs</code> attributes for * provided <code>element</code> (presumed to be an XSD element for which * these attributes makes sense. */ Element setXsCardinality(final Element xsElement, final int minOccurs, final int maxOccurs) { if (maxOccurs >= 0) { xsElement.setAttribute("minOccurs", "" + minOccurs); } if (maxOccurs >= 0) { if (maxOccurs == Integer.MAX_VALUE) { xsElement.setAttribute("maxOccurs", "unbounded"); } else { xsElement.setAttribute("maxOccurs", "" + maxOccurs); } } return xsElement; } }