package it.yup.xml;
// #debug
//@import it.yup.util.Logger;
import it.yup.util.Utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Vector;
public class Element {
protected static int array_inc = 5;
protected Object children[];
protected String attributes[][];
protected int nchildren;
protected int nattributes;
public String name;
public String uri;
// inserting time
public long queueTime;
public int maxWait;
private static int _internal_id_counter; //unique id
/**
* Build a full element. Warning children and attributes must not have empty space at end
* @param uri
* @param name
* @param children
* @param attributes
*/
public Element(String uri, String name, Object children[],
String attributes[][]) {
this.uri = uri;
this.name = name;
this.children = children;
this.nchildren = children.length;
this.attributes = attributes;
this.nattributes = attributes.length;
}
public Element(String uri, String name) {
this.uri = uri;
this.name = name;
nchildren = 0;
children = new Object[Element.array_inc];
nattributes = 0;
attributes = new String[Element.array_inc][3];
}
/**
* Copy constructor. The new element is a deep clone of the old one.
* @param e
*/
public Element(Element e) {
this.name = e.name;
this.uri = e.uri;
this.attributes = new String[e.attributes.length][3];
this.nattributes = e.nattributes;
this.children = new Object[e.children.length];
this.nchildren = e.nchildren;
for (int i = 0; i < e.nattributes; i++) {
String triplet[] = e.attributes[i];
this.attributes[i] = new String[] { triplet[0], triplet[1],
triplet[2] };
}
for (int i = 0; i < e.nchildren; i++) {
if (e.children[i].getClass() != String.class) {
this.children[i] = ((Element) e.children[i]).clone();
} else {
this.children[i] = e.children[i];
}
}
}
/* (non-Javadoc)
* @see it.yup.xml.Element#clone()
*/
public Element clone() {
return new Element(this);
}
/**
* Set the minimum capacity of the internal arrays
* @param size: the new array size
* @param type: either 'a' or 'c', indicating attributes or children vector
*/
public void ensureCapacity(int size, byte type) {
if (type == 'a' && this.attributes.length < size) {
String[][] old_v = this.attributes;
this.attributes = new String[size][2];
System.arraycopy(old_v, 0, this.attributes, 0, nattributes);
} else if (type == 'c' && this.children.length < size) {
Object[] old_v = this.children;
this.children = new Object[size];
System.arraycopy(old_v, 0, this.children, 0, nchildren);
}
}
/**
* Get the value of an attribute
* @param name
* @return
*/
public String getAttribute(String name) {
for (int i = 0; i < nattributes; i++) {
String attr[] = attributes[i];
if (attr[1].equals(name)) { return attr[2]; }
}
return null;
}
/**
* Set or add an attribute. Warning: internal buffers are arrays, calling too many times this
* method could be very inefficient
* @param name
* @param value
*/
public void setAttribute(String name, String value) {
// first look if we must change the value of an existing attribute
for (int i = 0; i < nattributes; i++) {
String triplet[] = attributes[i];
if (triplet[1].equals(name)) {
triplet[2] = value;
return;
}
}
// enlarge capacity if the array is full
if (nattributes >= attributes.length) {
String[][] old_attrs = attributes;
this.attributes = new String[attributes.length + Element.array_inc][3];
System.arraycopy(old_attrs, 0, attributes, 0, nattributes);
}
attributes[nattributes++] = new String[] { null, name, value };
}
public void setAttributes(String[] names, String[] values) {
for (int i = 0; i < names.length; i++)
this.setAttribute(names[i], values[i]);
}
/**
* Delete an attribute. The size of the internal array is not shrinked
* @param name
*/
public void delAttribute(String name) {
for (int i = 0, j = 0; i < nattributes; i++) {
String triplet[] = attributes[i];
if (!triplet[1].equals(name)) {
this.attributes[j++] = triplet;
}
}
nattributes--;
}
/**
* Build a unique id (to be used for IQs)
* XXX consider moving this to XMPP stanzas
* @return: the unique id
*/
public static String createUniqueId() {
Element._internal_id_counter++;
return "" + Element._internal_id_counter;
}
/**
* Create a child and add it to the dom tree
* @param uri
* @param name
* @return
*/
public Element addElement(String uri, String name) {
if (uri == null) {
uri = this.uri;
}
Element e = new Element(uri, name);
// enlarge capacity if the array is full
if (nchildren >= children.length) {
Object old_v[] = children;
this.children = new Object[old_v.length + 10];
System.arraycopy(old_v, 0, children, 0, nchildren);
}
children[nchildren++] = e;
return e;
}
// TODO rename addElement **after merge**
public Element addElementAndContent(String uri, String name, String content) {
if (uri == null) {
uri = this.uri;
}
Element e = new Element(uri, name);
e.addText(content);
// enlarge capacity if the array is full
if (nchildren >= children.length) {
Object old_v[] = children;
this.children = new Object[old_v.length + 10];
System.arraycopy(old_v, 0, children, 0, nchildren);
}
children[nchildren++] = e;
return e;
}
public void addElement(Element e) {
if (nchildren >= children.length) {
Object old_v[] = children;
this.children = new Object[old_v.length + Element.array_inc];
System.arraycopy(old_v, 0, children, 0, nchildren);
}
children[nchildren++] = e;
}
public void addText(String s) {
if (nchildren >= children.length) {
Object old_v[] = children;
this.children = new Object[old_v.length + Element.array_inc];
System.arraycopy(old_v, 0, children, 0, nchildren);
}
children[nchildren++] = s;
}
/**
*
* @param namespace
* @param name
* @return
*/
public Element removeChild(String namespace, String name) {
Object c, e = null;
int j = 0;
for (int i = 0; i < nchildren; i++) {
c = children[i];
if (c.getClass() != String.class
&& (namespace == null || namespace
.equals(((Element) c).uri))
&& (name.equals(((Element) c).name))) {
e = c;
nchildren--;
} else
children[j++] = c;
}
return (Element) e;
}
/**
*
* @param namespace
* @param name
* @return
*/
public void removeChild(Element el) {
Object c = null;
for (int i = 0; i < nchildren; i++) {
c = children[i];
if (c == el) {
ensureCapacity(nchildren + array_inc, (byte) 'c');
for (int l = i; l < nchildren; l++) {
children[l] = children[l + 1];
}
nchildren--;
break;
}
}
}
public void removeAllElements() {
nchildren = 0;
children = new Object[Element.array_inc];
}
/**
* Return the first child having the given name and namespace
* @param uri matched namespace (may be null, in this case any namaspace is ok)
* @param matched name
*/
public Element getChildByName(String uri, String name) {
for (int i = 0; i < nchildren; i++) {
Object e = children[i];
if ((e.getClass() != String.class)
&& ((Element) e).name.equals(name)
&& (uri == null || ((Element) e).uri.equals(uri))) { return (Element) e; }
}
return null;
}
/**
* Return the children having the given name and namespace
* @param uri matched namespace (may be null, in this case any namaspace is ok)
* @param matched name
*/
public Element[] getChildrenByName(String uri, String name) {
Element v[];
int n = 0;
// count the matching children
for (int i = 0; i < nchildren; i++) {
Object e = children[i];
if ((e.getClass() != String.class)
&& (((Element) e).name.equals(name))
&& (uri == null || ((Element) e).uri.equals(uri))) {
n++;
}
}
// build the result vector and copy the matching elements
v = new Element[n];
n = 0;
for (int i = 0; i < nchildren; i++) {
Object e = children[i];
if ((e.getClass() != String.class)
&& (((Element) e).name.equals(name))
&& (uri == null || ((Element) e).uri.equals(uri))) {
v[n++] = (Element) e;
}
}
return v;
}
/**
* Return all the non-text children
*/
public Element[] getChildren() {
Element v[];
int n = 0;
// count the matching children
for (int i = 0; i < nchildren; i++) {
Object e = children[i];
if (e.getClass() != String.class) {
n++;
}
}
// build the result vector and copy the matching elements
v = new Element[n];
n = 0;
for (int i = 0; i < nchildren; i++) {
Object e = children[i];
if (e.getClass() != String.class) {
v[n++] = (Element) e;
}
}
return v;
}
/**
* Get all the text
*/
public String getText() {
StringBuffer buf = new StringBuffer();
// count the matching children
for (int i = 0; i < nchildren; i++) {
Object e = children[i];
if (e.getClass() == String.class) {
buf.append((String) e);
}
}
return buf.toString();
}
public void resetText() {
// count the matching children
int j = 0;
for (int i = 0; i < nchildren; i++) {
Object e = children[i];
if (e.getClass() != String.class) children[j] = children[i];
else
j--;
j++;
}
}
/** Serialize the element and all its children
* (to plain xml)
* @return
*/
public byte[] toXml() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
writeXml(this, null, baos);
} catch (IOException e) {
// #debug
//@ Logger.log("[Element:toXml] IOException" + e.getMessage());
}
return baos.toByteArray();
}
private static String writeEscaped(String s, int quot) throws IOException {
StringBuffer escBuffer = new StringBuffer();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '\n':
case '\r':
case '\t':
if (quot == -1) escBuffer.append(c);
else
escBuffer.append("" + ((int) c) + ';');
break;
case '&':
escBuffer.append("&");
break;
case '>':
escBuffer.append(">");
break;
case '<':
escBuffer.append("<");
break;
case '"':
case '\'':
if (c == quot) {
escBuffer.append(c == '"' ? """ : "'");
break;
}
default:
//if(c < ' ')
// throw new IllegalArgumentException("Illegal control code:"+((int) c));
if (c >= ' ' && (c < 127)) escBuffer.append(c);
else
escBuffer.append("" + ((int) c) + ";");
}
}
return escBuffer.toString();
}
private static void writeXml(Element el, String ns, OutputStream os)
throws IOException {
// TODO XML escaping of data
os.write(Utils.getBytesUtf8("<" + el.name));
if (ns == null || !ns.equals(el.uri)) {
os.write(Utils.getBytesUtf8(" xmlns='" + el.uri + "'"));
ns = el.uri;
}
Vector nsUsed = new Vector(5);
for (int i = 0; i < el.nattributes; i++) {
String triplet[] = el.attributes[i];
String tripletString = " ";
if (triplet[0] != null && triplet[0].length() > 0) {
tripletString = " ns" + nsUsed.size() + ":";
nsUsed.addElement(triplet[0]);
}
tripletString += triplet[1] + "='" + writeEscaped(triplet[2],'\'') + "'";
os.write(Utils.getBytesUtf8(tripletString));
}
String nsDef = "";
Enumeration en = nsUsed.elements();
int index = 0;
while (en.hasMoreElements()) {
String ithNs = (String) en.nextElement();
nsDef += (" xmlns:ns" + index + "='" + ithNs + "'");
}
os.write(Utils.getBytesUtf8(nsDef));
if (el.children.length > 0) {
os.write(Utils.getBytesUtf8(">"));
for (int i = 0; i < el.nchildren; i++) {
if (el.children[i].getClass() != String.class) {
Element c = (Element) el.children[i];
Element.writeXml(c, ns, os);
} else {
String data = (String) el.children[i];
data = writeEscaped(data, -1);
os.write(Utils.getBytesUtf8(data));
}
}
os.write(Utils.getBytesUtf8("</" + el.name + ">"));
} else {
os.write(Utils.getBytesUtf8("/>"));
}
}
// public void serialize(DataOutputStream os) {
// try {
// os.writeUTF(name==null?"":name);
// os.writeUTF(uri==null?"":uri);
// os.writeShort(attributes.length);
// for(int i=0; i<nattributes; i++) {
// String attr[]=(String[]) attributes[i];
// os.writeUTF(attr[0]);
// os.writeUTF(attr[1]);
// }
// os.writeShort(children.length);
// for(int i=0; i<nchildren; i++) {
// Object el = children[i];
// if(el.getClass() == String.class) {
// os.writeByte('T');
// os.writeUTF((String)el);
// } else {
// os.writeByte('E');
// ((BElement)el).serialize(os);
// }
// }
// } catch (IOException e) {
// e.printStackTrace();
// };
//
// }
//
// public static BElement load(DataInputStream is) {
// String name;
// String uri;
// String attrs[][];
// Object children[];
// try {
// name = is.readUTF();
// uri = is.readUTF();
// int n = is.readShort();
// attrs = new String[n][2];
// for(int i=0; i<n; i++) {
// //String attr[] = new String[2];
// attrs[0][0] = is.readUTF();
// attrs[1][1] = is.readUTF();
// }
// n = is.readShort();
// children = new Object[n];
// for(int i=0; i<n; i++) {
// byte type = is.readByte();
// if(type == 'E') {
// children[i] = BElement.load(is);
// } else {
// children[i] = is.readUTF();
// }
// }
// } catch (IOException e) {
// return null;
// }
// return new BElement(name, uri, children, attrs);
// }
}
// the old code of Element (kept only a bit as a reference!!!)
/* Copyright (c) 2008 Bluendo S.r.L.
* See about.html for details about license.
*
* $Id: Element.java 1377 2009-04-21 14:17:38Z luca $
*/
//
//package it.yup.xml;
//
//// #debug
////@import it.yup.util.Logger;
//
//
//import java.io.ByteArrayOutputStream;
//import java.io.DataInputStream;
//import java.io.DataOutputStream;
//import java.io.IOException;
//import java.util.Enumeration;
//import java.util.Vector;
//
//import org.xmlpull.v1.XmlPullParser;
//import org.xmlpull.v1.XmlPullParserException;
//import org.xmlpull.v1.XmlSerializer;
//
//public class Element_old
//{
// public Vector children;
// public Vector attributes;
// public String content;
// public String name;
// public String uri;
//
// // XXX delete?
// // inserting time
// public long queueTime;
// public int maxWait;
//
// private static int _internal_id_counter; //unique id
//
// protected Element_old() {
// children = new Vector();
// attributes = new Vector();
// uri = null;
// name = null;
// }
//
// public Element_old(String uri, String name) {
// this.uri = uri;
// this.name = name;
// children = new Vector();
// attributes = new Vector(); //Hashtable();
// }
//
// public String getUri(String name) {
// return null;
// }
//
// // XXX theorically attributes may have prefixes too, but I don't remember any
// // jabber packet using them
// public String getAttribute(String name) {
// for(int i=0; i<attributes.size(); i++) {
// String attr[] = (String[]) attributes.elementAt(i);
// if(attr[1].equals(name)) {
// return attr[2];
// }
// }
// return null;
// }
//
// public void setAttribute (String name, String value) {
// Enumeration en = attributes.elements();
// while(en.hasMoreElements()) {
// String triplet[] = (String[]) en.nextElement();
// if(triplet[1].equals(name)) {
// triplet[2] = value;
// return;
// }
// }
// attributes.addElement(new String[] {null,name, value});
// }
//
// public void delAttribute (String name) {
// Enumeration en = attributes.elements();
// while(en.hasMoreElements()) {
// String triplet[] = (String[]) en.nextElement();
// if(triplet[1].equals(name)) {
// attributes.removeElement(triplet);
// return;
// }
// }
// }
//
// // easy creation a of child
// public Element_old addElement(String uri, String name) {
// if (uri == null){
// uri = this.uri;
// }
// Element_old e = new Element_old(uri, name);
// children.addElement(e);
// return e;
// }
//
// // set the id attribute to the element, using the static id class field
// public static String createUniqueId() {
// Element_old._internal_id_counter++;
// return "" + Element_old._internal_id_counter;
// }
//
// /**
// * Return the first child having the given name and namespace
// * @param uri matched namespace (may be null, in this case any namaspace is ok)
// * @param matched name
// */
// public Element_old getChildByName(String uri, String name) {
// for(int i=0; i<children.size(); i++) {
// Element_old e = (Element_old) children.elementAt(i);
// if(e.name.equals(name) && (uri == null || e.uri.equals(uri))) {
// return e;
// }
// }
// return null;
// }
//
// /**
// * Return the children having the given name and namespace
// * @param uri matched namespace (may be null, in this case any namaspace is ok)
// * @param matched name
// */
// public Element_old[] getChildrenByName(String uri, String name) {
// Element_old v[];
// int n = 0;
// for(int i=0; i<children.size(); i++) {
// Element_old e = (Element_old) children.elementAt(i);
// if(e.name.equals(name) && (uri == null || e.uri.equals(uri))) {
// n++;
// }
// }
// v = new Element_old[n];
// n=0;
// for(int i=0; i<children.size(); i++) {
// Element_old e = (Element_old) children.elementAt(i);
// if(e.name.equals(name) && (uri == null || e.uri.equals(uri))) {
// v[n++] = e;
// }
// }
// return v;
// }
//
// public static Element_old parseDocument(XmlPullParser parser) throws XmlPullParserException, IOException {
// Element_old el = new Element_old();
// parser.require(XmlPullParser.START_DOCUMENT, null, null);
// parser.nextToken();
// el._parse(parser);
// return el;
// }
//
// public static Element_old pullElement(XmlPullParser parser) throws XmlPullParserException, IOException {
// Element_old el = new Element_old();
// el._parse(parser);
// return el;
// }
//
// public static Element_old pullDocumentStart(XmlPullParser parser) throws XmlPullParserException, IOException {
// Element_old el = new Element_old();
// el.name = parser.getName();
// el.uri = parser.getNamespace();
// for(int i = 0; i<parser.getAttributeCount(); i++) {
// el.attributes.addElement(new String[] {parser.getAttributeNamespace(i),parser.getAttributeName(i), parser.getAttributeValue(i)});
// }
// return el;
// }
//
// protected void _parse(XmlPullParser parser) throws XmlPullParserException, IOException {
// for(int i = 0; i<parser.getAttributeCount(); i++) {
// attributes.addElement(new String[] {parser.getAttributeNamespace(i),parser.getAttributeName(i), parser.getAttributeValue(i)});
// }
// this.name = parser.getName();
// this.uri = parser.getNamespace();
//
// boolean finished = false;
// StringBuffer textBuffer = null;
// do {
// int type = parser.nextToken();
// switch(type) {
// case XmlPullParser.START_TAG:
// Element_old child = new Element_old();
// child._parse(parser);
// children.addElement(child);
// break;
// case XmlPullParser.END_TAG:
// finished = true;
// break;
// case XmlPullParser.COMMENT:
// break;
// default:
// if(parser.getText()!=null) {
// if(textBuffer==null) textBuffer = new StringBuffer();
// textBuffer.append(parser.getText());
// }
// }
//
// } while(!finished);
// if(textBuffer!=null){
// String content = textBuffer.toString();
// content.trim();
// if(content.length()>0){
// this.content = content;
// }
// }
// }
//
// // serialize the element and all its children
// public byte[] toXml()
// {
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// KXmlSerializer serializer = new KXmlSerializer();
// try {
// serializer.setOutput(baos, "UTF-8");
// write(serializer);
// serializer.endDocument();
// serializer.flush();
// } catch (IOException e) {
//// #debug
////@ Logger.log("[Element:toXml] IOException" + e.getMessage());
// }
// return baos.toByteArray();
// }
//
//
// private void write(XmlSerializer serializer) {
// try {
// serializer.setPrefix("", this.uri);
// serializer.startTag(this.uri, name);
// Enumeration attrEn = attributes.elements();
// while(attrEn.hasMoreElements()) {
// String attr[] = (String[]) attrEn.nextElement();
// serializer.attribute(attr[0], attr[1], attr[2]);
// }
// Enumeration childEn = children.elements();
// while(childEn.hasMoreElements()) {
// Element_old el = (Element_old) childEn.nextElement();
// el.write(serializer);
// }
// if(content!=null) {
// serializer.text(content);
// }
// serializer.endTag(this.uri, name);
// serializer.flush();
// } catch (IllegalArgumentException e) {
//// #debug
////@ Logger.log("[Element::write] IllegalArgumentException:" + e.getMessage());
// } catch (IllegalStateException e) {
//// #debug
////@ Logger.log("[Element::write] IllegalStateException: " + e.getMessage());
// } catch (IOException e) {
//// #debug
////@ Logger.log("[Element::write] IOException: " + e.getMessage());
// }
//
// }
//
// public Element_old(Element_old e) {
// this();
// this.name = e.name;
// this.uri = e.uri;
// this.content = e.content;
//
// for(int i=0; i<e.attributes.size(); i++) {
// String triplet[] = (String[]) e.attributes.elementAt(i);
// this.attributes.addElement(new String[] {triplet[0], triplet[1],triplet[2]});
// }
//
// for(int i=0; i<e.children.size(); i++) {
// Element_old c = (Element_old) e.children.elementAt(i);
// this.children.addElement(c.clone());
// }
// }
//
// public Element_old clone() {
// Element_old e = new Element_old();
// e.name = this.name;
// e.uri = this.uri;
// e.content = this.content;
//
// for(int i=0; i<this.attributes.size(); i++) {
// String triplet[] = (String[]) this.attributes.elementAt(i);
// e.attributes.addElement(new String[] {triplet[0], triplet[1], triplet[2]});
// }
//
// for(int i=0; i<this.children.size(); i++) {
// Element_old c = (Element_old) this.children.elementAt(i);
// e.children.addElement(c.clone());
// }
// return e;
// }
//
// public void serialize(DataOutputStream os) {
// try {
// os.writeUTF(name==null?"":name);
// os.writeUTF(uri==null?"":uri);
// os.writeUTF(content==null?"":content);
// os.writeShort(attributes.size());
// for(int i=0; i<attributes.size(); i++) {
// String attr[]=(String[]) attributes.elementAt(i);
// // XXX check what happens if null
// os.writeUTF(attr[0]);
// os.writeUTF(attr[1]);
// os.writeUTF(attr[2]);
// }
// os.writeShort(children.size());
// for(int i=0; i<children.size(); i++) {
// Element_old el = (Element_old) children.elementAt(i);
// el.serialize(os);
// }
// } catch (IOException e) {
// e.printStackTrace();
// };
//
// }
//
// public static Element_old load(DataInputStream is) {
// Element_old el = new Element_old();
// try {
// el.name = is.readUTF();
// el.uri = is.readUTF();
// el.content = is.readUTF();
// int n = is.readShort();
// for(int i=0; i<n; i++) {
// String attr[] = new String[3];
// attr[0] = is.readUTF();
// attr[1] = is.readUTF();
// attr[2] = is.readUTF();
// el.attributes.addElement(attr);
// }
// n = is.readShort();
// for(int i=0; i<n; i++) {
// el.children.addElement(Element_old.load(is));
// }
// } catch (IOException e) {
// return null;
// }
// return el;
// }
//}