001    /*
002     * Copyright 2014-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2014-2016 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    }