001/**
002 *
003 * Copyright © 2014-2024 Florian Schmaus
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jxmpp.jid.impl;
018
019import org.jxmpp.jid.EntityBareJid;
020
021import java.io.UnsupportedEncodingException;
022import java.net.URLEncoder;
023
024import org.jxmpp.jid.DomainBareJid;
025import org.jxmpp.jid.DomainFullJid;
026import org.jxmpp.jid.EntityFullJid;
027import org.jxmpp.jid.Jid;
028import org.jxmpp.jid.EntityJid;
029import org.jxmpp.jid.FullJid;
030import org.jxmpp.jid.parts.Localpart;
031import org.jxmpp.jid.parts.Resourcepart;
032
033public abstract class AbstractJid implements Jid {
034
035        /**
036         *
037         */
038        private static final long serialVersionUID = 1L;
039
040        /**
041         * Cache for the String representation of this JID.
042         */
043        protected String cache;
044
045        @Override
046        public final boolean isEntityJid() {
047                return isEntityBareJid() || isEntityFullJid();
048        }
049
050        @Override
051        public final boolean isEntityBareJid() {
052                return this instanceof EntityBareJid;
053        }
054
055        @Override
056        public final boolean isEntityFullJid() {
057                return this instanceof EntityFullJid;
058        }
059
060        @Override
061        public final boolean isDomainBareJid() {
062                return this instanceof DomainBareJid;
063        }
064
065        @Override
066        public final boolean isDomainFullJid() {
067                return this instanceof DomainFullJid;
068        }
069
070        @Override
071        public abstract boolean hasNoResource();
072
073        @Override
074        public final boolean hasResource() {
075                return this instanceof FullJid;
076        }
077
078        @Override
079        public final boolean hasLocalpart() {
080                return this instanceof EntityJid;
081        }
082
083        @Override
084        public final <T extends Jid> T downcast(Class<T> jidClass) {
085                return jidClass.cast(this);
086        }
087
088        @Override
089        public int length() {
090                return toString().length();
091        }
092
093        @Override
094        public char charAt(int index) {
095                return toString().charAt(index);
096        }
097
098        @Override
099        public CharSequence subSequence(int start, int end) {
100                return toString().subSequence(start, end);
101        }
102
103        @Override
104        public final EntityBareJid asEntityBareJidOrThrow() {
105                EntityBareJid entityBareJid = asEntityBareJidIfPossible();
106                if (entityBareJid == null) throwIse("can not be converted to EntityBareJid");
107                return entityBareJid;
108        }
109
110        @Override
111        public EntityFullJid asEntityFullJidOrThrow() {
112                EntityFullJid entityFullJid = asEntityFullJidIfPossible();
113                if (entityFullJid == null) throwIse("can not be converted to EntityFullJid");
114                return entityFullJid;
115        }
116
117        @Override
118        public EntityJid asEntityJidOrThrow() {
119                EntityJid entityJid = asEntityJidIfPossible();
120                if (entityJid == null) throwIse("can not be converted to EntityJid");
121                return entityJid;
122        }
123
124        @Override
125        public EntityFullJid asFullJidOrThrow() {
126                EntityFullJid entityFullJid = asEntityFullJidIfPossible();
127                if (entityFullJid == null) throwIse("can not be converted to EntityBareJid");
128                return entityFullJid;
129        }
130
131        @Override
132        public DomainFullJid asDomainFullJidOrThrow() {
133                DomainFullJid domainFullJid = asDomainFullJidIfPossible();
134                if (domainFullJid == null) throwIse("can not be converted to DomainFullJid");
135                return domainFullJid;
136        }
137
138        @Override
139        public abstract Resourcepart getResourceOrNull();
140
141        @Override
142        public final Resourcepart getResourceOrEmpty() {
143                Resourcepart resourcepart = getResourceOrNull();
144                if (resourcepart == null) return Resourcepart.EMPTY;
145                return resourcepart;
146        }
147
148        @Override
149        public final Resourcepart getResourceOrThrow() {
150                Resourcepart resourcepart = getResourceOrNull();
151                if (resourcepart == null) throwIse("has no resourcepart");
152                return resourcepart;
153        }
154
155        @Override
156        public abstract Localpart getLocalpartOrNull();
157
158        @Override
159        public final Localpart getLocalpartOrThrow() {
160                Localpart localpart = getLocalpartOrNull();
161                if (localpart == null) throwIse("has no localpart");
162                return localpart;
163        }
164
165        @Override
166        public final boolean isParentOf(Jid jid) {
167                EntityFullJid fullJid = jid.asEntityFullJidIfPossible();
168                if (fullJid != null) {
169                        return isParentOf(fullJid);
170                }
171                EntityBareJid bareJid = jid.asEntityBareJidIfPossible();
172                if (bareJid != null) {
173                        return isParentOf(bareJid);
174                }
175                DomainFullJid domainFullJid = jid.asDomainFullJidIfPossible();
176                if (domainFullJid != null) {
177                        return isParentOf(domainFullJid);
178                }
179
180                return isParentOf(jid.asDomainBareJid());
181        }
182
183        @Override
184        public final boolean isStrictParentOf(Jid jid) {
185                EntityFullJid fullJid = jid.asEntityFullJidIfPossible();
186                if (fullJid != null) {
187                        return isStrictParentOf(fullJid);
188                }
189                EntityBareJid bareJid = jid.asEntityBareJidIfPossible();
190                if (bareJid != null) {
191                        return isStrictParentOf(bareJid);
192                }
193                DomainFullJid domainFullJid = jid.asDomainFullJidIfPossible();
194                if (domainFullJid != null) {
195                        return isStrictParentOf(domainFullJid);
196                }
197
198                return isStrictParentOf(jid.asDomainBareJid());
199        }
200
201        @Override
202        public final int hashCode() {
203                return toString().hashCode();
204        }
205
206        @Override
207        public final boolean equals(Object other) {
208                if (other == null) {
209                        return false;
210                }
211                if (this == other) {
212                        return true;
213                }
214                if (other instanceof CharSequence) {
215                        return equals((CharSequence) other);
216                }
217                return false;
218        }
219
220        @SuppressWarnings("NonOverridingEquals")
221        @Override
222        public final boolean equals(CharSequence charSequence) {
223                if (charSequence == null) {
224                        return false;
225                }
226                return equals(charSequence.toString());
227        }
228
229        @SuppressWarnings("NonOverridingEquals")
230        @Override
231        public final boolean equals(String string) {
232                return toString().equals(string);
233        }
234
235        @Override
236        public final int compareTo(Jid  other) {
237                String otherString = other.toString();
238                String myString = toString();
239                return myString.compareTo(otherString);
240        }
241
242        /**
243         * The cache holding the internalized value of this part. This needs to be transient so that the
244         * cache is recreated once the data was de-serialized.
245         */
246        private transient String internalizedCache;
247
248        @Override
249        public final String intern() {
250                if (internalizedCache == null) {
251                        cache = internalizedCache = toString().intern();
252                }
253                return internalizedCache;
254        }
255
256        private transient String urlEncodedCache;
257
258        @Override
259        public final String asUrlEncodedString() {
260                if (urlEncodedCache == null) {
261                        String string = toString();
262                        try {
263                                urlEncodedCache = URLEncoder.encode(string, "UTF-8");
264                        } catch (UnsupportedEncodingException e) {
265                                throw new AssertionError(e);
266                        }
267                }
268                return urlEncodedCache;
269        }
270
271        private void throwIse(String message) {
272                String exceptionMessage = "The JID '" + this + "' " + message;
273                throw new IllegalStateException(exceptionMessage);
274        }
275
276        static <O> O requireNonNull(O object, String message) {
277                if (object != null) {
278                        return object;
279                }
280                throw new IllegalArgumentException(message);
281        }
282}