Tentative fix for bug 113: numbers as names of named properties do not

work in embedded db

When a property is encountered that *might* not be suitable as XML element name,
we use "property" as element name and add the real property name as attribute to
that element.
This commit is contained in:
hns 2002-10-30 16:35:48 +00:00
parent 6b7682b5a3
commit efa4512055
4 changed files with 99 additions and 36 deletions

View file

@ -99,19 +99,28 @@ public final class XmlDatabaseReader extends DefaultHandler implements XmlConsta
} else if ("hop:parent".equals (qName)) { } else if ("hop:parent".equals (qName)) {
currentNode.setParentHandle (handle); currentNode.setParentHandle (handle);
} else { } 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); prop.setNodeHandle (handle);
if (propMap == null) { if (propMap == null) {
propMap = new Hashtable (); propMap = new Hashtable ();
currentNode.setPropMap (propMap); currentNode.setPropMap (propMap);
} }
propMap.put (qName.toLowerCase(), prop); propMap.put (propName.toLowerCase(), prop);
} }
} else { } else {
// a primitive property // a primitive property
elementType = atts.getValue ("type"); elementType = atts.getValue ("type");
if (elementType == null) if (elementType == null)
elementType = "string"; elementType = "string";
// property name may be encoded as "propertyname" attribute,
// otherwise it is the element name
elementName = atts.getValue ("propertyname");
if (elementName == null)
elementName = qName; elementName = qName;
if (charBuffer == null) if (charBuffer == null)
charBuffer = new StringBuffer(); charBuffer = new StringBuffer();

View file

@ -108,11 +108,15 @@ public final class XmlReader extends DefaultHandler implements XmlConstants {
} else { } else {
// it's a named node property // it's a named node property
nodeStack.push (currentNode); 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 // set the prototype on the current node and
// add it to the map of parsed nodes. // add it to the map of parsed nodes.
String name = atts.getValue ("name");
String prototype = atts.getValue ("prototype"); String prototype = atts.getValue ("prototype");
if ( !"".equals(prototype) && !"hopobject".equals(prototype) ) if ( !"".equals(prototype) && !"hopobject".equals(prototype) )
currentNode.setPrototype (prototype); currentNode.setPrototype (prototype);
@ -148,7 +152,12 @@ public final class XmlReader extends DefaultHandler implements XmlConstants {
currentNode.addNode (n); currentNode.addNode (n);
} else { } else {
// set an already parsed node as node property to current node // set an already parsed node as node property to current node
currentNode.setNode (qName, n); // 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 { } else {
@ -158,6 +167,10 @@ public final class XmlReader extends DefaultHandler implements XmlConstants {
elementType = atts.getValue ("type"); elementType = atts.getValue ("type");
if (elementType == null) if (elementType == null)
elementType = "string"; elementType = "string";
// property name may be encoded as "propertyname" attribute,
// otherwise it is the element name
elementName = atts.getValue ("propertyname");
if (elementName == null)
elementName = qName; elementName = qName;
if (charBuffer == null) if (charBuffer == null)
charBuffer = new StringBuffer(); charBuffer = new StringBuffer();

View file

@ -30,6 +30,7 @@ public class XmlUtil {
return domBuilder; return domBuilder;
} else { } else {
try { try {
// domBuilderFactory.setExpandEntityReferences (false);
domBuilder = domBuilderFactory.newDocumentBuilder(); domBuilder = domBuilderFactory.newDocumentBuilder();
domBuilders.put (Thread.currentThread(), domBuilder); domBuilders.put (Thread.currentThread(), domBuilder);
return domBuilder; return domBuilder;
@ -48,7 +49,7 @@ public class XmlUtil {
DocumentBuilder d = getDocumentBuilder(); DocumentBuilder d = getDocumentBuilder();
try { try {
Document doc = d.parse (in); Document doc = d.parse (in);
doc.normalize(); // doc.normalize();
return doc; return doc;
} catch (SAXException e) { } catch (SAXException e) {
throw new RuntimeException ("Bad xml-code: "+e.toString()); throw new RuntimeException ("Bad xml-code: "+e.toString());

View file

@ -131,7 +131,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
write ("<xmlroot xmlns:hop=\""); write ("<xmlroot xmlns:hop=\"");
write (NAMESPACE); write (NAMESPACE);
writeln ("\">"); writeln ("\">");
write (node,null,0); write (node, null, null, 0);
writeln ("</xmlroot>"); writeln ("</xmlroot>");
convertedNodes = null; convertedNodes = null;
return true; 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 * 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 * 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) if (node==null)
return; return;
// if (stopTypes != null && stopTypes.contains (node.getPrototype())) // if (stopTypes != null && stopTypes.contains (node.getPrototype()))
@ -150,21 +150,22 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
int previousLength = prefix.length(); int previousLength = prefix.length();
prefix.append(indent); prefix.append(indent);
if ( ++level>maxLevels ) { if ( ++level>maxLevels ) {
writeReferenceTag (node, name); writeReferenceTag (node, elementName, propName);
prefix.setLength( previousLength ); prefix.setLength( previousLength );
return; return;
} }
if ( convertedNodes.contains(node) ) { if ( convertedNodes.contains(node) ) {
writeReferenceTag (node, name); writeReferenceTag (node, elementName, propName);
} else { } else {
convertedNodes.addElement (node); convertedNodes.addElement (node);
writeTagOpen (node,name); writeTagOpen (node, elementName, propName);
if ( node.getParent()!=null ) { INode parent = node.getParent ();
writeReferenceTag (node.getParent(),"hop:parent"); if ( parent!=null ) {
writeReferenceTag (parent, "hop:parent", null);
} }
writeProperties (node,level); writeProperties (node, level);
writeChildren (node,level); writeChildren (node, level);
writeTagClose (node,name); writeTagClose (elementName);
} }
prefix.setLength( previousLength ); prefix.setLength( previousLength );
} }
@ -191,39 +192,52 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
String key = (String)e.nextElement(); String key = (String)e.nextElement();
IProperty prop = node.get(key,false); IProperty prop = node.get(key,false);
if ( prop!=null ) { 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(); int type = prop.getType();
if( type==IProperty.NODE ) { if( type==IProperty.NODE ) {
write (node.getNode(key,false), key, level); write (prop.getNodeValue(), elementName, propName, level);
} else { } 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 (prefix.toString());
write (indent); write (indent);
write ("<"); write ("<");
write (key); write (key);
write (" type=\"null\"/>"); write (" type=\"null\"/>");
write (LINESEPARATOR); write (LINESEPARATOR);
} } */
/** /**
* write a single property, set attribute type according to type, * write a single property, set attribute type according to type,
* apply xml-encoding. * 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(); int propType = property.getType();
// we can't encode java objects in XML // we can't encode java objects in XML
if (propType == IProperty.JAVAOBJECT) if (propType == IProperty.JAVAOBJECT)
return; return;
String propName = property.getName();
write (prefix.toString()); write (prefix.toString());
write (indent); write (indent);
write ("<"); write ("<");
write (propName); write (elementName);
if (propName != null) {
write (" propertyname=\"");
write (HtmlEncoder.encodeXml (propName));
write ("\"");
}
switch (propType) { switch (propType) {
case IProperty.BOOLEAN: case IProperty.BOOLEAN:
write (" type=\"boolean\">"); write (" type=\"boolean\">");
@ -248,7 +262,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
write ( str ); write ( str );
} }
write ("</"); write ("</");
write (propName); write (elementName);
write (">"); write (">");
write (LINESEPARATOR); write (LINESEPARATOR);
} }
@ -266,7 +280,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
Enumeration e = node.getSubnodes(); Enumeration e = node.getSubnodes();
while (e.hasMoreElements()) { while (e.hasMoreElements()) {
INode nextNode = (INode)e.nextElement(); 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 * write an opening tag for a node. Include id and prototype, use a
* name if parameter is non-empty. * 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 (prefix.toString());
write ("<"); write ("<");
write ( (name==null)?"hopobject" : name); write ( (name==null)?"hopobject" : name);
write (" id=\""); write (" id=\"");
write (getNodeIdentifier(node)); write (getNodeIdentifier(node));
if (propName != null) {
write ("\" propertyname=\"");
write (HtmlEncoder.encodeXml (propName));
}
write ("\" name=\""); write ("\" name=\"");
write (node.getName()); write (node.getName());
write ("\" prototype=\""); write ("\" prototype=\"");
@ -297,7 +315,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
* write a closing tag for a node * write a closing tag for a node
* e.g. </root> * e.g. </root>
*/ */
public void writeTagClose (INode node, String name) throws IOException { public void writeTagClose (String name) throws IOException {
write (prefix.toString()); write (prefix.toString());
write ("</"); write ("</");
write ( (name==null)?"hopobject" : name); write ( (name==null)?"hopobject" : name);
@ -310,16 +328,19 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
* been written out before. * been written out before.
* e.g. <parent idref="35" prototyperef="hopobject"/> * e.g. <parent idref="35" prototyperef="hopobject"/>
*/ */
public void writeReferenceTag (INode node, String name) throws IOException { public void writeReferenceTag (INode node, String name, String propName) throws IOException {
write (prefix.toString()); write (prefix.toString());
write ("<"); write ("<");
write ( (name==null)?"hopobject" : name); write ( (name==null)?"hopobject" : name);
write ( " idref=\""); write ( " idref=\"");
write (getNodeIdentifier(node)); write (getNodeIdentifier(node));
if (propName != null) {
write ("\" propertyname=\"");
write (HtmlEncoder.encodeXml (propName));
}
write ("\" prototyperef=\""); write ("\" prototyperef=\"");
write (getNodePrototype(node)); write (getNodePrototype(node));
write ("\""); write ("\"/>");
write ("/>");
write (LINESEPARATOR); write (LINESEPARATOR);
} }
@ -352,5 +373,24 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
write (LINESEPARATOR); 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<l; i++) {
c = str.charAt (i);
if (!Character.isLetterOrDigit(c) && c != '-' && c != '_')
return false;
}
return true;
}
} }