/** * * 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.tuscany.samples.sdo.advanced; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import org.apache.tuscany.samples.sdo.SampleBase; import org.apache.tuscany.samples.sdo.internal.SampleInfrastructure; import commonj.sdo.DataObject; import commonj.sdo.Property; import commonj.sdo.Sequence; import commonj.sdo.Type; import commonj.sdo.helper.HelperContext; import commonj.sdo.helper.XMLDocument; import commonj.sdo.helper.XSDHelper; import commonj.sdo.impl.HelperProvider; /** * * This sample program traverses data graphs and builds up a text representation of the * data graph. As it traverses a graph it outputs commentary to the console * about what it has encountered and how it intends to process what it finds. At * the end of each traversal the text representation of the graph is printed to * the console. *

*

Running this Sample

See the main overview for instructions on how to run this * sample. */ public class PrintDataGraph extends SampleBase { StringBuffer buf = null; HelperContext scope = HelperProvider.getDefaultContext(); private int indent; private int indentIncrement = 2; public PrintDataGraph(Integer userLevel) { super(userLevel, SAMPLE_LEVEL_ADVANCED); buf = new StringBuffer(); } public static void main(String[] args) { PrintDataGraph sample = new PrintDataGraph(COMMENTARY_FOR_NOVICE); sample.run(); } /* * metadata for the sample documenting the areas of SDO that are explored */ public static int [] CORE_FUNCTION = { SDOFacets.GENERIC_DATA_GRAPH_TRAVERSAL }; public static int [] SIGNIFICANT_FUNCTION = { SDOFacets.GET_SET_PROPERTIES_BY_INSTANCE_PROPERTIES, SDOFacets.ISMANY_PROPERTIES, SDOFacets.CREATE_TYPES_USING_THE_SDO_API, SDOFacets.ACCESSING_VALUES_IN_A_SEQUENCE, SDOFacets.NON_CONTAINMENT }; public void runSample() throws Exception { commentary("This sample demonstrates a common pattern of traversing a data graph\n" + "and printing the values of its Properties. As the sample traverses a couple of\n" + "graphs it provides commentary about what it has found and what actions it\n" + "is taking, whilst building up a text representation of the graph. It then\n" + "shows you the results of its labours."); HelperContext scope = createScopeForTypes(); commentary( COMMENTARY_ALWAYS, "First we look at a data graph of a Purchase Order which has a fairly simple XML schema\n" + "and the graph's containment hierarchy has a couple of levels of depth"); loadTypesFromXMLSchemaFile(scope, SampleInfrastructure.PO_XSD_RESOURCE); XMLDocument purchaseOrder = getXMLDocumentFromFile(scope, SampleInfrastructure.PO_XML_RESOURCE); printXMLDocument(purchaseOrder); commentary(COMMENTARY_ALWAYS, "And here is the resultant view of the data graph\n\n"); System.out.println(getBuf().toString()); commentary(COMMENTARY_ALWAYS, "Next we look at a graph representing a form letter, where the Type of the\n" + "root data object is 'Sequenced'"); loadTypesFromXMLSchemaFile(scope, "letter.xsd"); DataObject letter = getDataObjectFromFile(scope, "letter.xml"); reset(); print(letter); commentary(COMMENTARY_ALWAYS, "And here is the resultant view of the data graph\n\n"); System.out.println(getBuf().toString()); } public void reset() { indent = 0; buf = new StringBuffer(); } /* * a convenience method allowing untyped access to the print function for * selected SDO artifacts */ public void print(Object sdoObject) throws Exception { if (sdoObject instanceof XMLDocument) { printXMLDocument((XMLDocument) sdoObject); } else if (sdoObject instanceof DataObject) { printDataObject((DataObject) sdoObject); } } public void printXMLDocument(XMLDocument xmlDocument) { commentary( COMMENTARY_FOR_NOVICE, "We are going to traverse a data graph that has been wrapped in an instance of XMLDocument\n" + "Amongst other things, the XMLDocument instance provides access to the root element name\n" + "and the root DataObject of the data graph.\n\n" + "xmlDocument.getRootElementName();\n" + "xmlDocument.getRootObject();", "Accessing another graph via an XMLDocument instance as we saw previously ...\n" + "xmlDocument.getRootElementName();\n" + "xmlDocument.getRootObject();"); buf.append("XMLDocument: ").append(xmlDocument.getRootElementName()); lineBreak(); incrementIndent(); printDataObject(xmlDocument.getRootObject()); decrementIndent(); } public void printDataObject(DataObject dataObject) { if (dataObject.getContainer() == null) { commentary( COMMENTARY_FOR_NOVICE, "We begin traversing the data graph by examining the root object of the graph's containment hierarchy,\n" + "making a record of the values of its Properties. As we inspect the values of the Properties of this object\n" + "if we encounter contained DataObjects, then we will recurse through the containment hierarchy of the\n" + "data graph in a depth first fashion, and create a text representaation of the graph that we'll print\n" + "out after the graph traversal has been completed.", "We are beginning to traverse another data graph from its root object, in the same way that we saw previously"); } else { commentary( COMMENTARY_FOR_NOVICE, "We have arrived at a contained dataObject in the graph, and will inspect its Property values,\n" + "recursing deeper if necessary", "Inspecting another contained dataObject"); } lineBreak(); indent(); buf.append("DataObject: "); Type type = dataObject.getType(); buf.append("Type: ").append(type.getURI()).append('#').append( type.getName()); lineBreak(); if (dataObject.getType().isSequenced()) { commentary( COMMENTARY_FOR_INTERMEDIATE, "We've encountered a DataObject in the graph for which the Type is 'Sequenced'\n" + "That is to say that the order of addition of Property values to the DataObject instance\n" + "is important, and is preserved by the DataObject\n\n" + "dataObject.getType().isSequenced();", "We've encountered another sequenced DataObject instance, and so will traverse the Property\n" + "values in the order preerved by the instance, as we saw before\n\n" + "dataObject.getType().isSequenced();"); commentary( "There's a subtlety here which we must deal with if this sample code is to\n" + "handle both Type systems that derive from XML schema, and those that come from elsewhere,\n" + "e.g. using the SDO API. If a Sequenced DataObject has a Type that comes from XML schema\n" + "then its Properties that derive from XML attributes are not ordered, whereas those that\n" + "derive from XML elements are ordered. The SDO specification doesn't say whether\n" + "the attribute related Properties should appear at the start of a Sequence or not.\n" + "Currently in Tuscany we leave them out of the Sequence; other SDO implementations may\n" + "include the XML attributes in the Sequence. This sample code is written to deal with\n" + "either approach\n." + "We use the XSDHelper.isAttribute(Property) and isElement(Property) methods to distinguish\n" + "between the two kinds of Property", "Examining the xml attributes and elements of a Sequenced DataObject again." ); XSDHelper xsdHelper = getScope().getXSDHelper(); incrementIndent(); for(Iterator it=dataObject.getInstanceProperties().iterator(); it.hasNext();) { Property property = (Property)it.next(); if (xsdHelper.isAttribute(property)) { indent(); buf.append("Property (XML Attribute): ").append(property.getName()).append(" - ").append(dataObject.get(property)); lineBreak(); } } decrementIndent(); Sequence seq = dataObject.getSequence(); commentary( "The Property/Value pairs of a Sequence can be accessed via the getProperty(int) and getValue(int)\n" + "accessor methods of the Sequence interface. The size() method of the Sequence tells us how many there are.\n" + "If the getProperty(int) method retunes null, then the value is text. These text values may be encountered\n" + "when the DataObject's type is 'mixed' (dataObject.getType().isMixed() == true). A typical example of this\n" + "is when the data graph represents a form letter.", "Inspecting the Property/Value pairs of another Sequence"); incrementIndent(); indent(); buf.append("Sequence: {\n"); incrementIndent(); for (int i = 0; i < seq.size(); i++) { Property p = seq.getProperty(i); if (p == null) { indent(); buf.append("text: ").append(seq.getValue(i)); lineBreak(); } else if(!xsdHelper.isAttribute(p)){ printPropertyValuePair(p, seq.getValue(i)); } } decrementIndent(); indent(); buf.append("}\n"); decrementIndent(); } else { incrementIndent(); commentary( COMMENTARY_FOR_INTERMEDIATE, "We access the Property values of this DataObject by first getting the list of 'Instance Properties'\n" + "from the DataObject. For many DataObjects, this will be the same set of Properties that are defined\n" + "by the DataObject's Type. However, if the DataObject's type is 'Open' then an instance of that Type\n" + "may contain more Properties than the type itself. The list of Instance Properties will always include\n" + "the Properties defined in the Type, but will also include any Properties that the instance has values for\n" + "by virtue of it's type being 'Open'\n\n" + "for (int i = 0; i < dataObject.getInstanceProperties().size(); i++) {\n" + " Property p = (Property) dataObject.getInstanceProperties().get(i);", "Traversing the instance Properties of this DataObject\n" + "for (int i = 0; i < dataObject.getInstanceProperties().size(); i++) {\n" + " Property p = (Property) dataObject.getInstanceProperties().get(i);" ); for (int i = 0; i < dataObject.getInstanceProperties().size(); i++) { Property p = (Property) dataObject.getInstanceProperties().get(i); indent(); printValueOfProperty(dataObject, p); } decrementIndent(); } } private void printPropertyValuePair(Property p, Object value) { indent(); buf.append("Property: ").append(p.getName()).append(": "); if(p.getType().isDataType()) { printSimpleValue(value); lineBreak(); } else { if(p.isContainment()) { incrementIndent(); printDataObject((DataObject)value); decrementIndent(); } else { printReferencedDataObject((DataObject)value); } } } private void printValueOfProperty(DataObject dataObject, Property p) { commentary( COMMENTARY_FOR_INTERMEDIATE, "We are about to inspect the value of a Property, but we must\n" + "consider the nature of that Property in order to deal with it appropriately.\n" + "Firstly we see if the Property value has been set (dataObject.isSet(property))\n" + "Then we see if the Property is simple valued (property.isDataType() == true)\n" + "--if not then we know it's a DataObject and we must recurse deeper into the graph's\n" + "containment hierarchy\n" + "Whether or not the property value is a DataObject, is may be single or multi-valued\n" + "so we must either use one of the DataObject's get*(Property) accessors for single\n" + "valued Properties or the getList() method for multi-valued properties.\n" + "Another thing we must deal with when the Property is a DataObject, is whether or not the\n" + "Property is a 'containment' Property. If it isn't, then here we simply record the fact that\n" + "we have encountered this non-containment relationship, and move on to the next Property", "Inspecting another property to determine how to access it's value, as we saw before"); // TODO deal with nullable buf.append("Property ").append(p.getName()).append(": ").append(" - "); if (dataObject.isSet(p)) { if (p.getType().isDataType()) { if (p.isMany()) { printSimpleValues(dataObject.getList(p)); } else { printSimpleValue(dataObject.get(p)); } } else { if (p.isContainment()) { incrementIndent(); if (p.isMany()) { printDataObjects(dataObject.getList(p)); } else { printDataObject(dataObject.getDataObject(p)); } decrementIndent(); } else { if (p.isMany()) { printReferencedDataObjects(dataObject.getList(p)); } else { printReferencedDataObject(dataObject.getDataObject(p)); } } } } else { buf.append(" is not set"); } lineBreak(); } private void printReferencedDataObject(DataObject dataObject) { commentary( COMMENTARY_FOR_INTERMEDIATE, "We have encounted a non-containment reference to a DataObject, and so\n" + "we know that this DataObject will be fully inspected when encountered by the\n" + "traversal of the data graph's containment hierarchy.\n" + "We therefore record the fact that this association has been encountered by\n" + "establishing the path from the root object of the data graph to the referenced\n" + "DataObject", "Recording the fact that we have encountered another non-containment reference"); List path = new ArrayList(); DataObject current = dataObject; while (current != null) { Property containmentProperty = current.getContainmentProperty(); if(containmentProperty != null) { if(containmentProperty.isMany()) { List pValues = current.getContainer().getList(containmentProperty); int index = pValues.indexOf(current)+1; path.add("["+index+"]"); } path.add("/"+current.getContainmentProperty().getName()); } current = current.getContainer(); } buf.append("reference to: "); for (ListIterator i = path.listIterator(path.size()); i.hasPrevious();) { buf.append(i.previous()); } } private void printReferencedDataObjects(List list) { commentary( COMMENTARY_FOR_NOVICE, "Traversing a list of DataObjects which represent the values of a multi-valued non-containment Property"); indent(); buf.append('['); for (Iterator i = list.iterator(); i.hasNext();) { printReferencedDataObject((DataObject) i.next()); } indent(); buf.append(']'); } private void printDataObjects(List list) { commentary( COMMENTARY_FOR_NOVICE, "Traversing a list of DataObjects which represent the values of a multi-valued containment Property"); lineBreak(); indent(); buf.append("["); incrementIndent(); for (Iterator i = list.iterator(); i.hasNext();) { printDataObject((DataObject) i.next()); } decrementIndent(); indent(); buf.append(']'); } private void printSimpleValue(Object object) { buf.append(object); } private void printSimpleValues(List values) { buf.append('['); for (Iterator i = values.iterator(); i.hasNext();) { printSimpleValue(i.next()); if (i.hasNext()) { buf.append(','); } } buf.append(']'); } private void decrementIndent() { indent -= indentIncrement; } private void incrementIndent() { indent += indentIncrement; } private void indent() { for (int i = 0; i < indent; i++) { buf.append(' '); } } private void lineBreak() { buf.append('\n'); } public StringBuffer getBuf() { return buf; } public void setBuf(StringBuffer b) { buf = b; } public HelperContext getScope() { return scope; } public void setScope(HelperContext scope) { this.scope = scope; } }