diff --git a/src/helma/objectmodel/dom/XmlDatabaseReader.java b/src/helma/objectmodel/dom/XmlDatabaseReader.java index 0222ec75..73db07c2 100644 --- a/src/helma/objectmodel/dom/XmlDatabaseReader.java +++ b/src/helma/objectmodel/dom/XmlDatabaseReader.java @@ -99,20 +99,29 @@ public final class XmlDatabaseReader extends DefaultHandler implements XmlConsta } else if ("hop:parent".equals (qName)) { currentNode.setParentHandle (handle); } else { - Property prop = new Property (qName, currentNode); + // property name may be encoded as "propertyname" attribute, + // otherwise it is the element name + String propName = atts.getValue ("propertyname"); + if (propName == null) + propName = qName; + Property prop = new Property (propName, currentNode); prop.setNodeHandle (handle); if (propMap == null) { propMap = new Hashtable (); currentNode.setPropMap (propMap); } - propMap.put (qName.toLowerCase(), prop); + propMap.put (propName.toLowerCase(), prop); } } else { // a primitive property elementType = atts.getValue ("type"); if (elementType == null) elementType = "string"; - elementName = qName; + // property name may be encoded as "propertyname" attribute, + // otherwise it is the element name + elementName = atts.getValue ("propertyname"); + if (elementName == null) + elementName = qName; if (charBuffer == null) charBuffer = new StringBuffer(); else diff --git a/src/helma/objectmodel/dom/XmlReader.java b/src/helma/objectmodel/dom/XmlReader.java index ba3b7ccc..dd52123e 100644 --- a/src/helma/objectmodel/dom/XmlReader.java +++ b/src/helma/objectmodel/dom/XmlReader.java @@ -108,11 +108,15 @@ public final class XmlReader extends DefaultHandler implements XmlConstants { } else { // it's a named node property nodeStack.push (currentNode); - currentNode = currentNode.createNode (qName); + // property name may be encoded as "propertyname" attribute, + // otherwise it is the element name + String propName = atts.getValue ("propertyname"); + if (propName == null) + propName = qName; + currentNode = currentNode.createNode (propName); } // set the prototype on the current node and // add it to the map of parsed nodes. - String name = atts.getValue ("name"); String prototype = atts.getValue ("prototype"); if ( !"".equals(prototype) && !"hopobject".equals(prototype) ) currentNode.setPrototype (prototype); @@ -120,7 +124,7 @@ public final class XmlReader extends DefaultHandler implements XmlConstants { convertedNodes.put( key, currentNode ); return; } - + // check if we have a currentNode to set properties on, // otherwise throw exception. if (currentNode == null) @@ -144,11 +148,16 @@ public final class XmlReader extends DefaultHandler implements XmlConstants { INode n = (INode) convertedNodes.get (key); if (n != null) { if ("hop:child".equals (qName)) { - // add an already parsed node as child to current node - currentNode.addNode (n); + // add an already parsed node as child to current node + currentNode.addNode (n); } else { - // set an already parsed node as node property to current node - currentNode.setNode (qName, n); + // set an already parsed node as node property to current node + // property name may be encoded as "propertyname" attribute, + // otherwise it is the element name + String propName = atts.getValue ("propertyname"); + if (propName == null) + propName = qName; + currentNode.setNode (propName, n); } } } else { @@ -158,7 +167,11 @@ public final class XmlReader extends DefaultHandler implements XmlConstants { elementType = atts.getValue ("type"); if (elementType == null) elementType = "string"; - elementName = qName; + // property name may be encoded as "propertyname" attribute, + // otherwise it is the element name + elementName = atts.getValue ("propertyname"); + if (elementName == null) + elementName = qName; if (charBuffer == null) charBuffer = new StringBuffer(); else diff --git a/src/helma/objectmodel/dom/XmlUtil.java b/src/helma/objectmodel/dom/XmlUtil.java index e4c519ea..d2eaecf9 100644 --- a/src/helma/objectmodel/dom/XmlUtil.java +++ b/src/helma/objectmodel/dom/XmlUtil.java @@ -30,6 +30,7 @@ public class XmlUtil { return domBuilder; } else { try { + // domBuilderFactory.setExpandEntityReferences (false); domBuilder = domBuilderFactory.newDocumentBuilder(); domBuilders.put (Thread.currentThread(), domBuilder); return domBuilder; @@ -48,7 +49,7 @@ public class XmlUtil { DocumentBuilder d = getDocumentBuilder(); try { Document doc = d.parse (in); - doc.normalize(); + // doc.normalize(); return doc; } catch (SAXException e) { throw new RuntimeException ("Bad xml-code: "+e.toString()); diff --git a/src/helma/objectmodel/dom/XmlWriter.java b/src/helma/objectmodel/dom/XmlWriter.java index 20675320..bcf76357 100644 --- a/src/helma/objectmodel/dom/XmlWriter.java +++ b/src/helma/objectmodel/dom/XmlWriter.java @@ -131,7 +131,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants { write (""); - write (node,null,0); + write (node, null, null, 0); writeln (""); convertedNodes = null; return true; @@ -142,7 +142,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants { * references are made here if a node already has been fully printed * or if this is the last level that's going to be dumped */ - public void write (INode node, String name, int level) throws IOException { + public void write (INode node, String elementName, String propName, int level) throws IOException { if (node==null) return; // if (stopTypes != null && stopTypes.contains (node.getPrototype())) @@ -150,21 +150,22 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants { int previousLength = prefix.length(); prefix.append(indent); if ( ++level>maxLevels ) { - writeReferenceTag (node, name); + writeReferenceTag (node, elementName, propName); prefix.setLength( previousLength ); return; } if ( convertedNodes.contains(node) ) { - writeReferenceTag (node, name); + writeReferenceTag (node, elementName, propName); } else { convertedNodes.addElement (node); - writeTagOpen (node,name); - if ( node.getParent()!=null ) { - writeReferenceTag (node.getParent(),"hop:parent"); + writeTagOpen (node, elementName, propName); + INode parent = node.getParent (); + if ( parent!=null ) { + writeReferenceTag (parent, "hop:parent", null); } - writeProperties (node,level); - writeChildren (node,level); - writeTagClose (node,name); + writeProperties (node, level); + writeChildren (node, level); + writeTagClose (elementName); } prefix.setLength( previousLength ); } @@ -191,39 +192,52 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants { String key = (String)e.nextElement(); IProperty prop = node.get(key,false); if ( prop!=null ) { + boolean validName = isValidElementName (key); + String elementName, propName; + if (validName) { + elementName = key; + propName = null; + } else { + elementName = "property"; + propName = key; + } int type = prop.getType(); if( type==IProperty.NODE ) { - write (node.getNode(key,false), key, level); + write (prop.getNodeValue(), elementName, propName, level); } else { - writeProperty (node.get(key,false)); + writeProperty (prop, elementName, propName); } } } } - public void writeNullProperty (String key) throws IOException { + /* public void writeNullProperty (String key) throws IOException { write (prefix.toString()); write (indent); write ("<"); write (key); write (" type=\"null\"/>"); write (LINESEPARATOR); - } + } */ /** * write a single property, set attribute type according to type, * apply xml-encoding. */ - public void writeProperty (IProperty property) throws IOException { + public void writeProperty (IProperty property, String elementName, String propName) throws IOException { int propType = property.getType(); // we can't encode java objects in XML if (propType == IProperty.JAVAOBJECT) return; - String propName = property.getName(); write (prefix.toString()); write (indent); write ("<"); - write (propName); + write (elementName); + if (propName != null) { + write (" propertyname=\""); + write (HtmlEncoder.encodeXml (propName)); + write ("\""); + } switch (propType) { case IProperty.BOOLEAN: write (" type=\"boolean\">"); @@ -248,7 +262,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants { write ( str ); } write (""); write (LINESEPARATOR); } @@ -266,7 +280,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants { Enumeration e = node.getSubnodes(); while (e.hasMoreElements()) { INode nextNode = (INode)e.nextElement(); - write (nextNode, "hop:child", level); + write (nextNode, "hop:child", null, level); } } @@ -274,12 +288,16 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants { * write an opening tag for a node. Include id and prototype, use a * name if parameter is non-empty. */ - public void writeTagOpen (INode node, String name) throws IOException { + public void writeTagOpen (INode node, String name, String propName) throws IOException { write (prefix.toString()); write ("<"); write ( (name==null)?"hopobject" : name); write (" id=\""); write (getNodeIdentifier(node)); + if (propName != null) { + write ("\" propertyname=\""); + write (HtmlEncoder.encodeXml (propName)); + } write ("\" name=\""); write (node.getName()); write ("\" prototype=\""); @@ -297,7 +315,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants { * write a closing tag for a node * e.g. */ - public void writeTagClose (INode node, String name) throws IOException { + public void writeTagClose (String name) throws IOException { write (prefix.toString()); write (" */ - public void writeReferenceTag (INode node, String name) throws IOException { + public void writeReferenceTag (INode node, String name, String propName) throws IOException { write (prefix.toString()); write ("<"); write ( (name==null)?"hopobject" : name); write ( " idref=\""); write (getNodeIdentifier(node)); + if (propName != null) { + write ("\" propertyname=\""); + write (HtmlEncoder.encodeXml (propName)); + } write ("\" prototyperef=\""); write (getNodePrototype(node)); - write ("\""); - write ("/>"); + write ("\"/>"); write (LINESEPARATOR); } @@ -352,5 +373,24 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants { write (LINESEPARATOR); } + + /** + * Check if a string is usable as XML element name. If not, the name + * will be appended as attribute to the XML element. We are + * conservative here, preferring to return false rather too often than + * not enough. + */ + private boolean isValidElementName (String str) { + char c = str.charAt (0); + if (!Character.isLetter(c)) + return false; + int l = str.length(); + for (int i=1; i