initial checkin xml/dom-extension
This commit is contained in:
parent
8e96f6fee8
commit
ddf1a9a88e
6 changed files with 900 additions and 0 deletions
8
src/helma/objectmodel/dom/XmlConstants.java
Normal file
8
src/helma/objectmodel/dom/XmlConstants.java
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package helma.objectmodel.dom;
|
||||||
|
|
||||||
|
public interface XmlConstants {
|
||||||
|
|
||||||
|
public final String NAMESPACE = "http://www.helma.org/";
|
||||||
|
public final String DATEFORMAT = "dd.MM.yyyy HH:mm:ss z";
|
||||||
|
|
||||||
|
}
|
239
src/helma/objectmodel/dom/XmlConverter.java
Normal file
239
src/helma/objectmodel/dom/XmlConverter.java
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
package helma.objectmodel.dom;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.xml.parsers.*;
|
||||||
|
import org.w3c.dom.*;
|
||||||
|
|
||||||
|
import helma.objectmodel.*;
|
||||||
|
import helma.util.SystemProperties;
|
||||||
|
|
||||||
|
public class XmlConverter implements XmlConstants {
|
||||||
|
|
||||||
|
private boolean DEBUG=false;
|
||||||
|
|
||||||
|
private Properties props;
|
||||||
|
|
||||||
|
private char defaultSeparator = '_';
|
||||||
|
|
||||||
|
private int offset = 0;
|
||||||
|
|
||||||
|
public XmlConverter() {
|
||||||
|
props = new SystemProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlConverter(String propFile) {
|
||||||
|
props = new SystemProperties(propFile);
|
||||||
|
extractProperties(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlConverter(File propFile) {
|
||||||
|
this ( propFile.getAbsolutePath() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlConverter(Properties props) {
|
||||||
|
this.props = props;
|
||||||
|
extractProperties(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode convert( String desc ) {
|
||||||
|
return convert(desc, new TransientNode() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode convert( String desc, INode helmaNode ) throws RuntimeException {
|
||||||
|
try {
|
||||||
|
return convert( new URL(desc), helmaNode );
|
||||||
|
} catch ( MalformedURLException notanurl ) {
|
||||||
|
try {
|
||||||
|
return convert( new File(desc), helmaNode );
|
||||||
|
} catch ( FileNotFoundException notfound ) {
|
||||||
|
throw new RuntimeException( "couldn't read xml: " + desc );
|
||||||
|
}
|
||||||
|
} catch ( IOException ioerror ) {
|
||||||
|
throw new RuntimeException( "couldn't read xml: " + desc );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode convert( File file, INode helmaNode ) throws RuntimeException, FileNotFoundException {
|
||||||
|
return convert( new FileInputStream(file), helmaNode );
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode convert( URL url, INode helmaNode ) throws RuntimeException, IOException, MalformedURLException {
|
||||||
|
return convert( url.openConnection().getInputStream(), helmaNode );
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode convert( InputStream in, INode helmaNode ) throws RuntimeException {
|
||||||
|
Document document = XmlUtil.parse (in);
|
||||||
|
if ( document!=null && document.getDocumentElement()!=null ) {
|
||||||
|
return convert( document.getDocumentElement(), helmaNode );
|
||||||
|
} else {
|
||||||
|
return helmaNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode convert( Element element, INode helmaNode ) {
|
||||||
|
offset++;
|
||||||
|
if (DEBUG) debug("reading " + element.getNodeName() );
|
||||||
|
helmaNode.setName( element.getNodeName() );
|
||||||
|
String prototype = (String)props.get(element.getNodeName().toLowerCase()+".prototype");
|
||||||
|
if ( prototype == null )
|
||||||
|
prototype = "HopObject";
|
||||||
|
helmaNode.setPrototype( prototype );
|
||||||
|
attributes(element, helmaNode);
|
||||||
|
if ( element.hasChildNodes() ) {
|
||||||
|
children(element, helmaNode);
|
||||||
|
}
|
||||||
|
offset--;
|
||||||
|
return helmaNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parse xml children and create hopobject-children
|
||||||
|
*/
|
||||||
|
private INode children( Element element, helma.objectmodel.INode helmaNode ) {
|
||||||
|
NodeList list = element.getChildNodes();
|
||||||
|
int len = list.getLength();
|
||||||
|
StringBuffer textcontent = new StringBuffer();
|
||||||
|
String domKey, helmaKey;
|
||||||
|
for ( int i=0; i<len; i++ ) {
|
||||||
|
|
||||||
|
// loop through the list of children
|
||||||
|
org.w3c.dom.Node childNode = list.item(i);
|
||||||
|
|
||||||
|
// if it's text content of this element -> append to StringBuffer
|
||||||
|
if ( childNode.getNodeType()==org.w3c.dom.Node.TEXT_NODE ) {
|
||||||
|
textcontent.append( childNode.getNodeValue().trim() );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's some kind of element (property or child)
|
||||||
|
if ( childNode.getNodeType()==org.w3c.dom.Node.ELEMENT_NODE ) {
|
||||||
|
|
||||||
|
Element childElement = (Element)childNode;
|
||||||
|
|
||||||
|
// get the basic key we have to look for in the properties-table
|
||||||
|
domKey = (element.getNodeName()+"."+childElement.getNodeName()).toLowerCase();
|
||||||
|
|
||||||
|
// is there a childtext-2-property mapping?
|
||||||
|
if ( props!=null && props.containsKey(domKey+"._text") ) {
|
||||||
|
helmaKey = (String)props.get(domKey+"._text");
|
||||||
|
if( helmaKey.equals("") )
|
||||||
|
// if property is set but without value, read elementname for this mapping
|
||||||
|
helmaKey = childElement.getNodeName().replace(':',defaultSeparator);
|
||||||
|
if (DEBUG) debug("childtext-2-property mapping, helmaKey " + helmaKey + " for domKey " + domKey );
|
||||||
|
if ( helmaNode.getString(helmaKey,false)==null ) {
|
||||||
|
helmaNode.setString( helmaKey, XmlUtil.getTextContent(childNode) );
|
||||||
|
if (DEBUG) debug("childtext-2-property mapping, setting " + helmaKey + " as string" );
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// is there a simple child-2-property mapping?
|
||||||
|
// (lets the user define to use only one element and make this a property
|
||||||
|
// and simply ignore other elements of the same name)
|
||||||
|
if ( props!=null && props.containsKey(domKey+"._property") ) {
|
||||||
|
helmaKey = (String)props.get(domKey+"._property");
|
||||||
|
// if property is set but without value, read elementname for this mapping:
|
||||||
|
if ( helmaKey.equals("") )
|
||||||
|
helmaKey = childElement.getNodeName().replace(':',defaultSeparator);
|
||||||
|
if (DEBUG) debug("child-2-property mapping, helmaKey " + helmaKey + " for domKey " + domKey);
|
||||||
|
if ( helmaNode.getNode(helmaKey,false)==null ) {
|
||||||
|
convert( childElement, helmaNode.createNode(helmaKey) );
|
||||||
|
if (DEBUG) debug( "read " + childElement.toString() + helmaNode.getNode(helmaKey,false).toString() );
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// map it to one of the children-lists
|
||||||
|
helma.objectmodel.INode newHelmaNode = null;
|
||||||
|
String childrenMapping = (String)props.get(element.getNodeName().toLowerCase()+"._children");
|
||||||
|
// do we need a mapping directly among _children of helmaNode?
|
||||||
|
// can either be through property elname._children=_all or elname._children=childname
|
||||||
|
if( childrenMapping!=null && ( childrenMapping.equals("_all") || childrenMapping.equals(childElement.getNodeName()) ) ) {
|
||||||
|
newHelmaNode = convert(childElement, helmaNode.createNode(null) );
|
||||||
|
}
|
||||||
|
// which name to choose for a virtual subnode:
|
||||||
|
helmaKey = (String)props.get(domKey);
|
||||||
|
if ( helmaKey==null ) {
|
||||||
|
helmaKey = childElement.getNodeName().replace(':',defaultSeparator);
|
||||||
|
}
|
||||||
|
// try to get the virtual node
|
||||||
|
helma.objectmodel.INode worknode = helmaNode.getNode( helmaKey, false );
|
||||||
|
if ( worknode==null ) {
|
||||||
|
// if virtual node doesn't exist, create it
|
||||||
|
worknode = helmaNode.createNode( helmaKey );
|
||||||
|
}
|
||||||
|
if (DEBUG) debug( "mounting child "+ childElement.getNodeName() + " at worknode " + worknode.toString() );
|
||||||
|
// now mount it, possibly re-using the helmaNode that's been created before
|
||||||
|
if ( newHelmaNode!=null ) {
|
||||||
|
worknode.addNode(newHelmaNode);
|
||||||
|
} else {
|
||||||
|
convert( childElement, worknode.createNode( null ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// forget about other types (comments etc)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's some text content for this element, map it:
|
||||||
|
if ( textcontent.length()>0 ) {
|
||||||
|
helmaKey = (String)props.get(element.getNodeName().toLowerCase()+"._text");
|
||||||
|
if ( helmaKey==null )
|
||||||
|
helmaKey = "text";
|
||||||
|
helmaNode.setString(helmaKey, textcontent.toString().trim() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return helmaNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set element's attributes as properties of helmaNode
|
||||||
|
*/
|
||||||
|
private INode attributes( Element element, INode helmaNode ) {
|
||||||
|
NamedNodeMap nnm = element.getAttributes();
|
||||||
|
int len = nnm.getLength();
|
||||||
|
for ( int i=0; i<len; i++ ) {
|
||||||
|
org.w3c.dom.Node attr = nnm.item(i);
|
||||||
|
if ( attr.getNodeName().equals("_prototype") ) {
|
||||||
|
helmaNode.setPrototype( attr.getNodeValue() );
|
||||||
|
} else if ( attr.getNodeName().equals("_name") ) {
|
||||||
|
helmaNode.setName( attr.getNodeValue() );
|
||||||
|
} else {
|
||||||
|
String helmaKey = (String)props.get(element.getNodeName().toLowerCase()+".attributes."+attr.getNodeName().toLowerCase());
|
||||||
|
if ( helmaKey==null )
|
||||||
|
helmaKey = attr.getNodeName().replace(':',defaultSeparator);
|
||||||
|
helmaNode.setString( helmaKey, attr.getNodeValue() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return helmaNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* utility function
|
||||||
|
*/
|
||||||
|
private void extractProperties( Properties props ) {
|
||||||
|
if ( props.containsKey("separator") ) {
|
||||||
|
defaultSeparator = ((String)props.get("separator")).charAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** for testing */
|
||||||
|
void debug(Object msg) {
|
||||||
|
for ( int i=0; i<offset; i++ ) {
|
||||||
|
System.out.print(" ");
|
||||||
|
}
|
||||||
|
System.out.println(msg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main ( String args[] ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
157
src/helma/objectmodel/dom/XmlReader.java
Normal file
157
src/helma/objectmodel/dom/XmlReader.java
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
package helma.objectmodel.dom;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.xml.parsers.*;
|
||||||
|
import org.w3c.dom.*;
|
||||||
|
|
||||||
|
import helma.objectmodel.*;
|
||||||
|
|
||||||
|
public class XmlReader implements XmlConstants {
|
||||||
|
|
||||||
|
private int offset = 0;
|
||||||
|
private HashMap convertedNodes;
|
||||||
|
|
||||||
|
public XmlReader() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode read( String desc ) {
|
||||||
|
return read(desc, new TransientNode() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode read( String desc, INode helmaNode ) throws RuntimeException {
|
||||||
|
try {
|
||||||
|
return read( new File(desc), helmaNode );
|
||||||
|
} catch ( FileNotFoundException notfound ) {
|
||||||
|
throw new RuntimeException( "couldn't find xml-file: " + desc );
|
||||||
|
} catch ( IOException ioerror ) {
|
||||||
|
throw new RuntimeException( "couldn't read xml: " + desc );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode read( File file, INode helmaNode ) throws RuntimeException, FileNotFoundException {
|
||||||
|
return read( new FileInputStream(file), helmaNode );
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode read( InputStream in, INode helmaNode ) throws RuntimeException {
|
||||||
|
Document document = XmlUtil.parse(in);
|
||||||
|
if ( document!=null && document.getDocumentElement()!=null ) {
|
||||||
|
Node tmp = document.getDocumentElement().getFirstChild();
|
||||||
|
Element workelement = null;
|
||||||
|
while( tmp!=null ) {
|
||||||
|
tmp = tmp.getNextSibling();
|
||||||
|
if ( tmp.getNodeType()==Node.ELEMENT_NODE ) {
|
||||||
|
workelement = (Element) tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return startConversion( helmaNode, workelement );
|
||||||
|
} else {
|
||||||
|
return helmaNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode startConversion( INode helmaNode, Element element ) {
|
||||||
|
convertedNodes = new HashMap();
|
||||||
|
INode convertedNode = convert(helmaNode, element );
|
||||||
|
convertedNodes = null;
|
||||||
|
return convertedNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode convert( INode helmaNode, Element element ) {
|
||||||
|
offset++;
|
||||||
|
String idref = element.getAttributeNS(NAMESPACE, "idref");
|
||||||
|
String key = idref + "-" + element.getAttributeNS(NAMESPACE, "prototyperef");
|
||||||
|
if( idref!=null && !idref.equals("") ) {
|
||||||
|
if( convertedNodes.containsKey(key) ) {
|
||||||
|
offset--;
|
||||||
|
return (INode)convertedNodes.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key = element.getAttributeNS(NAMESPACE, "id") + "-" + element.getAttributeNS(NAMESPACE, "prototype");
|
||||||
|
convertedNodes.put( key, helmaNode );
|
||||||
|
|
||||||
|
// FIXME: import id on persistent nodes
|
||||||
|
String prototype = element.getAttributeNS(NAMESPACE, "prototype");
|
||||||
|
if( !prototype.equals("") && !prototype.equals("hopobject") ) {
|
||||||
|
helmaNode.setPrototype( prototype );
|
||||||
|
}
|
||||||
|
children(helmaNode, element);
|
||||||
|
offset--;
|
||||||
|
return helmaNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private INode children( INode helmaNode, Element element ) {
|
||||||
|
NodeList list = element.getChildNodes();
|
||||||
|
int len = list.getLength();
|
||||||
|
Element childElement;
|
||||||
|
for ( int i=0; i<len; i++ ) {
|
||||||
|
try {
|
||||||
|
childElement = (Element)list.item(i);
|
||||||
|
} catch( ClassCastException e ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
INode workNode = null;
|
||||||
|
if ( childElement.getTagName().equals("hop:child") ) {
|
||||||
|
convert( helmaNode.createNode(null), childElement );
|
||||||
|
} else if ( !"".equals(childElement.getAttributeNS(NAMESPACE,"id")) || !"".equals(childElement.getAttributeNS(NAMESPACE,"idref")) ) {
|
||||||
|
// we've got an object!
|
||||||
|
helmaNode.setNode( childElement.getTagName(), convert( helmaNode.createNode(childElement.getTagName()), childElement ) );
|
||||||
|
} else {
|
||||||
|
String type = childElement.getAttribute("hop:type");
|
||||||
|
String key = childElement.getTagName();
|
||||||
|
String content = XmlUtil.getTextContent(childElement);
|
||||||
|
if ( type.equals("boolean") ) {
|
||||||
|
if ( content.equals("true") ) {
|
||||||
|
helmaNode.setBoolean(key,true);
|
||||||
|
} else {
|
||||||
|
helmaNode.setBoolean(key,false);
|
||||||
|
}
|
||||||
|
} else if ( type.equals("date") ) {
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat ( DATEFORMAT );
|
||||||
|
try {
|
||||||
|
Date date = format.parse(content);
|
||||||
|
helmaNode.setDate(key, date);
|
||||||
|
} catch ( ParseException e ) {
|
||||||
|
helmaNode.setString(key,content);
|
||||||
|
}
|
||||||
|
} else if ( type.equals("float") ) {
|
||||||
|
helmaNode.setFloat(key, (new Double(content)).doubleValue() );
|
||||||
|
} else if ( type.equals("integer") ) {
|
||||||
|
helmaNode.setInteger(key, (new Long(content)).longValue() );
|
||||||
|
} else {
|
||||||
|
helmaNode.setString(key,content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return helmaNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** for testing */
|
||||||
|
public static void main ( String args[] ) throws Exception {
|
||||||
|
try {
|
||||||
|
XmlReader x = new XmlReader ();
|
||||||
|
INode node = x.read("test.xml");
|
||||||
|
} catch ( Exception e ) {
|
||||||
|
System.out.println("exception " + e.toString() );
|
||||||
|
throw new RuntimeException(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** for testing */
|
||||||
|
void debug(Object msg) {
|
||||||
|
for ( int i=0; i<offset; i++ ) {
|
||||||
|
System.out.print(" ");
|
||||||
|
}
|
||||||
|
System.out.println(msg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
74
src/helma/objectmodel/dom/XmlUtil.java
Normal file
74
src/helma/objectmodel/dom/XmlUtil.java
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package helma.objectmodel.dom;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import helma.objectmodel.INode;
|
||||||
|
import helma.objectmodel.TransientNode;
|
||||||
|
|
||||||
|
public class XmlUtil {
|
||||||
|
|
||||||
|
private static final DocumentBuilderFactory domBuilderFactory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
|
||||||
|
private static final HashMap domBuilders = new HashMap();
|
||||||
|
|
||||||
|
private static synchronized DocumentBuilder getDocumentBuilder() {
|
||||||
|
if ( domBuilders.containsKey (Thread.currentThread()) ) {
|
||||||
|
return (DocumentBuilder)domBuilders.get (Thread.currentThread());
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
DocumentBuilder d = domBuilderFactory.newDocumentBuilder();
|
||||||
|
domBuilders.put (Thread.currentThread(),d);
|
||||||
|
return d;
|
||||||
|
} catch ( ParserConfigurationException e ) {
|
||||||
|
throw new RuntimeException ("Cannot build parser: "+e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Document newDocument() {
|
||||||
|
DocumentBuilder d = getDocumentBuilder();
|
||||||
|
return d.newDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Document parse (InputStream in) throws RuntimeException {
|
||||||
|
DocumentBuilder d = getDocumentBuilder();
|
||||||
|
try {
|
||||||
|
Document doc = d.parse (in);
|
||||||
|
doc.normalize();
|
||||||
|
return doc;
|
||||||
|
} catch (SAXException e) {
|
||||||
|
throw new RuntimeException ("Bad xml-code: "+e.toString());
|
||||||
|
} catch (IOException f) {
|
||||||
|
throw new RuntimeException ("Could not read Xml: "+f.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the text content of an element
|
||||||
|
*/
|
||||||
|
public static String getTextContent( org.w3c.dom.Node element ) {
|
||||||
|
StringBuffer childtext = new StringBuffer();
|
||||||
|
NodeList childlist = element.getChildNodes();
|
||||||
|
int ct = childlist.getLength();
|
||||||
|
for ( int j=0; j<ct; j++ ) {
|
||||||
|
org.w3c.dom.Node childNode = childlist.item(j);
|
||||||
|
if ( childNode.getNodeType()==org.w3c.dom.Node.TEXT_NODE ) {
|
||||||
|
childtext.append(childNode.getNodeValue().trim() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return childtext.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
259
src/helma/objectmodel/dom/XmlWriter.java
Normal file
259
src/helma/objectmodel/dom/XmlWriter.java
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
package helma.objectmodel.dom;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import helma.objectmodel.*;
|
||||||
|
import helma.util.HtmlEncoder;
|
||||||
|
|
||||||
|
public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
|
|
||||||
|
private final static String LINESEPARATOR = System.getProperty("line.separator");
|
||||||
|
|
||||||
|
private Vector convertedNodes;
|
||||||
|
private int maxLevels = 3;
|
||||||
|
|
||||||
|
private String indent = " ";
|
||||||
|
private StringBuffer prefix = new StringBuffer();
|
||||||
|
|
||||||
|
private static int fileid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create ids that can be used for temporary files.
|
||||||
|
*/
|
||||||
|
public static int generateID() {
|
||||||
|
return fileid++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* empty constructor, will use System.out as outputstream.
|
||||||
|
*/
|
||||||
|
public XmlWriter () {
|
||||||
|
super(System.out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlWriter (OutputStream out) {
|
||||||
|
super(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlWriter (String desc) throws FileNotFoundException {
|
||||||
|
super (new FileOutputStream (desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlWriter (File file) throws FileNotFoundException {
|
||||||
|
super (new FileOutputStream (file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by default writing only descends 50 levels into the node tree to prevent
|
||||||
|
* infite loops. number can be changed here.
|
||||||
|
*/
|
||||||
|
public void setMaxLevels (int levels) {
|
||||||
|
maxLevels = levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set the number of space chars
|
||||||
|
*/
|
||||||
|
public void setIndent (int ct) {
|
||||||
|
StringBuffer tmp = new StringBuffer ();
|
||||||
|
for ( int i=0; i<ct; i++ ) {
|
||||||
|
tmp.append(" ");
|
||||||
|
}
|
||||||
|
indent = tmp.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* starting point for printing a node tree.
|
||||||
|
* creates document header too and initializes
|
||||||
|
* the cache of already converted nodes.
|
||||||
|
*/
|
||||||
|
public boolean write( INode node ) throws IOException {
|
||||||
|
convertedNodes = new Vector();
|
||||||
|
writeln ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||||
|
writeln ("<!-- printed by helma object publisher -->");
|
||||||
|
writeln ("<!-- created " + (new Date()).toString() + " -->" );
|
||||||
|
write ("<xmlroot xmlns:hop=\"");
|
||||||
|
write (NAMESPACE);
|
||||||
|
writeln ("\">");
|
||||||
|
write (node,null,0);
|
||||||
|
writeln ("</xmlroot>");
|
||||||
|
convertedNodes = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write a hopobject and print all its properties and children.
|
||||||
|
* if node has already been fully printed, just make a reference here.
|
||||||
|
*/
|
||||||
|
public void write (INode node, String name, int level) throws IOException {
|
||||||
|
if ( ++level>maxLevels )
|
||||||
|
return;
|
||||||
|
prefix.append(indent);
|
||||||
|
if ( convertedNodes.contains(node) ) {
|
||||||
|
writeReferenceTag (node, name);
|
||||||
|
} else {
|
||||||
|
convertedNodes.addElement (node);
|
||||||
|
writeTagOpen (node,name);
|
||||||
|
writeProperties (node,level);
|
||||||
|
writeChildren (node,level);
|
||||||
|
writeTagClose (node,name);
|
||||||
|
}
|
||||||
|
prefix = prefix.delete( prefix.length()-indent.length(), Integer.MAX_VALUE );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loop through properties and print them with their property-name
|
||||||
|
* as elementname
|
||||||
|
*/
|
||||||
|
private void writeProperties (INode node, int level) throws IOException {
|
||||||
|
Enumeration e = node.properties();
|
||||||
|
while ( e.hasMoreElements() ) {
|
||||||
|
String key = (String)e.nextElement();
|
||||||
|
IProperty prop = node.get(key,false);
|
||||||
|
if ( prop!=null ) {
|
||||||
|
int type = prop.getType();
|
||||||
|
if( type==IProperty.NODE ) {
|
||||||
|
write (node.getNode(key,false), key, level);
|
||||||
|
} else {
|
||||||
|
writeProperty (node.get(key,false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeNullProperty (String key) throws IOException {
|
||||||
|
write (prefix.toString());
|
||||||
|
write (indent);
|
||||||
|
write ("<");
|
||||||
|
write (key);
|
||||||
|
write (" hop:type=\"null\"/>");
|
||||||
|
write (LINESEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write a single property, set attribute type according to type,
|
||||||
|
* apply xml-encoding.
|
||||||
|
*/
|
||||||
|
public void writeProperty (IProperty property) throws IOException {
|
||||||
|
write (prefix.toString());
|
||||||
|
write (indent);
|
||||||
|
write ("<");
|
||||||
|
write (property.getName());
|
||||||
|
switch (property.getType()) {
|
||||||
|
case IProperty.BOOLEAN:
|
||||||
|
write (" hop:type=\"boolean\"");
|
||||||
|
break;
|
||||||
|
case IProperty.FLOAT:
|
||||||
|
write (" hop:type=\"float\"");
|
||||||
|
break;
|
||||||
|
case IProperty.INTEGER:
|
||||||
|
write (" hop:type=\"integer\"");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( property.getType()==IProperty.DATE ) {
|
||||||
|
write (" hop:type=\"date\"");
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat ( DATEFORMAT );
|
||||||
|
write (">");
|
||||||
|
write ( format.format (property.getDateValue()) );
|
||||||
|
} else {
|
||||||
|
write (">");
|
||||||
|
write ( HtmlEncoder.encodeXml (property.getStringValue()) );
|
||||||
|
}
|
||||||
|
write ("</");
|
||||||
|
write (property.getName());
|
||||||
|
write (">");
|
||||||
|
write (LINESEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loop through the children-array and print them as <hop:child>
|
||||||
|
*/
|
||||||
|
private void writeChildren (INode node, int level) throws IOException {
|
||||||
|
Enumeration e = node.getSubnodes();
|
||||||
|
while (e.hasMoreElements()) {
|
||||||
|
INode nextNode = (INode)e.nextElement();
|
||||||
|
write (nextNode, "hop:child", level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
write (prefix.toString());
|
||||||
|
write ("<");
|
||||||
|
write ( (name==null)?"hopobject" : name);
|
||||||
|
write (" hop:id=\"");
|
||||||
|
write (getNodeIdentifier(node));
|
||||||
|
write ("\" hop:prototype=\"");
|
||||||
|
write (getNodePrototype(node));
|
||||||
|
write ("\"");
|
||||||
|
write (">");
|
||||||
|
write (LINESEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write a closing tag for a node
|
||||||
|
* e.g. </root>
|
||||||
|
*/
|
||||||
|
public void writeTagClose (INode node, String name) throws IOException {
|
||||||
|
write (prefix.toString());
|
||||||
|
write ("</");
|
||||||
|
write ( (name==null)?"hopobject" : name);
|
||||||
|
write (">");
|
||||||
|
write (LINESEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write a tag holding a reference to an element that has
|
||||||
|
* been dumped before.
|
||||||
|
* e.g. <parent hop:idref="t35" hop:prototyperef="hopobject"/>
|
||||||
|
*/
|
||||||
|
public void writeReferenceTag (INode node, String name) throws IOException {
|
||||||
|
write (prefix.toString());
|
||||||
|
write ("<");
|
||||||
|
write ( (name==null)?"hopobject" : name);
|
||||||
|
write ( " hop:idref=\"");
|
||||||
|
write (getNodeIdentifier(node));
|
||||||
|
write ("\" hop:prototyperef=\"");
|
||||||
|
write (getNodePrototype(node));
|
||||||
|
write ("\"");
|
||||||
|
write ("/>");
|
||||||
|
write (LINESEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve prototype-string of a node, defaults to "hopobject"
|
||||||
|
*/
|
||||||
|
private String getNodePrototype( INode node ) {
|
||||||
|
if ( node.getPrototype()==null || "".equals(node.getPrototype()) ) {
|
||||||
|
return "hopobject";
|
||||||
|
} else {
|
||||||
|
return node.getPrototype();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TransientNode produces a different ID each time we call the getID()-method
|
||||||
|
* this is a workaround and uses hashCode if INode stands for a TransientNode.
|
||||||
|
*/
|
||||||
|
private String getNodeIdentifier( INode node ) {
|
||||||
|
try {
|
||||||
|
TransientNode tmp = (TransientNode)node;
|
||||||
|
return Integer.toString( tmp.hashCode() );
|
||||||
|
} catch ( ClassCastException e ) {
|
||||||
|
return node.getID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeln(String str) throws IOException {
|
||||||
|
write (str);
|
||||||
|
write (LINESEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
163
src/helma/scripting/fesi/extensions/DomExtension.java
Normal file
163
src/helma/scripting/fesi/extensions/DomExtension.java
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
package helma.scripting.fesi.extensions;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
|
import FESI.Data.*;
|
||||||
|
import FESI.Exceptions.*;
|
||||||
|
import FESI.Extensions.*;
|
||||||
|
import FESI.Interpreter.*;
|
||||||
|
|
||||||
|
import helma.framework.core.Application;
|
||||||
|
import helma.framework.core.RequestEvaluator;
|
||||||
|
import helma.objectmodel.INode;
|
||||||
|
import helma.objectmodel.db.Node;
|
||||||
|
import helma.objectmodel.dom.*;
|
||||||
|
import helma.scripting.fesi.ESNode;
|
||||||
|
|
||||||
|
public class DomExtension extends Extension {
|
||||||
|
|
||||||
|
private transient Evaluator evaluator = null;
|
||||||
|
|
||||||
|
public DomExtension() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initializeExtension(Evaluator evaluator) throws EcmaScriptException {
|
||||||
|
this.evaluator = evaluator;
|
||||||
|
GlobalObject go = evaluator.getGlobalObject();
|
||||||
|
ObjectPrototype op = (ObjectPrototype) evaluator.getObjectPrototype();
|
||||||
|
FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype();
|
||||||
|
|
||||||
|
ESObject globalXml = new GlobalObjectXml("Xml", evaluator, fp);
|
||||||
|
globalXml.putHiddenProperty ("length",new ESNumber(1));
|
||||||
|
globalXml.putHiddenProperty ("load", new XmlLoad ("load", evaluator, fp));
|
||||||
|
globalXml.putHiddenProperty ("save", new XmlSave ("save", evaluator, fp));
|
||||||
|
globalXml.putHiddenProperty ("create", new XmlCreate ("create", evaluator, fp));
|
||||||
|
globalXml.putHiddenProperty ("get", new XmlGet ("get", evaluator, fp));
|
||||||
|
go.putHiddenProperty ("Xml", globalXml);
|
||||||
|
}
|
||||||
|
|
||||||
|
class GlobalObjectXml extends BuiltinFunctionObject {
|
||||||
|
GlobalObjectXml(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||||
|
super(fp, evaluator, name, 1);
|
||||||
|
}
|
||||||
|
public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||||
|
return doConstruct(thisObject, arguments);
|
||||||
|
}
|
||||||
|
public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||||
|
throw new EcmaScriptException("Xml can't be instanced");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class XmlSave extends BuiltinFunctionObject {
|
||||||
|
XmlSave(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||||
|
super(fp, evaluator, name, 1);
|
||||||
|
}
|
||||||
|
public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||||
|
if ( arguments==null || arguments.length<2 )
|
||||||
|
throw new EcmaScriptException("not enough arguments");
|
||||||
|
INode node = null;
|
||||||
|
try {
|
||||||
|
node = ((ESNode)arguments[1]).getNode();
|
||||||
|
} catch ( Exception e ) {
|
||||||
|
// we definitly need a node
|
||||||
|
throw new EcmaScriptException("argument is not an hopobject");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
File tmpFile = new File(arguments[0].toString()+".tmp."+XmlWriter.generateID());
|
||||||
|
XmlWriter writer = new XmlWriter (tmpFile);
|
||||||
|
boolean result = writer.write(node);
|
||||||
|
writer.close();
|
||||||
|
File finalFile = new File(arguments[0].toString());
|
||||||
|
tmpFile.renameTo (finalFile);
|
||||||
|
this.evaluator.reval.app.logEvent("wrote xml to " + finalFile.getAbsolutePath() );
|
||||||
|
} catch (IOException io) {
|
||||||
|
throw new EcmaScriptException (io.toString());
|
||||||
|
}
|
||||||
|
return ESBoolean.makeBoolean(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class XmlCreate extends BuiltinFunctionObject {
|
||||||
|
XmlCreate(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||||
|
super(fp, evaluator, name, 1);
|
||||||
|
}
|
||||||
|
public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||||
|
if ( arguments==null || arguments.length==0 )
|
||||||
|
throw new EcmaScriptException("not enough arguments");
|
||||||
|
INode node = null;
|
||||||
|
try {
|
||||||
|
node = ((ESNode)arguments[0]).getNode();
|
||||||
|
} catch ( Exception e ) {
|
||||||
|
// we definitly need a node
|
||||||
|
throw new EcmaScriptException("argument is not an hopobject");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
XmlWriter writer = new XmlWriter (out);
|
||||||
|
boolean result = writer.write(node);
|
||||||
|
writer.flush();
|
||||||
|
return new ESString (out.toString());
|
||||||
|
} catch (IOException io) {
|
||||||
|
throw new EcmaScriptException (io.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class XmlLoad extends BuiltinFunctionObject {
|
||||||
|
XmlLoad(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||||
|
super(fp, evaluator, name, 1);
|
||||||
|
}
|
||||||
|
public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||||
|
if ( arguments==null || arguments.length==0 )
|
||||||
|
throw new EcmaScriptException("no arguments for Xml.load()");
|
||||||
|
INode node = null;
|
||||||
|
try {
|
||||||
|
node = ((ESNode)arguments[1]).getNode();
|
||||||
|
} catch ( Exception e ) { //classcast, arrayindex etc
|
||||||
|
// make sure we have a node, even if 2nd arg doesn't exist or is not a node
|
||||||
|
node = new Node ( (String)null, (String)null, this.evaluator.reval.app.getWrappedNodeManager() );
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
XmlReader reader = new XmlReader ();
|
||||||
|
INode result = reader.read (arguments[0].toString(),node);
|
||||||
|
return this.evaluator.reval.getNodeWrapper (result);
|
||||||
|
} catch ( NoClassDefFoundError e ) {
|
||||||
|
throw new EcmaScriptException ("Can't load dom-capable xml parser.");
|
||||||
|
} catch ( RuntimeException f ) {
|
||||||
|
throw new EcmaScriptException (f.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class XmlGet extends BuiltinFunctionObject {
|
||||||
|
XmlGet(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||||
|
super(fp, evaluator, name, 1);
|
||||||
|
}
|
||||||
|
public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
|
||||||
|
if ( arguments==null || arguments.length==0 )
|
||||||
|
throw new EcmaScriptException("Xml.get() needs a location as an argument");
|
||||||
|
try {
|
||||||
|
XmlConverter converter;
|
||||||
|
if ( arguments.length>1 ) {
|
||||||
|
converter = new XmlConverter (arguments[1].toString());
|
||||||
|
} else {
|
||||||
|
converter = new XmlConverter ();
|
||||||
|
}
|
||||||
|
INode node = new helma.objectmodel.db.Node ( (String)null, (String)null, this.evaluator.reval.app.getWrappedNodeManager() );
|
||||||
|
INode result = converter.convert (arguments[0].toString(),node);
|
||||||
|
return this.evaluator.reval.getNodeWrapper(result);
|
||||||
|
} catch ( NoClassDefFoundError e ) {
|
||||||
|
throw new EcmaScriptException("Can't load dom-capable xml parser.");
|
||||||
|
} catch ( RuntimeException f ) {
|
||||||
|
throw new EcmaScriptException(f.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue