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 set 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 ASN1Set
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 = 2197633272056656073L;
061    
062    
063    
064      // The set of ASN.1 elements contained in this set.
065      private ArrayList<ASN1Element> elements;
066    
067    
068    
069    
070      /**
071       * Creates a new ASN.1 set element with the default type no value.
072       */
073      public ASN1Set()
074      {
075        super(UNIVERSAL_SET_TYPE);
076    
077    
078        this.elements = new ArrayList<ASN1Element>();
079      }
080    
081    
082    
083      /**
084       * Creates a new ASN.1 set element with the specified type and no value.
085       *
086       * @param  type  The BER type for this ASN.1 set.
087       */
088      public ASN1Set(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 set with the default type and the provided set of
100       * elements.
101       *
102       * @param  elements  The set of elements to include in this set.
103       */
104      public ASN1Set(ArrayList<ASN1Element> elements)
105      {
106        super(UNIVERSAL_SET_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 set with the specified type and the provided set of
123       * elements.
124       *
125       * @param  type      The BER type for this set.
126       * @param  elements  The set of elements to include in this set.
127       */
128      public ASN1Set(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 set with the specified type and value and the
147       * provided set of elements.
148       *
149       * @param  type      The BER type for this set.
150       * @param  value     The encoded value for this set.
151       * @param  elements  The set of elements to include in this set.
152       */
153      private ASN1Set(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 set.  The returned
172       * list must not be modified by the caller.
173       *
174       * @return  The set of elements contained in this ASN.1 set.
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 set.
185       *
186       * @param  elements  The set of elements for this ASN.1 set.
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 set element.
206       *
207       * @param  value  The encoded value for this ASN.1 set 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_SET_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 set element.
229       *
230       * @param  element  The ASN.1 element to decode as a set element.
231       *
232       * @return  The decoded ASN.1 set element.
233       *
234       * @throws  ASN1Exception  If the provided ASN.1 element cannot be decoded as
235       *                         a set element.
236       */
237      public static ASN1Set decodeAsSet(ASN1Element element)
238             throws ASN1Exception
239      {
240        if (element == null)
241        {
242          Message message = ERR_ASN1_SET_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 ASN1Set(element.getType(), value, elements);
249      }
250    
251    
252    
253      /**
254       * Decodes the provided byte array as an ASN.1 set element.
255       *
256       * @param  encodedElement  The byte array to decode as an ASN.1 set element.
257       *
258       * @return  The decoded ASN.1 set element.
259       *
260       * @throws  ASN1Exception  If the provided byte array cannot be decoded as an
261       *                         ASN.1 set element.
262       */
263      public static ASN1Set decodeAsSet(byte[] encodedElement)
264             throws ASN1Exception
265      {
266        // First make sure that the array is not null and long enough to contain
267        // a valid ASN.1 set element.
268        if (encodedElement == null)
269        {
270          Message message = ERR_ASN1_SET_DECODE_ARRAY_NULL.get();
271          throw new ASN1Exception(message);
272        }
273    
274        if (encodedElement.length < 2)
275        {
276          Message message = ERR_ASN1_SHORT_ELEMENT.get(encodedElement.length);
277          throw new ASN1Exception(message);
278        }
279    
280    
281        // Next, decode the length.  This allows multi-byte lengths with up to four
282        // bytes used to indicate how many bytes are in the length.
283        byte type = encodedElement[0];
284        int length = (encodedElement[1] & 0x7F);
285        int valueStartPos = 2;
286        if (length != encodedElement[1])
287        {
288          int numLengthBytes = length;
289          if (numLengthBytes > 4)
290          {
291            Message message = ERR_ASN1_INVALID_NUM_LENGTH_BYTES.get(numLengthBytes);
292            throw new ASN1Exception(message);
293          }
294          else if (encodedElement.length < (2 + numLengthBytes))
295          {
296            Message message = ERR_ASN1_TRUNCATED_LENGTH.get(numLengthBytes);
297            throw new ASN1Exception(message);
298          }
299    
300          length = 0x00;
301          valueStartPos = 2 + numLengthBytes;
302          for (int i=0; i < numLengthBytes; i++)
303          {
304            length = (length << 8) | (encodedElement[i+2] & 0xFF);
305          }
306        }
307    
308    
309        // Make sure that the number of bytes left is equal to the number of bytes
310        // in the value.
311        if ((encodedElement.length - valueStartPos) != length)
312        {
313          Message message = ERR_ASN1_LENGTH_MISMATCH.get(
314              length, (encodedElement.length - valueStartPos));
315          throw new ASN1Exception(message);
316        }
317    
318    
319        // Copy the value, decode the elements it contains, and construct the set to
320        // return.
321        byte[] value = new byte[length];
322        System.arraycopy(encodedElement, valueStartPos, value, 0, length);
323        ArrayList<ASN1Element> elements = decodeElements(value);
324        return new ASN1Set(type, value, elements);
325      }
326    
327    
328    
329      /**
330       * Appends a string representation of this ASN.1 set element to the
331       * provided buffer.
332       *
333       * @param  buffer  The buffer to which the information should be appended.
334       */
335      public void toString(StringBuilder buffer)
336      {
337        buffer.append("ASN1Set(type=");
338        buffer.append(byteToHex(getType()));
339        buffer.append(", values={ ");
340    
341        if (! elements.isEmpty())
342        {
343          Iterator<ASN1Element> iterator = elements.iterator();
344    
345          iterator.next().toString(buffer);
346    
347          while (iterator.hasNext())
348          {
349            buffer.append(", ");
350            iterator.next().toString(buffer);
351          }
352        }
353    
354        buffer.append(" })");
355      }
356    
357    
358    
359      /**
360       * Appends a string representation of this protocol element to the provided
361       * buffer.
362       *
363       * @param  buffer  The buffer into which the string representation should be
364       *                 written.
365       * @param  indent  The number of spaces that should be used to indent the
366       *                 resulting string representation.
367       */
368      public void toString(StringBuilder buffer, int indent)
369      {
370        StringBuilder indentBuf = new StringBuilder(indent);
371        for (int i=0 ; i < indent; i++)
372        {
373          indentBuf.append(' ');
374        }
375    
376        buffer.append(indentBuf);
377        buffer.append("ASN.1 Set");
378        buffer.append(EOL);
379    
380        buffer.append(indentBuf);
381        buffer.append("  BER Type:  ");
382        buffer.append(byteToHex(getType()));
383        buffer.append(EOL);
384    
385        if (! elements.isEmpty())
386        {
387          buffer.append(indentBuf);
388          buffer.append("  Decoded Values:");
389          buffer.append(EOL);
390    
391          Iterator<ASN1Element> iterator = elements.iterator();
392    
393          buffer.append(indentBuf);
394          buffer.append("  ");
395          iterator.next().toString(buffer);
396          buffer.append(EOL);
397    
398          while (iterator.hasNext())
399          {
400            buffer.append(indentBuf);
401            buffer.append("  ");
402            iterator.next().toString(buffer);
403            buffer.append(EOL);
404          }
405        }
406    
407    
408        buffer.append(indentBuf);
409        buffer.append("  Value:  ");
410        buffer.append(EOL);
411        byteArrayToHexPlusAscii(buffer, value(), indent+2);
412      }
413    }
414