IQ.java

  1. /**
  2.  *
  3.  * Copyright 2003-2007 Jive Software.
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.jivesoftware.smack.packet;

  18. import java.util.Locale;

  19. import org.jivesoftware.smack.util.Objects;
  20. import org.jivesoftware.smack.util.XmlStringBuilder;

  21. /**
  22.  * The base IQ (Info/Query) packet. IQ packets are used to get and set information
  23.  * on the server, including authentication, roster operations, and creating
  24.  * accounts. Each IQ packet has a specific type that indicates what type of action
  25.  * is being taken: "get", "set", "result", or "error".<p>
  26.  *
  27.  * IQ packets can contain a single child element that exists in a specific XML
  28.  * namespace. The combination of the element name and namespace determines what
  29.  * type of IQ packet it is. Some example IQ subpacket snippets:<ul>
  30.  *
  31.  *  <li>&lt;query xmlns="jabber:iq:auth"&gt; -- an authentication IQ.
  32.  *  <li>&lt;query xmlns="jabber:iq:private"&gt; -- a private storage IQ.
  33.  *  <li>&lt;pubsub xmlns="http://jabber.org/protocol/pubsub"&gt; -- a pubsub IQ.
  34.  * </ul>
  35.  *
  36.  * @author Matt Tucker
  37.  */
  38. public abstract class IQ extends Stanza {

  39.     // Don't name this field 'ELEMENT'. When it comes to IQ, ELEMENT is the child element!
  40.     public static final String IQ_ELEMENT = "iq";
  41.     public static final String QUERY_ELEMENT = "query";

  42.     private final String childElementName;
  43.     private final String childElementNamespace;

  44.     private Type type = Type.get;

  45.     public IQ(IQ iq) {
  46.         super(iq);
  47.         type = iq.getType();
  48.         this.childElementName = iq.childElementName;
  49.         this.childElementNamespace = iq.childElementNamespace;
  50.     }

  51.     protected IQ(String childElementName) {
  52.         this(childElementName, null);
  53.     }

  54.     protected IQ(String childElementName, String childElementNamespace) {
  55.         this.childElementName = childElementName;
  56.         this.childElementNamespace = childElementNamespace;
  57.     }

  58.     /**
  59.      * Returns the type of the IQ packet.
  60.      *
  61.      * @return the type of the IQ packet.
  62.      */
  63.     public Type getType() {
  64.         return type;
  65.     }

  66.     /**
  67.      * Sets the type of the IQ packet.
  68.      * <p>
  69.      * Since the type of an IQ must present, an IllegalArgmentException will be thrown when type is
  70.      * <code>null</code>.
  71.      * </p>
  72.      *
  73.      * @param type the type of the IQ packet.
  74.      */
  75.     public void setType(Type type) {
  76.         this.type = Objects.requireNonNull(type, "type must not be null");
  77.     }

  78.     /**
  79.      * Return true if this IQ is a request IQ, i.e. an IQ of type {@link Type#get} or {@link Type#set}.
  80.      *
  81.      * @return true if IQ type is 'get' or 'set', false otherwise.
  82.      * @since 4.1
  83.      */
  84.     public boolean isRequestIQ() {
  85.         switch (type) {
  86.         case get:
  87.         case set:
  88.             return true;
  89.         default:
  90.             return false;
  91.         }
  92.     }

  93.     public final String getChildElementName() {
  94.         return childElementName;
  95.     }

  96.     public final String getChildElementNamespace() {
  97.         return childElementNamespace;
  98.     }

  99.     @Override
  100.     public final XmlStringBuilder toXML() {
  101.         XmlStringBuilder buf = new XmlStringBuilder();
  102.         buf.halfOpenElement(IQ_ELEMENT);
  103.         addCommonAttributes(buf);
  104.         if (type == null) {
  105.             buf.attribute("type", "get");
  106.         }
  107.         else {
  108.             buf.attribute("type", type.toString());
  109.         }
  110.         buf.rightAngleBracket();
  111.         buf.append(getChildElementXML());
  112.         buf.closeElement(IQ_ELEMENT);
  113.         return buf;
  114.     }

  115.     /**
  116.      * Returns the sub-element XML section of the IQ packet, or the empty String if there
  117.      * isn't one.
  118.      *
  119.      * @return the child element section of the IQ XML.
  120.      */
  121.     public final XmlStringBuilder getChildElementXML() {
  122.         XmlStringBuilder xml = new XmlStringBuilder();
  123.         if (type == Type.error) {
  124.             // Add the error sub-packet, if there is one.
  125.             appendErrorIfExists(xml);
  126.         }
  127.         else if (childElementName != null) {
  128.             // Add the query section if there is one.
  129.             IQChildElementXmlStringBuilder iqChildElement = getIQChildElementBuilder(new IQChildElementXmlStringBuilder(this));
  130.             if (iqChildElement != null) {
  131.                 xml.append(iqChildElement);
  132.                 XmlStringBuilder extensionsXml = getExtensionsXML();
  133.                 if (iqChildElement.isEmptyElement) {
  134.                     if (extensionsXml.length() == 0) {
  135.                          xml.closeEmptyElement();
  136.                          return xml;
  137.                     } else {
  138.                         xml.rightAngleBracket();
  139.                     }
  140.                 }
  141.                 xml.append(extensionsXml);
  142.                 xml.closeElement(iqChildElement.element);
  143.             }
  144.         }
  145.         return xml;
  146.     }

  147.     /**
  148.      * This method must be overwritten by IQ subclasses to create their child content. It is important that the builder
  149.      * <b>does not include the final end element</b>. This will be done automatically by IQChildelementXmlStringBuilder
  150.      * after eventual existing packet extensions have been added.
  151.      * <p>
  152.      * For example to create an IQ with a extra attribute and an additional child element
  153.      * </p>
  154.      * <pre>
  155.      * {@code
  156.      * <iq to='foo@example.org' id='123'>
  157.      *   <bar xmlns='example:bar' extraAttribute='blaz'>
  158.      *      <extraElement>elementText</extraElement>
  159.      *   </bar>
  160.      * </iq>
  161.      * }
  162.      * </pre>
  163.      * the body of the {@code getIQChildElementBuilder} looks like
  164.      * <pre>
  165.      * {@code
  166.      * // The builder 'xml' will already have the child element and the 'xmlns' attribute added
  167.      * // So the current builder state is "<bar xmlns='example:bar'"
  168.      * xml.attribute("extraAttribute", "blaz");
  169.      * xml.rightAngleBracket();
  170.      * xml.element("extraElement", "elementText");
  171.      * // Do not close the 'bar' attribute by calling xml.closeElement('bar')
  172.      * }
  173.      * </pre>
  174.      * If your IQ only contains attributes and no child elements, i.e. it can be represented as empty element, then you
  175.      * can mark it as such.
  176.      * <pre>
  177.      * xml.attribute(&quot;myAttribute&quot;, &quot;myAttributeValue&quot;);
  178.      * xml.setEmptyElement();
  179.      * </pre>
  180.      * If your IQ does not contain any attributes or child elements (besides packet extensions), consider sub-classing
  181.      * {@link SimpleIQ} instead.
  182.      *
  183.      * @param xml a pre-created builder which already has the child element and the 'xmlns' attribute set.
  184.      * @return the build to create the IQ child content.
  185.      */
  186.     protected abstract IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml);

  187.     /**
  188.      * Convenience method to create a new empty {@link Type#result IQ.Type.result}
  189.      * IQ based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}
  190.      * IQ. The new packet will be initialized with:<ul>
  191.      *      <li>The sender set to the recipient of the originating IQ.
  192.      *      <li>The recipient set to the sender of the originating IQ.
  193.      *      <li>The type set to {@link Type#result IQ.Type.result}.
  194.      *      <li>The id set to the id of the originating IQ.
  195.      *      <li>No child element of the IQ element.
  196.      * </ul>
  197.      *
  198.      * @param request the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet.
  199.      * @throws IllegalArgumentException if the IQ packet does not have a type of
  200.      *      {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
  201.      * @return a new {@link Type#result IQ.Type.result} IQ based on the originating IQ.
  202.      */
  203.     public static IQ createResultIQ(final IQ request) {
  204.         return new EmptyResultIQ(request);
  205.     }

  206.     /**
  207.      * Convenience method to create a new {@link Type#error IQ.Type.error} IQ
  208.      * based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}
  209.      * IQ. The new packet will be initialized with:<ul>
  210.      *      <li>The sender set to the recipient of the originating IQ.
  211.      *      <li>The recipient set to the sender of the originating IQ.
  212.      *      <li>The type set to {@link Type#error IQ.Type.error}.
  213.      *      <li>The id set to the id of the originating IQ.
  214.      *      <li>The child element contained in the associated originating IQ.
  215.      *      <li>The provided {@link XMPPError XMPPError}.
  216.      * </ul>
  217.      *
  218.      * @param request the {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set} IQ packet.
  219.      * @param error the error to associate with the created IQ packet.
  220.      * @throws IllegalArgumentException if the IQ packet does not have a type of
  221.      *      {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
  222.      * @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
  223.      */
  224.     public static ErrorIQ createErrorResponse(final IQ request, final XMPPError error) {
  225.         if (!(request.getType() == Type.get || request.getType() == Type.set)) {
  226.             throw new IllegalArgumentException(
  227.                     "IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
  228.         }
  229.         final ErrorIQ result = new ErrorIQ(error);
  230.         result.setStanzaId(request.getStanzaId());
  231.         result.setFrom(request.getTo());
  232.         result.setTo(request.getFrom());
  233.         return result;
  234.     }

  235.     /**
  236.      * A enum to represent the type of the IQ stanza.
  237.      */
  238.     public enum Type {

  239.         /**
  240.          * The IQ stanza requests information, inquires about what data is needed in order to complete further operations, etc.
  241.          */
  242.         get,

  243.         /**
  244.          * The IQ stanza provides data that is needed for an operation to be completed, sets new values, replaces existing values, etc.
  245.          */
  246.         set,

  247.         /**
  248.          * The IQ stanza is a response to a successful get or set request.
  249.          */
  250.         result,

  251.         /**
  252.          * The IQ stanza reports an error that has occurred regarding processing or delivery of a get or set request.
  253.          */
  254.         error,
  255.         ;

  256.         /**
  257.          * Converts a String into the corresponding types. Valid String values
  258.          * that can be converted to types are: "get", "set", "result", and "error".
  259.          *
  260.          * @param string the String value to covert.
  261.          * @return the corresponding Type.
  262.          * @throws IllegalArgumentException when not able to parse the string parameter
  263.          * @throws NullPointerException if the string is null
  264.          */
  265.         public static Type fromString(String string) {
  266.             return Type.valueOf(string.toLowerCase(Locale.US));
  267.         }
  268.     }

  269.     public static class IQChildElementXmlStringBuilder extends XmlStringBuilder {
  270.         private final String element;

  271.         private boolean isEmptyElement;

  272.         private IQChildElementXmlStringBuilder(IQ iq) {
  273.             this(iq.getChildElementName(), iq.getChildElementNamespace());
  274.         }

  275.         public IQChildElementXmlStringBuilder(ExtensionElement pe) {
  276.             this(pe.getElementName(), pe.getNamespace());
  277.         }

  278.         private IQChildElementXmlStringBuilder(String element, String namespace) {
  279.             prelude(element, namespace);
  280.             this.element = element;
  281.         }

  282.         public void setEmptyElement() {
  283.             isEmptyElement = true;
  284.         }
  285.     }
  286. }