001/* 002 * Copyright 2014-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2014-2017 UnboundID Corp. 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 this object represents a valid numeric OID. 194 * 195 * @return {@code true} if this object represents a valid numeric OID, or 196 * {@code false} if not. 197 */ 198 public boolean isValidNumericOID() 199 { 200 return (components != null); 201 } 202 203 204 205 /** 206 * Retrieves the numeric components that comprise this OID. This will only 207 * return a non-{@code null} value if {@link #isValidNumericOID} returns 208 * {@code true}. 209 * 210 * @return The numeric components that comprise this OID, or {@code null} if 211 * this object does not represent a valid numeric OID. 212 */ 213 public List<Integer> getComponents() 214 { 215 return components; 216 } 217 218 219 220 /** 221 * Retrieves a hash code for this OID. 222 * 223 * @return A hash code for this OID. 224 */ 225 @Override() 226 public int hashCode() 227 { 228 if (components == null) 229 { 230 return oidString.hashCode(); 231 } 232 else 233 { 234 int hashCode = 0; 235 for (final int i : components) 236 { 237 hashCode += i; 238 } 239 return hashCode; 240 } 241 } 242 243 244 245 /** 246 * Indicates whether the provided object is equal to this OID. 247 * 248 * @param o The object for which to make the determination. 249 * 250 * @return {@code true} if the provided object is equal to this OID, or 251 * {@code false} if not. 252 */ 253 @Override() 254 public boolean equals(final Object o) 255 { 256 if (o == null) 257 { 258 return false; 259 } 260 261 if (o == this) 262 { 263 return true; 264 } 265 266 if (o instanceof OID) 267 { 268 final OID oid = (OID) o; 269 if (components == null) 270 { 271 return oidString.equals(oid.oidString); 272 } 273 else 274 { 275 return components.equals(oid.components); 276 } 277 } 278 279 return false; 280 } 281 282 283 284 /** 285 * Indicates the position of the provided object relative to this OID in a 286 * sorted list. 287 * 288 * @param oid The OID to compare against this OID. 289 * 290 * @return A negative value if this OID should come before the provided OID 291 * in a sorted list, a positive value if this OID should come after 292 * the provided OID in a sorted list, or zero if the two OIDs 293 * represent equivalent values. 294 */ 295 public int compareTo(final OID oid) 296 { 297 if (components == null) 298 { 299 if (oid.components == null) 300 { 301 // Neither is a valid numeric OID, so we'll just compare the string 302 // representations. 303 return oidString.compareTo(oid.oidString); 304 } 305 else 306 { 307 // A valid numeric OID will always come before a non-valid one. 308 return 1; 309 } 310 } 311 312 if (oid.components == null) 313 { 314 // A valid numeric OID will always come before a non-valid one. 315 return -1; 316 } 317 318 for (int i=0; i < Math.min(components.size(), oid.components.size()); i++) 319 { 320 final int thisValue = components.get(i); 321 final int thatValue = oid.components.get(i); 322 323 if (thisValue < thatValue) 324 { 325 // This OID has a lower number in the first non-equal slot than the 326 // provided OID. 327 return -1; 328 } 329 else if (thisValue > thatValue) 330 { 331 // This OID has a higher number in the first non-equal slot than the 332 // provided OID. 333 return 1; 334 } 335 } 336 337 // Where the values overlap, they are equivalent. Make the determination 338 // based on which is longer. 339 if (components.size() < oid.components.size()) 340 { 341 // The provided OID is longer than this OID. 342 return -1; 343 } 344 else if (components.size() > oid.components.size()) 345 { 346 // The provided OID is shorter than this OID. 347 return 1; 348 } 349 else 350 { 351 // They represent equivalent OIDs. 352 return 0; 353 } 354 } 355 356 357 358 /** 359 * Retrieves a string representation of this OID. 360 * 361 * @return A string representation of this OID. 362 */ 363 @Override() 364 public String toString() 365 { 366 return oidString; 367 } 368}