First sketch to implement sparse and deep parsing.
The old demo app examples still work. The new features may require some more tweaking, possibly also on the mapping prototype file syntax.
This commit is contained in:
parent
7088254cb1
commit
d6d7141350
1 changed files with 108 additions and 23 deletions
|
@ -69,25 +69,40 @@ public class XmlConverter implements XmlConstants {
|
||||||
public INode convert( InputStream in, INode helmaNode ) throws RuntimeException {
|
public INode convert( InputStream in, INode helmaNode ) throws RuntimeException {
|
||||||
Document document = XmlUtil.parse (in);
|
Document document = XmlUtil.parse (in);
|
||||||
if ( document!=null && document.getDocumentElement()!=null ) {
|
if ( document!=null && document.getDocumentElement()!=null ) {
|
||||||
return convert( document.getDocumentElement(), helmaNode );
|
return convert( document.getDocumentElement(), helmaNode, new HashMap() );
|
||||||
} else {
|
} else {
|
||||||
return helmaNode;
|
return helmaNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public INode convert( Element element, INode helmaNode ) {
|
public INode convert( Element element, INode helmaNode, Map nodeCache ) {
|
||||||
offset++;
|
offset++;
|
||||||
|
// previousNode is used to cache previous nodes with the same prototype
|
||||||
|
// so we can reset it in the nodeCache after we've run
|
||||||
|
Object previousNode = null;
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
debug("reading " + element.getNodeName() );
|
debug("reading " + element.getNodeName() );
|
||||||
helmaNode.setName( element.getNodeName() );
|
helmaNode.setName( element.getNodeName() );
|
||||||
String prototype = props.getProperty(element.getNodeName()+"._prototype");
|
String prototype = props.getProperty(element.getNodeName()+"._prototype");
|
||||||
if ( prototype == null )
|
if ( prototype == null && !sparse )
|
||||||
prototype = "HopObject";
|
prototype = "HopObject";
|
||||||
|
// if we have a prototype (either explicit or implicit "hopobject"),
|
||||||
|
// set it on the Helma node and store it in the node cache.
|
||||||
|
if ( prototype != null ) {
|
||||||
helmaNode.setPrototype( prototype );
|
helmaNode.setPrototype( prototype );
|
||||||
attributes(element, helmaNode);
|
previousNode = nodeCache.put (prototype, helmaNode);
|
||||||
if ( element.hasChildNodes() ) {
|
|
||||||
children(element, helmaNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check attributes of the current element
|
||||||
|
attributes(element, helmaNode, nodeCache);
|
||||||
|
// check child nodes of the current element
|
||||||
|
if ( element.hasChildNodes() )
|
||||||
|
children(element, helmaNode, nodeCache);
|
||||||
|
|
||||||
|
// if it exists, restore the previous node we've replaced in the node cache.
|
||||||
|
if (previousNode != null)
|
||||||
|
nodeCache.put (prototype, previousNode);
|
||||||
|
|
||||||
offset--;
|
offset--;
|
||||||
return helmaNode;
|
return helmaNode;
|
||||||
}
|
}
|
||||||
|
@ -95,9 +110,10 @@ public class XmlConverter implements XmlConstants {
|
||||||
/**
|
/**
|
||||||
* parse xml children and create hopobject-children
|
* parse xml children and create hopobject-children
|
||||||
*/
|
*/
|
||||||
private INode children( Element element, helma.objectmodel.INode helmaNode ) {
|
private INode children( Element element, helma.objectmodel.INode helmaNode, Map nodeCache ) {
|
||||||
NodeList list = element.getChildNodes();
|
NodeList list = element.getChildNodes();
|
||||||
int len = list.getLength();
|
int len = list.getLength();
|
||||||
|
boolean nodeHasPrototype = helmaNode.getPrototype() != null;
|
||||||
StringBuffer textcontent = new StringBuffer();
|
StringBuffer textcontent = new StringBuffer();
|
||||||
String domKey, helmaKey;
|
String domKey, helmaKey;
|
||||||
for ( int i=0; i<len; i++ ) {
|
for ( int i=0; i<len; i++ ) {
|
||||||
|
@ -105,16 +121,26 @@ public class XmlConverter implements XmlConstants {
|
||||||
// loop through the list of children
|
// loop through the list of children
|
||||||
org.w3c.dom.Node childNode = list.item(i);
|
org.w3c.dom.Node childNode = list.item(i);
|
||||||
|
|
||||||
|
// if the current node hasn't been initialized yet, try if it can
|
||||||
|
// be initialized and converted from one of the child elements.
|
||||||
|
if (!nodeHasPrototype) {
|
||||||
|
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||||
|
convert ((Element) childNode, helmaNode, nodeCache);
|
||||||
|
if (helmaNode.getPrototype() != null)
|
||||||
|
return helmaNode;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// if it's text content of this element -> append to StringBuffer
|
// if it's text content of this element -> append to StringBuffer
|
||||||
if ( childNode.getNodeType()==org.w3c.dom.Node.TEXT_NODE ) {
|
if ( childNode.getNodeType() == Node.TEXT_NODE ||
|
||||||
|
childNode.getNodeType() == Node.CDATA_SECTION_NODE ) {
|
||||||
textcontent.append( childNode.getNodeValue().trim() );
|
textcontent.append( childNode.getNodeValue().trim() );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: handle CDATA!
|
|
||||||
|
|
||||||
// it's some kind of element (property or child)
|
// it's some kind of element (property or child)
|
||||||
if ( childNode.getNodeType()==org.w3c.dom.Node.ELEMENT_NODE ) {
|
if ( childNode.getNodeType() == Node.ELEMENT_NODE ) {
|
||||||
|
|
||||||
Element childElement = (Element)childNode;
|
Element childElement = (Element)childNode;
|
||||||
|
|
||||||
|
@ -129,7 +155,16 @@ public class XmlConverter implements XmlConstants {
|
||||||
helmaKey = childElement.getNodeName().replace(':',defaultSeparator);
|
helmaKey = childElement.getNodeName().replace(':',defaultSeparator);
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
debug("childtext-2-property mapping, helmaKey " + helmaKey + " for domKey " + domKey );
|
debug("childtext-2-property mapping, helmaKey " + helmaKey + " for domKey " + domKey );
|
||||||
if ( helmaNode.getString(helmaKey,false)==null ) {
|
// check if helmaKey contains an explicit prototype name in which to
|
||||||
|
// set the property.
|
||||||
|
int dot = helmaKey.indexOf(".");
|
||||||
|
if (dot > -1) {
|
||||||
|
String prototype = helmaKey.substring (0, dot);
|
||||||
|
INode node = (INode) nodeCache.get (prototype);
|
||||||
|
if (node != null)
|
||||||
|
node.setString (helmaKey.substring (dot+1), XmlUtil.getTextContent (childNode));
|
||||||
|
}
|
||||||
|
if ( helmaNode.getPrototype() != null && helmaNode.getString(helmaKey,false)==null ) {
|
||||||
helmaNode.setString( helmaKey, XmlUtil.getTextContent(childNode) );
|
helmaNode.setString( helmaKey, XmlUtil.getTextContent(childNode) );
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
debug("childtext-2-property mapping, setting " + helmaKey + " as string" );
|
debug("childtext-2-property mapping, setting " + helmaKey + " as string" );
|
||||||
|
@ -147,10 +182,23 @@ public class XmlConverter implements XmlConstants {
|
||||||
helmaKey = childElement.getNodeName().replace(':',defaultSeparator);
|
helmaKey = childElement.getNodeName().replace(':',defaultSeparator);
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
debug("child-2-property mapping, helmaKey " + helmaKey + " for domKey " + domKey);
|
debug("child-2-property mapping, helmaKey " + helmaKey + " for domKey " + domKey);
|
||||||
if ( helmaNode.getNode(helmaKey,false)==null ) {
|
// get the node on which to opererate, depending on the helmaKey
|
||||||
convert( childElement, helmaNode.createNode(helmaKey) );
|
// value from the properties file.
|
||||||
|
INode node = helmaNode;
|
||||||
|
int dot = helmaKey.indexOf(".");
|
||||||
|
if (dot > -1) {
|
||||||
|
String prototype = helmaKey.substring (0, dot);
|
||||||
|
if (!prototype.equalsIgnoreCase (node.getPrototype()))
|
||||||
|
node = (INode) nodeCache.get (prototype);
|
||||||
|
helmaKey = helmaKey.substring (dot+1);
|
||||||
|
}
|
||||||
|
if (node == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( node.getNode(helmaKey,false)==null ) {
|
||||||
|
convert( childElement, node.createNode(helmaKey), nodeCache );
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
debug( "read " + childElement.toString() + helmaNode.getNode(helmaKey,false).toString() );
|
debug( "read " + childElement.toString() + node.getNode(helmaKey,false).toString() );
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -162,15 +210,29 @@ public class XmlConverter implements XmlConstants {
|
||||||
// do we need a mapping directly among _children of helmaNode?
|
// do we need a mapping directly among _children of helmaNode?
|
||||||
// can either be through property elname._children=_all or elname._children=childname
|
// can either be through property elname._children=_all or elname._children=childname
|
||||||
if( childrenMapping!=null && ( childrenMapping.equals("_all") || childrenMapping.equals(childElement.getNodeName()) ) ) {
|
if( childrenMapping!=null && ( childrenMapping.equals("_all") || childrenMapping.equals(childElement.getNodeName()) ) ) {
|
||||||
newHelmaNode = convert(childElement, helmaNode.createNode(null) );
|
newHelmaNode = convert(childElement, helmaNode.createNode(null), nodeCache );
|
||||||
}
|
}
|
||||||
// which name to choose for a virtual subnode:
|
// in which virtual subnode collection should objects of this type be stored?
|
||||||
helmaKey = props.getProperty(domKey);
|
helmaKey = props.getProperty(domKey);
|
||||||
if ( helmaKey==null ) {
|
if ( helmaKey==null && !sparse ) {
|
||||||
helmaKey = childElement.getNodeName().replace(':',defaultSeparator);
|
helmaKey = childElement.getNodeName().replace(':',defaultSeparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the node on which to opererate, depending on the helmaKey
|
||||||
|
// value from the properties file.
|
||||||
|
INode node = helmaNode;
|
||||||
|
int dot = helmaKey.indexOf(".");
|
||||||
|
if (dot > -1) {
|
||||||
|
String prototype = helmaKey.substring (0, dot);
|
||||||
|
if (!prototype.equalsIgnoreCase (node.getPrototype()))
|
||||||
|
node = (INode) nodeCache.get (prototype);
|
||||||
|
helmaKey = helmaKey.substring (dot+1);
|
||||||
|
}
|
||||||
|
if (node == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
// try to get the virtual node
|
// try to get the virtual node
|
||||||
helma.objectmodel.INode worknode = helmaNode.getNode( helmaKey, false );
|
helma.objectmodel.INode worknode = node.getNode( helmaKey, false );
|
||||||
if ( worknode==null ) {
|
if ( worknode==null ) {
|
||||||
// if virtual node doesn't exist, create it
|
// if virtual node doesn't exist, create it
|
||||||
worknode = helmaNode.createNode( helmaKey );
|
worknode = helmaNode.createNode( helmaKey );
|
||||||
|
@ -181,7 +243,7 @@ public class XmlConverter implements XmlConstants {
|
||||||
if ( newHelmaNode!=null ) {
|
if ( newHelmaNode!=null ) {
|
||||||
worknode.addNode(newHelmaNode);
|
worknode.addNode(newHelmaNode);
|
||||||
} else {
|
} else {
|
||||||
convert( childElement, worknode.createNode( null ) );
|
convert( childElement, worknode.createNode( null ), nodeCache );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// forget about other types (comments etc)
|
// forget about other types (comments etc)
|
||||||
|
@ -193,6 +255,8 @@ public class XmlConverter implements XmlConstants {
|
||||||
helmaKey = props.getProperty(element.getNodeName()+"._text");
|
helmaKey = props.getProperty(element.getNodeName()+"._text");
|
||||||
if ( helmaKey==null )
|
if ( helmaKey==null )
|
||||||
helmaKey = "text";
|
helmaKey = "text";
|
||||||
|
if (DEBUG)
|
||||||
|
debug ("setting text "+textcontent+" to property "+helmaKey+" of object "+helmaNode);
|
||||||
helmaNode.setString(helmaKey, textcontent.toString().trim() );
|
helmaNode.setString(helmaKey, textcontent.toString().trim() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,12 +266,14 @@ public class XmlConverter implements XmlConstants {
|
||||||
/**
|
/**
|
||||||
* set element's attributes as properties of helmaNode
|
* set element's attributes as properties of helmaNode
|
||||||
*/
|
*/
|
||||||
private INode attributes( Element element, INode helmaNode ) {
|
private INode attributes( Element element, INode helmaNode, Map nodeCache ) {
|
||||||
NamedNodeMap nnm = element.getAttributes();
|
NamedNodeMap nnm = element.getAttributes();
|
||||||
int len = nnm.getLength();
|
int len = nnm.getLength();
|
||||||
for ( int i=0; i<len; i++ ) {
|
for ( int i=0; i<len; i++ ) {
|
||||||
org.w3c.dom.Node attr = nnm.item(i);
|
org.w3c.dom.Node attr = nnm.item(i);
|
||||||
if ( attr.getNodeName().equals("_prototype") ) {
|
// NOTE: I can't find anything about the mapping of _prototype and _name
|
||||||
|
// attributes to Helma Node prototype or name. -Hannes
|
||||||
|
/* if ( attr.getNodeName().equals("_prototype") ) {
|
||||||
helmaNode.setPrototype( attr.getNodeValue() );
|
helmaNode.setPrototype( attr.getNodeValue() );
|
||||||
} else if ( attr.getNodeName().equals("_name") ) {
|
} else if ( attr.getNodeName().equals("_name") ) {
|
||||||
helmaNode.setName( attr.getNodeValue() );
|
helmaNode.setName( attr.getNodeValue() );
|
||||||
|
@ -216,6 +282,24 @@ public class XmlConverter implements XmlConstants {
|
||||||
if ( helmaKey==null )
|
if ( helmaKey==null )
|
||||||
helmaKey = attr.getNodeName().replace(':',defaultSeparator);
|
helmaKey = attr.getNodeName().replace(':',defaultSeparator);
|
||||||
helmaNode.setString( helmaKey, attr.getNodeValue() );
|
helmaNode.setString( helmaKey, attr.getNodeValue() );
|
||||||
|
} */
|
||||||
|
String helmaKey = props.getProperty(element.getNodeName()+"._attribute."+attr.getNodeName());
|
||||||
|
// unless we only map explicit attributes, use attribute name as property name
|
||||||
|
// in case no property name was defined.
|
||||||
|
if ( helmaKey==null && !sparse)
|
||||||
|
helmaKey = attr.getNodeName().replace(':',defaultSeparator);
|
||||||
|
if (helmaKey != null) {
|
||||||
|
// check if the mapping contains the prototype to which
|
||||||
|
// the property should be applied
|
||||||
|
int dot = helmaKey.indexOf (".");
|
||||||
|
if (dot > -1) {
|
||||||
|
String prototype = helmaKey.substring (0, dot);
|
||||||
|
INode node = (INode) nodeCache.get (prototype);
|
||||||
|
if (node != null)
|
||||||
|
node.setString (helmaKey.substring(dot+1), attr.getNodeValue());
|
||||||
|
} else if (helmaNode.getPrototype() != null) {
|
||||||
|
helmaNode.setString( helmaKey, attr.getNodeValue() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return helmaNode;
|
return helmaNode;
|
||||||
|
@ -231,6 +315,7 @@ public class XmlConverter implements XmlConstants {
|
||||||
sparse = "sparse".equalsIgnoreCase (props.getProperty("_mode"));
|
sparse = "sparse".equalsIgnoreCase (props.getProperty("_mode"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** for testing */
|
/** for testing */
|
||||||
void debug(Object msg) {
|
void debug(Object msg) {
|
||||||
for ( int i=0; i<offset; i++ ) {
|
for ( int i=0; i<offset; i++ ) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue