001 /*
002 * Copyright 2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2014 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 */
021 package com.unboundid.util;
022
023
024
025 import java.io.Serializable;
026 import java.util.ArrayList;
027 import java.util.Collections;
028 import java.util.List;
029 import 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)
042 public 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 }