You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2012/12/05 09:07:34 UTC
[4/11] ISIS-188: more refactoring of artifacts
http://git-wip-us.apache.org/repos/asf/isis/blob/6de87443/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XmlSchema.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XmlSchema.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XmlSchema.java
new file mode 100644
index 0000000..1bbab91
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XmlSchema.java
@@ -0,0 +1,623 @@
+/*
+ * 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 java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.NodeList;
+
+/**
+ * Represents the schema for the derived snapshot.
+ */
+public final class XmlSchema {
+
+ private final String prefix;
+ private final String uriBase;
+ private String uri;
+
+ private final IsisSchema nofMeta;
+ private final XsMetaModel xsMeta;
+ private final Helper helper;
+
+ /**
+ * The base part of the namespace prefix to use if none explicitly supplied
+ * in the constructor.
+ */
+ public final static String DEFAULT_PREFIX = "app";
+
+ public XmlSchema() {
+ this(IsisSchema.DEFAULT_URI_BASE, XmlSchema.DEFAULT_PREFIX);
+ }
+
+ /**
+ * @param uriBase
+ * the prefix for the application namespace's URIs
+ * @param prefix
+ * the prefix for the application namespace's prefix
+ */
+ public XmlSchema(final String uriBase, final String prefix) {
+ this.nofMeta = new IsisSchema();
+ this.xsMeta = new XsMetaModel();
+ this.helper = new Helper();
+
+ final String base = new Helper().trailingSlash(uriBase);
+ if (XsMetaModel.W3_ORG_XMLNS_URI.equals(base)) {
+ throw new IllegalArgumentException("Namespace URI reserved for w3.org XMLNS namespace");
+ }
+ if (XsMetaModel.W3_ORG_XMLNS_PREFIX.equals(prefix)) {
+ throw new IllegalArgumentException("Namespace prefix reserved for w3.org XMLNS namespace.");
+ }
+ if (XsMetaModel.W3_ORG_XS_URI.equals(base)) {
+ throw new IllegalArgumentException("Namespace URI reserved for w3.org XML schema namespace.");
+ }
+ if (XsMetaModel.W3_ORG_XS_PREFIX.equals(prefix)) {
+ throw new IllegalArgumentException("Namespace prefix reserved for w3.org XML schema namespace.");
+ }
+ if (XsMetaModel.W3_ORG_XSI_URI.equals(base)) {
+ throw new IllegalArgumentException("Namespace URI reserved for w3.org XML schema-instance namespace.");
+ }
+ if (XsMetaModel.W3_ORG_XSI_PREFIX.equals(prefix)) {
+ throw new IllegalArgumentException("Namespace prefix reserved for w3.org XML schema-instance namespace.");
+ }
+ if (IsisSchema.NS_URI.equals(base)) {
+ throw new IllegalArgumentException("Namespace URI reserved for NOF metamodel namespace.");
+ }
+ if (IsisSchema.NS_PREFIX.equals(prefix)) {
+ throw new IllegalArgumentException("Namespace prefix reserved for NOF metamodel namespace.");
+ }
+ this.uriBase = base;
+ this.prefix = prefix;
+ }
+
+ /**
+ * The base of the Uri in use. All namespaces are concatenated with this.
+ *
+ * The namespace string will be the concatenation of the plus the package
+ * name of the class of the object being referenced.
+ *
+ * If not specified in the constructor, then {@link #DEFAULT_URI_PREFIX} is
+ * used.
+ */
+ public String getUriBase() {
+ return uriBase;
+ }
+
+ /**
+ * Returns the namespace URI for the class.
+ */
+ void setUri(final String fullyQualifiedClassName) {
+ if (uri != null) {
+ throw new IllegalStateException("URI has already been specified.");
+ }
+ this.uri = getUriBase() + helper.packageNameFor(fullyQualifiedClassName) + "/" + helper.classNameFor(fullyQualifiedClassName);
+ }
+
+ /**
+ * The URI of the application namespace.
+ *
+ * The value returned will be <code>null</code> until a {@link Snapshot} is
+ * created.
+ */
+ public String getUri() {
+ if (uri == null) {
+ throw new IllegalStateException("URI has not been specified.");
+ }
+ return uri;
+ }
+
+ /**
+ * The prefix to the namespace for the application.
+ */
+ public String getPrefix() {
+ return this.prefix;
+ }
+
+ /**
+ * Creates an element with the specified localName, in the appropriate
+ * namespace for the NOS.
+ *
+ * If necessary the namespace definition is added to the root element of the
+ * doc used to create the element. The element is not parented but to avoid
+ * an error can only be added as a child of another element in the same doc.
+ */
+ Element createElement(final Document doc, final String localName, final String fullyQualifiedClassName, final String singularName, final String pluralName) {
+ final Element element = doc.createElementNS(getUri(), getPrefix() + ":" + localName);
+ element.setAttributeNS(IsisSchema.NS_URI, "nof:fqn", fullyQualifiedClassName);
+ element.setAttributeNS(IsisSchema.NS_URI, "nof:singular", singularName);
+ element.setAttributeNS(IsisSchema.NS_URI, "nof:plural", pluralName);
+ nofMeta.addNamespace(element); // good a place as any
+
+ addNamespace(element, getPrefix(), getUri());
+ return element;
+ }
+
+ /**
+ * Sets the target namespace for the XSD document to a URI derived from the
+ * fully qualified class name of the supplied object
+ */
+ void setTargetNamespace(final Document xsdDoc, final String fullyQualifiedClassName) {
+
+ final Element xsSchemaElement = xsdDoc.getDocumentElement();
+ if (xsSchemaElement == null) {
+ throw new IllegalArgumentException("XSD Document must have <xs:schema> element attached");
+ }
+
+ // targetNamespace="http://isis.apache.org/ns/app/<fully qualified class
+ // name>
+ xsSchemaElement.setAttribute("targetNamespace", getUri());
+
+ addNamespace(xsSchemaElement, getPrefix(), getUri());
+ }
+
+ /**
+ * Creates an <xs:element> element defining the presence of the named
+ * element representing a class
+ */
+ Element createXsElementForNofClass(final Document xsdDoc, final Element element, final boolean addCardinality, final Hashtable extensions) {
+
+ // gather details from XML element
+ final String localName = element.getLocalName();
+
+ // <xs:element name="AO11ConfirmAnimalRegistration">
+ // <xs:complexType>
+ // <xs:sequence>
+ // <xs:element ref="nof:title"/>
+ // <!-- placeholder -->
+ // </xs:sequence>
+ // <xs:attribute ref="nof:feature"
+ // default="class"/>
+ // <xs:attribute ref="nof:oid"/>
+ // <xs:attribute ref="nof:annotation"/>
+ // <xs:attribute ref="nof:fqn"/>
+ // </xs:complexType>
+ // </xs:element>
+
+ // xs:element/@name="class name"
+ // add to XML schema as a global attribute
+ final Element xsElementForNofClassElement = xsMeta.createXsElementElement(xsdDoc, localName, addCardinality);
+
+ // xs:element/xs:complexType
+ // xs:element/xs:complexType/xs:sequence
+ final Element xsComplexTypeElement = xsMeta.complexTypeFor(xsElementForNofClassElement);
+ final Element xsSequenceElement = xsMeta.sequenceFor(xsComplexTypeElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element ref="nof:title"
+ final Element xsTitleElement = xsMeta.createXsElement(helper.docFor(xsSequenceElement), "element");
+ xsTitleElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":" + "title");
+ xsSequenceElement.appendChild(xsTitleElement);
+ xsMeta.setXsCardinality(xsTitleElement, 0, 1);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element ref="extensions"
+ addXsElementForAppExtensions(xsSequenceElement, extensions);
+
+ // xs:element/xs:complexType/xs:attribute ...
+ xsMeta.addXsNofFeatureAttributeElements(xsComplexTypeElement, "class");
+ xsMeta.addXsNofAttribute(xsComplexTypeElement, "oid");
+ xsMeta.addXsNofAttribute(xsComplexTypeElement, "fqn");
+ xsMeta.addXsNofAttribute(xsComplexTypeElement, "singular");
+ xsMeta.addXsNofAttribute(xsComplexTypeElement, "plural");
+ xsMeta.addXsNofAttribute(xsComplexTypeElement, "annotation");
+
+ Place.setXsdElement(element, xsElementForNofClassElement);
+
+ return xsElementForNofClassElement;
+ }
+
+ /**
+ * Creates an <code>xs:element</code> element to represent a collection of
+ * application-defined extensions
+ *
+ * The returned element should be appended to <code>xs:sequence</code>
+ * element of the xs:element representing the type of the owning object.
+ */
+ void addXsElementForAppExtensions(final Element parentXsElementElement, final Hashtable extensions) {
+
+ if (extensions.size() == 0) {
+ return;
+ }
+
+ // <xs:element name="extensions">
+ // <xs:complexType>
+ // <xs:sequence>
+ // <xs:element name="app:%extension class short name%" minOccurs="0"
+ // maxOccurs="1" default="%value%"/>
+ // <xs:element name="app:%extension class short name%" minOccurs="0"
+ // maxOccurs="1" default="%value%"/>
+ // ...
+ // <xs:element name="app:%extension class short name%" minOccurs="0"
+ // maxOccurs="1" default="%value%"/>
+ // </xs:sequence>
+ // </xs:complexType>
+ // </xs:element>
+
+ // xs:element name="nof-extensions"
+ // xs:element/xs:complexType/xs:sequence
+ final Element xsExtensionsSequenceElement = addExtensionsElement(parentXsElementElement);
+
+ addExtensionElements(xsExtensionsSequenceElement, extensions);
+
+ return;
+ }
+
+ /**
+ * Adds an nof-extensions element and a complexType and sequence elements
+ * underneath.
+ *
+ * <p>
+ * Returns the sequence element so that it can be appended to.
+ */
+ private Element addExtensionsElement(final Element parentXsElement) {
+ final Element xsExtensionsElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsElement), "nof-extensions");
+ parentXsElement.appendChild(xsExtensionsElementElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence
+ final Element xsExtensionsComplexTypeElement = xsMeta.complexTypeFor(xsExtensionsElementElement);
+ final Element xsExtensionsSequenceElement = xsMeta.sequenceFor(xsExtensionsComplexTypeElement);
+
+ return xsExtensionsSequenceElement;
+ }
+
+ private String shortName(final String className) {
+ final int lastPeriodIdx = className.lastIndexOf('.');
+ if (lastPeriodIdx < 0) {
+ return className;
+ }
+ return className.substring(lastPeriodIdx + 1);
+ }
+
+ /**
+ * Creates an <code>xs:element</code> element to represent a value field in
+ * a class.
+ *
+ * The returned element should be appended to <code>xs:sequence</code>
+ * element of the xs:element representing the type of the owning object.
+ */
+ Element createXsElementForNofValue(final Element parentXsElementElement, final Element xmlValueElement, final Hashtable extensions) {
+
+ // gather details from XML element
+ final String datatype = xmlValueElement.getAttributeNS(IsisSchema.NS_URI, "datatype");
+ final String fieldName = xmlValueElement.getLocalName();
+
+ // <xs:element name="%owning object%">
+ // <xs:complexType>
+ // <xs:sequence>
+ // <xs:element name="%%field object%%">
+ // <xs:complexType>
+ // <xs:sequence>
+ // <xs:element name="nof-extensions">
+ // <xs:complexType>
+ // <xs:sequence>
+ // <xs:element name="%extensionClassShortName%"
+ // default="%extensionObjString" minOccurs="0"/>
+ // <xs:element name="%extensionClassShortName%"
+ // default="%extensionObjString" minOccurs="0"/>
+ // ...
+ // <xs:element name="%extensionClassShortName%"
+ // default="%extensionObjString" minOccurs="0"/>
+ // </xs:sequence>
+ // </xs:complexType>
+ // </xs:element>
+ // </xs:sequence>
+ // <xs:attribute ref="nof:feature" fixed="value"/>
+ // <xs:attribute ref="nof:datatype" fixed="nof:%datatype%"/>
+ // <xs:attribute ref="nof:isEmpty"/>
+ // <xs:attribute ref="nof:annotation"/>
+ // </xs:complexType>
+ // </xs:element>
+ // </xs:sequence>
+ // </xs:complexType>
+ // </xs:element>
+
+ // xs:element/xs:complexType/xs:sequence
+ final Element parentXsComplexTypeElement = xsMeta.complexTypeFor(parentXsElementElement);
+ final Element parentXsSequenceElement = xsMeta.sequenceFor(parentXsComplexTypeElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element
+ // name="%%field object%"
+ final Element xsFieldElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsSequenceElement), fieldName);
+ parentXsSequenceElement.appendChild(xsFieldElementElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType
+ final Element xsFieldComplexTypeElement = xsMeta.complexTypeFor(xsFieldElementElement);
+
+ // NEW CODE TO SUPPORT EXTENSIONS;
+ // uses a complexType/sequence
+
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence
+ final Element xsFieldSequenceElement = xsMeta.sequenceFor(xsFieldComplexTypeElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element
+ // name="nof-extensions"
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence
+ addXsElementForAppExtensions(xsFieldSequenceElement, extensions);
+
+ xsMeta.addXsNofFeatureAttributeElements(xsFieldComplexTypeElement, "value");
+ xsMeta.addXsNofAttribute(xsFieldComplexTypeElement, "datatype", datatype);
+ xsMeta.addXsNofAttribute(xsFieldComplexTypeElement, "isEmpty");
+ xsMeta.addXsNofAttribute(xsFieldComplexTypeElement, "annotation");
+
+ return xsFieldElementElement;
+ }
+
+ private void addExtensionElements(final Element parentElement, final Hashtable extensions) {
+ for (final Enumeration e = extensions.keys(); e.hasMoreElements();) {
+ final Class<?> extensionClass = (Class<?>) e.nextElement();
+ final Object extensionObject = extensions.get(extensionClass);
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element
+ // name="%extensionClassShortName%"
+ final Element xsExtensionElementElement = xsMeta.createXsElementElement(helper.docFor(parentElement), "x-" + shortName(extensionClass.getName()));
+ xsExtensionElementElement.setAttribute("default", extensionObject.toString()); // the
+ // value
+ xsExtensionElementElement.setAttribute("minOccurs", "0"); // doesn't
+ // need to
+ // appear
+ // in XML
+ // (and
+ // indeed won't)
+ parentElement.appendChild(xsExtensionElementElement);
+ }
+ }
+
+ /**
+ * Creates an <xs:element> element defining the presence of the named
+ * element representing a reference to a class; appended to xs:sequence
+ * element
+ */
+ Element createXsElementForNofReference(final Element parentXsElementElement, final Element xmlReferenceElement, final String referencedClassName, final Hashtable extensions) {
+
+ // gather details from XML element
+ final String fieldName = xmlReferenceElement.getLocalName();
+
+ // <xs:element name="%owning object%">
+ // <xs:complexType>
+ // <xs:sequence>
+ // <xs:element name="%%field object%%">
+ // <xs:complexType>
+ // <xs:sequence>
+ // <xs:element ref="nof:title" minOccurs="0"/>
+ // <xs:element name="nof-extensions">
+ // <xs:complexType>
+ // <xs:sequence>
+ // <xs:element name="app:%extension class short name%" minOccurs="0"
+ // maxOccurs="1" default="%value%"/>
+ // <xs:element name="app:%extension class short name%" minOccurs="0"
+ // maxOccurs="1" default="%value%"/>
+ // ...
+ // <xs:element name="app:%extension class short name%" minOccurs="0"
+ // maxOccurs="1" default="%value%"/>
+ // </xs:sequence>
+ // </xs:complexType>
+ // </xs:element>
+ // <xs:sequence minOccurs="0" maxOccurs="1"/>
+ // </xs:sequence>
+ // <xs:attribute ref="nof:feature" fixed="reference"/>
+ // <xs:attribute ref="nof:type" default="%%appX%%:%%type%%"/>
+ // <xs:attribute ref="nof:isEmpty"/>
+ // <xs:attribute ref="nof:annotation"/>
+ // </xs:complexType>
+ // </xs:element>
+ // </xs:sequence>
+ // </xs:complexType>
+ // </xs:element>
+
+ // xs:element/xs:complexType/xs:sequence
+ final Element parentXsComplexTypeElement = xsMeta.complexTypeFor(parentXsElementElement);
+ final Element parentXsSequenceElement = xsMeta.sequenceFor(parentXsComplexTypeElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element
+ // name="%%field object%"
+ final Element xsFieldElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsSequenceElement), fieldName);
+ parentXsSequenceElement.appendChild(xsFieldElementElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence
+ final Element xsFieldComplexTypeElement = xsMeta.complexTypeFor(xsFieldElementElement);
+ final Element xsFieldSequenceElement = xsMeta.sequenceFor(xsFieldComplexTypeElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element
+ // ref="nof:title"
+ final Element xsFieldTitleElement = xsMeta.createXsElement(helper.docFor(xsFieldSequenceElement), "element");
+ xsFieldTitleElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":" + "title");
+ xsFieldSequenceElement.appendChild(xsFieldTitleElement);
+ xsMeta.setXsCardinality(xsFieldTitleElement, 0, 1);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element
+ // name="nof-extensions"
+ addXsElementForAppExtensions(xsFieldSequenceElement, extensions);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:sequence
+ // //
+ // placeholder
+ final Element xsReferencedElementSequenceElement = xsMeta.sequenceFor(xsFieldSequenceElement);
+ xsMeta.setXsCardinality(xsReferencedElementSequenceElement, 0, 1);
+
+ xsMeta.addXsNofFeatureAttributeElements(xsFieldComplexTypeElement, "reference");
+ xsMeta.addXsNofAttribute(xsFieldComplexTypeElement, "type", "app:" + referencedClassName, false);
+ xsMeta.addXsNofAttribute(xsFieldComplexTypeElement, "isEmpty");
+ xsMeta.addXsNofAttribute(xsFieldComplexTypeElement, "annotation");
+
+ return xsFieldElementElement;
+ }
+
+ /**
+ * Creates an <xs:element> element defining the presence of the named
+ * element representing a collection in a class; appended to xs:sequence
+ * element
+ */
+ Element createXsElementForNofCollection(final Element parentXsElementElement, final Element xmlCollectionElement, final String referencedClassName, final Hashtable extensions) {
+
+ // gather details from XML element
+ final String fieldName = xmlCollectionElement.getLocalName();
+
+ // <xs:element name="%owning object%">
+ // <xs:complexType>
+ // <xs:sequence>
+ // <xs:element name="%%field object%%">
+ // <xs:complexType>
+ // <xs:sequence>
+ // <xs:element ref="nof:oids" minOccurs="0" maxOccurs="1"/>
+ // <!-- nested element definitions go here -->
+ // </xs:sequence>
+ // <xs:attribute ref="nof:feature" fixed="collection"/>
+ // <xs:attribute ref="nof:type" fixed="%%appX%%:%%type%%"/>
+ // <xs:attribute ref="nof:size"/>
+ // <xs:attribute ref="nof:annotation"/>
+ // </xs:complexType>
+ // </xs:element>
+ // </xs:sequence>
+ // </xs:complexType>
+ // </xs:element>
+
+ // xs:element/xs:complexType/xs:sequence
+ final Element parentXsComplexTypeElement = xsMeta.complexTypeFor(parentXsElementElement);
+ final Element parentXsSequenceElement = xsMeta.sequenceFor(parentXsComplexTypeElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element
+ // name="%field object%%"
+ final Element xsFieldElementElement = xsMeta.createXsElementElement(helper.docFor(parentXsSequenceElement), fieldName);
+ parentXsSequenceElement.appendChild(xsFieldElementElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType
+ final Element xsFieldComplexTypeElement = xsMeta.complexTypeFor(xsFieldElementElement);
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence
+ final Element xsFieldSequenceElement = xsMeta.sequenceFor(xsFieldComplexTypeElement);
+
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element
+ // ref="nof:oids"
+ final Element xsFieldOidsElement = xsMeta.createXsElement(helper.docFor(xsFieldSequenceElement), "element");
+ xsFieldOidsElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":" + "oids");
+ xsFieldSequenceElement.appendChild(xsFieldOidsElement);
+ xsMeta.setXsCardinality(xsFieldOidsElement, 0, 1);
+
+ // extensions
+ addXsElementForAppExtensions(xsFieldSequenceElement, extensions);
+
+ // //
+ // xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:choice
+ // Element xsFieldChoiceElement =
+ // xsMeta.choiceFor(xsFieldComplexTypeElement); // placeholder
+ // xsMeta.setXsCardinality(xsFieldChoiceElement, 0, Integer.MAX_VALUE);
+
+ // Element xsFieldTitleElement =
+ // addXsNofRefElementElement(xsFieldSequenceElement, "title");
+
+ // Element xsReferencedElementSequenceElement =
+ // sequenceFor(xsFieldSequenceElement);
+ // setXsCardinality(xsReferencedElementSequenceElement, 0, 1);
+
+ xsMeta.addXsNofFeatureAttributeElements(xsFieldComplexTypeElement, "collection");
+ xsMeta.addXsNofAttribute(xsFieldComplexTypeElement, "type", "app:" + referencedClassName, false);
+ xsMeta.addXsNofAttribute(xsFieldComplexTypeElement, "size");
+ xsMeta.addXsNofAttribute(xsFieldComplexTypeElement, "annotation");
+
+ return xsFieldElementElement;
+ }
+
+ /**
+ *
+ * <pre>
+ * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ * xsi:schemaLocation="http://isis.apache.org/ns/app/sdm.common.fixture.schemes.ao.communications ddd.xsd"
+ * </pre>
+ *
+ * Assumes that the URI has been specified.
+ *
+ * @param xmlDoc
+ * @param fullyQualifiedClassName
+ * @param schemaLocationFileName
+ */
+ void assignSchema(final Document xmlDoc, final String fullyQualifiedClassName, final String schemaLocationFileName) {
+
+ final String xsiSchemaLocationAttrValue = getUri() + " " + schemaLocationFileName;
+
+ final Element rootElement = xmlDoc.getDocumentElement();
+
+ // xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ addNamespace(rootElement, XsMetaModel.W3_ORG_XSI_PREFIX, XsMetaModel.W3_ORG_XSI_URI);
+
+ // xsi:schemaLocation="http://isis.apache.org/ns/app/<fully qualified
+ // class name>
+ // sdm.common.fixture.schemes.ao.communications
+ // sdm.common.fixture.schemes.ao.communications.AO11ConfirmAnimalRegistration.xsd"
+ rootElement.setAttributeNS(XsMetaModel.W3_ORG_XSI_URI, "xsi:schemaLocation", xsiSchemaLocationAttrValue);
+ }
+
+ /**
+ * Adds a previously created <xs:element> element (representing a
+ * field of an object) to the supplied element (presumed to be a
+ * <code>complexType/sequence</code>).
+ */
+ void addFieldXsElement(final Element xsElement, final Element xsFieldElement) {
+ if (xsFieldElement == null) {
+ return;
+ }
+ final Element sequenceElement = xsMeta.sequenceForComplexTypeFor(xsElement);
+ sequenceElement.appendChild(xsFieldElement);
+ }
+
+ /**
+ * Adds a namespace using the supplied prefix and the supplied URI to the
+ * root element of the document that is the parent of the supplied element.
+ *
+ * If the namespace declaration already exists but has a different URI
+ * (shouldn't normally happen) overwrites with supplied URI.
+ */
+ private void addNamespace(final Element element, final String prefix, final String nsUri) {
+ final Element rootElement = helper.rootElementFor(element);
+ // see if we have the NS prefix there already
+ final String existingNsUri = rootElement.getAttributeNS(XsMetaModel.W3_ORG_XMLNS_URI, prefix);
+ // if there is none (or it is different from what we want), then set the
+ // attribute
+ if (existingNsUri == null || !existingNsUri.equals(nsUri)) {
+ helper.rootElementFor(element).setAttributeNS(XsMetaModel.W3_ORG_XMLNS_URI, XsMetaModel.W3_ORG_XMLNS_PREFIX + ":" + prefix, nsUri);
+ }
+ }
+
+ Element addXsElementIfNotPresent(final Element parentXsElement, final Element childXsElement) {
+
+ final Element parentChoiceOrSequenceElement = xsMeta.choiceOrSequenceFor(xsMeta.complexTypeFor(parentXsElement));
+
+ if (parentChoiceOrSequenceElement == null) {
+ throw new IllegalArgumentException("Unable to locate complexType/sequence or complexType/choice under supplied parent XSD element");
+ }
+
+ final NamedNodeMap childXsElementAttributeMap = childXsElement.getAttributes();
+ final Attr childXsElementAttr = (Attr) childXsElementAttributeMap.getNamedItem("name");
+ final String localName = childXsElementAttr.getValue();
+
+ final NodeList existingElements = parentChoiceOrSequenceElement.getElementsByTagNameNS("*", childXsElement.getLocalName());
+ for (int i = 0; i < existingElements.getLength(); i++) {
+ final Element xsElement = (Element) existingElements.item(i);
+ final NamedNodeMap xsElementAttributeMap = xsElement.getAttributes();
+ final Attr attr = (Attr) xsElementAttributeMap.getNamedItem("name");
+ if (attr != null && attr.getValue().equals(localName)) {
+ return xsElement;
+ }
+ }
+
+ parentChoiceOrSequenceElement.appendChild(childXsElement);
+ return childXsElement;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/6de87443/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XmlSnapshot.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XmlSnapshot.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XmlSnapshot.java
new file mode 100644
index 0000000..f0591d8
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XmlSnapshot.java
@@ -0,0 +1,823 @@
+/*
+ * 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 java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.isis.applib.snapshot.SnapshottableWithInclusions;
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
+import org.apache.isis.core.metamodel.facetapi.FacetUtil;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionFacet;
+import org.apache.isis.core.metamodel.facets.object.encodeable.EncodableFacet;
+import org.apache.isis.core.metamodel.facets.object.parseable.ParseableFacet;
+import org.apache.isis.core.metamodel.facets.object.value.ValueFacet;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.ObjectSpecificationException;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+
+/**
+ * Traverses object graph from specified root, so that an XML representation of
+ * the graph can be returned.
+ *
+ * <p>
+ * Initially designed to allow snapshots to be easily created.
+ *
+ * <p>
+ * Typical use:
+ *
+ * <pre>
+ * XmlSnapshot snapshot = new XmlSnapshot(customer); // where customer is a
+ * // reference to an
+ * // ObjectAdapter
+ * Element customerAsXml = snapshot.toXml(); // returns customer's fields, titles
+ * // of simple references, number of
+ * // items in collections
+ * snapshot.include("placeOfBirth"); // navigates to another object represented by
+ * // simple reference "placeOfBirth"
+ * snapshot.include("orders/product"); // navigates to all <tt>Order</tt>s of
+ * // <tt>Customer</tt>, and from them for
+ * // their <tt>Product</tt>s
+ * </pre>
+ */
+public class XmlSnapshot {
+
+ private static final Logger LOG = Logger.getLogger(XmlSnapshot.class);
+
+ private final IsisSchema isisMetaModel;
+
+ private final Place rootPlace;
+
+ private final XmlSchema schema;
+
+ /**
+ * the suggested location for the schema (xsi:schemaLocation attribute)
+ */
+ private String schemaLocationFileName;
+ private boolean topLevelElementWritten = false;
+
+ private final Document xmlDocument;
+
+ /**
+ * root element of {@link #xmlDocument}
+ */
+ private Element xmlElement;
+ private final Document xsdDocument;
+ /**
+ * root element of {@link #xsdDocument}
+ */
+ private final Element xsdElement;
+
+ private final XsMetaModel xsMeta;
+
+ private final OidMarshaller oidMarshaller;
+
+ /**
+ * Start a snapshot at the root object, using own namespace manager.
+ * @param oidMarshaller TODO
+ */
+ public XmlSnapshot(final ObjectAdapter rootAdapter, OidMarshaller oidMarshaller) {
+ this(rootAdapter, new XmlSchema(), oidMarshaller);
+ }
+
+ /**
+ * Start a snapshot at the root object, using supplied namespace manager.
+ * @param oidMarshaller TODO
+ */
+ public XmlSnapshot(final ObjectAdapter rootAdapter, final XmlSchema schema, final OidMarshaller oidMarshaller) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(".ctor(" + log("rootObj", rootAdapter) + andlog("schema", schema) + andlog("addOids", "" + true) + ")");
+ }
+
+ this.isisMetaModel = new IsisSchema();
+ this.xsMeta = new XsMetaModel();
+
+ this.schema = schema;
+ this.oidMarshaller = oidMarshaller;
+
+ final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ DocumentBuilder db;
+ try {
+ db = dbf.newDocumentBuilder();
+ this.xmlDocument = db.newDocument();
+ this.xsdDocument = db.newDocument();
+
+ xsdElement = xsMeta.createXsSchemaElement(xsdDocument);
+
+ this.rootPlace = appendXml(rootAdapter);
+
+ } catch (final ParserConfigurationException e) {
+ LOG.error("unable to build snapshot", e);
+ throw new IsisException(e);
+ }
+
+ for (final String path : getPathsFor(rootAdapter.getObject())) {
+ include(path);
+ }
+
+ }
+
+ private List<String> getPathsFor(final Object object) {
+ if (!(object instanceof SnapshottableWithInclusions)) {
+ return Collections.emptyList();
+ }
+ final List<String> paths = ((SnapshottableWithInclusions) object).snapshotInclusions();
+ if (paths == null) {
+ return Collections.emptyList();
+ }
+ return paths;
+ }
+
+ private String andlog(final String label, final ObjectAdapter object) {
+ return ", " + log(label, object);
+ }
+
+ private String andlog(final String label, final Object object) {
+ return ", " + log(label, object);
+ }
+
+ /**
+ * Creates an Element representing this object, and appends it as the root
+ * element of the Document.
+ *
+ * The Document must not yet have a root element Additionally, the supplied
+ * schemaManager must be populated with any application-level namespaces
+ * referenced in the document that the parentElement resides within.
+ * (Normally this is achieved simply by using appendXml passing in a new
+ * schemaManager - see {@link #toXml()}or {@link XmlSnapshot}).
+ */
+ private Place appendXml(final ObjectAdapter object) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXml(" + log("obj", object) + "')");
+ }
+
+ final String fullyQualifiedClassName = object.getSpecification().getFullIdentifier();
+
+ schema.setUri(fullyQualifiedClassName); // derive
+ // URI
+ // from
+ // fully
+ // qualified
+ // name
+
+ final Place place = objectToElement(object);
+
+ final Element element = place.getXmlElement();
+ final Element xsElementElement = place.getXsdElement();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXml(NO): add as element to XML doc");
+ }
+ getXmlDocument().appendChild(element);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXml(NO): add as xs:element to xs:schema of the XSD document");
+ }
+ getXsdElement().appendChild(xsElementElement);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXml(NO): set target name in XSD, derived from FQCN of obj");
+ }
+ schema.setTargetNamespace(getXsdDocument(), fullyQualifiedClassName);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXml(NO): set schema location file name to XSD, derived from FQCN of obj");
+ }
+ final String schemaLocationFileName = fullyQualifiedClassName + ".xsd";
+ schema.assignSchema(getXmlDocument(), fullyQualifiedClassName, schemaLocationFileName);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXml(NO): copy into snapshot obj");
+ }
+ setXmlElement(element);
+ setSchemaLocationFileName(schemaLocationFileName);
+
+ return place;
+ }
+
+ /**
+ * Creates an Element representing this object, and appends it to the
+ * supplied parentElement, provided that an element for the object is not
+ * already appended.
+ *
+ * The method uses the OID to determine if an object's element is already
+ * present. If the object is not yet persistent, then the hashCode is used
+ * instead.
+ *
+ * The parentElement must have an owner document, and should define the nof
+ * namespace. Additionally, the supplied schemaManager must be populated
+ * with any application-level namespaces referenced in the document that the
+ * parentElement resides within. (Normally this is achieved simply by using
+ * appendXml passing in a rootElement and a new schemaManager - see
+ * {@link #toXml()}or {@link XmlSnapshot}).
+ */
+ private Element appendXml(final Place parentPlace, final ObjectAdapter childObject) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXml(" + log("parentPlace", parentPlace) + andlog("childObj", childObject) + ")");
+ }
+
+ final Element parentElement = parentPlace.getXmlElement();
+ final Element parentXsElement = parentPlace.getXsdElement();
+
+ if (parentElement.getOwnerDocument() != getXmlDocument()) {
+ throw new IllegalArgumentException("parent XML Element must have snapshot's XML document as its owner");
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXml(Pl, NO): invoking objectToElement() for " + log("childObj", childObject));
+ }
+ final Place childPlace = objectToElement(childObject);
+ Element childElement = childPlace.getXmlElement();
+ final Element childXsElement = childPlace.getXsdElement();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXml(Pl, NO): invoking mergeTree of parent with child");
+ }
+ childElement = mergeTree(parentElement, childElement);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXml(Pl, NO): adding XS Element to schema if required");
+ }
+ schema.addXsElementIfNotPresent(parentXsElement, childXsElement);
+
+ return childElement;
+ }
+
+ private boolean appendXmlThenIncludeRemaining(final Place parentPlace, final ObjectAdapter referencedObject, final Vector fieldNames, final String annotation) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXmlThenIncludeRemaining(: " + log("parentPlace", parentPlace) + andlog("referencedObj", referencedObject) + andlog("fieldNames", fieldNames) + andlog("annotation", annotation) + ")");
+ LOG.debug("appendXmlThenIncludeRemaining(..): invoking appendXml(parentPlace, referencedObject)");
+ }
+
+ final Element referencedElement = appendXml(parentPlace, referencedObject);
+ final Place referencedPlace = new Place(referencedObject, referencedElement);
+
+ final boolean includedField = includeField(referencedPlace, fieldNames, annotation);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("appendXmlThenIncludeRemaining(..): invoked includeField(referencedPlace, fieldNames)" + andlog("returned", "" + includedField));
+ }
+
+ return includedField;
+ }
+
+ private Vector elementsUnder(final Element parentElement, final String localName) {
+ final Vector v = new Vector();
+ final NodeList existingNodes = parentElement.getChildNodes();
+ for (int i = 0; i < existingNodes.getLength(); i++) {
+ final Node node = existingNodes.item(i);
+ if (!(node instanceof Element)) {
+ continue;
+ }
+ final Element element = (Element) node;
+ if (localName.equals("*") || element.getLocalName().equals(localName)) {
+ v.addElement(element);
+ }
+ }
+ return v;
+ }
+
+ public ObjectAdapter getObject() {
+ return rootPlace.getObject();
+ }
+
+ public XmlSchema getSchema() {
+ return schema;
+ }
+
+ /**
+ * The name of the <code>xsi:schemaLocation</code> in the XML document.
+ *
+ * Taken from the <code>fullyQualifiedClassName</code> (which also is used
+ * as the basis for the <code>targetNamespace</code>.
+ *
+ * Populated in {@link #appendXml(ObjectAdapter)}.
+ */
+ public String getSchemaLocationFileName() {
+ return schemaLocationFileName;
+ }
+
+ public Document getXmlDocument() {
+ return xmlDocument;
+ }
+
+ /**
+ * The root element of {@link #getXmlDocument()}. Returns <code>null</code>
+ * until the snapshot has actually been built.
+ */
+ public Element getXmlElement() {
+ return xmlElement;
+ }
+
+ public Document getXsdDocument() {
+ return xsdDocument;
+ }
+
+ /**
+ * The root element of {@link #getXsdDocument()}. Returns <code>null</code>
+ * until the snapshot has actually been built.
+ */
+ public Element getXsdElement() {
+ return xsdElement;
+ }
+
+ public void include(final String path) {
+ include(path, null);
+ }
+
+ public void include(final String path, final String annotation) {
+
+ // tokenize into successive fields
+ final Vector fieldNames = new Vector();
+ for (final StringTokenizer tok = new StringTokenizer(path, "/"); tok.hasMoreTokens();) {
+ final String token = tok.nextToken();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("include(..): " + log("token", token));
+ }
+ fieldNames.addElement(token);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("include(..): " + log("fieldNames", fieldNames));
+ }
+
+ // navigate first field, from the root.
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("include(..): invoking includeField");
+ }
+ includeField(rootPlace, fieldNames, annotation);
+ }
+
+ /**
+ * @return true if able to navigate the complete vector of field names
+ * successfully; false if a field could not be located or it turned
+ * out to be a value.
+ */
+ private boolean includeField(final Place place, final Vector fieldNames, final String annotation) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("includeField(: " + log("place", place) + andlog("fieldNames", fieldNames) + andlog("annotation", annotation) + ")");
+ }
+
+ final ObjectAdapter object = place.getObject();
+ final Element xmlElement = place.getXmlElement();
+
+ // we use a copy of the path so that we can safely traverse collections
+ // without side-effects
+ final Vector originalNames = fieldNames;
+ final Vector names = new Vector();
+ for (final java.util.Enumeration e = originalNames.elements(); e.hasMoreElements();) {
+ names.addElement(e.nextElement());
+ }
+
+ // see if we have any fields to process
+ if (names.size() == 0) {
+ return true;
+ }
+
+ // take the first field name from the list, and remove
+ final String fieldName = (String) names.elementAt(0);
+ names.removeElementAt(0);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("includeField(Pl, Vec, Str):" + log("processing field", fieldName) + andlog("left", "" + names.size()));
+ }
+
+ // locate the field in the object's class
+ final ObjectSpecification nos = object.getSpecification();
+ ObjectAssociation field = null;
+ try {
+ // HACK: really want a ObjectSpecification.hasField method to
+ // check first.
+ field = nos.getAssociation(fieldName);
+ } catch (final ObjectSpecificationException ex) {
+ if (LOG.isInfoEnabled()) {
+ LOG.info("includeField(Pl, Vec, Str): could not locate field, skipping");
+ }
+ return false;
+ }
+
+ // locate the corresponding XML element
+ // (the corresponding XSD element will later be attached to xmlElement
+ // as its userData)
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("includeField(Pl, Vec, Str): locating corresponding XML element");
+ }
+ final Vector xmlFieldElements = elementsUnder(xmlElement, field.getId());
+ if (xmlFieldElements.size() != 1) {
+ if (LOG.isInfoEnabled()) {
+ LOG.info("includeField(Pl, Vec, Str): could not locate " + log("field", field.getId()) + andlog("xmlFieldElements.size", "" + xmlFieldElements.size()));
+ }
+ return false;
+ }
+ final Element xmlFieldElement = (Element) xmlFieldElements.elementAt(0);
+
+ if (names.size() == 0 && annotation != null) {
+ // nothing left in the path, so we will apply the annotation now
+ isisMetaModel.setAnnotationAttribute(xmlFieldElement, annotation);
+ }
+
+ final Place fieldPlace = new Place(object, xmlFieldElement);
+
+ if (field instanceof OneToOneAssociation) {
+ if (field.getSpecification().getAssociations().size() == 0) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("includeField(Pl, Vec, Str): field is value; done");
+ }
+ return false;
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("includeField(Pl, Vec, Str): field is 1->1");
+ }
+
+ final OneToOneAssociation oneToOneAssociation = ((OneToOneAssociation) field);
+ final ObjectAdapter referencedObject = oneToOneAssociation.get(fieldPlace.getObject());
+
+ if (referencedObject == null) {
+ return true; // not a failure if the reference was null
+ }
+
+ final boolean appendedXml = appendXmlThenIncludeRemaining(fieldPlace, referencedObject, names, annotation);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("includeField(Pl, Vec, Str): 1->1: invoked appendXmlThenIncludeRemaining for " + log("referencedObj", referencedObject) + andlog("returned", "" + appendedXml));
+ }
+
+ return appendedXml;
+
+ } else if (field instanceof OneToManyAssociation) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("includeField(Pl, Vec, Str): field is 1->M");
+ }
+
+ final OneToManyAssociation oneToManyAssociation = (OneToManyAssociation) field;
+ final ObjectAdapter collection = oneToManyAssociation.get(fieldPlace.getObject());
+ final CollectionFacet facet = collection.getSpecification().getFacet(CollectionFacet.class);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("includeField(Pl, Vec, Str): 1->M: " + log("collection.size", "" + facet.size(collection)));
+ }
+ boolean allFieldsNavigated = true;
+ final Enumeration elements = facet.elements(collection);
+ while (elements.hasMoreElements()) {
+ final ObjectAdapter referencedObject = (ObjectAdapter) elements.nextElement();
+ final boolean appendedXml = appendXmlThenIncludeRemaining(fieldPlace, referencedObject, names, annotation);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("includeField(Pl, Vec, Str): 1->M: + invoked appendXmlThenIncludeRemaining for " + log("referencedObj", referencedObject) + andlog("returned", "" + appendedXml));
+ }
+ allFieldsNavigated = allFieldsNavigated && appendedXml;
+ }
+ LOG.debug("includeField(Pl, Vec, Str): " + log("returning", "" + allFieldsNavigated));
+ return allFieldsNavigated;
+ }
+
+ return false; // fall through, shouldn't get here but just in
+ // case.
+ }
+
+ private String log(final String label, final ObjectAdapter adapter) {
+ return log(label, (adapter == null ? "(null)" : adapter.titleString() + "[" + oidAsString(adapter) + "]"));
+ }
+
+ private String log(final String label, final Object pojo) {
+ return (label == null ? "?" : label) + "='" + (pojo == null ? "(null)" : pojo.toString()) + "'";
+ }
+
+ /**
+ * Merges the tree of Elements whose root is <code>childElement</code>
+ * underneath the <code>parentElement</code>.
+ *
+ * If the <code>parentElement</code> already has an element that matches the
+ * <code>childElement</code>, then recursively attaches the grandchildren
+ * instead.
+ *
+ * The element returned will be either the supplied
+ * <code>childElement</code>, or an existing child element if one already
+ * existed under <code>parentElement</code>.
+ */
+ private Element mergeTree(final Element parentElement, final Element childElement) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("mergeTree(" + log("parent", parentElement) + andlog("child", childElement));
+ }
+
+ final String childElementOid = isisMetaModel.getAttribute(childElement, "oid");
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("mergeTree(El,El): " + log("childOid", childElementOid));
+ }
+ if (childElementOid != null) {
+
+ // before we add the child element, check to see if it is already
+ // there
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("mergeTree(El,El): check if child already there");
+ }
+ final Vector existingChildElements = elementsUnder(parentElement, childElement.getLocalName());
+ for (final Enumeration childEnum = existingChildElements.elements(); childEnum.hasMoreElements();) {
+ final Element possibleMatchingElement = (Element) childEnum.nextElement();
+
+ final String possibleMatchOid = isisMetaModel.getAttribute(possibleMatchingElement, "oid");
+ if (possibleMatchOid == null || !possibleMatchOid.equals(childElementOid)) {
+ continue;
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("mergeTree(El,El): child already there; merging grandchildren");
+ }
+
+ // match: transfer the children of the child (grandchildren) to
+ // the
+ // already existing matching child
+ final Element existingChildElement = possibleMatchingElement;
+ final Vector grandchildrenElements = elementsUnder(childElement, "*");
+ for (final Enumeration grandchildEnum = grandchildrenElements.elements(); grandchildEnum.hasMoreElements();) {
+ final Element grandchildElement = (Element) grandchildEnum.nextElement();
+ childElement.removeChild(grandchildElement);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("mergeTree(El,El): merging " + log("grandchild", grandchildElement));
+ }
+
+ mergeTree(existingChildElement, grandchildElement);
+ }
+ return existingChildElement;
+ }
+ }
+
+ parentElement.appendChild(childElement);
+ return childElement;
+ }
+
+ Place objectToElement(final ObjectAdapter object) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("objectToElement(" + log("object", object) + ")");
+ }
+
+ final ObjectSpecification nos = object.getSpecification();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("objectToElement(NO): create element and nof:title");
+ }
+ final Element element = schema.createElement(getXmlDocument(), nos.getShortIdentifier(), nos.getFullIdentifier(), nos.getSingularName(), nos.getPluralName());
+ isisMetaModel.appendNofTitle(element, object.titleString());
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("objectToElement(NO): create XS element for NOF class");
+ }
+ final Element xsElement = schema.createXsElementForNofClass(getXsdDocument(), element, topLevelElementWritten, FacetUtil.getFacetsByType(nos));
+
+ // hack: every element in the XSD schema apart from first needs minimum
+ // cardinality setting.
+ topLevelElementWritten = true;
+
+ final Place place = new Place(object, element);
+
+ isisMetaModel.setAttributesForClass(element, oidAsString(object).toString());
+
+ final List<ObjectAssociation> fields = nos.getAssociations();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("objectToElement(NO): processing fields");
+ }
+ eachField: for (int i = 0; i < fields.size(); i++) {
+ final ObjectAssociation field = fields.get(i);
+ final String fieldName = field.getId();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("objectToElement(NO): " + log("field", fieldName));
+ }
+
+ // Skip field if we have seen the name already
+ // This is a workaround for getLastActivity(). This method exists
+ // in AbstractObjectAdapter, but is not (at some level) being picked
+ // up
+ // by the dot-net reflector as a property. On the other hand it does
+ // exist as a field in the meta model (ObjectSpecification).
+ //
+ // Now, to re-expose the lastactivity field for .Net, a
+ // deriveLastActivity()
+ // has been added to BusinessObject. This caused another field of
+ // the
+ // same name, ultimately breaking the XSD.
+ for (int j = 0; j < i; j++) {
+ if (fieldName.equals(fields.get(i).getName())) {
+ LOG.debug("objectToElement(NO): " + log("field", fieldName) + " SKIPPED");
+ continue eachField;
+ }
+ }
+
+ Element xmlFieldElement = getXmlDocument().createElementNS(schema.getUri(), // scoped
+ // by
+ // namespace
+ // of class of
+ // containing object
+ schema.getPrefix() + ":" + fieldName);
+
+ Element xsdFieldElement = null;
+
+ if (field.getSpecification().containsFacet(ValueFacet.class)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("objectToElement(NO): " + log("field", fieldName) + " is value");
+ }
+
+ final ObjectSpecification fieldNos = field.getSpecification();
+ // skip fields of type XmlValue
+ if (fieldNos == null) {
+ continue eachField;
+ }
+ if (fieldNos.getFullIdentifier() != null && fieldNos.getFullIdentifier().endsWith("XmlValue")) {
+ continue eachField;
+ }
+
+ final OneToOneAssociation valueAssociation = ((OneToOneAssociation) field);
+ final Element xmlValueElement = xmlFieldElement; // more
+ // meaningful
+ // locally
+ // scoped name
+
+ ObjectAdapter value;
+ try {
+ value = valueAssociation.get(object);
+
+ final ObjectSpecification valueNos = value.getSpecification();
+
+ // XML
+ isisMetaModel.setAttributesForValue(xmlValueElement, valueNos.getShortIdentifier());
+
+ // return parsed string, else encoded string, else title.
+ String valueStr;
+ final ParseableFacet parseableFacet = fieldNos.getFacet(ParseableFacet.class);
+ final EncodableFacet encodeableFacet = fieldNos.getFacet(EncodableFacet.class);
+ if (parseableFacet != null) {
+ valueStr = parseableFacet.parseableTitle(value);
+ } else if (encodeableFacet != null) {
+ valueStr = encodeableFacet.toEncodedString(value);
+ } else {
+ valueStr = value.titleString();
+ }
+
+ final boolean notEmpty = (valueStr.length() > 0);
+ if (notEmpty) {
+ xmlValueElement.appendChild(getXmlDocument().createTextNode(valueStr));
+ } else {
+ isisMetaModel.setIsEmptyAttribute(xmlValueElement, true);
+ }
+
+ } catch (final Exception ex) {
+ LOG.warn("objectToElement(NO): " + log("field", fieldName) + ": getField() threw exception - skipping XML generation");
+ }
+
+ // XSD
+ xsdFieldElement = schema.createXsElementForNofValue(xsElement, xmlValueElement, FacetUtil.getFacetsByType(valueAssociation));
+
+ } else if (field instanceof OneToOneAssociation) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("objectToElement(NO): " + log("field", fieldName) + " is OneToOneAssociation");
+ }
+
+ final OneToOneAssociation oneToOneAssociation = ((OneToOneAssociation) field);
+ final String fullyQualifiedClassName = nos.getFullIdentifier();
+ final Element xmlReferenceElement = xmlFieldElement; // more
+ // meaningful
+ // locally
+ // scoped
+ // name
+
+ ObjectAdapter referencedObjectAdapter;
+
+ try {
+ referencedObjectAdapter = oneToOneAssociation.get(object);
+
+ // XML
+ isisMetaModel.setAttributesForReference(xmlReferenceElement, schema.getPrefix(), fullyQualifiedClassName);
+
+ if (referencedObjectAdapter != null) {
+ isisMetaModel.appendNofTitle(xmlReferenceElement, referencedObjectAdapter.titleString());
+ } else {
+ isisMetaModel.setIsEmptyAttribute(xmlReferenceElement, true);
+ }
+
+ } catch (final Exception ex) {
+ LOG.warn("objectToElement(NO): " + log("field", fieldName) + ": getAssociation() threw exception - skipping XML generation");
+ }
+
+ // XSD
+ xsdFieldElement = schema.createXsElementForNofReference(xsElement, xmlReferenceElement, oneToOneAssociation.getSpecification().getFullIdentifier(), FacetUtil.getFacetsByType(oneToOneAssociation));
+
+ } else if (field instanceof OneToManyAssociation) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("objectToElement(NO): " + log("field", fieldName) + " is OneToManyAssociation");
+ }
+
+ final OneToManyAssociation oneToManyAssociation = (OneToManyAssociation) field;
+ final Element xmlCollectionElement = xmlFieldElement; // more meaningful locally scoped name
+
+ ObjectAdapter collection;
+ try {
+ collection = oneToManyAssociation.get(object);
+ final ObjectSpecification referencedTypeNos = oneToManyAssociation.getSpecification();
+ final String fullyQualifiedClassName = referencedTypeNos.getFullIdentifier();
+
+ // XML
+ isisMetaModel.setIsisCollection(xmlCollectionElement, schema.getPrefix(), fullyQualifiedClassName, collection);
+ } catch (final Exception ex) {
+ LOG.warn("objectToElement(NO): " + log("field", fieldName) + ": get(obj) threw exception - skipping XML generation");
+ }
+
+ // XSD
+ xsdFieldElement = schema.createXsElementForNofCollection(xsElement, xmlCollectionElement, oneToManyAssociation.getSpecification().getFullIdentifier(), FacetUtil.getFacetsByType(oneToManyAssociation));
+
+ } else {
+ if (LOG.isInfoEnabled()) {
+ LOG.info("objectToElement(NO): " + log("field", fieldName) + " is unknown type; ignored");
+ }
+ continue;
+ }
+
+ if (xsdFieldElement != null) {
+ Place.setXsdElement(xmlFieldElement, xsdFieldElement);
+ }
+
+ // XML
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("objectToElement(NO): invoking mergeTree for field");
+ }
+ xmlFieldElement = mergeTree(element, xmlFieldElement);
+
+ // XSD
+ if (xsdFieldElement != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("objectToElement(NO): adding XS element for field to schema");
+ }
+ schema.addFieldXsElement(xsElement, xsdFieldElement);
+ }
+ }
+
+ return place;
+ }
+
+ private String oidAsString(final ObjectAdapter adapter) {
+ return adapter.getOid().enString(oidMarshaller);
+ }
+
+ /**
+ * @param schemaLocationFileName
+ * The schemaLocationFileName to set.
+ */
+ private void setSchemaLocationFileName(final String schemaLocationFileName) {
+ this.schemaLocationFileName = schemaLocationFileName;
+ }
+
+ /**
+ * @param xmlElement
+ * The xmlElement to set.
+ */
+ private void setXmlElement(final Element xmlElement) {
+ this.xmlElement = xmlElement;
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/6de87443/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XsMetaModel.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XsMetaModel.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XsMetaModel.java
new file mode 100644
index 0000000..4d759e9
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/snapshot/XsMetaModel.java
@@ -0,0 +1,337 @@
+/*
+ * 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 nofMeta;
+
+ public XsMetaModel() {
+ this.helper = new Helper();
+ this.nofMeta = 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");
+
+ nofMeta.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="nof:xxx" element, and appends to specified
+ * owning element.
+ */
+ Element addXsNofAttribute(final Element parentXsElement, final String nofAttributeRef) {
+ return addXsNofAttribute(parentXsElement, nofAttributeRef, null);
+ }
+
+ /**
+ * Adds <code>xs:attribute ref="nof:xxx" fixed="yyy"</code> element, and
+ * appends to specified parent XSD element.
+ */
+ Element addXsNofAttribute(final Element parentXsElement, final String nofAttributeRef, final String fixedValue) {
+ return addXsNofAttribute(parentXsElement, nofAttributeRef, fixedValue, true);
+ }
+
+ /**
+ * Adds <code>xs:attribute ref="nof: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 addXsNofAttribute(final Element parentXsElement, final String nofAttributeRef, final String value, final boolean useFixed) {
+ final Element xsNofAttributeElement = createXsElement(helper.docFor(parentXsElement), "attribute");
+ xsNofAttributeElement.setAttribute("ref", IsisSchema.NS_PREFIX + ":" + nofAttributeRef);
+ parentXsElement.appendChild(xsNofAttributeElement);
+ if (value != null) {
+ if (useFixed) {
+ xsNofAttributeElement.setAttribute("fixed", value);
+ } else {
+ xsNofAttributeElement.setAttribute("default", value);
+ }
+ }
+ return parentXsElement;
+ }
+
+ /**
+ * Adds <code>xs:attribute ref="nof:feature" fixed="(feature)"</code>
+ * element as child to supplied XSD element, presumed to be an
+ * <xs:complexType</code>.
+ */
+ Element addXsNofFeatureAttributeElements(final Element parentXsElement, final String feature) {
+ final Element xsNofFeatureAttributeElement = createXsElement(helper.docFor(parentXsElement), "attribute");
+ xsNofFeatureAttributeElement.setAttribute("ref", "nof: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;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/6de87443/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/sysout/SystemPrinter.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/sysout/SystemPrinter.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/sysout/SystemPrinter.java
new file mode 100644
index 0000000..76c6776
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/sysout/SystemPrinter.java
@@ -0,0 +1,148 @@
+/*
+ * 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.sysout;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.io.LineNumberReader;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.TimeZone;
+
+import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.commons.lang.IoUtils;
+import org.apache.isis.core.runtime.about.AboutIsis;
+
+public class SystemPrinter {
+
+ private final PrintStream output;
+
+ public SystemPrinter() {
+ this(System.out);
+ }
+
+ public SystemPrinter(final PrintStream output) {
+ this.output = output;
+ }
+
+ protected PrintStream getOutput() {
+ return output;
+ }
+
+ void print(final String string) {
+ output.println(string);
+ }
+
+ void printBlock(final String title) {
+ print("");
+ print("------------------------------------------");
+ print(title);
+ print("------------------------------------------");
+ }
+
+ public void printDiagnostics() {
+ print("------- Apache Isis diagnostics report -------");
+ printVersion();
+
+ printBlock("System properties");
+ final Properties properties = System.getProperties();
+ final Enumeration<?> propertyNames = properties.propertyNames();
+ while (propertyNames.hasMoreElements()) {
+ final String name = (String) propertyNames.nextElement();
+ final String property = properties.getProperty(name);
+ final StringBuilder buf = new StringBuilder();
+ if (name.endsWith(".path") || name.endsWith(".dirs")) {
+ final String[] split = property.split(":");
+ buf.append(split[0]);
+ for (int i = 1; i < split.length; i++) {
+ buf.append("\n\t\t" + split[i]);
+ }
+ }
+ print(name + "= " + buf.toString());
+ }
+
+ File file = new File("../lib");
+ if (file.isDirectory()) {
+ final String[] files = file.list(new FilenameFilter() {
+ @Override
+ public boolean accept(final File dir, final String name) {
+ return name.endsWith(".jar");
+ }
+ });
+ printBlock("Libs");
+ for (final String file2 : files) {
+ print(file2);
+ }
+ }
+
+ printBlock("Locale information");
+ print("Default locale: " + Locale.getDefault());
+ print("Default timezone: " + TimeZone.getDefault());
+
+ file = new File("config");
+ if (file.isDirectory()) {
+ final String[] files = file.list(new FilenameFilter() {
+ @Override
+ public boolean accept(final File dir, final String name) {
+ return new File(dir, name).isFile();
+ }
+ });
+ printBlock("Config files");
+ for (final String file2 : files) {
+ print(file2);
+ }
+
+ for (final String file2 : files) {
+ print("");
+ print("--------------------------------------------------------------------------------------------------------");
+ print(file2);
+ print("");
+ LineNumberReader fileInputStream = null;
+ try {
+ fileInputStream = new LineNumberReader(new FileReader(new File(file, file2)));
+ String line;
+ while ((line = fileInputStream.readLine()) != null) {
+ print(fileInputStream.getLineNumber() + " " + line);
+ }
+ } catch (final Exception e) {
+ throw new IsisException(e);
+ } finally {
+ IoUtils.closeSafely(fileInputStream);
+ }
+ print("");
+ }
+
+ }
+ }
+
+ public void printVersion() {
+ final String date = AboutIsis.getFrameworkCompileDate();
+ final String compileDate = date == null ? "" : ", compiled on " + date;
+ print(AboutIsis.getFrameworkName() + ", " + AboutIsis.getFrameworkVersion() + compileDate);
+ }
+
+ public void printErrorMessage(final String message) {
+ output.println("Error: " + message);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/6de87443/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/userprofile/Options.java
----------------------------------------------------------------------
diff --git a/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/userprofile/Options.java b/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/userprofile/Options.java
new file mode 100644
index 0000000..a959889
--- /dev/null
+++ b/framework/core/metamodel/src/main/java/org/apache/isis/core/runtime/userprofile/Options.java
@@ -0,0 +1,113 @@
+/*
+ * 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.userprofile;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.apache.isis.core.commons.debug.DebugBuilder;
+import org.apache.isis.core.commons.debug.DebuggableWithTitle;
+
+public class Options implements DebuggableWithTitle {
+
+ private final Properties properties = new Properties();
+
+ public void addOption(final String name, final String value) {
+ properties.put(name, value);
+ }
+
+ public void addOptions(final String name, final Options options) {
+ properties.put(name, options);
+ }
+
+ public Iterator<String> names() {
+ final Enumeration<?> propertyNames = properties.propertyNames();
+ return new Iterator<String>() {
+ @Override
+ public boolean hasNext() {
+ return propertyNames.hasMoreElements();
+ }
+
+ @Override
+ public String next() {
+ return (String) propertyNames.nextElement();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public String getString(final String name) {
+ return properties.getProperty(name);
+ }
+
+ public String getString(final String name, final String defaultValue) {
+ return properties.getProperty(name, defaultValue);
+ }
+
+ public int getInteger(final String name, final int defaultValue) {
+ final String value = getString(name);
+ if (value == null) {
+ return defaultValue;
+ } else {
+ return Integer.valueOf(value).intValue();
+ }
+ }
+
+ public Options getOptions(final String name) {
+ Options options = (Options) properties.get(name);
+ if (options == null) {
+ options = new Options();
+ addOptions(name, options);
+ }
+ return options;
+ }
+
+ public boolean isOptions(final String name) {
+ return properties.get(name) instanceof Options;
+ }
+
+ public void copy(final Options options) {
+ properties.putAll(options.properties);
+ }
+
+ // ///////////////////////////////
+ // Debugging
+ // ///////////////////////////////
+
+ @Override
+ public String debugTitle() {
+ return "Options";
+ }
+
+ @Override
+ public void debugData(final DebugBuilder debug) {
+ final Enumeration<Object> keys = properties.keys();
+ while (keys.hasMoreElements()) {
+ final String name = (String) keys.nextElement();
+ debug.appendln(name, properties.get(name));
+ }
+ }
+
+}