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.annotation.ASN1Extendable;
26 import jp.bitmeister.asn1.exception.ASN1IllegalArgument;
27 import jp.bitmeister.asn1.exception.ASN1IllegalDefinition;
28 import jp.bitmeister.asn1.type.builtin.CHOICE;
31 * The base class for structured types defined by referencing a list of distinct
35 * This class provides generic interfaces and common methods for classes that
36 * represents structured types which defined by referencing a list of distinct
37 * ASN.1 types. This class is the parent class of {code CHOICE}.
41 * @author WATANABE, Jun. <jwat at bitmeister.jp>
43 public abstract class SelectiveType extends StructuredType {
45 private static final Map<Class<? extends StructuredType>, NamedTypeSpecification[]> ALTERNATIVES_MAP = new HashMap<Class<? extends StructuredType>, NamedTypeSpecification[]>();
48 * Returns an array of {@code NamedTypeSpecification} that represents
49 * alternatives of the type.
53 * @return An array of {@code NamedTypeSpecification}.
55 private static NamedTypeSpecification[] getAlternativeTypeList(
56 Class<? extends SelectiveType> type) {
57 if (ALTERNATIVES_MAP.containsKey(type)) {
58 return ALTERNATIVES_MAP.get(type);
60 List<NamedTypeSpecification> alternatives = new ArrayList<NamedTypeSpecification>();
61 for (Field f : type.getDeclaredFields()) {
62 if (f.isAnnotationPresent(ASN1Alternative.class)) {
63 alternatives.add(new NamedTypeSpecification(f.getAnnotation(
64 ASN1Alternative.class).value(), f));
67 @SuppressWarnings("unchecked")
68 Class<? extends SelectiveType> parent = (Class<? extends SelectiveType>) type
70 NamedTypeSpecification[] array;
71 if (parent == CHOICE.class || parent.isAnnotationPresent(ASN1Extendable.class)) {
72 if (alternatives.isEmpty()) {
73 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
75 "CHOICE type shall have at least one alternative.",
76 null, type, null, null);
79 array = alternatives.toArray(new NamedTypeSpecification[0]);
80 if (TypeSpecification.getSpecification(type).tagDefault() == ASN1TagDefault.AUTOMATIC_TAGS) {
81 generateAutomaticTags(array);
83 new UnorderedElementsChecker(type).check(array);
85 if (!alternatives.isEmpty()) {
86 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
88 "If a class does not extend CHOICE or a type annotated as ASN1Extendable directly," +
89 "it can not define own alternatives.",
90 null, type, null, null);
93 array = getAlternativeTypeList(parent);
95 ALTERNATIVES_MAP.put(type, array);
100 * Selected alternative.
102 private NamedTypeSpecification selection;
105 * Instantiates an empty {@code SelectiveType}.
107 public SelectiveType() {
111 * Instantiates a {@code SelectiveType} and initialize it with the
112 * parameter. The type of the data is used for select alternative. If this
113 * {@code SelectiveType} has two or more alternatives that are defined as
114 * same type, the result of this constructor is undefined.
117 * The ASN.1 data assigned to this instance.
119 public SelectiveType(ASN1Type data) {
120 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
121 if (e.type() == data.getClass()) {
126 ASN1IllegalArgument ex = new ASN1IllegalArgument();
127 ex.setMessage("Can't select alternative by the type of the data.",
128 null, getClass(), null, data);
133 * Instantiates a {@code SelectiveType} and initialize it with parameters.
136 * The tag class used for select an alternative.
138 * The tag number used for select an alternative.
140 * The data to be assigned.
142 public SelectiveType(ASN1TagClass tagClass, int tagNumber, ASN1Type data) {
143 set(alternative(tagClass, tagNumber), data);
147 * Instantiates a {@code SelectiveType} and initialize it with parameters.
150 * The element name used for select an alternative.
152 * The data to be assigned.
154 public SelectiveType(String elementName, ASN1Type data) {
155 set(elementName, data);
159 * Returns the element specified by the ASN.1 tag class and number.
165 * @return The element specified by the ASN.1 tag class and number.
167 public NamedTypeSpecification alternative(ASN1TagClass tagClass,
169 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
170 if (e.matches(tagClass, tagNumber)) {
174 ASN1IllegalArgument ex = new ASN1IllegalArgument();
175 ex.setMessage("The tag '" + tagClass + " " + tagNumber
176 + "' does not match to any alternatives of this type.", null,
177 getClass(), null, null);
185 * jp.bitmeister.asn1.type.ASN1Type#matches(jp.bitmeister.asn1.type.ASN1TagClass
189 public boolean matches(ASN1TagClass tagClass, int tagNumber) {
190 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
191 if (e.matches(tagClass, tagNumber)) {
201 * @see jp.bitmeister.asn1.type.StructuredType#set(jp.bitmeister.asn1.type.
202 * NamedTypeSpecification, jp.bitmeister.asn1.type.ASN1Type)
205 public void set(NamedTypeSpecification alternative, ASN1Type data) {
206 alternative.assign(this, data);
207 selection = alternative;
213 * @see jp.bitmeister.asn1.type.StructuredType#set(java.lang.String,
214 * jp.bitmeister.asn1.type.ASN1Type)
217 public void set(String elementName, ASN1Type component) {
218 set(getElement(elementName), component);
224 * @see jp.bitmeister.asn1.type.StructuredType#get(java.lang.String)
227 public ASN1Type get(String elementName) {
228 return getElement(elementName).retrieve(this);
234 * @see jp.bitmeister.asn1.type.StructuredType#getElement(java.lang.String)
237 public NamedTypeSpecification getElement(String elementName) {
238 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
239 if (e.identifier().equals(elementName)) {
243 ASN1IllegalArgument ex = new ASN1IllegalArgument();
245 "No such alternative '" + elementName + "' in this type.",
246 null, getClass(), null, null);
251 * Returns current selection.
253 * @return An alternative currently selected.
255 private NamedTypeSpecification selection() {
256 if (selection == null) {
257 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
258 if (e.retrieve(this) != null) {
268 * Returns the ASN.1 data of selected alternative.
270 * @return The ASN.1 data of selected alternative.
272 public ASN1Type selectedValue() {
273 if (selection() != null) {
274 return selection.retrieve(this);
280 * Returns the identifier of selected alternative.
282 * @return The identifier of selected alternative.
284 public String selectedIdentifier() {
285 if (selection() != null) {
286 return selection.identifier();
292 * Returns the ASN.1 tag of selected alternative.
294 * @return The tag of selected alternative.
296 public ASN1TagValue selectedTag() {
297 if (selection() != null) {
298 return selection.tag();
304 * Clears the selection of this instance.
306 public void clearSelection() {
307 if (selection() != null) {
308 selection.assign(this, null);
316 * @see jp.bitmeister.asn1.type.ASN1Type#clear()
319 public void clear() {
321 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
322 e.assign(this, null);
329 * @see jp.bitmeister.asn1.type.ASN1Type#hasValue()
332 public boolean hasValue() {
333 return selection() != null;
339 * @see jp.bitmeister.asn1.type.ASN1Type#valueEquals(java.lang.Object)
342 public boolean valueEquals(Object other) {
343 if (other instanceof SelectiveType) {
344 ASN1Type thisSelection = selectedValue();
345 ASN1Type otherSelection = ((SelectiveType) other).selectedValue();
346 if (thisSelection != null) {
347 return thisSelection.equals(otherSelection);
349 return otherSelection == null;
357 * @see jp.bitmeister.asn1.type.ASN1Type#hashCode()
360 public int hashCode() {
361 ASN1Type data = selectedValue();
365 return data.hashCode();
371 * @see jp.bitmeister.asn1.type.ASN1Type#clone()
374 public Object clone() {
375 SelectiveType clone = ASN1Type.instantiate(getClass());
376 ASN1Type value = selectedValue();
378 selection.assign(clone, (ASN1Type) value.clone());
379 clone.selection = selection;