blob: 6647f54501ceb38ae0313b410e01d4400e8567e2 [file] [log] [blame]
/**
*
* 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.
* <p>
* <h3>Running this Sample</h3> See <A HREF="../../../../../../index.html"
* target="_top">the main overview</A> 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 recurs through the containment hierarchy of the\n"
+ "data graph in a depth first fashion, and create a text representation 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 returns 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 recurs 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 its 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;
}
}