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;
18 import java.lang.reflect.Field;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
24 import jp.bitmeister.asn1.annotation.ASN1Alternative;
25 import jp.bitmeister.asn1.exception.ASN1IllegalArgument;
26 import jp.bitmeister.asn1.exception.ASN1IllegalDefinition;
27 import jp.bitmeister.asn1.type.builtin.CHOICE;
30 * The base class for structured types defined by referencing a list of distinct
34 * This class provides generic interfaces and common methods for classes that
35 * represents structured types which defined by referencing a list of distinct
36 * ASN.1 types. This class is the parent class of {code CHOICE}.
40 * @author WATANABE, Jun. <jwat at bitmeister.jp>
42 public abstract class SelectiveType extends StructuredType {
44 private static final Map<Class<? extends StructuredType>, NamedTypeSpecification[]> ALTERNATIVES_MAP = new HashMap<Class<? extends StructuredType>, NamedTypeSpecification[]>();
47 * Returns an array of {@code NamedTypeSpecification} that represents
48 * alternatives of the type.
52 * @return An array of {@code NamedTypeSpecification}.
54 private static NamedTypeSpecification[] getAlternativeTypeList(
55 Class<? extends SelectiveType> type) {
56 if (ALTERNATIVES_MAP.containsKey(type)) {
57 return ALTERNATIVES_MAP.get(type);
59 List<NamedTypeSpecification> alternatives = new ArrayList<NamedTypeSpecification>();
60 for (Field f : type.getDeclaredFields()) {
61 if (f.isAnnotationPresent(ASN1Alternative.class)) {
62 alternatives.add(new NamedTypeSpecification(f.getAnnotation(
63 ASN1Alternative.class).value(), f));
66 @SuppressWarnings("unchecked")
67 Class<? extends SelectiveType> parent = (Class<? extends SelectiveType>) type
69 NamedTypeSpecification[] array;
70 if (parent == CHOICE.class) {
71 if (alternatives.isEmpty()) {
72 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
74 "CHOICE type shall have at least one alternative.",
75 null, type, null, null);
78 array = alternatives.toArray(new NamedTypeSpecification[0]);
79 if (TypeSpecification.getSpecification(type).tagDefault() == ASN1TagDefault.AUTOMATIC_TAGS) {
80 generateAutomaticTags(array);
82 new UnorderedElementsChecker(type).check(array);
84 if (!alternatives.isEmpty()) {
85 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
87 "If a class does not extend CHOICE directly, it can not define own alternatives.",
88 null, type, null, null);
91 array = getAlternativeTypeList(parent);
93 ALTERNATIVES_MAP.put(type, array);
98 * Selected alternative.
100 private NamedTypeSpecification selection;
103 * Instantiates an empty {@code SelectiveType}.
105 public SelectiveType() {
109 * Instantiates a {@code SelectiveType} and initialize it with the
110 * parameter. The ASN.1 tag that assigned to the type of the data is used
111 * for select a field.
114 * The ASN.1 data assigned to this instance.
116 public SelectiveType(ASN1Type data) {
117 if (!data.specification().tagged()) {
118 ASN1IllegalArgument ex = new ASN1IllegalArgument();
120 "The type of the data has no ASN.1 tag for specifying alternative.",
121 null, getClass(), null, data);
124 ASN1TagValue tag = data.specification().tag();
125 set(alternative(tag.tagClass(), tag.tagNumber()), data);
129 * Instantiates a {@code SelectiveType} and initialize it with parameters.
132 * The tag class used for select a field.
134 * The tag number used for select a field.
136 * The data to be assigned.
138 public SelectiveType(ASN1TagClass tagClass, int tagNumber, ASN1Type data) {
139 set(alternative(tagClass, tagNumber), data);
143 * Returns the element specified by the ASN.1 tag class and number.
149 * @return The element specified by the ASN.1 tag class and number.
151 public NamedTypeSpecification alternative(ASN1TagClass tagClass,
153 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
154 if (e.matches(tagClass, tagNumber)) {
158 ASN1IllegalArgument ex = new ASN1IllegalArgument();
159 ex.setMessage("The tag '" + tagClass + " " + tagNumber
160 + "' does not match to any alternatives of this type.", null,
161 getClass(), null, null);
169 * jp.bitmeister.asn1.type.ASN1Type#matches(jp.bitmeister.asn1.type.ASN1TagClass
173 public boolean matches(ASN1TagClass tagClass, int tagNumber) {
174 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
175 if (e.matches(tagClass, tagNumber)) {
185 * @see jp.bitmeister.asn1.type.StructuredType#set(jp.bitmeister.asn1.type.
186 * NamedTypeSpecification, jp.bitmeister.asn1.type.ASN1Type)
189 public void set(NamedTypeSpecification alternative, ASN1Type data) {
190 alternative.assign(this, data);
191 selection = alternative;
197 * @see jp.bitmeister.asn1.type.StructuredType#set(java.lang.String,
198 * jp.bitmeister.asn1.type.ASN1Type)
201 public void set(String elementName, ASN1Type component) {
202 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
203 if (e.identifier().equals(elementName)) {
208 ASN1IllegalArgument ex = new ASN1IllegalArgument();
210 "No such alternative '" + elementName + "' in this type.",
211 null, getClass(), null, null);
218 * @see jp.bitmeister.asn1.type.StructuredType#get(java.lang.String)
221 public ASN1Type get(String elementName) {
222 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
223 if (e.identifier().equals(elementName)) {
224 return e.retrieve(this);
227 ASN1IllegalArgument ex = new ASN1IllegalArgument();
229 "No such alternative '" + elementName + "' in this type.",
230 null, getClass(), null, null);
235 * Returns current selection.
237 * @return An alternative currently selected.
239 private NamedTypeSpecification selection() {
240 if (selection == null) {
241 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
242 if (e.retrieve(this) != null) {
252 * Returns the ASN.1 data of selected alternative.
254 * @return The ASN.1 data of selected alternative.
256 public ASN1Type selectedValue() {
257 if (selection() != null) {
258 return selection.retrieve(this);
264 * Returns the identifier of selected alternative.
266 * @return The identifier of selected alternative.
268 public String selectedIdentifier() {
269 if (selection() != null) {
270 return selection.identifier();
276 * Returns the ASN.1 tag of selected alternative.
278 * @return The tag of selected alternative.
280 public ASN1TagValue selectedTag() {
281 if (selection() != null) {
282 return selection.tag();
288 * Clears the selection of this instance.
290 public void clearSelection() {
291 if (selection() != null) {
292 selection.assign(this, null);
300 * @see jp.bitmeister.asn1.type.ASN1Type#clear()
303 public void clear() {
305 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
306 e.assign(this, null);
313 * @see jp.bitmeister.asn1.type.ASN1Type#hasValue()
316 public boolean hasValue() {
317 return selection() != null;
323 * @see jp.bitmeister.asn1.type.ASN1Type#valueEquals(java.lang.Object)
326 public boolean valueEquals(Object other) {
327 if (other instanceof SelectiveType) {
328 ASN1Type thisSelection = selectedValue();
329 ASN1Type otherSelection = ((SelectiveType) other).selectedValue();
330 if (thisSelection != null) {
331 return thisSelection.equals(otherSelection);
333 return otherSelection == null;
341 * @see jp.bitmeister.asn1.type.ASN1Type#hashCode()
344 public int hashCode() {
345 ASN1Type data = selectedValue();
349 return data.hashCode();
355 * @see jp.bitmeister.asn1.type.ASN1Type#clone()
358 public Object clone() {
359 SelectiveType clone = ASN1Type.instantiate(getClass());
360 ASN1Type value = selectedValue();
362 selection.assign(clone, (ASN1Type) value.clone());
363 clone.selection = selection;