View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.asn1.ber ;
18  
19  
20  import org.apache.asn1.codec.DecoderException;
21  
22  
23  /***
24   * The Tag component of a BER TLV Tuple.
25   *
26   * @author <a href="mailto:dev@directory.apache.org">
27   * Apache Directory Project</a>
28   * @version $Rev: 157644 $
29   */
30  public class Tag
31  {
32      /*** tag flag for the primitive/constructed bit - 0010 0000 - 0x20 */
33      private static final int CONSTRUCTED_FLAG = 0x20 ;
34      /*** tag mask for the primitive/constructed bit - 1101 1111 - 0xDF */
35      // private static final int CONSTRUCTED_MASK = ~CONSTRUCTED_FLAG ;
36  
37      /*** tag mask for the short tag format - 0001 1111 - 0x1F */
38      static final int SHORT_MASK = 0x1F ;
39      /*** tag mask for the long tag format - 0111 1111 - 0x7F */
40      static final int LONG_MASK = 0x7F ;
41      /*** tag flag indicating the use of the long tag encoding form */
42      private static final int LONG_FLAG = 0x80 ;
43  
44      /*** the max id size with one tag octet */
45      private static final int ONE_OCTET_IDMAX = 30 ;
46      /*** the max id size with two tag octets */
47      private static final int TWO_OCTET_IDMAX = (1<<7)-1 ;
48      /*** the max id size with three tag octets */
49      private static final int THREE_OCTET_IDMAX = (1<<14)-1 ;
50      /*** the max id size with four tag octets */
51      private static final int FOUR_OCTET_IDMAX = (1<<21)-1 ;
52  
53      /*** tag id */
54      private int id = 0 ;
55      /*** whether or not this tag represents a primitive type */
56      private boolean isPrimitive = true ;
57      /*** whether or not this tag has been fixated */
58      private boolean isFixated = false ;
59      /*** the type class of this tag */
60      private TypeClass typeClass = TypeClass.APPLICATION ;
61      /*** buffer backed by a Java int to collect the arriving tag octets */
62      private final TagOctetCollector buf = new TagOctetCollector() ;
63  
64  
65      /***
66       * Clears this tag's data of all bytes and values calculated so all is as it
67       * was when this instance was created.
68       */
69      void clear()
70      {
71          id = 0 ;
72          isFixated = false ;
73          isPrimitive = true ;
74          typeClass = TypeClass.APPLICATION ;
75          buf.clear() ;
76      }
77      
78      
79      /***
80       * Fixates the data within this Tag calculating all the derived 
81       * properties from the existing set of octets.  While fixated octets
82       * cannot be added.
83       * 
84       * @throws DecoderException if this Tag is invalid
85       */
86      void fixate() throws DecoderException
87      {
88          isFixated = true ;
89          id = getTagId( buf ) ;
90          isPrimitive = isPrimitive( buf.get( 0 ) ) ;
91          typeClass = TypeClass.getTypeClass( buf.get( 0 ) ) ;
92      }
93      
94      
95      /***
96       * Adds an octet to this Tag and as a size effect may fixate the Tag if
97       * all the expected data has arrived.
98       * 
99       * @param octet the 8 bit byte to add
100      */
101     void add( byte octet ) throws DecoderException
102     {
103         if ( isFixated )
104         {  
105             throw new IllegalStateException( "data added to fixated tag" ) ;
106         }
107         
108         buf.put( octet ) ;
109         
110         if ( buf.size() == 1 )
111         {
112             // if its the short form so we just fixate now!
113             if ( ( SHORT_MASK & octet ) != SHORT_MASK )
114             {
115                 fixate() ;
116             }
117         }
118         
119         /*
120          * From here on we're dealing with the long form of the tag.  The
121          * terminating octet for the long form uses a 0 for the most 
122          * significant bit to flag the end of the train of octets for the 
123          * tag id.
124          */ 
125         else if ( ( octet & LONG_FLAG ) == 0 )
126         {
127             fixate() ;
128         }
129     }
130     
131     
132     /***
133      * Gets a copy of the octets composing this Tag.
134      * 
135      * @return the octets representing this Tag
136      */
137     public byte[] getOctets()
138     {
139         return buf.toArray() ;
140     }
141     
142     
143     /***
144      * Gets the number of octets in this Tag.
145      * 
146      * @return the number of octets within this Tag
147      */
148     public int size()
149     {
150         return buf.size() ;
151     }
152 
153     
154     /***
155      * Gets the id.
156      * 
157      * @return the id
158      */
159     public int getId()
160     {
161         return id ;
162     }
163     
164     
165     /***
166      * Gets the raw tag as it is stuffed into a primitive int.
167      * 
168      * @return a primitive int stuffed with the first four octets of the tag
169      */
170     public int getRawTag()
171     {
172         return buf.getIntValue() ;
173     }
174     
175     
176     /***
177      * Checks to see if the tag represented by this Tag is primitive or 
178      * constructed.
179      * 
180      * @return true if it is primitive, false if it is constructed
181      */
182     public boolean isPrimitive()
183     {
184         return isPrimitive ;
185     }
186     
187     
188     /***
189      * Checks to see if the tag has been fixated.
190      * 
191      * @return true if it is fixated, false if not
192      */
193     public boolean isFixated()
194     {
195         return isFixated ;
196     }
197     
198     
199     /***
200      * Gets the type class for this Tag.
201      * 
202      * @return the typeClass for this Tag
203      */
204     public TypeClass getTypeClass()
205     {
206         return typeClass ;
207     }
208 
209 
210     // ------------------------------------------------------------------------
211     // Utility Methods For Dealing With Tags and Tag Octets
212     // ------------------------------------------------------------------------
213 
214 
215     /***
216      * Sets the id of a tag encoded as a Java primitive integer.
217      *
218      * @param encodedTag the tag encoded as a Java primitive integer
219      * @param id the new tag id to set within the encodedTag
220      * @return the modified Java primitive int encoded tag with the new tag id
221      */
222     public final static int setIntEncodedId( int encodedTag, int id )
223     {
224         if ( id <= ONE_OCTET_IDMAX )
225         {
226             encodedTag |= ( id << 24 ) ;
227         }
228         else if ( id <= TWO_OCTET_IDMAX )
229         {
230             encodedTag |= ( SHORT_MASK << 24 ) ;
231             encodedTag |= ( id & 0x0000007F ) << 16 ;
232         }
233         else if ( id <= THREE_OCTET_IDMAX )
234         {
235             encodedTag |= ( SHORT_MASK << 24 ) ;
236             encodedTag |= ( id & 0x00003F80 ) << 9 ;
237             encodedTag |= ( id & 0x0000007F ) << 8 ;
238             encodedTag |= 0x00800000 ;
239         }
240         else if ( id <= FOUR_OCTET_IDMAX )
241         {
242             encodedTag |= ( SHORT_MASK << 24 ) ;
243             encodedTag |= ( id & 0x001FC000 ) << 2 ;
244             encodedTag |= ( id & 0x00003F80 ) << 1 ;
245             encodedTag |= ( id & 0x0000007F ) ;
246             encodedTag |= 0x00808000 ;
247         }
248         else
249         {
250             String msg = "Id argument value of " + id
251                     + " was greater than the maximum supported id of "
252                     + FOUR_OCTET_IDMAX ;
253             throw new IllegalArgumentException( msg ) ;
254         }
255 
256         return encodedTag;
257     }
258 
259 
260     /***
261      * Assembles the Java primitive int based encoding for a tag using a set
262      * of parameters.
263      *
264      * @param type
265      * @param id
266      * @param isConstructed
267      * @return
268      */
269     public final static int
270             getIntEncodedTag( TypeClass type, int id, boolean isConstructed )
271     {
272         int value = type.getValue() << 24 ;
273 
274         if ( isConstructed )
275         {
276             value |= ( CONSTRUCTED_FLAG << 24 ) ;
277         }
278 
279         value = setIntEncodedId( value, id );
280 
281         return value ;
282     }
283 
284 
285     /***
286      * Gets the tag id of a TLV from tag octets.
287      *
288      * @param octets the set of octets needed to determine the tag value
289      *      (a.k.a identifier octets)
290      * @return the tag id
291      * @throws DecoderException if the id cannot be determined due to
292      *      type limitations of this method's return type.
293      */
294     public final static int getTagId( byte[] octets )
295         throws DecoderException
296     {
297         if ( octets.length > 4 )
298         {
299             /*
300              * If this exception is ever thrown which is highly unlikely, then
301              * we need to switch to another data type to return because after
302              * 4 bytes the int can no longer hold the number.
303              */
304             throw new DecoderException( "Tag number is too large." ) ;
305         }
306 
307         int id = octets[0] & SHORT_MASK ;
308 
309         // if bits are not all 1's then return the value which is less than 31
310         if ( id != SHORT_MASK && octets.length == 1 )
311         {
312             return id ;
313         }
314 
315         // clear the id now
316         id = 0 ;
317 
318         // calculate tag value w/ long tag format
319         for( int ii = 1 ; ii < octets.length; ii++ )
320         {
321         	id = (id << 7) | (octets[ii] & LONG_MASK);
322         }
323 
324         return id ;
325     }
326 
327 
328     /***
329      * Gets the tag id of a TLV from tag octets encoded as a Java primitive int.
330      *
331      * @param octets the tag octets encoded as a Java primitive int
332      * @return the tag id
333      */
334     public final static int getTagId( int octets )
335     {
336         // set id to the most significant octet in the int
337         int id = ( octets >> 24 ) & SHORT_MASK;
338 
339         // if bits are not all 1's then return the value which is less than 31
340         if ( id != SHORT_MASK )
341         {
342             return id;
343         }
344 
345         // clear the id now to prepare for long tag form
346         id = 0;
347 
348         // get the second most significant octet from int and apply it to the id
349         int octet = ( octets & 0x00ff0000 ) >> 16;
350         id |= octet & LONG_MASK ;
351 
352         // if this is the last octet in long form return
353         if ( ( octet & 0x80 ) == 0 )
354         {
355             return id ;
356         }
357 
358         // clear octet and get the third most significant octet and apply it
359         octet = 0;
360         octet = ( octets & 0x0000ff00 ) >> 8;
361 
362         if ( octet == 0 )
363         {
364             return id << 7 ;
365         }
366 
367         id <<= 7;
368         id |= octet & LONG_MASK;
369 
370         // if this is the last octet in long form return
371         if ( ( octet & 0x80 ) == 0 )
372         {
373             return id ;
374         }
375 
376         // clear octet and get the least significant octet and apply it
377         octet = 0;
378         octet = octets & 0x000000ff;
379         id <<= 7;
380         id |= octet & LONG_MASK;
381 
382         return id ;
383     }
384 
385 
386     /***
387      * Gets the tag id of a TLV from the tag octets.
388      * 
389      * @param octets the set of octets needed to determine the tag value 
390      *      (a.k.a identifier octets)
391      * @return the tag id
392      */
393     public final static int getTagId( TagOctetCollector octets )
394     {
395         int id = octets.get( 0 ) & SHORT_MASK ;
396         
397         // if bits are not all 1's then return the value which is less than 31
398         if ( id != SHORT_MASK && octets.size() == 1 )
399         {
400             return id ;
401         }
402         
403         // clear the id now
404         id = 0 ;
405     
406         // calculate tag value w/ long tag format
407         for( int ii = 1 ; ii < octets.size(); ii++ )
408         {    
409         	id = (id << 7) | (octets.get(ii) & LONG_MASK);
410         }
411         
412         return id ;
413     }
414 
415 
416     /***
417      * Checks to see if the tag is a primitive.
418      * 
419      * @param octet the first octet of the tag
420      * @return true if this tag is of the simple type, false if constructed
421      */
422     public final static boolean isPrimitive( int octet )
423     {
424         return ( octet & CONSTRUCTED_FLAG ) == 0 ;
425     }
426 
427 
428     /***
429      * Checks to see if the tag is constructed.
430      * 
431      * @param octet the first octet of the tag
432      * @return true if constructed, false if primitive
433      */
434     public final static boolean isConstructed( int octet )
435     {
436         return ( octet & CONSTRUCTED_FLAG ) == CONSTRUCTED_FLAG ;
437     }
438 
439 
440     public static boolean isRawTagConstructed( int rawTag )
441     {
442         if ( ( rawTag & 0x20000000 ) > 0 )
443         {
444             return true;
445         }
446 
447         return false;
448     }
449 }