2 * Copyright 2011 BitMeister Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package jp.bitmeister.asn1.type.builtin;
18 import java.lang.reflect.Field;
19 import java.lang.reflect.Modifier;
20 import java.util.Arrays;
21 import java.util.HashMap;
23 import java.util.Map.Entry;
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;
42 * Represents ASN.1 'BIT STRING' type.
45 * An instance of this class represents a 'BIT STRING' type data, and has an
46 * array of {@code boolean} value.
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.
55 * @author WATANABE, Jun. <jwat at bitmeister.jp>
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 {
67 * Contains the maps of named bits for all of the sub-types of 'BIT STRING'.
69 private static final Map<Class<? extends BIT_STRING>, Map<Integer, String>> NAMED_BIT_MAP = new HashMap<Class<? extends BIT_STRING>, Map<Integer, String>>();
72 NAMED_BIT_MAP.put(BIT_STRING.class, new HashMap<Integer, String>());
76 * Returns the map of named bits for the type. The {@code Map} maps value to
81 * @return The map of enumerations.
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);
88 Map<Integer, String> map = new HashMap<Integer, String>();
89 for (Field f : type.getDeclaredFields()) {
90 if (!f.isAnnotationPresent(ASN1NamedBit.class)) {
93 final int modifier = Modifier.PUBLIC | Modifier.STATIC
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();
100 "A named bit must be a public static final field.",
101 null, type, fieldId, null);
105 if (f.getType() == int.class) {
107 value = f.getInt(null);
108 } catch (Exception e) {
109 ASN1RuntimeException ex = new ASN1RuntimeException();
111 "Failed to retrieve the value from the field.", e,
112 type, fieldId, null);
116 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
118 "A value of named bit must be a non-negative value.",
119 null, type, fieldId, null);
123 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
124 ex.setMessage("A named bit must be an 'int' field.", null,
125 type, fieldId, null);
128 if (map.containsKey(value)) {
129 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
131 "A value of named bit shall be distinct from all other named bits in the type.",
132 null, type, fieldId, null);
135 map.put(value, fieldId);
137 @SuppressWarnings("unchecked")
138 Class<? extends BIT_STRING> parent = (Class<? extends BIT_STRING>) type
140 if (parent != BIT_STRING.class) {
141 if (!map.isEmpty()) {
142 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
144 "A class that does not extend 'BIT_STRING' directly, can not have own named bits.",
145 null, type, null, null);
148 map = getNamedBitMap(parent);
150 NAMED_BIT_MAP.put(type, map);
155 * Instantiates an empty {@code BIT_STRING}.
157 public BIT_STRING() {
161 * Instantiates a {@code BIT_STRING} and initialize it with the array of
162 * {@code boolean} value.
165 * The value to be assigned.
167 public BIT_STRING(boolean... array) {
172 * Instantiates a {@code BIT_STRING} and initialize it with the
173 * {@link StringItem}.
176 * The value to be assigned.
178 public BIT_STRING(StringItem item) {
183 * Instantiates a {@code BIT_STRING} and sets {@code true} to bits which
184 * specified by the indexes.
187 * Indexes of bits to be set true.
189 public BIT_STRING(int... indexes) {
194 * Sets the {@link StringItem} value to this data.
197 * The value to be set.
199 public void set(StringItem item) {
200 set(item.toBinArray());
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
210 * Indexes of bits to be set true.
212 public void set(int ...indexes) {
213 Arrays.sort(indexes);
214 expand(indexes[indexes.length - 1]);
215 for (int e: indexes) {
221 * Sets {@code true} to bits specified by the name of bit.
226 public void set(String nameOfBit) {
227 for (Entry<Integer, String> e: getNamedBitMap(getClass()).entrySet()) {
228 if (e.getValue().equals(nameOfBit)) {
233 ASN1IllegalArgument e = new ASN1IllegalArgument();
234 e.setMessage("The name '" + nameOfBit + "' is not defined in this type.", null, getClass(), null, null);
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
245 * Indexes of bits to be set false.
247 public void unset(int ...indexes) {
248 Arrays.sort(indexes);
249 expand(indexes[indexes.length - 1]);
250 for (int e: indexes) {
256 * Contracts the {@code boolean} array of the value of this data to enough
257 * size to hold the last {@code true} bit.
259 public void contract() {
260 if (value() == null) {
265 for (index--; index >= 0; index--) {
266 if (value()[index]) {
270 if (index + 1 != size()) {
271 boolean[] newValue = new boolean[index + 1];
272 System.arraycopy(value(), 0, newValue, 0, newValue.length);
278 * Returns the value of a bit specified by the index number.
282 * @return The value of the bit.
284 public boolean bit(int index) {
285 if (index < size()) {
286 return value()[index];
292 * Tests if the type has named bits definition.
294 * @return {@code true} when the type has named bits.
296 public boolean hasNamedBits() {
297 return !getNamedBitMap(getClass()).isEmpty();
301 * Returns the indentifier of the bit specified by the index number.
304 * The index number of a bit.
305 * @return The identifier of the bit.
307 public String nameOfBit(int index) {
308 return getNamedBitMap(getClass()).get(index);
312 * Expands the {@code boolean} array of the value of this data to
313 * enough size to hold the bit specified by the index.
316 * The index of a bit.
318 private void expand(int index) {
319 if (size() <= index) {
320 boolean[] newValue = new boolean[index + 1];
322 System.arraycopy(value(), 0, newValue, 0, size());
331 * @see jp.bitmeister.asn1.type.Concatenatable#concatenate(null)
333 public void concatenate(BIT_STRING data) {
337 if (!getClass().equals(data.getClass())) {
338 ASN1IllegalArgument ex = new ASN1IllegalArgument();
341 + data.specification().fullIdentifier()
342 + "' of the data to be concatenated is not the same type of this data.",
343 null, getClass(), null, null);
346 if (data.hasValue()) {
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);
363 * @see jp.bitmeister.asn1.type.SizeCountable#size()
366 if (value() == null) {
369 return value().length;
375 * @see jp.bitmeister.asn1.type.PrimitiveType#cloneValue()
378 protected boolean[] cloneValue() {
379 if (value() == null) {
382 return value().clone();
388 * @see jp.bitmeister.asn1.type.PrimitiveType#valueEquals(java.lang.Object)
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);
397 return otherValue == null;
405 * @see jp.bitmeister.asn1.type.PrimitiveType#hashCode()
408 public int hashCode() {
409 return Arrays.hashCode(value());
416 * jp.bitmeister.asn1.type.ASN1Type#accept(jp.bitmeister.asn1.processor.
420 public <R, E extends Throwable> R accept(ASN1Visitor<R, E> visitor) throws E {
421 return visitor.visit(this);