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 java.util.Enumeration; 023import java.util.Hashtable; 024 025import org.w3c.dom.Attr; 026import org.w3c.dom.Document; 027import org.w3c.dom.Element; 028import org.w3c.dom.NamedNodeMap; 029import org.w3c.dom.NodeList; 030 031import org.apache.isis.applib.services.xmlsnapshot.XmlSnapshotService.Snapshot; 032 033/** 034 * Represents the schema for the derived snapshot. 035 */ 036public final class XmlSchema { 037 038 private final String prefix; 039 private final String uriBase; 040 private String uri; 041 042 private final IsisSchema isisMeta; 043 private final XsMetaModel xsMeta; 044 private final Helper helper; 045 046 /** 047 * The base part of the namespace prefix to use if none explicitly supplied 048 * in the constructor. 049 */ 050 public final static String DEFAULT_PREFIX = "app"; 051 052 public XmlSchema() { 053 this(IsisSchema.DEFAULT_URI_BASE, XmlSchema.DEFAULT_PREFIX); 054 } 055 056 /** 057 * @param uriBase 058 * the prefix for the application namespace's URIs 059 * @param prefix 060 * the prefix for the application namespace's prefix 061 */ 062 public XmlSchema(final String uriBase, final String prefix) { 063 this.isisMeta = new IsisSchema(); 064 this.xsMeta = new XsMetaModel(); 065 this.helper = new Helper(); 066 067 final String base = new Helper().trailingSlash(uriBase); 068 if (XsMetaModel.W3_ORG_XMLNS_URI.equals(base)) { 069 throw new IllegalArgumentException("Namespace URI reserved for w3.org XMLNS namespace"); 070 } 071 if (XsMetaModel.W3_ORG_XMLNS_PREFIX.equals(prefix)) { 072 throw new IllegalArgumentException("Namespace prefix reserved for w3.org XMLNS namespace."); 073 } 074 if (XsMetaModel.W3_ORG_XS_URI.equals(base)) { 075 throw new IllegalArgumentException("Namespace URI reserved for w3.org XML schema namespace."); 076 } 077 if (XsMetaModel.W3_ORG_XS_PREFIX.equals(prefix)) { 078 throw new IllegalArgumentException("Namespace prefix reserved for w3.org XML schema namespace."); 079 } 080 if (XsMetaModel.W3_ORG_XSI_URI.equals(base)) { 081 throw new IllegalArgumentException("Namespace URI reserved for w3.org XML schema-instance namespace."); 082 } 083 if (XsMetaModel.W3_ORG_XSI_PREFIX.equals(prefix)) { 084 throw new IllegalArgumentException("Namespace prefix reserved for w3.org XML schema-instance namespace."); 085 } 086 if (IsisSchema.NS_URI.equals(base)) { 087 throw new IllegalArgumentException("Namespace URI reserved for NOF metamodel namespace."); 088 } 089 if (IsisSchema.NS_PREFIX.equals(prefix)) { 090 throw new IllegalArgumentException("Namespace prefix reserved for NOF metamodel namespace."); 091 } 092 this.uriBase = base; 093 this.prefix = prefix; 094 } 095 096 /** 097 * The base of the Uri in use. All namespaces are concatenated with this. 098 * 099 * The namespace string will be the concatenation of the plus the package 100 * name of the class of the object being referenced. 101 * 102 * If not specified in the constructor, then {@link #DEFAULT_URI_PREFIX} is 103 * used. 104 */ 105 public String getUriBase() { 106 return uriBase; 107 } 108 109 /** 110 * Returns the namespace URI for the class. 111 */ 112 void setUri(final String fullyQualifiedClassName) { 113 if (uri != null) { 114 throw new IllegalStateException("URI has already been specified."); 115 } 116 this.uri = getUriBase() + helper.packageNameFor(fullyQualifiedClassName) + "/" + helper.classNameFor(fullyQualifiedClassName); 117 } 118 119 /** 120 * The URI of the application namespace. 121 * 122 * The value returned will be <code>null</code> until a {@link Snapshot} is 123 * created. 124 */ 125 public String getUri() { 126 if (uri == null) { 127 throw new IllegalStateException("URI has not been specified."); 128 } 129 return uri; 130 } 131 132 /** 133 * The prefix to the namespace for the application. 134 */ 135 public String getPrefix() { 136 return this.prefix; 137 } 138 139 /** 140 * Creates an element with the specified localName, in the appropriate 141 * namespace for the NOS. 142 * 143 * If necessary the namespace definition is added to the root element of the 144 * doc used to create the element. The element is not parented but to avoid 145 * an error can only be added as a child of another element in the same doc. 146 */ 147 Element createElement(final Document doc, final String localName, final String fullyQualifiedClassName, final String singularName, final String pluralName) { 148 final Element element = doc.createElementNS(getUri(), getPrefix() + ":" + localName); 149 element.setAttributeNS(IsisSchema.NS_URI, IsisSchema.NS_PREFIX + ":fqn", fullyQualifiedClassName); 150 element.setAttributeNS(IsisSchema.NS_URI, IsisSchema.NS_PREFIX + ":singular", singularName); 151 element.setAttributeNS(IsisSchema.NS_URI, IsisSchema.NS_PREFIX + ":plural", pluralName); 152 isisMeta.addNamespace(element); // good a place as any 153 154 addNamespace(element, getPrefix(), getUri()); 155 return element; 156 } 157 158 /** 159 * Sets the target namespace for the XSD document to a URI derived from the 160 * fully qualified class name of the supplied object 161 */ 162 void setTargetNamespace(final Document xsdDoc, final String fullyQualifiedClassName) { 163 164 final Element xsSchemaElement = xsdDoc.getDocumentElement(); 165 if (xsSchemaElement == null) { 166 throw new IllegalArgumentException("XSD Document must have <xs:schema> element attached"); 167 } 168 169 // targetNamespace="http://isis.apache.org/ns/app/<fully qualified class 170 // name> 171 xsSchemaElement.setAttribute("targetNamespace", getUri()); 172 173 addNamespace(xsSchemaElement, getPrefix(), getUri()); 174 } 175 176 /** 177 * Creates an <xs:element> element defining the presence of the named 178 * element representing a class 179 */ 180 Element createXsElementForNofClass(final Document xsdDoc, final Element element, final boolean addCardinality, final Hashtable extensions) { 181 182 // gather details from XML element 183 final String localName = element.getLocalName(); 184 185 // <xs:element name="AO11ConfirmAnimalRegistration"> 186 // <xs:complexType> 187 // <xs:sequence> 188 // <xs:element ref="isis:title"/> 189 // <!-- placeholder --> 190 // </xs:sequence> 191 // <xs:attribute ref="isis:feature" 192 // default="class"/> 193 // <xs:attribute ref="isis:oid"/> 194 // <xs:attribute ref="isis:annotation"/> 195 // <xs:attribute ref="isis:fqn"/> 196 // </xs:complexType> 197 // </xs:element> 198 199 // xs:element/@name="class name" 200 // add to XML schema as a global attribute 201 final Element xsElementForNofClassElement = xsMeta.createXsElementElement(xsdDoc, localName, addCardinality); 202 203 // xs:element/xs:complexType 204 // xs:element/xs:complexType/xs:sequence 205 final Element xsComplexTypeElement = xsMeta.complexTypeFor(xsElementForNofClassElement); 206 final Element xsSequenceElement = xsMeta.sequenceFor(xsComplexTypeElement); 207 208 // xs:element/xs:complexType/xs:sequence/xs:element ref="isis:title" 209 final Element xsTitleElement = xsMeta.createXsElement(helper.docFor(xsSequenceElement), "element"); 210 xsTitleElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":" + "title"); 211 xsSequenceElement.appendChild(xsTitleElement); 212 xsMeta.setXsCardinality(xsTitleElement, 0, 1); 213 214 // xs:element/xs:complexType/xs:sequence/xs:element ref="extensions" 215 addXsElementForAppExtensions(xsSequenceElement, extensions); 216 217 // xs:element/xs:complexType/xs:attribute ... 218 xsMeta.addXsIsisFeatureAttributeElements(xsComplexTypeElement, "class"); 219 xsMeta.addXsIsisAttribute(xsComplexTypeElement, "oid"); 220 xsMeta.addXsIsisAttribute(xsComplexTypeElement, "fqn"); 221 xsMeta.addXsIsisAttribute(xsComplexTypeElement, "singular"); 222 xsMeta.addXsIsisAttribute(xsComplexTypeElement, "plural"); 223 xsMeta.addXsIsisAttribute(xsComplexTypeElement, "annotation"); 224 225 Place.setXsdElement(element, xsElementForNofClassElement); 226 227 return xsElementForNofClassElement; 228 } 229 230 /** 231 * Creates an <code>xs:element</code> element to represent a collection of 232 * application-defined extensions 233 * 234 * The returned element should be appended to <code>xs:sequence</code> 235 * element of the xs:element representing the type of the owning object. 236 */ 237 void addXsElementForAppExtensions(final Element parentXsElementElement, final Hashtable extensions) { 238 239 if (extensions.size() == 0) { 240 return; 241 } 242 243 // <xs:element name="extensions"> 244 // <xs:complexType> 245 // <xs:sequence> 246 // <xs:element name="app:%extension class short name%" minOccurs="0" 247 // maxOccurs="1" default="%value%"/> 248 // <xs:element name="app:%extension class short name%" minOccurs="0" 249 // maxOccurs="1" default="%value%"/> 250 // ... 251 // <xs:element name="app:%extension class short name%" minOccurs="0" 252 // maxOccurs="1" default="%value%"/> 253 // </xs:sequence> 254 // </xs:complexType> 255 // </xs:element> 256 257 // xs:element name="isis-extensions" 258 // xs:element/xs:complexType/xs:sequence 259 final Element xsExtensionsSequenceElement = addExtensionsElement(parentXsElementElement); 260 261 addExtensionElements(xsExtensionsSequenceElement, extensions); 262 263 return; 264 } 265 266 /** 267 * Adds an isis-extensions element and a complexType and sequence elements 268 * underneath. 269 * 270 * <p> 271 * Returns the sequence element so that it can be appended to. 272 */ 273 private Element addExtensionsElement(final Element parentXsElement) { 274 final Element xsExtensionsElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsElement), "isis-extensions"); 275 parentXsElement.appendChild(xsExtensionsElementElement); 276 277 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence 278 final Element xsExtensionsComplexTypeElement = xsMeta.complexTypeFor(xsExtensionsElementElement); 279 final Element xsExtensionsSequenceElement = xsMeta.sequenceFor(xsExtensionsComplexTypeElement); 280 281 return xsExtensionsSequenceElement; 282 } 283 284 private String shortName(final String className) { 285 final int lastPeriodIdx = className.lastIndexOf('.'); 286 if (lastPeriodIdx < 0) { 287 return className; 288 } 289 return className.substring(lastPeriodIdx + 1); 290 } 291 292 /** 293 * Creates an <code>xs:element</code> element to represent a value field in 294 * a class. 295 * 296 * The returned element should be appended to <code>xs:sequence</code> 297 * element of the xs:element representing the type of the owning object. 298 */ 299 Element createXsElementForNofValue(final Element parentXsElementElement, final Element xmlValueElement, final Hashtable extensions) { 300 301 // gather details from XML element 302 final String datatype = xmlValueElement.getAttributeNS(IsisSchema.NS_URI, "datatype"); 303 final String fieldName = xmlValueElement.getLocalName(); 304 305 // <xs:element name="%owning object%"> 306 // <xs:complexType> 307 // <xs:sequence> 308 // <xs:element name="%%field object%%"> 309 // <xs:complexType> 310 // <xs:sequence> 311 // <xs:element name="isis-extensions"> 312 // <xs:complexType> 313 // <xs:sequence> 314 // <xs:element name="%extensionClassShortName%" 315 // default="%extensionObjString" minOccurs="0"/> 316 // <xs:element name="%extensionClassShortName%" 317 // default="%extensionObjString" minOccurs="0"/> 318 // ... 319 // <xs:element name="%extensionClassShortName%" 320 // default="%extensionObjString" minOccurs="0"/> 321 // </xs:sequence> 322 // </xs:complexType> 323 // </xs:element> 324 // </xs:sequence> 325 // <xs:attribute ref="isis:feature" fixed="value"/> 326 // <xs:attribute ref="isis:datatype" fixed="isis:%datatype%"/> 327 // <xs:attribute ref="isis:isEmpty"/> 328 // <xs:attribute ref="isis:annotation"/> 329 // </xs:complexType> 330 // </xs:element> 331 // </xs:sequence> 332 // </xs:complexType> 333 // </xs:element> 334 335 // xs:element/xs:complexType/xs:sequence 336 final Element parentXsComplexTypeElement = xsMeta.complexTypeFor(parentXsElementElement); 337 final Element parentXsSequenceElement = xsMeta.sequenceFor(parentXsComplexTypeElement); 338 339 // xs:element/xs:complexType/xs:sequence/xs:element 340 // name="%%field object%" 341 final Element xsFieldElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsSequenceElement), fieldName); 342 parentXsSequenceElement.appendChild(xsFieldElementElement); 343 344 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType 345 final Element xsFieldComplexTypeElement = xsMeta.complexTypeFor(xsFieldElementElement); 346 347 // NEW CODE TO SUPPORT EXTENSIONS; 348 // uses a complexType/sequence 349 350 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence 351 final Element xsFieldSequenceElement = xsMeta.sequenceFor(xsFieldComplexTypeElement); 352 353 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element 354 // name="isis-extensions" 355 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence 356 addXsElementForAppExtensions(xsFieldSequenceElement, extensions); 357 358 xsMeta.addXsIsisFeatureAttributeElements(xsFieldComplexTypeElement, "value"); 359 xsMeta.addXsIsisAttribute(xsFieldComplexTypeElement, "datatype", datatype); 360 xsMeta.addXsIsisAttribute(xsFieldComplexTypeElement, "isEmpty"); 361 xsMeta.addXsIsisAttribute(xsFieldComplexTypeElement, "annotation"); 362 363 return xsFieldElementElement; 364 } 365 366 private void addExtensionElements(final Element parentElement, final Hashtable extensions) { 367 for (final Enumeration e = extensions.keys(); e.hasMoreElements();) { 368 final Class<?> extensionClass = (Class<?>) e.nextElement(); 369 final Object extensionObject = extensions.get(extensionClass); 370 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element 371 // name="%extensionClassShortName%" 372 final Element xsExtensionElementElement = xsMeta.createXsElementElement(helper.docFor(parentElement), "x-" + shortName(extensionClass.getName())); 373 xsExtensionElementElement.setAttribute("default", extensionObject.toString()); // the 374 // value 375 xsExtensionElementElement.setAttribute("minOccurs", "0"); // doesn't 376 // need to 377 // appear 378 // in XML 379 // (and 380 // indeed won't) 381 parentElement.appendChild(xsExtensionElementElement); 382 } 383 } 384 385 /** 386 * Creates an <xs:element> element defining the presence of the named 387 * element representing a reference to a class; appended to xs:sequence 388 * element 389 */ 390 Element createXsElementForNofReference(final Element parentXsElementElement, final Element xmlReferenceElement, final String referencedClassName, final Hashtable extensions) { 391 392 // gather details from XML element 393 final String fieldName = xmlReferenceElement.getLocalName(); 394 395 // <xs:element name="%owning object%"> 396 // <xs:complexType> 397 // <xs:sequence> 398 // <xs:element name="%%field object%%"> 399 // <xs:complexType> 400 // <xs:sequence> 401 // <xs:element ref="isis:title" minOccurs="0"/> 402 // <xs:element name="isis-extensions"> 403 // <xs:complexType> 404 // <xs:sequence> 405 // <xs:element name="app:%extension class short name%" minOccurs="0" 406 // maxOccurs="1" default="%value%"/> 407 // <xs:element name="app:%extension class short name%" minOccurs="0" 408 // maxOccurs="1" default="%value%"/> 409 // ... 410 // <xs:element name="app:%extension class short name%" minOccurs="0" 411 // maxOccurs="1" default="%value%"/> 412 // </xs:sequence> 413 // </xs:complexType> 414 // </xs:element> 415 // <xs:sequence minOccurs="0" maxOccurs="1"/> 416 // </xs:sequence> 417 // <xs:attribute ref="isis:feature" fixed="reference"/> 418 // <xs:attribute ref="isis:type" default="%%appX%%:%%type%%"/> 419 // <xs:attribute ref="isis:isEmpty"/> 420 // <xs:attribute ref="isis:annotation"/> 421 // </xs:complexType> 422 // </xs:element> 423 // </xs:sequence> 424 // </xs:complexType> 425 // </xs:element> 426 427 // xs:element/xs:complexType/xs:sequence 428 final Element parentXsComplexTypeElement = xsMeta.complexTypeFor(parentXsElementElement); 429 final Element parentXsSequenceElement = xsMeta.sequenceFor(parentXsComplexTypeElement); 430 431 // xs:element/xs:complexType/xs:sequence/xs:element 432 // name="%%field object%" 433 final Element xsFieldElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsSequenceElement), fieldName); 434 parentXsSequenceElement.appendChild(xsFieldElementElement); 435 436 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence 437 final Element xsFieldComplexTypeElement = xsMeta.complexTypeFor(xsFieldElementElement); 438 final Element xsFieldSequenceElement = xsMeta.sequenceFor(xsFieldComplexTypeElement); 439 440 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element 441 // ref="isis:title" 442 final Element xsFieldTitleElement = xsMeta.createXsElement(helper.docFor(xsFieldSequenceElement), "element"); 443 xsFieldTitleElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":" + "title"); 444 xsFieldSequenceElement.appendChild(xsFieldTitleElement); 445 xsMeta.setXsCardinality(xsFieldTitleElement, 0, 1); 446 447 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element 448 // name="isis-extensions" 449 addXsElementForAppExtensions(xsFieldSequenceElement, extensions); 450 451 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:sequence 452 // // 453 // placeholder 454 final Element xsReferencedElementSequenceElement = xsMeta.sequenceFor(xsFieldSequenceElement); 455 xsMeta.setXsCardinality(xsReferencedElementSequenceElement, 0, 1); 456 457 xsMeta.addXsIsisFeatureAttributeElements(xsFieldComplexTypeElement, "reference"); 458 xsMeta.addXsIsisAttribute(xsFieldComplexTypeElement, "type", "app:" + referencedClassName, false); 459 xsMeta.addXsIsisAttribute(xsFieldComplexTypeElement, "isEmpty"); 460 xsMeta.addXsIsisAttribute(xsFieldComplexTypeElement, "annotation"); 461 462 return xsFieldElementElement; 463 } 464 465 /** 466 * Creates an <xs:element> element defining the presence of the named 467 * element representing a collection in a class; appended to xs:sequence 468 * element 469 */ 470 Element createXsElementForNofCollection(final Element parentXsElementElement, final Element xmlCollectionElement, final String referencedClassName, final Hashtable extensions) { 471 472 // gather details from XML element 473 final String fieldName = xmlCollectionElement.getLocalName(); 474 475 // <xs:element name="%owning object%"> 476 // <xs:complexType> 477 // <xs:sequence> 478 // <xs:element name="%%field object%%"> 479 // <xs:complexType> 480 // <xs:sequence> 481 // <xs:element ref="isis:oids" minOccurs="0" maxOccurs="1"/> 482 // <!-- nested element definitions go here --> 483 // </xs:sequence> 484 // <xs:attribute ref="isis:feature" fixed="collection"/> 485 // <xs:attribute ref="isis:type" fixed="%%appX%%:%%type%%"/> 486 // <xs:attribute ref="isis:size"/> 487 // <xs:attribute ref="isis:annotation"/> 488 // </xs:complexType> 489 // </xs:element> 490 // </xs:sequence> 491 // </xs:complexType> 492 // </xs:element> 493 494 // xs:element/xs:complexType/xs:sequence 495 final Element parentXsComplexTypeElement = xsMeta.complexTypeFor(parentXsElementElement); 496 final Element parentXsSequenceElement = xsMeta.sequenceFor(parentXsComplexTypeElement); 497 498 // xs:element/xs:complexType/xs:sequence/xs:element 499 // name="%field object%%" 500 final Element xsFieldElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsSequenceElement), fieldName); 501 parentXsSequenceElement.appendChild(xsFieldElementElement); 502 503 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType 504 final Element xsFieldComplexTypeElement = xsMeta.complexTypeFor(xsFieldElementElement); 505 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence 506 final Element xsFieldSequenceElement = xsMeta.sequenceFor(xsFieldComplexTypeElement); 507 508 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element 509 // ref="isis:oids" 510 final Element xsFieldOidsElement = xsMeta.createXsElement(helper.docFor(xsFieldSequenceElement), "element"); 511 xsFieldOidsElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":" + "oids"); 512 xsFieldSequenceElement.appendChild(xsFieldOidsElement); 513 xsMeta.setXsCardinality(xsFieldOidsElement, 0, 1); 514 515 // extensions 516 addXsElementForAppExtensions(xsFieldSequenceElement, extensions); 517 518 // // 519 // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:choice 520 // Element xsFieldChoiceElement = 521 // xsMeta.choiceFor(xsFieldComplexTypeElement); // placeholder 522 // xsMeta.setXsCardinality(xsFieldChoiceElement, 0, Integer.MAX_VALUE); 523 524 // Element xsFieldTitleElement = 525 // addXsNofRefElementElement(xsFieldSequenceElement, "title"); 526 527 // Element xsReferencedElementSequenceElement = 528 // sequenceFor(xsFieldSequenceElement); 529 // setXsCardinality(xsReferencedElementSequenceElement, 0, 1); 530 531 xsMeta.addXsIsisFeatureAttributeElements(xsFieldComplexTypeElement, "collection"); 532 xsMeta.addXsIsisAttribute(xsFieldComplexTypeElement, "type", "app:" + referencedClassName, false); 533 xsMeta.addXsIsisAttribute(xsFieldComplexTypeElement, "size"); 534 xsMeta.addXsIsisAttribute(xsFieldComplexTypeElement, "annotation"); 535 536 return xsFieldElementElement; 537 } 538 539 /** 540 * 541 * <pre> 542 * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 543 * xsi:schemaLocation="http://isis.apache.org/ns/app/sdm.common.fixture.schemes.ao.communications ddd.xsd" 544 * </pre> 545 * 546 * Assumes that the URI has been specified. 547 * 548 * @param xmlDoc 549 * @param fullyQualifiedClassName 550 * @param schemaLocationFileName 551 */ 552 void assignSchema(final Document xmlDoc, final String fullyQualifiedClassName, final String schemaLocationFileName) { 553 554 final String xsiSchemaLocationAttrValue = getUri() + " " + schemaLocationFileName; 555 556 final Element rootElement = xmlDoc.getDocumentElement(); 557 558 // xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 559 addNamespace(rootElement, XsMetaModel.W3_ORG_XSI_PREFIX, XsMetaModel.W3_ORG_XSI_URI); 560 561 // xsi:schemaLocation="http://isis.apache.org/ns/app/<fully qualified 562 // class name> 563 // sdm.common.fixture.schemes.ao.communications 564 // sdm.common.fixture.schemes.ao.communications.AO11ConfirmAnimalRegistration.xsd" 565 rootElement.setAttributeNS(XsMetaModel.W3_ORG_XSI_URI, "xsi:schemaLocation", xsiSchemaLocationAttrValue); 566 } 567 568 /** 569 * Adds a previously created <xs:element> element (representing a 570 * field of an object) to the supplied element (presumed to be a 571 * <code>complexType/sequence</code>). 572 */ 573 void addFieldXsElement(final Element xsElement, final Element xsFieldElement) { 574 if (xsFieldElement == null) { 575 return; 576 } 577 final Element sequenceElement = xsMeta.sequenceForComplexTypeFor(xsElement); 578 sequenceElement.appendChild(xsFieldElement); 579 } 580 581 /** 582 * Adds a namespace using the supplied prefix and the supplied URI to the 583 * root element of the document that is the parent of the supplied element. 584 * 585 * If the namespace declaration already exists but has a different URI 586 * (shouldn't normally happen) overwrites with supplied URI. 587 */ 588 private void addNamespace(final Element element, final String prefix, final String nsUri) { 589 final Element rootElement = helper.rootElementFor(element); 590 // see if we have the NS prefix there already 591 final String existingNsUri = rootElement.getAttributeNS(XsMetaModel.W3_ORG_XMLNS_URI, prefix); 592 // if there is none (or it is different from what we want), then set the 593 // attribute 594 if (existingNsUri == null || !existingNsUri.equals(nsUri)) { 595 helper.rootElementFor(element).setAttributeNS(XsMetaModel.W3_ORG_XMLNS_URI, XsMetaModel.W3_ORG_XMLNS_PREFIX + ":" + prefix, nsUri); 596 } 597 } 598 599 Element addXsElementIfNotPresent(final Element parentXsElement, final Element childXsElement) { 600 601 final Element parentChoiceOrSequenceElement = xsMeta.choiceOrSequenceFor(xsMeta.complexTypeFor(parentXsElement)); 602 603 if (parentChoiceOrSequenceElement == null) { 604 throw new IllegalArgumentException("Unable to locate complexType/sequence or complexType/choice under supplied parent XSD element"); 605 } 606 607 final NamedNodeMap childXsElementAttributeMap = childXsElement.getAttributes(); 608 final Attr childXsElementAttr = (Attr) childXsElementAttributeMap.getNamedItem("name"); 609 final String localName = childXsElementAttr.getValue(); 610 611 final NodeList existingElements = parentChoiceOrSequenceElement.getElementsByTagNameNS("*", childXsElement.getLocalName()); 612 for (int i = 0; i < existingElements.getLength(); i++) { 613 final Element xsElement = (Element) existingElements.item(i); 614 final NamedNodeMap xsElementAttributeMap = xsElement.getAttributes(); 615 final Attr attr = (Attr) xsElementAttributeMap.getNamedItem("name"); 616 if (attr != null && attr.getValue().equals(localName)) { 617 return xsElement; 618 } 619 } 620 621 parentChoiceOrSequenceElement.appendChild(childXsElement); 622 return childXsElement; 623 } 624 625}