Index: dna-graph/pom.xml
===================================================================
--- dna-graph/pom.xml (revision 854)
+++ dna-graph/pom.xml (working copy)
@@ -32,6 +32,11 @@
joda-timejoda-time
+
+ com.google.code.google-collections
+ google-collect
+ snapshot-20080530
+
Index: dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java
===================================================================
--- dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java (revision 854)
+++ dna-graph/src/main/java/org/jboss/dna/graph/io/Destination.java (working copy)
@@ -66,6 +66,15 @@
Property... additionalProperties );
/**
+ * Sets the given properties on the node at the supplied path. The path will be absolute.
+ *
+ * @param path the absolute path of the node
+ * @param properties the remaining properties for the node
+ */
+ public void setProperties( Path path,
+ Property... properties );
+
+ /**
* Signal to this destination that any enqueued create requests should be submitted. Usually this happens at the end of the
* document parsing, but an implementer must allow for it to be called multiple times and anytime during parsing.
*/
Index: dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java
===================================================================
--- dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java (revision 854)
+++ dna-graph/src/main/java/org/jboss/dna/graph/io/GraphBatchDestination.java (working copy)
@@ -117,6 +117,18 @@
/**
* {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.io.Destination#setProperties(org.jboss.dna.graph.property.Path, org.jboss.dna.graph.property.Property[])
+ */
+ public void setProperties( Path path,
+ Property... properties ) {
+ if (properties == null) return;
+
+ batch.set(properties).on(path);
+ }
+
+ /**
+ * {@inheritDoc}
*
* @see org.jboss.dna.graph.io.Destination#submit()
*/
Index: dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java
===================================================================
--- dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java (revision 854)
+++ dna-graph/src/main/java/org/jboss/dna/graph/request/BatchRequestBuilder.java (working copy)
@@ -94,7 +94,7 @@
return newRequest;
}
// We have at least one other request, so add the pending request ...
- add(newRequest);
+ addPending();
++number;
} else {
// There is no pending request ...
Index: dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java
===================================================================
--- dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java (revision 854)
+++ dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlHandler.java (working copy)
@@ -24,6 +24,8 @@
package org.jboss.dna.graph.xml;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -45,6 +47,8 @@
import org.jboss.dna.graph.property.basic.LocalNamespaceRegistry;
import org.xml.sax.Attributes;
import org.xml.sax.ext.DefaultHandler2;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
/**
* A {@link DefaultHandler2} specialization that responds to XML content events by creating the corresponding content in the
@@ -145,6 +149,7 @@
*/
protected Path currentPath;
+
/**
* Flag the records whether the first element should be skipped.
*/
@@ -161,7 +166,20 @@
*/
protected final Object[] propertyValues = new Object[1];
+
+ /**
+ * Character buffer to aggregate nested character data
+ * @see ElementEntry
+ */
+ private StringBuilder characterDataBuffer = new StringBuilder();
+
/**
+ * Stack of pending {@link ElementEntry element entries} from the root of the imported content to the current node.
+ * @see ElementEntry
+ */
+ private final LinkedList elementStack = new LinkedList();
+
+ /**
* Create a handler that creates content in the supplied graph
*
* @param destination the destination where the content should be sent.graph in which the content should be placed
@@ -310,6 +328,16 @@
assert localName != null;
Name nodeName = null;
+ ElementEntry element;
+ if (!elementStack.isEmpty()) {
+ // Add the parent
+ elementStack.peek().addAsNode();
+ element = new ElementEntry(elementStack.peek(), currentPath, null);
+ } else {
+ element = new ElementEntry(null, currentPath, null);
+ }
+ elementStack.push(element);
+
properties.clear();
Object typePropertyValue = null;
// Convert each of the attributes to a property ...
@@ -333,6 +361,7 @@
// Check to see if this is an attribute that represents the node name (which may be null) ...
if (nodeName == null && attributeName.equals(nameAttribute)) {
nodeName = nameFactory.create(attributes.getValue(i)); // don't use a decoder
+ element.setName(nodeName);
continue;
}
if (typePropertyValue == null && attributeName.equals(typeAttribute)) {
@@ -340,13 +369,13 @@
continue;
}
// Create a property for this attribute ...
- Property property = createProperty(attributeName, attributes.getValue(i));
- properties.add(property);
+ element.addProperty(attributeName, attributes.getValue(i));
}
// Create the node name if required ...
if (nodeName == null) {
// No attribute defines the node name ...
nodeName = nameFactory.create(uri, localName, decoder);
+ element.setName(nodeName);
} else {
typePropertyValue = nameFactory.create(uri, localName, decoder);
}
@@ -354,15 +383,12 @@
// A attribute defines the node name. Set the type property, if required
if (typePropertyValue == null) typePropertyValue = typeAttributeValue;
if (typePropertyValue != null) {
- propertyValues[0] = typePropertyValue;
- Property property = propertyFactory.create(typeAttribute, propertyValues);
- properties.add(property);
+ element.addProperty(typeAttribute, typePropertyValue);
}
}
+
// Update the current path ...
- currentPath = pathFactory.create(currentPath, nodeName);
- // Create the node, and note that we don't care about same-name siblings (as the graph will correct them) ...
- destination.create(currentPath, properties);
+ currentPath = element.path();
}
/**
@@ -374,6 +400,15 @@
public void endElement( String uri,
String localName,
String name ) {
+
+ String s = characterDataBuffer.toString().trim();
+ if (s.length() > 0) {
+ elementStack.pop().addAsPropertySetTo(s);
+ } else if (!elementStack.isEmpty()) {
+ elementStack.pop().submit();
+ }
+ characterDataBuffer = new StringBuilder();
+
// Nothing to do but to change the current path to be the parent ...
currentPath = currentPath.getParent();
}
@@ -381,6 +416,19 @@
/**
* {@inheritDoc}
*
+ * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
+ */
+ @Override
+ public void characters( char[] ch,
+ int start,
+ int length ) {
+ // Have to add this to a buffer as one logical set of character data can cause this method to fire multiple times
+ characterDataBuffer.append(ch, start, length);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.xml.sax.helpers.DefaultHandler#endDocument()
*/
@Override
@@ -402,8 +450,113 @@
protected Property createProperty( Name propertyName,
Object value ) {
propertyValues[0] = value;
- Property result = propertyFactory.create(propertyName, propertyValues);
- return result;
+ return propertyFactory.create(propertyName, propertyValues);
}
+ /**
+ * Create a property with the given name and values, obtained from an attribute name and value in the XML content.
+ *
+ * By default, this method creates a property by directly using the values as the values of the property.
+ *
+ *
+ * @param propertyName the name of the property; never null
+ * @param values the attribute values
+ * @return the property; may not be null
+ */
+ protected Property createProperty( Name propertyName,
+ Collection