OSDN Git Service

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