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 */
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}