001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.protocols.asn1;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.ArrayList;
033    import java.util.Iterator;
034    
035    import static org.opends.messages.ProtocolMessages.*;
036    import static org.opends.server.protocols.asn1.ASN1Constants.*;
037    import static org.opends.server.util.ServerConstants.*;
038    import static org.opends.server.util.StaticUtils.*;
039    
040    
041    
042    /**
043     * This class defines the data structures and methods to use when interacting
044     * with ASN.1 sequence elements.
045     */
046    @org.opends.server.types.PublicAPI(
047         stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
048         mayInstantiate=true,
049         mayExtend=false,
050         mayInvoke=true)
051    public final class ASN1Sequence
052           extends ASN1Element
053    {
054      /**
055       * The serial version identifier required to satisfy the compiler because this
056       * class implements the <CODE>java.io.Serializable</CODE> interface.  This
057       * value was generated using the <CODE>serialver</CODE> command-line utility
058       * included with the Java SDK.
059       */
060      private static final long serialVersionUID = 694649828357992307L;
061    
062    
063    
064      // The set of ASN.1 elements contained in this sequence.
065      private ArrayList<ASN1Element> elements;
066    
067    
068    
069    
070      /**
071       * Creates a new ASN.1 sequence element with the default type no value.
072       */
073      public ASN1Sequence()
074      {
075        super(UNIVERSAL_SEQUENCE_TYPE);
076    
077    
078        this.elements = new ArrayList<ASN1Element>();
079      }
080    
081    
082    
083      /**
084       * Creates a new ASN.1 sequence element with the specified type and no value.
085       *
086       * @param  type  The BER type for this ASN.1 sequence.
087       */
088      public ASN1Sequence(byte type)
089      {
090        super(type);
091    
092    
093        this.elements = new ArrayList<ASN1Element>();
094      }
095    
096    
097    
098      /**
099       * Creates a new ASN.1 sequence with the default type and the provided set of
100       * elements.
101       *
102       * @param  elements  The set of elements to include in this sequence.
103       */
104      public ASN1Sequence(ArrayList<ASN1Element> elements)
105      {
106        super(UNIVERSAL_SEQUENCE_TYPE, encodeValue(elements));
107    
108    
109        if (elements == null)
110        {
111          this.elements = new ArrayList<ASN1Element>();
112        }
113        else
114        {
115          this.elements = elements;
116        }
117      }
118    
119    
120    
121      /**
122       * Creates a new ASN.1 sequence with the specified type and the provided set
123       * of elements.
124       *
125       * @param  type      The BER type for this sequence.
126       * @param  elements  The set of elements to include in this sequence.
127       */
128      public ASN1Sequence(byte type, ArrayList<ASN1Element> elements)
129      {
130        super(type, encodeValue(elements));
131    
132    
133        if (elements == null)
134        {
135          this.elements = new ArrayList<ASN1Element>();
136        }
137        else
138        {
139          this.elements = elements;
140        }
141      }
142    
143    
144    
145      /**
146       * Creates a new ASN.1 sequence with the specified type and value and the
147       * provided set of elements.
148       *
149       * @param  type      The BER type for this sequence.
150       * @param  value     The encoded value for this sequence.
151       * @param  elements  The set of elements to include in this sequence.
152       */
153      private ASN1Sequence(byte type, byte[] value, ArrayList<ASN1Element> elements)
154      {
155        super(type, value);
156    
157    
158        if (elements == null)
159        {
160          this.elements = new ArrayList<ASN1Element>();
161        }
162        else
163        {
164          this.elements = elements;
165        }
166      }
167    
168    
169    
170      /**
171       * Retrieves the set of elements contained in this ASN.1 sequence.  The
172       * returned list must not be modified by the caller.
173       *
174       * @return  The set of elements contained in this ASN.1 sequence.
175       */
176      public ArrayList<ASN1Element> elements()
177      {
178        return elements;
179      }
180    
181    
182    
183      /**
184       * Specifies the set of elements for this ASN.1 sequence.
185       *
186       * @param  elements  The set of elements for this ASN.1 sequence.
187       */
188      public void setElements(ArrayList<ASN1Element> elements)
189      {
190        if (elements == null)
191        {
192          this.elements.clear();
193          setValueInternal(NO_VALUE);
194        }
195        else
196        {
197          this.elements = elements;
198          setValueInternal(encodeValue(elements));
199        }
200      }
201    
202    
203    
204      /**
205       * Specifies the value for this ASN.1 sequence element.
206       *
207       * @param  value  The encoded value for this ASN.1 sequence element.
208       *
209       * @throws  ASN1Exception  If the provided array is null or cannot be decoded
210       *                         as a set of ASN.1 elements.
211       */
212      public void setValue(byte[] value)
213             throws ASN1Exception
214      {
215        if (value == null)
216        {
217          Message message = ERR_ASN1_SEQUENCE_SET_VALUE_NULL.get();
218          throw new ASN1Exception(message);
219        }
220    
221        elements = decodeElements(value);
222        setValueInternal(value);
223      }
224    
225    
226    
227      /**
228       * Decodes the provided ASN.1 element as a sequence element.
229       *
230       * @param  element  The ASN.1 element to decode as a sequence element.
231       *
232       * @return  The decoded ASN.1 sequence element.
233       *
234       * @throws  ASN1Exception  If the provided ASN.1 element cannot be decoded as
235       *                         a sequence element.
236       */
237      public static ASN1Sequence decodeAsSequence(ASN1Element element)
238             throws ASN1Exception
239      {
240        if (element == null)
241        {
242          Message message = ERR_ASN1_SEQUENCE_DECODE_ELEMENT_NULL.get();
243          throw new ASN1Exception(message);
244        }
245    
246        byte[] value = element.value();
247        ArrayList<ASN1Element> elements = decodeElements(value);
248        return new ASN1Sequence(element.getType(), value, elements);
249      }
250    
251    
252    
253      /**
254       * Decodes the provided byte array as an ASN.1 sequence element.
255       *
256       * @param  encodedElement  The byte array to decode as an ASN.1 sequence
257       *                         element.
258       *
259       * @return  The decoded ASN.1 sequence element.
260       *
261       * @throws  ASN1Exception  If the provided byte array cannot be decoded as an
262       *                         ASN.1 sequence element.
263       */
264      public static ASN1Sequence decodeAsSequence(byte[] encodedElement)
265             throws ASN1Exception
266      {
267        // First make sure that the array is not null and long enough to contain
268        // a valid ASN.1 sequence element.
269        if (encodedElement == null)
270        {
271          Message message = ERR_ASN1_SEQUENCE_DECODE_ARRAY_NULL.get();
272          throw new ASN1Exception(message);
273        }
274    
275        if (encodedElement.length < 2)
276        {
277          Message message = ERR_ASN1_SHORT_ELEMENT.get(encodedElement.length);
278          throw new ASN1Exception(message);
279        }
280    
281    
282        // Next, decode the length.  This allows multi-byte lengths with up to four
283        // bytes used to indicate how many bytes are in the length.
284        byte type = encodedElement[0];
285        int length = (encodedElement[1] & 0x7F);
286        int valueStartPos = 2;
287        if (length != encodedElement[1])
288        {
289          int numLengthBytes = length;
290          if (numLengthBytes > 4)
291          {
292            Message message = ERR_ASN1_INVALID_NUM_LENGTH_BYTES.get(numLengthBytes);
293            throw new ASN1Exception(message);
294          }
295          else if (encodedElement.length < (2 + numLengthBytes))
296          {
297            Message message = ERR_ASN1_TRUNCATED_LENGTH.get(numLengthBytes);
298            throw new ASN1Exception(message);
299          }
300    
301          length = 0x00;
302          valueStartPos = 2 + numLengthBytes;
303          for (int i=0; i < numLengthBytes; i++)
304          {
305            length = (length << 8) | (encodedElement[i+2] & 0xFF);
306          }
307        }
308    
309    
310        // Make sure that the number of bytes left is equal to the number of bytes
311        // in the value.
312        if ((encodedElement.length - valueStartPos) != length)
313        {
314          Message message = ERR_ASN1_LENGTH_MISMATCH.get(
315              length, (encodedElement.length - valueStartPos));
316          throw new ASN1Exception(message);
317        }
318    
319    
320        // Copy the value, decode the elements it contains, and construct the
321        // sequence to return.
322        byte[] value = new byte[length];
323        System.arraycopy(encodedElement, valueStartPos, value, 0, length);
324        ArrayList<ASN1Element> elements = decodeElements(value);
325        return new ASN1Sequence(type, value, elements);
326      }
327    
328    
329    
330      /**
331       * Decodes the provided information as an ASN.1 sequence.
332       *
333       * @param  type          The BER type to use for the sequence.
334       * @param  encodedValue  The encoded value to decode as the set of elements
335       *                       for the sequence.
336       *
337       * @return  The decoded ASN.1 sequence element.
338       *
339       * @throws  ASN1Exception  If the provided byte array cannot be decoded as an
340       *                         ASN.1 sequence element.
341       */
342      public static ASN1Sequence decodeAsSequence(byte type, byte[] encodedValue)
343             throws ASN1Exception
344      {
345        ArrayList<ASN1Element> elements = decodeElements(encodedValue);
346        return new ASN1Sequence(type, encodedValue, elements);
347      }
348    
349    
350    
351      /**
352       * Appends a string representation of this ASN.1 sequence element to the
353       * provided buffer.
354       *
355       * @param  buffer  The buffer to which the information should be appended.
356       */
357      public void toString(StringBuilder buffer)
358      {
359        buffer.append("ASN1Sequence(type=");
360        buffer.append(byteToHex(getType()));
361        buffer.append(", values={ ");
362    
363        if (! elements.isEmpty())
364        {
365          Iterator<ASN1Element> iterator = elements.iterator();
366    
367          iterator.next().toString(buffer);
368    
369          while (iterator.hasNext())
370          {
371            buffer.append(", ");
372            iterator.next().toString(buffer);
373          }
374        }
375    
376        buffer.append(" })");
377      }
378    
379    
380    
381      /**
382       * Appends a string representation of this protocol element to the provided
383       * buffer.
384       *
385       * @param  buffer  The buffer into which the string representation should be
386       *                 written.
387       * @param  indent  The number of spaces that should be used to indent the
388       *                 resulting string representation.
389       */
390      public void toString(StringBuilder buffer, int indent)
391      {
392        StringBuilder indentBuf = new StringBuilder(indent);
393        for (int i=0 ; i < indent; i++)
394        {
395          indentBuf.append(' ');
396        }
397    
398        buffer.append(indentBuf);
399        buffer.append("ASN.1 Sequence");
400        buffer.append(EOL);
401    
402        buffer.append(indentBuf);
403        buffer.append("  BER Type:  ");
404        buffer.append(byteToHex(getType()));
405        buffer.append(EOL);
406    
407        if (! elements.isEmpty())
408        {
409          buffer.append(indentBuf);
410          buffer.append("  Decoded Values:");
411          buffer.append(EOL);
412    
413          Iterator<ASN1Element> iterator = elements.iterator();
414    
415          buffer.append(indentBuf);
416          buffer.append("  ");
417          iterator.next().toString(buffer);
418          buffer.append(EOL);
419    
420          while (iterator.hasNext())
421          {
422            buffer.append(indentBuf);
423            buffer.append("  ");
424            iterator.next().toString(buffer);
425            buffer.append(EOL);
426          }
427        }
428    
429    
430        buffer.append(indentBuf);
431        buffer.append("  Value:  ");
432        buffer.append(EOL);
433        byteArrayToHexPlusAscii(buffer, value(), indent+2);
434      }
435    }
436