/**
Copyright 2008, 2009 Mark Hooijkaas
This file is part of the Caas tool.
The Caas tool is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The Caas tool is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Caas tool. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kisst.util;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
public class XmlNode {
public final Element element;
public XmlNode(Element element) { this.element = element; }
public XmlNode(String name, String namespace) {
this.element=new Element(name, Namespace.getNamespace(namespace));
}
public XmlNode(String xml) {
if (xml.indexOf("<")<0) {
//xml is just an element name
this.element=new Element(xml);
return;
}
SAXBuilder builder = new SAXBuilder();
Document doc;
try {
doc = builder.build(new StringReader(xml));
}
catch (JDOMException e) { throw new RuntimeException(e); }
catch (IOException e) { throw new RuntimeException(e); }
this.element =doc.getRootElement();
}
@Override public XmlNode clone() { return new XmlNode((Element) this.element.clone()); }
public String getName() { return element.getName(); }
public String getNamespace() { return element.getNamespace().getURI(); }
public String getText() { return element.getText(); }
public String getAttribute(String name) { return getAttribute(name,null);}
public String getAttribute(String name, String defaultValue) {
Attribute attr = element.getAttribute(name);
if (attr==null)
return defaultValue;
else
return attr.getValue();
}
public XmlNode getChild(String path) { return (XmlNode) get(path); }
public XmlNode getParent() { return new XmlNode(element.getParentElement()); }
public Object propertyMissing(String name) { return get(name); }
public String getChildText(String path) {
Object child=get(path);
if (child==null)
return null;
if (child instanceof String)
return (String) child;
return ((XmlNode) child).getText();
}
public void setPrefix(String prefix) {
Namespace ns = element.getNamespace();
Namespace ns2 = Namespace.getNamespace(prefix, ns.getURI());
element.setNamespace(ns2);
}
public void setChildText(String path, String value) {
if (path.indexOf("@")>=0)
throw new RuntimeException("changing an attribute not yet supported in path "+path);
Object child=get(path);
((XmlNode) child).setText(value);
}
public List<XmlNode> getChildren() {
List<?> l = element.getChildren();
ArrayList<XmlNode> result=new ArrayList<XmlNode>(l.size());
for (Object o: l)
result.add(new XmlNode((Element) o));
return result;
}
public List<XmlNode> getChildren(String name) {
List<?> l = element.getChildren(name, null);
ArrayList<XmlNode> result=new ArrayList<XmlNode>(l.size());
for (Object o: l)
result.add(new XmlNode((Element) o));
return result;
}
public Object get(String path) {
String[] parts=path.split("/");
Element e=element;
for (String part:parts) {
boolean optional=false;
if (part.startsWith("?")) {
part=part.substring(1);
optional=true;
}
if (part.equals(".."))
e=e.getParentElement();
else if (part.equals("text()"))
return e.getText();
else if (part.startsWith("@"))
return e.getAttribute(part.substring(1)).getValue();
else
e=e.getChild(part,null);
if (e==null && optional)
return null;
if (e==null)
throw new RuntimeException("Could not find non-optional element "+part+" in path "+path+" in xml "+this+ " from total message "+ this.getRoot());
}
return new XmlNode(e);
}
public String compact() {
XMLOutputter out=new XMLOutputter(Format.getCompactFormat());
return out.outputString(element);
}
@Override public String toString() {
XMLOutputter out=new XMLOutputter();
return out.outputString(element);
}
public String shortString(int maxlen) {
String xml= toString();
if (xml.length()<=maxlen)
return xml;
return "<"+getName()+">...<"+getName()+">";
}
public XmlNode setAttribute(String name, String value) {
element.setAttribute(name, value);
return this;
}
public XmlNode add(String name, String namespace) {
Element child=new Element(name, namespace);
element.addContent(child);
return new XmlNode(child);
}
public XmlNode add(String name) {
Element child=new Element(name, element.getNamespace());
element.addContent(child);
return new XmlNode(child);
}
public XmlNode detach() { this.element.detach(); return this; }
public void setText(String text) { element.setText(text); }
public void add(XmlNode node) { element.addContent(node.element); }
public void remove(XmlNode e) { element.getChildren().remove(e.element); }
public String getPretty() {
XMLOutputter out=new XMLOutputter();
out.setFormat(Format.getPrettyFormat());
String xml= out.outputString(element);
return xml;
}
public void save(String filename) { FileUtil.saveString(new File(filename), getPretty()); }
public XmlNode getRoot() {
Element e = element;
while (e.getParentElement()!=null)
e=e.getParentElement();
if (e==element)
return this;
else
return new XmlNode(e);
}
public boolean equals(Object obj) {
if (obj==this)
return true;
if (! (obj instanceof XmlNode))
return false;
XmlNode other=(XmlNode) obj;
if (! this.getName().equals(other.getName()))
return false;
if (! this.getNamespace().equals(other.getNamespace()))
return false;
if (! this.getText().equals(other.getText()))
return false;
List<XmlNode> c1 = this.getChildren();
List<XmlNode> c2 = this.getChildren();
if (c1.size()!=c2.size())
return false;
for (int i=0; i<c1.size(); i++) {
if (! c1.get(i).equals(c2.get(i)))
return false;
}
// TODO: compare attributes, but beware of order and xmlns definitions etc.
return true;
}
}