001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.isis.core.runtime.snapshot; 021 022import org.w3c.dom.Document; 023import org.w3c.dom.Element; 024import org.w3c.dom.NodeList; 025 026/** 027 * Stateless utility methods relating to the w3.org schema and schema-instance 028 * meta models. 029 */ 030public final class XsMetaModel { 031 032 private final Helper helper; 033 /** 034 * URI representing the namespace of the in-built xmlns namespace as defined 035 * by w3.org. 036 * 037 * The NamespaceManager will not allow any namespaces with this URI to be 038 * added. 039 */ 040 public static final String W3_ORG_XMLNS_URI = "http://www.w3.org/2000/xmlns/"; 041 /** 042 * Namespace prefix for {@link W3_ORG_XMLNS_URI}. 043 * 044 * The NamespaceManager will not allow any namespace to use this prefix. 045 */ 046 public static final String W3_ORG_XMLNS_PREFIX = "xmlns"; 047 /** 048 * Namespace prefix for XML schema. 049 */ 050 public static final String W3_ORG_XS_URI = "http://www.w3.org/2001/XMLSchema"; 051 /** 052 * Namespace prefix for {@link W3_ORG_XS_URI}. 053 * 054 * The NamespaceManager will not allow any namespace to use this prefix. 055 */ 056 public static final String W3_ORG_XS_PREFIX = "xs"; 057 /** 058 * Namespace prefix for XML schema instance. 059 */ 060 public static final String W3_ORG_XSI_URI = "http://www.w3.org/2001/XMLSchema-instance"; 061 /** 062 * Namespace prefix for {@link W3_ORG_XSI_URI}. 063 * 064 * The NamespaceManager will not allow any namespace to use this prefix. 065 */ 066 public static final String W3_ORG_XSI_PREFIX = "xsi"; 067 068 private final IsisSchema isisMeta; 069 070 public XsMetaModel() { 071 this.helper = new Helper(); 072 this.isisMeta = new IsisSchema(); 073 } 074 075 /** 076 * Creates an <xs:schema> element for the document to the provided 077 * element, attaching to root of supplied Xsd doc. 078 * 079 * In addition: 080 * <ul> 081 * <li>the elementFormDefault is set 082 * <li>the NOF namespace is set 083 * <li>the <code>xs:import</code> element referencing the NOF namespace is 084 * added as a child 085 * </ul> 086 */ 087 Element createXsSchemaElement(final Document xsdDoc) { 088 if (xsdDoc.getDocumentElement() != null) { 089 throw new IllegalArgumentException("XSD document already has content"); 090 } 091 final Element xsSchemaElement = createXsElement(xsdDoc, "schema"); 092 093 xsSchemaElement.setAttribute("elementFormDefault", "qualified"); 094 095 isisMeta.addNamespace(xsSchemaElement); 096 097 xsdDoc.appendChild(xsSchemaElement); 098 final Element xsImportElement = createXsElement(xsdDoc, "import"); 099 xsImportElement.setAttribute("namespace", IsisSchema.NS_URI); 100 xsImportElement.setAttribute("schemaLocation", IsisSchema.DEFAULT_LOCATION); 101 102 xsSchemaElement.appendChild(xsImportElement); 103 104 return xsSchemaElement; 105 } 106 107 Element createXsElementElement(final Document xsdDoc, final String className) { 108 return createXsElementElement(xsdDoc, className, true); 109 } 110 111 Element createXsElementElement(final Document xsdDoc, final String className, final boolean includeCardinality) { 112 final Element xsElementElement = createXsElement(xsdDoc, "element"); 113 xsElementElement.setAttribute("name", className); 114 if (includeCardinality) { 115 setXsCardinality(xsElementElement, 0, Integer.MAX_VALUE); 116 } 117 return xsElementElement; 118 } 119 120 /** 121 * Creates an element in the XS namespace, adding the definition of the 122 * namespace to the root element of the document if required, 123 */ 124 Element createXsElement(final Document xsdDoc, final String localName) { 125 126 final Element element = xsdDoc.createElementNS(XsMetaModel.W3_ORG_XS_URI, XsMetaModel.W3_ORG_XS_PREFIX + ":" + localName); 127 // xmlns:xs="..." added to root 128 helper.rootElementFor(element).setAttributeNS(XsMetaModel.W3_ORG_XMLNS_URI, XsMetaModel.W3_ORG_XMLNS_PREFIX + ":" + XsMetaModel.W3_ORG_XS_PREFIX, XsMetaModel.W3_ORG_XS_URI); 129 return element; 130 } 131 132 // private Element addAnyToSequence(final Element xsSequenceElement) { 133 // Element xsAnyElement = createXsElement(docFor(xsSequenceElement), "any"); 134 // xsAnyElement.setAttribute("namespace", "##other"); 135 // xsAnyElement.setAttribute("minOccurs", "0"); 136 // xsAnyElement.setAttribute("maxOccurs", "unbounded"); 137 // xsAnyElement.setAttribute("processContents", "lax"); 138 // xsSequenceElement.appendChild(xsAnyElement); 139 // return xsSequenceElement; 140 // } 141 142 /** 143 * Creates an xs:attribute ref="isis:xxx" element, and appends to specified 144 * owning element. 145 */ 146 Element addXsIsisAttribute(final Element parentXsElement, final String isisAttributeRef) { 147 return addXsIsisAttribute(parentXsElement, isisAttributeRef, null); 148 } 149 150 /** 151 * Adds <code>xs:attribute ref="isis:xxx" fixed="yyy"</code> element, and 152 * appends to specified parent XSD element. 153 */ 154 Element addXsIsisAttribute(final Element parentXsElement, final String isisAttributeRef, final String fixedValue) { 155 return addXsIsisAttribute(parentXsElement, isisAttributeRef, fixedValue, true); 156 } 157 158 /** 159 * Adds <code>xs:attribute ref="isis:xxx" default="yyy"</code> element, and 160 * appends to specified parent XSD element. 161 * 162 * The last parameter determines whether to use <code>fixed="yyy"</code> 163 * rather than <code>default="yyy"</code>. 164 */ 165 Element addXsIsisAttribute(final Element parentXsElement, final String isisAttributeRef, final String value, final boolean useFixed) { 166 final Element xsIsisAttributeElement = createXsElement(helper.docFor(parentXsElement), "attribute"); 167 xsIsisAttributeElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":" + isisAttributeRef); 168 parentXsElement.appendChild(xsIsisAttributeElement); 169 if (value != null) { 170 if (useFixed) { 171 xsIsisAttributeElement.setAttribute("fixed", value); 172 } else { 173 xsIsisAttributeElement.setAttribute("default", value); 174 } 175 } 176 return parentXsElement; 177 } 178 179 /** 180 * Adds <code>xs:attribute ref="isis:feature" fixed="(feature)"</code> 181 * element as child to supplied XSD element, presumed to be an 182 * <xs:complexType</code>. 183 */ 184 Element addXsIsisFeatureAttributeElements(final Element parentXsElement, final String feature) { 185 final Element xsNofFeatureAttributeElement = createXsElement(helper.docFor(parentXsElement), "attribute"); 186 xsNofFeatureAttributeElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":feature"); 187 xsNofFeatureAttributeElement.setAttribute("fixed", feature); 188 parentXsElement.appendChild(xsNofFeatureAttributeElement); 189 return xsNofFeatureAttributeElement; 190 } 191 192 /** 193 * returns child <code>xs:complexType</code> element allowing mixed content 194 * for supplied parent XSD element, creating and appending if necessary. 195 * 196 * <p> 197 * The supplied element is presumed to be one for which 198 * <code>xs:complexType</code> is valid as a child (eg 199 * <code>xs:element</code>). 200 */ 201 Element complexTypeFor(final Element parentXsElement) { 202 return complexTypeFor(parentXsElement, true); 203 } 204 205 /** 206 * returns child <code>xs:complexType</code> element, optionally allowing 207 * mixed content, for supplied parent XSD element, creating and appending if 208 * necessary. 209 * 210 * <p> 211 * The supplied element is presumed to be one for which 212 * <code>xs:complexType</code> is valid as a child (eg 213 * <code>xs:element</code>). 214 */ 215 Element complexTypeFor(final Element parentXsElement, final boolean mixed) { 216 final Element el = childXsElement(parentXsElement, "complexType"); 217 if (mixed) { 218 el.setAttribute("mixed", "true"); 219 } 220 return el; 221 } 222 223 /** 224 * returns child <code>xs:sequence</code> element for supplied parent XSD 225 * element, creating and appending if necessary. 226 * 227 * The supplied element is presumed to be one for which 228 * <code>xs:simpleContent</code> is valid as a child (eg 229 * <code>xs:complexType</code>). 230 */ 231 Element sequenceFor(final Element parentXsElement) { 232 return childXsElement(parentXsElement, "sequence"); 233 } 234 235 /** 236 * returns child <code>xs:choice</code> element for supplied parent XSD 237 * element, creating and appending if necessary. 238 * 239 * The supplied element is presumed to be one for which 240 * <code>xs:simpleContent</code> is valid as a child (eg 241 * <code>xs:complexType</code>). 242 */ 243 Element choiceFor(final Element parentXsElement) { 244 return childXsElement(parentXsElement, "choice"); 245 } 246 247 Element sequenceForComplexTypeFor(final Element parentXsElement) { 248 return sequenceFor(complexTypeFor(parentXsElement)); 249 } 250 251 Element choiceForComplexTypeFor(final Element parentXsElement) { 252 return choiceFor(complexTypeFor(parentXsElement)); 253 } 254 255 /** 256 * Returns the <code>xs:choice</code> or <code>xs:sequence</code> element 257 * under the supplied XSD element, or null if neither can be found. 258 */ 259 Element choiceOrSequenceFor(final Element parentXsElement) { 260 final NodeList choiceNodeList = parentXsElement.getElementsByTagNameNS(XsMetaModel.W3_ORG_XS_URI, "choice"); 261 if (choiceNodeList.getLength() > 0) { 262 return (Element) choiceNodeList.item(0); 263 } 264 final NodeList sequenceNodeList = parentXsElement.getElementsByTagNameNS(XsMetaModel.W3_ORG_XS_URI, "sequence"); 265 if (sequenceNodeList.getLength() > 0) { 266 return (Element) sequenceNodeList.item(0); 267 } 268 return null; 269 } 270 271 /** 272 * returns child <code>xs:simpleContent</code> element for supplied parent 273 * XSD element, creating and appending if necessary. 274 * 275 * The supplied element is presumed to be one for which 276 * <code>xs:simpleContent</code> is valid as a child (eg 277 * <code>xs:complexType</code>). 278 */ 279 Element simpleContentFor(final Element parentXsElement) { 280 return childXsElement(parentXsElement, "simpleContent"); 281 } 282 283 /** 284 * returns child <code>xs:extension</code> element for supplied parent XSD 285 * element, creating and appending if nec; also sets the <code>base</code> 286 * attribute. 287 * 288 * The supplied element is presumed to be one for which 289 * <code>xs:extension</code> is valid as a child (eg 290 * <code>xs:complexType</code>). 291 */ 292 Element extensionFor(final Element parentXsElement, final String base) { 293 final Element childXsElement = childXsElement(parentXsElement, "extension"); 294 childXsElement.setAttribute("base", XsMetaModel.W3_ORG_XS_PREFIX + ":" + base); 295 return childXsElement; 296 } 297 298 Element childXsElement(final Element parentXsElement, final String localName) { 299 final NodeList nodeList = parentXsElement.getElementsByTagNameNS(XsMetaModel.W3_ORG_XS_URI, localName); 300 if (nodeList.getLength() > 0) { 301 return (Element) nodeList.item(0); 302 } 303 304 final Element childXsElement = createXsElement(helper.docFor(parentXsElement), localName); 305 parentXsElement.appendChild(childXsElement); 306 307 return childXsElement; 308 } 309 310 /** 311 * @return the <code>xs:schema</code> element (the root element of the 312 * owning XSD Doc). 313 */ 314 Element schemaFor(final Element xsElement) { 315 return xsElement.getOwnerDocument().getDocumentElement(); 316 } 317 318 /** 319 * Sets the <code>minOccurs</code> and <code>maxOccurs</code> attributes for 320 * provided <code>element</code> (presumed to be an XSD element for which 321 * these attributes makes sense. 322 */ 323 Element setXsCardinality(final Element xsElement, final int minOccurs, final int maxOccurs) { 324 if (maxOccurs >= 0) { 325 xsElement.setAttribute("minOccurs", "" + minOccurs); 326 } 327 if (maxOccurs >= 0) { 328 if (maxOccurs == Integer.MAX_VALUE) { 329 xsElement.setAttribute("maxOccurs", "unbounded"); 330 } else { 331 xsElement.setAttribute("maxOccurs", "" + maxOccurs); 332 } 333 } 334 return xsElement; 335 } 336 337}