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<>(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 = new ArrayList<>(components.length); 142 for (final int i : components) 143 { 144 compList.add(i); 145 } 146 return compList; 147 } 148 149 150 151 /** 152 * Parses the provided string as a numeric OID and extracts the numeric 153 * components from it. 154 * 155 * @param oidString The string to parse as a numeric OID. 156 * 157 * @return The numeric components extracted from the provided string, or 158 * {@code null} if the provided string does not represent a valid 159 * numeric OID. 160 */ 161 public static List<Integer> parseComponents(final String oidString) 162 { 163 if ((oidString == null) || oidString.isEmpty() || 164 oidString.startsWith(".") || oidString.endsWith(".") || 165 (oidString.indexOf("..") > 0)) 166 { 167 return null; 168 } 169 170 final StringTokenizer tokenizer = new StringTokenizer(oidString, "."); 171 final ArrayList<Integer> compList = new ArrayList<>(10); 172 while (tokenizer.hasMoreTokens()) 173 { 174 final String token = tokenizer.nextToken(); 175 try 176 { 177 compList.add(Integer.parseInt(token)); 178 } 179 catch (final Exception e) 180 { 181 Debug.debugException(e); 182 return null; 183 } 184 } 185 186 return Collections.unmodifiableList(compList); 187 } 188 189 190 191 /** 192 * Indicates whether the provided string represents a valid numeric OID. Note 193 * this this method only ensures that the value is made up of a dotted list of 194 * numbers that does not start or end with a period and does not contain two 195 * consecutive periods. The {@link #isStrictlyValidNumericOID(String)} method 196 * performs additional validation, including ensuring that the OID contains 197 * at least two components, that the value of the first component is not 198 * greater than two, and that the value of the second component is not greater 199 * than 39 if the value of the first component is zero or one. 200 * 201 * @param s The string for which to make the determination. 202 * 203 * @return {@code true} if the provided string represents a valid numeric 204 * OID, or {@code false} if not. 205 */ 206 public static boolean isValidNumericOID(final String s) 207 { 208 return new OID(s).isValidNumericOID(); 209 } 210 211 212 213 /** 214 * Indicates whether the provided string represents a valid numeric OID. Note 215 * this this method only ensures that the value is made up of a dotted list of 216 * numbers that does not start or end with a period and does not contain two 217 * consecutive periods. The {@link #isStrictlyValidNumericOID()} method 218 * performs additional validation, including ensuring that the OID contains 219 * at least two components, that the value of the first component is not 220 * greater than two, and that the value of the second component is not greater 221 * than 39 if the value of the first component is zero or one. 222 * 223 * @return {@code true} if this object represents a valid numeric OID, or 224 * {@code false} if not. 225 */ 226 public boolean isValidNumericOID() 227 { 228 return (components != null); 229 } 230 231 232 233 /** 234 * Indicates whether this object represents a strictly valid numeric OID. 235 * In addition to ensuring that the value is made up of a dotted list of 236 * numbers that does not start or end with a period or contain two consecutive 237 * periods, this method also ensures that the OID contains at least two 238 * components, that the value of the first component is not greater than two, 239 * and that the value of the second component is not greater than 39 if the 240 * value of the first component is zero or one. 241 * 242 * @param s The string for which to make the determination. 243 * 244 * @return {@code true} if this object represents a strictly valid numeric 245 * OID, or {@code false} if not. 246 */ 247 public static boolean isStrictlyValidNumericOID(final String s) 248 { 249 return new OID(s).isStrictlyValidNumericOID(); 250 } 251 252 253 254 /** 255 * Indicates whether this object represents a strictly valid numeric OID. 256 * In addition to ensuring that the value is made up of a dotted list of 257 * numbers that does not start or end with a period or contain two consecutive 258 * periods, this method also ensures that the OID contains at least two 259 * components, that the value of the first component is not greater than two, 260 * and that the value of the second component is not greater than 39 if the 261 * value of the first component is zero or one. 262 * 263 * @return {@code true} if this object represents a strictly valid numeric 264 * OID, or {@code false} if not. 265 */ 266 public boolean isStrictlyValidNumericOID() 267 { 268 if ((components == null) || (components.size() < 2)) 269 { 270 return false; 271 } 272 273 final int firstComponent = components.get(0); 274 final int secondComponent = components.get(1); 275 switch (firstComponent) 276 { 277 case 0: 278 case 1: 279 // The value of the second component must not be greater than 39. 280 return (secondComponent <= 39); 281 282 case 2: 283 // We don't need to do any more validation. 284 return true; 285 286 default: 287 // Invalid value for the first component. 288 return false; 289 } 290 } 291 292 293 294 /** 295 * Retrieves the numeric components that comprise this OID. This will only 296 * return a non-{@code null} value if {@link #isValidNumericOID} returns 297 * {@code true}. 298 * 299 * @return The numeric components that comprise this OID, or {@code null} if 300 * this object does not represent a valid numeric OID. 301 */ 302 public List<Integer> getComponents() 303 { 304 return components; 305 } 306 307 308 309 /** 310 * Retrieves a hash code for this OID. 311 * 312 * @return A hash code for this OID. 313 */ 314 @Override() 315 public int hashCode() 316 { 317 if (components == null) 318 { 319 return oidString.hashCode(); 320 } 321 else 322 { 323 int hashCode = 0; 324 for (final int i : components) 325 { 326 hashCode += i; 327 } 328 return hashCode; 329 } 330 } 331 332 333 334 /** 335 * Indicates whether the provided object is equal to this OID. 336 * 337 * @param o The object for which to make the determination. 338 * 339 * @return {@code true} if the provided object is equal to this OID, or 340 * {@code false} if not. 341 */ 342 @Override() 343 public boolean equals(final Object o) 344 { 345 if (o == null) 346 { 347 return false; 348 } 349 350 if (o == this) 351 { 352 return true; 353 } 354 355 if (o instanceof OID) 356 { 357 final OID oid = (OID) o; 358 if (components == null) 359 { 360 return oidString.equals(oid.oidString); 361 } 362 else 363 { 364 return components.equals(oid.components); 365 } 366 } 367 368 return false; 369 } 370 371 372 373 /** 374 * Indicates the position of the provided object relative to this OID in a 375 * sorted list. 376 * 377 * @param oid The OID to compare against this OID. 378 * 379 * @return A negative value if this OID should come before the provided OID 380 * in a sorted list, a positive value if this OID should come after 381 * the provided OID in a sorted list, or zero if the two OIDs 382 * represent equivalent values. 383 */ 384 @Override() 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}