Jid.java

  1package eu.siacs.conversations.xmpp;
  2
  3import androidx.annotation.NonNull;
  4import com.google.common.base.CharMatcher;
  5import im.conversations.android.xmpp.model.stanza.Stanza;
  6import java.io.Serializable;
  7import org.jxmpp.jid.impl.JidCreate;
  8import org.jxmpp.jid.parts.Domainpart;
  9import org.jxmpp.jid.parts.Localpart;
 10import org.jxmpp.jid.parts.Resourcepart;
 11import org.jxmpp.stringprep.XmppStringprepException;
 12
 13public abstract class Jid implements Comparable<Jid>, Serializable, CharSequence {
 14
 15    public static Jid of(
 16            final CharSequence local, final CharSequence domain, final CharSequence resource) {
 17        if (local == null) {
 18            if (resource == null) {
 19                return ofDomain(domain);
 20            } else {
 21                return ofDomainAndResource(domain, resource);
 22            }
 23        }
 24        if (resource == null) {
 25            return ofLocalAndDomain(local, domain);
 26        }
 27        try {
 28            return new InternalRepresentation(
 29                    JidCreate.entityFullFrom(
 30                            Localpart.from(local.toString()),
 31                            Domainpart.from(domain.toString()),
 32                            Resourcepart.from(resource.toString())));
 33        } catch (final XmppStringprepException e) {
 34            throw new IllegalArgumentException(e);
 35        }
 36    }
 37
 38    public static Jid ofDomain(final CharSequence domain) {
 39        try {
 40            return new InternalRepresentation(JidCreate.domainBareFrom(domain));
 41        } catch (final XmppStringprepException e) {
 42            throw new IllegalArgumentException(e);
 43        }
 44    }
 45
 46    public static Jid ofLocalAndDomain(final CharSequence local, final CharSequence domain) {
 47        try {
 48            return new InternalRepresentation(
 49                    JidCreate.bareFrom(
 50                            Localpart.from(local.toString()), Domainpart.from(domain.toString())));
 51        } catch (final XmppStringprepException e) {
 52            throw new IllegalArgumentException(e);
 53        }
 54    }
 55
 56    public static Jid ofDomainAndResource(CharSequence domain, CharSequence resource) {
 57        try {
 58            return new InternalRepresentation(
 59                    JidCreate.domainFullFrom(
 60                            Domainpart.from(domain.toString()),
 61                            Resourcepart.from(resource.toString())));
 62        } catch (final XmppStringprepException e) {
 63            throw new IllegalArgumentException(e);
 64        }
 65    }
 66
 67    public static Jid of(final CharSequence input) {
 68        if (input instanceof Jid jid) {
 69            return jid;
 70        }
 71        try {
 72            return new InternalRepresentation(JidCreate.from(input));
 73        } catch (final XmppStringprepException e) {
 74            throw new IllegalArgumentException(e);
 75        }
 76    }
 77
 78    public static Jid ofUserInput(final CharSequence input) {
 79        final var jid = of(input);
 80        if (CharMatcher.is('@').matchesAnyOf(jid.getDomain())) {
 81            throw new IllegalArgumentException("Domain should not contain @");
 82        }
 83        return jid;
 84    }
 85
 86    public static Jid ofOrInvalid(final String input) {
 87        return ofOrInvalid(input, false);
 88    }
 89
 90    /**
 91     * @param jid a string representation of the jid to parse
 92     * @param fallback indicates whether an attempt should be made to parse a bare version of the
 93     *     jid
 94     * @return an instance of Jid; may be Jid.Invalid
 95     */
 96    public static Jid ofOrInvalid(final String jid, final boolean fallback) {
 97        try {
 98            return Jid.of(jid);
 99        } catch (final IllegalArgumentException e) {
100            return Jid.invalidOf(jid, fallback);
101        }
102    }
103
104    private static Jid invalidOf(final String jid, boolean fallback) {
105        final int pos = jid.indexOf('/');
106        if (fallback && pos >= 0 && jid.length() >= pos + 1) {
107            if (jid.substring(pos + 1).trim().isEmpty()) {
108                return Jid.of(jid.substring(0, pos));
109            }
110        }
111        return new Invalid(jid);
112    }
113
114    public abstract boolean isFullJid();
115
116    public abstract boolean isBareJid();
117
118    public abstract boolean isDomainJid();
119
120    public abstract Jid asBareJid();
121
122    public abstract Jid withResource(CharSequence resource);
123
124    public abstract String getLocal();
125
126    public abstract Jid getDomain();
127
128    public abstract String getResource();
129
130    private static class InternalRepresentation extends Jid {
131        private final org.jxmpp.jid.Jid inner;
132
133        private InternalRepresentation(final org.jxmpp.jid.Jid inner) {
134            this.inner = inner;
135        }
136
137        @Override
138        public boolean isFullJid() {
139            return inner.isEntityFullJid() || inner.isDomainFullJid();
140        }
141
142        @Override
143        public boolean isBareJid() {
144            return inner.isDomainBareJid() || inner.isEntityBareJid();
145        }
146
147        @Override
148        public boolean isDomainJid() {
149            return inner.isDomainBareJid() || inner.isDomainFullJid();
150        }
151
152        @Override
153        public Jid asBareJid() {
154            return new InternalRepresentation(inner.asBareJid());
155        }
156
157        @Override
158        public Jid withResource(CharSequence resource) {
159            final Localpart localpart = inner.getLocalpartOrNull();
160            try {
161                final Resourcepart resourcepart = Resourcepart.from(resource.toString());
162                if (localpart == null) {
163                    return new InternalRepresentation(
164                            JidCreate.domainFullFrom(inner.getDomain(), resourcepart));
165                } else {
166                    return new InternalRepresentation(
167                            JidCreate.fullFrom(localpart, inner.getDomain(), resourcepart));
168                }
169            } catch (XmppStringprepException e) {
170                throw new IllegalArgumentException(e);
171            }
172        }
173
174        @Override
175        public String getLocal() {
176            final Localpart localpart = inner.getLocalpartOrNull();
177            return localpart == null ? null : localpart.toString();
178        }
179
180        @Override
181        public Jid getDomain() {
182            return new InternalRepresentation(inner.asDomainBareJid());
183        }
184
185        @Override
186        public String getResource() {
187            final Resourcepart resourcepart = inner.getResourceOrNull();
188            return resourcepart == null ? null : resourcepart.toString();
189        }
190
191        @NonNull
192        @Override
193        public String toString() {
194            return inner.toString();
195        }
196
197        @Override
198        public int length() {
199            return inner.length();
200        }
201
202        @Override
203        public char charAt(int i) {
204            return inner.charAt(i);
205        }
206
207        @NonNull
208        @Override
209        public CharSequence subSequence(int i, int i1) {
210            return inner.subSequence(i, i1);
211        }
212
213        @Override
214        public int compareTo(Jid jid) {
215            if (jid instanceof InternalRepresentation) {
216                return inner.compareTo(((InternalRepresentation) jid).inner);
217            } else {
218                return 0;
219            }
220        }
221
222        @Override
223        public boolean equals(Object o) {
224            if (this == o) return true;
225            if (o == null || getClass() != o.getClass()) return false;
226            InternalRepresentation that = (InternalRepresentation) o;
227            return inner.equals(that.inner);
228        }
229
230        @Override
231        public int hashCode() {
232            return inner.hashCode();
233        }
234    }
235
236    public static class Invalid extends Jid {
237
238        private final String value;
239
240        private Invalid(final String jid) {
241            this.value = jid;
242        }
243
244        @Override
245        @NonNull
246        public String toString() {
247            return value;
248        }
249
250        @Override
251        public boolean isFullJid() {
252            throw new AssertionError("Not implemented");
253        }
254
255        @Override
256        public boolean isBareJid() {
257            throw new AssertionError("Not implemented");
258        }
259
260        @Override
261        public boolean isDomainJid() {
262            throw new AssertionError("Not implemented");
263        }
264
265        @Override
266        public Jid asBareJid() {
267            throw new AssertionError("Not implemented");
268        }
269
270        @Override
271        public Jid withResource(CharSequence charSequence) {
272            throw new AssertionError("Not implemented");
273        }
274
275        @Override
276        public String getLocal() {
277            throw new AssertionError("Not implemented");
278        }
279
280        @Override
281        public Jid getDomain() {
282            throw new AssertionError("Not implemented");
283        }
284
285        @Override
286        public String getResource() {
287            throw new AssertionError("Not implemented");
288        }
289
290        @Override
291        public int length() {
292            return value.length();
293        }
294
295        @Override
296        public char charAt(int index) {
297            return value.charAt(index);
298        }
299
300        @NonNull
301        @Override
302        public CharSequence subSequence(int start, int end) {
303            return value.subSequence(start, end);
304        }
305
306        @Override
307        public int compareTo(@NonNull Jid o) {
308            throw new AssertionError("Not implemented");
309        }
310
311        public static Jid getNullForInvalid(final Jid jid) {
312            if (jid instanceof Invalid) {
313                return null;
314            } else {
315                return jid;
316            }
317        }
318
319        public static boolean isValid(Jid jid) {
320            return !(jid instanceof Invalid);
321        }
322
323        public static boolean hasValidFrom(final Stanza stanza) {
324            final String from = stanza.getAttribute("from");
325            if (from == null) {
326                return false;
327            }
328            try {
329                Jid.of(from);
330                return true;
331            } catch (final IllegalArgumentException e) {
332                return false;
333            }
334        }
335    }
336}