001/* 002 * Copyright 2014-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2014-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.util; 022 023 024 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.List; 029import java.util.StringTokenizer; 030 031 032 033/** 034 * This class provides a data structure that may be used for representing object 035 * identifiers. Since some directory servers support using strings that aren't 036 * valid object identifiers where OIDs are required, this implementation 037 * supports arbitrary strings, but some methods may only be available for valid 038 * OIDs. 039 */ 040@NotMutable() 041@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 042public final class OID 043 implements Serializable, Comparable<OID> 044{ 045 /** 046 * The serial version UID for this serializable class. 047 */ 048 private static final long serialVersionUID = -4542498394670806081L; 049 050 051 052 // The numeric components that comprise this OID. 053 private final List<Integer> components; 054 055 // The string representation for this OID. 056 private final String oidString; 057 058 059 060 /** 061 * Creates a new OID object from the provided string representation. 062 * 063 * @param oidString The string to use to create this OID. 064 */ 065 public OID(final String oidString) 066 { 067 if (oidString == null) 068 { 069 this.oidString = ""; 070 } 071 else 072 { 073 this.oidString = oidString; 074 } 075 076 components = parseComponents(oidString); 077 } 078 079 080 081 /** 082 * Creates a new OID object from the provided set of numeric components. At 083 * least one component must be provided for a valid OID. 084 * 085 * @param components The numeric components to include in the OID. 086 */ 087 public OID(final int... components) 088 { 089 this(toList(components)); 090 } 091 092 093 094 /** 095 * Creates a new OID object from the provided set of numeric components. At 096 * least one component must be provided for a valid OID. 097 * 098 * @param components The numeric components to include in the OID. 099 */ 100 public OID(final List<Integer> components) 101 { 102 if ((components == null) || components.isEmpty()) 103 { 104 this.components = null; 105 oidString = ""; 106 } 107 else 108 { 109 this.components = 110 Collections.unmodifiableList(new ArrayList<Integer>(components)); 111 112 final StringBuilder buffer = new StringBuilder(); 113 for (final Integer i : components) 114 { 115 if (buffer.length() > 0) 116 { 117 buffer.append('.'); 118 } 119 buffer.append(i); 120 } 121 oidString = buffer.toString(); 122 } 123 } 124 125 126 127 /** 128 * Retrieves a list corresponding to the elements in the provided array. 129 * 130 * @param components The array to convert to a list. 131 * 132 * @return The list of elements. 133 */ 134 private static List<Integer> toList(final int... components) 135 { 136 if (components == null) 137 { 138 return null; 139 } 140 141 final ArrayList<Integer> compList = 142 new ArrayList<Integer>(components.length); 143 for (final int i : components) 144 { 145 compList.add(i); 146 } 147 return compList; 148 } 149 150 151 152 /** 153 * Parses the provided string as a numeric OID and extracts the numeric 154 * components from it. 155 * 156 * @param oidString The string to parse as a numeric OID. 157 * 158 * @return The numeric components extracted from the provided string, or 159 * {@code null} if the provided string does not represent a valid 160 * numeric OID. 161 */ 162 public static List<Integer> parseComponents(final String oidString) 163 { 164 if ((oidString == null) || (oidString.length() == 0) || 165 oidString.startsWith(".") || oidString.endsWith(".") || 166 (oidString.indexOf("..") > 0)) 167 { 168 return null; 169 } 170 171 final StringTokenizer tokenizer = new StringTokenizer(oidString, "."); 172 final ArrayList<Integer> compList = new ArrayList<Integer>(10); 173 while (tokenizer.hasMoreTokens()) 174 { 175 final String token = tokenizer.nextToken(); 176 try 177 { 178 compList.add(Integer.parseInt(token)); 179 } 180 catch (final Exception e) 181 { 182 Debug.debugException(e); 183 return null; 184 } 185 } 186 187 return Collections.unmodifiableList(compList); 188 } 189 190 191 192 /** 193 * Indicates whether the provided string represents a valid numeric OID. Note 194 * this this method only ensures that the value is made up of a dotted list of 195 * numbers that does not start or end with a period and does not contain two 196 * consecutive periods. The {@link #isStrictlyValidNumericOID(String)} method 197 * performs additional validation, including ensuring that the OID contains 198 * at least two components, that the value of the first component is not 199 * greater than two, and that the value of the second component is not greater 200 * than 39 if the value of the first component is zero or one. 201 * 202 * @param s The string for which to make the determination. 203 * 204 * @return {@code true} if the provided string represents a valid numeric 205 * OID, or {@code false} if not. 206 */ 207 public static boolean isValidNumericOID(final String s) 208 { 209 return new OID(s).isValidNumericOID(); 210 } 211 212 213 214 /** 215 * Indicates whether the provided string represents a valid numeric OID. Note 216 * this this method only ensures that the value is made up of a dotted list of 217 * numbers that does not start or end with a period and does not contain two 218 * consecutive periods. The {@link #isStrictlyValidNumericOID()} method 219 * performs additional validation, including ensuring that the OID contains 220 * at least two components, that the value of the first component is not 221 * greater than two, and that the value of the second component is not greater 222 * than 39 if the value of the first component is zero or one. 223 * 224 * @return {@code true} if this object represents a valid numeric OID, or 225 * {@code false} if not. 226 */ 227 public boolean isValidNumericOID() 228 { 229 return (components != null); 230 } 231 232 233 234 /** 235 * Indicates whether this object represents a strictly valid numeric OID. 236 * In addition to ensuring that the value is made up of a dotted list of 237 * numbers that does not start or end with a period or contain two consecutive 238 * periods, this method also ensures that the OID contains at least two 239 * components, that the value of the first component is not greater than two, 240 * and that the value of the second component is not greater than 39 if the 241 * value of the first component is zero or one. 242 * 243 * @param s The string for which to make the determination. 244 * 245 * @return {@code true} if this object represents a strictly valid numeric 246 * OID, or {@code false} if not. 247 */ 248 public static boolean isStrictlyValidNumericOID(final String s) 249 { 250 return new OID(s).isStrictlyValidNumericOID(); 251 } 252 253 254 255 /** 256 * Indicates whether this object represents a strictly valid numeric OID. 257 * In addition to ensuring that the value is made up of a dotted list of 258 * numbers that does not start or end with a period or contain two consecutive 259 * periods, this method also ensures that the OID contains at least two 260 * components, that the value of the first component is not greater than two, 261 * and that the value of the second component is not greater than 39 if the 262 * value of the first component is zero or one. 263 * 264 * @return {@code true} if this object represents a strictly valid numeric 265 * OID, or {@code false} if not. 266 */ 267 public boolean isStrictlyValidNumericOID() 268 { 269 if ((components == null) || (components.size() < 2)) 270 { 271 return false; 272 } 273 274 final int firstComponent = components.get(0); 275 final int secondComponent = components.get(1); 276 switch (firstComponent) 277 { 278 case 0: 279 case 1: 280 // The value of the second component must not be greater than 39. 281 return (secondComponent <= 39); 282 283 case 2: 284 // We don't need to do any more validation. 285 return true; 286 287 default: 288 // Invalid value for the first component. 289 return false; 290 } 291 } 292 293 294 295 /** 296 * Retrieves the numeric components that comprise this OID. This will only 297 * return a non-{@code null} value if {@link #isValidNumericOID} returns 298 * {@code true}. 299 * 300 * @return The numeric components that comprise this OID, or {@code null} if 301 * this object does not represent a valid numeric OID. 302 */ 303 public List<Integer> getComponents() 304 { 305 return components; 306 } 307 308 309 310 /** 311 * Retrieves a hash code for this OID. 312 * 313 * @return A hash code for this OID. 314 */ 315 @Override() 316 public int hashCode() 317 { 318 if (components == null) 319 { 320 return oidString.hashCode(); 321 } 322 else 323 { 324 int hashCode = 0; 325 for (final int i : components) 326 { 327 hashCode += i; 328 } 329 return hashCode; 330 } 331 } 332 333 334 335 /** 336 * Indicates whether the provided object is equal to this OID. 337 * 338 * @param o The object for which to make the determination. 339 * 340 * @return {@code true} if the provided object is equal to this OID, or 341 * {@code false} if not. 342 */ 343 @Override() 344 public boolean equals(final Object o) 345 { 346 if (o == null) 347 { 348 return false; 349 } 350 351 if (o == this) 352 { 353 return true; 354 } 355 356 if (o instanceof OID) 357 { 358 final OID oid = (OID) o; 359 if (components == null) 360 { 361 return oidString.equals(oid.oidString); 362 } 363 else 364 { 365 return components.equals(oid.components); 366 } 367 } 368 369 return false; 370 } 371 372 373 374 /** 375 * Indicates the position of the provided object relative to this OID in a 376 * sorted list. 377 * 378 * @param oid The OID to compare against this OID. 379 * 380 * @return A negative value if this OID should come before the provided OID 381 * in a sorted list, a positive value if this OID should come after 382 * the provided OID in a sorted list, or zero if the two OIDs 383 * represent equivalent values. 384 */ 385 public int compareTo(final OID oid) 386 { 387 if (components == null) 388 { 389 if (oid.components == null) 390 { 391 // Neither is a valid numeric OID, so we'll just compare the string 392 // representations. 393 return oidString.compareTo(oid.oidString); 394 } 395 else 396 { 397 // A valid numeric OID will always come before a non-valid one. 398 return 1; 399 } 400 } 401 402 if (oid.components == null) 403 { 404 // A valid numeric OID will always come before a non-valid one. 405 return -1; 406 } 407 408 for (int i=0; i < Math.min(components.size(), oid.components.size()); i++) 409 { 410 final int thisValue = components.get(i); 411 final int thatValue = oid.components.get(i); 412 413 if (thisValue < thatValue) 414 { 415 // This OID has a lower number in the first non-equal slot than the 416 // provided OID. 417 return -1; 418 } 419 else if (thisValue > thatValue) 420 { 421 // This OID has a higher number in the first non-equal slot than the 422 // provided OID. 423 return 1; 424 } 425 } 426 427 // Where the values overlap, they are equivalent. Make the determination 428 // based on which is longer. 429 if (components.size() < oid.components.size()) 430 { 431 // The provided OID is longer than this OID. 432 return -1; 433 } 434 else if (components.size() > oid.components.size()) 435 { 436 // The provided OID is shorter than this OID. 437 return 1; 438 } 439 else 440 { 441 // They represent equivalent OIDs. 442 return 0; 443 } 444 } 445 446 447 448 /** 449 * Retrieves a string representation of this OID. 450 * 451 * @return A string representation of this OID. 452 */ 453 @Override() 454 public String toString() 455 { 456 return oidString; 457 } 458}