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:
parent
6b7682b5a3
commit
efa4512055
4 changed files with 99 additions and 36 deletions
|
@ -99,20 +99,29 @@ 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";
|
||||||
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)
|
if (charBuffer == null)
|
||||||
charBuffer = new StringBuffer();
|
charBuffer = new StringBuffer();
|
||||||
else
|
else
|
||||||
|
|
|
@ -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);
|
||||||
|
@ -120,7 +124,7 @@ public final class XmlReader extends DefaultHandler implements XmlConstants {
|
||||||
convertedNodes.put( key, currentNode );
|
convertedNodes.put( key, currentNode );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we have a currentNode to set properties on,
|
// check if we have a currentNode to set properties on,
|
||||||
// otherwise throw exception.
|
// otherwise throw exception.
|
||||||
if (currentNode == null)
|
if (currentNode == null)
|
||||||
|
@ -144,11 +148,16 @@ public final class XmlReader extends DefaultHandler implements XmlConstants {
|
||||||
INode n = (INode) convertedNodes.get (key);
|
INode n = (INode) convertedNodes.get (key);
|
||||||
if (n != null) {
|
if (n != null) {
|
||||||
if ("hop:child".equals (qName)) {
|
if ("hop:child".equals (qName)) {
|
||||||
// add an already parsed node as child to current node
|
// add an already parsed node as child to current node
|
||||||
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,7 +167,11 @@ 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";
|
||||||
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)
|
if (charBuffer == null)
|
||||||
charBuffer = new StringBuffer();
|
charBuffer = new StringBuffer();
|
||||||
else
|
else
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue