+ * Examples: + *
+ *localpart@domain.part
domain.part
+ * Examples: + *
+ *xmpp.org
example.net
+ * Examples: + *
+ *domain.part/resource
example.net/8c6def89
+ * Examples: + *
+ *domain.part
domain.part/resourcepart
+ * Examples: + *
+ *localpart@domain.part
user@example.net
+ * Examples: + *
+ *localpart@domain.part/resource
juliet@xmpp.org/balcony
+ * Examples: + *
+ *localpart@domain.part
localpart@domain.part/resourcepart
+ * Examples: + *
+ *localpart@domain.part/resourcepart
domain.part/resourcepart
+ * JIDs are created from {@link String} or {@link CharSequence} with the {@link org.jxmpp.jid.impl.JidCreate} utility. + *
+ * + *+ * Jid jid = JidCreate.from("juliet@capulet.org/balcony"); + * EntityBareJid bareJid = JidCreate.entityBareFrom("romeo@montague.net"); + *+ *
+ * This is the super interface for all JID types, which are constructed from two dimensions: Bare/Full and + * Domain/Entity. Every JID consists at least of a {@link Domainpart}. Bare JID types do not come with a + * {@link Resourcepart}, full JID types always have a {@link Resourcepart}. Domain JID types do not possess a + * {@link Localpart}, whereas entity JID types always do. + *
+ *+ * The following table shows a few examples of JID types. + *
+ *Example | + *Type | + *
example.org |
+ * {@link DomainBareJid} | + *
example.org/resource |
+ * {@link DomainFullJid} | + *
user@example.org |
+ * {@link EntityBareJid} | + *
user@example.org/resource |
+ * {@link EntityFullJid} | + *
+ * You can retrieve the escaped String representing the Jid with {@link #toString()} or the unescaped String of the JID + * with {@link #asUnescapedString()}. + *
+ *+ * The URL encoded representation of a JID is ideal if a JID should be stored as part of a path name, e.g. as file name. + * You can retrieve this information using {@link #asUrlEncodedString()}. The JidCreate API provides methods to create + * JIDs from URL encoded Strings like {@link org.jxmpp.jid.impl.JidCreate#fromUrlEncoded(CharSequence)}. + *
+ * + * + * @see RFC 6120 (XMPP: Core) § 2.1 Global Addresses + * @see RFC 6122 (XMPP: Address Format) § 2.1 + * Fundamentals + */ +public interface Jid extends Comparable+ * Since certain Unicode code points are disallowed in the localpart of a JID by the required stringprep profile, + * those need to get escaped when used in a real JID. The unescaped representation of the JID is only for + * presentation to a human user or for gatewaying to a non-XMPP system. + *
+ * For example, if the users inputs {@code 'at&t guy@example.com'}, the escaped real JID created with + * {@link org.jxmpp.jid.impl.JidCreate} will be {@code 'at\26t\20guy@example.com'}, which is what + * {@link Jid#toString()} will return. But {@link Jid#asUnescapedString()} will return again + * {@code 'at&t guy@example.com'}. + * + * @return the unescaped String representation of this JID. + */ + String asUnescapedString(); + + /** + * Get the URL encoded version of this JID. + * + * @return the URL encoded version of this JID. + * @since 0.7.0 + */ + String asUrlEncodedString(); + + /** + * Check if this is a {@link EntityBareJid} or {@link EntityFullJid}. + * + * @return true if this is an instance of BareJid or FullJid. + */ + boolean isEntityJid(); + + /** + * Check if this is an instance of {@link EntityBareJid}. + * + * @return true if this is an instance of EntityBareJid + */ + boolean isEntityBareJid(); + + /** + * Check if this is an instance of {@link EntityFullJid}. + * + * @return true if this is an instance of EntityFullJid + */ + boolean isEntityFullJid(); + + /** + * Check if this is an instance of {@link DomainBareJid}. + * + * @return true if this is an instance of DomainBareJid + */ + boolean isDomainBareJid(); + + /** + * Check if this is an instance of {@link DomainFullJid}. + * + * @return true if this is an instance of DomainFullJid + */ + boolean isDomainFullJid(); + + /** + * Check if this is an instance of {@link EntityBareJid} or {@link DomainBareJid}. + * + * @return true if this is an instance of BareJid or DomainBareJid + */ + boolean hasNoResource(); + + /** + * Check if this is a Jid with a {@link Resourcepart}. + * + * @return true if this Jid has a resourcepart. + */ + boolean hasResource(); + + /** + * Check if this is a Jid with a {@link Localpart}. + * + * @return true if this Jid has a localpart. + */ + boolean hasLocalpart(); + + /** + * Return a JID created by removing the Resourcepart from this JID. + * + * @return this Jid without a Resourcepart. + */ + BareJid asBareJid(); + + /** + * Convert this Jid to a {@link EntityBareJid} if possible. + * + * @return the corresponding {@link EntityBareJid} or null. + */ + EntityBareJid asEntityBareJidIfPossible(); + + /** + * Convert this Jid to a {@link EntityBareJid} or throw an {@code IllegalStateException} if this is not possible. + * + * @return the corresponding {@link EntityBareJid}. + */ + EntityBareJid asEntityBareJidOrThrow(); + + /** + * Convert this Jid to a {@link EntityFullJid} if possible. + * + * @return the corresponding {@link EntityFullJid} or null. + */ + EntityFullJid asEntityFullJidIfPossible(); + + /** + * Convert this Jid to a {@link EntityFullJid} or throw an {@code IllegalStateException} if this is not possible. + * + * @return the corresponding {@link EntityFullJid}. + */ + EntityFullJid asEntityFullJidOrThrow(); + + /** + * Convert this Jid to a {@link EntityJid} if possible. + * + * @return the corresponding {@link EntityJid} or null. + */ + EntityJid asEntityJidIfPossible(); + + /** + * Convert this Jid to a {@link EntityJid} or throw an {@code IllegalStateException} if this is not possible. + * + * @return the corresponding {@link EntityJid}. + */ + EntityJid asEntityJidOrThrow(); + + /** + * Convert this Jid to a {@link FullJid} if possible. + * + * @return the corresponding {@link FullJid} or null. + */ + FullJid asFullJidIfPossible(); + + /** + * Convert this Jid to a {@link FullJid} or throw an {@code IllegalStateException} if this is not possible. + * + * @return the corresponding {@link FullJid}. + */ + EntityFullJid asFullJidOrThrow(); + + /** + * Convert this Jid to a {@link DomainBareJid}. + *+ * Note that it is always possible to convert a Jid to a DomainBareJid, since every Jid has a domain part. + *
+ * + * @return the corresponding DomainBareJid. + */ + DomainBareJid asDomainBareJid(); + + /** + * Convert this Jid to a {@link DomainFullJid} if possible. + * + * @return the corresponding DomainFullJid or null. + */ + DomainFullJid asDomainFullJidIfPossible(); + + /** + * Convert this Jid to a {@link DomainFullJid} or throw an {@code IllegalStateException} if this is not possible. + * + * @return the corresponding {@link DomainFullJid}. + */ + DomainFullJid asDomainFullJidOrThrow(); + + /** + * Get the resourcepart of this JID or null. + *
+ * If the JID is of form {@code null
is returned.
+ *
+ * If the JID is of form {@code
+ * If the JID is of form {@code
+ * If the JID is of form {@code null
is returned.
+ *
+ * If the JID is of form {@code null
is returned.
+ *
+ * | this JID (parentOf) | other JID | result | + * |---------------------+---------------------+--------| + * | dom.example | dom.example | true | + * | dom.example | dom.example/res | true | + * | dom.example | loc@dom.example | true | + * | dom.example | loc@dom.example/res | true | + * | dom.example/res | dom.exmple | false | + * | dom.example/res | dom.example/res | true | + * | dom.example/res | loc@dom.example | false | + * | dom.example/res | loc@dom.example/res | false | + * | loc@dom.example | dom.example | false | + * | loc@dom.example | dom.example/res | false | + * | loc@dom.example | loc@dom.example | true | + * | loc@dom.example | loc@dom.example/res | true | + * | loc@dom.example/res | dom.example | false | + * | loc@dom.example/res | dom.example/res | false | + * | loc@dom.example/res | loc@dom.example | false | + * | loc@dom.example/res | loc@dom.example/res | true | + *+ * + * @param jid + * the other JID to compare with + * @return true if this JID is a parent of the given JID. + */ + boolean isParentOf(Jid jid); + + /** + * See {@link #isParentOf(Jid)}. + * + * @param bareJid the bare JID. + * @return true if this JID is a parent of the given JID. + */ + boolean isParentOf(EntityBareJid bareJid); + + /** + * See {@link #isParentOf(Jid)}. + * + * @param fullJid the full JID. + * @return true if this JID is a parent of the given JID. + */ + boolean isParentOf(EntityFullJid fullJid); + + /** + * See {@link #isParentOf(Jid)}. + * + * @param domainBareJid the domain bare JID. + * @return true if this JID is a parent of the given JID. + */ + boolean isParentOf(DomainBareJid domainBareJid); + + /** + * See {@link #isParentOf(Jid)}. + * + * @param domainFullJid the domain full JID. + * @return true if this JID is a parent of the given JID. + */ + boolean isParentOf(DomainFullJid domainFullJid); + + /** + * Check if this JID is the strict parent of another JID. In other words, all parts of this JID must + * exist on the other JID, and match this JID's values. Furthermore, and this is what makes this + * method different from {@link #isParentOf(Jid)}, the other JID must have one additional part, + * that this JID does not have. The parent of relation is defined, under the + * precondition that the JID parts (localpart, domainpart and resourcepart) are equal, as follows: + *
+ * | this JID | other JID | result | + * |---------------------+---------------------+--------| + * | dom.example | dom.example | false | (different from isParentOf) + * | dom.example | dom.example/res | true | + * | dom.example | loc@dom.example | true | + * | dom.example | loc@dom.example/res | true | + * | dom.example/res | dom.exmple | false | + * | dom.example/res | dom.example/res | false | (different from isParentOf) + * | dom.example/res | loc@dom.example | false | + * | dom.example/res | loc@dom.example/res | false | + * | loc@dom.example | dom.example | false | + * | loc@dom.example | dom.example/res | false | + * | loc@dom.example | loc@dom.example | false | (different from isParentOf) + * | loc@dom.example | loc@dom.example/res | true | + * | loc@dom.example/res | dom.example | false | + * | loc@dom.example/res | dom.example/res | false | + * | loc@dom.example/res | loc@dom.example | false | + * | loc@dom.example/res | loc@dom.example/res | false | (different from isParentOf) + *+ * + * @param jid + * the other JID to compare with + * @return true if this JID is a parent of the given JID. + */ + boolean isStrictParentOf(Jid jid); + + /** + * See {@link #isParentOf(Jid)}. + * + * @param bareJid the bare JID. + * @return true if this JID is a parent of the given JID. + */ + boolean isStrictParentOf(EntityBareJid bareJid); + + /** + * See {@link #isStrictParentOf(Jid)}. + * + * @param fullJid the full JID. + * @return true if this JID is a parent of the given JID. + */ + boolean isStrictParentOf(EntityFullJid fullJid); + + /** + * See {@link #isStrictParentOf(Jid)}. + * + * @param domainBareJid the domain bare JID. + * @return true if this JID is a parent of the given JID. + */ + boolean isStrictParentOf(DomainBareJid domainBareJid); + + /** + * See {@link #isStrictParentOf(Jid)}. + * + * @param domainFullJid the domain full JID. + * @return true if this JID is a parent of the given JID. + */ + boolean isStrictParentOf(DomainFullJid domainFullJid); + + /** + * Return the downcasted instance of this Jid. This method is unsafe, make sure to check that this is actually of the type of are casting to. + * + * @param jidClass the class of JID to downcast too. + * @param
+ * Returns true if {@code toString().equals(string)}, that is if the String representation of this JID matches the given string. + *
+ * + * @param string the String to compare this JID with. + * @return true if {@code toString().equals(string)}. + */ + @SuppressWarnings("NonOverridingEquals") + boolean equals(String string); + + /** + * Returns the canonical String representation of this JID. See {@link String#intern} for details. + * + * @return the canonical String representation. + */ + String intern(); +} diff --git a/src/org/jxmpp/jid/impl/AbstractJid.java b/src/org/jxmpp/jid/impl/AbstractJid.java new file mode 100644 index 0000000..3efdad4 --- /dev/null +++ b/src/org/jxmpp/jid/impl/AbstractJid.java @@ -0,0 +1,282 @@ +/** + * + * Copyright © 2014-2024 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.jxmpp.jid.impl; + +import org.jxmpp.jid.EntityBareJid; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import org.jxmpp.jid.DomainBareJid; +import org.jxmpp.jid.DomainFullJid; +import org.jxmpp.jid.EntityFullJid; +import org.jxmpp.jid.Jid; +import org.jxmpp.jid.EntityJid; +import org.jxmpp.jid.FullJid; +import org.jxmpp.jid.parts.Localpart; +import org.jxmpp.jid.parts.Resourcepart; + +public abstract class AbstractJid implements Jid { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Cache for the String representation of this JID. + */ + protected String cache; + + @Override + public final boolean isEntityJid() { + return isEntityBareJid() || isEntityFullJid(); + } + + @Override + public final boolean isEntityBareJid() { + return this instanceof EntityBareJid; + } + + @Override + public final boolean isEntityFullJid() { + return this instanceof EntityFullJid; + } + + @Override + public final boolean isDomainBareJid() { + return this instanceof DomainBareJid; + } + + @Override + public final boolean isDomainFullJid() { + return this instanceof DomainFullJid; + } + + @Override + public abstract boolean hasNoResource(); + + @Override + public final boolean hasResource() { + return this instanceof FullJid; + } + + @Override + public final boolean hasLocalpart() { + return this instanceof EntityJid; + } + + @Override + public final+ * Note that this implementation does not require an cache for the unescaped + * string, compared to {@link LocalDomainAndResourcepartJid}. + *
+ * + */ +public final class DomainAndResourcepartJid extends AbstractJid implements DomainFullJid { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private final DomainBareJid domainBareJid; + private final Resourcepart resource; + + DomainAndResourcepartJid(String domain, String resource, JxmppContext context) throws XmppStringprepException { + this(new DomainpartJid(domain, context), Resourcepart.from(resource, context)); + } + + DomainAndResourcepartJid(DomainBareJid domainBareJid, Resourcepart resource) { + this.domainBareJid = requireNonNull(domainBareJid, "The DomainBareJid must not be null"); + this.resource = requireNonNull(resource, "The Resource must not be null"); + } + + @Override + public String toString() { + if (cache != null) { + return cache; + } + cache = domainBareJid.toString() + '/' + resource; + return cache; + } + + @Override + public DomainBareJid asDomainBareJid() { + return domainBareJid; + } + + @Override + public boolean hasNoResource() { + return false; + } + + @Override + public EntityBareJid asEntityBareJidIfPossible() { + return null; + } + + @Override + public EntityFullJid asEntityFullJidIfPossible() { + return null; + } + + @Override + public DomainFullJid asDomainFullJidIfPossible() { + return this; + } + + @Override + public Resourcepart getResourceOrNull() { + return getResourcepart(); + } + + @Override + public boolean isParentOf(EntityBareJid bareJid) { + return false; + } + + @Override + public boolean isParentOf(EntityFullJid fullJid) { + return false; + } + + @Override + public boolean isParentOf(DomainBareJid domainBareJid) { + return false; + } + + @Override + public boolean isParentOf(DomainFullJid domainFullJid) { + return domainBareJid.equals(domainFullJid.getDomain()) && resource.equals(domainFullJid.getResourcepart()); + } + + @Override + public boolean isStrictParentOf(EntityBareJid bareJid) { + return false; + } + + @Override + public boolean isStrictParentOf(EntityFullJid fullJid) { + return false; + } + + @Override + public boolean isStrictParentOf(DomainBareJid domainBareJid) { + return false; + } + + @Override + public boolean isStrictParentOf(DomainFullJid domainFullJid) { + // A DomainFullJid can never be the strict parent of another DomainFullJid. + return false; + } + + @Override + public Resourcepart getResourcepart() { + return resource; + } + + @Override + public BareJid asBareJid() { + return asDomainBareJid(); + } + + @Override + public Domainpart getDomain() { + return domainBareJid.getDomain(); + } + + @Override + public String asUnescapedString() { + return toString(); + } + + @Override + public EntityJid asEntityJidIfPossible() { + return null; + } + + @Override + public FullJid asFullJidIfPossible() { + return this; + } + + @Override + public Localpart getLocalpartOrNull() { + return null; + } +} diff --git a/src/org/jxmpp/jid/impl/DomainpartJid.java b/src/org/jxmpp/jid/impl/DomainpartJid.java new file mode 100644 index 0000000..716a1df --- /dev/null +++ b/src/org/jxmpp/jid/impl/DomainpartJid.java @@ -0,0 +1,162 @@ +/** + * + * Copyright © 2014-2024 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.jxmpp.jid.impl; + +import org.jxmpp.JxmppContext; +import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.DomainBareJid; +import org.jxmpp.jid.DomainFullJid; +import org.jxmpp.jid.EntityFullJid; +import org.jxmpp.jid.EntityJid; +import org.jxmpp.jid.FullJid; +import org.jxmpp.jid.parts.Domainpart; +import org.jxmpp.jid.parts.Localpart; +import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; + +public final class DomainpartJid extends AbstractJid implements DomainBareJid { + + /** + * + */ + private static final long serialVersionUID = 1L; + + final Domainpart domain; + + DomainpartJid(String domain, JxmppContext context) throws XmppStringprepException { + this(Domainpart.from(domain, context)); + } + + DomainpartJid(Domainpart domain) { + this.domain = requireNonNull(domain, "The Domainpart must not be null"); + } + + @Override + public Domainpart getDomain() { + return domain; + } + + @Override + public String toString() { + // Prefer the cached version over the domain.toString() one, since the cached version may + // also be the internalized representation of the String. Which, e.g. provides benefits when + // comparing JIDs. + if (cache != null) { + return cache; + } + cache = domain.toString(); + return cache; + } + + @Override + public String asUnescapedString() { + // No un-escaping necessary for DomainpartJid + return toString(); + } + + @Override + public DomainBareJid asDomainBareJid() { + return this; + } + + @Override + public boolean hasNoResource() { + return true; + } + + @Override + public EntityBareJid asEntityBareJidIfPossible() { + return null; + } + + @Override + public EntityFullJid asEntityFullJidIfPossible() { + return null; + } + + @Override + public DomainFullJid asDomainFullJidIfPossible() { + return null; + } + + @Override + public boolean isParentOf(EntityBareJid bareJid) { + return domain.equals(bareJid.getDomain()); + } + + @Override + public boolean isParentOf(EntityFullJid fullJid) { + return domain.equals(fullJid.getDomain()); + } + + @Override + public boolean isParentOf(DomainBareJid domainBareJid) { + return domain.equals(domainBareJid.getDomain()); + } + + @Override + public boolean isParentOf(DomainFullJid domainFullJid) { + return domain.equals(domainFullJid.getDomain()); + } + + @Override + public boolean isStrictParentOf(EntityBareJid bareJid) { + return isParentOf(bareJid); + } + + @Override + public boolean isStrictParentOf(EntityFullJid fullJid) { + return isParentOf(fullJid); + } + + @Override + public boolean isStrictParentOf(DomainBareJid domainBareJid) { + return false; + } + + @Override + public boolean isStrictParentOf(DomainFullJid domainFullJid) { + return isParentOf(domainFullJid); + } + + @Override + public BareJid asBareJid() { + return this; + } + + @Override + public EntityJid asEntityJidIfPossible() { + return null; + } + + @Override + public FullJid asFullJidIfPossible() { + return null; + } + + @Override + public Resourcepart getResourceOrNull() { + return null; + } + + @Override + public Localpart getLocalpartOrNull() { + return null; + } + +} diff --git a/src/org/jxmpp/jid/impl/JidCreate.java b/src/org/jxmpp/jid/impl/JidCreate.java new file mode 100644 index 0000000..be5f60c --- /dev/null +++ b/src/org/jxmpp/jid/impl/JidCreate.java @@ -0,0 +1,1684 @@ +/** + * + * Copyright © 2014-2022 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.jxmpp.jid.impl; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +import org.jxmpp.JxmppContext; +import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.DomainBareJid; +import org.jxmpp.jid.DomainFullJid; +import org.jxmpp.jid.EntityFullJid; +import org.jxmpp.jid.EntityJid; +import org.jxmpp.jid.FullJid; +import org.jxmpp.jid.Jid; +import org.jxmpp.jid.parts.Domainpart; +import org.jxmpp.jid.parts.Localpart; +import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprep; +import org.jxmpp.stringprep.XmppStringprepException; +import org.jxmpp.util.cache.Cache; +import org.jxmpp.util.cache.LruCache; +import org.jxmpp.util.XmppStringUtils; + +/** + * API to create JIDs (XMPP addresses) from Strings and CharSequences. + *+ * If the input was user generated, e.g. captured from some sort of user + * interface, {@link #fromUnescaped(String)} should be used instead. This allows + * the user to enter unescaped JID values. You can use + * {@link org.jxmpp.jid.util.JidUtil#isValidEntityBareJid(CharSequence)} to + * query, e.g. while the user it entering it, if a given CharSequence is a valid + * bare JID. + *
+ *+ * JIDs created from input received from an XMPP source should use + * {@link #from(String)}. + *
+ *+ * JidCreate uses caches for efficient Jid construction, But it's not guaranteed + * that the same String or CharSequence will yield the same Jid instance. + *
+ * + * @see Jid + */ +public class JidCreate { + + private static class JidStringAndStringprep { + private final String jidString; + private final XmppStringprep stringprep; + + private JidStringAndStringprep(String jidString, JxmppContext context) { + this(jidString, context.xmppStringprep); + } + + private JidStringAndStringprep(String jidString, XmppStringprep stringprep) { + this.jidString = jidString; + this.stringprep = stringprep; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof JidStringAndStringprep)) + return false; + + JidStringAndStringprep otherJidStringAndStringprep = (JidStringAndStringprep) other; + return jidString.equals(otherJidStringAndStringprep.jidString) && stringprep.equals(otherJidStringAndStringprep.stringprep); + } + + private transient Integer hashCode; + + @Override + public int hashCode() { + if (hashCode == null) { + int result = 17; + result = 31 * result + jidString.hashCode(); + result = 31 * result + stringprep.hashCode(); + hashCode = result; + } + return hashCode; + } + } + + private static final Cache+ * Only the domainpart is required. + *
+ * + * @param localpart a optional localpart. + * @param domainpart a required domainpart. + * @param resource a optional resourcepart. + * @return a JID which consists of the given parts. + * @throws XmppStringprepException if an error occurs. + */ + public static Jid from(CharSequence localpart, CharSequence domainpart, CharSequence resource) + throws XmppStringprepException { + return from(localpart.toString(), domainpart.toString(), resource.toString()); + } + + /** + * Get a {@link Jid} from the given parts. + *+ * Only the domainpart is required. + *
+ * + * @param localpart a optional localpart. + * @param domainpart a required domainpart. + * @param resource a optional resourcepart. + * @return a JID which consists of the given parts. + * @throws XmppStringprepException if an error occurs. + */ + public static Jid from(String localpart, String domainpart, String resource) throws XmppStringprepException { + return from(localpart, domainpart, resource, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link Jid} from the given parts. + *+ * Only the domainpart is required. + *
+ * + * @param localpart a optional localpart. + * @param domainpart a required domainpart. + * @param resource a optional resourcepart. + * @param context the JXMPP context. + * @return a JID which consists of the given parts. + * @throws XmppStringprepException if an error occurs. + */ + public static Jid from(String localpart, String domainpart, String resource, JxmppContext context) throws XmppStringprepException { + String jid = XmppStringUtils.completeJidFrom(localpart, domainpart, resource); + return from(localpart, domainpart, resource, jid, context); + } + + /** + * Get a {@link Jid} from the given parts. + + * + * @param localpart a optional localpart. + * @param domainpart a required domainpart. + * @param resource a optional resourcepart. + * @param jidString the raw String of the as parsed. + * @param context the JXMPP context. + * @return a JID which consists of the given parts. + * @throws XmppStringprepException if an error occurs. + */ + private static Jid from(String localpart, String domainpart, String resource, String jidString, JxmppContext context) throws XmppStringprepException { + // Every JID must come with an domainpart. + if (domainpart.isEmpty()) { + throw XmppStringprepException.MissingDomainpart.from(localpart, resource); + } + + // The provided jidString must be equal to the assembled parts. + assert jidString.equals(XmppStringUtils.completeJidFrom(localpart, domainpart, resource)); + + Jid jid; + + JidStringAndStringprep jidStringAndStringprep = null; + if (context.isCachingEnabled()) { + jidStringAndStringprep = new JidStringAndStringprep(jidString, context); + } + if (jidStringAndStringprep != null) { + jid = JID_CACHE.lookup(jidStringAndStringprep); + if (jid != null) { + return jid; + } + } + + jid = null; + if (localpart != null && resource != null) { + jid = new LocalDomainAndResourcepartJid(localpart, domainpart, resource, context); + } else if (localpart != null && resource == null) { + jid = new LocalAndDomainpartJid(localpart, domainpart, context); + } else if (localpart == null && resource == null) { + jid = new DomainpartJid(domainpart, context); + } else if (localpart == null && resource != null) { + jid = new DomainAndResourcepartJid(domainpart, resource, context); + } + assert jid != null; + + if (jidStringAndStringprep != null) { + JID_CACHE.put(jidStringAndStringprep, jid); + } + return jid; + } + + /** + * Like {@link #from(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link Jid} + * @return the {@link Jid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #from(String) + * @since 0.6.2 + */ + public static Jid fromOrThrowUnchecked(CharSequence cs) { + try { + return from(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link Jid} from a CharSequence. + * + * @param jid the input CharSequence. + * @return the Jid represented by the input CharSequence. + * @throws XmppStringprepException if an error occurs. + * @see #from(String) + */ + public static Jid from(CharSequence jid) throws XmppStringprepException { + return from(jid.toString()); + } + + /** + * Get a {@link Jid} from the given String. + * + * @param jidString the input String. + * @return the Jid represented by the input String. + * @throws XmppStringprepException if an error occurs. + * @see #from(CharSequence) + */ + public static Jid from(String jidString) throws XmppStringprepException { + return from(jidString, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link Jid} from the given String. + * + * @param jidString the input String. + * @param context the JXMPP context. + * @return the Jid represented by the input String. + * @throws XmppStringprepException if an error occurs. + * @see #from(CharSequence) + */ + public static Jid from(String jidString, JxmppContext context) throws XmppStringprepException { + String localpart = XmppStringUtils.parseLocalpart(jidString); + String domainpart = XmppStringUtils.parseDomain(jidString); + String resource = XmppStringUtils.parseResource(jidString); + try { + return from(localpart, domainpart, resource, jidString, context); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(jidString, e); + } + } + + /** + * Get a {@link Jid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static Jid fromOrNull(CharSequence cs) { + try { + return from(cs); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Like {@link #fromUnescaped(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link Jid} + * @return the {@link Jid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #fromUnescaped(CharSequence) + * @since 0.6.2 + */ + public static Jid fromUnescapedOrThrowUnchecked(CharSequence cs) { + try { + return fromUnescaped(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link Jid} from the given unescaped CharSequence. + * + * @param unescapedJid an unescaped CharSequence representing a JID. + * @return a JID. + * @throws XmppStringprepException if an error occurs. + */ + public static Jid fromUnescaped(CharSequence unescapedJid) throws XmppStringprepException { + return fromUnescaped(unescapedJid.toString()); + } + + /** + * Get a {@link Jid} from the given unescaped String. + * + * @param unescapedJidString a unescaped String representing a JID. + * @return a JID. + * @throws XmppStringprepException if an error occurs. + */ + public static Jid fromUnescaped(String unescapedJidString) throws XmppStringprepException { + String localpart = XmppStringUtils.parseLocalpart(unescapedJidString); + // Some as from(String), but we escape the localpart + localpart = XmppStringUtils.escapeLocalpart(localpart); + + String domainpart = XmppStringUtils.parseDomain(unescapedJidString); + String resource = XmppStringUtils.parseResource(unescapedJidString); + try { + return from(localpart, domainpart, resource); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(unescapedJidString, e); + } + } + + /** + * Get a {@link Jid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static Jid fromUnescapedOrNull(CharSequence cs) { + try { + return fromUnescaped(cs); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link Jid} from an URL encoded CharSequence. + * + * @param cs a CharSequence representing an URL encoded JID. + * @return a JID + * @throws XmppStringprepException if an error occurs. + * @see URLDecoder + */ + public static Jid fromUrlEncoded(CharSequence cs) throws XmppStringprepException { + String decoded = urlDecode(cs); + return from(decoded); + } + + /** + * Like {@link #bareFrom(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link BareJid} + * @return the {@link BareJid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #bareFrom(CharSequence) + * @since 0.6.2 + */ + public static BareJid bareFromOrThrowUnchecked(CharSequence cs) { + try { + return bareFrom(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link BareJid} representing the given CharSequence. + * + * @param jid the input CharSequence. + * @return a bare JID representing the given CharSequence. + * @throws XmppStringprepException if an error occurs. + */ + public static BareJid bareFrom(CharSequence jid) throws XmppStringprepException { + return bareFrom(jid.toString()); + } + + /** + * Get a {@link BareJid} representing the given String. + * + * @param jid the input String. + * @return a bare JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static BareJid bareFrom(String jid) throws XmppStringprepException { + return bareFrom(jid, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link BareJid} representing the given String. + * + * @param jid the input String. + * @param context the JXMPP context. + * @return a bare JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static BareJid bareFrom(String jid, JxmppContext context) throws XmppStringprepException { + BareJid bareJid; + JidStringAndStringprep jidStringAndStringprep = null; + if (context.isCachingEnabled()) { + jidStringAndStringprep = new JidStringAndStringprep(jid, context); + } + + if (jidStringAndStringprep != null) { + bareJid = BAREJID_CACHE.lookup(jidStringAndStringprep); + if (bareJid != null) { + return bareJid; + } + } + + String localpart = XmppStringUtils.parseLocalpart(jid); + String domainpart = XmppStringUtils.parseDomain(jid); + try { + if (localpart == null || localpart.length() == 0) { + bareJid = new DomainpartJid(domainpart, context); + } else { + bareJid = new LocalAndDomainpartJid(localpart, domainpart, context); + } + } catch (XmppStringprepException e) { + throw new XmppStringprepException(jid, e); + } + + if (jidStringAndStringprep != null) { + BAREJID_CACHE.put(jidStringAndStringprep, bareJid); + } + return bareJid; + } + + /** + * Get a {@link BareJid} constructed from the optionally given {@link Localpart} and {link DomainBareJid}. + * + * @param localpart a optional localpart. + * @param domainBareJid a domain bare JID. + * @return a bare JID. + */ + public static BareJid bareFrom(Localpart localpart, DomainBareJid domainBareJid) { + return bareFrom(localpart, domainBareJid.getDomain()); + } + + /** + * Get a {@link BareJid} constructed from the optionally given {@link Localpart} and {@link Domainpart}. + * + * @param localpart a optional localpart. + * @param domain a domainpart. + * @return a bare JID constructed from the given parts. + */ + public static BareJid bareFrom(Localpart localpart, Domainpart domain) { + if (localpart != null) { + return new LocalAndDomainpartJid(localpart, domain); + } else { + return new DomainpartJid(domain); + } + } + + /** + * Get a {@link BareJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static BareJid bareFromOrNull(CharSequence cs) { + try { + return bareFrom(cs); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link BareJid} from an URL encoded CharSequence. + * + * @param cs a CharSequence representing an URL encoded bare JID. + * @return a bare JID + * @throws XmppStringprepException if an error occurs. + * @see URLDecoder + */ + public static BareJid bareFromUrlEncoded(CharSequence cs) throws XmppStringprepException { + String decoded = urlDecode(cs.toString()); + return bareFrom(decoded); + } + + /** + * Like {@link #fullFrom(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link FullJid} + * @return the {@link FullJid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #fullFrom(CharSequence) + * @since 0.6.2 + */ + public static FullJid fullFromOrThrowUnchecked(CharSequence cs) { + try { + return fullFrom(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link FullJid} representing the given CharSequence. + * + * @param jid a CharSequence representing a JID. + * @return a full JID representing the given CharSequence. + * @throws XmppStringprepException if an error occurs. + */ + public static FullJid fullFrom(CharSequence jid) throws XmppStringprepException { + return fullFrom(jid.toString()); + } + + /** + * Get a {@link FullJid} representing the given String. + * + * @param jid the JID's String. + * @return a full JID representing the input String. + * @throws XmppStringprepException if an error occurs. + */ + public static FullJid fullFrom(String jid) throws XmppStringprepException { + return fullFrom(jid, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link FullJid} representing the given String. + * + * @param jid the JID's String. + * @param context the JXMPP context. + * @return a full JID representing the input String. + * @throws XmppStringprepException if an error occurs. + */ + public static FullJid fullFrom(String jid, JxmppContext context) throws XmppStringprepException { + JidStringAndStringprep jidStringAndStringprep = null; + if (context.isCachingEnabled()) { + jidStringAndStringprep = new JidStringAndStringprep(jid, context); + } + + FullJid fullJid; + if (jidStringAndStringprep != null) { + fullJid = FULLJID_CACHE.lookup(jidStringAndStringprep); + if (fullJid != null) { + return fullJid; + } + } + + String localpart = XmppStringUtils.parseLocalpart(jid); + String domainpart = XmppStringUtils.parseDomain(jid); + String resource = XmppStringUtils.parseResource(jid); + try { + fullJid = fullFrom(localpart, domainpart, resource); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(jid, e); + } + + if (jidStringAndStringprep != null) { + FULLJID_CACHE.put(jidStringAndStringprep, fullJid); + } + + return fullJid; + } + + /** + * Get a {@link FullJid} constructed from the given parts. + * + * @param localpart a optional localpart. + * @param domainpart a domainpart. + * @param resource a resourcepart. + * @return a full JID. + * @throws XmppStringprepException if an error occurs. + */ + public static FullJid fullFrom(String localpart, String domainpart, String resource) throws XmppStringprepException { + return fullFrom(localpart, domainpart, resource, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link FullJid} constructed from the given parts. + * + * @param localpart a optional localpart. + * @param domainpart a domainpart. + * @param resource a resourcepart. + * @param context the JXMPP context. + * @return a full JID. + * @throws XmppStringprepException if an error occurs. + */ + public static FullJid fullFrom(String localpart, String domainpart, String resource, JxmppContext context) throws XmppStringprepException { + FullJid fullJid; + try { + if (localpart == null || localpart.length() == 0) { + fullJid = new DomainAndResourcepartJid(domainpart, resource, context); + } else { + fullJid = new LocalDomainAndResourcepartJid(localpart, domainpart, resource, context); + } + } catch (XmppStringprepException e) { + throw new XmppStringprepException(localpart + '@' + domainpart + '/' + resource, e); + } + return fullJid; + } + + /** + * Get a {@link FullJid} constructed from the given parts. + * + * @param localpart a optional localpart. + * @param domainBareJid a domain bare JID. + * @param resource a resourcepart + * @return a full JID. + */ + public static FullJid fullFrom(Localpart localpart, DomainBareJid domainBareJid, Resourcepart resource) { + return fullFrom(localpart, domainBareJid.getDomain(), resource); + } + + /** + * Get a {@link FullJid} constructed from the given parts. + * + * @param localpart the optional localpart. + * @param domainpart the domainpart. + * @param resource the resourcepart. + * @return a full JID. + */ + public static FullJid fullFrom(Localpart localpart, Domainpart domainpart, Resourcepart resource) { + return fullFrom(entityBareFrom(localpart, domainpart), resource); + } + + /** + * Get a {@link FullJid} constructed from a {@link BareJid} and a {@link Resourcepart}. + * + * @param bareJid a entity bare JID. + * @param resource a resourcepart. + * @return a full JID. + */ + public static FullJid fullFrom(BareJid bareJid, Resourcepart resource) { + if (bareJid.isEntityBareJid()) { + EntityBareJid entityBareJid = (EntityBareJid) bareJid; + return new LocalDomainAndResourcepartJid(entityBareJid, resource); + } else { + DomainBareJid domainBareJid = (DomainBareJid) bareJid; + return new DomainAndResourcepartJid(domainBareJid, resource); + } + } + + /** + * Get a {@link FullJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static FullJid fullFromOrNull(CharSequence cs) { + try { + return fullFrom(cs); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link FullJid} from an URL encoded CharSequence. + * + * @param cs a CharSequence representing an URL encoded full JID. + * @return a full JID + * @throws XmppStringprepException if an error occurs. + * @see URLDecoder + */ + public static FullJid fullFromUrlEncoded(CharSequence cs) throws XmppStringprepException { + String decoded = urlDecode(cs); + return fullFrom(decoded); + } + + /** + * Like {@link #entityFrom(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link EntityJid} + * @return the {@link EntityJid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #entityFrom(CharSequence) + * @since 0.6.2 + */ + public static EntityJid entityFromOrThrowUnchecked(CharSequence cs) { + try { + return entityFrom(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link EntityJid} representing the given String. + * + * @param jid the JID's string. + * @return an entity JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityJid entityFrom(CharSequence jid) throws XmppStringprepException { + return entityFrom(jid.toString()); + } + + /** + * Get a {@link EntityJid} representing the given String. + * + * @param jidString the JID's string. + * @return an entity JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityJid entityFrom(String jidString) throws XmppStringprepException { + return entityFrom(jidString, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link EntityJid} representing the given String. + * + * @param jidString the JID's string. + * @param context the JXMPP context. + * @return an entity JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityJid entityFrom(String jidString, JxmppContext context) throws XmppStringprepException { + return entityFrom(jidString, false, context); + } + + /** + * Like {@link #entityFromUnescaped(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link EntityJid} + * @return the {@link EntityJid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #entityFromUnescaped(CharSequence) + * @since 0.6.2 + */ + public static EntityJid entityFromUnescapedOrThrowUnchecked(CharSequence cs) { + return entityFromUnescapedOrThrowUnchecked(cs, JxmppContext.getDefaultContext()); + } + + /** + * Like {@link #entityFromUnescaped(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link EntityJid} + * @param context the JXMPP context. + * @return the {@link EntityJid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #entityFromUnescaped(CharSequence) + * @since 0.6.2 + */ + public static EntityJid entityFromUnescapedOrThrowUnchecked(CharSequence cs, JxmppContext context) { + try { + return entityFromUnescaped(cs, context); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link EntityJid} representing the given String. + * + * @param jid the JID. + * @return an entity JID representing the given input.. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityJid entityFromUnescaped(CharSequence jid) throws XmppStringprepException { + return entityFromUnescaped(jid, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link EntityJid} representing the given String. + * + * @param jid the JID. + * @param context the JXMPP context. + * @return an entity JID representing the given input. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityJid entityFromUnescaped(CharSequence jid, JxmppContext context) throws XmppStringprepException { + return entityFromUnescaped(jid.toString(), context); + } + + /** + * Get a {@link EntityJid} representing the given String. + * + * @param jidString the JID's string. + * @return an entity JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityJid entityFromUnescaped(String jidString) throws XmppStringprepException { + return entityFromUnescaped(jidString, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link EntityJid} representing the given String. + * + * @param jidString the JID's string. + * @param context the JXMPP context. + * @return an entity JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityJid entityFromUnescaped(String jidString, JxmppContext context) throws XmppStringprepException { + return entityFrom(jidString, true, context); + } + + /** + * Get a {@link EntityJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + * @deprecated use {@link #entityFromUnescapedOrNull(CharSequence)} instead. + */ + // TODO: remove in jxmpp 1.1 + @Deprecated + public static EntityJid entityFromUnesacpedOrNull(CharSequence cs) { + return entityFromUnescapedOrNull(cs); + } + + /** + * Get a {@link EntityJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static EntityJid entityFromUnescapedOrNull(CharSequence cs) { + try { + return entityFromUnescaped(cs.toString()); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link EntityJid} representing the given String. + * + * @param jidString the JID's string. + * @param unescaped if the JID string is unescaped. + * @return an entity JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + private static EntityJid entityFrom(String jidString, boolean unescaped, JxmppContext context) throws XmppStringprepException { + JidStringAndStringprep jidStringAndStringprep = null; + if (context.isCachingEnabled()) { + jidStringAndStringprep = new JidStringAndStringprep(jidString, context); + } + + EntityJid entityJid; + if (jidStringAndStringprep != null) { + entityJid = ENTITYJID_CACHE.lookup(jidStringAndStringprep); + if (entityJid != null) { + return entityJid; + } + } + String localpartString = XmppStringUtils.parseLocalpart(jidString); + if (localpartString == null) { + throw new XmppStringprepException("Does not contain a localpart", jidString); + } + Localpart localpart; + try { + if (unescaped) { + localpart = Localpart.fromUnescaped(localpartString); + } else { + localpart = Localpart.from(localpartString); + } + } catch (XmppStringprepException e) { + throw new XmppStringprepException(jidString, e); + } + + String domainpartString = XmppStringUtils.parseDomain(jidString); + Domainpart domainpart; + try { + domainpart = Domainpart.from(domainpartString); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(jidString, e); + } + + String resourceString = XmppStringUtils.parseResource(jidString); + if (resourceString != null) { + Resourcepart resourcepart; + try { + resourcepart = Resourcepart.from(resourceString); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(jidString, e); + } + entityJid = entityFullFrom(localpart, domainpart, resourcepart); + } else { + entityJid = entityBareFrom(localpart, domainpart); + } + + if (jidStringAndStringprep != null) { + ENTITYJID_CACHE.put(jidStringAndStringprep, entityJid); + } + return entityJid; + } + + /** + * Get a {@link EntityJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static EntityJid entityFromOrNull(CharSequence cs) { + try { + return entityFrom(cs); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link EntityJid} from an URL encoded CharSequence. + * + * @param cs a CharSequence representing an URL encoded entity JID. + * @return an entity JID + * @throws XmppStringprepException if an error occurs. + * @see URLDecoder + */ + public static EntityJid entityFromUrlEncoded(CharSequence cs) throws XmppStringprepException { + String decoded = urlDecode(cs); + return entityFrom(decoded); + } + + /** + * Like {@link #entityBareFrom(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link EntityBareJid} + * @return the {@link EntityBareJid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #entityBareFrom(CharSequence) + * @since 0.6.2 + */ + public static EntityBareJid entityBareFromOrThrowUnchecked(CharSequence cs) { + try { + return entityBareFrom(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link EntityBareJid} representing the given CharSequence. + * + * @param jid the input CharSequence. + * @return a bare JID representing the given CharSequence. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityBareJid entityBareFrom(CharSequence jid) throws XmppStringprepException { + return entityBareFrom(jid.toString()); + } + + /** + * Get a {@link EntityBareJid} representing the given String. + * + * @param jid the input String. + * @return a bare JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityBareJid entityBareFrom(String jid) throws XmppStringprepException { + return entityBareFrom(jid, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link EntityBareJid} representing the given String. + * + * @param jid the input String. + * @param context the JXMPP context. + * @return a bare JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityBareJid entityBareFrom(String jid, JxmppContext context) throws XmppStringprepException { + JidStringAndStringprep jidStringAndStringprep = null; + if (context.isCachingEnabled()) { + jidStringAndStringprep = new JidStringAndStringprep(jid, context); + } + + EntityBareJid bareJid; + if (jidStringAndStringprep != null) { + bareJid = ENTITY_BAREJID_CACHE.lookup(jidStringAndStringprep); + if (bareJid != null) { + return bareJid; + } + } + + String localpart = XmppStringUtils.parseLocalpart(jid); + String domainpart = XmppStringUtils.parseDomain(jid); + try { + bareJid = new LocalAndDomainpartJid(localpart, domainpart, context); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(jid, e); + } + + if (jidStringAndStringprep != null) { + ENTITY_BAREJID_CACHE.put(jidStringAndStringprep, bareJid); + } + return bareJid; + } + + /** + * Like {@link #entityBareFromUnescaped(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link EntityBareJid} + * @return the {@link EntityBareJid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #entityBareFromUnescaped(CharSequence) + * @since 0.6.2 + */ + public static EntityBareJid entityBareFromUnescapedOrThrowUnchecked(CharSequence cs) { + try { + return entityBareFromUnescaped(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link EntityBareJid} representing the given unescaped CharSequence. + * + * @param unescapedJid the input CharSequence. + * @return a bare JID representing the given CharSequence. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityBareJid entityBareFromUnescaped(CharSequence unescapedJid) throws XmppStringprepException { + return entityBareFromUnescaped(unescapedJid.toString()); + } + + /** + * Get a {@link EntityBareJid} representing the given unescaped String. + * + * @param unescapedJidString the input String. + * @return a bare JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityBareJid entityBareFromUnescaped(String unescapedJidString) throws XmppStringprepException { + return entityBareFromUnescaped(unescapedJidString, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link EntityBareJid} representing the given unescaped String. + * + * @param unescapedJidString the input String. + * @param context the JXMPP context. + * @return a bare JID representing the given String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityBareJid entityBareFromUnescaped(String unescapedJidString, JxmppContext context) throws XmppStringprepException { + JidStringAndStringprep jidStringAndStringprep = null; + if (context.isCachingEnabled()) { + jidStringAndStringprep = new JidStringAndStringprep(unescapedJidString, context); + } + + EntityBareJid bareJid; + if (jidStringAndStringprep != null) { + bareJid = ENTITY_BAREJID_CACHE.lookup(jidStringAndStringprep); + if (bareJid != null) { + return bareJid; + } + } + + String localpart = XmppStringUtils.parseLocalpart(unescapedJidString); + // Some as from(String), but we escape the localpart + localpart = XmppStringUtils.escapeLocalpart(localpart); + + String domainpart = XmppStringUtils.parseDomain(unescapedJidString); + try { + bareJid = new LocalAndDomainpartJid(localpart, domainpart, context); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(unescapedJidString, e); + } + + if (jidStringAndStringprep != null) { + ENTITY_BAREJID_CACHE.put(jidStringAndStringprep, bareJid); + } + + return bareJid; + } + + /** + * Get a {@link EntityBareJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static EntityBareJid entityBareFromUnescapedOrNull(CharSequence cs) { + try { + return entityBareFromUnescaped(cs.toString()); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link EntityBareJid} constructed from the given {@link Localpart} and {link DomainBareJid}. + * + * @param localpart a localpart. + * @param domainBareJid a domain bare JID. + * @return a bare JID. + */ + public static EntityBareJid entityBareFrom(Localpart localpart, DomainBareJid domainBareJid) { + return entityBareFrom(localpart, domainBareJid.getDomain()); + } + + /** + * Get a {@link EntityBareJid} constructed from the given {@link Localpart} and {@link Domainpart}. + * + * @param localpart a localpart. + * @param domain a domainpart. + * @return a bare JID constructed from the given parts. + */ + public static EntityBareJid entityBareFrom(Localpart localpart, Domainpart domain) { + return new LocalAndDomainpartJid(localpart, domain); + } + + /** + * Get a {@link EntityBareJid} constructed from the given {@link Localpart} and {@link Domainpart}. + * + * @param localpart a localpart. + * @param domain a domainpart. + * @return a bare JID constructed from the given parts. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityBareJid entityBareFrom(CharSequence localpart, Domainpart domain) throws XmppStringprepException { + return new LocalAndDomainpartJid(Localpart.fromUnescaped(localpart), domain); + } + + /** + * Get a {@link EntityBareJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static EntityBareJid entityBareFromOrNull(CharSequence cs) { + try { + return entityBareFrom(cs); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link EntityBareJid} from an URL encoded CharSequence. + * + * @param cs a CharSequence representing an URL encoded entity bare JID. + * @return an entity bare JID + * @throws XmppStringprepException if an error occurs. + * @see URLDecoder + */ + public static EntityBareJid entityBareFromUrlEncoded(CharSequence cs) throws XmppStringprepException { + String decoded = urlDecode(cs); + return entityBareFrom(decoded); + } + + /** + * Like {@link #entityFullFrom(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link EntityFullJid} + * @return the {@link EntityFullJid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #entityFullFrom(CharSequence) + * @since 0.6.2 + */ + public static EntityFullJid entityFullFromOrThrowUnchecked(CharSequence cs) { + try { + return entityFullFrom(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link EntityFullJid} representing the given CharSequence. + * + * @param jid a CharSequence representing a JID. + * @return a full JID representing the given CharSequence. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityFullJid entityFullFrom(CharSequence jid) throws XmppStringprepException { + return entityFullFrom(jid.toString(), JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link EntityFullJid} representing the given CharSequence. + * + * @param jid a CharSequence representing a JID. + * @return a full JID representing the given CharSequence. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityFullJid entityFullFrom(String jid) throws XmppStringprepException { + return entityFullFrom(jid, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link EntityFullJid} representing the given String. + * + * @param jid the JID's String. + * @param context the JXMPP context. + * @return a full JID representing the input String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityFullJid entityFullFrom(String jid, JxmppContext context) throws XmppStringprepException { + JidStringAndStringprep jidStringAndStringprep = null; + if (context.isCachingEnabled()) { + jidStringAndStringprep = new JidStringAndStringprep(jid, context); + } + + EntityFullJid fullJid; + if (jidStringAndStringprep != null) { + fullJid = ENTITY_FULLJID_CACHE.lookup(jidStringAndStringprep); + if (fullJid != null) { + return fullJid; + } + } + + String localpart = XmppStringUtils.parseLocalpart(jid); + String domainpart = XmppStringUtils.parseDomain(jid); + String resource = XmppStringUtils.parseResource(jid); + try { + fullJid = entityFullFrom(localpart, domainpart, resource); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(jid, e); + } + + if (jidStringAndStringprep != null) { + ENTITY_FULLJID_CACHE.put(jidStringAndStringprep, fullJid); + } + + return fullJid; + } + + /** + * Get a {@link EntityFullJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static EntityFullJid entityFullFromOrNull(CharSequence cs) { + try { + return entityFullFrom(cs); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Like {@link #entityFullFromUnescaped(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link EntityFullJid} + * @return the {@link EntityFullJid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #entityFullFromUnescaped(CharSequence) + * @since 0.6.2 + */ + public static EntityFullJid entityFullFromUnescapedOrThrowUnchecked(CharSequence cs) { + try { + return entityFullFromUnescaped(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link EntityFullJid} representing the given unescaped CharSequence. + * + * @param unescapedJid a CharSequence representing a JID. + * @return a full JID representing the given CharSequence. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityFullJid entityFullFromUnescaped(CharSequence unescapedJid) throws XmppStringprepException { + return entityFullFromUnescaped(unescapedJid.toString()); + } + + /** + * Get a {@link EntityFullJid} representing the given unescaped String. + * + * @param unescapedJidString the JID's String. + * @return a full JID representing the input String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityFullJid entityFullFromUnescaped(String unescapedJidString) throws XmppStringprepException { + return entityFullFromUnescaped(unescapedJidString, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link EntityFullJid} representing the given unescaped String. + * + * @param unescapedJidString the JID's String. + * @param context the JXMPP context. + * @return a full JID representing the input String. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityFullJid entityFullFromUnescaped(String unescapedJidString, JxmppContext context) throws XmppStringprepException { + JidStringAndStringprep jidStringAndStringprep = null; + if (context.isCachingEnabled()) { + jidStringAndStringprep = new JidStringAndStringprep(unescapedJidString, context); + } + + EntityFullJid fullJid; + if (jidStringAndStringprep != null) { + fullJid = ENTITY_FULLJID_CACHE.lookup(jidStringAndStringprep); + if (fullJid != null) { + return fullJid; + } + } + + String localpart = XmppStringUtils.parseLocalpart(unescapedJidString); + // Some as from(String), but we escape the localpart + localpart = XmppStringUtils.escapeLocalpart(localpart); + + String domainpart = XmppStringUtils.parseDomain(unescapedJidString); + String resource = XmppStringUtils.parseResource(unescapedJidString); + try { + fullJid = new LocalDomainAndResourcepartJid(localpart, domainpart, resource, context); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(unescapedJidString, e); + } + + if (jidStringAndStringprep != null) { + ENTITY_FULLJID_CACHE.put(jidStringAndStringprep, fullJid); + } + + return fullJid; + } + + /** + * Get a {@link EntityFullJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static EntityFullJid entityFullFromUnescapedOrNull(CharSequence cs) { + try { + return entityFullFromUnescaped(cs.toString()); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link EntityFullJid} constructed from the given parts. + * + * @param localpart a localpart. + * @param domainpart a domainpart. + * @param resource a resourcepart. + * @return a full JID. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityFullJid entityFullFrom(String localpart, String domainpart, String resource) throws XmppStringprepException { + return entityFullFrom(localpart, domainpart, resource, JxmppContext.getDefaultContext()); + } + + /** + * Get a {@link EntityFullJid} constructed from the given parts. + * + * @param localpart a localpart. + * @param domainpart a domainpart. + * @param resource a resourcepart. + * @param context the JXMPP context. + * @return a full JID. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityFullJid entityFullFrom(String localpart, String domainpart, String resource, JxmppContext context) throws XmppStringprepException { + EntityFullJid fullJid; + try { + fullJid = new LocalDomainAndResourcepartJid(localpart, domainpart, resource, context); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(localpart + '@' + domainpart + '/' + resource, e); + } + return fullJid; + } + + /** + * Get a {@link EntityFullJid} constructed from the given parts. + * + * @param localpart a localpart. + * @param domainBareJid a domain bare JID.. + * @param resource a resourcepart + * @return a full JID. + */ + public static EntityFullJid entityFullFrom(Localpart localpart, DomainBareJid domainBareJid, Resourcepart resource) { + return entityFullFrom(localpart, domainBareJid.getDomain(), resource); + } + + /** + * Get a {@link EntityFullJid} constructed from the given parts. + * + * @param localpart the localpart. + * @param domainpart the domainpart. + * @param resource the resourcepart. + * @return a full JID. + */ + public static EntityFullJid entityFullFrom(Localpart localpart, Domainpart domainpart, Resourcepart resource) { + return entityFullFrom(entityBareFrom(localpart, domainpart), resource); + } + + /** + * Get a {@link EntityFullJid} constructed from the given parts. + * + * @param localpart the localpart. + * @param domainpart the domainpart. + * @param resource the resourcepart. + * @return a full JID. + * @throws XmppStringprepException if an error occurs. + */ + public static EntityFullJid entityFullFrom(CharSequence localpart, Domainpart domainpart, CharSequence resource) throws XmppStringprepException { + return entityFullFrom(entityBareFrom(Localpart.fromUnescaped(localpart), domainpart), Resourcepart.from(resource)); + } + + /** + * Get a {@link EntityFullJid} constructed from a {@link EntityBareJid} and a {@link Resourcepart}. + * + * @param bareJid a bare JID. + * @param resource a resourcepart. + * @return a full JID. + */ + public static EntityFullJid entityFullFrom(EntityBareJid bareJid, Resourcepart resource) { + return new LocalDomainAndResourcepartJid(bareJid, resource); + } + + /** + * Get a {@link EntityFullJid} from an URL encoded CharSequence. + * + * @param cs a CharSequence representing an URL encoded entity full JID. + * @return an entity full JID + * @throws XmppStringprepException if an error occurs. + * @see URLDecoder + */ + public static EntityFullJid entityFullFromUrlEncoded(CharSequence cs) throws XmppStringprepException { + String decoded = urlDecode(cs); + return entityFullFrom(decoded); + } + + /** + * Like {@link #domainBareFrom(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link EntityFullJid} + * @return the {@link EntityFullJid} if no exception occurs + * @see #from(String) + * @since 0.6.2 + */ + public static DomainBareJid domainBareFromOrThrowUnchecked(CharSequence cs) { + try { + return domainBareFrom(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a domain bare JID. + * + * @param jid the JID CharSequence. + * @return a domain bare JID. + * @throws XmppStringprepException if an error occurs. + */ + public static DomainBareJid domainBareFrom(CharSequence jid) throws XmppStringprepException { + return domainBareFrom(jid.toString()); + } + + /** + * Get a domain bare JID. + * + * @param jid the JID String. + * @return a domain bare JID. + * @throws XmppStringprepException if an error occurs. + */ + public static DomainBareJid domainBareFrom(String jid) throws XmppStringprepException { + return domainBareFrom(jid, JxmppContext.getDefaultContext()); + } + + /** + * Get a domain bare JID. + * + * @param jid the JID String. + * @param context the JXMPP context. + * @return a domain bare JID. + * @throws XmppStringprepException if an error occurs. + */ + public static DomainBareJid domainBareFrom(String jid, JxmppContext context) throws XmppStringprepException { + JidStringAndStringprep jidStringAndStringprep = null; + if (context.isCachingEnabled()) { + jidStringAndStringprep = new JidStringAndStringprep(jid, context); + } + + DomainBareJid domainJid; + if (jidStringAndStringprep != null) { + domainJid = DOMAINJID_CACHE.lookup(jidStringAndStringprep); + if (domainJid != null) { + return domainJid; + } + } + + String domain = XmppStringUtils.parseDomain(jid); + try { + domainJid = new DomainpartJid(domain, context); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(jid, e); + } + + if (context.isCachingEnabled()) { + DOMAINJID_CACHE.put(jidStringAndStringprep, domainJid); + } + return domainJid; + } + + /** + * Get a {@link DomainBareJid} consisting of the given {@link Domainpart}. + * + * @param domainpart the domainpart. + * @return a domain bare JID. + */ + public static DomainBareJid domainBareFrom(Domainpart domainpart) { + return new DomainpartJid(domainpart); + } + + /** + * Get a {@link DomainBareJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static DomainBareJid domainBareFromOrNull(CharSequence cs) { + try { + return domainBareFrom(cs); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link DomainBareJid} from an URL encoded CharSequence. + * + * @param cs a CharSequence representing an URL encoded domain bare JID. + * @return a domain bare JID + * @throws XmppStringprepException if an error occurs. + * @see URLDecoder + */ + public static DomainBareJid domainBareFromUrlEncoded(CharSequence cs) throws XmppStringprepException { + String decode = urlDecode(cs); + return domainBareFrom(decode); + } + + /** + * Like {@link #domainFullFrom(CharSequence)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link DomainFullJid} + * @return the {@link DomainFullJid} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid JID + * @see #domainFullFrom(CharSequence) + * @since 0.6.2 + */ + public static DomainFullJid domainFullFromOrThrowUnchecked(CharSequence cs) { + try { + return domainFullFrom(cs); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a domain full JID from the given CharSequence. + * + * @param jid the JID. + * @return a domain full JID. + * @throws XmppStringprepException if an error happens. + */ + public static DomainFullJid domainFullFrom(CharSequence jid) throws XmppStringprepException { + return domainFullFrom(jid.toString()); + } + + /** + * Get a domain full JID from the given String. + * + * @param jid the JID. + * @return a DomainFullJid. + * @throws XmppStringprepException if an error happens. + */ + public static DomainFullJid domainFullFrom(String jid) throws XmppStringprepException { + return domainFullFrom(jid, JxmppContext.getDefaultContext()); + } + + /** + * Get a domain full JID from the given String. + * + * @param jid the JID. + * @param context the JXMPP context. + * @return a DomainFullJid. + * @throws XmppStringprepException if an error happens. + */ + public static DomainFullJid domainFullFrom(String jid, JxmppContext context) throws XmppStringprepException { + JidStringAndStringprep jidStringAndStringprep = null; + if (context.isCachingEnabled()) { + jidStringAndStringprep = new JidStringAndStringprep(jid, context); + } + + DomainFullJid domainResourceJid; + if (jidStringAndStringprep != null) { + domainResourceJid = DOMAINRESOURCEJID_CACHE.lookup(jidStringAndStringprep); + if (domainResourceJid != null) { + return domainResourceJid; + } + } + + String domain = XmppStringUtils.parseDomain(jid); + String resource = XmppStringUtils.parseResource(jid); + try { + domainResourceJid = new DomainAndResourcepartJid(domain, resource, context); + } catch (XmppStringprepException e) { + throw new XmppStringprepException(jid, e); + } + + if (jidStringAndStringprep != null) { + DOMAINRESOURCEJID_CACHE.put(jidStringAndStringprep, domainResourceJid); + } + + return domainResourceJid; + } + + /** + * Get a domain full JID. + * + * @param domainpart the domainpart. + * @param resource the resourcepart. + * @return a domain full JID. + */ + public static DomainFullJid domainFullFrom(Domainpart domainpart, Resourcepart resource) { + return domainFullFrom(domainBareFrom(domainpart), resource); + } + + /** + * Get a domain full JID. + * + * @param domainBareJid a domain bare JID. + * @param resource a resourcepart. + * @return a domain full JID. + */ + public static DomainFullJid domainFullFrom(DomainBareJid domainBareJid, Resourcepart resource) { + return new DomainAndResourcepartJid(domainBareJid, resource); + } + + /** + * Get a {@link DomainFullJid} from a given {@link CharSequence} or {@code null} if the input does not represent a JID. + * + * @param cs the input {@link CharSequence} + * @return a JID or {@code null} + */ + public static DomainFullJid domainFullFromOrNull(CharSequence cs) { + try { + return domainFullFrom(cs); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link DomainFullJid} from an URL encoded CharSequence. + * + * @param cs a CharSequence representing an URL encoded domain full JID. + * @return a domain full JID + * @throws XmppStringprepException if an error occurs. + * @see URLDecoder + */ + public static DomainFullJid domainFullFromUrlEncoded(CharSequence cs) throws XmppStringprepException { + String decoded = urlDecode(cs); + return domainFullFrom(decoded); + } + + private static String urlDecode(CharSequence cs) { + try { + return URLDecoder.decode(cs.toString(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } + } +} diff --git a/src/org/jxmpp/jid/impl/LocalAndDomainpartJid.java b/src/org/jxmpp/jid/impl/LocalAndDomainpartJid.java new file mode 100644 index 0000000..845b6d7 --- /dev/null +++ b/src/org/jxmpp/jid/impl/LocalAndDomainpartJid.java @@ -0,0 +1,182 @@ +/** + * + * Copyright © 2014-2024 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.jxmpp.jid.impl; + +import org.jxmpp.JxmppContext; +import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.DomainBareJid; +import org.jxmpp.jid.DomainFullJid; +import org.jxmpp.jid.EntityFullJid; +import org.jxmpp.jid.EntityJid; +import org.jxmpp.jid.FullJid; +import org.jxmpp.jid.parts.Domainpart; +import org.jxmpp.jid.parts.Localpart; +import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; + + +public final class LocalAndDomainpartJid extends AbstractJid implements EntityBareJid { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private final DomainBareJid domainBareJid; + private final Localpart localpart; + + private transient String unescapedCache; + + LocalAndDomainpartJid(String localpart, String domain, JxmppContext context) throws XmppStringprepException { + domainBareJid = new DomainpartJid(domain, context); + this.localpart = Localpart.from(localpart, context); + } + + LocalAndDomainpartJid(Localpart localpart, Domainpart domain) { + this.localpart = requireNonNull(localpart, "The Localpart must not be null"); + this.domainBareJid = new DomainpartJid(domain); + } + + @Override + public Localpart getLocalpart() { + return localpart; + } + + @Override + public String toString() { + if (cache != null) { + return cache; + } + cache = getLocalpart().toString() + '@' + domainBareJid.toString(); + return cache; + } + + @Override + public String asUnescapedString() { + if (unescapedCache != null) { + return unescapedCache; + } + unescapedCache = getLocalpart().asUnescapedString() + '@' + domainBareJid.toString(); + return unescapedCache; + } + + @Override + public EntityBareJid asEntityBareJidIfPossible() { + return this; + } + + @Override + public EntityFullJid asEntityFullJidIfPossible() { + return null; + } + + @Override + public DomainFullJid asDomainFullJidIfPossible() { + return null; + } + + @Override + public boolean isParentOf(EntityBareJid bareJid) { + return domainBareJid.equals(bareJid.getDomain()) && localpart.equals(bareJid.getLocalpart()); + } + + @Override + public boolean isParentOf(EntityFullJid fullJid) { + return isParentOf(fullJid.asBareJid()); + } + + @Override + public boolean isParentOf(DomainBareJid domainBareJid) { + return false; + } + + @Override + public boolean isParentOf(DomainFullJid domainFullJid) { + return false; + } + + @Override + public boolean isStrictParentOf(EntityBareJid bareJid) { + return false; + } + + @Override + public boolean isStrictParentOf(EntityFullJid fullJid) { + return isParentOf(fullJid); + } + + @Override + public boolean isStrictParentOf(DomainBareJid domainBareJid) { + return false; + } + + @Override + public boolean isStrictParentOf(DomainFullJid domainFullJid) { + return false; + } + + @Override + public DomainBareJid asDomainBareJid() { + return domainBareJid; + } + + @Override + public Domainpart getDomain() { + return domainBareJid.getDomain(); + } + + @Override + public BareJid asBareJid() { + return this; + } + + @Override + public boolean hasNoResource() { + return true; + } + + @Override + public EntityJid asEntityJidIfPossible() { + return this; + } + + @Override + public FullJid asFullJidIfPossible() { + return null; + } + + @Override + public EntityBareJid asEntityBareJid() { + return this; + } + + @Override + public Resourcepart getResourceOrNull() { + return null; + } + + @Override + public Localpart getLocalpartOrNull() { + return getLocalpart(); + } + + @Override + public String asEntityBareJidString() { + return toString(); + } +} diff --git a/src/org/jxmpp/jid/impl/LocalDomainAndResourcepartJid.java b/src/org/jxmpp/jid/impl/LocalDomainAndResourcepartJid.java new file mode 100644 index 0000000..4f6be3b --- /dev/null +++ b/src/org/jxmpp/jid/impl/LocalDomainAndResourcepartJid.java @@ -0,0 +1,185 @@ +/** + * + * Copyright © 2014-2024 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.jxmpp.jid.impl; + +import org.jxmpp.JxmppContext; +import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.DomainBareJid; +import org.jxmpp.jid.DomainFullJid; +import org.jxmpp.jid.EntityFullJid; +import org.jxmpp.jid.EntityJid; +import org.jxmpp.jid.FullJid; +import org.jxmpp.jid.parts.Domainpart; +import org.jxmpp.jid.parts.Localpart; +import org.jxmpp.jid.parts.Resourcepart; +import org.jxmpp.stringprep.XmppStringprepException; + +public final class LocalDomainAndResourcepartJid extends AbstractJid implements EntityFullJid { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private final EntityBareJid bareJid; + private final Resourcepart resource; + + private String unescapedCache; + + LocalDomainAndResourcepartJid(String localpart, String domain, String resource, JxmppContext context) throws XmppStringprepException { + this(new LocalAndDomainpartJid(localpart, domain, context), Resourcepart.from(resource, context)); + } + + LocalDomainAndResourcepartJid(EntityBareJid bareJid, Resourcepart resource) { + this.bareJid = requireNonNull(bareJid, "The EntityBareJid must not be null"); + this.resource = requireNonNull(resource, "The Resourcepart must not be null"); + } + + @Override + public String toString() { + if (cache != null) { + return cache; + } + cache = bareJid.toString() + '/' + resource; + return cache; + } + + @Override + public String asUnescapedString() { + if (unescapedCache != null) { + return unescapedCache; + } + unescapedCache = bareJid.asUnescapedString() + '/' + resource; + return unescapedCache; + } + + @Override + public EntityBareJid asEntityBareJid() { + return bareJid; + } + + @Override + public String asEntityBareJidString() { + return asEntityBareJid().toString(); + } + + @Override + public boolean hasNoResource() { + return false; + } + + @Override + public EntityBareJid asEntityBareJidIfPossible() { + return asEntityBareJid(); + } + + @Override + public EntityFullJid asEntityFullJidIfPossible() { + return this; + } + + @Override + public DomainFullJid asDomainFullJidIfPossible() { + return null; + } + + @Override + public Localpart getLocalpartOrNull() { + return bareJid.getLocalpart(); + } + + @Override + public Resourcepart getResourceOrNull() { + return getResourcepart(); + } + + @Override + public boolean isParentOf(EntityBareJid bareJid) { + return false; + } + + @Override + public boolean isParentOf(EntityFullJid fullJid) { + return this.equals(fullJid); + } + + @Override + public boolean isParentOf(DomainBareJid domainBareJid) { + return false; + } + + @Override + public boolean isParentOf(DomainFullJid domainFullJid) { + return false; + } + + @Override + public boolean isStrictParentOf(EntityBareJid bareJid) { + return false; + } + + @Override + public boolean isStrictParentOf(EntityFullJid fullJid) { + return false; + } + + @Override + public boolean isStrictParentOf(DomainBareJid domainBareJid) { + return false; + } + + @Override + public boolean isStrictParentOf(DomainFullJid domainFullJid) { + return false; + } + + @Override + public DomainBareJid asDomainBareJid() { + return bareJid.asDomainBareJid(); + } + + @Override + public Resourcepart getResourcepart() { + return resource; + } + + @Override + public BareJid asBareJid() { + return asEntityBareJid(); + } + + @Override + public Domainpart getDomain() { + return bareJid.getDomain(); + } + + @Override + public Localpart getLocalpart() { + return bareJid.getLocalpart(); + } + + @Override + public EntityJid asEntityJidIfPossible() { + return this; + } + + @Override + public FullJid asFullJidIfPossible() { + return this; + } +} diff --git a/src/org/jxmpp/jid/impl/package-info.java b/src/org/jxmpp/jid/impl/package-info.java new file mode 100644 index 0000000..bf824ed --- /dev/null +++ b/src/org/jxmpp/jid/impl/package-info.java @@ -0,0 +1,21 @@ +/** + * + * Copyright 2015 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. + */ + +/** + * JXMPP's implementation of {@link org.jxmpp.jid.Jid} and it's subtypes. + */ +package org.jxmpp.jid.impl; diff --git a/src/org/jxmpp/jid/package-info.java b/src/org/jxmpp/jid/package-info.java new file mode 100644 index 0000000..ee10d57 --- /dev/null +++ b/src/org/jxmpp/jid/package-info.java @@ -0,0 +1,21 @@ +/** + * + * Copyright 2015 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. + */ + +/** + * Interfaces and classes for XMPP Addresses (JIDs). + */ +package org.jxmpp.jid; diff --git a/src/org/jxmpp/jid/parts/Domainpart.java b/src/org/jxmpp/jid/parts/Domainpart.java new file mode 100644 index 0000000..832a533 --- /dev/null +++ b/src/org/jxmpp/jid/parts/Domainpart.java @@ -0,0 +1,108 @@ +/** + * + * Copyright © 2014-2018 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.jxmpp.jid.parts; + +import org.jxmpp.JxmppContext; +import org.jxmpp.stringprep.XmppStringPrepUtil; +import org.jxmpp.stringprep.XmppStringprepException; + +/** + * A domainpart of an XMPP address (JID). + *+ * You can create instances of this class from Strings using {@link #from(String)}. + *
+ * + * @see RFC 6122 § 2.2. Domainpart + */ +public class Domainpart extends Part { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private Domainpart(String domain) { + super(domain); + } + + /** + * Get a {@link Domainpart} from a given {@link CharSequence} or {@code null} if the input is not a valid domainpart. + * + * @param cs the input CharSequence + * @return a Domainpart or {@code null} + */ + public static Domainpart fromOrNull(CharSequence cs) { + try { + return from(cs.toString()); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Like {@link #from(String)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link Domainpart} + * @return the {@link Domainpart} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid {@link Domainpart} + * @see #from(String) + * @since 0.6.2 + */ + public static Domainpart fromOrThrowUnchecked(CharSequence cs) { + try { + return from(cs.toString()); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get the {@link Domainpart} representing the input String. + * + * @param domain the input String. + * @return the domainpart. + * @throws XmppStringprepException if an error occurs. + */ + public static Domainpart from(String domain) throws XmppStringprepException { + return from(domain, JxmppContext.getDefaultContext()); + } + + /** + * Get the {@link Domainpart} representing the input String. + * + * @param domain the input String. + * @param context the JXMPP context. + * @return the domainpart. + * @throws XmppStringprepException if an error occurs. + */ + public static Domainpart from(String domain, JxmppContext context) throws XmppStringprepException { + if (domain == null) { + throw new XmppStringprepException(domain, "Input 'domain' must not be null"); + } + // TODO cache + // RFC 6122 § 2.2 "If the domainpart includes a final character considered to be a label + // separator (dot) by [IDNA2003] or [DNS], this character MUST be stripped …" + if (domain.length() > 0 && domain.charAt(domain.length() - 1) == '.') { + domain = domain.substring(0, domain.length() - 1); + } + domain = XmppStringPrepUtil.domainprep(domain, context); + // First prep the String, then assure the limits of the *result* + assertNotLongerThan1023BytesOrEmpty(domain); + return new Domainpart(domain); + } +} diff --git a/src/org/jxmpp/jid/parts/Localpart.java b/src/org/jxmpp/jid/parts/Localpart.java new file mode 100644 index 0000000..90a283e --- /dev/null +++ b/src/org/jxmpp/jid/parts/Localpart.java @@ -0,0 +1,183 @@ +/** + * + * Copyright © 2014-2018 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.jxmpp.jid.parts; + +import org.jxmpp.JxmppContext; +import org.jxmpp.stringprep.XmppStringPrepUtil; +import org.jxmpp.stringprep.XmppStringprepException; +import org.jxmpp.util.XmppStringUtils; + +/** + * A localpart of an XMPP address (JID). The localpart is the part before the + * first @ sign in an XMPP address and usually identifies the user (or the XMPP + * entity) within an XMPP service. It is also often referred to as "username", + * but note that the actual username used to login may be different from the + * resulting localpart of the user's JID. + *+ * You can create instances of this class from Strings using {@link #from(String)}. + *
+ * + * @see RFC + * 6122 § 2.3. Localpart + */ +public class Localpart extends Part { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private transient String unescapedCache; + + private Localpart(String localpart) { + super(localpart); + } + + /** + * Return the unescaped String representation of this Localpart. + *+ * Since certain Unicode code points are disallowed in the localpart of a JID by the required stringprep profile, + * those need to get escaped when used in a real JID. The unescaped representation of the JID is only for + * presentation to a human user or for gatewaying to a non-XMPP system. + *
+ * + * @return the unescaped String representation of this JID. + * @see org.jxmpp.jid.Jid#asUnescapedString() + * @since 0.6.1 + */ + public String asUnescapedString() { + if (unescapedCache != null) { + return unescapedCache; + } + unescapedCache = XmppStringUtils.unescapeLocalpart(toString()); + return unescapedCache; + } + + /** + * Like {@link #from(String)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link Localpart} + * @return the {@link Localpart} if no exception occurs + * @throws IllegalArgumentException if the given input is not a valid {@link Localpart} + * @see #from(String) + * @since 0.6.2 + */ + public static Localpart fromOrThrowUnchecked(CharSequence cs) { + try { + return from(cs.toString()); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Like {@link #fromUnescaped(String)} but does throw an unchecked {@link IllegalArgumentException} instead of a + * {@link XmppStringprepException}. + * + * @param cs the character sequence which should be transformed to a {@link Localpart} + * @return the {@link Localpart} if no exception occurs + * @see #from(String) + * @since 0.6.2 + */ + public static Localpart fromUnescapedOrThrowUnchecked(CharSequence cs) { + try { + return fromUnescaped(cs.toString()); + } catch (XmppStringprepException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Get a {@link Localpart} from a given {@link CharSequence} or {@code null} if the input is not a valid localpart. + * + * @param cs the input CharSequence + * @return a Localpart or {@code null} + */ + public static Localpart formUnescapedOrNull(CharSequence cs) { + try { + return fromUnescaped(cs); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get a {@link Localpart} from an unescaped String. + * + * @param unescapedLocalpart an unescaped String representing a Localpart. + * @return a Localpart + * @throws XmppStringprepException if an error occurs. + * @since 0.6.2 + */ + public static Localpart fromUnescaped(String unescapedLocalpart) throws XmppStringprepException { + String escapedLocalpartString = XmppStringUtils.escapeLocalpart(unescapedLocalpart); + return from(escapedLocalpartString); + } + + /** + * Get a {@link Localpart} from an unescaped CharSequence. + * + * @param unescapedLocalpart an unescaped CharSequence representing a Localpart. + * @return a Localpart + * @throws XmppStringprepException if an error occurs. + * @since 0.6.2 + */ + public static Localpart fromUnescaped(CharSequence unescapedLocalpart) throws XmppStringprepException { + return fromUnescaped(unescapedLocalpart.toString()); + } + + /** + * Get a {@link Localpart} from a given {@link CharSequence} or {@code null} if the input is not a valid localpart. + * + * @param cs the input CharSequence + * @return a Localpart or {@code null} + */ + public static Localpart fromOrNull(CharSequence cs) { + try { + return from(cs.toString()); + } catch (XmppStringprepException e) { + return null; + } + } + + /** + * Get the {@link Localpart} representing the input String. + * + * @param localpart the input String. + * @return the localpart. + * @throws XmppStringprepException if an error occurs. + */ + public static Localpart from(String localpart) throws XmppStringprepException { + return from(localpart, JxmppContext.getDefaultContext()); + } + + /** + * Get the {@link Localpart} representing the input String. + * + * @param localpart the input String. + * @param context the JXMPP context. + * @return the localpart. + * @throws XmppStringprepException if an error occurs. + */ + public static Localpart from(String localpart, JxmppContext context) throws XmppStringprepException { + localpart = XmppStringPrepUtil.localprep(localpart, context); + // First prep the String, then assure the limits of the *result* + assertNotLongerThan1023BytesOrEmpty(localpart); + return new Localpart(localpart); + } +} diff --git a/src/org/jxmpp/jid/parts/Part.java b/src/org/jxmpp/jid/parts/Part.java new file mode 100644 index 0000000..b2c3ad3 --- /dev/null +++ b/src/org/jxmpp/jid/parts/Part.java @@ -0,0 +1,102 @@ +/** + * + * Copyright © 2014-2019 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.jxmpp.jid.parts; + +import java.io.Serializable; +import java.nio.charset.StandardCharsets; + +import org.jxmpp.stringprep.XmppStringprepException; + +public abstract class Part implements CharSequence, Serializable { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private final String part; + + protected Part(String part) { + this.part = part; + } + + @Override + public final int length() { + return part.length(); + } + + @Override + public final char charAt(int index) { + return part.charAt(index); + } + + @Override + public final CharSequence subSequence(int start, int end) { + return part.subSequence(start, end); + } + + @Override + public final String toString() { + return part; + } + + @Override + public final boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null) { + return false; + } + return part.equals(other.toString()); + } + + @Override + public final int hashCode() { + return part.hashCode(); + } + + protected static void assertNotLongerThan1023BytesOrEmpty(String string) throws XmppStringprepException { + byte[] bytes = string.getBytes(StandardCharsets.UTF_8); + + // Better throw XmppStringprepException instead of IllegalArgumentException here, because users don't expect an + // IAE and it also makes the error handling for users easier. + if (bytes.length > 1023) { + throw new XmppStringprepException(string, "Given string is longer then 1023 bytes"); + } else if (bytes.length == 0) { + throw new XmppStringprepException(string, "Argument can't be the empty string"); + } + } + + /** + * The cache holding the internalized value of this part. This needs to be transient so that the + * cache is recreated once the data was de-serialized. + */ + private transient String internalizedCache; + + /** + * Returns the canonical String representation of this Part. See {@link String#intern} for details. + * + * @return the canonical String representation. + */ + public final String intern() { + if (internalizedCache == null) { + internalizedCache = toString().intern(); + } + return internalizedCache; + } +} diff --git a/src/org/jxmpp/jid/parts/Resourcepart.java b/src/org/jxmpp/jid/parts/Resourcepart.java new file mode 100644 index 0000000..39be445 --- /dev/null +++ b/src/org/jxmpp/jid/parts/Resourcepart.java @@ -0,0 +1,119 @@ +/** + * + * Copyright © 2014-2024 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.jxmpp.jid.parts; + +import org.jxmpp.JxmppContext; +import org.jxmpp.stringprep.XmppStringPrepUtil; +import org.jxmpp.stringprep.XmppStringprepException; + +/** + * A resourcepart of an XMPP address (JID). + *+ * You can create instances of this class from Strings using {@link #from(String)}. + *
+ * + * @see RFC 6122 § 2.4. Resourcepart + */ +public class Resourcepart extends Part { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * The empty resource part. + *
+ * This empty resource part is the part that is represented by the empty String. This is useful in cases where you
+ * have a collection of Resourceparts that does not allow null
values, but you want to deal with the
+ * "no resource" case.
+ *
+ * For more information about the different verification methods see {@link #validateEntityBareJid(CharSequence)}. + *
+ * + * @param jid the CharSequence to check. + * @return true if {@code jid} represents a valid entity bare JID, false otherwise + * @see #isValidEntityBareJid(CharSequence) + * @see EntityBareJid + */ + public static boolean isTypicalValidEntityBareJid(CharSequence jid) { + try { + validateTypicalEntityBareJid(jid); + } catch (NotAEntityBareJidStringException | XmppStringprepException e) { + return false; + } + return true; + } + + /** + * Check if the given CharSequence is a typical and valid entity bare JID. This method does perform the same + * check as {@link #isValidEntityBareJid(CharSequence)} and additionally verifies that the domainpart of the JID + * contains at least one dot ('.') character. + *
+ * The …TypicalValidEntityBareJid(CharSequence)
methods are useful if you expect your users to always
+ * enter a FQDN as domainpart. Whereas isValidEntityBareJid(CharSequence)
and
+ * validateEntityBareJid
accept also inputs like "foo@example", the "is typical JID" methods require
+ * the domainpart to contain a dot, e.g. "foo@example.org".
+ *
+ * This method is meant to validate user input and give fast feedback (e.g. + * with a red or green light) about if the user entered CharSequence + * represents a bare JID. + *
+ * + * @param jid + * the CharSequence to check. + * @return true if {@code jid} represents a valid entity bare JID, false otherwise + * @see EntityBareJid + */ + public static boolean isValidEntityBareJid(CharSequence jid) { + try { + validateEntityBareJid(jid); + } catch (NotAEntityBareJidStringException | XmppStringprepException e) { + return false; + } + return true; + } + + /** + * Check if the given CharSequence is a valid entity bare JID. That + * is, it must consists exactly of a local- and a domainpart + * (<localpart@domainpart>). + *+ * This is a convenience method meant to validate user entered bare JIDs. If + * the given {@code jid} is not a valid bare JID, then this method will + * throw either {@link NotAEntityBareJidStringException} or + * {@link XmppStringprepException}. The NotABareJidStringException will + * contain a meaningful message explaining why the given CharSequence is not a + * valid bare JID (e.g. "does not contain a '@' character"). + *
+ * + * @param jidcs the JID CharSequence + * @return a BareJid instance representing the given JID CharSequence + * @throws NotAEntityBareJidStringException if the given CharSequence is not a bare JID. + * @throws XmppStringprepException if an error happens. + */ + public static EntityBareJid validateEntityBareJid(CharSequence jidcs) throws NotAEntityBareJidStringException, XmppStringprepException { + String jid = jidcs.toString(); + final int atIndex = jid.indexOf('@'); + if (atIndex == -1) { + throw new NotAEntityBareJidStringException("'" + jid + "' does not contain a '@' character"); + } else if (jid.indexOf('@', atIndex + 1) != -1) { + throw new NotAEntityBareJidStringException("'" + jid + "' contains multiple '@' characters"); + } + final String localpart = XmppStringUtils.parseLocalpart(jid); + if (localpart == null || localpart.length() == 0) { + throw new NotAEntityBareJidStringException("'" + jid + "' has empty localpart"); + } + final String domainpart = XmppStringUtils.parseDomain(jid); + if (domainpart == null || domainpart.length() == 0) { + throw new NotAEntityBareJidStringException("'" + jid + "' has empty domainpart"); + } + return JidCreate.entityBareFromUnescaped(jid); + } + + public static class NotAEntityBareJidStringException extends Exception { + /** + * + */ + private static final long serialVersionUID = -1710386661031655082L; + + /** + * Construct a new "not a entity bare JID" exception. + * + * @param message the message of the exception. + */ + public NotAEntityBareJidStringException(String message) { + super(message); + } + } + + /** + * Filter all entity bare JIDs. + * + * @param in the input collection. + * @param out the collection where the filtered JIDs are added to. + */ + public static void filterEntityBareJid(Collection extends Jid> in, Collection super EntityBareJid> out) { + for (Jid jid : in) { + EntityBareJid bareJid = jid.asEntityBareJidIfPossible(); + if (bareJid != null) { + out.add(bareJid); + } + } + } + + /** + * Filter all entity bare JIDs. + * + * @param input the input collection. + * @return a set containing all bare JIDs of the input collection. + */ + public static Set
+ * If the optional argument exceptions
is given, then all {@link XmppStringprepException} thrown while
+ * converting will be added to the list. Otherwise, if an XmppStringprepExceptions is thrown, it will be wrapped in
+ * a AssertionError Exception and throw.
+ *
+ * Note that errors while converting the Strings will be silently ignored. + *
+ * + * @param jids a array of JID Strings. + * @return a set of JIDs. + */ + public static Set
+ * If the optional argument exceptions
is given, then all {@link XmppStringprepException} thrown while
+ * converting will be added to the list. Otherwise, if an XmppStringprepExceptions is thrown, it will be wrapped in
+ * a AssertionError Exception and throw.
+ *
null
values into consideration. Which means that this method will return true
if both JIDs are null
.
+ *
+ * @param jidOne The first JID to compare.
+ * @param jidTwo The second JID to compare.
+ * @return true
if both JIDs are equals.
+ * @since 0.7.0
+ */
+ public static boolean equals(Jid jidOne, Jid jidTwo) {
+ if (jidOne != null) {
+ return jidOne.equals(jidTwo);
+ }
+
+ return jidTwo == null;
+ }
+}
diff --git a/src/org/jxmpp/jid/util/package-info.java b/src/org/jxmpp/jid/util/package-info.java
new file mode 100644
index 0000000..f33d57f
--- /dev/null
+++ b/src/org/jxmpp/jid/util/package-info.java
@@ -0,0 +1,21 @@
+/**
+ *
+ * Copyright 2015 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.
+ */
+
+/**
+ * Utility classes for JIDs.
+ */
+package org.jxmpp.jid.util;