From 9b152c00ad1853a569bc201d2925601b4f64fa2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Mart=C3=ADnez?= Date: Thu, 2 Jun 2011 16:47:39 +0200 Subject: [PATCH 1/6] Remove dom4j dependency, use Java XML APIs instead (Part 1) --- pom.xml | 5 - .../org/xmpp/component/AbstractComponent.java | 44 +-- src/main/java/org/xmpp/forms/DataForm.java | 7 +- src/main/java/org/xmpp/forms/FormField.java | 4 +- src/main/java/org/xmpp/muc/DestroyRoom.java | 16 +- src/main/java/org/xmpp/muc/Invitation.java | 13 +- src/main/java/org/xmpp/muc/JoinRoom.java | 7 +- src/main/java/org/xmpp/muc/LeaveRoom.java | 5 +- .../java/org/xmpp/muc/RoomConfiguration.java | 28 +- src/main/java/org/xmpp/packet/IQ.java | 165 +++------ src/main/java/org/xmpp/packet/Message.java | 158 +-------- src/main/java/org/xmpp/packet/Packet.java | 312 ++++-------------- .../java/org/xmpp/packet/PacketError.java | 99 ++---- .../java/org/xmpp/packet/PacketExtension.java | 106 ++++-- src/main/java/org/xmpp/packet/Presence.java | 149 ++------- src/main/java/org/xmpp/packet/Roster.java | 31 +- .../java/org/xmpp/packet/StreamError.java | 58 +--- .../xmpp/resultsetmanagement/ResultSet.java | 4 +- src/main/java/org/xmpp/util/BaseXML.java | 270 +++++++++++++++ .../AbstractComponentIsConsumerTest.java | 10 +- ...ractComponentRespondsToIQRequestsTest.java | 43 +-- .../AbstractComponentServiceDiscovery.java | 42 +-- .../xmpp/component/AbstractComponentTest.java | 40 +-- .../SlowRespondingThreadNameComponent.java | 20 +- .../xmpp/forms/DataFormAddingFieldsTest.java | 4 +- .../org/xmpp/forms/FormFieldGetSetTest.java | 3 +- .../org/xmpp/packet/PacketAddressingTest.java | 115 +------ 27 files changed, 684 insertions(+), 1074 deletions(-) create mode 100644 src/main/java/org/xmpp/util/BaseXML.java diff --git a/pom.xml b/pom.xml index 6cb3390..c559665 100644 --- a/pom.xml +++ b/pom.xml @@ -85,11 +85,6 @@ - - dom4j - dom4j - 1.6.1 - org.slf4j slf4j-api diff --git a/src/main/java/org/xmpp/component/AbstractComponent.java b/src/main/java/org/xmpp/component/AbstractComponent.java index bf14945..33d3870 100644 --- a/src/main/java/org/xmpp/component/AbstractComponent.java +++ b/src/main/java/org/xmpp/component/AbstractComponent.java @@ -24,9 +24,9 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; import org.xmpp.packet.IQ; import org.xmpp.packet.IQ.Type; import org.xmpp.packet.JID; @@ -77,7 +77,7 @@ * * @author Guus der Kinderen, guus.der.kinderen@gmail.com */ -// TODO define JCIP annotation +//TODO define JCIP annotation public abstract class AbstractComponent implements Component { /** * The object that's responsible for logging. @@ -205,7 +205,7 @@ public final void initialize(final JID jid, final ComponentManager componentMana */ @Override final public void processPacket(final Packet packet) { - final Packet copy = packet.createCopy(); + final Packet copy = packet.clone(); if (executor == null) { @@ -214,7 +214,7 @@ final public void processPacket(final Packet packet) { executor.execute(new PacketProcessor(copy)); } catch (final RejectedExecutionException ex) { log.error("(serving component '" + getName() + "') Unable to process packet! " + "Is the thread pool queue exhausted? " - + "Packet dropped in component '" + getName() + "'. Packet that's dropped: " + packet.toXML(), ex); + + "Packet dropped in component '" + getName() + "'. Packet that's dropped: " + packet.toString(), ex); // If the original packet was an IQ request, we should return an // error. if (packet instanceof IQ && ((IQ) packet).isRequest()) { @@ -271,7 +271,7 @@ final private void processQueuedPacket(final Packet packet) { * The IQ stanza that was received by this component. */ final private void processIQ(final IQ iq) { - log.debug("(serving component '{}') Processing IQ (packetId {}): {}", new Object[] { getName(), iq.getID(), iq.toXML() }); + log.debug("(serving component '{}') Processing IQ (packetId {}): {}", new Object[] { getName(), iq.getID(), iq.toString() }); IQ response = null; final Type type = iq.getType(); @@ -299,20 +299,20 @@ final private void processIQ(final IQ iq) { if (!response.isResponse()) throw new IllegalStateException("Responses to IQ " + "of type get or set can " + "only be IQ stanza's of type error " + "or result. The response to this " + "packet was incorrect: " - + iq.toXML() + ". The response was: " + response.toXML()); + + iq.toString() + ". The response was: " + response.toString()); // responses must have the same packet ID as the request if (!requestID.equals(response.getID())) throw new IllegalStateException("The response to " + "an request IQ must have the same packet " + "ID. If this was done intentionally, " + "#send(Packet) should have been used " + "instead. The response to this packet " - + "was incorrect: " + iq.toXML() + ". The response was: " + response.toXML()); - log.debug("(serving component '{}') Responding to IQ (packetId {}) with: {}", new Object[] { getName(), iq.getID(), response.toXML() }); + + "was incorrect: " + iq.toString() + ". The response was: " + response.toString()); + log.debug("(serving component '{}') Responding to IQ (packetId {}) with: {}", new Object[] { getName(), iq.getID(), response.toString() }); } break; case result: if (servesLocalUsersOnly() && !sentByLocalEntity(iq)) { log.info("(serving component '{}') Dropping IQ " + "stanza sent by a user from another domain: {}", getName(), iq.getFrom()); - log.debug("(serving component '{}') Dropping IQ " + "stanza sent by a user from another domain: {}", getName(), iq.toXML()); + log.debug("(serving component '{}') Dropping IQ " + "stanza sent by a user from another domain: {}", getName(), iq.toString()); return; } handleIQResult(iq); @@ -321,14 +321,14 @@ final private void processIQ(final IQ iq) { case error: if (servesLocalUsersOnly() && !sentByLocalEntity(iq)) { log.info("(serving component '{}') Dropping IQ " + "stanza sent by a user from another domain: {}", getName(), iq.getFrom()); - log.debug("(serving component '{}') Dropping IQ " + "stanza sent by a user from another domain: {}", getName(), iq.toXML()); + log.debug("(serving component '{}') Dropping IQ " + "stanza sent by a user from another domain: {}", getName(), iq.toString()); return; } handleIQError(iq); break; } } catch (final Exception ex) { - log.warn("(serving component '" + getName() + "') Unexpected exception while processing IQ stanza: " + iq.toXML(), ex); + log.warn("(serving component '" + getName() + "') Unexpected exception while processing IQ stanza: " + iq.toString(), ex); if (iq.isRequest()) { // if the received IQ stanza was a 'get' or 'set' request, // return an error, as some kind of response MUST be sent back @@ -353,10 +353,10 @@ final private void processIQ(final IQ iq) { * The message stanza to process. */ final private void processMessage(final Message message) { - log.trace("(serving component '{}') Processing message stanza: {}", getName(), message.toXML()); + log.trace("(serving component '{}') Processing message stanza: {}", getName(), message.toString()); if (servesLocalUsersOnly() && !sentByLocalEntity(message)) { log.info("(serving component '{}') Dropping message " + "stanza sent by a user from another domain: {}", getName(), message.getFrom()); - log.debug("(serving component '{}') Dropping message " + "stanza sent by a user from another domain: {}", getName(), message.toXML()); + log.debug("(serving component '{}') Dropping message " + "stanza sent by a user from another domain: {}", getName(), message.toString()); return; } handleMessage(message); @@ -372,10 +372,10 @@ final private void processMessage(final Message message) { * The presence stanza to process. */ final private void processPresence(final Presence presence) { - log.trace("(serving component '{}') Processing presence stanza: {}", getName(), presence.toXML()); + log.trace("(serving component '{}') Processing presence stanza: {}", getName(), presence.toString()); if (servesLocalUsersOnly() && !sentByLocalEntity(presence)) { log.info("(serving component '{}') Dropping presence " + "stanza sent by a user from another domain: {}", getName(), presence.getFrom()); - log.debug("(serving component '{}') Dropping presence " + "stanza sent by a user from another domain: {}", getName(), presence.toXML()); + log.debug("(serving component '{}') Dropping presence " + "stanza sent by a user from another domain: {}", getName(), presence.toString()); return; } handlePresence(presence); @@ -421,7 +421,7 @@ final private IQ processIQRequest(final IQ iq) throws Exception { log.debug("(serving component '{}') Processing IQ " + "request (packetId {}).", getName(), iq.getID()); // IQ get (and set) stanza's MUST be replied to. - final Element childElement = iq.getChildElement(); + final Element childElement = iq.getIQChildElement(); String namespace = null; if (childElement != null) { namespace = childElement.getNamespaceURI(); @@ -436,7 +436,7 @@ final private IQ processIQRequest(final IQ iq) throws Exception { // check if this is a component for local users only. if (servesLocalUsersOnly() && !sentByLocalEntity(iq)) { log.info("(serving component '{}') Returning " + "'not-authorized' IQ error to a user from " + "another domain: {}", getName(), iq.getFrom()); - log.debug("(serving component '{}') Returning " + "'not-authorized' IQ error to a user from " + "another domain: {}", getName(), iq.toXML()); + log.debug("(serving component '{}') Returning " + "'not-authorized' IQ error to a user from " + "another domain: {}", getName(), iq.toString()); final IQ error = IQ.createResultIQ(iq); error.setError(Condition.not_authorized); return error; @@ -495,7 +495,7 @@ protected void handleIQResult(final IQ iq) { protected void handleIQError(final IQ iq) { // Doesn't do anything. Override this method to process IQ error // stanzas. - log.info("(serving component '{}') IQ stanza " + "of type error received: {}", getName(), iq.toXML()); + log.info("(serving component '{}') IQ stanza " + "of type error received: {}", getName(), iq.toString()); } /** @@ -601,7 +601,7 @@ protected IQ handleDiscoItems(final IQ iq) { */ protected IQ handleDiscoInfo(final IQ iq) { final IQ replyPacket = IQ.createResultIQ(iq); - final Element responseElement = replyPacket.setChildElement("query", NAMESPACE_DISCO_INFO); + final Element responseElement = replyPacket.setIQChildElement("query", NAMESPACE_DISCO_INFO); // identity responseElement.addElement("identity").addAttribute("category", discoInfoIdentityCategory()).addAttribute("type", discoInfoIdentityCategoryType()) @@ -643,7 +643,7 @@ protected IQ handlePing(final IQ iq) { protected IQ handleLastActivity(final IQ iq) { final long uptime = (System.currentTimeMillis() - lastStartMillis) / 1000; final IQ result = IQ.createResultIQ(iq); - result.setChildElement("query", NAMESPACE_LAST_ACTIVITY).addAttribute("seconds", Long.toString(uptime)); + result.setIQChildElement("query", NAMESPACE_LAST_ACTIVITY).setAttribute("seconds", Long.toString(uptime)); return result; } @@ -665,7 +665,7 @@ protected IQ handleEntityTime(final IQ iq) { final String tzo = new StringBuilder(tz).insert(3, ':').toString(); final IQ result = IQ.createResultIQ(iq); - final Element el = result.setChildElement("time", NAMESPACE_ENTITY_TIME); + final Element el = result.setIQChildElement("time", NAMESPACE_ENTITY_TIME); el.addElement("tzo").setText(tzo); el.addElement("utc").setText(utc); return result; @@ -843,7 +843,7 @@ private void closeQueue() { if (packet instanceof IQ) { final IQ iq = (IQ) packet; if (iq.isRequest()) { - log.debug("Responding 'service unavailable' to " + "unprocessed stanza: {}", iq.toXML()); + log.debug("Responding 'service unavailable' to " + "unprocessed stanza: {}", iq.toString()); final IQ error = IQ.createResultIQ(iq); error.setError(Condition.service_unavailable); send(error); diff --git a/src/main/java/org/xmpp/forms/DataForm.java b/src/main/java/org/xmpp/forms/DataForm.java index 8a4f4ca..59f477c 100644 --- a/src/main/java/org/xmpp/forms/DataForm.java +++ b/src/main/java/org/xmpp/forms/DataForm.java @@ -28,11 +28,12 @@ import java.util.Map.Entry; import java.util.TimeZone; +import javax.xml.namespace.QName; + import net.jcip.annotations.NotThreadSafe; -import org.dom4j.Element; -import org.dom4j.QName; import org.jivesoftware.util.FastDateFormat; +import org.w3c.dom.Element; import org.xmpp.packet.PacketExtension; import org.xmpp.util.XMPPConstants; @@ -77,7 +78,7 @@ public class DataForm extends PacketExtension { static { UTC_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); // Register that DataForms uses the jabber:x:data namespace - registeredExtensions.put(QName.get(ELEMENT_NAME, NAMESPACE), DataForm.class); + registeredExtensions.put(new QName(NAMESPACE, ELEMENT_NAME), DataForm.class); } /** diff --git a/src/main/java/org/xmpp/forms/FormField.java b/src/main/java/org/xmpp/forms/FormField.java index ed900c1..654d1f4 100644 --- a/src/main/java/org/xmpp/forms/FormField.java +++ b/src/main/java/org/xmpp/forms/FormField.java @@ -22,7 +22,7 @@ import net.jcip.annotations.NotThreadSafe; -import org.dom4j.Element; +import org.w3c.dom.Element; /** * Represents a field of a form. The field could be used to represent a question @@ -36,7 +36,7 @@ public class FormField { private final Element element; - FormField(final Element element) { + private FormField(final Element element) { this.element = element; } diff --git a/src/main/java/org/xmpp/muc/DestroyRoom.java b/src/main/java/org/xmpp/muc/DestroyRoom.java index 856172f..246a332 100644 --- a/src/main/java/org/xmpp/muc/DestroyRoom.java +++ b/src/main/java/org/xmpp/muc/DestroyRoom.java @@ -18,7 +18,7 @@ import net.jcip.annotations.NotThreadSafe; -import org.dom4j.Element; +import org.w3c.dom.Element; import org.xmpp.packet.IQ; import org.xmpp.packet.JID; @@ -47,15 +47,17 @@ public class DestroyRoom extends IQ { * reason for the destruction or null if none. */ public DestroyRoom(final JID alternateJID, final String reason) { - super(); - setType(Type.set); - final Element query = setChildElement("query", "http://jabber.org/protocol/muc#owner"); - final Element destroy = query.addElement("destroy"); + super(Type.set); + + final Element query = setIQChildElement("query", "http://jabber.org/protocol/muc#owner"); + final Element destroy = addChildElement(query, "destroy"); + if (alternateJID != null) { - destroy.addAttribute("jid", alternateJID.toString()); + destroy.setAttribute("jid", alternateJID.toString()); } + if (reason != null) { - destroy.addElement("reason").setText(reason); + addChildElement(destroy, "reason").setTextContent(reason); } } } diff --git a/src/main/java/org/xmpp/muc/Invitation.java b/src/main/java/org/xmpp/muc/Invitation.java index e4b31f0..ddc2630 100644 --- a/src/main/java/org/xmpp/muc/Invitation.java +++ b/src/main/java/org/xmpp/muc/Invitation.java @@ -18,7 +18,7 @@ import net.jcip.annotations.NotThreadSafe; -import org.dom4j.Element; +import org.w3c.dom.Element; import org.xmpp.packet.JID; import org.xmpp.packet.Message; @@ -57,11 +57,12 @@ public class Invitation extends Message { */ public Invitation(final JID invitee, final String reason) { super(); - final Element element = addChildElement("x", "http://jabber.org/protocol/muc#user"); - final Element invite = element.addElement("invite"); - invite.addAttribute("to", invitee.toString()); - if (reason != null && reason.length() > 0) { - invite.addElement("reason").setText(reason); + final Element child = addChildElement(element, "x", "http://jabber.org/protocol/muc#user"); + final Element invite = addChildElement(child, "invite"); + + invite.setAttribute("to", invitee.toString()); + if (reason != null && !reason.isEmpty()) { + addChildElement(invite, "reason").setTextContent(reason); } } } diff --git a/src/main/java/org/xmpp/muc/JoinRoom.java b/src/main/java/org/xmpp/muc/JoinRoom.java index 8c617ac..ca55f63 100644 --- a/src/main/java/org/xmpp/muc/JoinRoom.java +++ b/src/main/java/org/xmpp/muc/JoinRoom.java @@ -52,9 +52,8 @@ public class JoinRoom extends Presence { * resource is the nickname of the user joining the room. */ public JoinRoom(final JID from, final JID to) { - super(); - setFrom(from); - setTo(to); - addChildElement("x", "http://jabber.org/protocol/muc"); + super(Presence.Type.available, from, to); + + addChildElement(element, "x", "http://jabber.org/protocol/muc"); } } diff --git a/src/main/java/org/xmpp/muc/LeaveRoom.java b/src/main/java/org/xmpp/muc/LeaveRoom.java index a83f19b..d7eebda 100644 --- a/src/main/java/org/xmpp/muc/LeaveRoom.java +++ b/src/main/java/org/xmpp/muc/LeaveRoom.java @@ -51,9 +51,6 @@ public class LeaveRoom extends Presence { * the user as a resource. */ public LeaveRoom(final JID from, final JID to) { - super(); - setFrom(from); - setTo(to); - setType(Type.unavailable); + super(Presence.Type.unavailable, from, to); } } diff --git a/src/main/java/org/xmpp/muc/RoomConfiguration.java b/src/main/java/org/xmpp/muc/RoomConfiguration.java index 1fd5af2..24f1758 100644 --- a/src/main/java/org/xmpp/muc/RoomConfiguration.java +++ b/src/main/java/org/xmpp/muc/RoomConfiguration.java @@ -22,7 +22,7 @@ import net.jcip.annotations.NotThreadSafe; -import org.dom4j.Element; +import org.w3c.dom.Element; import org.xmpp.packet.IQ; /** @@ -63,21 +63,25 @@ public class RoomConfiguration extends IQ { * the list of fields associated with the list of values. */ public RoomConfiguration(final Map> fieldValues) { - super(); - setType(Type.set); - final Element query = setChildElement("query", "http://jabber.org/protocol/muc#owner"); - final Element form = query.addElement("x", "jabber:x:data"); - form.addAttribute("type", "submit"); + super(Type.set); + + final Element query = setIQChildElement("query", "http://jabber.org/protocol/muc#owner"); + final Element form = addChildElement(query, "x", "jabber:x:data"); + form.setAttribute("type", "submit"); + + // TODO: use XMPP Forms + // Add static field - Element field = form.addElement("field"); - field.addAttribute("var", "FORM_TYPE"); - field.addElement("value").setText("http://jabber.org/protocol/muc#roomconfig"); + Element field = addChildElement(form, "field"); + field.setAttribute("var", "FORM_TYPE"); + addChildElement(field, "value").setTextContent("http://jabber.org/protocol/muc#roomconfig"); + // Add the specified fields and their corresponding values for (final Entry> entry : fieldValues.entrySet()) { - field = form.addElement("field"); - field.addAttribute("var", entry.getKey()); + field = addChildElement(form, "field"); + field.setAttribute("var", entry.getKey()); for (final String value : entry.getValue()) { - field.addElement("value").setText(value); + addChildElement(field, "value").setTextContent(value); } } } diff --git a/src/main/java/org/xmpp/packet/IQ.java b/src/main/java/org/xmpp/packet/IQ.java index 4759c0c..994cfac 100644 --- a/src/main/java/org/xmpp/packet/IQ.java +++ b/src/main/java/org/xmpp/packet/IQ.java @@ -16,17 +16,12 @@ package org.xmpp.packet; -import java.lang.reflect.Constructor; -import java.util.Iterator; -import java.util.List; import java.util.Random; import net.jcip.annotations.NotThreadSafe; -import org.dom4j.Element; -import org.dom4j.QName; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; /** * IQ (Info/Query) packet. IQ packets are used to get and set information on the @@ -41,21 +36,16 @@ @NotThreadSafe public class IQ extends Packet { - private static final Logger Log = LoggerFactory.getLogger(Packet.class); - // Sequence and random number generator used for creating unique ID's. + private static final Random random = new Random(); private static int sequence = 0; - private static Random random = new Random(); /** * Constructs a new IQ with an automatically generated ID and a type of * {@link Type#get IQ.Type.get}. */ public IQ() { - element = docFactory.createDocument().addElement("iq"); - final String id = String.valueOf(random.nextInt(1000) + "-" + sequence++); - setType(Type.get); - setID(id); + this(Type.get, null); } /** @@ -66,10 +56,7 @@ public IQ() { * the IQ type. */ public IQ(final Type type) { - element = docFactory.createDocument().addElement("iq"); - setType(type); - final String id = String.valueOf(random.nextInt(1000) + "-" + sequence++); - setID(id); + this(type, null); } /** @@ -81,9 +68,9 @@ public IQ(final Type type) { * the IQ type. */ public IQ(final Type type, final String ID) { - element = docFactory.createDocument().addElement("iq"); + super("iq"); setType(type); - setID(ID); + setID(ID != null ? ID : String.valueOf(random.nextInt(1000) + "-" + sequence++)); } /** @@ -97,48 +84,16 @@ public IQ(final Element element) { super(element); } - /** - * Constructs a new IQ using an existing Element. This is useful for parsing - * incoming IQ Elements into IQ objects. Stringprep validation on the TO - * address can be disabled. The FROM address will not be validated since the - * server is the one that sets that value. - * - * @param element - * the IQ Element. - * @param skipValidation - * true if stringprep should not be applied to the TO address. - */ - public IQ(final Element element, final boolean skipValidation) { - super(element, skipValidation); - } - - /** - * Constructs a new IQ that is a copy of an existing IQ. - * - * @param iq - * the iq packet. - * @see #createCopy() - */ - private IQ(final IQ iq) { - final Element elementCopy = iq.element.createCopy(); - docFactory.createDocument().add(elementCopy); - element = elementCopy; - // Copy cached JIDs (for performance reasons) - toJID = iq.toJID; - fromJID = iq.fromJID; - } - /** * Returns the type of this IQ. * * @return the IQ type. * @see Type */ - public Type getType() { - final String type = element.attributeValue("type"); - if (type != null) - return Type.valueOf(type); - return null; + public final Type getType() { + final String type = getAttribute(element, "type"); + + return type != null ? Type.valueOf(type) : null; } /** @@ -148,8 +103,8 @@ public Type getType() { * the IQ type. * @see Type */ - public void setType(final Type type) { - element.addAttribute("type", type == null ? null : type.toString()); + public final void setType(final Type type) { + setAttribute(element, "type", type != null ? type.toString() : null); } /** @@ -157,8 +112,9 @@ public void setType(final Type type) { * * @return True or false if this is a request stanza */ - public boolean isRequest() { + public final boolean isRequest() { final Type type = getType(); + return type != null && (type.equals(Type.get) || type.equals(Type.set)); } @@ -168,8 +124,9 @@ public boolean isRequest() { * * @return True or false if this is a response stanza */ - public boolean isResponse() { + public final boolean isResponse() { final Type type = getType(); + return type != null && (type.equals(Type.result) || type.equals(Type.error)); } @@ -186,17 +143,16 @@ public boolean isResponse() { * * @return the child element. */ - @SuppressWarnings("unchecked") - public Element getChildElement() { - final List elements = element.elements(); - if (elements.isEmpty()) + public Element getIQChildElement() { + final NodeList elements = element.getChildNodes(); + if (elements.getLength() == 0) return null; // Search for a child element that is in a different namespace. - for (int i = 0; i < elements.size(); i++) { - final Element element = elements.get(i); + for (int i = 0; i < elements.getLength(); i++) { + final Element element = (Element) elements.item(i); final String namespace = element.getNamespaceURI(); - if (!namespace.equals("") && !namespace.equals("jabber:client") && !namespace.equals("jabber:server")) + if (namespace != null && !namespace.equals("jabber:client") && !namespace.equals("jabber:server")) return element; } return null; @@ -226,12 +182,13 @@ public Element getChildElement() { * @param childElement * the child element. */ - @SuppressWarnings("unchecked") - public void setChildElement(final Element childElement) { - for (final Iterator i = element.elementIterator(); i.hasNext();) { - element.remove(i.next()); + public void setIQChildElement(final Element childElement) { + final Element currentChild = getIQChildElement(); + if (currentChild == null) { + element.appendChild(childElement); + } else { + element.replaceChild(childElement, currentChild); } - element.add(childElement); } /** @@ -262,12 +219,11 @@ public void setChildElement(final Element childElement) { * the child element namespace. * @return the newly created child element. */ - @SuppressWarnings("unchecked") - public Element setChildElement(final String name, final String namespace) { - for (final Iterator i = element.elementIterator(); i.hasNext();) { - element.remove(i.next()); - } - return element.addElement(name, namespace); + public Element setIQChildElement(final String qualifiedName, final String namespaceURI) { + final Element childElement = element.getOwnerDocument().createElementNS(namespaceURI, qualifiedName); + setIQChildElement(childElement); + return childElement; + } /** @@ -295,11 +251,12 @@ public Element setChildElement(final String name, final String namespace) { */ @Override public void addExtension(final PacketExtension extension) { - final Element childElement = getChildElement(); + final Element childElement = getIQChildElement(); if (childElement == null) throw new IllegalStateException("Cannot add packet extension when child element is null"); + // Add the extension to the child element - childElement.add(extension.getElement()); + PacketExtension.addExtension(childElement, extension); } /** @@ -322,25 +279,13 @@ public void addExtension(final PacketExtension extension) { * found. */ @Override - @SuppressWarnings("unchecked") - public PacketExtension getExtension(final String name, final String namespace) { - final Element childElement = getChildElement(); + public PacketExtension getExtension(final String name, final String namespaceURI) { + final Element childElement = getIQChildElement(); if (childElement == null) return null; + // Search for extensions in the child element - final List extensions = childElement.elements(QName.get(name, namespace)); - if (!extensions.isEmpty()) { - final Class extensionClass = PacketExtension.getExtensionClass(name, namespace); - if (extensionClass != null) { - try { - final Constructor constructor = extensionClass.getDeclaredConstructor(new Class[] { Element.class }); - return constructor.newInstance(new Object[] { extensions.get(0) }); - } catch (final Exception e) { - Log.warn("Packet extension (name " + name + ", namespace " + namespace + ") cannot be found.", e); - } - } - } - return null; + return PacketExtension.getExtension(childElement, name, namespaceURI); } /** @@ -366,28 +311,13 @@ public PacketExtension getExtension(final String name, final String namespace) { * @return true if a child element was removed. */ @Override - @SuppressWarnings("unchecked") - public boolean deleteExtension(final String name, final String namespace) { - final Element childElement = getChildElement(); + public boolean deleteExtension(final String name, final String namespaceURI) { + final Element childElement = getIQChildElement(); if (childElement == null) return false; - // Delete extensions in the child element - final List extensions = childElement.elements(QName.get(name, namespace)); - if (!extensions.isEmpty()) { - childElement.remove(extensions.get(0)); - return true; - } - return false; - } - /** - * Returns a deep copy of this IQ. - * - * @return a deep copy of this IQ. - */ - @Override - public IQ createCopy() { - return new IQ(this); + // Delete extensions in the child element + return PacketExtension.deleteExtension(childElement, name, namespaceURI); } /** @@ -412,8 +342,9 @@ public IQ createCopy() { * originating IQ. */ public static IQ createResultIQ(final IQ iq) { - if (!(iq.getType() == Type.get || iq.getType() == Type.set)) - throw new IllegalArgumentException("IQ must be of type 'set' or 'get'. Original IQ: " + iq.toXML()); + if (!iq.isRequest()) + throw new IllegalArgumentException("IQ must be of type 'set' or 'get'. Original IQ: " + iq.toString()); + final IQ result = new IQ(Type.result, iq.getID()); result.setFrom(iq.getTo()); result.setTo(iq.getFrom()); diff --git a/src/main/java/org/xmpp/packet/Message.java b/src/main/java/org/xmpp/packet/Message.java index be5c9b2..1e7ec1d 100644 --- a/src/main/java/org/xmpp/packet/Message.java +++ b/src/main/java/org/xmpp/packet/Message.java @@ -16,11 +16,9 @@ package org.xmpp.packet; -import java.util.Iterator; - import net.jcip.annotations.NotThreadSafe; -import org.dom4j.Element; +import org.w3c.dom.Element; /** * Message packet. @@ -84,7 +82,7 @@ public class Message extends Packet { * Constructs a new Message. */ public Message() { - element = docFactory.createDocument().addElement("message"); + super("message"); } /** @@ -98,37 +96,6 @@ public Message(final Element element) { super(element); } - /** - * Constructs a new Message using an existing Element. This is useful for - * parsing incoming message Elements into Message objects. Stringprep - * validation on the TO address can be disabled. The FROM address will not - * be validated since the server is the one that sets that value. - * - * @param element - * the message Element. - * @param skipValidation - * true if stringprep should not be applied to the TO address. - */ - public Message(final Element element, final boolean skipValidation) { - super(element, skipValidation); - } - - /** - * Constructs a new Message that is a copy of an existing Message. - * - * @param message - * the message packet. - * @see #createCopy() - */ - private Message(final Message message) { - final Element elementCopy = message.element.createCopy(); - docFactory.createDocument().add(elementCopy); - element = elementCopy; - // Copy cached JIDs (for performance reasons) - toJID = message.toJID; - fromJID = message.fromJID; - } - /** * Returns the type of this message * @@ -136,10 +103,9 @@ private Message(final Message message) { * @see Type */ public Type getType() { - final String type = element.attributeValue("type"); - if (type != null) - return Type.valueOf(type); - return Type.normal; + final String type = getAttribute(element, "type"); + + return type != null ? Type.valueOf(type) : Type.normal; } /** @@ -150,7 +116,7 @@ public Type getType() { * @see Type */ public void setType(final Type type) { - element.addAttribute("type", type == null ? null : type.toString()); + setAttribute(element, "type", type != null ? type.toString() : null); } /** @@ -160,7 +126,7 @@ public void setType(final Type type) { * @return the subject. */ public String getSubject() { - return element.elementText("subject"); + return getChildElementText(element, "subject"); } /** @@ -170,19 +136,7 @@ public String getSubject() { * the subject. */ public void setSubject(final String subject) { - Element subjectElement = element.element("subject"); - // If subject is null, clear the subject. - if (subject == null && subjectElement != null) { - element.remove(subjectElement); - return; - } - // Do nothing if the new subject is null - if (subject == null) - return; - if (subjectElement == null) { - subjectElement = element.addElement("subject"); - } - subjectElement.setText(subject); + setChildElementText(element, "subject", subject); } /** @@ -191,7 +145,7 @@ public void setSubject(final String subject) { * @return the body. */ public String getBody() { - return element.elementText("body"); + return getChildElementText(element, "body"); } /** @@ -201,18 +155,7 @@ public String getBody() { * the body. */ public void setBody(final String body) { - Element bodyElement = element.element("body"); - // If body is null, clear the body. - if (body == null) { - if (bodyElement != null) { - element.remove(bodyElement); - } - return; - } - if (bodyElement == null) { - bodyElement = element.addElement("body"); - } - bodyElement.setText(body); + setChildElementText(element, "body", body); } /** @@ -223,7 +166,7 @@ public void setBody(final String body) { * @return the thread value. */ public String getThread() { - return element.elementText("thread"); + return getChildElementText(element, "thread"); } /** @@ -235,84 +178,7 @@ public String getThread() { * thread value. */ public void setThread(final String thread) { - Element threadElement = element.element("thread"); - // If thread is null, clear the thread. - if (thread == null) { - if (threadElement != null) { - element.remove(threadElement); - } - return; - } - - if (threadElement == null) { - threadElement = element.addElement("thread"); - } - threadElement.setText(thread); - } - - /** - * Returns the first child element of this packet that matches the given - * name and namespace. If no matching element is found, null will - * be returned. This is a convenience method to avoid manipulating this - * underlying packet's Element instance directly. - *

- * - * Child elements in extended namespaces are used to extend the features of - * XMPP. Examples include a "user is typing" indicator and invitations to - * group chat rooms. Although any valid XML can be included in a child - * element in an extended namespace, many common features have been - * standardized as XMPP Extension - * Protocols (XEPs). - * - * @param name - * the element name. - * @param namespace - * the element namespace. - * @return the first matching child element, or null if there is no - * matching child element. - */ - @SuppressWarnings("unchecked") - public Element getChildElement(final String name, final String namespace) { - for (final Iterator i = element.elementIterator(name); i.hasNext();) { - final Element element = i.next(); - if (element.getNamespaceURI().equals(namespace)) - return element; - } - return null; - } - - /** - * Adds a new child element to this packet with the given name and - * namespace. The newly created Element is returned. This is a convenience - * method to avoid manipulating this underlying packet's Element instance - * directly. - *

- * - * Child elements in extended namespaces are used to extend the features of - * XMPP. Examples include a "user is typing" indicator and invitations to - * group chat rooms. Although any valid XML can be included in a child - * element in an extended namespace, many common features have been - * standardized as XMPP Extension - * Protocols (XEPs). - * - * @param name - * the element name. - * @param namespace - * the element namespace. - * @return the newly created child element. - */ - public Element addChildElement(final String name, final String namespace) { - return element.addElement(name, namespace); - } - - /** - * Returns a deep copy of this Message. - * - * @return a deep copy of this Message. - */ - @Override - public Message createCopy() { - return new Message(this); + setChildElementText(element, "thread", thread); } /** diff --git a/src/main/java/org/xmpp/packet/Packet.java b/src/main/java/org/xmpp/packet/Packet.java index bfaf33d..6703354 100644 --- a/src/main/java/org/xmpp/packet/Packet.java +++ b/src/main/java/org/xmpp/packet/Packet.java @@ -16,19 +16,10 @@ package org.xmpp.packet; -import java.io.StringWriter; -import java.lang.reflect.Constructor; -import java.util.List; - import net.jcip.annotations.NotThreadSafe; -import org.dom4j.DocumentFactory; -import org.dom4j.Element; -import org.dom4j.QName; -import org.dom4j.io.OutputFormat; -import org.dom4j.io.XMLWriter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; +import org.xmpp.util.BaseXML; /** * An XMPP packet (also referred to as a stanza). Each packet is backed by a @@ -48,78 +39,18 @@ * @author Matt Tucker */ @NotThreadSafe -public abstract class Packet { - - private static final Logger Log = LoggerFactory.getLogger(Packet.class); - - protected static final DocumentFactory docFactory = DocumentFactory.getInstance(); +public class Packet extends BaseXML { - protected Element element; - - // Cache to and from JIDs - protected JID toJID; - protected JID fromJID; - - /** - * Constructs a new Packet. The TO address contained in the XML Element will - * only be validated. In other words, stringprep operations will only be - * performed on the TO JID to verify that it is well-formed. The FROM - * address is assigned by the server so there is no need to verify it. - * - * @param element - * the XML Element that contains the packet contents. - */ - public Packet(final Element element) { - this(element, false); + protected Packet(final String elementName) { + super(elementName); } - /** - * Constructs a new Packet. The JID address contained in the XML Element may - * not be validated. When validation can be skipped then stringprep - * operations will not be performed on the JIDs to verify that addresses are - * well-formed. However, when validation cannot be skipped then - * only the TO address will be verified. The FROM address is - * assigned by the server so there is no need to verify it. - * - * @param element - * the XML Element that contains the packet contents. - * @param skipValidation - * true if stringprep should not be applied to the TO address. - */ - public Packet(final Element element, final boolean skipValidation) { - this.element = element; - // Apply stringprep profiles to the "to" and "from" values. - final String to = element.attributeValue("to"); - if (to != null) { - if (to.length() == 0) { - // Remove empty TO values - element.addAttribute("to", null); - } else { - final String[] parts = JID.getParts(to); - toJID = new JID(parts[0], parts[1], parts[2], skipValidation); - element.addAttribute("to", toJID.toString()); - } - } - final String from = element.attributeValue("from"); - if (from != null) { - if (from.length() == 0) { - // Remove empty FROM values - element.addAttribute("from", null); - } else { - final String[] parts = JID.getParts(from); - fromJID = new JID(parts[0], parts[1], parts[2], true); - element.addAttribute("from", fromJID.toString()); - } - } + protected Packet(final String qualifiedName, final String namespaceURI) { + super(qualifiedName, namespaceURI); } - /** - * Constructs a new Packet with no element data. This method is used by - * extensions of this class that require a more optimized path for creating - * new packets. - */ - protected Packet() { - + protected Packet(final Element element) { + super(element); } /** @@ -128,8 +59,8 @@ protected Packet() { * * @return the packet ID. */ - public String getID() { - return element.attributeValue("id"); + public final String getID() { + return getAttribute(element, "id"); } /** @@ -138,69 +69,8 @@ public String getID() { * @param ID * the packet ID. */ - public void setID(final String ID) { - element.addAttribute("id", ID); - } - - /** - * Returns the XMPP address (JID) that the packet is addressed to, or - * null if the "to" attribute is not set. The XMPP protocol often - * makes the "to" attribute optional, so it does not always need to be set. - * - * @return the XMPP address (JID) that the packet is addressed to, or - * null if not set. - */ - public JID getTo() { - final String to = element.attributeValue("to"); - if (to == null || to.length() == 0) - return null; - - if (toJID != null && to.equals(toJID.toString())) - return toJID; - - // Return a new JID that bypasses stringprep profile checking. - // This improves speed and is safe as long as the user doesn't - // directly manipulate the attributes of the underlying Element - // that represent JID's. - final String[] parts = JID.getParts(to); - toJID = new JID(parts[0], parts[1], parts[2], true); - return toJID; - } - - /** - * Sets the XMPP address (JID) that the packet is addressed to. The XMPP - * protocol often makes the "to" attribute optional, so it does not always - * need to be set. - * - * @param to - * the XMPP address (JID) that the packet is addressed to. - */ - public void setTo(String to) { - // Apply stringprep profiles to value. - if (to != null) { - toJID = new JID(to); - to = toJID.toString(); - } else { - toJID = null; - } - element.addAttribute("to", to); - } - - /** - * Sets the XMPP address (JID) that the packet is address to. The XMPP - * protocol often makes the "to" attribute optional, so it does not always - * need to be set. - * - * @param to - * the XMPP address (JID) that the packet is addressed to. - */ - public void setTo(final JID to) { - toJID = to; - if (to == null) { - element.addAttribute("to", null); - } else { - element.addAttribute("to", to.toString()); - } + public final void setID(final String id) { + setAttribute(element, "id", id); } /** @@ -211,21 +81,10 @@ public void setTo(final JID to) { * @return the XMPP address that the packet is from, or null if not * set. */ - public JID getFrom() { - final String from = element.attributeValue("from"); - if (from == null || from.length() == 0) - return null; - - if (fromJID != null && from.equals(fromJID.toString())) - return fromJID; + public final JID getFrom() { + final String from = getAttribute(element, "from"); - // Return a new JID that bypasses stringprep profile checking. - // This improves speed and is safe as long as the user doesn't - // directly manipulate the attributes of the underlying Element - // that represent JID's. - final String[] parts = JID.getParts(from); - fromJID = new JID(parts[0], parts[1], parts[2], true); - return fromJID; + return from != null ? new JID(from) : null; } /** @@ -236,32 +95,34 @@ public JID getFrom() { * @param from * the XMPP address (JID) that the packet comes from. */ - public void setFrom(String from) { - // Apply stringprep profiles to value. - if (from != null) { - fromJID = new JID(from); - from = fromJID.toString(); - } else { - fromJID = null; - } - element.addAttribute("from", from); + public final void setFrom(final JID from) { + setAttribute(element, "from", from != null ? from.toString() : null); } /** - * Sets the XMPP address (JID) that the packet comes from. The XMPP protocol - * often makes the "from" attribute optional, so it does not always need to - * be set. + * Returns the XMPP address (JID) that the packet is addressed to, or + * null if the "to" attribute is not set. The XMPP protocol often + * makes the "to" attribute optional, so it does not always need to be set. * - * @param from - * the XMPP address (JID) that the packet comes from. + * @return the XMPP address (JID) that the packet is addressed to, or + * null if not set. */ - public void setFrom(final JID from) { - fromJID = from; - if (from == null) { - element.addAttribute("from", null); - } else { - element.addAttribute("from", from.toString()); - } + public final JID getTo() { + final String to = getAttribute(element, "to"); + + return to != null ? new JID(to) : null; + } + + /** + * Sets the XMPP address (JID) that the packet is address to. The XMPP + * protocol often makes the "to" attribute optional, so it does not always + * need to be set. + * + * @param to + * the XMPP address (JID) that the packet is addressed to. + */ + public final void setTo(final JID to) { + setAttribute(element, "to", to != null ? to.toString() : null); } /** @@ -277,7 +138,7 @@ public void setFrom(final JID from) { * Packet's element. */ public void addExtension(final PacketExtension extension) { - element.add(extension.getElement()); + PacketExtension.addExtension(element, extension); } /** @@ -292,24 +153,8 @@ public void addExtension(final PacketExtension extension) { * @return a PacketExtension on the first element found in this packet for * the specified name and namespace or null if none was found. */ - @SuppressWarnings("unchecked") - public PacketExtension getExtension(final String name, final String namespace) { - final List extensions = element.elements(QName.get(name, namespace)); - if (!extensions.isEmpty()) { - final Class extensionClass = PacketExtension.getExtensionClass(name, namespace); - // If a specific PacketExtension implementation has been registered, - // use that. - if (extensionClass != null) { - try { - final Constructor constructor = extensionClass.getDeclaredConstructor(Element.class); - return constructor.newInstance(extensions.get(0)); - } catch (final Exception e) { - Log.warn("Packet extension (name " + name + ", namespace " + namespace + ") cannot be found.", e); - } - } else - return new PacketExtension(extensions.get(0)); - } - return null; + public PacketExtension getExtension(final String name, final String namespaceURI) { + return PacketExtension.getExtension(element, name, namespaceURI); } /** @@ -328,14 +173,8 @@ public PacketExtension getExtension(final String name, final String namespace) { * the child element namespace. * @return true if a child element was removed. */ - @SuppressWarnings("unchecked") - public boolean deleteExtension(final String name, final String namespace) { - final List extensions = element.elements(QName.get(name, namespace)); - if (!extensions.isEmpty()) { - element.remove(extensions.get(0)); - return true; - } - return false; + public boolean deleteExtension(final String name, final String namespaceURI) { + return PacketExtension.deleteExtension(element, name, namespaceURI); } /** @@ -343,11 +182,10 @@ public boolean deleteExtension(final String name, final String namespace) { * * @return the packet error. */ - public PacketError getError() { - final Element error = element.element("error"); - if (error != null) - return new PacketError(error); - return null; + public final PacketError getError() { + final Element error = getChildElement(element, "error"); + + return error != null ? new PacketError(error) : null; } /** @@ -357,17 +195,18 @@ public PacketError getError() { * @param error * the packet error. */ - public void setError(final PacketError error) { - if (element == null) + public final void setError(final PacketError error) { + if (error == null) throw new NullPointerException("Error cannot be null"); + // Force the packet type to "error". - element.addAttribute("type", "error"); + setAttribute(element, "type", "error"); // Remove an existing error packet. - if (element.element("error") != null) { - element.remove(element.element("error")); + if (getError() != null) { + element.removeChild(getError().getElement()); } // Add the error element. - element.add(error.getElement()); + element.appendChild(error.getElement()); } /** @@ -380,46 +219,13 @@ public void setError(final PacketError error) { * @param condition * the error condition. */ - public void setError(final PacketError.Condition condition) { + public final void setError(final PacketError.Condition condition) { setError(new PacketError(condition)); } - /** - * Creates a deep copy of this packet. - * - * @return a deep copy of this packet. - */ - public abstract Packet createCopy(); - - /** - * Returns the DOM4J Element that backs the packet. The element is the - * definitive representation of the packet and can be manipulated directly - * to change packet contents. - * - * @return the DOM4J Element that represents the packet. - */ - public Element getElement() { - return element; - } - - /** - * Returns the textual XML representation of this packet. - * - * @return the textual XML representation of this packet. - */ - public String toXML() { - return element.asXML(); - } - @Override - public String toString() { - final StringWriter out = new StringWriter(); - final XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint()); - try { - writer.write(element); - } catch (final Exception e) { - // Ignore. - } - return out.toString(); + public Packet clone() { + return new Packet(element); } + } \ No newline at end of file diff --git a/src/main/java/org/xmpp/packet/PacketError.java b/src/main/java/org/xmpp/packet/PacketError.java index 65863cd..5588b01 100644 --- a/src/main/java/org/xmpp/packet/PacketError.java +++ b/src/main/java/org/xmpp/packet/PacketError.java @@ -16,16 +16,15 @@ package org.xmpp.packet; -import java.io.StringWriter; import java.util.Iterator; +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; + import net.jcip.annotations.NotThreadSafe; -import org.dom4j.DocumentFactory; -import org.dom4j.Element; -import org.dom4j.QName; -import org.dom4j.io.OutputFormat; -import org.dom4j.io.XMLWriter; +import org.w3c.dom.Element; +import org.xmpp.util.BaseXML; /** * A packet error. Errors must have a type and condition. Optionally, they can @@ -34,14 +33,10 @@ * @author Matt Tucker */ @NotThreadSafe -public class PacketError { +public class PacketError extends BaseXML { private static final String ERROR_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-stanzas"; - private static DocumentFactory docFactory = DocumentFactory.getInstance(); - - private final Element element; - /** * Construcs a new PacketError with the specified condition. The error type * will be set to the default for the specified condition. @@ -50,9 +45,7 @@ public class PacketError { * the error condition. */ public PacketError(final Condition condition) { - element = docFactory.createElement("error"); - setCondition(condition); - setType(condition.getDefaultType()); + this(condition, condition.getDefaultType(), null, null); } /** @@ -64,9 +57,7 @@ public PacketError(final Condition condition) { * the error type. */ public PacketError(final Condition condition, final Type type) { - element = docFactory.createElement("error"); - setCondition(condition); - setType(type); + this(condition, type, null, null); } /** @@ -80,10 +71,7 @@ public PacketError(final Condition condition, final Type type) { * the text description of the error. */ public PacketError(final Condition condition, final Type type, final String text) { - element = docFactory.createElement("error"); - setType(type); - setCondition(condition); - setText(text, null); + this(condition, type, text, null); } /** @@ -99,9 +87,9 @@ public PacketError(final Condition condition, final Type type, final String text * the language code of the error description (e.g. "en"). */ public PacketError(final Condition condition, final Type type, final String text, final String lang) { - element = docFactory.createElement("error"); - setType(type); + super("error"); setCondition(condition); + setType(type); setText(text, lang); } @@ -113,7 +101,7 @@ public PacketError(final Condition condition, final Type type, final String text * the error Element. */ public PacketError(final Element element) { - this.element = element; + super(element); } /** @@ -122,11 +110,10 @@ public PacketError(final Element element) { * @return the error type. * @see Type */ - public Type getType() { - final String type = element.attributeValue("type"); - if (type != null) - return Type.fromXMPP(type); - return null; + public final Type getType() { + final String type = element.getAttribute("type"); + + return !type.isEmpty() ? Type.fromXMPP(type) : null; } /** @@ -136,8 +123,12 @@ public Type getType() { * the error type. * @see Type */ - public void setType(final Type type) { - element.addAttribute("type", type == null ? null : type.toXMPP()); + public final void setType(final Type type) { + if (type != null) { + element.setAttribute("type", type.toXMPP()); + } else { + element.removeAttribute("type"); + } } /** @@ -227,11 +218,11 @@ public void setText(final String text) { * specify no language code. */ public void setText(final String text, final String lang) { - Element textElement = element.element("text"); + final Element textElement = (Element) element.getElementsByTagName("text").item(0); // If text is null, clear the text. if (text == null) { if (textElement != null) { - element.remove(textElement); + element.removeChild(textElement); } return; } @@ -243,7 +234,8 @@ public void setText(final String text, final String lang) { } element.add(textElement); } - textElement.setText(text); + + textElement.setTextContent(text); } /** @@ -253,10 +245,9 @@ public void setText(final String text, final String lang) { * @return the language code of the text description, if it exists. */ public String getTextLang() { - final Element textElement = element.element("text"); - if (textElement != null) - return textElement.attributeValue(QName.get("lang", "xml", "http://www.w3.org/XML/1998/namespace")); - return null; + final Element textElement = (Element) element.getElementsByTagName("text").item(0); + + return textElement != null ? textElement.getAttributeNS(XMLConstants.XML_NS_URI, "xml:lang") : null; } /** @@ -343,38 +334,6 @@ public String getApplicationConditionNamespaceURI() { return null; } - /** - * Returns the DOM4J Element that backs the error. The element is the - * definitive representation of the error and can be manipulated directly to - * change error contents. - * - * @return the DOM4J Element. - */ - public Element getElement() { - return element; - } - - /** - * Returns the textual XML representation of this error. - * - * @return the textual XML representation of this error. - */ - public String toXML() { - return element.asXML(); - } - - @Override - public String toString() { - final StringWriter out = new StringWriter(); - final XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint()); - try { - writer.write(element); - } catch (final Exception e) { - // Ignore. - } - return out.toString(); - } - /** * Type-safe enumeration for the error condition. *

diff --git a/src/main/java/org/xmpp/packet/PacketExtension.java b/src/main/java/org/xmpp/packet/PacketExtension.java index 839948c..a6995b0 100644 --- a/src/main/java/org/xmpp/packet/PacketExtension.java +++ b/src/main/java/org/xmpp/packet/PacketExtension.java @@ -16,14 +16,18 @@ package org.xmpp.packet; +import java.lang.reflect.Constructor; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import javax.xml.namespace.QName; + import net.jcip.annotations.NotThreadSafe; -import org.dom4j.DocumentFactory; -import org.dom4j.Element; -import org.dom4j.QName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; +import org.xmpp.util.BaseXML; /** * A packet extension represents a child element of a Packet for a given @@ -43,17 +47,16 @@ * @author Gaston Dombiak */ @NotThreadSafe -public class PacketExtension { +public class PacketExtension extends BaseXML { + + private static final Logger log = LoggerFactory.getLogger(PacketExtension.class); - protected static final DocumentFactory docFactory = DocumentFactory.getInstance(); /** * Subclasses of PacketExtension should register the element name and * namespace that the subclass is using. */ protected static final Map> registeredExtensions = new ConcurrentHashMap>(); - protected Element element; - /** * Returns the extension class to use for the specified element name and * namespace. For instance, the DataForm class should be used for the @@ -66,8 +69,8 @@ public class PacketExtension { * @return the extension class to use for the specified element name and * namespace. */ - public static Class getExtensionClass(final String name, final String namespace) { - return registeredExtensions.get(QName.get(name, namespace)); + public static final Class getExtensionClass(final String localName, final String namespaceURI) { + return registeredExtensions.get(new QName(namespaceURI, localName)); } /** @@ -78,8 +81,8 @@ public static Class getExtensionClass(final String na * @param namespace * the child element namespace. */ - public PacketExtension(final String name, final String namespace) { - element = docFactory.createDocument().addElement(name, namespace); + protected PacketExtension(final String qualifiedName, final String namespaceURI) { + super(qualifiedName, namespaceURI); } /** @@ -88,29 +91,82 @@ public PacketExtension(final String name, final String namespace) { * @param element * the XML Element that contains the packet extension contents. */ - public PacketExtension(final Element element) { - this.element = element; + protected PacketExtension(final Element element) { + super(element); } /** - * Returns the DOM4J Element that backs the packet. The element is the - * definitive representation of the packet and can be manipulated directly - * to change packet contents. + * Adds the element contained in the PacketExtension to the element of this + * packet. It is important that this is the first and last time the element + * contained in PacketExtension is added to another Packet. Otherwise, a + * runtime error will be thrown when trying to add the PacketExtension's + * element to the Packet's element. Future modifications to the + * PacketExtension will be reflected in this Packet. * - * @return the DOM4J Element that represents the packet. + * @param extension + * the PacketExtension whose element will be added to this + * Packet's element. */ - public Element getElement() { - return element; + public static final void addExtension(final Element element, final PacketExtension extension) { + element.appendChild(extension.getElement()); } /** - * Creates a deep copy of this packet extension. + * Returns a {@link PacketExtension} on the first element found in this + * packet for the specified name and namespace or + * null if none was found. * - * @return a deep copy of this packet extension. + * @param name + * the child element name. + * @param namespace + * the child element namespace. + * @return a PacketExtension on the first element found in this packet for + * the specified name and namespace or null if none was found. */ - public PacketExtension createCopy() { - final Element copy = element.createCopy(); - docFactory.createDocument().add(copy); - return new PacketExtension(element); + public static final PacketExtension getExtension(final Element element, final String name, final String namespaceURI) { + final Element extension = getChildElement(element, name, namespaceURI); + if (extension == null) + return null; + + final Class extensionClass = PacketExtension.getExtensionClass(name, namespaceURI); + + // If a specific PacketExtension implementation has been registered, use + // that + if (extensionClass != null) { + try { + final Constructor constructor = extensionClass.getDeclaredConstructor(Element.class); + return constructor.newInstance(extension); + } catch (final Exception e) { + log.warn("Packet extension (name " + name + ", namespace " + namespaceURI + ") cannot be found.", e); + return null; + } + } + + return new PacketExtension(extension); } + + /** + * Deletes the first element whose element name and namespace matches the + * specified element name and namespace. + *

+ * + * Notice that this method may remove any child element that matches the + * specified element name and namespace even if that element was not added + * to the Packet using a {@link PacketExtension}. + * + * + * @param name + * the child element name. + * @param namespace + * the child element namespace. + * @return true if a child element was removed. + */ + public static final boolean deleteExtension(final Element element, final String name, final String namespaceURI) { + final Element extension = getChildElement(element, name, namespaceURI); + if (extension == null) + return false; + + return element.removeChild(extension) != null; + } + } diff --git a/src/main/java/org/xmpp/packet/Presence.java b/src/main/java/org/xmpp/packet/Presence.java index a76f058..3b2805a 100644 --- a/src/main/java/org/xmpp/packet/Presence.java +++ b/src/main/java/org/xmpp/packet/Presence.java @@ -16,11 +16,9 @@ package org.xmpp.packet; -import java.util.Iterator; - import net.jcip.annotations.NotThreadSafe; -import org.dom4j.Element; +import org.w3c.dom.Element; /** * Presence packet. Presence packets are used to express an entity's current @@ -40,7 +38,7 @@ public class Presence extends Packet { * Constructs a new Presence. */ public Presence() { - element = docFactory.createDocument().addElement("presence"); + super("presence"); } /** @@ -54,59 +52,10 @@ public Presence(final Presence.Type type) { setType(type); } - /** - * Constructs a new Presence using an existing Element. This is useful for - * parsing incoming presence Elements into Presence objects. - * - * @param element - * the presence Element. - */ - public Presence(final Element element) { - super(element); - } - - /** - * Constructs a new Presence using an existing Element. This is useful for - * parsing incoming Presence Elements into Presence objects. Stringprep - * validation on the TO address can be disabled. The FROM address will not - * be validated since the server is the one that sets that value. - * - * @param element - * the Presence Element. - * @param skipValidation - * true if stringprep should not be applied to the TO address. - */ - public Presence(final Element element, final boolean skipValidation) { - super(element, skipValidation); - } - - /** - * Constructs a new Presence that is a copy of an existing Presence. - * - * @param presence - * the presence packet. - * @see #createCopy() - */ - private Presence(final Presence presence) { - final Element elementCopy = presence.element.createCopy(); - docFactory.createDocument().add(elementCopy); - element = elementCopy; - // Copy cached JIDs (for performance reasons) - toJID = presence.toJID; - fromJID = presence.fromJID; - } - - /** - * Returns true if the presence type is "available". This is a convenience - * method that is equivalent to: - * - *

-	 * getType() == null
-	 * 
- * - */ - public boolean isAvailable() { - return getType() == null; + public Presence(final Presence.Type type, final JID from, final JID to) { + this(type); + setFrom(from); + setTo(to); } /** @@ -118,10 +67,12 @@ public boolean isAvailable() { * @see Type */ public Type getType() { - final String type = element.attributeValue("type"); - if (type != null) - return Type.valueOf(type); - return null; + final String type = element.getAttribute("type"); + + if (type == null) + return Type.available; + + return Type.valueOf(type); } /** @@ -132,7 +83,11 @@ public Type getType() { * @see Type */ public void setType(final Type type) { - element.addAttribute("type", type == null ? null : type.toString()); + if (type == null || type == Type.available) { + element.removeAttribute("type"); + } else { + element.setAttribute("type", type.toString()); + } } /** @@ -251,71 +206,6 @@ public void setPriority(final int priority) { priorityElement.setText(Integer.toString(priority)); } - /** - * Returns the first child element of this packet that matches the given - * name and namespace. If no matching element is found, null will - * be returned. This is a convenience method to avoid manipulating this - * underlying packet's Element instance directly. - *

- * - * Child elements in extended namespaces are used to extend the features of - * XMPP. Examples include a "user is typing" indicator and invitations to - * group chat rooms. Although any valid XML can be included in a child - * element in an extended namespace, many common features have been - * standardized as XMPP Extension - * Protocols (XEPs). - * - * @param name - * the element name. - * @param namespace - * the element namespace. - * @return the first matching child element, or null if there is no - * matching child element. - */ - @SuppressWarnings("unchecked") - public Element getChildElement(final String name, final String namespace) { - for (final Iterator i = element.elementIterator(name); i.hasNext();) { - final Element element = i.next(); - if (element.getNamespaceURI().equals(namespace)) - return element; - } - return null; - } - - /** - * Adds a new child element to this packet with the given name and - * namespace. The newly created Element is returned. This is a convenience - * method to avoid manipulating this underlying packet's Element instance - * directly. - *

- * - * Child elements in extended namespaces are used to extend the features of - * XMPP. Examples include a "user is typing" indicator and invitations to - * group chat rooms. Although any valid XML can be included in a child - * element in an extended namespace, many common features have been - * standardized as XMPP Extension - * Protocols (XEPs). - * - * @param name - * the element name. - * @param namespace - * the element namespace. - * @return the newly created child element. - */ - public Element addChildElement(final String name, final String namespace) { - return element.addElement(name, namespace); - } - - /** - * Returns a deep copy of this Presence. - * - * @return a deep copy of this Presence. - */ - @Override - public Presence createCopy() { - return new Presence(this); - } - /** * Represents the type of a presence packet. Note: the presence is assumed * to be "available" when the type attribute of the packet is null. @@ -342,6 +232,11 @@ public Presence createCopy() { */ public enum Type { + /** + * The sender is available. + */ + available, + /** * Typically short text message used in line-by-line chat interfaces. */ diff --git a/src/main/java/org/xmpp/packet/Roster.java b/src/main/java/org/xmpp/packet/Roster.java index d566c05..6556846 100644 --- a/src/main/java/org/xmpp/packet/Roster.java +++ b/src/main/java/org/xmpp/packet/Roster.java @@ -23,9 +23,7 @@ import net.jcip.annotations.NotThreadSafe; -import org.dom4j.Element; -import org.dom4j.Namespace; -import org.dom4j.QName; +import org.w3c.dom.Element; /** * Roster packet. The roster is a list of JIDs (typically other users) that the @@ -44,7 +42,7 @@ public class Roster extends IQ { */ public Roster() { super(); - element.addElement("query", "jabber:iq:roster"); + setIQChildElement("query", "jabber:iq:roster"); } /** @@ -56,7 +54,7 @@ public Roster() { */ public Roster(final Type type) { super(type); - element.addElement("query", "jabber:iq:roster"); + addChildElement(element, "query", "jabber:iq:roster"); } /** @@ -72,19 +70,6 @@ public Roster(final Type type, final String ID) { element.addElement("query", "jabber:iq:roster"); } - /** - * Constructs a new Roster that is a copy of an existing Roster. - * - * @param roster - * the roster packet. - * @see #createCopy() - */ - private Roster(final Roster roster) { - final Element elementCopy = roster.element.createCopy(); - docFactory.createDocument().add(elementCopy); - element = elementCopy; - } - /** * Constructs a new Roster using an existing Element. This is useful for * parsing incoming roster Elements into Roster objects. @@ -261,16 +246,6 @@ public Collection getItems() { return Collections.unmodifiableCollection(items); } - /** - * Returns a deep copy of this Roster. - * - * @return a deep copy of this Roster. - */ - @Override - public Roster createCopy() { - return new Roster(this); - } - /** * Item in a roster, which represents an individual contact. Each contact * has a JID, an optional nickname, a subscription type, and can belong to diff --git a/src/main/java/org/xmpp/packet/StreamError.java b/src/main/java/org/xmpp/packet/StreamError.java index b8084a6..d29f515 100644 --- a/src/main/java/org/xmpp/packet/StreamError.java +++ b/src/main/java/org/xmpp/packet/StreamError.java @@ -16,16 +16,12 @@ package org.xmpp.packet; -import java.io.StringWriter; import java.util.Iterator; import net.jcip.annotations.NotThreadSafe; -import org.dom4j.DocumentFactory; -import org.dom4j.Element; -import org.dom4j.QName; -import org.dom4j.io.OutputFormat; -import org.dom4j.io.XMLWriter; +import org.w3c.dom.Element; +import org.xmpp.util.BaseXML; /** * A stream error. Stream errors have a condition and they can optionally @@ -34,14 +30,10 @@ * @author Matt Tucker */ @NotThreadSafe -public class StreamError { +public class StreamError extends BaseXML { private static final String ERROR_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-streams"; - private static DocumentFactory docFactory = DocumentFactory.getInstance(); - - private final Element element; - /** * Construcs a new StreamError with the specified condition. * @@ -49,7 +41,7 @@ public class StreamError { * the error condition. */ public StreamError(final Condition condition) { - element = docFactory.createElement(docFactory.createQName("error", "stream", "http://etherx.jabber.org/streams")); + super("error:stream", "http://etherx.jabber.org/streams"); setCondition(condition); } @@ -62,8 +54,7 @@ public StreamError(final Condition condition) { * the text description of the error. */ public StreamError(final Condition condition, final String text) { - element = docFactory.createElement(docFactory.createQName("error", "stream", "http://etherx.jabber.org/streams")); - setCondition(condition); + this(condition); setText(text, null); } @@ -78,8 +69,7 @@ public StreamError(final Condition condition, final String text) { * the language code of the error description (e.g. "en"). */ public StreamError(final Condition condition, final String text, final String language) { - element = docFactory.createElement(docFactory.createQName("error", "stream", "http://etherx.jabber.org/streams")); - setCondition(condition); + this(condition); setText(text, language); } @@ -91,7 +81,7 @@ public StreamError(final Condition condition, final String text, final String la * the stream error Element. */ public StreamError(final Element element) { - this.element = element; + super(element); } /** @@ -143,7 +133,7 @@ public void setCondition(final Condition condition) { * @return the text description of the error. */ public String getText() { - return element.elementText("text"); + return getChildElementText(element, "text"); } /** @@ -199,38 +189,6 @@ public String getTextLanguage() { return null; } - /** - * Returns the DOM4J Element that backs the error. The element is the - * definitive representation of the error and can be manipulated directly to - * change error contents. - * - * @return the DOM4J Element. - */ - public Element getElement() { - return element; - } - - /** - * Returns the textual XML representation of this stream error. - * - * @return the textual XML representation of this stream error. - */ - public String toXML() { - return element.asXML(); - } - - @Override - public String toString() { - final StringWriter out = new StringWriter(); - final XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint()); - try { - writer.write(element); - } catch (final Exception e) { - e.printStackTrace(); - } - return out.toString(); - } - /** * Type-safe enumeration for the error condition. *

diff --git a/src/main/java/org/xmpp/resultsetmanagement/ResultSet.java b/src/main/java/org/xmpp/resultsetmanagement/ResultSet.java index 33fd58a..e0b4cdf 100644 --- a/src/main/java/org/xmpp/resultsetmanagement/ResultSet.java +++ b/src/main/java/org/xmpp/resultsetmanagement/ResultSet.java @@ -23,9 +23,7 @@ import java.util.Iterator; import java.util.List; -import org.dom4j.DocumentHelper; -import org.dom4j.Element; -import org.dom4j.QName; +import org.w3c.dom.Element; /** * A result set representation as described in XEP-0059. A result set is a diff --git a/src/main/java/org/xmpp/util/BaseXML.java b/src/main/java/org/xmpp/util/BaseXML.java new file mode 100644 index 0000000..ca4d665 --- /dev/null +++ b/src/main/java/org/xmpp/util/BaseXML.java @@ -0,0 +1,270 @@ +package org.xmpp.util; + +import java.io.StringWriter; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public abstract class BaseXML implements Cloneable { + + private static final DocumentBuilder docBuilder; + + static { + final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); + + docBuilderFactory.setIgnoringElementContentWhitespace(true); + docBuilderFactory.setIgnoringComments(true); + + try { + docBuilder = docBuilderFactory.newDocumentBuilder(); + } catch (final ParserConfigurationException e) { + throw new InternalError("Error creating Document Builder"); + } + } + + protected final Document document; + protected final Element element; + + /** + * Constructs a new Packet with no element data. This method is used by + * extensions of this class that require a more optimized path for creating + * new packets. + */ + protected BaseXML(final String elementName) { + this(elementName, XMLConstants.NULL_NS_URI); + } + + /** + * Constructs a new Packet with no element data. This method is used by + * extensions of this class that require a more optimized path for creating + * new packets. + */ + protected BaseXML(final String qualifiedName, final String namespaceURI) { + document = docBuilder.newDocument(); + element = document.createElementNS(namespaceURI, qualifiedName); + } + + /** + * Constructs a new Packet. The TO address contained in the XML Element will + * only be validated. In other words, stringprep operations will only be + * performed on the TO JID to verify that it is well-formed. The FROM + * address is assigned by the server so there is no need to verify it. + * + * @param element + * the XML Element that contains the packet contents. + */ + protected BaseXML(final Element element) { + document = docBuilder.newDocument(); + this.element = (Element) document.importNode(element, true); + } + + /** + * Returns the first child element of this packet that matches the given + * name and namespace. If no matching element is found, null will + * be returned. This is a convenience method to avoid manipulating this + * underlying packet's Element instance directly. + *

+ * + * Child elements in extended namespaces are used to extend the features of + * XMPP. Examples include a "user is typing" indicator and invitations to + * group chat rooms. Although any valid XML can be included in a child + * element in an extended namespace, many common features have been + * standardized as XMPP Extension + * Protocols (XEPs). + * + * @param name + * the element name. + * @return the first matching child element, or null if there is no + * matching child element. + */ + protected final static Element getChildElement(final Element element, final String name) { + return getChildElement(element, name, XMLConstants.NULL_NS_URI); + } + + /** + * Returns the first child element of this packet that matches the given + * name and namespace. If no matching element is found, null will + * be returned. This is a convenience method to avoid manipulating this + * underlying packet's Element instance directly. + *

+ * + * Child elements in extended namespaces are used to extend the features of + * XMPP. Examples include a "user is typing" indicator and invitations to + * group chat rooms. Although any valid XML can be included in a child + * element in an extended namespace, many common features have been + * standardized as XMPP Extension + * Protocols (XEPs). + * + * @param name + * the element name. + * @param namespace + * the element namespace. + * @return the first matching child element, or null if there is no + * matching child element. + */ + protected final static Element getChildElement(final Element element, final String localName, final String namespaceURI) { + return (Element) element.getElementsByTagNameNS(namespaceURI, localName).item(0); + } + + /** + * Adds a new child element to this packet with the given name and + * namespace. The newly created Element is returned. This is a convenience + * method to avoid manipulating this underlying packet's Element instance + * directly. + *

+ * + * Child elements in extended namespaces are used to extend the features of + * XMPP. Examples include a "user is typing" indicator and invitations to + * group chat rooms. Although any valid XML can be included in a child + * element in an extended namespace, many common features have been + * standardized as XMPP Extension + * Protocols (XEPs). + * + * @param name + * the element name. + * @return the newly created child element. + */ + protected final static Element addChildElement(final Element element, final String tagName) { + return addChildElement(element, tagName, XMLConstants.NULL_NS_URI); + } + + /** + * Adds a new child element to this packet with the given name and + * namespace. The newly created Element is returned. This is a convenience + * method to avoid manipulating this underlying packet's Element instance + * directly. + *

+ * + * Child elements in extended namespaces are used to extend the features of + * XMPP. Examples include a "user is typing" indicator and invitations to + * group chat rooms. Although any valid XML can be included in a child + * element in an extended namespace, many common features have been + * standardized as XMPP Extension + * Protocols (XEPs). + * + * @param name + * the element name. + * @param namespace + * the element namespace. + * @return the newly created child element. + */ + protected final static Element addChildElement(final Element element, final String qualifiedName, final String namespaceURI) { + final Element newElement = element.getOwnerDocument().createElementNS(namespaceURI, qualifiedName); + element.appendChild(newElement); + return newElement; + } + + protected final static String getChildElementText(final Element element, final String name) { + return getChildElementText(element, name, XMLConstants.NULL_NS_URI); + } + + protected final static String getChildElementText(final Element element, final String qualifiedName, final String namespaceURI) { + final Element childElement = getChildElement(element, qualifiedName, namespaceURI); + + return childElement != null ? childElement.getTextContent() : null; + } + + protected final static void setChildElementText(final Element element, final String name, final String text) { + setChildElementText(element, name, XMLConstants.NULL_NS_URI, text); + } + + protected final static void setChildElementText(final Element element, final String qualifiedName, final String namespaceURI, final String text) { + Element childElement = getChildElement(element, qualifiedName, namespaceURI); + + // If text is null, remove the element. + if (text == null && childElement != null) { + element.removeChild(childElement); + return; + } + + // Do nothing if the new text is null + if (text == null) + return; + + if (childElement == null) { + childElement = addChildElement(element, "body"); + } + childElement.setTextContent(text); + } + + protected final static String getChildElementLang(final Element element, final String name) { + return getChildElementLang(element, name, XMLConstants.NULL_NS_URI); + } + + protected final static String getChildElementLang(final Element element, final String qualifiedName, final String namespaceURI) { + final Element childElement = getChildElement(element, qualifiedName, namespaceURI); + + return getAttribute(childElement, "xml:lang", XMLConstants.XML_NS_URI); + } + + protected final static void setChildElementLang(final Element element, final String name, final String language) { + setChildElementLang(element, name, XMLConstants.NULL_NS_URI, language); + } + + protected final static void setChildElementLang(final Element element, final String qualifiedName, final String namespaceURI, final String language) { + final Element childElement = getChildElement(element, qualifiedName, namespaceURI); + + setAttribute(childElement, "xml:lang", XMLConstants.XML_NS_URI, language); + } + + protected final static String getAttribute(final Element element, final String name) { + return getAttribute(element, name, XMLConstants.NULL_NS_URI); + } + + protected final static String getAttribute(final Element element, final String qualifiedName, final String namespaceURI) { + final String value = element.getAttributeNS(namespaceURI, qualifiedName); + + return !value.isEmpty() ? value : null; + } + + protected final static void setAttribute(final Element element, final String name, final String value) { + setAttribute(element, name, XMLConstants.NULL_NS_URI, value); + } + + protected final static void setAttribute(final Element element, final String qualifiedName, final String namespaceURI, final String value) { + if (value != null) { + element.setAttributeNS(namespaceURI, qualifiedName, value); + } else { + element.removeAttributeNS(namespaceURI, qualifiedName); + } + } + + /** + * Returns the DOM4J Element that backs the packet. The element is the + * definitive representation of the packet and can be manipulated directly + * to change packet contents. + * + * @return the DOM4J Element that represents the packet. + */ + public final Element getElement() { + return element; + } + + @Override + public final String toString() { + try { + final Transformer trans = TransformerFactory.newInstance().newTransformer(); + trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + trans.setOutputProperty(OutputKeys.INDENT, "no"); + + final StringWriter buffer = new StringWriter(); + trans.transform(new DOMSource(element), new StreamResult(buffer)); + + return buffer.toString(); + } catch (final TransformerException e) { + throw new InternalError("Transformer error"); + } + } + +} diff --git a/src/test/java/org/xmpp/component/AbstractComponentIsConsumerTest.java b/src/test/java/org/xmpp/component/AbstractComponentIsConsumerTest.java index 61962f4..8df7efb 100644 --- a/src/test/java/org/xmpp/component/AbstractComponentIsConsumerTest.java +++ b/src/test/java/org/xmpp/component/AbstractComponentIsConsumerTest.java @@ -19,10 +19,10 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import org.dom4j.Element; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.w3c.dom.Element; import org.xmpp.packet.IQ; import org.xmpp.packet.IQ.Type; @@ -74,15 +74,15 @@ public void consumesOnDifferentThreadTest() throws Exception { // setup final String producerThreadName = Thread.currentThread().getName(); final IQ request = new IQ(Type.get); - request.setChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_THREADNAME, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); + request.setIQChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_THREADNAME, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); // do magic debugComp.processPacket(request); final IQ response = (IQ) debugComp.getSentPacket(); // verify - final Element elem = response.getChildElement(); - final String consumerThreadName = elem.getText(); + final Element elem = response.getIQChildElement(); + final String consumerThreadName = elem.getTextContent(); assertFalse(consumerThreadName.equals(producerThreadName)); } @@ -99,7 +99,7 @@ public void consumesOnDifferentThreadTest() throws Exception { public void consumesAsynchronouslyTest() throws Exception { // setup final IQ request = new IQ(Type.get); - request.setChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); + request.setIQChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); // do magic final long start = System.currentTimeMillis(); diff --git a/src/test/java/org/xmpp/component/AbstractComponentRespondsToIQRequestsTest.java b/src/test/java/org/xmpp/component/AbstractComponentRespondsToIQRequestsTest.java index 91f8099..ec1e16e 100644 --- a/src/test/java/org/xmpp/component/AbstractComponentRespondsToIQRequestsTest.java +++ b/src/test/java/org/xmpp/component/AbstractComponentRespondsToIQRequestsTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import org.xmpp.packet.IQ; import org.xmpp.packet.IQ.Type; +import org.xmpp.packet.JID; /** * The XMPP specification states that every IQ request should be responded to. @@ -45,9 +46,9 @@ public void testSimpleResponse() throws Exception { // setup final DummyAbstractComponent component = new DummyAbstractComponent(); final IQ pingRequest = new IQ(Type.get); - pingRequest.setChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); - pingRequest.setFrom("from.address"); - pingRequest.setTo("to.address"); + pingRequest.setIQChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); + pingRequest.setFrom(new JID("from.address")); + pingRequest.setTo(new JID("to.address")); // do magic component.start(); @@ -71,9 +72,9 @@ public void testNoImplementation() throws Exception { // setup final DummyAbstractComponent component = new DummyAbstractComponent(); final IQ request = new IQ(Type.set); - request.setChildElement("junit", "test"); - request.setFrom("from.address"); - request.setTo("to.address"); + request.setIQChildElement("junit", "test"); + request.setFrom(new JID("from.address")); + request.setTo(new JID("to.address")); // do magic component.start(); @@ -100,9 +101,9 @@ public void testExceptionResponse() throws Exception { // setup final DummyAbstractComponent component = new ThrowExceptionOnGetComponent(); final IQ request = new IQ(Type.get); - request.setChildElement("junit", "test"); - request.setFrom("from.address"); - request.setTo("to.address"); + request.setIQChildElement("junit", "test"); + request.setFrom(new JID("from.address")); + request.setTo(new JID("to.address")); // do magic component.start(); @@ -154,9 +155,9 @@ public void testResponseAfterShutdown() throws Exception { // setup final DummyAbstractComponent component = new SlowRespondingThreadNameComponent(); final IQ request = new IQ(); - request.setChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); - request.setFrom("from.address"); - request.setTo("to.address"); + request.setIQChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); + request.setFrom(new JID("from.address")); + request.setTo(new JID("to.address")); // do magic component.start(); @@ -186,9 +187,9 @@ public void testResponseAfterShutdownWhileProcessing() throws Exception { // setup final DummyAbstractComponent component = new SlowRespondingThreadNameComponent(); final IQ request = new IQ(); - request.setChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); - request.setFrom("from.address"); - request.setTo("to.address"); + request.setIQChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); + request.setFrom(new JID("from.address")); + request.setTo(new JID("to.address")); // do magic component.start(); @@ -223,9 +224,9 @@ public void testResponseWhenFlooded() throws Exception { // that we can queue enough requests to cause an overflow of the queue. for (int i = 1; i <= 17; i++) { final IQ request = new IQ(); - request.setChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); - request.setFrom("from.address"); - request.setTo("to.address"); + request.setIQChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_SLOWRESPONSE, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); + request.setFrom(new JID("from.address")); + request.setTo(new JID("to.address")); request.setID("slow" + i); requests.add(request); } @@ -234,9 +235,9 @@ public void testResponseWhenFlooded() throws Exception { // overflow. for (int i = 1; i <= 1001; i++) { final IQ request = new IQ(); - request.setChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_THREADNAME, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); - request.setFrom("from.address"); - request.setTo("to.address"); + request.setIQChildElement(SlowRespondingThreadNameComponent.ELEMENTNAME_THREADNAME, SlowRespondingThreadNameComponent.DEBUG_NAMESPACE); + request.setFrom(new JID("from.address")); + request.setTo(new JID("to.address")); request.setID("thread" + i); requests.add(request); } diff --git a/src/test/java/org/xmpp/component/AbstractComponentServiceDiscovery.java b/src/test/java/org/xmpp/component/AbstractComponentServiceDiscovery.java index e8997e4..d098d49 100644 --- a/src/test/java/org/xmpp/component/AbstractComponentServiceDiscovery.java +++ b/src/test/java/org/xmpp/component/AbstractComponentServiceDiscovery.java @@ -21,11 +21,10 @@ import java.util.Iterator; -import org.dom4j.Attribute; -import org.dom4j.Element; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.w3c.dom.Element; import org.xmpp.packet.IQ; import org.xmpp.packet.IQ.Type; @@ -55,7 +54,7 @@ public class AbstractComponentServiceDiscovery { public void setUp() throws Exception { final IQ request = new IQ(); request.setType(Type.get); - request.setChildElement("query", DISCOINFONS); + request.setIQChildElement("query", DISCOINFONS); comp.start(); comp.processPacket(request); response = (IQ) comp.getSentPacket(); @@ -78,17 +77,15 @@ public void testIdentityHasNameAndCategory() throws Exception { assertNotNull(response); assertTrue(response.isResponse()); - final Element childElement = response.getChildElement(); + final Element childElement = response.getIQChildElement(); final Iterator iter = childElement.elementIterator("identity"); while (iter.hasNext()) { final Element element = iter.next(); - final Attribute category = element.attribute("category"); - if (category == null || category.getValue() != "component") { + if (element.getAttribute("category") != "component") { continue; } - final Attribute name = element.attribute("name"); - if (name != null && name.getValue() == comp.getName()) + if (element.getAttribute("name") == comp.getName()) // succes! return; } @@ -106,12 +103,11 @@ public void testHasDiscoInfoFeature() throws Exception { assertNotNull(response); assertTrue(response.isResponse()); - final Element childElement = response.getChildElement(); + final Element childElement = response.getIQChildElement(); final Iterator iter = childElement.elementIterator("feature"); while (iter.hasNext()) { final Element element = iter.next(); - final Attribute attr = element.attribute("var"); - if (attr != null && attr.getValue() == DISCOINFONS) + if (element.getAttribute("var") == DISCOINFONS) // succes! return; } @@ -129,12 +125,11 @@ public void testHasPingFeature() throws Exception { assertNotNull(response); assertTrue(response.isResponse()); - final Element childElement = response.getChildElement(); + final Element childElement = response.getIQChildElement(); final Iterator iter = childElement.elementIterator("feature"); while (iter.hasNext()) { final Element element = iter.next(); - final Attribute attr = element.attribute("var"); - if (attr != null && attr.getValue() == "urn:xmpp:ping") + if (element.getAttribute("var") == "urn:xmpp:ping") // succes! return; } @@ -152,12 +147,11 @@ public void testHasLastActivityFeature() throws Exception { assertNotNull(response); assertTrue(response.isResponse()); - final Element childElement = response.getChildElement(); + final Element childElement = response.getIQChildElement(); final Iterator iter = childElement.elementIterator("feature"); while (iter.hasNext()) { final Element element = iter.next(); - final Attribute attr = element.attribute("var"); - if (attr != null && attr.getValue() == "jabber:iq:last") + if (element.getAttribute("var") == "jabber:iq:last") // succes! return; } @@ -175,12 +169,11 @@ public void testHasEntityTimeFeature() throws Exception { assertNotNull(response); assertTrue(response.isResponse()); - final Element childElement = response.getChildElement(); + final Element childElement = response.getIQChildElement(); final Iterator iter = childElement.elementIterator("feature"); while (iter.hasNext()) { final Element element = iter.next(); - final Attribute attr = element.attribute("var"); - if (attr != null && attr.getValue() == "urn:xmpp:time") + if (element.getAttribute("var") == "urn:xmpp:time") // succes! return; } @@ -211,7 +204,7 @@ protected String[] discoInfoFeatureNamespaces() { // do magic final IQ request = new IQ(); request.setType(Type.get); - request.setChildElement("query", DISCOINFONS); + request.setIQChildElement("query", DISCOINFONS); component.start(); component.processPacket(request); response = (IQ) component.getSentPacket(); @@ -220,14 +213,13 @@ protected String[] discoInfoFeatureNamespaces() { boolean has1 = false; boolean has2 = false; - final Element childElement = response.getChildElement(); + final Element childElement = response.getIQChildElement(); final Iterator iter = childElement.elementIterator("feature"); while (iter.hasNext()) { final Element element = iter.next(); - final Attribute attr = element.attribute("var"); - if (attr != null && attr.getValue() == ns1) { + if (element.getAttribute("var") == ns1) { has1 = true; - } else if (attr != null && attr.getValue() == ns2) { + } else if (element.getAttribute("var") == ns2) { has2 = true; } } diff --git a/src/test/java/org/xmpp/component/AbstractComponentTest.java b/src/test/java/org/xmpp/component/AbstractComponentTest.java index 1db9a2f..614f490 100644 --- a/src/test/java/org/xmpp/component/AbstractComponentTest.java +++ b/src/test/java/org/xmpp/component/AbstractComponentTest.java @@ -49,8 +49,8 @@ public void testXmppPing() throws Exception { component.initialize(new JID("sub.domain"), null); final IQ pingRequest = new IQ(Type.get); - pingRequest.setChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); - pingRequest.setFrom("from.address"); + pingRequest.setIQChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); + pingRequest.setFrom(new JID("from.address")); pingRequest.setTo(component.jid); // do magic @@ -83,8 +83,8 @@ public void testIsRestartable() throws Exception { component.initialize(new JID("sub.domain"), null); final IQ pingRequest = new IQ(Type.get); - pingRequest.setChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); - pingRequest.setFrom("from.address"); + pingRequest.setIQChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); + pingRequest.setFrom(new JID("from.address")); pingRequest.setTo(component.jid); // do magic @@ -120,8 +120,8 @@ public boolean servesLocalUsersOnly() { component.initialize(new JID("sub.domain"), null); final IQ pingRequest = new IQ(Type.get); - pingRequest.setChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); - pingRequest.setFrom("user@notthesame" + component.getDomain()); + pingRequest.setIQChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); + pingRequest.setFrom(new JID("user@notthesame" + component.getDomain())); pingRequest.setTo(component.jid); // do magic @@ -155,8 +155,8 @@ public boolean servesLocalUsersOnly() { component.initialize(new JID("sub.domain"), null); final IQ pingRequest = new IQ(Type.get); - pingRequest.setChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); - pingRequest.setFrom("user@" + component.getDomain()); + pingRequest.setIQChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); + pingRequest.setFrom(new JID("user@" + component.getDomain())); pingRequest.setTo(component.jid); // do magic @@ -190,8 +190,8 @@ public boolean servesLocalUsersOnly() { component.initialize(new JID("sub.domain"), null); final IQ pingRequest = new IQ(Type.get); - pingRequest.setChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); - pingRequest.setFrom("user@notthesame" + component.getDomain()); + pingRequest.setIQChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); + pingRequest.setFrom(new JID("user@notthesame" + component.getDomain())); pingRequest.setTo(component.jid); // do magic @@ -225,8 +225,8 @@ public boolean servesLocalUsersOnly() { component.initialize(new JID("sub.domain"), null); final IQ pingRequest = new IQ(Type.get); - pingRequest.setChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); - pingRequest.setFrom("user@" + component.getDomain()); + pingRequest.setIQChildElement("ping", AbstractComponent.NAMESPACE_XMPP_PING); + pingRequest.setFrom(new JID("user@" + component.getDomain())); pingRequest.setTo(component.jid); // do magic @@ -327,8 +327,8 @@ public void testLastActivity() throws Exception { component.initialize(new JID("sub.domain"), null); final IQ request = new IQ(Type.get); - request.setChildElement("ping", AbstractComponent.NAMESPACE_LAST_ACTIVITY); - request.setFrom("from.address"); + request.setIQChildElement("ping", AbstractComponent.NAMESPACE_LAST_ACTIVITY); + request.setFrom(new JID("from.address")); request.setTo(component.jid); final int wait = 2; @@ -341,7 +341,7 @@ public void testLastActivity() throws Exception { final IQ result = (IQ) component.getSentPacket(); assertNotNull(result); assertEquals(Type.result, result.getType()); - assertEquals(String.valueOf(wait), result.getChildElement().attributeValue("seconds")); + assertEquals(String.valueOf(wait), result.getIQChildElement().getAttribute("seconds")); } /** @@ -358,8 +358,8 @@ public void testLastActivityAfterRestart() throws Exception { component.initialize(new JID("sub.domain"), null); final IQ request = new IQ(Type.get); - request.setChildElement("ping", AbstractComponent.NAMESPACE_LAST_ACTIVITY); - request.setFrom("from.address"); + request.setIQChildElement("ping", AbstractComponent.NAMESPACE_LAST_ACTIVITY); + request.setFrom(new JID("from.address")); request.setTo(component.jid); final int wait = 2; @@ -375,7 +375,7 @@ public void testLastActivityAfterRestart() throws Exception { final IQ result = (IQ) component.getSentPacket(); assertNotNull(result); assertEquals(Type.result, result.getType()); - assertEquals(String.valueOf(wait), result.getChildElement().attributeValue("seconds")); + assertEquals(String.valueOf(wait), result.getIQChildElement().getAttribute("seconds")); } /** @@ -389,8 +389,8 @@ public void testEntityTime() throws Exception { component.initialize(new JID("sub.domain"), null); final IQ request = new IQ(Type.get); - request.setChildElement("ping", AbstractComponent.NAMESPACE_ENTITY_TIME); - request.setFrom("from.address"); + request.setIQChildElement("ping", AbstractComponent.NAMESPACE_ENTITY_TIME); + request.setFrom(new JID("from.address")); request.setTo(component.jid); // do magic diff --git a/src/test/java/org/xmpp/component/SlowRespondingThreadNameComponent.java b/src/test/java/org/xmpp/component/SlowRespondingThreadNameComponent.java index dfd8a06..2252bf6 100644 --- a/src/test/java/org/xmpp/component/SlowRespondingThreadNameComponent.java +++ b/src/test/java/org/xmpp/component/SlowRespondingThreadNameComponent.java @@ -16,7 +16,7 @@ package org.xmpp.component; -import org.dom4j.Element; +import org.w3c.dom.Element; import org.xmpp.packet.IQ; /** @@ -66,28 +66,28 @@ public class SlowRespondingThreadNameComponent extends DummyAbstractComponent { */ @Override protected IQ handleIQGet(final IQ request) throws Exception { - final Element element = request.getChildElement(); + final Element element = request.getIQChildElement(); if (!DEBUG_NAMESPACE.equals(element.getNamespaceURI())) { - log.debug("Can not process {}", request.toXML()); + log.debug("Can not process {}", request.toString()); return null; } - if (ELEMENTNAME_SLOWRESPONSE.equals(element.getName())) { - log.debug("Waiting 4000 millis before responding to: {}", request.toXML()); + if (ELEMENTNAME_SLOWRESPONSE.equals(element.getTagName())) { + log.debug("Waiting 4000 millis before responding to: {}", request.toString()); Thread.sleep(4000); - log.debug("Responding to {} now.", request.toXML()); + log.debug("Responding to {} now.", request.toString()); return IQ.createResultIQ(request); } - if (ELEMENTNAME_THREADNAME.equals(element.getName())) { + if (ELEMENTNAME_THREADNAME.equals(element.getTagName())) { final String threadName = Thread.currentThread().getName(); final IQ response = IQ.createResultIQ(request); - response.setChildElement(ELEMENTNAME_THREADNAME, DEBUG_NAMESPACE).addText(threadName); - log.debug("Responding to {} with {}", request.toXML(), response.toXML()); + response.setIQChildElement(ELEMENTNAME_THREADNAME, DEBUG_NAMESPACE).setTextContent(threadName); + log.debug("Responding to {} with {}", request.toString(), response.toString()); return response; } - log.debug("Cannot process {}", request.toXML()); + log.debug("Cannot process {}", request.toString()); return null; } } diff --git a/src/test/java/org/xmpp/forms/DataFormAddingFieldsTest.java b/src/test/java/org/xmpp/forms/DataFormAddingFieldsTest.java index ff60af9..aa5e51b 100644 --- a/src/test/java/org/xmpp/forms/DataFormAddingFieldsTest.java +++ b/src/test/java/org/xmpp/forms/DataFormAddingFieldsTest.java @@ -91,8 +91,8 @@ public void testValidBehaviorAddingComplexField() throws Exception { field = form.getField(var); if (field == null || !var.equals(field.getVariable())) { fail("Can't add a complete field into a Data Form."); - } - if (!LABEL.equals(field.getLabel()) || !FIELD_TYPE.equals(field.getType()) || field.getValues().size() != 1 || !VALUE.equals(field.getFirstValue())) { + } else if (!LABEL.equals(field.getLabel()) || !FIELD_TYPE.equals(field.getType()) || field.getValues().size() != 1 + || !VALUE.equals(field.getFirstValue())) { fail("Any paramameter wasn't applied correctly."); } } diff --git a/src/test/java/org/xmpp/forms/FormFieldGetSetTest.java b/src/test/java/org/xmpp/forms/FormFieldGetSetTest.java index d3fde7b..8c55055 100644 --- a/src/test/java/org/xmpp/forms/FormFieldGetSetTest.java +++ b/src/test/java/org/xmpp/forms/FormFieldGetSetTest.java @@ -23,10 +23,9 @@ import java.util.List; -import org.dom4j.DocumentFactory; -import org.dom4j.Element; import org.junit.Before; import org.junit.Test; +import org.w3c.dom.Element; import org.xmpp.forms.FormField.Option; import org.xmpp.forms.FormField.Type; diff --git a/src/test/java/org/xmpp/packet/PacketAddressingTest.java b/src/test/java/org/xmpp/packet/PacketAddressingTest.java index cfed31c..af2d0e0 100644 --- a/src/test/java/org/xmpp/packet/PacketAddressingTest.java +++ b/src/test/java/org/xmpp/packet/PacketAddressingTest.java @@ -18,8 +18,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import org.dom4j.DocumentFactory; -import org.dom4j.Element; import org.junit.Before; import org.junit.Test; @@ -41,14 +39,7 @@ public class PacketAddressingTest { */ @Before public void setUp() throws Exception { - final Element element = DocumentFactory.getInstance().createElement("packet"); - packet = new Packet(element) { - - @Override - public Packet createCopy() { - return null; - } - }; + packet = new Packet("packet"); } /** @@ -56,19 +47,9 @@ public Packet createCopy() { */ @Test public void testAllowNullToJID() throws Exception { - packet.setTo((JID) null); - assertNull(packet.getTo()); - assertNull(packet.getElement().attributeValue("to")); - } - - /** - * To and From addresses should be allowed to be null. - */ - @Test - public void testAllowNullToString() throws Exception { - packet.setTo((String) null); + packet.setTo(null); assertNull(packet.getTo()); - assertNull(packet.getElement().attributeValue("to")); + assertNull(packet.getElement().getAttribute("to")); } /** @@ -84,23 +65,7 @@ public void testAllowToJID() throws Exception { // verify assertEquals(jid, packet.getTo()); - assertEquals(jid.toFullJID(), packet.getElement().attributeValue("to")); - } - - /** - * Verifies that using a String value works as expected. - */ - @Test - public void testAllowToString() throws Exception { - // setup - final String string = "test@example.org/junit"; - - // do magic - packet.setTo(string); - - // verify - assertEquals(string, packet.getTo().toFullJID()); - assertEquals(string, packet.getElement().attributeValue("to")); + assertEquals(jid.toFullJID(), packet.getElement().getAttribute("to")); } /** @@ -118,24 +83,7 @@ public void testCanResetTo() throws Exception { // verify assertEquals(jid, packet.getTo()); - assertEquals(jid.toFullJID(), packet.getElement().attributeValue("to")); - } - - /** - * Verifies that setting an address twice causes the last value to be used. - */ - @Test - public void testCanResetNullStringTo() throws Exception { - // setup - final JID orig = new JID("test", "example.org", "junit"); - packet.setTo(orig); - - // do magic - packet.setTo((String) null); - - // verify - assertNull(packet.getTo()); - assertNull(packet.getElement().attributeValue("to")); + assertEquals(jid.toFullJID(), packet.getElement().getAttribute("to")); } /** @@ -152,7 +100,7 @@ public void testCanResetNullJIDTo() throws Exception { // verify assertNull(packet.getTo()); - assertNull(packet.getElement().attributeValue("to")); + assertNull(packet.getElement().getAttribute("to")); } /** @@ -162,17 +110,7 @@ public void testCanResetNullJIDTo() throws Exception { public void testAllowNullFromJID() throws Exception { packet.setFrom((JID) null); assertNull(packet.getFrom()); - assertNull(packet.getElement().attributeValue("from")); - } - - /** - * To and From addresses should be allowed to be null. - */ - @Test - public void testAllowNullFromString() throws Exception { - packet.setFrom((String) null); - assertNull(packet.getFrom()); - assertNull(packet.getElement().attributeValue("from")); + assertNull(packet.getElement().getAttribute("from")); } /** @@ -188,23 +126,7 @@ public void testAllowFromJID() throws Exception { // verify assertEquals(jid, packet.getFrom()); - assertEquals(jid.toFullJID(), packet.getElement().attributeValue("from")); - } - - /** - * Verifies that using a String value works as expected. - */ - @Test - public void testAllowFromString() throws Exception { - // setup - final String string = "test@example.org/junit"; - - // do magic - packet.setFrom(string); - - // verify - assertEquals(string, packet.getFrom().toFullJID()); - assertEquals(string, packet.getElement().attributeValue("from")); + assertEquals(jid.toFullJID(), packet.getElement().getAttribute("from")); } /** @@ -222,24 +144,7 @@ public void testCanResetFrom() throws Exception { // verify assertEquals(jid, packet.getFrom()); - assertEquals(jid.toFullJID(), packet.getElement().attributeValue("from")); - } - - /** - * Verifies that setting an address twice causes the last value to be used. - */ - @Test - public void testCanResetNullStringFrom() throws Exception { - // setup - final JID orig = new JID("test", "example.org", "junit"); - packet.setFrom(orig); - - // do magic - packet.setFrom((String) null); - - // verify - assertNull(packet.getFrom()); - assertNull(packet.getElement().attributeValue("from")); + assertEquals(jid.toFullJID(), packet.getElement().getAttribute("from")); } /** @@ -256,6 +161,6 @@ public void testCanResetNullJIDFrom() throws Exception { // verify assertNull(packet.getFrom()); - assertNull(packet.getElement().attributeValue("from")); + assertNull(packet.getElement().getAttribute("from")); } } From 54c0cf31f1a14f14c84fd7ab0a67c0da96702504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Mart=C3=ADnez?= Date: Fri, 3 Jun 2011 12:12:32 +0200 Subject: [PATCH 2/6] Remove dom4j dependency, use Java XML APIs instead (Part 2) --- .../org/xmpp/component/AbstractComponent.java | 2 +- src/main/java/org/xmpp/forms/DataForm.java | 7 +- src/main/java/org/xmpp/packet/IQ.java | 13 +- .../java/org/xmpp/packet/PacketError.java | 125 ++++++------------ src/main/java/org/xmpp/packet/Presence.java | 58 +++----- src/main/java/org/xmpp/packet/Roster.java | 108 ++++++--------- .../java/org/xmpp/packet/StreamError.java | 60 +++------ src/main/java/org/xmpp/util/BaseXML.java | 42 +++++- 8 files changed, 157 insertions(+), 258 deletions(-) diff --git a/src/main/java/org/xmpp/component/AbstractComponent.java b/src/main/java/org/xmpp/component/AbstractComponent.java index 33d3870..d80c686 100644 --- a/src/main/java/org/xmpp/component/AbstractComponent.java +++ b/src/main/java/org/xmpp/component/AbstractComponent.java @@ -77,7 +77,7 @@ * * @author Guus der Kinderen, guus.der.kinderen@gmail.com */ -//TODO define JCIP annotation +// TODO define JCIP annotation public abstract class AbstractComponent implements Component { /** * The object that's responsible for logging. diff --git a/src/main/java/org/xmpp/forms/DataForm.java b/src/main/java/org/xmpp/forms/DataForm.java index 59f477c..2bf7486 100644 --- a/src/main/java/org/xmpp/forms/DataForm.java +++ b/src/main/java/org/xmpp/forms/DataForm.java @@ -123,7 +123,7 @@ else if (object instanceof Date) public DataForm(final Type type) { super(ELEMENT_NAME, NAMESPACE); // Set the type of the data form - element.addAttribute("type", type.toString()); + element.setAttribute("type", type.toString()); } public DataForm(final Element element) { @@ -372,11 +372,6 @@ public void addItemFields(final Map fields) { } } - @Override - public DataForm createCopy() { - return new DataForm(getElement().createCopy()); - } - /** * Type-safe enumeration to represent the type of the Data forms. */ diff --git a/src/main/java/org/xmpp/packet/IQ.java b/src/main/java/org/xmpp/packet/IQ.java index 994cfac..3be530a 100644 --- a/src/main/java/org/xmpp/packet/IQ.java +++ b/src/main/java/org/xmpp/packet/IQ.java @@ -21,7 +21,6 @@ import net.jcip.annotations.NotThreadSafe; import org.w3c.dom.Element; -import org.w3c.dom.NodeList; /** * IQ (Info/Query) packet. IQ packets are used to get and set information on the @@ -144,16 +143,11 @@ public final boolean isResponse() { * @return the child element. */ public Element getIQChildElement() { - final NodeList elements = element.getChildNodes(); - if (elements.getLength() == 0) - return null; - // Search for a child element that is in a different namespace. - for (int i = 0; i < elements.getLength(); i++) { - final Element element = (Element) elements.item(i); - final String namespace = element.getNamespaceURI(); + for (final Element child : getChildElements(element)) { + final String namespace = child.getNamespaceURI(); if (namespace != null && !namespace.equals("jabber:client") && !namespace.equals("jabber:server")) - return element; + return child; } return null; } @@ -223,7 +217,6 @@ public Element setIQChildElement(final String qualifiedName, final String namesp final Element childElement = element.getOwnerDocument().createElementNS(namespaceURI, qualifiedName); setIQChildElement(childElement); return childElement; - } /** diff --git a/src/main/java/org/xmpp/packet/PacketError.java b/src/main/java/org/xmpp/packet/PacketError.java index 5588b01..f0e8f7a 100644 --- a/src/main/java/org/xmpp/packet/PacketError.java +++ b/src/main/java/org/xmpp/packet/PacketError.java @@ -16,11 +16,6 @@ package org.xmpp.packet; -import java.util.Iterator; - -import javax.xml.XMLConstants; -import javax.xml.namespace.QName; - import net.jcip.annotations.NotThreadSafe; import org.w3c.dom.Element; @@ -111,9 +106,9 @@ public PacketError(final Element element) { * @see Type */ public final Type getType() { - final String type = element.getAttribute("type"); + final String type = getAttribute(element, "type"); - return !type.isEmpty() ? Type.fromXMPP(type) : null; + return type != null ? Type.fromXMPP(type) : null; } /** @@ -124,11 +119,7 @@ public final Type getType() { * @see Type */ public final void setType(final Type type) { - if (type != null) { - element.setAttribute("type", type.toXMPP()); - } else { - element.removeAttribute("type"); - } + setAttribute(element, "type", type.toXMPP()); } /** @@ -137,24 +128,22 @@ public final void setType(final Type type) { * @return the error condition. * @see Condition */ - @SuppressWarnings("unchecked") public Condition getCondition() { - for (final Iterator i = element.elementIterator(); i.hasNext();) { - final Element el = i.next(); - if (el.getNamespaceURI().equals(ERROR_NAMESPACE) && !el.getName().equals("text")) - return Condition.fromXMPP(el.getName()); + for (final Element el : getChildElements(element)) { + if (ERROR_NAMESPACE.equals(el.getNamespaceURI()) && !el.getTagName().equals("text")) + return Condition.fromXMPP(el.getTagName()); } + // Looking for XMPP condition failed. See if a legacy error code exists, // which can be mapped into an XMPP error condition. - final String code = element.attributeValue("code"); - if (code != null) { - try { - return Condition.fromLegacyCode(Integer.parseInt(code)); - } catch (final Exception e) { - // Ignore -- unable to map legacy code into a valid condition - // so return null. - } + try { + final String code = getAttribute(element, "code"); + return Condition.fromLegacyCode(Integer.parseInt(code)); + } catch (final Exception e) { + // Ignore -- unable to map legacy code into a valid condition so + // return null. } + return null; } @@ -165,26 +154,21 @@ public Condition getCondition() { * the error condition. * @see Condition */ - @SuppressWarnings("unchecked") public void setCondition(final Condition condition) { if (condition == null) throw new NullPointerException("Condition cannot be null"); + // Set the error code for legacy support. - element.addAttribute("code", Integer.toString(condition.getLegacyCode())); + element.setAttribute("code", Integer.toString(condition.getLegacyCode())); - Element conditionElement = null; - for (final Iterator i = element.elementIterator(); i.hasNext();) { - final Element el = i.next(); - if (el.getNamespaceURI().equals(ERROR_NAMESPACE) && !el.getName().equals("text")) { - conditionElement = el; + // Delete current condition. + for (final Element el : getChildElements(element)) { + if (ERROR_NAMESPACE.equals(el.getNamespaceURI()) && !el.getTagName().equals("text")) { + element.removeChild(element); } } - if (conditionElement != null) { - element.remove(conditionElement); - } - conditionElement = docFactory.createElement(condition.toXMPP(), ERROR_NAMESPACE); - element.add(conditionElement); + addChildElement(element, condition.toXMPP(), ERROR_NAMESPACE); } /** @@ -194,7 +178,7 @@ public void setCondition(final Condition condition) { * @return the text description of the error. */ public String getText() { - return element.elementText("text"); + return getChildElementText(element, "text"); } /** @@ -204,7 +188,7 @@ public String getText() { * the text description of the error. */ public void setText(final String text) { - setText(text, null); + setChildElementText(element, "text", text); } /** @@ -218,24 +202,8 @@ public void setText(final String text) { * specify no language code. */ public void setText(final String text, final String lang) { - final Element textElement = (Element) element.getElementsByTagName("text").item(0); - // If text is null, clear the text. - if (text == null) { - if (textElement != null) { - element.removeChild(textElement); - } - return; - } - - if (textElement == null) { - textElement = docFactory.createElement("text", ERROR_NAMESPACE); - if (lang != null) { - textElement.addAttribute(QName.get("lang", "xml", "http://www.w3.org/XML/1998/namespace"), lang); - } - element.add(textElement); - } - - textElement.setTextContent(text); + setChildElementText(element, "text", text); + setChildElementLang(element, "text", lang); } /** @@ -245,9 +213,7 @@ public void setText(final String text, final String lang) { * @return the language code of the text description, if it exists. */ public String getTextLang() { - final Element textElement = (Element) element.getElementsByTagName("text").item(0); - - return textElement != null ? textElement.getAttributeNS(XMLConstants.XML_NS_URI, "xml:lang") : null; + return getChildElementLang(element, "text"); } /** @@ -270,34 +236,22 @@ public void setApplicationCondition(final String name) { * @param namespaceURI * the namespace of the application. */ - @SuppressWarnings("unchecked") - public void setApplicationCondition(final String name, String namespaceURI) { + public void setApplicationCondition(final String name, final String namespaceURI) { if (ERROR_NAMESPACE.equals(namespaceURI)) throw new IllegalArgumentException(); - Element applicationError = null; - for (final Iterator i = element.elementIterator(); i.hasNext();) { - - final Element el = i.next(); - if (!el.getNamespaceURI().equals(ERROR_NAMESPACE)) { - applicationError = el; + // Delete current condition. + for (final Element el : getChildElements(element)) { + if (!ERROR_NAMESPACE.equals(el.getNamespaceURI())) { + element.removeChild(element); } } - if (applicationError != null) { - element.remove(applicationError); - } - // If name is null, clear the application condition. if (name == null) return; - if (namespaceURI == null) { - // Set fallback namespace (see XEP-0182) - namespaceURI = "urn:xmpp:errors"; - } - applicationError = docFactory.createElement(name, namespaceURI); - element.add(applicationError); + addChildElement(element, name, namespaceURI != null ? namespaceURI : "urn:xmpp:errors"); } /** @@ -307,13 +261,12 @@ public void setApplicationCondition(final String name, String namespaceURI) { * @return the name of the application-specific error condition, if it * exists. */ - @SuppressWarnings("unchecked") public String getApplicationConditionName() { - for (final Iterator i = element.elementIterator(); i.hasNext();) { - final Element el = i.next(); - if (!el.getNamespaceURI().equals(ERROR_NAMESPACE)) - return el.getName(); + for (final Element el : getChildElements(element)) { + if (!ERROR_NAMESPACE.equals(el.getNamespaceURI())) + return el.getTagName(); } + return null; } @@ -324,11 +277,9 @@ public String getApplicationConditionName() { * @return the namespace of the application-specific error condition, if it * exists. */ - @SuppressWarnings("unchecked") public String getApplicationConditionNamespaceURI() { - for (final Iterator i = element.elementIterator(); i.hasNext();) { - final Element el = i.next(); - if (!el.getNamespaceURI().equals(ERROR_NAMESPACE)) + for (final Element el : getChildElements(element)) { + if (!ERROR_NAMESPACE.equals(el.getNamespaceURI())) return el.getNamespaceURI(); } return null; diff --git a/src/main/java/org/xmpp/packet/Presence.java b/src/main/java/org/xmpp/packet/Presence.java index 3b2805a..f69a4b0 100644 --- a/src/main/java/org/xmpp/packet/Presence.java +++ b/src/main/java/org/xmpp/packet/Presence.java @@ -58,6 +58,10 @@ public Presence(final Presence.Type type, final JID from, final JID to) { setTo(to); } + public Presence(final Element element) { + super(element); + } + /** * Returns the type of this presence. If the presence is "available", the * type will be null (in XMPP, no value for the type attribute is @@ -101,10 +105,9 @@ public void setType(final Type type) { * @see Show */ public Show getShow() { - final String show = element.elementText("show"); - if (show != null) - return Show.valueOf(show); - return null; + final String show = getChildElementText(element, "show"); + + return show != null ? Show.valueOf(show) : null; } /** @@ -120,20 +123,10 @@ public Show getShow() { * @see Show */ public void setShow(final Show show) { - Element showElement = element.element("show"); - // If show is null, clear the subject. - if (show == null) { - if (showElement != null) { - element.remove(showElement); - } - return; - } - if (showElement == null) { - if (!isAvailable()) - throw new IllegalArgumentException("Cannot set 'show' if 'type' attribute is set."); - showElement = element.addElement("show"); - } - showElement.setText(show.toString()); + if (getType() != Type.available) + throw new IllegalArgumentException("Cannot set 'show' if 'type' attribute is set."); + + setChildElementText(element, "show", show.toString()); } /** @@ -143,7 +136,7 @@ public void setShow(final Show show) { * @return the status. */ public String getStatus() { - return element.elementText("status"); + return getChildElementText(element, "status"); } /** @@ -154,19 +147,7 @@ public String getStatus() { * the status. */ public void setStatus(final String status) { - Element statusElement = element.element("status"); - // If subject is null, clear the subject. - if (status == null) { - if (statusElement != null) { - element.remove(statusElement); - } - return; - } - - if (statusElement == null) { - statusElement = element.addElement("status"); - } - statusElement.setText(status); + setChildElementText(element, "status", status); } /** @@ -177,12 +158,8 @@ public void setStatus(final String status) { * @return the priority. */ public int getPriority() { - final String priority = element.elementText("priority"); - if (priority == null) - return 0; - try { - return Integer.parseInt(priority); + return Integer.parseInt(getChildElementText(element, "priority")); } catch (final Exception e) { return 0; } @@ -199,11 +176,8 @@ public int getPriority() { public void setPriority(final int priority) { if (priority < -128 || priority > 128) throw new IllegalArgumentException("Priority value of " + priority + " is outside the valid range of -128 through 128"); - Element priorityElement = element.element("priority"); - if (priorityElement == null) { - priorityElement = element.addElement("priority"); - } - priorityElement.setText(Integer.toString(priority)); + + setChildElementText(element, "priority", Integer.toString(priority)); } /** diff --git a/src/main/java/org/xmpp/packet/Roster.java b/src/main/java/org/xmpp/packet/Roster.java index 6556846..32c325a 100644 --- a/src/main/java/org/xmpp/packet/Roster.java +++ b/src/main/java/org/xmpp/packet/Roster.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Iterator; import net.jcip.annotations.NotThreadSafe; @@ -54,7 +53,7 @@ public Roster() { */ public Roster(final Type type) { super(type); - addChildElement(element, "query", "jabber:iq:roster"); + setIQChildElement("query", "jabber:iq:roster"); } /** @@ -67,7 +66,7 @@ public Roster(final Type type) { */ public Roster(final Type type, final String ID) { super(type, ID); - element.addElement("query", "jabber:iq:roster"); + setIQChildElement("query", "jabber:iq:roster"); } /** @@ -81,32 +80,6 @@ public Roster(final Element element) { super(element); } - /** - * Adds a new item to the roster. The name and groups are set to - * null If the roster packet already contains an item using the - * same JID, the information in the existing item will be overwritten with - * the new information. - *

- * - * The XMPP specification recommends that if the roster item is associated - * with another instant messaging user (human), that the JID be in bare form - * (e.g. user@domain). Use the {@link JID#toBareJID() toBareJID()} method - * for a bare JID. - * - * @param jid - * the JID. - * @param subscription - * the subscription type. - * @return the newly created item. - */ - public Item addItem(final String jid, final Subscription subscription) { - if (getType() == IQ.Type.get || getType() == IQ.Type.error) - throw new IllegalStateException("IQ type must be 'result' or 'set'"); - if (jid == null) - throw new NullPointerException("JID cannot be null"); - return addItem(new JID(jid), null, null, subscription, null); - } - /** * Adds a new item to the roster. The name and groups are set to * null If the roster packet already contains an item using the @@ -156,40 +129,42 @@ public Item addItem(final JID jid, final Subscription subscription) { * a Collection of groups. * @return the newly created item. */ - @SuppressWarnings("unchecked") public Item addItem(final JID jid, final String name, final Ask ask, final Subscription subscription, final Collection groups) { if (jid == null) throw new NullPointerException("JID cannot be null"); if (subscription == null) throw new NullPointerException("Subscription cannot be null"); - Element query = element.element(new QName("query", Namespace.get("jabber:iq:roster"))); + + Element query = getIQChildElement(); if (query == null) { - query = element.addElement("query", "jabber:iq:roster"); + query = setIQChildElement("query", "jabber:iq:roster"); } + Element item = null; - for (final Iterator i = query.elementIterator("item"); i.hasNext();) { - final Element el = i.next(); - if (el.attributeValue("jid").equals(jid.toString())) { + for (final Element el : getChildElements(query, "item")) { + if (el.getAttribute("jid").equals(jid.toString())) { item = el; + break; } } if (item == null) { - item = query.addElement("item"); + item = addChildElement(query, "item"); } - item.addAttribute("jid", jid.toBareJID()); - item.addAttribute("name", name); + + item.setAttribute("jid", jid.toBareJID()); + item.setAttribute("name", name); if (ask != null) { - item.addAttribute("ask", ask.toString()); + item.setAttribute("ask", ask.toString()); } - item.addAttribute("subscription", subscription.toString()); + item.setAttribute("subscription", subscription.toString()); // Erase existing groups in case the item previously existed. - for (final Iterator i = item.elementIterator("group"); i.hasNext();) { - item.remove(i.next()); + for (final Element group : getChildElements(item, "group")) { + item.removeChild(group); } // Add in groups. if (groups != null) { for (final String group : groups) { - item.addElement("group").setText(group); + setChildElementText(item, "group", group); } } return new Item(jid, name, ask, subscription, groups); @@ -201,14 +176,12 @@ public Item addItem(final JID jid, final String name, final Ask ask, final Subsc * @param jid * the JID of the item to remove. */ - @SuppressWarnings("unchecked") public void removeItem(final JID jid) { - final Element query = element.element(new QName("query", Namespace.get("jabber:iq:roster"))); + final Element query = getIQChildElement(); if (query != null) { - for (final Iterator i = query.elementIterator("item"); i.hasNext();) { - final Element item = i.next(); - if (item.attributeValue("jid").equals(jid.toString())) { - query.remove(item); + for (final Element item : getChildElements(query, "item")) { + if (item.getAttribute("jid").equals(jid.toString())) { + query.removeChild(item); return; } } @@ -222,27 +195,28 @@ public void removeItem(final JID jid) { * @return an unmodifable copy of the {@link Item Items} in the roster * packet. */ - @SuppressWarnings("unchecked") public Collection getItems() { final Collection items = new ArrayList(); - final Element query = element.element(new QName("query", Namespace.get("jabber:iq:roster"))); + + final Element query = getIQChildElement(); if (query != null) { - for (final Iterator i = query.elementIterator("item"); i.hasNext();) { - final Element item = i.next(); - final String jid = item.attributeValue("jid"); - final String name = item.attributeValue("name"); - final String ask = item.attributeValue("ask"); - final String subscription = item.attributeValue("subscription"); + for (final Element item : getChildElements(query, "item")) { + final String jid = item.getAttribute("jid"); + final String name = item.getAttribute("name"); + final String ask = item.getAttribute("ask"); + final String subscription = item.getAttribute("subscription"); final Collection groups = new ArrayList(); - for (final Iterator j = item.elementIterator("group"); j.hasNext();) { - final Element group = j.next(); - groups.add(group.getText().trim()); + + for (final Element group : getChildElements(item, "group")) { + groups.add(group.getTextContent().trim()); } - final Ask askStatus = ask == null ? null : Ask.valueOf(ask); + + final Ask askStatus = ask != null ? Ask.valueOf(ask) : null; final Subscription subStatus = subscription == null ? null : Subscription.valueOf(subscription); items.add(new Item(new JID(jid), name, askStatus, subStatus, groups)); } } + return Collections.unmodifiableCollection(items); } @@ -288,7 +262,7 @@ private Item(final JID jid, final String name, final Ask ask, final Subscription * * @return the JID associated with this item. */ - public JID getJID() { + public final JID getJID() { return jid; } @@ -298,7 +272,7 @@ public JID getJID() { * * @return the nickname, or null if it doesn't exist. */ - public String getName() { + public final String getName() { return name; } @@ -307,7 +281,7 @@ public String getName() { * * @return the ask state of this item. */ - public Ask getAsk() { + public final Ask getAsk() { return ask; } @@ -316,7 +290,7 @@ public Ask getAsk() { * * @return the subscription state of this item. */ - public Subscription getSubscription() { + public final Subscription getSubscription() { return subscription; } @@ -326,14 +300,14 @@ public Subscription getSubscription() { * * @return the groups in this item. */ - public Collection getGroups() { + public final Collection getGroups() { if (groups == null) return Collections.emptyList(); return groups; } @Override - public String toString() { + public final String toString() { final StringBuffer buf = new StringBuffer(); buf.append(" i = element.elementIterator(); i.hasNext();) { - final Element el = i.next(); - if (el.getNamespaceURI().equals(ERROR_NAMESPACE) && !el.getName().equals("text")) - return Condition.fromXMPP(el.getName()); + for (final Element el : getChildElements(element)) { + if (ERROR_NAMESPACE.equals(el.getNamespaceURI()) && !el.getTagName().equals("text")) + return Condition.fromXMPP(el.getTagName()); } + return null; } @@ -107,23 +104,18 @@ public Condition getCondition() { * the error condition. * @see Condition */ - @SuppressWarnings("unchecked") public void setCondition(final Condition condition) { if (condition == null) throw new NullPointerException("Condition cannot be null"); - Element conditionElement = null; - for (final Iterator i = element.elementIterator(); i.hasNext();) { - final Element el = i.next(); - if (el.getNamespaceURI().equals(ERROR_NAMESPACE) && !el.getName().equals("text")) { - conditionElement = el; + + // Delete current condition. + for (final Element el : getChildElements(element)) { + if (ERROR_NAMESPACE.equals(el.getNamespaceURI()) && !el.getTagName().equals("text")) { + element.removeChild(element); } } - if (conditionElement != null) { - element.remove(conditionElement); - } - conditionElement = docFactory.createElement(condition.toXMPP(), ERROR_NAMESPACE); - element.add(conditionElement); + addChildElement(element, condition.toXMPP(), ERROR_NAMESPACE); } /** @@ -143,7 +135,7 @@ public String getText() { * the text description of the error. */ public void setText(final String text) { - setText(text, null); + setChildElementText(element, "text", text); } /** @@ -152,28 +144,13 @@ public void setText(final String text) { * * @param text * the text description of the error. - * @param language + * @param lang * the language code of the description, or null to * specify no language code. */ - public void setText(final String text, final String language) { - Element textElement = element.element("text"); - // If text is null, clear the text. - if (text == null) { - if (textElement != null) { - element.remove(textElement); - } - return; - } - - if (textElement == null) { - textElement = docFactory.createElement("text", ERROR_NAMESPACE); - if (language != null) { - textElement.addAttribute(QName.get("lang", "xml", "http://www.w3.org/XML/1998/namespace"), language); - } - element.add(textElement); - } - textElement.setText(text); + public void setText(final String text, final String lang) { + setChildElementText(element, "text", text); + setChildElementLang(element, "text", lang); } /** @@ -182,11 +159,8 @@ public void setText(final String text, final String language) { * * @return the language code of the text description, if it exists. */ - public String getTextLanguage() { - final Element textElement = element.element("text"); - if (textElement != null) - return textElement.attributeValue(QName.get("lang", "xml", "http://www.w3.org/XML/1998/namespace")); - return null; + public String getTextLang() { + return getChildElementLang(element, "text"); } /** diff --git a/src/main/java/org/xmpp/util/BaseXML.java b/src/main/java/org/xmpp/util/BaseXML.java index ca4d665..031b32c 100644 --- a/src/main/java/org/xmpp/util/BaseXML.java +++ b/src/main/java/org/xmpp/util/BaseXML.java @@ -1,6 +1,7 @@ package org.xmpp.util; import java.io.StringWriter; +import java.util.Iterator; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; @@ -15,6 +16,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.NodeList; public abstract class BaseXML implements Cloneable { @@ -69,6 +71,42 @@ protected BaseXML(final Element element) { this.element = (Element) document.importNode(element, true); } + protected final static Iterable getChildElements(final Element element) { + return getChildElements(element, "*", "*"); + } + + protected final static Iterable getChildElements(final Element element, final String name) { + return getChildElements(element, name, XMLConstants.NULL_NS_URI); + } + + protected final static Iterable getChildElements(final Element element, final String localName, final String namespaceURI) { + return new Iterable() { + + @Override + public Iterator iterator() { + return new Iterator() { + private final NodeList childList = element.getElementsByTagNameNS(namespaceURI, localName); + private int current = 0; + + @Override + public boolean hasNext() { + return current < childList.getLength(); + } + + @Override + public Element next() { + return (Element) childList.item(current++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + /** * Returns the first child element of this packet that matches the given * name and namespace. If no matching element is found, null will @@ -193,7 +231,7 @@ protected final static void setChildElementText(final Element element, final Str return; if (childElement == null) { - childElement = addChildElement(element, "body"); + childElement = addChildElement(element, qualifiedName); } childElement.setTextContent(text); } @@ -266,5 +304,5 @@ public final String toString() { throw new InternalError("Transformer error"); } } - + } From 216466e6989e11d68e8ba5e55afde68b16b1290a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Mart=C3=ADnez?= Date: Fri, 3 Jun 2011 12:36:27 +0200 Subject: [PATCH 3/6] Improvements to some enums. --- .../xmpp/component/ComponentException.java | 2 +- src/main/java/org/xmpp/forms/FormField.java | 66 ++---- .../java/org/xmpp/packet/PacketError.java | 218 +++++------------- .../java/org/xmpp/packet/StreamError.java | 117 +++------- 4 files changed, 110 insertions(+), 293 deletions(-) diff --git a/src/main/java/org/xmpp/component/ComponentException.java b/src/main/java/org/xmpp/component/ComponentException.java index d5d02ff..67738a7 100644 --- a/src/main/java/org/xmpp/component/ComponentException.java +++ b/src/main/java/org/xmpp/component/ComponentException.java @@ -51,7 +51,7 @@ public ComponentException(final String message, final StreamError streamError) { } public ComponentException(final StreamError streamError) { - super(streamError.getCondition().toXMPP()); + super(streamError.getCondition().toString()); this.streamError = streamError; } diff --git a/src/main/java/org/xmpp/forms/FormField.java b/src/main/java/org/xmpp/forms/FormField.java index 654d1f4..d4736d1 100644 --- a/src/main/java/org/xmpp/forms/FormField.java +++ b/src/main/java/org/xmpp/forms/FormField.java @@ -128,7 +128,7 @@ public List