package com.rayo.client.xmpp.stanza;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.Node;
import org.dom4j.QName;
public abstract class AbstractXmppObject implements XmppObject {
protected static final DocumentFactory factory = DocumentFactory.getInstance();
private Element element;
private boolean fromServer = false;
private String sessionId;
/**
* Creates an empty XMPP object..
*/
public AbstractXmppObject() {
this.element = factory.createDocument().addElement(getStanzaName());
}
public AbstractXmppObject(String namespace) {
this.element = factory.createDocument().addElement(new QName(getStanzaName(),new Namespace("", namespace)));
}
/**
* Creates an empty XMPP object..
*/
public AbstractXmppObject(XmppObject object) {
this(object.getElement());
setSessionId(object.getSessionId());
}
/**
* Constructs a new XMPP Object using the given dom4j element as data.
*
* @param element dom4j element that will back up this object
*/
public AbstractXmppObject(Element element) {
this(element,true);
}
/**
* Constructs a new XMPP Object using the given dom4j element as data.
*
* @param element dom4j element that will back up this object
* @param boolean This parameter specifies whether the given element should be copied or not. If
* <code>true</code> then a new copy of the dom4j element will be created. If <code>false</code> then
* the same reference will be used. Not copying the objects can save memory and CPU but developers have
* to put extra care on handling these objects as any write operation will be affecting to the original
* dom4j element.
*/
public AbstractXmppObject(Element element, boolean copy) {
if (copy) {
this.element = element.createCopy();
} else {
this.element = element;
}
}
public void setElement(Element element) {
this.element = element;
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#getRawType()
*/
@Override
public String getRawType() {
return attribute("type");
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#getNamespace()
*/
@Override
public String getNamespace() {
return element.getNamespaceURI();
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#getStanzaName()
*/
@Override
public abstract String getStanzaName();
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#copy()
*/
@Override
public XmppObject copy() {
return XmppObjectSupport.copy(getClass(), this);
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#copy(com.voxeo.rayo.stanza.IXmppObject)
*/
@Override
public XmppObject copy(XmppObject object) {
element = object.getElement().createCopy();
return this;
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#getElement()
*/
@Override
public Element getElement() {
return element;
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#getChildElement(java.lang.String)
*/
@Override
public Element getChildElement(String childName) {
return element.element(childName);
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#getFirstChild()
*/
@Override
public Element getFirstChild() {
List<Element> list = element.elements();
if (list.size() > 0) {
return list.get(0);
}
return null;
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#getChildElements(java.lang.String)
*/
@Override
@SuppressWarnings("unchecked")
public List<Element> getChildElements(String childName) {
return (List<Element>)element.elements(childName);
}
@Override
@SuppressWarnings("unchecked")
public List<Element> getChildElements() {
return (List<Element>)element.elements();
}
/**
* Returns name of the first child of this XMPP object if any
*
* @return String Name of the first child of this XMPP object if any
*/
public String getChildName() {
if (element.elementIterator().hasNext()) {
return ((Element)element.elementIterator().next()).getName();
}
return null;
}
protected String getRootName() {
return element.getName();
}
/**
* Returns namespace of the first child of this XMPP object if any
*
* @return String Namespace of the first child of this XMPP object if any
*/
public String getChildNamespace() {
if (element.elementIterator().hasNext()) {
return ((Element)element.elementIterator().next()).getNamespaceURI();
}
return null;
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#attribute(java.lang.String)
*/
@Override
public String attribute(String name) {
return element.attributeValue(name);
}
/**
* Returns the value of a given node in this XMPP object.
*
* @param name Name of the node that we are querying for.
*
* @return String Value for the given node or <code>null</code> if the key is not found
*/
protected String value(String name) {
return element.elementText(name);
}
protected void set(String name, String value) {
set(name,value,null);
}
/**
* <p>Sets the value of a given XMPP element. If the element does not exist then a new element
* with the given name will be created.</p>
* <p>The value can be null. If the value is null then the element will be removed from the XMPP object.</p>
*
* @param name Name of the element to be added
* @param value Value that the element will have or <code>null</code>
* @param namespaceUri Namespace for this new element. If the namespace is <code>null</code> then the element will
* be added without a namespace definition.
*/
protected void set(String name, String value, String namespaceUri) {
Element currentElement = element.element(name);
if (value == null) {
if (element != null) {
element.remove(currentElement);
}
}
if (currentElement == null) {
if (namespaceUri == null) {
currentElement = element.addElement(name);
} else {
currentElement = element.addElement(new QName(name, new Namespace("", namespaceUri)));
}
if (value != null) {
currentElement.setText(value);
}
}
}
@SuppressWarnings("rawtypes")
protected void clearChildren() {
Iterator it = element.elementIterator();
while (it.hasNext()) {
Element child = (Element)it.next();
element.remove(child);
}
}
/**
* <p>Sets the text of this dom4j node</p>
*
* @param text Text to be set
*/
protected void set(String text) {
element.setText(text);
}
/**
* Returns the textual element of this XMPP object or <code>null</code> if there is no text
*
* @return String Text of this XMPP object or <code>null</code> if no text is defined
*/
protected String text() {
return element.getText();
}
/**
* <p>Sets an XMPP object passed in as parameter within the containing XMPP Object. The name used to set the
* XMPP object will get the result of invoking {@link AbstractXmppObject}{@link #getName()} in the XMPP object passed.</p>
*
* <p>This method removes any existing child object in the containing XMPP Object with the given object's name.</p>
*
* @param object XMPP Object that has to be set. It cannot be <code>null</code> otherwise an
* {@link IllegalArgumentException} will be thrown.
*/
protected void set(XmppObject object) {
if (object == null) {
throw new IllegalArgumentException("Invalid argument: NULL");
}
Element currentElement = element.element(object.getStanzaName());
if (currentElement != null) {
element.remove(currentElement);
}
element.add(object.getElement());
}
/**
* <p>Adds a child XMPP Object to the recipient XMPP Object. The child node will be inserted using the output
* of {@link AbstractXmppObject}{@link #getName()} as name.</p>
*
* @param object XMPP Object that has to be set. It cannot be <code>null</code> otherwise an
* {@link IllegalArgumentException} will be thrown.
*/
protected void add(XmppObject object) {
if (object == null) {
throw new IllegalArgumentException("Invalid argument: NULL");
}
if (element.getNamespace() != null) {
setNamespaces(object.getElement(),element.getNamespace());
}
element.add(object.getElement());
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#getId()
*/
@Override
public String getId() {
return attribute("id");
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#setId(java.lang.String)
*/
@Override
public XmppObject setId(String id) {
setAttribute("id", id);
return this;
}
private void setNamespace(Element element, Namespace ns) {
if (element.getNamespaceURI() == null || element.getNamespaceURI().isEmpty()) {
element.setQName(QName.get(element.getName(), ns, element.getQualifiedName()));
}
}
/**
* Recursively sets the namespace of the element and all its children.
*/
@SuppressWarnings("unchecked")
private void setNamespaces(Element elem, Namespace ns) {
setNamespace(elem, ns);
setNamespaces(elem.content(), ns);
}
/**
* Recursively sets the namespace of the List and all children if the current namespace is match
*/
private void setNamespaces(List<Element> l, Namespace ns) {
Node n = null;
for (int i = 0; i < l.size(); i++) {
n = (Node) l.get(i);
if (n.getNodeType() == Node.ATTRIBUTE_NODE) {
((Attribute) n).setNamespace(ns);
}
if (n.getNodeType() == Node.ELEMENT_NODE) {
setNamespaces((Element) n, ns);
}
}
}
/**
* Sets an attribute on this XMPP Object. This method will use the {@link String}{@link #toString()} method to
* first convert the object to String.
*
* @param name Name of the attribute to set
* @param attribute value of the attribute or <code>null</code>
*/
protected void setAttribute(String name, Object attribute) {
element.addAttribute(name, (attribute == null)?null:attribute.toString());
}
/**
* Sets an attribute on this XMPP Object.
*
* @param name Name of the attribute to set
* @param attribute value of the attribute or <code>null</code>
*/
protected void setAttribute(String name, String attribute) {
element.addAttribute(name, attribute);
}
protected void setChildAttribute(String name, String attribute, String childName) {
Element child = element.element(childName);
if (child != null) {
child.addAttribute(name,attribute);
}
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#getXmlLang()
*/
@Override
public String getXmlLang() {
QName qname = new QName("lang", Namespace.XML_NAMESPACE);
return element.attributeValue(qname);
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#setXmlLang(java.lang.String)
*/
@Override
public void setXmlLang(String xmlLang) {
QName qname = new QName("lang", Namespace.XML_NAMESPACE);
element.addAttribute(qname, xmlLang);
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#getError()
*/
@Override
public Error getError() {
Element error = element.element("error");
if (error != null) {
return new Error(error);
}
return null;
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#setError(com.voxeo.rayo.stanza.Error)
*/
@Override
public void setError(Error error) {
if (error == null) {
return;
}
setAttribute("type", "error");
if (element.element("error") != null) {
element.remove(element.element("error"));
}
add(error);
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#hasChild(java.lang.String)
*/
@Override
public boolean hasChild(String childName) {
return element.element(childName) != null;
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#hasChild(java.lang.String, java.lang.String)
*/
@Override
public boolean hasChild(String childName, String namespace) {
return element.element(new QName(childName, new Namespace("", namespace))) != null;
}
@Override
public IQ asIQ() {
if (this instanceof IQ) {
return (IQ)this;
}
return new IQ(this);
}
@Override
public String toString() {
if (element == null) {
return "";
}
return element.asXML();
}
//TODO: This could be refactored
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#fromClient()
*/
@Override
public boolean fromClient() {
return !fromServer;
}
//TODO: This could be refactored
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#fromServer()
*/
@Override
public boolean fromServer() {
return fromServer;
}
/* (non-Javadoc)
* @see com.voxeo.rayo.stanza.IXmppObject#setFromServer(boolean)
*/
@Override
public void setFromServer(boolean fromServer) {
this.fromServer = fromServer;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
}