OSDN Git Service

modifications for Java1.5
[bm-asn1/bm-asn1.git] / jp / bitmeister / asn1 / type / builtin / BIT_STRING.java
1 /*
2  * Copyright 2011 BitMeister Inc.
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 package jp.bitmeister.asn1.type.builtin;
17
18 import java.lang.reflect.Field;
19 import java.lang.reflect.Modifier;
20 import java.util.Arrays;
21 import java.util.HashMap;
22 import java.util.Map;
23 import java.util.Map.Entry;
24
25 import jp.bitmeister.asn1.annotation.ASN1BuiltIn;
26 import jp.bitmeister.asn1.annotation.ASN1Identifier;
27 import jp.bitmeister.asn1.annotation.ASN1NamedBit;
28 import jp.bitmeister.asn1.annotation.ASN1Tag;
29 import jp.bitmeister.asn1.annotation.ASN1XmlTypeName;
30 import jp.bitmeister.asn1.exception.ASN1IllegalArgument;
31 import jp.bitmeister.asn1.exception.ASN1IllegalDefinition;
32 import jp.bitmeister.asn1.exception.ASN1RuntimeException;
33 import jp.bitmeister.asn1.processor.ASN1Visitor;
34 import jp.bitmeister.asn1.type.ASN1TagClass;
35 import jp.bitmeister.asn1.type.ASN1TagMode;
36 import jp.bitmeister.asn1.type.Concatenatable;
37 import jp.bitmeister.asn1.type.PrimitiveType;
38 import jp.bitmeister.asn1.type.SizeCountable;
39 import jp.bitmeister.asn1.value.StringItem;
40
41 /**
42  * Represents ASN.1 'BIT STRING' type.
43  * 
44  * <p>
45  * An instance of this class represents a 'BIT STRING' type data, and has an
46  * array of {@code boolean} value.
47  * </p>
48  * <p>
49  * A sub-class of {@code BIT_STRING} can contain one or more {@code public}
50  * {@code static} {@code final} {@code int} fields which annotated as
51  * {@code @ASN1NamedBit}. Each of these fields represents an index number of a
52  * bit in a bitstring value.
53  * </p>
54  * 
55  * @author WATANABE, Jun. <jwat at bitmeister.jp>
56  * 
57  * @see ASN1NamedBit
58  */
59 @ASN1BuiltIn
60 @ASN1Identifier("BIT STRING")
61 @ASN1XmlTypeName("BIT_STRING")
62 @ASN1Tag(tagClass = ASN1TagClass.UNIVERSAL, value = 3, tagMode = ASN1TagMode.IMPLICIT)
63 public class BIT_STRING extends PrimitiveType<boolean[]> implements
64                 Concatenatable<BIT_STRING>, SizeCountable {
65
66         /**
67          * Contains the maps of named bits for all of the sub-types of 'BIT STRING'.
68          */
69         private static final Map<Class<? extends BIT_STRING>, Map<Integer, String>> NAMED_BIT_MAP = new HashMap<Class<? extends BIT_STRING>, Map<Integer, String>>();
70
71         static {
72                 NAMED_BIT_MAP.put(BIT_STRING.class, new HashMap<Integer, String>());
73         }
74
75         /**
76          * Returns the map of named bits for the type. The {@code Map} maps value to
77          * identifier.
78          * 
79          * @param type
80          *            The type.
81          * @return The map of enumerations.
82          */
83         private static Map<Integer, String> getNamedBitMap(
84                         Class<? extends BIT_STRING> type) {
85                 if (NAMED_BIT_MAP.containsKey(type)) {
86                         return NAMED_BIT_MAP.get(type);
87                 }
88                 Map<Integer, String> map = new HashMap<Integer, String>();
89                 for (Field f : type.getDeclaredFields()) {
90                         if (!f.isAnnotationPresent(ASN1NamedBit.class)) {
91                                 continue;
92                         }
93                         final int modifier = Modifier.PUBLIC | Modifier.STATIC
94                                         | Modifier.FINAL;
95                         ASN1Identifier id = f.getAnnotation(ASN1Identifier.class);
96                         String fieldId = id != null ? id.value() : f.getName();
97                         if ((f.getModifiers() & modifier) != modifier) {
98                                 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
99                                 ex.setMessage(
100                                                 "A named bit must be a public static final field.",
101                                                 null, type, fieldId, null);
102                                 throw ex;
103                         }
104                         Integer value;
105                         if (f.getType() == int.class) {
106                                 try {
107                                         value = f.getInt(null);
108                                 } catch (Exception e) {
109                                         ASN1RuntimeException ex = new ASN1RuntimeException();
110                                         ex.setMessage(
111                                                         "Failed to retrieve the value from the field.", e,
112                                                         type, fieldId, null);
113                                         throw ex;
114                                 }
115                                 if (value < 0) {
116                                         ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
117                                         ex.setMessage(
118                                                         "A value of named bit must be a non-negative value.",
119                                                         null, type, fieldId, null);
120                                         throw ex;
121                                 }
122                         } else {
123                                 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
124                                 ex.setMessage("A named bit must be an 'int' field.", null,
125                                                 type, fieldId, null);
126                                 throw ex;
127                         }
128                         if (map.containsKey(value)) {
129                                 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
130                                 ex.setMessage(
131                                                 "A value of named bit shall be distinct from all other named bits in the type.",
132                                                 null, type, fieldId, null);
133                                 throw ex;
134                         }
135                         map.put(value, fieldId);
136                 }
137                 @SuppressWarnings("unchecked")
138                 Class<? extends BIT_STRING> parent = (Class<? extends BIT_STRING>) type
139                                 .getSuperclass();
140                 if (parent != BIT_STRING.class) {
141                         if (!map.isEmpty()) {
142                                 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
143                                 ex.setMessage(
144                                                 "A class that does not extend 'BIT_STRING' directly, can not have own named bits.",
145                                                 null, type, null, null);
146                                 throw ex;
147                         }
148                         map = getNamedBitMap(parent);
149                 }
150                 NAMED_BIT_MAP.put(type, map);
151                 return map;
152         }
153
154         /**
155          * Instantiates an empty {@code BIT_STRING}.
156          */
157         public BIT_STRING() {
158         }
159
160         /**
161          * Instantiates a {@code BIT_STRING} and initialize it with the array of
162          * {@code boolean} value.
163          * 
164          * @param array
165          *            The value to be assigned.
166          */
167         public BIT_STRING(boolean... array) {
168                 set(array);
169         }
170
171         /**
172          * Instantiates a {@code BIT_STRING} and initialize it with the
173          * {@link StringItem}.
174          * 
175          * @param item
176          *            The value to be assigned.
177          */
178         public BIT_STRING(StringItem item) {
179                 set(item);
180         }
181
182         /**
183          * Instantiates a {@code BIT_STRING} and sets {@code true} to bits which
184          * specified by the indexes.
185          * 
186          * @param indexes
187          *            Indexes of bits to be set true.
188          */
189         public BIT_STRING(int... indexes) {
190                 set(indexes);
191         }
192
193         /**
194          * Sets the {@link StringItem} value to this data.
195          * 
196          * @param item
197          *            The value to be set.
198          */
199         public void set(StringItem item) {
200                 set(item.toBinArray());
201         }
202
203         /**
204          * Sets {@code true} to bits specified by index numbers. If the
205          * {@code boolean} array of the value of this data does not have enough size
206          * to hold the bit specified by the biggest index, it will be expanded
207          * automatically.
208          * 
209          * @param indexes
210          *            Indexes of bits to be set true.
211          */
212         public void set(int ...indexes) {
213                 Arrays.sort(indexes);
214                 expand(indexes[indexes.length - 1]);
215                 for (int e: indexes) {
216                         value()[e] = true;
217                 }
218         }
219         
220         /**
221          * Sets {@code true} to bits specified by the name of bit. 
222          * 
223          * @param nameOfBit
224          *            The name of bit.
225          */
226         public void set(String nameOfBit) {
227                 for (Entry<Integer, String> e: getNamedBitMap(getClass()).entrySet()) {
228                         if (e.getValue().equals(nameOfBit)) {
229                                 set(e.getKey());
230                                 return;
231                         }
232                 }
233                 ASN1IllegalArgument e = new ASN1IllegalArgument();
234                 e.setMessage("The name '" + nameOfBit + "' is not defined in this type.", null, getClass(), null, null);
235                 throw e;
236         }
237
238         /**
239          * Sets {@code false} to bits specified by index numbers. If the
240          * {@code boolean} array of the value of this data does not have enough size
241          * to hold the bit specified by the biggest index, it will be expanded
242          * automatically.
243          * 
244          * @param indexes
245          *            Indexes of bits to be set false.
246          */
247         public void unset(int ...indexes) {
248                 Arrays.sort(indexes);
249                 expand(indexes[indexes.length - 1]);
250                 for (int e: indexes) {
251                         value()[e] = false;
252                 }
253         }
254         
255         /**
256          * Contracts the {@code boolean} array of the value of this data to enough
257          * size to hold the last {@code true} bit.
258          */
259         public void contract() {
260                 if (value() == null) {
261                         set(new boolean[0]);
262                         return;
263                 }
264                 int index = size();
265                 for (index--; index >= 0; index--) {
266                         if (value()[index]) {
267                                 break;
268                         }
269                 }
270                 if (index + 1 != size()) {
271                         boolean[] newValue = new boolean[index + 1];
272                         System.arraycopy(value(), 0, newValue, 0, newValue.length);
273                         set(newValue);
274                 }
275         }
276
277         /**
278          * Returns the value of a bit specified by the index number.
279          * 
280          * @param index
281          *            The index number.
282          * @return The value of the bit.
283          */
284         public boolean bit(int index) {
285                 if (index < size()) {
286                         return value()[index];
287                 }
288                 return false;
289         }
290
291         /**
292          * Tests if the type has named bits definition.
293          * 
294          * @return {@code true} when the type has named bits.
295          */
296         public boolean hasNamedBits() {
297                 return !getNamedBitMap(getClass()).isEmpty();
298         }
299
300         /**
301          * Returns the indentifier of the bit specified by the index number.
302          * 
303          * @param index
304          *            The index number of a bit.
305          * @return The identifier of the bit.
306          */
307         public String nameOfBit(int index) {
308                 return getNamedBitMap(getClass()).get(index);
309         }
310
311         /**
312          * Expands the {@code boolean} array of the value of this data to
313          * enough size to hold the bit specified by the index.
314          * 
315          * @param index
316          *            The index of a bit.
317          */
318         private void expand(int index) {
319                 if (size() <= index) {
320                         boolean[] newValue = new boolean[index + 1];
321                         if (size() > 0) {
322                                 System.arraycopy(value(), 0, newValue, 0, size());
323                         }
324                         set(newValue);
325                 }
326         }
327         
328         /*
329          * (non-Javadoc)
330          * 
331          * @see jp.bitmeister.asn1.type.Concatenatable#concatenate(null)
332          */
333         public void concatenate(BIT_STRING data) {
334                 if (data == null) {
335                         return;
336                 }
337                 if (!getClass().equals(data.getClass())) {
338                         ASN1IllegalArgument ex = new ASN1IllegalArgument();
339                         ex.setMessage(
340                                         "The type '"
341                                                         + data.specification().fullIdentifier()
342                                                         + "' of the data to be concatenated is not the same type of this data.",
343                                         null, getClass(), null, null);
344                         throw ex;
345                 }
346                 if (data.hasValue()) {
347                         if (!hasValue()) {
348                                 set(data.value());
349                         } else {
350                                 boolean[] newValue = new boolean[value().length
351                                                 + data.value().length];
352                                 System.arraycopy(value(), 0, newValue, 0, value().length);
353                                 System.arraycopy(data.value(), 0, newValue, value().length,
354                                                 data.value().length);
355                                 set(newValue);
356                         }
357                 }
358         }
359
360         /*
361          * (non-Javadoc)
362          * 
363          * @see jp.bitmeister.asn1.type.SizeCountable#size()
364          */
365         public int size() {
366                 if (value() == null) {
367                         return 0;
368                 }
369                 return value().length;
370         }
371
372         /*
373          * (non-Javadoc)
374          * 
375          * @see jp.bitmeister.asn1.type.PrimitiveType#cloneValue()
376          */
377         @Override
378         protected boolean[] cloneValue() {
379                 if (value() == null) {
380                         return null;
381                 }
382                 return value().clone();
383         }
384
385         /*
386          * (non-Javadoc)
387          * 
388          * @see jp.bitmeister.asn1.type.PrimitiveType#valueEquals(java.lang.Object)
389          */
390         @Override
391         public boolean valueEquals(Object other) {
392                 if (other instanceof BIT_STRING) {
393                         boolean[] otherValue = ((BIT_STRING) other).value();
394                         if (value() != null) {
395                                 return Arrays.equals(value(), otherValue);
396                         }
397                         return otherValue == null;
398                 }
399                 return false;
400         }
401
402         /* 
403          * (non-Javadoc)
404          * 
405          * @see jp.bitmeister.asn1.type.PrimitiveType#hashCode()
406          */
407         @Override
408         public int hashCode() {
409                 return Arrays.hashCode(value());
410         }
411
412         /*
413          * (non-Javadoc)
414          * 
415          * @see
416          * jp.bitmeister.asn1.type.ASN1Type#accept(jp.bitmeister.asn1.processor.
417          * ASN1Visitor)
418          */
419         @Override
420         public <R, E extends Throwable> R accept(ASN1Visitor<R, E> visitor) throws E {
421                 return visitor.visit(this);
422         }
423
424 }