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