OSDN Git Service

* Add ASN1Extendable annotation.
[bm-asn1/bm-asn1.git] / jp / bitmeister / asn1 / type / SelectiveType.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;
17
18 import java.lang.reflect.Field;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23
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;
29
30 /**
31  * The base class for structured types defined by referencing a list of distinct
32  * ASN.1 types.
33  * 
34  * <p>
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}.
38  * </p>
39  * 
40  * @see CHOICE
41  * @author WATANABE, Jun. <jwat at bitmeister.jp>
42  */
43 public abstract class SelectiveType extends StructuredType {
44
45         private static final Map<Class<? extends StructuredType>, NamedTypeSpecification[]> ALTERNATIVES_MAP = new HashMap<Class<? extends StructuredType>, NamedTypeSpecification[]>();
46
47         /**
48          * Returns an array of {@code NamedTypeSpecification} that represents
49          * alternatives of the type.
50          * 
51          * @param type
52          *            The type.
53          * @return An array of {@code NamedTypeSpecification}.
54          */
55         private static NamedTypeSpecification[] getAlternativeTypeList(
56                         Class<? extends SelectiveType> type) {
57                 if (ALTERNATIVES_MAP.containsKey(type)) {
58                         return ALTERNATIVES_MAP.get(type);
59                 }
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));
65                         }
66                 }
67                 @SuppressWarnings("unchecked")
68                 Class<? extends SelectiveType> parent = (Class<? extends SelectiveType>) type
69                                 .getSuperclass();
70                 NamedTypeSpecification[] array;
71                 if (parent == CHOICE.class || parent.isAnnotationPresent(ASN1Extendable.class)) {
72                         if (alternatives.isEmpty()) {
73                                 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
74                                 ex.setMessage(
75                                                 "CHOICE type shall have at least one alternative.",
76                                                 null, type, null, null);
77                                 throw ex;
78                         }
79                         array = alternatives.toArray(new NamedTypeSpecification[0]);
80                         if (TypeSpecification.getSpecification(type).tagDefault() == ASN1TagDefault.AUTOMATIC_TAGS) {
81                                 generateAutomaticTags(array);
82                         }
83                         new UnorderedElementsChecker(type).check(array);
84                 } else {
85                         if (!alternatives.isEmpty()) {
86                                 ASN1IllegalDefinition ex = new ASN1IllegalDefinition();
87                                 ex.setMessage(
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);
91                                 throw ex;
92                         }
93                         array = getAlternativeTypeList(parent);
94                 }
95                 ALTERNATIVES_MAP.put(type, array);
96                 return array;
97         }
98
99         /**
100          * Selected alternative.
101          */
102         private NamedTypeSpecification selection;
103
104         /**
105          * Instantiates an empty {@code SelectiveType}.
106          */
107         public SelectiveType() {
108         }
109
110         /**
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.
115          * 
116          * @param data
117          *            The ASN.1 data assigned to this instance.
118          */
119         public SelectiveType(ASN1Type data) {
120                 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
121                         if (e.type() == data.getClass()) {
122                                 set(e, data);
123                                 return;
124                         }
125                 }
126                 ASN1IllegalArgument ex = new ASN1IllegalArgument();
127                 ex.setMessage("Can't select alternative by the type of the data.",
128                                 null, getClass(), null, data);
129                 throw ex;
130         }
131
132         /**
133          * Instantiates a {@code SelectiveType} and initialize it with parameters.
134          * 
135          * @param tagClass
136          *            The tag class used for select an alternative.
137          * @param tagNumber
138          *            The tag number used for select an alternative.
139          * @param data
140          *            The data to be assigned.
141          */
142         public SelectiveType(ASN1TagClass tagClass, int tagNumber, ASN1Type data) {
143                 set(alternative(tagClass, tagNumber), data);
144         }
145
146         /**
147          * Instantiates a {@code SelectiveType} and initialize it with parameters.
148          * 
149          * @param elementName
150          *            The element name used for select an alternative.
151          * @param data
152          *            The data to be assigned.
153          */
154         public SelectiveType(String elementName, ASN1Type data) {
155                 set(elementName, data);
156         }
157
158         /**
159          * Returns the element specified by the ASN.1 tag class and number.
160          * 
161          * @param tagClass
162          *            ASN.1 tag class.
163          * @param tagNumber
164          *            ASN.1 tag number.
165          * @return The element specified by the ASN.1 tag class and number.
166          */
167         public NamedTypeSpecification alternative(ASN1TagClass tagClass,
168                         int tagNumber) {
169                 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
170                         if (e.matches(tagClass, tagNumber)) {
171                                 return e;
172                         }
173                 }
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);
178                 throw ex;
179         }
180
181         /*
182          * (non-Javadoc)
183          * 
184          * @see
185          * jp.bitmeister.asn1.type.ASN1Type#matches(jp.bitmeister.asn1.type.ASN1TagClass
186          * , int)
187          */
188         @Override
189         public boolean matches(ASN1TagClass tagClass, int tagNumber) {
190                 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
191                         if (e.matches(tagClass, tagNumber)) {
192                                 return true;
193                         }
194                 }
195                 return false;
196         }
197
198         /*
199          * (non-Javadoc)
200          * 
201          * @see jp.bitmeister.asn1.type.StructuredType#set(jp.bitmeister.asn1.type.
202          * NamedTypeSpecification, jp.bitmeister.asn1.type.ASN1Type)
203          */
204         @Override
205         public void set(NamedTypeSpecification alternative, ASN1Type data) {
206                 alternative.assign(this, data);
207                 selection = alternative;
208         }
209
210         /*
211          * (non-Javadoc)
212          * 
213          * @see jp.bitmeister.asn1.type.StructuredType#set(java.lang.String,
214          * jp.bitmeister.asn1.type.ASN1Type)
215          */
216         @Override
217         public void set(String elementName, ASN1Type component) {
218                 set(getElement(elementName), component);
219         }
220
221         /*
222          * (non-Javadoc)
223          * 
224          * @see jp.bitmeister.asn1.type.StructuredType#get(java.lang.String)
225          */
226         @Override
227         public ASN1Type get(String elementName) {
228                 return getElement(elementName).retrieve(this);
229         }
230
231         /*
232          * (non-Javadoc)
233          * 
234          * @see jp.bitmeister.asn1.type.StructuredType#getElement(java.lang.String)
235          */
236         @Override
237         public NamedTypeSpecification getElement(String elementName) {
238                 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
239                         if (e.identifier().equals(elementName)) {
240                                 return e;
241                         }
242                 }
243                 ASN1IllegalArgument ex = new ASN1IllegalArgument();
244                 ex.setMessage(
245                                 "No such alternative '" + elementName + "' in this type.",
246                                 null, getClass(), null, null);
247                 throw ex;
248         }
249
250         /**
251          * Returns current selection.
252          * 
253          * @return An alternative currently selected.
254          */
255         private NamedTypeSpecification selection() {
256                 if (selection == null) {
257                         for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
258                                 if (e.retrieve(this) != null) {
259                                         selection = e;
260                                         break;
261                                 }
262                         }
263                 }
264                 return selection;
265         }
266
267         /**
268          * Returns the ASN.1 data of selected alternative.
269          * 
270          * @return The ASN.1 data of selected alternative.
271          */
272         public ASN1Type selectedValue() {
273                 if (selection() != null) {
274                         return selection.retrieve(this);
275                 }
276                 return null;
277         }
278
279         /**
280          * Returns the identifier of selected alternative.
281          * 
282          * @return The identifier of selected alternative.
283          */
284         public String selectedIdentifier() {
285                 if (selection() != null) {
286                         return selection.identifier();
287                 }
288                 return null;
289         }
290
291         /**
292          * Returns the ASN.1 tag of selected alternative.
293          * 
294          * @return The tag of selected alternative.
295          */
296         public ASN1TagValue selectedTag() {
297                 if (selection() != null) {
298                         return selection.tag();
299                 }
300                 return null;
301         }
302
303         /**
304          * Clears the selection of this instance.
305          */
306         public void clearSelection() {
307                 if (selection() != null) {
308                         selection.assign(this, null);
309                         selection = null;
310                 }
311         }
312
313         /*
314          * (non-Javadoc)
315          * 
316          * @see jp.bitmeister.asn1.type.ASN1Type#clear()
317          */
318         @Override
319         public void clear() {
320                 selection = null;
321                 for (NamedTypeSpecification e : getAlternativeTypeList(getClass())) {
322                         e.assign(this, null);
323                 }
324         }
325
326         /*
327          * (non-Javadoc)
328          * 
329          * @see jp.bitmeister.asn1.type.ASN1Type#hasValue()
330          */
331         @Override
332         public boolean hasValue() {
333                 return selection() != null;
334         }
335
336         /*
337          * (non-Javadoc)
338          * 
339          * @see jp.bitmeister.asn1.type.ASN1Type#valueEquals(java.lang.Object)
340          */
341         @Override
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);
348                         }
349                         return otherSelection == null;
350                 }
351                 return false;
352         }
353
354         /*
355          * (non-Javadoc)
356          * 
357          * @see jp.bitmeister.asn1.type.ASN1Type#hashCode()
358          */
359         @Override
360         public int hashCode() {
361                 ASN1Type data = selectedValue();
362                 if (data == null) {
363                         return 0;
364                 }
365                 return data.hashCode();
366         }
367
368         /*
369          * (non-Javadoc)
370          * 
371          * @see jp.bitmeister.asn1.type.ASN1Type#clone()
372          */
373         @Override
374         public Object clone() {
375                 SelectiveType clone = ASN1Type.instantiate(getClass());
376                 ASN1Type value = selectedValue();
377                 if (value != null) {
378                         selection.assign(clone, (ASN1Type) value.clone());
379                         clone.selection = selection;
380                 }
381                 return clone;
382         }
383
384 }