001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 * 
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 * 
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 * 
019 */
020package org.apache.directory.api.asn1.util;
021
022
023import java.io.IOException;
024import java.io.OutputStream;
025import java.math.BigInteger;
026import java.util.Arrays;
027
028import org.apache.directory.api.asn1.DecoderException;
029import org.apache.directory.api.i18n.I18n;
030
031
032/**
033 * An immutable representation of an object identifier that provides conversion 
034 * between their <code>String</code>, and encoded <code>byte[]</code> 
035 * representations.
036 * 
037 * <p> The encoding of OID values is performed according to 
038 * <a href='http://www.itu.int/rec/T-REC-X.690/en'>itu X.690</a> section 8.19.
039 * Specifically:</p>
040 * 
041 * <p><b>8.19.2</b> The contents octets shall be an (ordered) list of encodings
042 * of subidentifiers (see 8.19.3 and 8.19.4) concatenated together. Each 
043 * subidentifier is represented as a series of (one or more) octets. Bit 8 of 
044 * each octet indicates whether it is the last in the series: bit 8 of the last 
045 * octet is zero; bit 8 of each preceding octet is one. Bits 7 to 1 of the 
046 * octets in the series collectively encode the subidentifier. Conceptually, 
047 * these groups of bits are concatenated to form an unsigned binary number whose 
048 * most significant bit is bit 7 of the first octet and whose least significant 
049 * bit is bit 1 of the last octet. The subidentifier shall be encoded in the 
050 * fewest possible octets, that is, the leading octet of the subidentifier shall 
051 * not have the value 0x80. </p>
052 * 
053 * <p><b>8.19.3</b> The number of subidentifiers (N) shall be one less than the 
054 * number of object identifier components in the object identifier value being 
055 * encoded.</p>
056 * 
057 * <p><b>8.19.4</b> The numerical value of the first subidentifier is derived 
058 * from the values of the first two object identifier components in the object 
059 * identifier value being encoded, using the formula:
060 * <br><code>(X*40) + Y</code><br> 
061 * where X is the value of the first object identifier component and Y is the 
062 * value of the second object identifier component. <i>NOTE – This packing of 
063 * the first two object identifier components recognizes that only three values 
064 * are allocated from the root node, and at most 39 subsequent values from nodes 
065 * reached by X = 0 and X = 1.</i></p>
066 * 
067 * <p>For example, the OID "2.12.3456.7" would be turned into a list of 3 values:
068 * <code>[((2*40)+12), 3456, 7]</code>. The first of which, 
069 * <code>92</code>, would be encoded as the bytes <code>0x5C</code>, the second 
070 * would be <code>[0x9B, 0x00]</code>, and the third as <code>0x07</code>
071 * giving the final encoding <code>[0x5C, 0x9B, 0x00, 0x07]</code>.</p>
072 * 
073 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
074 */
075public final class Oid
076{
077    /** A byte[] representation of an OID */
078    private byte[] oidBytes;
079    
080    /** The OID as a String */
081    private String oidString;
082    
083    private static final BigInteger JOINT_ISO_ITU_T = new BigInteger( "80" );
084    
085    /**
086     * The OID FSA states. We have the following Finite State Automaton :
087     * 
088     * <pre>
089     * (Start) --['0','1']--> (A)
090     * (start) --['2']--> (F)
091     * 
092     * (A) --['.']--> (B)
093     * 
094     * (B) --['0']--> (D)
095     * (B) --['1'..'3']--> (C)
096     * (B) --['4'..'9']--> (E)
097     * 
098     * (C) --[]--> (End)
099     * (C) --['.']--> (K)
100     * (C) --['0'..'9']--> (E)
101     * 
102     * (D) --[]--> (End)
103     * (D) --['.']--> (K)
104     * 
105     * (E) --[]--> (End)
106     * (E) --['.']--> (K)
107     * 
108     * (F) --['.']--> (G)
109     * 
110     * (G) --['0']--> (I)
111     * (G) --['1'..'9']--> (H)
112     *
113     * (H) --[]--> (End)
114     * (H) --['.']--> (K)
115     * (H) --['0'..'9']--> (J)
116     * 
117     * (I) --[]--> (End)
118     * (I) --['.']--> (K)
119     *
120     * (J) --[]--> (End)
121     * (J) --['.']--> (K)
122     * (J) --['0'..'9']--> (J)
123     * 
124     * (K) --['0']--> (M) 
125     * (K) --['1'..'9']--> (L)
126     * 
127     * (L) --[]--> (End)
128     * (L) --['.']--> (K)
129     * (L) --['0'..'9']--> (L)
130     * 
131     * (M) --[]--> (End)
132     * (M) --['.']--> (K)
133     * </pre>
134     */
135    private enum OidFSAState 
136    {
137        START,
138        STATE_A,
139        STATE_B,
140        STATE_C,
141        STATE_D,
142        STATE_E,
143        STATE_F,
144        STATE_G,
145        STATE_H,
146        STATE_I,
147        STATE_J,
148        STATE_K,
149        STATE_L,
150        STATE_M,
151    }
152
153
154    /**
155     * Creates a new instance of Oid.
156     *
157     * @param oidString The OID as a String
158     * @param oidBytes The OID as a byte[]
159     */
160    private Oid( String oidString, byte[] oidBytes )
161    {
162        this.oidString = oidString;
163        this.oidBytes = new byte[oidBytes.length];
164        System.arraycopy( oidBytes, 0, this.oidBytes, 0, oidBytes.length );
165    }
166
167
168    /**
169     * {@inheritDoc}
170     */
171    @Override
172    public boolean equals( Object other )
173    {
174        return ( other instanceof Oid )
175            && oidString.equals( ( ( Oid ) other ).oidString );
176    }
177
178
179    /**
180     * Decodes an OID from a <code>byte[]</code>.
181     * 
182     * @param oidBytes The encoded<code>byte[]</code>
183     * @return A new Oid
184     * @throws DecoderException When the OID is not valid
185     */
186    public static Oid fromBytes( byte[] oidBytes ) throws DecoderException
187    {
188        if ( ( oidBytes == null ) || ( oidBytes.length < 1 ) )
189        {
190            throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, Arrays.toString( oidBytes ) ) );
191        }
192
193        StringBuilder builder = new StringBuilder();
194        long value = 0;
195        int valStart = 0;
196        int valLength = 0;
197        boolean firstArc = true;
198        
199        for ( int i = 0; i < oidBytes.length; i++ )
200        {
201            value |= oidBytes[i] & 0x7F;
202
203            if ( oidBytes[i] < 0 )
204            {
205                // leading 1, so value continues
206                value = value << 7;
207                valLength++;
208            }
209            else
210            {
211                valLength++;
212                
213                if ( valLength > 8 )
214                {
215                    // Above 9 bytes, we won't be able to store the value in a long...
216                    // Compute the number of necessary bytes
217                    int nbBytes = valLength * 7 / 8;
218                    
219                    if ( valLength % 7 != 0 )
220                    {
221                        nbBytes++;
222                    }
223                    
224                    byte[] result = new byte[nbBytes];
225                    
226                    // Now iterate on the incoming bytes
227                    int pos = nbBytes - 1;
228                    int valEnd = valStart + valLength - 1;
229                    int j = 0;
230                    
231                    while ( j < valLength - 8 )
232                    {
233                        result[pos--] = ( byte ) ( ( oidBytes[valEnd - j - 1] << 7 ) | ( oidBytes[valEnd - j] & 0x7F ) );
234                        result[pos--] = ( byte ) ( ( oidBytes[valEnd - j - 2] << 6 ) | ( ( oidBytes[valEnd - j - 1] & 0x7E ) >> 1 ) );
235                        result[pos--] = ( byte ) ( ( oidBytes[valEnd - j - 3] << 5 ) | ( ( oidBytes[valEnd - j - 2] & 0x7C ) >> 2 ) );
236                        result[pos--] = ( byte ) ( ( oidBytes[valEnd - j - 4] << 4 ) | ( ( oidBytes[valEnd - j - 3] & 0x78 ) >> 3 ) );
237                        result[pos--] = ( byte ) ( ( oidBytes[valEnd - j - 5] << 3 ) | ( ( oidBytes[valEnd - j - 4] & 0x70 ) >> 4 ) );
238                        result[pos--] = ( byte ) ( ( oidBytes[valEnd - j - 6] << 2 ) | ( ( oidBytes[valEnd - j - 5] & 0x60 ) >> 5 ) );
239                        result[pos--] = ( byte ) ( ( oidBytes[valEnd - j - 7] << 1 ) | ( ( oidBytes[valEnd - j - 6] & 0x40 ) >> 6 ) );
240                        j += 8;
241                    }
242                    
243                    switch ( valLength - j )
244                    {
245                        case 7 :
246                            result[pos--] = ( byte ) ( ( oidBytes[5] << 7 ) | ( oidBytes[6] & 0x7F ) );
247                            result[pos--] = ( byte ) ( ( oidBytes[4] << 6 ) | ( ( oidBytes[5] & 0x7E ) >> 1 ) );
248                            result[pos--] = ( byte ) ( ( oidBytes[3] << 5 ) | ( ( oidBytes[4] & 0x7C ) >> 2 ) );
249                            result[pos--] = ( byte ) ( ( oidBytes[2] << 4 ) | ( ( oidBytes[3] & 0x78 ) >> 3 ) );
250                            result[pos--] = ( byte ) ( ( oidBytes[1] << 3 ) | ( ( oidBytes[2] & 0x70 ) >> 4 ) );
251                            result[pos--] = ( byte ) ( ( oidBytes[0] << 2 ) | ( ( oidBytes[1] & 0x60 ) >> 5 ) );
252                            result[pos--] = ( byte ) ( ( oidBytes[0] & 0x40 ) >> 6 );
253                            break;
254                            
255                        case 6 :
256                            result[pos--] = ( byte ) ( ( oidBytes[4] << 7 ) | ( oidBytes[5] & 0x7F ) );
257                            result[pos--] = ( byte ) ( ( oidBytes[3] << 6 ) | ( ( oidBytes[4] & 0x7E ) >> 1 ) );
258                            result[pos--] = ( byte ) ( ( oidBytes[2] << 5 ) | ( ( oidBytes[3] & 0x7C ) >> 2 ) );
259                            result[pos--] = ( byte ) ( ( oidBytes[1] << 4 ) | ( ( oidBytes[2] & 0x78 ) >> 3 ) );
260                            result[pos--] = ( byte ) ( ( oidBytes[0] << 3 ) | ( ( oidBytes[1] & 0x70 ) >> 4 ) );
261                            result[pos--] = ( byte ) ( ( oidBytes[0] & 0x60 ) >> 5 );
262                            break;
263
264                        case 5 :
265                            result[pos--] = ( byte ) ( ( oidBytes[3] << 7 ) | ( oidBytes[4] & 0x7F ) );
266                            result[pos--] = ( byte ) ( ( oidBytes[2] << 6 ) | ( ( oidBytes[3] & 0x7E ) >> 1 ) );
267                            result[pos--] = ( byte ) ( ( oidBytes[1] << 5 ) | ( ( oidBytes[2] & 0x7C ) >> 2 ) );
268                            result[pos--] = ( byte ) ( ( oidBytes[0] << 4 ) | ( ( oidBytes[1] & 0x78 ) >> 3 ) );
269                            result[pos--] = ( byte ) ( ( oidBytes[0] & 0x70 ) >> 4 );
270                            break;
271                            
272                        case 4 :
273                            result[pos--] = ( byte ) ( ( oidBytes[2] << 7 ) | ( oidBytes[3] & 0x7F ) );
274                            result[pos--] = ( byte ) ( ( oidBytes[1] << 6 ) | ( ( oidBytes[2] & 0x7E ) >> 1 ) );
275                            result[pos--] = ( byte ) ( ( oidBytes[0] << 5 ) | ( ( oidBytes[1] & 0x7C ) >> 2 ) );
276                            result[pos--] = ( byte ) ( ( oidBytes[0] & 0x78 ) >> 3 );
277                            break;
278                            
279                        case 3 :
280                            result[pos--] = ( byte ) ( ( oidBytes[1] << 7 ) | ( oidBytes[2] & 0x7F ) );
281                            result[pos--] = ( byte ) ( ( oidBytes[0] << 6 ) | ( ( oidBytes[1] & 0x7E ) >> 1 ) );
282                            result[pos--] = ( byte ) ( ( oidBytes[0] & 0x7C ) >> 2 );
283                            break;
284
285                        case 2 :
286                            result[pos--] = ( byte ) ( ( oidBytes[0] << 7 ) | ( oidBytes[1] & 0x7F ) );
287                            result[pos--] = ( byte ) ( ( oidBytes[0] & 0x7E ) >> 1 );
288                            break;
289                            
290                        case 1 :
291                            result[pos--] = ( byte ) ( oidBytes[0] & 0x7F );
292                            break;
293                            
294                        default :
295                            // Exist to please checkstyle...
296                            break;
297                    }
298                    
299                    BigInteger bigInteger = null;
300                    
301                    if ( ( result[0] & 0x80 ) == 0x80 )
302                    {
303                        byte[] newResult = new byte[result.length + 1];
304                        System.arraycopy( result, 0, newResult, 1, result.length );
305                        result = newResult;
306                    }
307                    
308                    bigInteger = new BigInteger( result );
309                    
310                    if ( firstArc )
311                    {
312                        // This is a joint-iso-itu-t(2) arc
313                        bigInteger = bigInteger.subtract( JOINT_ISO_ITU_T );
314                        builder.append( '2' );
315                    }
316                    
317                    builder.append( '.' ).append( bigInteger.toString() );
318                }
319                else
320                {
321                    // value completed
322                    if ( firstArc )
323                    {
324                        // first value special processing
325                        if ( value >= 80 )
326                        {
327                            // starts with 2
328                            builder.append( '2' );
329                            value = value - 80;
330                        }
331                        else
332                        {
333                            // starts with 0 or 1
334                            long one = value / 40;
335                            long two = value % 40;
336    
337                            if ( ( one < 0 ) || ( one > 2 ) || ( two < 0 ) || ( ( one < 2 ) && ( two > 39 ) ) )
338                            {
339                                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID,
340                                    Arrays.toString( oidBytes ) ) );
341                            }
342    
343                            if ( one < 2 )
344                            {
345                                builder.append( one );
346                                value = two;
347                            }
348                        }
349                        
350                        firstArc = false;
351                    }
352    
353                    // normal processing
354                    builder.append( '.' ).append( value );
355                    value = 0;
356                }
357                
358                valStart = i;
359                valLength = 0;
360                value = 0;
361            }
362        }
363    
364        return new Oid( builder.toString(), oidBytes );
365    }
366
367
368    /**
369     * Process state A
370     * <pre>
371     * (Start) --['0','1']--> (A)
372     * (start) --['2']--> (F)
373     * </pre>
374     */
375    private static OidFSAState processStateStart( String oid, byte[] buffer, int pos ) throws DecoderException
376    {
377        char c = oid.charAt( pos );
378        
379        switch ( c )
380        {
381            case '0' :
382            case '1' :
383                buffer[0] = ( byte ) ( ( c - '0' ) * 40 );
384                return OidFSAState.STATE_A;
385                
386            case '2' :
387                return OidFSAState.STATE_F;
388                
389            default :
390                // This is an error
391                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "Should start with 0, 1 or 2" ) );
392        }
393    }
394    
395    
396    /**
397     * Process state B
398     * <pre>
399     * (A) --['.']--> (B)
400     * </pre>
401     */
402    private static OidFSAState processStateA( String oid, int pos ) throws DecoderException
403    {
404        if ( oid.charAt( pos ) != '.' )
405        {
406            // Expecting a Dot here
407            throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a '.' is expected" ) );
408        }
409        
410        return OidFSAState.STATE_B;
411    }
412    
413    
414    /**
415     * Process state B
416     * <pre>
417     * (B) --['0']--> (D)
418     * (B) --['1'..'3']--> (C)
419     * (B) --['4'..'9']--> (E)
420     * </pre>
421     */
422    private static OidFSAState processStateB( String oid, byte[] buffer, int pos ) throws DecoderException
423    {
424        char c = oid.charAt( pos );
425        
426        switch ( c )
427        {
428            case '0' :
429                return OidFSAState.STATE_D;
430                
431            case '1' :
432            case '2' :
433            case '3' :
434                // We may have a second digit. Atm, store the current one in the second psotion
435                buffer[1] = ( byte ) ( c - '0' );
436                
437                return  OidFSAState.STATE_C;
438                
439            case '4' :
440            case '5' :
441            case '6' :
442            case '7' :
443            case '8' :
444            case '9' :
445                buffer[0] += ( byte ) ( c - '0' );
446                return OidFSAState.STATE_E;
447                
448            default :
449                // Expecting a digit here
450                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a digit is expected" ) );
451        }
452    }
453    
454    
455    /**
456     * Process state C
457     * <pre>
458     * (C) --['.']--> (K)
459     * (C) --['0'..'9']--> (E)
460     * </pre>
461     */
462    private static OidFSAState processStateC( String oid, byte[] buffer, int pos ) throws DecoderException
463    {
464        char c = oid.charAt( pos );
465        
466        switch ( c )
467        {
468            case '0' :
469            case '1' :
470            case '2' :
471            case '3' :
472            case '4' :
473            case '5' :
474            case '6' :
475            case '7' :
476            case '8' :
477            case '9' :
478                buffer[0] += ( byte ) ( buffer[1] * 10 + ( c - '0' ) );
479                buffer[1] = 0;
480                return OidFSAState.STATE_E;
481
482            case '.' :
483                buffer[0] += buffer[1];
484                buffer[1] = 0;
485                return OidFSAState.STATE_K;
486                
487            default :
488                // Expecting a digit here
489                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a digit is expected" ) );
490        }
491    }
492    
493    
494    /**
495     * Process state D and E
496     * <pre>
497     * (D) --['.']--> (K)
498     * (E) --['.']--> (K)
499     * </pre>
500     */
501    private static OidFSAState processStateDE( String oid, byte[] buffer, int pos ) throws DecoderException
502    {
503        char c = oid.charAt( pos );
504        
505        if ( c != '.' )
506        {
507            // Expecting a '.' here
508            throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a dot is expected" ) );
509        }
510        
511        // Store the first byte into it
512        buffer[0] = ( byte ) ( buffer[0] | buffer[1] );
513        buffer[1] = 0;
514        
515        return OidFSAState.STATE_K;
516    }
517    
518    
519    /**
520     * Process state F
521     * <pre>
522     * (F) --['.']--> (G)
523     * </pre>
524     */
525    private static OidFSAState processStateF( String oid, int pos ) throws DecoderException
526    {
527        if ( oid.charAt( pos ) != '.' )
528        {
529            // Expecting a Dot here
530            throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a '.' is expected" ) );
531        }
532        
533        return OidFSAState.STATE_G;
534    }
535    
536    
537    /**
538     * Process state G
539     * <pre>
540     * (G) --['0']--> (I)
541     * (G) --['1'..'9']--> (H)
542     * </pre>
543     */
544    private static OidFSAState processStateG( String oid, byte[] buffer, int pos ) throws DecoderException
545    {
546        char c = oid.charAt( pos );
547        
548        switch ( c )
549        {
550            case '0' :
551                buffer[0] = ( byte ) 80;
552                return OidFSAState.STATE_I;
553                
554            case '1' :
555            case '2' :
556            case '3' :
557            case '4' :
558            case '5' :
559            case '6' :
560            case '7' :
561            case '8' :
562            case '9' :
563                // Store the read digit in the second position in the buffer
564                buffer[0] = ( byte ) ( c - '0' );
565                return OidFSAState.STATE_H;
566
567            default :
568                // Expecting a digit here
569                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a digit is expected" ) );
570        }
571    }
572    
573    
574    /**
575     * Process state H
576     * <pre>
577     * (H) --['.']--> (K)
578     * (H) --['0'..'9']--> (J)
579     * </pre>
580     */
581    private static OidFSAState processStateH( String oid, byte[] buffer, int pos ) throws DecoderException
582    {
583        char c = oid.charAt( pos );
584        
585        switch ( c )
586        {
587            case '0' :
588            case '1' :
589            case '2' :
590            case '3' :
591            case '4' :
592            case '5' :
593            case '6' :
594            case '7' :
595            case '8' :
596            case '9' :
597                // Store the read digit in the first position in the buffer
598                buffer[1] = ( byte ) ( c - '0' );
599                return OidFSAState.STATE_J;
600
601            case '.' :
602                // The first 2 arcs are single digit, we can collapse them in one byte.
603                buffer[0] = ( byte ) ( 80 + buffer[0] );
604                
605                return OidFSAState.STATE_K;
606                
607            default :
608                // Expecting a digit here
609                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a digit is expected" ) );
610        }
611    }
612    
613    
614    /**
615     * Process state I
616     * <pre>
617     * (I) --['.']--> (K)
618     * </pre>
619     */
620    private static OidFSAState processStateI( String oid, byte[] buffer, int pos ) throws DecoderException
621    {
622        char c = oid.charAt( pos );
623        
624        switch ( c )
625        {
626            case '.' :
627                // The first 2 arcs are single digit, we can collapse them in one byte.
628                buffer[0] = ( byte ) ( 80 + buffer[1] );
629                return OidFSAState.STATE_K;
630                
631            default :
632                // Expecting a digit here
633                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a digit is expected" ) );
634        }
635    }
636    
637    
638    /**
639     * Process state J
640     * <pre>
641     * (J) --['.']--> (K)
642     * (J) --['0'..'9']--> (J)
643     * </pre>
644     */
645    private static OidFSAState processStateJ( String oid, byte[] buffer, int bufferPos, int pos ) throws DecoderException
646    {
647        char c = oid.charAt( pos );
648        
649        switch ( c )
650        {
651            case '.' :
652                return OidFSAState.STATE_K;
653                
654            case '0' :
655            case '1' :
656            case '2' :
657            case '3' :
658            case '4' :
659            case '5' :
660            case '6' :
661            case '7' :
662            case '8' :
663            case '9' :
664                // Store the new digit at the right position in the buffer
665                buffer[bufferPos] = ( byte ) ( c - '0' );
666                return OidFSAState.STATE_J;
667                
668            default :
669                // Expecting a digit here
670                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a digit is expected" ) );
671        }
672    }
673    
674    
675    /**
676     * Process state J
677     * <pre>
678     * (K) --['0']--> (M)
679     * (K) --['1'..'9']--> (L)
680     * </pre>
681     */
682    private static OidFSAState processStateK( String oid, byte[] buffer, int bufferPos, int pos ) throws DecoderException
683    {
684        char c = oid.charAt( pos );
685        
686        switch ( c )
687        {
688            case '0' :
689                buffer[bufferPos] = 0x00;
690                return OidFSAState.STATE_M;
691                
692            case '1' :
693            case '2' :
694            case '3' :
695            case '4' :
696            case '5' :
697            case '6' :
698            case '7' :
699            case '8' :
700            case '9' :
701                // Store the new digit at the right position in the buffer
702                return OidFSAState.STATE_L;
703                
704            default :
705                // Expecting a digit here
706                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a digit is expected" ) );
707        }
708    }
709    
710    
711    /**
712     * Process state J
713     * <pre>
714     * (L) --['.']--> (K)
715     * (L) --['0'..'9']--> (L)
716     * </pre>
717     */
718    private static OidFSAState processStateL( String oid, byte[] buffer, int bufferPos, int pos ) throws DecoderException
719    {
720        char c = oid.charAt( pos );
721        
722        switch ( c )
723        {
724            case '.' :
725                return OidFSAState.STATE_K;
726                
727            case '0' :
728            case '1' :
729            case '2' :
730            case '3' :
731            case '4' :
732            case '5' :
733            case '6' :
734            case '7' :
735            case '8' :
736            case '9' :
737                // Store the new digit at the right position in the buffer
738                buffer[bufferPos] = ( byte ) ( c - '0' );
739                
740                return OidFSAState.STATE_L;
741                
742            default :
743                // Expecting a digit here
744                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a digit or a dot is expected" ) );
745        }
746    }
747
748    
749    /**
750     * Process state J
751     * <pre>
752     * (M) --['.']--> (K)
753     * </pre>
754     */
755    private static OidFSAState processStateM( String oid, int pos ) throws DecoderException
756    {
757        char c = oid.charAt( pos );
758        
759        switch ( c )
760        {
761            case '.' :
762                return OidFSAState.STATE_K;
763                
764            default :
765                // Expecting a '.' here
766                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "a '.' is expected" ) );
767        }
768    }
769
770    
771    /**
772     * Convert a list of digits to a list of 7 bits bytes. We must start by the end, and we don't
773     * know how many bytes we will need, except when we will be done with the conversion.
774     */
775    private static int convert( String oid, byte[] buffer, int start, int nbDigits, int posBuffer, boolean isJointIsoItuT )
776    {
777        if ( nbDigits < 3 )
778        {
779            // Speedup when we have a number in [0..99] : it's guaranteed to be hold
780            // by a single byte.
781            if ( isJointIsoItuT )
782            {
783                // Another special case : this is an OID that starts with '2.'
784                buffer[0] =  ( byte ) ( 80 + ( oid.charAt( 2 ) - '0' ) * 10 + ( oid.charAt( 3 ) - '0' ) );
785                
786                if ( buffer[0] < 0 )
787                {
788                    // Here, we need 2 bytes
789                    buffer[1] = ( byte ) ( buffer[0] & 0x007F );
790                    buffer[0] = ( byte ) 0x81;
791                    
792                    return 2;
793                }
794                else
795                {
796                    return 1;
797                }
798            }
799            else
800            {
801                if ( nbDigits == 1 )
802                {
803                    buffer[posBuffer] = ( byte ) ( oid.charAt( start ) - '0' );
804                }
805                else
806                {
807                    buffer[posBuffer] = ( byte ) ( ( oid.charAt( start ) - '0' ) * 10 + ( oid.charAt( start + 1 ) - '0' ) );
808                    
809                }
810                return 1;
811            }
812
813        }
814        else if ( nbDigits < 19 )
815        {
816            // The value can be hold in a Long if it's up to 999999999999999999 
817            // Convert the String to a long :
818            String number = oid.substring( start, start + nbDigits );
819
820            long value = Long.parseLong( number );
821
822            if ( isJointIsoItuT )
823            {
824                value += 80L;
825            }
826            
827            // Convert the long to a byte array
828            if ( ( value & 0xFFFFFFFFFFFFFF80L ) == 0 )
829            {
830                // The value will be hold in one byte
831                buffer[posBuffer] = ( byte ) ( value );
832                
833                return 1;
834            }
835            
836            if ( ( value & 0xFFFFFFFFFFFFC000L ) == 0 )
837            {
838                // The value is between 0x80 and 0x3FFF : it will be hold in 2 bytes
839                buffer[posBuffer] = ( byte ) ( ( byte ) ( ( value & 0x0000000000003F80L ) >> 7 ) | 0x80 );
840                buffer[posBuffer + 1] = ( byte ) ( value & 0x000000000000007FL );
841                
842                return 2;
843            }
844            
845            if ( ( value & 0xFFFFFFFFFFE00000L ) == 0 )
846            {
847                // The value is between 0x4000 and 0x1FFFFF : it will be hold in 3 bytes
848                buffer[posBuffer] = ( byte ) ( ( byte ) ( ( value & 0x00000000001FC000L ) >> 14 ) | 0x80 );
849                buffer[posBuffer + 1] = ( byte ) ( ( byte ) ( ( value & 0x0000000000003F80L ) >> 7 ) | 0x80 );
850                buffer[posBuffer + 2] = ( byte ) ( value & 0x000000000000007FL );
851                
852                return 3;
853            }
854            
855            if ( ( value & 0xFFFFFFFFF0000000L ) == 0 )
856            {
857                // The value is between 0x200000 and 0xFFFFFFF : it will be hold in 4 bytes
858                buffer[posBuffer] = ( byte ) ( ( byte ) ( ( value & 0x000000000FE00000L ) >> 21 ) | 0x80 );
859                buffer[posBuffer + 1] = ( byte ) ( ( byte ) ( ( value & 0x00000000001FC000L ) >> 14 ) | 0x80 );
860                buffer[posBuffer + 2] = ( byte ) ( ( byte ) ( ( value & 0x0000000000003F80L ) >> 7 ) | 0x80 );
861                buffer[posBuffer + 3] = ( byte ) ( value & 0x000000000000007FL );
862                
863                return 4;
864            }
865
866            if ( ( value & 0xFFFFFFF800000000L ) == 0 )
867            {
868                // The value is between 0x10000000 and 0x7FFFFFFFF : it will be hold in 5 bytes
869                buffer[posBuffer] = ( byte ) ( ( byte ) ( ( value & 0x00000007F0000000L ) >> 28 ) | 0x80 );
870                buffer[posBuffer + 1] = ( byte ) ( ( byte ) ( ( value & 0x000000000FE00000L ) >> 21 ) | 0x80 );
871                buffer[posBuffer + 2] = ( byte ) ( ( byte ) ( ( value & 0x00000000001FC000L ) >> 14 ) | 0x80 );
872                buffer[posBuffer + 3] = ( byte ) ( ( byte ) ( ( value & 0x0000000000003F80L ) >> 7 ) | 0x80 );
873                buffer[posBuffer + 4] = ( byte ) ( value & 0x000000000000007FL );
874                
875                return 5;
876            }
877
878            if ( ( value & 0xFFFFFC0000000000L ) == 0 )
879            {
880                // The value is between 0x800000000 and 0x3FFFFFFFFFF : it will be hold in 6 bytes
881                buffer[posBuffer] = ( byte ) ( ( byte ) ( ( value & 0x000003F800000000L ) >> 35 ) | 0x80 );
882                buffer[posBuffer + 1] = ( byte ) ( ( byte ) ( ( value & 0x00000007F0000000L ) >> 28 ) | 0x80 );
883                buffer[posBuffer + 2] = ( byte ) ( ( byte ) ( ( value & 0x000000000FE00000L ) >> 21 ) | 0x80 );
884                buffer[posBuffer + 3] = ( byte ) ( ( byte ) ( ( value & 0x00000000001FC000L ) >> 14 ) | 0x80 );
885                buffer[posBuffer + 4] = ( byte ) ( ( byte ) ( ( value & 0x0000000000003F80L ) >> 7 ) | 0x80 );
886                buffer[posBuffer + 5] = ( byte ) ( value & 0x000000000000007FL );
887                
888                return 6;
889            }
890
891            if ( ( value & 0xFFFE000000000000L ) == 0 )
892            {
893                // The value is between 0x40000000000 and 0x1FFFFFFFFFFFF : it will be hold in 7 bytes
894                buffer[posBuffer] = ( byte ) ( ( byte ) ( ( value & 0x0001FC0000000000L ) >> 42 ) | 0x80 );
895                buffer[posBuffer + 1] = ( byte ) ( ( byte ) ( ( value & 0x000003F800000000L ) >> 35 ) | 0x80 );
896                buffer[posBuffer + 2] = ( byte ) ( ( byte ) ( ( value & 0x00000007F0000000L ) >> 28 ) | 0x80 );
897                buffer[posBuffer + 3] = ( byte ) ( ( byte ) ( ( value & 0x000000000FE00000L ) >> 21 ) | 0x80 );
898                buffer[posBuffer + 4] = ( byte ) ( ( byte ) ( ( value & 0x00000000001FC000L ) >> 14 ) | 0x80 );
899                buffer[posBuffer + 5] = ( byte ) ( ( byte ) ( ( value & 0x0000000000003F80L ) >> 7 ) | 0x80 );
900                buffer[posBuffer + 6] = ( byte ) ( value & 0x000000000000007FL );
901                
902                return 7;
903            }
904
905            if ( ( value & 0xFF00000000000000L ) == 0 )
906            {
907                // The value is between 0x2000000000000 and 0xFF000000000000 : it will be hold in 8 bytes
908                buffer[posBuffer] = ( byte ) ( ( byte ) ( ( value & 0x00FE000000000000L ) >> 49 ) | 0x80 );
909                buffer[posBuffer + 1] = ( byte ) ( ( byte ) ( ( value & 0x0001FC0000000000L ) >> 42 ) | 0x80 );
910                buffer[posBuffer + 2] = ( byte ) ( ( byte ) ( ( value & 0x000003F800000000L ) >> 35 ) | 0x80 );
911                buffer[posBuffer + 3] = ( byte ) ( ( byte ) ( ( value & 0x00000007F0000000L ) >> 28 ) | 0x80 );
912                buffer[posBuffer + 4] = ( byte ) ( ( byte ) ( ( value & 0x000000000FE00000L ) >> 21 ) | 0x80 );
913                buffer[posBuffer + 5] = ( byte ) ( ( byte ) ( ( value & 0x00000000001FC000L ) >> 14 ) | 0x80 );
914                buffer[posBuffer + 6] = ( byte ) ( ( byte ) ( ( value & 0x0000000000003F80L ) >> 7 ) | 0x80 );
915                buffer[posBuffer + 7] = ( byte ) ( value & 0x000000000000007FL );
916                
917                return 8;
918            }
919            else
920            {
921                // The value is between 0x100000000000000 and 0x7F00000000000000 : it will be hold in 9 bytes
922                buffer[posBuffer] = ( byte ) ( ( byte ) ( ( value & 0x7F00000000000000L ) >> 56 ) | 0x80 );
923                buffer[posBuffer + 1] = ( byte ) ( ( byte ) ( ( value & 0x00FE000000000000L ) >> 49 ) | 0x80 );
924                buffer[posBuffer + 2] = ( byte ) ( ( byte ) ( ( value & 0x0001FC0000000000L ) >> 42 ) | 0x80 );
925                buffer[posBuffer + 3] = ( byte ) ( ( byte ) ( ( value & 0x000003F800000000L ) >> 35 ) | 0x80 );
926                buffer[posBuffer + 4] = ( byte ) ( ( byte ) ( ( value & 0x00000007F0000000L ) >> 28 ) | 0x80 );
927                buffer[posBuffer + 5] = ( byte ) ( ( byte ) ( ( value & 0x000000000FE00000L ) >> 21 ) | 0x80 );
928                buffer[posBuffer + 6] = ( byte ) ( ( byte ) ( ( value & 0x00000000001FC000L ) >> 14 ) | 0x80 );
929                buffer[posBuffer + 7] = ( byte ) ( ( byte ) ( ( value & 0x0000000000003F80L ) >> 7 ) | 0x80 );
930                buffer[posBuffer + 8] = ( byte ) ( value & 0x000000000000007FL );
931                
932                return 9;
933            }
934        }
935        else
936        {
937            // The value is bigger than 9999999999999999999, we need to use a BigInteger
938            // First, get the number of bytes we need to store the value in base 16
939            String number = oid.substring( start, start + nbDigits );
940            BigInteger bigInteger = new BigInteger( number );
941            
942            if ( isJointIsoItuT )
943            {
944                bigInteger = bigInteger.add( JOINT_ISO_ITU_T );
945                posBuffer = 0;
946            }
947            
948            byte[] bytes = bigInteger.toByteArray();
949            
950            // Now, convert this value to the ASN.1 OID format : we store the value
951            // as 7 bits bytes 
952            int nbNeededBytes = ( bytes.length * 8 ) / 7;
953            
954            switch ( ( bytes.length - 1 ) % 7 )
955            {
956                case 0 :
957                    if ( ( bytes[0] & 0x0080 ) != 0 )
958                    {
959                        nbNeededBytes++;
960                    }
961                    
962                    break;
963                    
964                case 1 :
965                    if ( ( bytes[0] & 0x00C0 ) != 0 )
966                    {
967                        nbNeededBytes++;
968                    }
969                    
970                    break;
971                    
972                case 2 :
973                    if ( ( bytes[0] & 0x00E0 ) != 0 )
974                    {
975                        nbNeededBytes++;
976                    }
977                    
978                    break;
979                    
980                case 3 : 
981                    if ( ( bytes[0] & 0x00F0 ) != 0 )
982                    {
983                        nbNeededBytes++;
984                    }
985                    
986                    break;
987                    
988                case 4 :
989                    if ( ( bytes[0] & 0x00F8 ) != 0 )
990                    {
991                        nbNeededBytes++;
992                    }
993                    
994                    break;
995                    
996                case 5 :
997                    if ( ( bytes[0] & 0x00FC ) != 0 )
998                    {
999                        nbNeededBytes++;
1000                    }
1001                    
1002                    break;
1003                    
1004                case 6 : 
1005                    if ( ( bytes[0] & 0x00FE ) != 0 )
1006                    {
1007                        nbNeededBytes++;
1008                    }
1009                    
1010                    break;
1011                    
1012                default :
1013                    // Exist to please checkstyle...
1014                    break;
1015            }
1016            
1017            byte[] converted = new byte[nbNeededBytes];
1018            
1019            int posConverted = nbNeededBytes - 1;
1020            int posBytes = bytes.length - 1;
1021            int counter = 0;
1022            byte reminder = 0;
1023            
1024            while ( posBytes >= 0 )
1025            {
1026                byte newByte = ( byte ) ( ( bytes[posBytes] & 0x00FF ) << counter );
1027                converted[posConverted] = ( byte ) ( reminder | newByte | 0x0080 );
1028                reminder = ( byte ) ( ( bytes[posBytes] & 0x00FF ) >> ( 7 - counter ) );
1029                counter =  ( counter + 1 ) % 8; 
1030                posConverted--;
1031                
1032                if ( counter != 0 )
1033                {
1034                    posBytes--;
1035                }
1036                else
1037                {
1038                    reminder = 0;
1039                }
1040            }
1041            
1042            converted[nbNeededBytes - 1] &= 0x7F;
1043            
1044            // Copy the converted bytes in the buffer
1045            System.arraycopy( converted, 0, buffer, posBuffer, nbNeededBytes );
1046            
1047            return nbNeededBytes;
1048        }
1049    }
1050    
1051    
1052    /**
1053     * Returns an OID object representing <code>oidString</code>.  
1054     *  
1055     * @param oidString The string representation of the OID
1056     * @return A new Oid
1057     * @throws DecoderException  When the OID is not valid
1058     */
1059    public static Oid fromString( String oidString ) throws DecoderException
1060    {
1061        if ( ( oidString == null ) || oidString.isEmpty() )
1062        {
1063            throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "empty" ) );
1064        }
1065
1066        // Create a buffer that is wide enough to contain all the values
1067        byte[] buffer = new byte[oidString.length()];
1068
1069        OidFSAState state = OidFSAState.START;
1070        
1071        // A counter of chars used for an arc. In 1.2.45345, this counter will be 5 for the '45345' arc.
1072        int arcNbChars = 0;
1073        
1074        // The position in the buffer where we accumulate the result. 
1075        int bufPos = 0;
1076        
1077        // The position in the OID string where we started to read an arc
1078        int startArc = 0;
1079        
1080        // The number of bytes in the resulting OID byte[]
1081        int nbBytes = 0;
1082        
1083        for ( int i = 0; i < oidString.length(); i++ )
1084        {
1085            switch ( state )
1086            {
1087                case START :
1088                    // (Start) --['0'..'1']--> (A)
1089                    // (start) --['2']--> (F)
1090                    state = processStateStart( oidString, buffer, i );
1091                    break;
1092                    
1093                case STATE_A :
1094                    // (A) --['.']--> (B)
1095                    state = processStateA( oidString, i );
1096
1097                    
1098                    break;
1099                    
1100                case STATE_B :
1101                    // (B) --['0']--> (D)
1102                    // (B) --['1'..'3']--> (C)
1103                    // (B) --['4'..'9']--> (E)
1104                    state = processStateB( oidString, buffer, i );
1105                    
1106                    break;
1107                    
1108                case STATE_C :
1109                    // (C) --['.']--> (K)
1110                    // (C) --['0'..'9']--> (E)
1111                    state = processStateC( oidString, buffer, i );
1112
1113                    // the next arc will be store at position 1 in the buffer
1114                    bufPos = 1;
1115
1116                    break;
1117                    
1118                case STATE_D :
1119                    // (D) --['.']--> (K)
1120                    // Fallthrough
1121                    
1122                case STATE_E :
1123                    // (E) --['.']--> (K)
1124                    state = processStateDE( oidString, buffer, i );
1125                    
1126                    // the next arc will be store at position 1 in teh buffer
1127                    bufPos = 1;
1128
1129                    break;
1130                    
1131                case STATE_F :
1132                    // (F) --['.']--> (G)
1133                    state = processStateF( oidString, i );
1134                    
1135                    break;
1136                    
1137                case STATE_G :
1138                    // (G) --['0']--> (I)
1139                    // (G) --['1'..'9']--> (H)
1140                    state = processStateG( oidString, buffer, i );
1141                    arcNbChars = 1;
1142                    startArc = i;
1143
1144                    break;
1145
1146                case STATE_H :
1147                    // (H) --['.']--> (K)
1148                    // (H) --['0'..'9']--> (J)
1149                    state = processStateH( oidString, buffer, i );
1150                    
1151                    if ( state == OidFSAState.STATE_J )
1152                    {
1153                        // We have already two digits
1154                        arcNbChars = 2;
1155                        bufPos = 0;
1156                    }
1157                    
1158                    break;
1159
1160                case STATE_I :
1161                    // (I) --['.']--> (K)
1162                    state = processStateI( oidString, buffer, i );
1163                    
1164                    // Set the arc position to buffer[1], we haven't yet accumulated digits.
1165                    bufPos = 1;
1166                    
1167                    break;
1168
1169                case STATE_J :
1170                    // (J) --['.']--> (K)
1171                    // (J) --['0'..'9']--> (J)
1172                    state = processStateJ( oidString, buffer, arcNbChars + bufPos, i );
1173                    
1174                    if ( state == OidFSAState.STATE_J )
1175                    {
1176                        // We can increment the number of digit for this arc
1177                        arcNbChars++;
1178                    }
1179                    else
1180                    {
1181                        // We are done with the first arc : convert it
1182                        bufPos += convert( oidString, buffer, bufPos, arcNbChars, 0, true );
1183                    }
1184                    
1185                    break;
1186
1187                case STATE_K :
1188                    startArc = i;
1189                    state = processStateK( oidString, buffer, bufPos, i );
1190                    
1191                    if ( state == OidFSAState.STATE_M )
1192                    { 
1193                        bufPos++;
1194                    }
1195                    else
1196                    {
1197                        arcNbChars = 1;
1198                    }
1199                    
1200                    break;
1201
1202                case STATE_L :
1203                    state = processStateL( oidString, buffer, arcNbChars + bufPos, i );
1204                    
1205                    if ( state == OidFSAState.STATE_L )
1206                    {
1207                        arcNbChars++;
1208                        break;
1209                    }
1210                    else
1211                    {
1212                        // We are done with the arc : convert it
1213                        bufPos += convert( oidString, buffer, startArc, arcNbChars, bufPos, false );
1214                    }
1215
1216                    break;
1217                    
1218                case STATE_M :
1219                    state = processStateM( oidString, i );
1220                    break;
1221                    
1222                default :
1223                    // Exist to please checkstyle...
1224                    break;
1225            }
1226        }
1227        
1228        // End of the string : check that we are in a correct state for a completion
1229        // The only valid exit states are :
1230        // (C) --[]--> (End)
1231        // (D) --[]--> (End)
1232        // (E) --[]--> (End)
1233        // (H) --[]--> (End)
1234        // (I) --[]--> (End)
1235        // (J) --[]--> (End)
1236        // (L) --[]--> (End)
1237        // (M) --[]--> (End)
1238        switch ( state )
1239        {
1240            case STATE_C :
1241                // (C) --[]--> (End)
1242                // fallthrough
1243                
1244            case STATE_D :
1245                // (D) --[]--> (End)
1246                // fallthrough
1247                
1248            case STATE_E :
1249                // (E) --[]--> (End)
1250                // fallthrough
1251
1252            case STATE_H :
1253                // (H) --[]--> (End)
1254                // fallthrough
1255                
1256            case STATE_I :
1257                // (I) --[]--> (End)
1258                byte[] bytes = new byte[1];
1259                bytes[0] = ( byte ) ( buffer[0] | buffer[1] );
1260
1261                return new Oid( oidString, bytes );
1262                
1263            case STATE_J :
1264                // (J) --[]--> (End)
1265                nbBytes = convert( oidString, buffer, 2, arcNbChars, 0, true );
1266                bytes = new byte[nbBytes];
1267                System.arraycopy( buffer, 0, bytes, 0, nbBytes );
1268                
1269                return new Oid( oidString, bytes );
1270
1271            case STATE_L :
1272                bufPos += convert( oidString, buffer, startArc, arcNbChars, bufPos, false );
1273                bytes = new byte[bufPos];
1274                System.arraycopy( buffer, 0, bytes, 0, bufPos );
1275                
1276                return new Oid( oidString, bytes );
1277                
1278            case STATE_M :
1279                bytes = new byte[bufPos];
1280                System.arraycopy( buffer, 0, bytes, 0, bufPos );
1281                
1282                return new Oid( oidString, bytes );
1283                
1284            default :
1285                // This should never happen...
1286                throw new DecoderException( I18n.err( I18n.ERR_00033_INVALID_OID, "Wrong OID" ) );
1287        }
1288    }
1289
1290    
1291    /**
1292     * Returns the length of the encoded <code>byte[]</code> representation.
1293     * 
1294     * @return The length of the byte[]
1295     */
1296    public int getEncodedLength()
1297    {
1298        return oidBytes.length;
1299    }
1300
1301
1302    /**
1303     * {@inheritDoc}
1304     */
1305    @Override
1306    public int hashCode()
1307    {
1308        return oidString.hashCode();
1309    }
1310
1311
1312    /**
1313     * Returns true if <code>oidString</code> is a valid string representation
1314     * of an OID.  This method simply calls {@link #fromString(String)} and 
1315     * returns true if no exception was thrown.  As such, it should not be used 
1316     * in an attempt to check if a string is a valid OID before calling 
1317     * {@link #fromString(String)}.
1318     * 
1319     * @param oidString The string to test
1320     * @return True, if <code>oidString</code> is valid
1321     */
1322    public static boolean isOid( String oidString )
1323    {
1324        try
1325        {
1326            Oid.fromString( oidString );
1327
1328            return true;
1329        }
1330        catch ( DecoderException e )
1331        {
1332            return false;
1333        }
1334    }
1335
1336
1337    /**
1338     * Returns the <code>byte[]</code> representation of the OID. The 
1339     * <code>byte[]</code> that is returned is <i>copied</i> from the internal
1340     * value so as to preserve the immutability of an OID object.  If the 
1341     * output of a call to this method is intended to be written to a stream,
1342     * the {@link #writeBytesTo(OutputStream)} should be used instead as it will
1343     * avoid creating this copy. 
1344     * 
1345     * @return The encoded <code>byte[]</code> representation of the OID.
1346     */
1347    public byte[] toBytes()
1348    {
1349        return Arrays.copyOf( oidBytes, oidBytes.length );
1350    }
1351
1352
1353    /**
1354     * Returns the string representation of the OID.
1355     * 
1356     * @return The string representation of the OID
1357     */
1358    @Override
1359    public String toString()
1360    {
1361        return oidString;
1362    }
1363
1364
1365    /**
1366     * Writes the bytes respresenting this OID to the provided buffer.  This 
1367     * should be used in preference to the {@link #toBytes()} method in order
1368     * to prevent the creation of copies of the actual <code>byte[]</code>.
1369     * 
1370     * @param buffer The buffer to write the bytes into
1371     */
1372    public void writeBytesTo( java.nio.ByteBuffer buffer )
1373    {
1374        buffer.put( oidBytes );
1375    }
1376
1377
1378    /**
1379     * Writes the bytes respresenting this OID to the provided stream.  This 
1380     * should be used in preference to the {@link #toBytes()} method in order
1381     * to prevent the creation of copies of the actual <code>byte[]</code>.
1382     * 
1383     * @param outputStream The stream to write the bytes to
1384     * @throws IOException When we can't write the OID into a Stream
1385     */
1386    public void writeBytesTo( OutputStream outputStream ) throws IOException
1387    {
1388        outputStream.write( oidBytes );
1389    }
1390}