commit dbfb93c5ed02fdc6ee20fc785fa4755214e186f9 Author: moparisthebest Date: Sat Jul 7 19:33:10 2012 -0400 First Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40d0ced --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml +target/ +out.xml diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/license.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/pom.xml b/pom.xml new file mode 100755 index 0000000..3271bed --- /dev/null +++ b/pom.xml @@ -0,0 +1,67 @@ + + 4.0.0 + + org.moparscape + sxf4j + 0.0.1 + jar + + sxf4j + + The Simple XML Facade for Java or (SXF4J) serves as a simple facade or abstraction for various xml frameworks, e.g. org.w3c.dom, dom4j, xpp, xpp3 and xom, allowing the end user to plug in the desired xml framework at deployment time. + + https://github.com/moparisthebest/sxf4j + + + + + + org.codehaus.plexus + plexus-utils + 3.0.1 + + + + + com.thoughtworks.xstream + xstream + 1.4.2 + + + + + dom4j + dom4j + 1.6.1 + + + + + xom + xom + 1.2.5 + + + + + + + Travis Burtrum + http://www.moparisthebest.com/ + + + + + + GNU LESSER GENERAL PUBLIC LICENSE, Version 3 + http://www.gnu.org/licenses/lgpl.html + + + + + scm:git:git@github.com:moparisthebest/sxf4j.git + scm:git:git@github.com:moparisthebest/sxf4j.git + git@github.com:moparisthebest/sxf4j.git + + diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..62ffece --- /dev/null +++ b/readme.txt @@ -0,0 +1,20 @@ +SXF4J + +This library provides a common interface for multiple different XML implementations, so they can be plugged in at runtime, +or programmatically selected by the programmer, allowing them to access the backing implementation for extra features, +and seamlessly convert between different implementations via a copy method. + +Currently, and as a side project that may be separated from this in the near future, the class ClassXmlElement may be +extended by any class, which can be annotated with @Attribute or @Child annotations so it can be seamlessly dumped to an +XML file by calling the toXml() method of ClassXmlElement, this makes it easy to represent an XML file in java code without +any logic per class to dump it to XML. Functionality is planned to load the class from the XML file as well. After writing +this class, I discovered that a program called XStream offers similar (if more complex) functionality, but I still believe +this is simpler and easier to work with, and they still have different purposes. (XStream offers serialization, this class +simply aims to simplify the XML to Java Class and back process.) + +Hopefully someone besides me will find this useful, I would be more than happy to accept feature requests, patches, and +pull requests. + +SXF4J is currently licensed under the GNU/LGPLv3. If you need this under another license, let me know and I'll see what I can do. + +Enjoy! \ No newline at end of file diff --git a/src/main/java/org/moparscape/XmlTest.java b/src/main/java/org/moparscape/XmlTest.java new file mode 100755 index 0000000..a3009ee --- /dev/null +++ b/src/main/java/org/moparscape/XmlTest.java @@ -0,0 +1,136 @@ +package org.moparscape; + +import org.moparscape.xml.ClassXmlElement; + +import org.moparscape.xml.ClassXmlElement.Root; +import org.moparscape.xml.impl.*; + +import java.util.*; + +@Root(name = "xmltest1") +public class XmlTest extends ClassXmlElement { + + private enum Bob { + TOM, + CHARLES, + EDWARD + } + + @Child + private Bob bobenum = Bob.TOM; + + @Child + private String bob = "tom"; + + @Attribute + private String xmlns_xsl = "http://www.w3.org/1999/XSL/Transform"; + + @Attribute + public String xmlns = "http://www.w3.org/1999/xhtml"; + + @Attribute + public static String xmlnsstatic = "http://www.w3.org/1999/xhtml"; + + @Attribute + private static String xmlns_xslstatic = "http://www.w3.org/1999/XSL/Transform"; + + @Child(children = {"dep", "subdep", "subsubdep"}) + private static List deps = Arrays.asList((Object) "dep1", "dep2", "dep3" + , new Object[]{"subdep1", "subdep2" + , new Object[]{"subsubdep1", "subsubdep2"} + } + // , new XmlTest() + ); + + @Attribute + private String[] modules = new String[]{"mod1", "mod2"}; + + @Attribute() + private static Map map = new HashMap() {{ + put("k1", "v1"); + put("k2", "v2"); +// put("k3", new XmlTest()); +// put("k4", new Object[]{"mapsubdep1", "mapsubdep2" +// ,new Object[]{"mapsubsubdep1", "mapsubsubdep2"} +// }); + }}; + + @Child + private static XmlTest childXmlTest = new XmlTest(); +// @Attribute private static XmlTest childXmlTest = new XmlTest(); + + @Attribute + private String getCharley() { + return "charley content"; + } + + @Child() + private String[] getdependencies() { + return new String[]{"dependency1", "dependency2", "dependency3"}; + } + + public static void main(String[] args) throws Exception { + System.setErr(System.out); + //testXmlElement();System.exit(0); + XmlElementFactory[] factories = new XmlElementFactory[]{ + new XppXmlElement(), + new Xpp3XmlElement(), + new Dom4jXmlElement(), + new W3CXmlElement(), + new XomXmlElement(), + }; + for(XmlElementFactory factory : factories) + testXmlElement(factory); + } + + public static void testDynamicImpls() throws Exception { + + String[] impls = new String[]{ + AbstractXmlElement.XPP + , AbstractXmlElement.XPP3 + , AbstractXmlElement.DOM4J + , AbstractXmlElement.XOM + , AbstractXmlElement.W3C + }; + for (String impl : impls) { + System.setProperty(AbstractXmlElement.implProperty, impl); + testXmlElement(); + } + } + + public static void testXmlElement() throws Exception { + testXmlElement(null); + } + + public static void testXmlElement(XmlElementFactory copyTo) throws Exception { + XmlElement xml = new XmlTest().toXml(); + System.out.println(xml.getClass()); + //nu.xom.Element e = xml.unwrap(nu.xom.Element.class); + //System.out.println("internal class: " + xml.unwrap(nu.xom.Element.class)); + if (copyTo != null) { + xml = xml.copyTo(copyTo); + System.out.println("copied: " + xml.getClass()); + } + + xml.writeToFile("./out.xml"); + System.out.printf("child name: '%s' value: '%s'\nattribute xmlns: '%s'\n", + xml.getChild("bob").getName(), + xml.getChild("bob").getValue(), +// "" + xml.getAttribute("xmlns") + ); + System.out.println("parent node (should be null): " + (xml.getParent() == null ? "null" : xml.getParent().getName())); + System.out.println("parent node of child (NOT null): " + (xml.getChildren()[0].getParent() == null ? "null" : xml.getChildren()[0].getParent().getName())); + + System.out.println("attribute names:" + Arrays.toString(xml.getAttributeNames())); + + System.out.println("children count:" + xml.getChildCount()); + //System.out.println("children: "+Arrays.toString(xml.getChildren())); + for (XmlElement children : xml.getChildren()) { + System.out.print(children.getName() + ", "); + } + System.out.println("\n-----"); + //System.exit(0); + } + +} diff --git a/src/main/java/org/moparscape/xml/ClassXmlElement.java b/src/main/java/org/moparscape/xml/ClassXmlElement.java new file mode 100755 index 0000000..72928a9 --- /dev/null +++ b/src/main/java/org/moparscape/xml/ClassXmlElement.java @@ -0,0 +1,346 @@ +package org.moparscape.xml; + +import org.moparscape.xml.impl.AbstractXmlElement; +import org.moparscape.xml.impl.XmlElement; +import org.moparscape.xml.impl.XmlElementFactory; + +import java.lang.annotation.*; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.*; + +public class ClassXmlElement { + + public static final String[] errors = new String[]{ + "ERROR GETTING VALUE" + , "ERROR: Cannot add " + ClassXmlElement.class.getSimpleName() + " without value as attribute!" + , "WARNING: INFINITE RECURSION DETECTED" + }; + + public static boolean debug = false; + + private final String uuid; + + { + uuid = UUID.randomUUID().toString(); + } + + @Target({ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + public @interface Root { + String name() default ""; + } + + @Target({ElementType.FIELD, ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface Attribute { + String name() default ""; + } + + @Target({ElementType.FIELD, ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface Child { + String name() default ""; + + String childName() default ""; + + String[] children() default {}; + } + + private String getString(Object ret) { + if (ret == null) + ret = errors[0]; + return ret.toString(); + } + + private void addToDom(XmlElement addTo, Object key, Object val, boolean child) { + if (child) + addTo.getNewChildXmlElement(key.toString()).setValue(val.toString()); + else + addTo.setAttribute(key.toString(), val.toString()); + } + + private XmlElement getAddTo(XmlElement ret, String name, boolean child) { + if (!child) // if we are expecting an attribute, never create a subobject + return ret; + XmlElement addTo = ret; + if (!name.isEmpty()) + addTo = ret.getNewChildXmlElement(name); + return addTo; + } + + private void addValueToDom(Object value, MethodField field, XmlElement ret, String name, int childDepth, boolean child, final Set recursionDetector, final XmlElementFactory factory) { + if (value == null) + value = field.getValue(); + + if (name == null) + name = field.getName(); + + // if it's an array, we convert it to a List + // so our List handling code can be re-used + if (value instanceof Object[]) + value = Arrays.asList((Object[]) value); + if (value instanceof List) { + XmlElement addTo = getAddTo(ret, name, child); + String childName = field.getName(child, childDepth); + //System.out.printf("name: '%s', childName: '%s'\n", name, childName); + List list = (List) value; + if (child) { + ++childDepth; + for (Object o : list) + addValueToDom(o, field, addTo, childName, childDepth, child, recursionDetector, factory); + } else { + // if we want an attribute, assume they want the list delimited by spaces + // don't bother recursing, attributes can only handle values of type String anyhow + StringBuilder sb = new StringBuilder(); + for (Object o : list) + sb.append(o.toString()).append(" "); + sb.delete(sb.length() - 1, sb.length()); + addToDom(addTo, name, sb.toString(), false); + } + } else if (value instanceof Map) { + XmlElement addTo = getAddTo(ret, name, child); + Map map = (Map) value; + for (Map.Entry entry : map.entrySet()) { + // not sure how to handle this, I guess key would be name + addValueToDom(entry.getValue(), field, addTo, entry.getKey().toString(), childDepth, child, recursionDetector, factory); + //addToDom(addTo, entry.getKey(), entry.getValue(), child); + } + ++childDepth; + } else if (value instanceof ClassXmlElement) { + ClassXmlElement other = ((ClassXmlElement) value); + if (child) + other.toXml(factory, getAddTo(ret, name, child), recursionDetector); + else { + XmlElement xmlChild = other.toXml(factory, null, recursionDetector); + String xmlChildValue = xmlChild.getValue(); + if (xmlChildValue == null) + xmlChildValue = errors[1]; + ret.setAttribute(xmlChild.getName(), xmlChildValue); + } + } else + addToDom(ret, name, getString(value), child); + } + + private void processMethodField(MethodField field, XmlElement ret, final Set recursionDetector, final XmlElementFactory factory) { + if (debug) + System.out.println(field); + boolean accessible = field.isAccessible(); + field.setAccessible(true); + if (field.isAnnotationPresent(Child.class) || field.isAnnotationPresent(Attribute.class)) + addValueToDom(null, field, ret, null, 0, field.isChild(), recursionDetector, factory); + field.setAccessible(accessible); + } + + public final XmlElement toXml() { + return this.toXml(null); + } + + public final XmlElement toXml(final XmlElementFactory factory) { + return this.toXml(factory == null ? AbstractXmlElement.getFactory() : factory, null, new HashSet()); + } + + private XmlElement toXml(final XmlElementFactory factory, final XmlElement parent, final Set recursionDetector) { + + Class thisClass = this.getClass(); + String rootName = thisClass.getSimpleName(); + if (thisClass.isAnnotationPresent(Root.class)) { + String rootAnnotationName = this.getClass().getAnnotation(Root.class).name(); + if (!rootAnnotationName.isEmpty()) + rootName = rootAnnotationName; + } + XmlElement ret = parent != null ? parent.getNewChildXmlElement(rootName) : factory.getNewChildXmlElement(rootName); + + if (recursionDetector.contains(this.uuid)) { + if (debug) + System.out.println("Infinite Recursion detected, returning..."); + ret.setValue(errors[2]); + return ret; + //return E("WARNING", "INFINITE RECURSION DETECTED"); + //return null; + } + recursionDetector.add(this.uuid); + + List mfList = new LinkedList(); + + for (Field field : thisClass.getDeclaredFields()) + mfList.add(new MethodField(this).setField(field)); + for (Method method : thisClass.getDeclaredMethods()) + mfList.add(new MethodField(this).setMethod(method)); + + // find and add xmlns first thing, some implementations require this (currently Dom4jXmlElement and XomXmlElement) + Iterator iter = mfList.iterator(); + while (iter.hasNext()) { + MethodField mf = iter.next(); + if(mf.isChild()) + continue; + String name = mf.getName(); + if(!name.equals("xmlns") && !name.startsWith("xmlns:")) + continue; + // otherwise, process the entry and remove it from the list + processMethodField(mf, ret, recursionDetector, factory); + iter.remove(); + } + + for(MethodField mf : mfList) + processMethodField(mf, ret, recursionDetector, factory); + + recursionDetector.remove(this.uuid); + return ret; + } + + private static class MethodField extends java.lang.reflect.AccessibleObject { + private final Object container; + + private Method m; + private Field f; + + private AccessibleObject ao; + + public MethodField(Object container) { + this.container = container; + } + + public MethodField setField(Field f) { + return setMethodField(null, f); + } + + public MethodField setMethod(Method m) { + return setMethodField(m, null); + } + + private MethodField setMethodField(Method m, Field f) { + if (m == null && f == null) + throw new Error("Method and field cannot be null!"); + this.m = m; + if (m != null) + ao = m; + this.f = f; + if (f != null) + ao = f; + return this; + } + + public boolean isChild() { + return ao.isAnnotationPresent(Child.class); + } + + public String getName() { + return getName(false, 0); + } + + public String getName(boolean child, int childDepth) { + String name = ""; + if (ao.isAnnotationPresent(Attribute.class)) + name = ao.getAnnotation(Attribute.class).name(); + else if (ao.isAnnotationPresent(Child.class)) { + name = ao.getAnnotation(Child.class).name(); + if (child) { + String childName = ao.getAnnotation(Child.class).childName(); + String[] childrenNames = ao.getAnnotation(Child.class).children(); + if (childDepth < childrenNames.length) + childName = childrenNames[childDepth]; + + // if childName isn't set, but children is, then take the last valid child + if (childName.isEmpty() && childrenNames.length > 0) + childName = childrenNames[childrenNames.length - 1]; + + // if the above doesn't exist, then use name but stripS + if (childName.isEmpty()) + name = stripS(name); + else + name = childName; + } + } + + if (!name.isEmpty()) + return name; + + if (f != null) + name = f.getName(); + else { + name = m.getName(); + // if there is a leading get, strip it off + if (name.toLowerCase().startsWith("get") && name.length() > 3) + name = name.substring(3, name.length()); + } + if (child) + name = stripS(name); + + // then set it as the field name, replacing _ with : + name = name.replace('_', ':'); + return name; + } + + public static String stripS(String name) { + String nameLower = name.toLowerCase(); + if (name.length() < 2 || !nameLower.endsWith("s")) + return name; + if (name.length() > 3 && nameLower.endsWith("ies")) + name = name.substring(0, name.length() - 3) + "y"; + else + name = name.substring(0, name.length() - 1); + return name; + + } + + public Object getValue() { + Object ret = null; + try { + if (f != null) + ret = f.get(container); + else + ret = m.invoke(container); + } catch (Exception e) { + e.printStackTrace(); + } + return ret; + } + + @Override + public void setAccessible(boolean flag) throws SecurityException { + ao.setAccessible(flag); + } + + @Override + public boolean isAccessible() { + return ao.isAccessible(); + } + + @Override + public T getAnnotation(Class annotationClass) { + return ao.getAnnotation(annotationClass); + } + + @Override + public boolean isAnnotationPresent(Class annotationClass) { + return ao.isAnnotationPresent(annotationClass); + } + + @Override + public Annotation[] getAnnotations() { + return ao.getAnnotations(); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + return ao.getDeclaredAnnotations(); + } + + @Override + public int hashCode() { + return ao.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return ao.equals(obj); + } + + @Override + public String toString() { + return (f == null ? "method" : "field ") + "(" + (isChild() ? "child) " : "attribute)") + ": " + ao.toString(); + } + } +} diff --git a/src/main/java/org/moparscape/xml/impl/AbstractXmlElement.java b/src/main/java/org/moparscape/xml/impl/AbstractXmlElement.java new file mode 100755 index 0000000..00c03b1 --- /dev/null +++ b/src/main/java/org/moparscape/xml/impl/AbstractXmlElement.java @@ -0,0 +1,241 @@ +package org.moparscape.xml.impl; + +import java.io.*; +import java.lang.reflect.Constructor; + +public abstract class AbstractXmlElement implements XmlElement, XmlElementFactory { + + public static final String implProperty = "xmlElementImpl"; + + // provided implementations + public static final String XPP3 = "Xpp3"; + public static final String XPP = "Xpp"; + public static final String DOM4J = "Dom4j"; + public static final String XOM = "Xom"; + public static final String W3C = "W3C"; + + public static XmlElementFactory getFactory() { + XmlElementFactory ret = null; + + //ret = new org.moparscape.xml.impl.W3CXmlElement(); + //ret = new org.moparscape.xml.impl.XppXmlElement(); + //ret = new org.moparscape.xml.impl.Xpp3XmlElement(); + //ret = new org.moparscape.xml.impl.Dom4jXmlElement(); + //ret = new org.moparscape.xml.impl.XomXmlElement(); + final String xmlDocType = System.getProperty(implProperty); + //System.out.println("xmlDocType: "+xmlDocType); + String implPkgClass = AbstractXmlElement.class.getPackage().getName() + "." + xmlDocType; + if (ret == null && xmlDocType != null) + ret = objectForName(implPkgClass + "XmlElement", null); + if (ret == null && xmlDocType != null) + ret = objectForName(implPkgClass, null); + if (ret == null && xmlDocType != null) + ret = objectForName(xmlDocType, null); + // try them in a defined default order + if (ret == null) { + Class[] xmlDocs = new Class[]{XppXmlElement.class, Xpp3XmlElement.class, Dom4jXmlElement.class, XomXmlElement.class}; + for (Class xmlDoc : xmlDocs) { + ret = objectForName(null, xmlDoc); + if (ret != null) + break; + } + } + // as a last resort, W3CXmlElement should ALWAYS be available + if (ret == null) + ret = new W3CXmlElement(); + return ret; + } + + @SuppressWarnings({"unchecked"}) + private static E objectForName(String name, Class clazz) { + try { + if (clazz == null) + clazz = Class.forName(name); + Constructor constructor = null; + try { + constructor = clazz.getDeclaredConstructor(); + } catch (Exception e) { + // we would only reach here if there is no default no-arg constructor + // we must use sun classes to get around this, unfortunately + sun.reflect.ReflectionFactory rf = sun.reflect.ReflectionFactory.getReflectionFactory(); + constructor = rf.newConstructorForSerialization(clazz, Object.class.getDeclaredConstructor(new Class[0])); + } + if (!constructor.isAccessible()) + constructor.setAccessible(true); + return (E) constructor.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } catch (Error e) { + e.printStackTrace(); + } + return null; + } + + protected static String emptyForNull(String ret) { + return ret == null ? "" : ret; + } + + protected void writeHeader(OutputStreamWriter osw, boolean newLine) throws IOException { + // only write the header if parent is null (top level) + if (getParent() != null) + return; + String header = ""; + if (newLine) + header += "\n"; + osw.write(header, 0, header.length()); + } + + @Override + public final XmlElement[] getChildren() { + return getChildren(null); + } + + @Override + public void writeToFile(File file) throws Exception { + writeToStream(new FileOutputStream(file)); + } + + @Override + public void writeToFile(String fileName) throws Exception { + writeToFile(new File(fileName)); + } + + @Override + public String toString() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try { + writeToStream(bos); + return new String(bos.toByteArray(), "UTF-8"); + } catch (Exception e) { + e.printStackTrace(); + } + return new String(bos.toByteArray()); + } + + @Override + public String toStringCompact() { + // this removes all whitespace between tags + String prettyXml = toString().replaceAll(">\\s+<", "><"); + // now we have to remove all whitespace between attributes + StringBuilder compactXml = new StringBuilder(prettyXml.length()); + boolean inTag = false; + boolean inAttribute = false; + boolean appendedWhitespace = false; + for (int x = 0; x < prettyXml.length(); ++x) { + char c = prettyXml.charAt(x); + if (c == '<') + inTag = true; + else if (c == '>') + inTag = false; + else if (c == '"') { + inAttribute = !inAttribute; + appendedWhitespace = false; + } + + if (inTag && !inAttribute && Character.isWhitespace(c)) { + if (appendedWhitespace) + continue; + appendedWhitespace = true; + c = ' '; + } + + compactXml.append(c); + } + return compactXml.toString(); + } + + private int byteLength(String s) { + try { + return s.getBytes("UTF-8").length; + } catch (Exception e) { + return s.getBytes().length; + } + } + + @Override + public int byteLength() { + return byteLength(toString()); + } + + @Override + public int byteLengthCompact() { + return byteLength(toStringCompact()); + } + + @Override + public XmlElement readFromFile(File file) throws Exception { + return readFromStream(new FileInputStream(file)); + } + + @Override + public XmlElement readFromFile(String file) throws Exception { + return readFromFile(new File(file)); + } + + @Override + public XmlElement readFromString(String string) throws Exception { + return readFromStream(new ByteArrayInputStream(string.getBytes())); + } + + @Override + public boolean isWrapperFor(Class iface) { + return iface != null && iface.isInstance(getInternal()); + } + + @Override + public T unwrap(Class iface) { + if (!isWrapperFor(iface)) + return null; + return iface.cast(getInternal()); + } + + /** + * For isWrapperFor and unwrap implementation + * + * @return + */ + protected abstract Object getInternal(); + + protected XmlElement[] wrapArray(Object[] os) { + XmlElement[] ret = new XmlElement[os.length]; + for (int x = 0; x < ret.length; ++x) + ret[x] = wrapObject(os[x]); + return ret; + } + + protected XmlElement wrapParent(Object parent) { + return parent == null ? null : wrapObject(parent); + } + + protected abstract XmlElement wrapObject(Object o); + + public XmlElement copyTo(XmlElementFactory factory) throws Exception { + if (factory == null) + return null; + + return factory.readFromString(this.toString()); + } + + /** old way, read/write above probably better + public XmlElement copyTo(XmlElementFactory factory) { + if (factory == null) + return null; + + XmlElement ret = factory.getNewChildXmlElement(this.getName()); + copyXmlElement(this, ret); + return ret; + } + + private void copyXmlElement(XmlElement src, XmlElement dst) { + // set the value + if (src.getValue() != null) + dst.setValue(src.getValue()); + // set all the attributes + for (String attName : src.getAttributeNames()) + dst.setAttribute(attName, src.getAttribute(attName)); + // recursively set all the children + for (XmlElement child : src.getChildren()) + copyXmlElement(child, dst.getNewChildXmlElement(child.getName())); + } + */ +} diff --git a/src/main/java/org/moparscape/xml/impl/Dom4jXmlElement.java b/src/main/java/org/moparscape/xml/impl/Dom4jXmlElement.java new file mode 100755 index 0000000..3324ccc --- /dev/null +++ b/src/main/java/org/moparscape/xml/impl/Dom4jXmlElement.java @@ -0,0 +1,150 @@ +package org.moparscape.xml.impl; + +import org.dom4j.*; +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.SAXReader; +import org.dom4j.io.XMLWriter; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.List; + +public class Dom4jXmlElement extends AbstractXmlElement { + + private final Element internal; + + public Dom4jXmlElement() { + internal = null; + } + + public Dom4jXmlElement(String name, Element parent) { + if (parent == null) { + Document document = DocumentHelper.createDocument(); + internal = document.addElement(name); + } else + internal = parent.addElement(name); + } + + private Dom4jXmlElement(Element internal) { + this.internal = internal; + } + + @Override + public String getName() { + return internal.getName(); + } + + @Override + public String getValue() { + return internal.getStringValue(); + } + + @Override + public String getAttribute(String name) { + Attribute attr = internal.attribute(name); + return attr == null ? "" : attr.getValue(); + } + + @Override + public String[] getAttributeNames() { + String[] ret = new String[internal.attributeCount()]; + for(int x = 0; x < ret.length; ++x) + ret[x] = internal.attribute(x).getQualifiedName(); + return ret; + } + + @Override + public XmlElement getParent() { + return wrapParent(internal.getParent()); + } + + @Override + public int getChildCount() { + return internal.elements().size(); + } + + @Override + @SuppressWarnings({"unchecked"}) + public XmlElement[] getChildren(String name){ + List elements = name == null ? internal.elements() : internal.elements(name); + return wrapArray(elements.toArray(new Object[elements.size()])); + } + + @Override + public XmlElement getChild(String name) { + return new Dom4jXmlElement(internal.element(name)); + } + + @Override + public XmlElement setAttribute(String name, String value) { + if (name.startsWith("xmlns")) + try { + String prefix = name.split(":")[1]; + internal.add(new Namespace(prefix, value)); + } catch (Exception e) { + if(name.equals("xmlns")) + internal.setQName(QName.get(internal.getName(), value)); + } + internal.addAttribute(name, value); + return this; + } + + @Override + public XmlElement setValue(String value) { + internal.setText(value); + return this; + } + + @Override + public XmlElement getNewChildXmlElement(String name) { + return new Dom4jXmlElement(name, internal); + } + + @Override + public XmlElement addChild(XmlElement other) { + if (other instanceof Dom4jXmlElement) { + Dom4jXmlElement o = (Dom4jXmlElement) other; + internal.add(o.internal); + } + return this; + } + + @Override + public XmlElement readFromStream(InputStream is) throws Exception { + SAXReader reader = new SAXReader(); + Document doc = reader.read(is); + Element root = doc.getRootElement(); + if(root == null) + return null; + return new Dom4jXmlElement(root); + } + + @Override + public void writeToStream(OutputStream os) throws Exception { + OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8"); + writeHeader(osw, false); + + OutputFormat format = OutputFormat.createPrettyPrint(); + //format = OutputFormat.createCompactFormat(); + XMLWriter writer = new XMLWriter(osw, format); + writer.write(internal); + + osw.close(); + os.close(); + } + + @Override + protected Object getInternal() { + return this.internal; + } + + @Override + protected XmlElement wrapObject(Object internal) { + if(internal instanceof Element) + return new Dom4jXmlElement((Element)internal); + return null; + } +} diff --git a/src/main/java/org/moparscape/xml/impl/W3CXmlElement.java b/src/main/java/org/moparscape/xml/impl/W3CXmlElement.java new file mode 100755 index 0000000..2710e66 --- /dev/null +++ b/src/main/java/org/moparscape/xml/impl/W3CXmlElement.java @@ -0,0 +1,170 @@ +package org.moparscape.xml.impl; + +import org.w3c.dom.*; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +public class W3CXmlElement extends AbstractXmlElement { + + private final Element internal; + + public W3CXmlElement(){ + internal = null; + } + + public W3CXmlElement(String name, Element parent) { + if(parent == null){ + DocumentBuilder db = null; + try{ + db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + }catch(Exception e){ + e.printStackTrace(); + } + if(db == null){ + internal = null; + return; + } + Document doc = db.newDocument(); + internal = doc.createElement(name); + doc.appendChild(internal); + }else{ + internal = parent.getOwnerDocument().createElement(name); + parent.appendChild(internal); + } + } + + private W3CXmlElement(Element internal) { + this.internal = internal; + } + + @Override + public String getName() { + return internal.getNodeName(); + } + + @Override + public String getValue() { + return internal.getTextContent(); + } + + @Override + public String getAttribute(String name) { + return internal.getAttributes().getNamedItem(name).getNodeValue(); + } + + @Override + public String[] getAttributeNames() { + NamedNodeMap nnm = internal.getAttributes(); + if(nnm == null) + return new String[0]; + String[] ret = new String[nnm.getLength()]; + for(int x = 0; x < ret.length; ++x) + ret[x] = nnm.item(x).getNodeName(); + return ret; + } + + @Override + public XmlElement getParent() { + return wrapParent(internal.getParentNode()); + } + + @Override + public int getChildCount() { + return internal.getChildNodes().getLength(); + } + + @Override + public XmlElement[] getChildren(String name){ + NodeList nl = internal.getChildNodes(); + List ret = new ArrayList(nl.getLength()); + for(int x = 0; x < nl.getLength(); ++x){ + Node n = nl.item(x); + if( n instanceof Element && (name == null || name.equals(n.getNodeName())) ) + ret.add(new W3CXmlElement((Element)n)); + } + return ret.toArray(new XmlElement[ret.size()]); + } + + @Override + public XmlElement getChild(String name) { + return new W3CXmlElement((Element)internal.getElementsByTagName(name).item(0)); + } + + @Override + public XmlElement getNewChildXmlElement(String name) { + return new W3CXmlElement(name, this.internal); + } + + @Override + public XmlElement addChild(XmlElement other) { + if (other instanceof W3CXmlElement){ + W3CXmlElement o = (W3CXmlElement) other; + internal.appendChild(o.internal); + } + return this; + } + + @Override + public XmlElement setAttribute(String name, String value) { + internal.setAttribute(name, value); + return this; + } + + @Override + public XmlElement setValue(String value) { + internal.setTextContent(value); + return this; + } + + @Override + public XmlElement readFromStream(InputStream is) throws Exception { + DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + if(db == null) + return null; + Document doc = db.parse(is); + if(doc == null) + return null; + return new W3CXmlElement(doc.getDocumentElement()); + } + + @Override + public void writeToStream(OutputStream os) throws Exception { + OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8"); + + // Use a Transformer for output + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, getParent() == null ? "no" : "yes"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + + transformer.transform(new DOMSource(internal), new StreamResult(osw)); + + osw.close(); + os.close(); + } + + @Override + protected Object getInternal() { + return this.internal; + } + + @Override + protected XmlElement wrapObject(Object internal) { + if(internal instanceof Element) + return new W3CXmlElement((Element)internal); + return null; + } + +} diff --git a/src/main/java/org/moparscape/xml/impl/XmlElement.java b/src/main/java/org/moparscape/xml/impl/XmlElement.java new file mode 100755 index 0000000..74b9259 --- /dev/null +++ b/src/main/java/org/moparscape/xml/impl/XmlElement.java @@ -0,0 +1,51 @@ +package org.moparscape.xml.impl; + +import java.io.File; +import java.io.OutputStream; + +public interface XmlElement { + public String getName(); + + public String getValue(); + + public String getAttribute(String name); + + public String[] getAttributeNames(); + + public XmlElement getParent(); + + public int getChildCount(); + + public XmlElement[] getChildren(); + + public XmlElement[] getChildren(String name); + + public XmlElement getChild(String name); + + public XmlElement setAttribute(String name, String value); + + public XmlElement setValue(String value); + + public XmlElement getNewChildXmlElement(String name); + + public XmlElement addChild(XmlElement other); + + public void writeToStream(OutputStream os) throws Exception; + + public void writeToFile(File file) throws Exception; + + public void writeToFile(String fileName) throws Exception; + + public String toString(); + + public String toStringCompact(); + + public int byteLength(); + + public int byteLengthCompact(); + + public boolean isWrapperFor(Class iface); + public T unwrap(Class iface); + + public XmlElement copyTo(XmlElementFactory factory) throws Exception; +} diff --git a/src/main/java/org/moparscape/xml/impl/XmlElementFactory.java b/src/main/java/org/moparscape/xml/impl/XmlElementFactory.java new file mode 100755 index 0000000..e40751b --- /dev/null +++ b/src/main/java/org/moparscape/xml/impl/XmlElementFactory.java @@ -0,0 +1,18 @@ +package org.moparscape.xml.impl; + +import java.io.File; +import java.io.InputStream; + +public interface XmlElementFactory { + + public XmlElement getNewChildXmlElement(String name); + + public XmlElement readFromStream(InputStream is) throws Exception; + + public XmlElement readFromFile(File file) throws Exception; + + public XmlElement readFromFile(String file) throws Exception; + + public XmlElement readFromString(String string) throws Exception; + +} diff --git a/src/main/java/org/moparscape/xml/impl/XomXmlElement.java b/src/main/java/org/moparscape/xml/impl/XomXmlElement.java new file mode 100755 index 0000000..e279145 --- /dev/null +++ b/src/main/java/org/moparscape/xml/impl/XomXmlElement.java @@ -0,0 +1,192 @@ +package org.moparscape.xml.impl; + +import nu.xom.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +public class XomXmlElement extends AbstractXmlElement { + + private final Element internal; + + public XomXmlElement() { + internal = null; + } + + public XomXmlElement(Element internal) { + this.internal = internal; + } + + private XomXmlElement(String name, Element parent) { + internal = new Element(name); + if (parent == null) + return; + String nameSpaceUri = parent.getNamespaceURI(); + if (nameSpaceUri != null) + internal.setNamespaceURI(nameSpaceUri); + parent.appendChild(internal); + } + + @Override + public String getName() { + return internal.getQualifiedName(); + } + + @Override + public String getValue() { + return internal.getValue(); + } + + @Override + public String getAttribute(String name) { + // Xom doesn't allow xmlns(:.*)? to be attributes, but we want them to be, so implement that here + if (name.equals("xmlns")) + return emptyForNull(internal.getNamespaceURI()); + String nameSpaceStart = "xmlns:"; + if (name.startsWith(nameSpaceStart) && name.length() > nameSpaceStart.length()) { + return emptyForNull(internal.getNamespaceURI(name.split(":")[1])); + } + // otherwise it's a regular attribute + Attribute attr = internal.getAttribute(name); + return attr == null ? null : attr.getValue(); + } + + @Override + public String[] getAttributeNames() { + String[] ret = new String[internal.getAttributeCount()+internal.getNamespaceDeclarationCount()]; + int x = 0; + for(; x < internal.getAttributeCount(); ++x) + ret[x] = internal.getAttribute(x).getQualifiedName(); + // special handling for xmlns, again... + for(int y = 0; y < internal.getNamespaceDeclarationCount(); ++y){ + String name = internal.getNamespacePrefix(y); + name = name.isEmpty() ? "xmlns" : ("xmlns:"+name); + ret[x++] = name; + } + return ret; + } + + @Override + public XmlElement getParent() { + return wrapParent(internal.getParent()); + } + + @Override + public int getChildCount() { + return internal.getChildCount(); + } + + @Override + public XmlElement[] getChildren(String name){ + Elements nl = internal.getChildElements(); + List ret = new ArrayList(nl.size()); + for(int x = 0; x < nl.size(); ++x){ + Element n = nl.get(x); + if(name == null || name.equals(n.getQualifiedName())) + ret.add(new XomXmlElement(n)); + } + return ret.toArray(new XmlElement[ret.size()]); + } + + @Override + public XmlElement getChild(String name) { + if(name == null) + return null; + + //Element child = internal.getFirstChildElement(name, internal.getNamespaceURI()); + //return child == null ? null : new XomXmlElement(child); + + // in order to avoid namespace issues, we need to call getChildElements and find them ourselves... + Elements elements = internal.getChildElements(); + for(int x = 0; x < elements.size(); ++x){ + Element element = elements.get(x); + if(name.equals(element.getQualifiedName())) + return new XomXmlElement(element); + } + return null; + } + + @Override + public XmlElement setAttribute(String name, String value) { + if (name.startsWith("xmlns")) + try { + String prefix = name.split(":")[1]; + internal.addNamespaceDeclaration(prefix, value); + } catch (Exception e) { + if (name.equals("xmlns")) + internal.setNamespaceURI(value); + else + internal.addAttribute(new Attribute(name, value)); + } + else + internal.addAttribute(new Attribute(name, value)); + return this; + } + + @Override + public XmlElement setValue(String value) { + internal.appendChild(value); + return this; + } + + @Override + public XmlElement getNewChildXmlElement(String name) { + return new XomXmlElement(name, internal); + } + + @Override + public XmlElement addChild(XmlElement other) { + if (other instanceof XomXmlElement) { + XomXmlElement o = (XomXmlElement) other; + internal.appendChild(o.internal); + } + return this; + } + + @Override + public XmlElement readFromStream(InputStream is) throws Exception { + Builder parser = new Builder(); + Document doc = parser.build(is); + if(doc == null) + return null; + return new XomXmlElement(doc.getRootElement()); + } + + @Override + public void writeToStream(OutputStream os) throws Exception { + final Serializer serializer; + Element toWrite = internal; + if(internal.getParent() != null){ + // then we already have a parent, so can't create a new document, so print a copy of this object + toWrite = new Element(toWrite); + serializer = new Serializer(os, "UTF-8"){ + @Override + protected void writeXMLDeclaration() throws IOException { + // we don't want this for a child + } + }; + }else + serializer = new Serializer(os, "UTF-8"); + + serializer.setIndent(2); + //serializer.setMaxLength(64); // line length + serializer.write(new Document(toWrite)); + + os.close(); + } + + @Override + protected Object getInternal() { + return this.internal; + } + + @Override + protected XmlElement wrapObject(Object internal) { + if(internal instanceof Element) + return new XomXmlElement((Element)internal); + return null; + } +} diff --git a/src/main/java/org/moparscape/xml/impl/Xpp3XmlElement.java b/src/main/java/org/moparscape/xml/impl/Xpp3XmlElement.java new file mode 100755 index 0000000..22b9004 --- /dev/null +++ b/src/main/java/org/moparscape/xml/impl/Xpp3XmlElement.java @@ -0,0 +1,119 @@ +package org.moparscape.xml.impl; + +import org.codehaus.plexus.util.xml.XmlStreamReader; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.Xpp3DomBuilder; +import org.codehaus.plexus.util.xml.Xpp3DomWriter; + +import java.io.*; + +public class Xpp3XmlElement extends AbstractXmlElement { + + private final Xpp3Dom internal; + + public Xpp3XmlElement() { + internal = null; + } + + public Xpp3XmlElement(String name, Xpp3Dom parent) { + internal = new Xpp3Dom(name); + if(parent != null) + parent.addChild(internal); + } + + private Xpp3XmlElement(Xpp3Dom internal) { + this.internal = internal; + } + + @Override + public String getName() { + return internal.getName(); + } + + @Override + public String getValue() { + return internal.getValue(); + } + + @Override + public String getAttribute(String name) { + return internal.getAttribute(name); + } + + @Override + public String[] getAttributeNames() { + return internal.getAttributeNames(); + } + + @Override + public XmlElement getParent() { + return wrapParent(internal.getParent()); + } + + @Override + public int getChildCount() { + return internal.getChildCount(); + } + + @Override + public XmlElement[] getChildren(String name){ + return wrapArray(name == null ? internal.getChildren() : internal.getChildren(name)); + } + + @Override + public XmlElement getChild(String name) { + return new Xpp3XmlElement(internal.getChild(name)); + } + + @Override + public XmlElement getNewChildXmlElement(String name){ + return new Xpp3XmlElement(name, internal); + } + + @Override + public XmlElement addChild(XmlElement other) { + if (other instanceof Xpp3XmlElement) + internal.addChild(((Xpp3XmlElement) other).internal); + return this; + } + + @Override + public XmlElement setAttribute(String name, String value) { + internal.setAttribute(name, value); + return this; + } + + @Override + public XmlElement setValue(String value) { + internal.setValue(value); + return this; + } + + @Override + public XmlElement readFromStream(InputStream is) throws Exception { + return new Xpp3XmlElement(Xpp3DomBuilder.build(new XmlStreamReader(is))); + } + + @Override + public void writeToStream(OutputStream os) throws IOException { + OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8"); + writeHeader(osw, true); + + Xpp3DomWriter.write(osw, internal); + osw.close(); + os.close(); + } + + @Override + protected Object getInternal() { + return this.internal; + } + + @Override + protected XmlElement wrapObject(Object internal) { + if(internal instanceof Xpp3Dom) + return new Xpp3XmlElement((Xpp3Dom)internal); + return null; + } + +} diff --git a/src/main/java/org/moparscape/xml/impl/XppXmlElement.java b/src/main/java/org/moparscape/xml/impl/XppXmlElement.java new file mode 100755 index 0000000..e6451fd --- /dev/null +++ b/src/main/java/org/moparscape/xml/impl/XppXmlElement.java @@ -0,0 +1,119 @@ +package org.moparscape.xml.impl; + +import com.thoughtworks.xstream.io.copy.HierarchicalStreamCopier; +import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; +import com.thoughtworks.xstream.io.xml.XppDomReader; +import com.thoughtworks.xstream.io.xml.xppdom.XppDom; +import com.thoughtworks.xstream.io.xml.xppdom.XppFactory; + +import java.io.*; + +public class XppXmlElement extends AbstractXmlElement { + + private final XppDom internal; + + public XppXmlElement() { + internal = null; + } + + public XppXmlElement(String name, XppDom parent) { + internal = new XppDom(name); + if(parent != null) + parent.addChild(internal); + } + + private XppXmlElement(XppDom internal) { + this.internal = internal; + } + + @Override + public String getName() { + return internal.getName(); + } + + @Override + public String getValue() { + return internal.getValue(); + } + + @Override + public String getAttribute(String name) { + return internal.getAttribute(name); + } + + @Override + public String[] getAttributeNames() { + return internal.getAttributeNames(); + } + + @Override + public XmlElement getParent() { + return wrapParent(internal.getParent()); + } + + @Override + public int getChildCount() { + return internal.getChildCount(); + } + + @Override + public XmlElement[] getChildren(String name){ + return wrapArray(name == null ? internal.getChildren() : internal.getChildren(name)); + } + + @Override + public XmlElement getChild(String name) { + return new XppXmlElement(internal.getChild(name)); + } + + @Override + public XmlElement getNewChildXmlElement(String name){ + return new XppXmlElement(name, this.internal); + } + + @Override + public XmlElement addChild(XmlElement other) { + if (other instanceof XppXmlElement) + internal.addChild(((XppXmlElement) other).internal); + return this; + } + + @Override + public XmlElement setAttribute(String name, String value) { + internal.setAttribute(name, value); + return this; + } + + @Override + public XmlElement setValue(String value) { + internal.setValue(value); + return this; + } + + @Override + public XmlElement readFromStream(InputStream is) throws Exception { + return new XppXmlElement(XppFactory.buildDom(is, "UTF-8")); // guess at encoding... + } + + @Override + public void writeToStream(OutputStream os) throws IOException { + OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8"); + writeHeader(osw, true); + + new HierarchicalStreamCopier().copy(new XppDomReader(internal), new PrettyPrintWriter(osw)); + osw.close(); + os.close(); + } + + @Override + protected Object getInternal() { + return this.internal; + } + + @Override + protected XmlElement wrapObject(Object internal) { + if(internal instanceof XppDom) + return new XppXmlElement((XppDom)internal); + return null; + } +}