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.util; 018 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.Collection; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Set; 025 026import org.jxmpp.jid.EntityBareJid; 027import org.jxmpp.jid.DomainFullJid; 028import org.jxmpp.jid.EntityFullJid; 029import org.jxmpp.jid.Jid; 030import org.jxmpp.jid.impl.JidCreate; 031import org.jxmpp.stringprep.XmppStringprepException; 032import org.jxmpp.util.XmppStringUtils; 033 034public class JidUtil { 035 036 /** 037 * Check if the given CharSequence represents a typical and valid entity bare JID. This method does perform the same 038 * check as {@link #isValidEntityBareJid(CharSequence)} and additionally verifies that the domainpart of the JID 039 * contains at least one dot ('.') character. 040 * <p> 041 * For more information about the different verification methods see {@link #validateEntityBareJid(CharSequence)}. 042 * </p> 043 * 044 * @param jid the CharSequence to check. 045 * @return true if {@code jid} represents a valid entity bare JID, false otherwise 046 * @see #isValidEntityBareJid(CharSequence) 047 * @see EntityBareJid 048 */ 049 public static boolean isTypicalValidEntityBareJid(CharSequence jid) { 050 try { 051 validateTypicalEntityBareJid(jid); 052 } catch (NotAEntityBareJidStringException | XmppStringprepException e) { 053 return false; 054 } 055 return true; 056 } 057 058 /** 059 * Check if the given CharSequence is a typical and valid entity bare JID. This method does perform the same 060 * check as {@link #isValidEntityBareJid(CharSequence)} and additionally verifies that the domainpart of the JID 061 * contains at least one dot ('.') character. 062 * <p> 063 * The <code>…TypicalValidEntityBareJid(CharSequence)</code> methods are useful if you expect your users to always 064 * enter a FQDN as domainpart. Whereas <code>isValidEntityBareJid(CharSequence)</code> and 065 * <code>validateEntityBareJid</code> accept also inputs like "foo@example", the "is typical JID" methods require 066 * the domainpart to contain a dot, e.g. "foo@example.org". 067 * </p> 068 * 069 * @param jidcs the JID CharSequence 070 * @return a BareJid instance representing the given JID CharSequence 071 * @throws NotAEntityBareJidStringException if the given CharSequence is not a bare JID. 072 * @throws XmppStringprepException if an error happens. 073 */ 074 public static EntityBareJid validateTypicalEntityBareJid(CharSequence jidcs) throws NotAEntityBareJidStringException, XmppStringprepException { 075 EntityBareJid jid = validateEntityBareJid(jidcs); 076 if (jid.getDomain().toString().indexOf('.') == -1) { 077 throw new NotAEntityBareJidStringException("Domainpart does not include a dot ('.') character"); 078 } 079 return jid; 080 } 081 082 /** 083 * Check if the given CharSequence represents a valid entity bare JID. That 084 * is, it must consists exactly of a local- and a domainpart 085 * (<localpart@domainpart>). 086 * <p> 087 * This method is meant to validate user input and give fast feedback (e.g. 088 * with a red or green light) about if the user entered CharSequence 089 * represents a bare JID. 090 * </p> 091 * 092 * @param jid 093 * the CharSequence to check. 094 * @return true if {@code jid} represents a valid entity bare JID, false otherwise 095 * @see EntityBareJid 096 */ 097 public static boolean isValidEntityBareJid(CharSequence jid) { 098 try { 099 validateEntityBareJid(jid); 100 } catch (NotAEntityBareJidStringException | XmppStringprepException e) { 101 return false; 102 } 103 return true; 104 } 105 106 /** 107 * Check if the given CharSequence is a valid entity bare JID. That 108 * is, it must consists exactly of a local- and a domainpart 109 * (<localpart@domainpart>). 110 * <p> 111 * This is a convenience method meant to validate user entered bare JIDs. If 112 * the given {@code jid} is not a valid bare JID, then this method will 113 * throw either {@link NotAEntityBareJidStringException} or 114 * {@link XmppStringprepException}. The NotABareJidStringException will 115 * contain a meaningful message explaining why the given CharSequence is not a 116 * valid bare JID (e.g. "does not contain a '@' character"). 117 * </p> 118 * 119 * @param jidcs the JID CharSequence 120 * @return a BareJid instance representing the given JID CharSequence 121 * @throws NotAEntityBareJidStringException if the given CharSequence is not a bare JID. 122 * @throws XmppStringprepException if an error happens. 123 */ 124 public static EntityBareJid validateEntityBareJid(CharSequence jidcs) throws NotAEntityBareJidStringException, XmppStringprepException { 125 String jid = jidcs.toString(); 126 final int atIndex = jid.indexOf('@'); 127 if (atIndex == -1) { 128 throw new NotAEntityBareJidStringException("'" + jid + "' does not contain a '@' character"); 129 } else if (jid.indexOf('@', atIndex + 1) != -1) { 130 throw new NotAEntityBareJidStringException("'" + jid + "' contains multiple '@' characters"); 131 } 132 final String localpart = XmppStringUtils.parseLocalpart(jid); 133 if (localpart == null || localpart.length() == 0) { 134 throw new NotAEntityBareJidStringException("'" + jid + "' has empty localpart"); 135 } 136 final String domainpart = XmppStringUtils.parseDomain(jid); 137 if (domainpart == null || domainpart.length() == 0) { 138 throw new NotAEntityBareJidStringException("'" + jid + "' has empty domainpart"); 139 } 140 return JidCreate.entityBareFromUnescaped(jid); 141 } 142 143 public static class NotAEntityBareJidStringException extends Exception { 144 /** 145 * 146 */ 147 private static final long serialVersionUID = -1710386661031655082L; 148 149 /** 150 * Construct a new "not a entity bare JID" exception. 151 * 152 * @param message the message of the exception. 153 */ 154 public NotAEntityBareJidStringException(String message) { 155 super(message); 156 } 157 } 158 159 /** 160 * Filter all entity bare JIDs. 161 * 162 * @param in the input collection. 163 * @param out the collection where the filtered JIDs are added to. 164 */ 165 public static void filterEntityBareJid(Collection<? extends Jid> in, Collection<? super EntityBareJid> out) { 166 for (Jid jid : in) { 167 EntityBareJid bareJid = jid.asEntityBareJidIfPossible(); 168 if (bareJid != null) { 169 out.add(bareJid); 170 } 171 } 172 } 173 174 /** 175 * Filter all entity bare JIDs. 176 * 177 * @param input the input collection. 178 * @return a set containing all bare JIDs of the input collection. 179 */ 180 public static Set<EntityBareJid> filterEntityBareJidSet(Collection<? extends Jid> input) { 181 Set<EntityBareJid> res = new HashSet<EntityBareJid>(input.size()); 182 filterEntityBareJid(input, res); 183 return res; 184 } 185 186 /** 187 * Filter all entity bare JIDs. 188 * 189 * @param input the input collection. 190 * @return a list containing all bare JIDs of the input collection. 191 */ 192 public static List<EntityBareJid> filterEntityBareJidList(Collection<? extends Jid> input) { 193 List<EntityBareJid> res = new ArrayList<EntityBareJid>(input.size()); 194 filterEntityBareJid(input, res); 195 return res; 196 } 197 198 /** 199 * Filter all entity full JIDs. 200 * 201 * @param in the input collection. 202 * @param out the collection where the filtered JIDs are added to. 203 */ 204 public static void filterEntityFullJid(Collection<? extends Jid> in, Collection<? super EntityFullJid> out) { 205 for (Jid jid : in) { 206 EntityFullJid fullJid = jid.asEntityFullJidIfPossible(); 207 if (fullJid != null) { 208 out.add(fullJid); 209 } 210 } 211 } 212 213 /** 214 * Filter all full JIDs. 215 * 216 * @param input the input collection. 217 * @return a set containing all full JIDs of the input collection. 218 */ 219 public static Set<EntityFullJid> filterEntityFullJidSet(Collection<? extends Jid> input) { 220 Set<EntityFullJid> res = new HashSet<EntityFullJid>(input.size()); 221 filterEntityFullJid(input, res); 222 return res; 223 } 224 225 /** 226 * Filter all full JIDs. 227 * 228 * @param input the input collection. 229 * @return a list containing all full JIDs of the input collection. 230 */ 231 public static List<EntityFullJid> filterEntityFullJidList(Collection<? extends Jid> input) { 232 List<EntityFullJid> res = new ArrayList<EntityFullJid>(input.size()); 233 filterEntityFullJid(input, res); 234 return res; 235 } 236 237 /** 238 * Filter all domain full JIDs. 239 * 240 * @param in the input collection. 241 * @param out the collection where the filtered JIDs are added to. 242 */ 243 public static void filterDomainFullJid(Collection<? extends Jid> in, Collection<? super DomainFullJid> out) { 244 for (Jid jid : in) { 245 DomainFullJid domainFullJid = jid.asDomainFullJidIfPossible(); 246 if (domainFullJid != null) { 247 out.add(domainFullJid); 248 } 249 } 250 } 251 252 /** 253 * Filter all domain full JIDs. 254 * 255 * @param input the input collection. 256 * @return a set containing all domain full JIDs of the input collection. 257 */ 258 public static Set<DomainFullJid> filterDomainFullJidSet(Collection<? extends Jid> input) { 259 Set<DomainFullJid> res = new HashSet<DomainFullJid>(input.size()); 260 filterDomainFullJid(input, res); 261 return res; 262 } 263 264 /** 265 * Filter all domain full JIDs. 266 * 267 * @param input the input collection. 268 * @return a list containing all domain full JIDs of the input collection. 269 */ 270 public static List<DomainFullJid> filterDomainFullJidList(Collection<? extends Jid> input) { 271 List<DomainFullJid> res = new ArrayList<DomainFullJid>(input.size()); 272 filterDomainFullJid(input, res); 273 return res; 274 } 275 276 /** 277 * Convert the given collection of CharSequences to bare JIDs. 278 * 279 * @param jidStrings the collection of CharSequences. 280 * @return a set of bare JIDs. 281 */ 282 public static Set<EntityBareJid> entityBareJidSetFrom(Collection<? extends CharSequence> jidStrings) { 283 Set<EntityBareJid> res = new HashSet<EntityBareJid>(jidStrings.size()); 284 entityBareJidsFrom(jidStrings, res, null); 285 return res; 286 } 287 288 /** 289 * Convert a collection of Strings to a Set of {@link EntityBareJid}'s. 290 * <p> 291 * If the optional argument <code>exceptions</code> is given, then all {@link XmppStringprepException} thrown while 292 * converting will be added to the list. Otherwise, if an XmppStringprepExceptions is thrown, it will be wrapped in 293 * a AssertionError Exception and throw. 294 * </p> 295 * 296 * @param jidStrings 297 * the strings that are going to get converted 298 * @param output 299 * the collection where the BareJid's will be added to 300 * @param exceptions the list of exceptions thrown while converting. 301 */ 302 public static void entityBareJidsFrom(Collection<? extends CharSequence> jidStrings, Collection<? super EntityBareJid> output, 303 List<XmppStringprepException> exceptions) { 304 for (CharSequence jid : jidStrings) { 305 try { 306 EntityBareJid bareJid = JidCreate.entityBareFrom(jid); 307 output.add(bareJid); 308 } catch (XmppStringprepException e) { 309 if (exceptions != null) { 310 exceptions.add(e); 311 } else { 312 throw new AssertionError(e); 313 } 314 } 315 } 316 } 317 318 /** 319 * Convert the given array of Strings to JIDs. 320 * <p> 321 * Note that errors while converting the Strings will be silently ignored. 322 * </p> 323 * 324 * @param jids a array of JID Strings. 325 * @return a set of JIDs. 326 */ 327 public static Set<Jid> jidSetFrom(String[] jids) { 328 return jidSetFrom(Arrays.asList(jids)); 329 } 330 331 /** 332 * Convert the given collection of CharSequences to JIDs. 333 * 334 * @param jidStrings the collection of CharSequences. 335 * @return a set of JIDs. 336 */ 337 public static Set<Jid> jidSetFrom(Collection<? extends CharSequence> jidStrings) { 338 Set<Jid> res = new HashSet<Jid>(jidStrings.size()); 339 jidsFrom(jidStrings, res, null); 340 return res; 341 } 342 343 /** 344 * Convert a collection of Strings to a Set of {@link Jid}'s. 345 * <p> 346 * If the optional argument <code>exceptions</code> is given, then all {@link XmppStringprepException} thrown while 347 * converting will be added to the list. Otherwise, if an XmppStringprepExceptions is thrown, it will be wrapped in 348 * a AssertionError Exception and throw. 349 * </p> 350 * 351 * @param jidStrings 352 * the strings that are going to get converted 353 * @param output 354 * the collection where the Jid's will be added to 355 * @param exceptions the list of exceptions thrown while converting. 356 */ 357 public static void jidsFrom(Collection<? extends CharSequence> jidStrings, Collection<? super Jid> output, 358 List<XmppStringprepException> exceptions) { 359 for (CharSequence jidString : jidStrings) { 360 try { 361 Jid jid = JidCreate.from(jidString); 362 output.add(jid); 363 } catch (XmppStringprepException e) { 364 if (exceptions != null) { 365 exceptions.add(e); 366 } else { 367 throw new AssertionError(e); 368 } 369 } 370 } 371 } 372 373 /** 374 * Convert a collection of JIDs to a list of Strings representing those JIDs. 375 * 376 * @param jids a collection of JIDs. 377 * @return a list of Strings. 378 */ 379 public static List<String> toStringList(Collection<? extends Jid> jids) { 380 List<String> res = new ArrayList<String>(jids.size()); 381 toStrings(jids, res); 382 return res; 383 } 384 385 /** 386 * convert a collection of JIDs to a set of Strings representing those JIDs. 387 * 388 * @param jids a collection of JIDs. 389 * @return a set of String. 390 */ 391 public static Set<String> toStringSet(Collection<? extends Jid> jids) { 392 Set<String> res = new HashSet<String>(jids.size()); 393 toStrings(jids, res); 394 return res; 395 } 396 397 /** 398 * Convert a collection of JIDs to a Collection of Strings. 399 * 400 * @param jids the collection of Strings to convert. 401 * @param jidStrings the collection of Strings to append to. 402 */ 403 public static void toStrings(Collection<? extends Jid> jids, Collection<? super String> jidStrings) { 404 for (Jid jid : jids) { 405 jidStrings.add(jid.toString()); 406 } 407 } 408 409 /** 410 * Check if two JIDs are equals. Takes <code>null</code> values into consideration. Which means that this method will return <code>true</code> if both JIDs are <code>null</code>. 411 * 412 * @param jidOne The first JID to compare. 413 * @param jidTwo The second JID to compare. 414 * @return <code>true</code> if both JIDs are equals. 415 * @since 0.7.0 416 */ 417 public static boolean equals(Jid jidOne, Jid jidTwo) { 418 if (jidOne != null) { 419 return jidOne.equals(jidTwo); 420 } 421 422 return jidTwo == null; 423 } 424}