1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-10 18:59:41 +02:00

Enable PacketExtensions for IQs

This is actually only part one, i.e. with this commit if the user adds a
PacketExtension to an IQ it will be included in IQ.toXml(). Which was
previously only the case if the IQ subclass explicitly included packet
extensions.

The second part of the change is to change the IQ provider, so that
packet extensions are automatically parsed.

Cases where PacketExtensions are used for Message and IQ are slightly
changed. The IQ sublcass now only has a field with this
PacketExtension (see for example
bytestreams.ibb.packet.DataPacketExtension).

Also changed hoxt API: Removed unnecessary indirection and made the
API more Smack idiomatic.
This commit is contained in:
Florian Schmaus 2014-11-07 21:12:01 +01:00
parent a9c798f3bb
commit 9e797c1b17
93 changed files with 1347 additions and 1438 deletions

View file

@ -17,8 +17,6 @@
package org.jivesoftware.smack.packet;
import org.jivesoftware.smack.util.XmlStringBuilder;
/**
* IQ packet used by Smack to bind a resource and to obtain the jid assigned by the server.
* There are two ways to bind a resource. One is simply sending an empty Bind packet where the
@ -39,6 +37,7 @@ public class Bind extends IQ {
private final String jid;
public Bind(String resource, String jid) {
super(ELEMENT, NAMESPACE);
this.resource = resource;
this.jid = jid;
}
@ -62,12 +61,10 @@ public class Bind extends IQ {
}
@Override
public XmlStringBuilder getChildElementXML() {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(ELEMENT).xmlnsAttribute(NAMESPACE).rightAngleBracket();
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.rightAngleBracket();
xml.optElement("resource", resource);
xml.optElement("jid", jid);
xml.closeElement(ELEMENT);
return xml;
}

View file

@ -0,0 +1,42 @@
/**
*
* Copyright © 2014 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.packet;
public class EmptyResultIQ extends IQ {
public EmptyResultIQ() {
super(null, null);
type = IQ.Type.result;
}
public EmptyResultIQ(IQ request) {
this();
if (!(request.getType() == Type.get || request.getType() == Type.set)) {
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
}
setPacketID(request.getPacketID());
setFrom(request.getTo());
setTo(request.getFrom());
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
// Empty result IQs don't have an child elements
return null;
}
}

View file

@ -0,0 +1,38 @@
/**
*
* Copyright © 2014 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.packet;
public class ErrorIQ extends SimpleIQ {
public static final String ELEMENT = XMPPError.ERROR;
/**
* Constructs a new error IQ.
* <p>
* According to RFC 6120 § 8.3.1 "4. An error stanza MUST contain an <error/> child element.", so the xmppError argument is mandatory.
* </p>
* @param xmppError the XMPPError (required).
*/
public ErrorIQ(XMPPError xmppError) {
super(ELEMENT, null);
if (xmppError == null) {
throw new IllegalArgumentException("XMPPError must not be null");
}
type = IQ.Type.error;
setError(xmppError);
}
}

View file

@ -43,16 +43,27 @@ public abstract class IQ extends Packet {
public static final String ELEMENT = "iq";
public static final String QUERY_ELEMENT = "query";
private Type type = Type.get;
private final String childElementName;
private final String childElementNamespace;
public IQ() {
super();
}
protected Type type = Type.get;
public IQ(IQ iq) {
super(iq);
type = iq.getType();
this.childElementName = iq.childElementName;
this.childElementNamespace = iq.childElementNamespace;
}
protected IQ(String childElementName) {
this(childElementName, null);
}
protected IQ(String childElementName, String childElementNamespace) {
this.childElementName = childElementName;
this.childElementNamespace = childElementNamespace;
}
/**
* Returns the type of the IQ packet.
*
@ -76,8 +87,16 @@ public abstract class IQ extends Packet {
}
}
public final String getChildElementName() {
return childElementName;
}
public final String getChildElementNamespace() {
return childElementNamespace;
}
@Override
public CharSequence toXML() {
public final XmlStringBuilder toXML() {
XmlStringBuilder buf = new XmlStringBuilder();
buf.halfOpenElement(ELEMENT);
addCommonAttributes(buf);
@ -88,26 +107,84 @@ public abstract class IQ extends Packet {
buf.attribute("type", type.toString());
}
buf.rightAngleBracket();
// Add the query section if there is one.
buf.optAppend(getChildElementXML());
// Add the error sub-packet, if there is one.
XMPPError error = getError();
if (error != null) {
buf.append(error.toXML());
}
buf.append(getChildElementXML());
buf.closeElement(ELEMENT);
return buf;
}
/**
* Returns the sub-element XML section of the IQ packet, or <tt>null</tt> if there
* isn't one. Packet extensions <b>must</b> be included, if any are defined.<p>
*
* Extensions of this class must override this method.
* Returns the sub-element XML section of the IQ packet, or the empty String if there
* isn't one.
*
* @return the child element section of the IQ XML.
*/
public abstract CharSequence getChildElementXML();
public final XmlStringBuilder getChildElementXML() {
XmlStringBuilder xml = new XmlStringBuilder();
if (type == Type.error) {
// Add the error sub-packet, if there is one.
appendErrorIfExists(xml);
}
else if (childElementName != null) {
// Add the query section if there is one.
IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this));
if (iqChildElement != null) {
xml.append(iqChildElement);
XmlStringBuilder extensionsXml = getExtensionsXML();
if (iqChildElement.isEmptyElement) {
if (extensionsXml.length() == 0) {
xml.closeEmptyElement();
return xml;
} else {
xml.rightAngleBracket();
}
}
xml.append(extensionsXml);
xml.closeElement(iqChildElement.element);
}
}
return xml;
}
/**
* This method must be overwritten by IQ subclasses to create their child content. It is important that the builder
* <b>does not include the final end element</b>. This will be done automatically by IQChildelementXmlStringBuilder
* after eventual existing packet extensions have been added.
* <p>
* For example to create an IQ with a extra attribute and an additional child element
* </p>
* <pre>
* {@code
* <iq to='foo@example.org' id='123'>
* <bar xmlns='example:bar' extraAttribute='blaz'>
* <extraElement>elementText</extraElement>
* </bar>
* </iq>
* }
* </pre>
* the body of the {@code getIQChildElementBuilder} looks like
* <pre>
* {@code
* // The builder 'xml' will already have the child element and the 'xmlns' attribute added
* // So the current builder state is "<bar xmlns='example:bar'"
* xml.attribute("extraAttribute", "blaz");
* xml.rightAngleBracket();
* xml.element("extraElement", "elementText");
* // Do not close the 'bar' attribute by calling xml.closeElement('bar')
* }
* </pre>
* If your IQ only contains attributes and no child elements, i.e. it can be represented as empty element, then you
* can mark it as such.
* <pre>
* xml.attribute(&quot;myAttribute&quot;, &quot;myAttributeValue&quot;);
* xml.setEmptyElement();
* </pre>
* If your IQ does not contain any attributes or child elements (besides packet extensions), consider sub-classing
* {@link SimpleIQ} instead.
*
* @param xml a pre-created builder which already has the child element and the 'xmlns' attribute set.
* @return the build to create the IQ child content.
*/
protected abstract IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml);
/**
* Convenience method to create a new empty {@link Type#result IQ.Type.result}
@ -126,20 +203,7 @@ public abstract class IQ extends Packet {
* @return a new {@link Type#result IQ.Type.result} IQ based on the originating IQ.
*/
public static IQ createResultIQ(final IQ request) {
if (!(request.getType() == Type.get || request.getType() == Type.set)) {
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
}
final IQ result = new IQ() {
public String getChildElementXML() {
return null;
}
};
result.setType(Type.result);
result.setPacketID(request.getPacketID());
result.setFrom(request.getTo());
result.setTo(request.getFrom());
return result;
return new EmptyResultIQ(request);
}
/**
@ -165,17 +229,10 @@ public abstract class IQ extends Packet {
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
}
final IQ result = new IQ() {
@Override
public CharSequence getChildElementXML() {
return request.getChildElementXML();
}
};
result.setType(Type.error);
final IQ result = new ErrorIQ(error);
result.setPacketID(request.getPacketID());
result.setFrom(request.getTo());
result.setTo(request.getFrom());
result.setError(error);
return result;
}
@ -209,4 +266,27 @@ public abstract class IQ extends Packet {
return Type.valueOf(string.toLowerCase(Locale.US));
}
}
public static class IQChildElementXmlStringBuilder extends XmlStringBuilder {
private final String element;
private boolean isEmptyElement;
private IQChildElementXmlStringBuilder(IQ iq) {
this(iq.getChildElementName(), iq.getChildElementNamespace());
}
public IQChildElementXmlStringBuilder(PacketExtension pe) {
this(pe.getElementName(), pe.getNamespace());
}
private IQChildElementXmlStringBuilder(String element, String namespace) {
prelude(element, namespace);
this.element = element;
}
public void setEmptyElement() {
isEmptyElement = true;
}
}
}

View file

@ -430,10 +430,7 @@ public final class Message extends Packet {
buf.optElement("thread", thread);
// Append the error subpacket if the message type is an error.
if (type == Type.error) {
XMPPError error = getError();
if (error != null) {
buf.append(error.toXML());
}
appendErrorIfExists(buf);
}
// Add packet extensions, if any are defined.
buf.append(getExtensionsXML());

View file

@ -383,9 +383,21 @@ public abstract class Packet extends TopLevelStreamElement {
* @param xml
*/
protected void addCommonAttributes(XmlStringBuilder xml) {
xml.optAttribute("id", getPacketID());
xml.optAttribute("to", getTo());
xml.optAttribute("from", getFrom());
xml.optAttribute("id", getPacketID());
xml.xmllangAttribute(getLanguage());
}
/**
* Append an XMPPError is this packet has one set.
*
* @param xml the XmlStringBuilder to append the error to.
*/
protected void appendErrorIfExists(XmlStringBuilder xml) {
XMPPError error = getError();
if (error != null) {
xml.append(error.toXML());
}
}
}

View file

@ -223,10 +223,8 @@ public final class Presence extends Packet {
buf.append(getExtensionsXML());
// Add the error sub-packet, if there is one.
XMPPError error = getError();
if (error != null) {
buf.append(error.toXML());
}
appendErrorIfExists(buf);
buf.closeElement(ELEMENT);
return buf;

View file

@ -40,6 +40,10 @@ public class RosterPacket extends IQ {
private final List<Item> rosterItems = new ArrayList<Item>();
private String rosterVersion;
public RosterPacket() {
super(ELEMENT, NAMESPACE);
}
/**
* Adds a roster item to the packet.
*
@ -74,10 +78,7 @@ public class RosterPacket extends IQ {
}
@Override
public XmlStringBuilder getChildElementXML() {
XmlStringBuilder buf = new XmlStringBuilder();
buf.halfOpenElement(ELEMENT);
buf.xmlnsAttribute(NAMESPACE);
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder buf) {
buf.optAttribute("ver", rosterVersion);
buf.rightAngleBracket();
@ -86,7 +87,6 @@ public class RosterPacket extends IQ {
buf.append(entry.toXML());
}
}
buf.closeElement(ELEMENT);
return buf;
}

View file

@ -30,7 +30,7 @@ package org.jivesoftware.smack.packet;
*
* @author Gaston Dombiak
*/
public class Session extends IQ {
public class Session extends SimpleIQ {
public static final String ELEMENT = "session";
public static final String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-session";
@ -38,14 +38,10 @@ public class Session extends IQ {
private static final String SESSION = '<' + ELEMENT + " xmlns='" + NAMESPACE + "'/>";
public Session() {
super(ELEMENT, NAMESPACE);
setType(IQ.Type.set);
}
@Override
public String getChildElementXML() {
return SESSION;
}
public static class Feature implements PacketExtension {
public static final Session.Feature INSTANCE = new Feature();

View file

@ -0,0 +1,38 @@
/**
*
* Copyright © 2014 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.packet;
/**
* A simple IQ.
* <p>
* Simple IQs child elements do not contain further attributes besides 'xmlns'. They may contain additional packet
* extensions.
* </p>
*/
public abstract class SimpleIQ extends IQ {
protected SimpleIQ(String childElementName, String childElementNamespace) {
super(childElementName, childElementNamespace);
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
xml.setEmptyElement();
return xml;
}
}

View file

@ -32,6 +32,8 @@ import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.compress.packet.Compress;
import org.jivesoftware.smack.packet.DefaultPacketExtension;
import org.jivesoftware.smack.packet.EmptyResultIQ;
import org.jivesoftware.smack.packet.ErrorIQ;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
@ -634,10 +636,12 @@ public class PacketParserUtils {
}
// Only handle unknown IQs of type result. Types of 'get' and 'set' which are not understood
// have to be answered with an IQ error response. See the code a few lines below
// Note that if we reach this code, it is guranteed that the result IQ contained a child element
// (RFC 6120 § 8.2.3 6) because otherwhise we would have reached the END_TAG first.
else if (IQ.Type.result == type){
// No Provider found for the IQ stanza, parse it to an UnparsedIQ instance
// so that the content of the IQ can be examined later on
iqPacket = new UnparsedResultIQ(parseElement(parser));
iqPacket = new UnparsedResultIQ(elementName, namespace, parseElement(parser));
}
}
break;
@ -652,32 +656,28 @@ public class PacketParserUtils {
}
// Decide what to do when an IQ packet was not understood
if (iqPacket == null) {
if (connection != null && (IQ.Type.get == type || IQ.Type.set == type)) {
switch (type) {
case get:
case set:
if (connection == null) {
return null;
}
// If the IQ stanza is of type "get" or "set" containing a child element qualified
// by a namespace with no registered Smack provider, then answer an IQ of type
// "error" with code 501 ("feature-not-implemented")
iqPacket = new IQ() {
@Override
public String getChildElementXML() {
return null;
}
};
iqPacket = new ErrorIQ(new XMPPError(XMPPError.Condition.feature_not_implemented));
iqPacket.setPacketID(id);
iqPacket.setTo(from);
iqPacket.setFrom(to);
iqPacket.setType(IQ.Type.error);
iqPacket.setError(new XMPPError(XMPPError.Condition.feature_not_implemented));
connection.sendPacket(iqPacket);
return null;
}
else {
// If an IQ packet wasn't created above, create an empty IQ packet.
iqPacket = new IQ() {
@Override
public String getChildElementXML() {
return null;
}
};
case error:
// If an IQ packet wasn't created above, create an empty error IQ packet.
iqPacket = new ErrorIQ(error);
break;
case result:
iqPacket = new EmptyResultIQ();
break;
}
}
@ -1077,15 +1077,20 @@ public class PacketParserUtils {
*
*/
public static class UnparsedResultIQ extends IQ {
public UnparsedResultIQ(CharSequence content) {
private UnparsedResultIQ(String element, String namespace, CharSequence content) {
super(element, namespace);
this.content = content;
}
private final CharSequence content;
public CharSequence getContent() {
return content;
}
@Override
public CharSequence getChildElementXML() {
return this.content;
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
throw new UnsupportedOperationException();
}
}
}

View file

@ -39,8 +39,15 @@ public class XmlStringBuilder implements Appendable, CharSequence {
halfOpenElement(e.getElementName());
}
public XmlStringBuilder escapedElement(String name, String escapedContent) {
assert escapedContent != null;
openElement(name);
append(escapedContent);
closeElement(name);
return this;
}
/**
* Does nothing if content is null.
*
* @param name
* @param content
@ -94,6 +101,7 @@ public class XmlStringBuilder implements Appendable, CharSequence {
}
public XmlStringBuilder halfOpenElement(String name) {
assert(StringUtils.isNotEmpty(name));
sb.append('<').append(name);
return this;
}
@ -235,8 +243,12 @@ public class XmlStringBuilder implements Appendable, CharSequence {
}
public XmlStringBuilder prelude(PacketExtension pe) {
halfOpenElement(pe.getElementName());
xmlnsAttribute(pe.getNamespace());
return prelude(pe.getElementName(), pe.getNamespace());
}
public XmlStringBuilder prelude(String elementName, String namespace) {
halfOpenElement(elementName);
xmlnsAttribute(namespace);
return this;
}
@ -247,6 +259,13 @@ public class XmlStringBuilder implements Appendable, CharSequence {
return this;
}
public XmlStringBuilder optAppend(Element element) {
if (element != null) {
append(element.toXML());
}
return this;
}
public XmlStringBuilder append(XmlStringBuilder xsb) {
assert xsb != null;
sb.append(xsb.sb);

View file

@ -655,14 +655,7 @@ public class RosterTest {
connection.processPacket(rosterPush);
// Create and process the IQ response
final IQ response = new IQ() {
public String getChildElementXML() {
return null;
}
};
response.setPacketID(rosterRequest.getPacketID());
response.setType(Type.result);
response.setTo(connection.getUser());
final IQ response = IQ.createResultIQ(rosterRequest);
connection.processPacket(response);
// Verify the roster update request

View file

@ -1,6 +1,6 @@
/**
*
* Copyright © Florian Schmaus
* Copyright © 2014 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,6 +20,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.TestIQ;
import org.jivesoftware.smack.util.StringUtils;
import org.junit.Test;
@ -27,26 +28,15 @@ public class StanzaIdTest {
@Test
public void testIqId() {
IQ iq1 = new TestIqId();
IQ iq1 = new TestIQ();
String iq1Id = iq1.getPacketID();
assertTrue(StringUtils.isNotEmpty(iq1Id));
IQ iq2 = new TestIqId();
IQ iq2 = new TestIQ();
String iq2Id = iq2.getPacketID();
assertTrue(StringUtils.isNotEmpty(iq2Id));
assertFalse(iq1Id.equals(iq2Id));
}
private static class TestIqId extends IQ {
public TestIqId() {
setType(Type.set);
}
@Override
public CharSequence getChildElementXML() {
return "<testIqId/>";
}
}
}

View file

@ -19,7 +19,6 @@ package org.jivesoftware.smack.packet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import org.junit.Test;
@ -32,18 +31,15 @@ import org.junit.Test;
*/
public class IQResponseTest {
final static private String childElement = "<child xmlns=\"http://igniterealtime.org/protocol/test\"/>";
private static final String ELEMENT = "child";
private static final String NAMESPACE = "http://igniterealtime.org/protocol/test";
/**
* Test creating a simple and empty IQ response.
*/
@Test
public void testGeneratingSimpleResponse() {
final IQ request = new IQ() {
public String getChildElementXML() {
return childElement;
}
};
final IQ request = new TestIQ(ELEMENT, NAMESPACE);
request.setFrom("sender@test/Smack");
request.setTo("receiver@test/Smack");
@ -54,7 +50,7 @@ public class IQResponseTest {
assertEquals(request.getPacketID(), result.getPacketID());
assertEquals(request.getFrom(), result.getTo());
assertEquals(request.getTo(), result.getFrom());
assertNull(result.getChildElementXML());
assertEquals("", result.getChildElementXML().toString());
}
/**
@ -63,11 +59,8 @@ public class IQResponseTest {
@Test
public void testGeneratingValidErrorResponse() {
final XMPPError error = new XMPPError(XMPPError.Condition.bad_request);
final IQ request = new IQ() {
public String getChildElementXML() {
return childElement;
}
};
final IQ request = new TestIQ(ELEMENT, NAMESPACE);
request.setType(IQ.Type.set);
request.setFrom("sender@test/Smack");
request.setTo("receiver@test/Smack");
@ -79,7 +72,8 @@ public class IQResponseTest {
assertEquals(request.getPacketID(), result.getPacketID());
assertEquals(request.getFrom(), result.getTo());
assertEquals(error, result.getError());
assertEquals(childElement, result.getChildElementXML());
// TODO this test was never valid
// assertEquals(CHILD_ELEMENT, result.getChildElementXML());
}
/**
@ -88,11 +82,8 @@ public class IQResponseTest {
*/
@Test
public void testGeneratingResponseBasedOnResult() {
final IQ request = new IQ() {
public String getChildElementXML() {
return childElement;
}
};
final IQ request = new TestIQ(ELEMENT, NAMESPACE);
request.setType(IQ.Type.result);
request.setFrom("sender@test/Smack");
request.setTo("receiver@test/Smack");
@ -114,11 +105,8 @@ public class IQResponseTest {
@Test
public void testGeneratingErrorBasedOnError() {
final XMPPError error = new XMPPError(XMPPError.Condition.bad_request);
final IQ request = new IQ() {
public String getChildElementXML() {
return childElement;
}
};
final IQ request = new TestIQ(ELEMENT, NAMESPACE);
request.setType(IQ.Type.error);
request.setFrom("sender@test/Smack");
request.setTo("receiver@test/Smack");

View file

@ -0,0 +1,35 @@
/**
*
* Copyright © 2014 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.packet;
public class TestIQ extends SimpleIQ {
public TestIQ() {
this(null, null);
}
public TestIQ(String element, String namespace) {
super(element, namespace);
}
@Override
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
if (getChildElementName() == null)
return null;
return super.getIQChildElementBuilder(xml);
}
}