diff options
Diffstat (limited to 'src/org/jivesoftware/smackx/packet/DiscoverInfo.java')
-rw-r--r-- | src/org/jivesoftware/smackx/packet/DiscoverInfo.java | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smackx/packet/DiscoverInfo.java b/src/org/jivesoftware/smackx/packet/DiscoverInfo.java new file mode 100644 index 0000000..ba873a9 --- /dev/null +++ b/src/org/jivesoftware/smackx/packet/DiscoverInfo.java @@ -0,0 +1,507 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2007 Jive Software. + * + * All rights reserved. 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.smackx.packet; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.util.StringUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * A DiscoverInfo IQ packet, which is used by XMPP clients to request and receive information + * to/from other XMPP entities.<p> + * + * The received information may contain one or more identities of the requested XMPP entity, and + * a list of supported features by the requested XMPP entity. + * + * @author Gaston Dombiak + */ +public class DiscoverInfo extends IQ { + + public static final String NAMESPACE = "http://jabber.org/protocol/disco#info"; + + private final List<Feature> features = new CopyOnWriteArrayList<Feature>(); + private final List<Identity> identities = new CopyOnWriteArrayList<Identity>(); + private String node; + + public DiscoverInfo() { + super(); + } + + /** + * Copy constructor + * + * @param d + */ + public DiscoverInfo(DiscoverInfo d) { + super(d); + + // Set node + setNode(d.getNode()); + + // Copy features + synchronized (d.features) { + for (Feature f : d.features) { + addFeature(f); + } + } + + // Copy identities + synchronized (d.identities) { + for (Identity i : d.identities) { + addIdentity(i); + } + } + } + + /** + * Adds a new feature to the discovered information. + * + * @param feature the discovered feature + */ + public void addFeature(String feature) { + addFeature(new Feature(feature)); + } + + /** + * Adds a collection of features to the packet. Does noting if featuresToAdd is null. + * + * @param featuresToAdd + */ + public void addFeatures(Collection<String> featuresToAdd) { + if (featuresToAdd == null) return; + for (String feature : featuresToAdd) { + addFeature(feature); + } + } + + private void addFeature(Feature feature) { + synchronized (features) { + features.add(feature); + } + } + + /** + * Returns the discovered features of an XMPP entity. + * + * @return an Iterator on the discovered features of an XMPP entity + */ + public Iterator<Feature> getFeatures() { + synchronized (features) { + return Collections.unmodifiableList(features).iterator(); + } + } + + /** + * Adds a new identity of the requested entity to the discovered information. + * + * @param identity the discovered entity's identity + */ + public void addIdentity(Identity identity) { + synchronized (identities) { + identities.add(identity); + } + } + + /** + * Adds identities to the DiscoverInfo stanza + * + * @param identitiesToAdd + */ + public void addIdentities(Collection<Identity> identitiesToAdd) { + if (identitiesToAdd == null) return; + synchronized (identities) { + identities.addAll(identitiesToAdd); + } + } + + /** + * Returns the discovered identities of an XMPP entity. + * + * @return an Iterator on the discoveted identities + */ + public Iterator<Identity> getIdentities() { + synchronized (identities) { + return Collections.unmodifiableList(identities).iterator(); + } + } + + /** + * Returns the node attribute that supplements the 'jid' attribute. A node is merely + * something that is associated with a JID and for which the JID can provide information.<p> + * + * Node attributes SHOULD be used only when trying to provide or query information which + * is not directly addressable. + * + * @return the node attribute that supplements the 'jid' attribute + */ + public String getNode() { + return node; + } + + /** + * Sets the node attribute that supplements the 'jid' attribute. A node is merely + * something that is associated with a JID and for which the JID can provide information.<p> + * + * Node attributes SHOULD be used only when trying to provide or query information which + * is not directly addressable. + * + * @param node the node attribute that supplements the 'jid' attribute + */ + public void setNode(String node) { + this.node = node; + } + + /** + * Returns true if the specified feature is part of the discovered information. + * + * @param feature the feature to check + * @return true if the requestes feature has been discovered + */ + public boolean containsFeature(String feature) { + for (Iterator<Feature> it = getFeatures(); it.hasNext();) { + if (feature.equals(it.next().getVar())) + return true; + } + return false; + } + + public String getChildElementXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<query xmlns=\"" + NAMESPACE + "\""); + if (getNode() != null) { + buf.append(" node=\""); + buf.append(StringUtils.escapeForXML(getNode())); + buf.append("\""); + } + buf.append(">"); + synchronized (identities) { + for (Identity identity : identities) { + buf.append(identity.toXML()); + } + } + synchronized (features) { + for (Feature feature : features) { + buf.append(feature.toXML()); + } + } + // Add packet extensions, if any are defined. + buf.append(getExtensionsXML()); + buf.append("</query>"); + return buf.toString(); + } + + /** + * Test if a DiscoverInfo response contains duplicate identities. + * + * @return true if duplicate identities where found, otherwise false + */ + public boolean containsDuplicateIdentities() { + List<Identity> checkedIdentities = new LinkedList<Identity>(); + for (Identity i : identities) { + for (Identity i2 : checkedIdentities) { + if (i.equals(i2)) + return true; + } + checkedIdentities.add(i); + } + return false; + } + + /** + * Test if a DiscoverInfo response contains duplicate features. + * + * @return true if duplicate identities where found, otherwise false + */ + public boolean containsDuplicateFeatures() { + List<Feature> checkedFeatures = new LinkedList<Feature>(); + for (Feature f : features) { + for (Feature f2 : checkedFeatures) { + if (f.equals(f2)) + return true; + } + checkedFeatures.add(f); + } + return false; + } + + /** + * Represents the identity of a given XMPP entity. An entity may have many identities but all + * the identities SHOULD have the same name.<p> + * + * Refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> + * in order to get the official registry of values for the <i>category</i> and <i>type</i> + * attributes. + * + */ + public static class Identity implements Comparable<Identity> { + + private String category; + private String name; + private String type; + private String lang; // 'xml:lang; + + /** + * Creates a new identity for an XMPP entity. + * + * @param category the entity's category. + * @param name the entity's name. + * @deprecated As per the spec, the type field is mandatory and the 3 argument constructor should be used instead. + */ + public Identity(String category, String name) { + this.category = category; + this.name = name; + } + + /** + * Creates a new identity for an XMPP entity. + * 'category' and 'type' are required by + * <a href="http://xmpp.org/extensions/xep-0030.html#schemas">XEP-30 XML Schemas</a> + * + * @param category the entity's category (required as per XEP-30). + * @param name the entity's name. + * @param type the entity's type (required as per XEP-30). + */ + public Identity(String category, String name, String type) { + if ((category == null) || (type == null)) + throw new IllegalArgumentException("category and type cannot be null"); + + this.category = category; + this.name = name; + this.type = type; + } + + /** + * Returns the entity's category. To get the official registry of values for the + * 'category' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> + * + * @return the entity's category. + */ + public String getCategory() { + return category; + } + + /** + * Returns the identity's name. + * + * @return the identity's name. + */ + public String getName() { + return name; + } + + /** + * Returns the entity's type. To get the official registry of values for the + * 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> + * + * @return the entity's type. + */ + public String getType() { + return type; + } + + /** + * Sets the entity's type. To get the official registry of values for the + * 'type' attribute refer to <a href="http://www.jabber.org/registrar/disco-categories.html">Jabber::Registrar</a> + * + * @param type the identity's type. + * @deprecated As per the spec, this field is mandatory and the 3 argument constructor should be used instead. + */ + public void setType(String type) { + this.type = type; + } + + /** + * Sets the natural language (xml:lang) for this identity (optional) + * + * @param lang the xml:lang of this Identity + */ + public void setLanguage(String lang) { + this.lang = lang; + } + + /** + * Returns the identities natural language if one is set + * + * @return the value of xml:lang of this Identity + */ + public String getLanguage() { + return lang; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<identity"); + // Check if this packet has 'lang' set and maybe append it to the resulting string + if (lang != null) + buf.append(" xml:lang=\"").append(StringUtils.escapeForXML(lang)).append("\""); + // Category must always be set + buf.append(" category=\"").append(StringUtils.escapeForXML(category)).append("\""); + // Name must always be set + buf.append(" name=\"").append(StringUtils.escapeForXML(name)).append("\""); + // Check if this packet has 'type' set and maybe append it to the resulting string + if (type != null) { + buf.append(" type=\"").append(StringUtils.escapeForXML(type)).append("\""); + } + buf.append("/>"); + return buf.toString(); + } + + /** + * Check equality for Identity for category, type, lang and name + * in that order as defined by + * <a href="http://xmpp.org/extensions/xep-0115.html#ver-proc">XEP-0015 5.4 Processing Method (Step 3.3)</a> + * + */ + public boolean equals(Object obj) { + if (obj == null) + return false; + if (obj == this) + return true; + if (obj.getClass() != getClass()) + return false; + + DiscoverInfo.Identity other = (DiscoverInfo.Identity) obj; + if (!this.category.equals(other.category)) + return false; + + String otherLang = other.lang == null ? "" : other.lang; + String thisLang = lang == null ? "" : lang; + if (!otherLang.equals(thisLang)) + return false; + + // This safeguard can be removed once the deprecated constructor is removed. + String otherType = other.type == null ? "" : other.type; + String thisType = type == null ? "" : type; + if (!otherType.equals(thisType)) + return false; + + String otherName = other.name == null ? "" : other.name; + String thisName = name == null ? "" : other.name; + if (!thisName.equals(otherName)) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = 1; + result = 37 * result + category.hashCode(); + result = 37 * result + (lang == null ? 0 : lang.hashCode()); + result = 37 * result + (type == null ? 0 : type.hashCode()); + result = 37 * result + (name == null ? 0 : name.hashCode()); + return result; + } + + /** + * Compares this identity with another one. The comparison order is: + * Category, Type, Lang. If all three are identical the other Identity is considered equal. + * Name is not used for comparision, as defined by XEP-0115 + * + * @param obj + * @return + */ + public int compareTo(DiscoverInfo.Identity other) { + String otherLang = other.lang == null ? "" : other.lang; + String thisLang = lang == null ? "" : lang; + + // This can be removed once the deprecated constructor is removed. + String otherType = other.type == null ? "" : other.type; + String thisType = type == null ? "" : type; + + if (category.equals(other.category)) { + if (thisType.equals(otherType)) { + if (thisLang.equals(otherLang)) { + // Don't compare on name, XEP-30 says that name SHOULD + // be equals for all identities of an entity + return 0; + } else { + return thisLang.compareTo(otherLang); + } + } else { + return thisType.compareTo(otherType); + } + } else { + return category.compareTo(other.category); + } + } + } + + /** + * Represents the features offered by the item. This information helps requestors determine + * what actions are possible with regard to this item (registration, search, join, etc.) + * as well as specific feature types of interest, if any (e.g., for the purpose of feature + * negotiation). + */ + public static class Feature { + + private String variable; + + /** + * Creates a new feature offered by an XMPP entity or item. + * + * @param variable the feature's variable. + */ + public Feature(String variable) { + if (variable == null) + throw new IllegalArgumentException("variable cannot be null"); + this.variable = variable; + } + + /** + * Returns the feature's variable. + * + * @return the feature's variable. + */ + public String getVar() { + return variable; + } + + public String toXML() { + StringBuilder buf = new StringBuilder(); + buf.append("<feature var=\"").append(StringUtils.escapeForXML(variable)).append("\"/>"); + return buf.toString(); + } + + public boolean equals(Object obj) { + if (obj == null) + return false; + if (obj == this) + return true; + if (obj.getClass() != getClass()) + return false; + + DiscoverInfo.Feature other = (DiscoverInfo.Feature) obj; + return variable.equals(other.variable); + } + + @Override + public int hashCode() { + return 37 * variable.hashCode(); + } + } +} |