OSDN Git Service

modifications for Java1.5
[bm-asn1/bm-asn1.git] / jp / bitmeister / asn1 / codec / xer / XerEncoder.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.codec.xer;
17
18 import static jp.bitmeister.asn1.codec.xer.XerConstants.BOOLEAN_FALSE;
19 import static jp.bitmeister.asn1.codec.xer.XerConstants.BOOLEAN_TRUE;
20 import static jp.bitmeister.asn1.codec.xer.XerConstants.END_TAG_START;
21 import static jp.bitmeister.asn1.codec.xer.XerConstants.REAL_MINUS_INFINITY;
22 import static jp.bitmeister.asn1.codec.xer.XerConstants.REAL_PLUS_INFINITY;
23 import static jp.bitmeister.asn1.codec.xer.XerConstants.REAL_ZERO;
24 import static jp.bitmeister.asn1.codec.xer.XerConstants.SINGLE_TAG_END;
25 import static jp.bitmeister.asn1.codec.xer.XerConstants.TAG_END;
26 import static jp.bitmeister.asn1.codec.xer.XerConstants.TAG_START;
27 import static jp.bitmeister.asn1.codec.xer.XerConstants.XML_PROLOG;
28
29 import java.io.OutputStream;
30 import java.io.UnsupportedEncodingException;
31 import java.math.BigDecimal;
32
33 import jp.bitmeister.asn1.codec.ASN1Encoder;
34 import jp.bitmeister.asn1.exception.ASN1EncodingException;
35 import jp.bitmeister.asn1.processor.ASN1Visitor;
36 import jp.bitmeister.asn1.type.ASN1Module;
37 import jp.bitmeister.asn1.type.ASN1Type;
38 import jp.bitmeister.asn1.type.CollectionType;
39 import jp.bitmeister.asn1.type.ConstructiveType;
40 import jp.bitmeister.asn1.type.ElementSpecification;
41 import jp.bitmeister.asn1.type.StringType;
42 import jp.bitmeister.asn1.type.TimeType;
43 import jp.bitmeister.asn1.type.UnknownType;
44 import jp.bitmeister.asn1.type.builtin.ANY;
45 import jp.bitmeister.asn1.type.builtin.BIT_STRING;
46 import jp.bitmeister.asn1.type.builtin.BOOLEAN;
47 import jp.bitmeister.asn1.type.builtin.CHOICE;
48 import jp.bitmeister.asn1.type.builtin.ENUMERATED;
49 import jp.bitmeister.asn1.type.builtin.INTEGER;
50 import jp.bitmeister.asn1.type.builtin.NULL;
51 import jp.bitmeister.asn1.type.builtin.OBJECT_IDENTIFIER;
52 import jp.bitmeister.asn1.type.builtin.OCTET_STRING;
53 import jp.bitmeister.asn1.type.builtin.REAL;
54 import jp.bitmeister.asn1.type.builtin.RELATIVE_OID;
55 import jp.bitmeister.asn1.type.builtin.SEQUENCE;
56 import jp.bitmeister.asn1.type.builtin.SEQUENCE_OF;
57 import jp.bitmeister.asn1.type.builtin.SET;
58 import jp.bitmeister.asn1.type.builtin.SET_OF;
59 import jp.bitmeister.asn1.value.BinString;
60 import jp.bitmeister.asn1.value.HexString;
61
62 /**
63  * XER (XML Encoding Rules) encoder.
64  * 
65  * <p>
66  * {@code XerEncoder} is an implementation of {@code ASN1Encoder}. It encodes an
67  * ASN.1 data to an XML document with XML Encoding Rules(XER) and writes the
68  * result to {@code OutputStream} that is specified when the encoder was
69  * instantiated. XER encoding process is light-weight compared with Canonical
70  * XML Encoding Rules (CXER) encoding because some restrictions on CXER are not
71  * considered.
72  * </p>
73  * 
74  * @author WATANABE, Jun. <jwat at bitmeister.jp>
75  * 
76  * @see ASN1Encoder
77  * @see XerDecoder
78  */
79 public class XerEncoder implements ASN1Encoder,
80                 ASN1Visitor<String, ASN1EncodingException> {
81
82         private Class<? extends ASN1Module> module;
83
84         private OutputStream out;
85
86         private StringBuilder builder;
87
88         private boolean prologIsEmpty = false;
89
90         /**
91          * Instantiates a {@code XEREncoder}.
92          * 
93          * @param out
94          *            The {@code OutputStream} that encoded octets will be written.
95          */
96         public XerEncoder(OutputStream out) {
97                 this.out = out;
98         }
99
100         /**
101          * Instantiates a {@code XEREncoder}.
102          * 
103          * @param module
104          *            The ASN.1 module used for encoding.
105          * @param out
106          *            The {@code OutputStream} that encoded octets will be written.
107          */
108         public XerEncoder(Class<? extends ASN1Module> module, OutputStream out) {
109                 this(out);
110                 this.module = module;
111         }
112
113         /**
114          * Instantiates a {@code XEREncoder}.
115          * 
116          * @param out
117          *            The {@code OutputStream} that encoded octets will be written.
118          * @param prologIsEmpty
119          *            If {@code true}, the XML prolog for result XML document will
120          *            be empty.
121          */
122         public XerEncoder(OutputStream out, boolean prologIsEmpty) {
123                 this(out);
124                 this.prologIsEmpty = prologIsEmpty;
125         }
126
127         /**
128          * Instantiates a {@code XEREncoder}.
129          * 
130          * @param module
131          *            The ASN.1 module used for encoding.
132          * @param out
133          *            The {@code OutputStream} that encoded octets will be written.
134          * @param prologIsEmpty
135          *            If {@code true}, the XML prolog for result XML document will
136          *            be empty.
137          */
138         public XerEncoder(Class<? extends ASN1Module> module, OutputStream out, boolean prologIsEmpty) {
139                 this(module, out);
140                 this.prologIsEmpty = prologIsEmpty;
141         }
142         
143         /**
144          * Encodes an ASN.1 data.
145          * 
146          * @param data
147          *            The ASN.1 data to be encoded.
148          * @return The size of encoded octets.
149          * @throws ASN1EncodingException
150          *             When an error occurred while the encoding process.
151          */     
152         public int encode(ASN1Type data) throws ASN1EncodingException {
153                 if (module == null) {
154                         module = data.specification().module();
155                 }
156                 builder = new StringBuilder();
157                 if (!prologIsEmpty) {
158                         builder.append(XML_PROLOG);
159                 }
160                 encodeImpl(data, data.specification().xmlTypeName(module));
161                 try {
162                         byte[] result = builder.toString().getBytes("UTF-8");
163                         out.write(result);
164                         return result.length;
165                 } catch (Exception e) {
166                         ASN1EncodingException ex = new ASN1EncodingException();
167                         ex.setMessage("Failed to write result to stream.", e, null, null,
168                                         data);
169                         throw ex;
170                 }
171         }
172
173         /**
174          * Encodes the source data to xml documents and writes it to the
175          * {@code builder}.
176          * 
177          * @param data
178          *            The ASN.1 data to be encoded.
179          * @param xmlTagName
180          *            The xml tag name to be set.
181          * @throws ASN1EncodingException
182          *             When an error occurred while the encoding process.
183          */
184         private void encodeImpl(ASN1Type data, String xmlTagName)
185                         throws ASN1EncodingException {
186                 String contents = data.accept(this);
187                 if (contents == null) {
188                         return;
189                 } else if (contents.length() == 0) {
190                         builder.append(TAG_START).append(xmlTagName).append(SINGLE_TAG_END);
191                 } else {
192                         builder.append(TAG_START).append(xmlTagName).append(TAG_END);
193                         builder.append(contents);
194                         builder.append(END_TAG_START).append(xmlTagName).append(TAG_END);
195                 }
196         }
197
198         /*
199          * (non-Javadoc)
200          * 
201          * @see
202          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
203          * .builtin.BOOLEAN)
204          */
205         public String visit(BOOLEAN data) {
206                 return TAG_START + (data.value() ? BOOLEAN_TRUE : BOOLEAN_FALSE)
207                                 + SINGLE_TAG_END;
208         }
209
210         /*
211          * (non-Javadoc)
212          * 
213          * @see
214          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
215          * .builtin.INTEGER)
216          */
217         public String visit(INTEGER data) {
218                 String numberId = data.identifier();
219                 if (numberId != null) {
220                         return TAG_START + numberId + SINGLE_TAG_END;
221                 } else {
222                         return data.value().toString();
223                 }
224         }
225
226         /*
227          * (non-Javadoc)
228          * 
229          * @see
230          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
231          * .builtin.BIT_STRING)
232          */
233         public String visit(BIT_STRING data) {
234                 if (data.hasNamedBits()) {
235                         StringBuilder builder = new StringBuilder();
236                         for (int i = 0; i < data.size(); i++) {
237                                 if (data.bit(i)) {
238                                         String name = data.nameOfBit(i);
239                                         if (name != null) {
240                                                 builder.append(TAG_START).append(name)
241                                                                 .append(SINGLE_TAG_END);
242                                         }
243                                 }
244                         }
245                         return builder.toString();
246                 } else {
247                         return new BinString(data.value()).string();
248                 }
249         }
250
251         /*
252          * (non-Javadoc)
253          * 
254          * @see
255          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
256          * .builtin.OCTET_STRING)
257          */
258         public String visit(OCTET_STRING data) {
259                 return new HexString(data.value()).string();
260         }
261
262         /*
263          * (non-Javadoc)
264          * 
265          * @see
266          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
267          * .builtin.NULL)
268          */
269         public String visit(NULL data) {
270                 return "";
271         }
272
273         /*
274          * (non-Javadoc)
275          * 
276          * @see
277          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
278          * .builtin.OBJECT_IDENTIFIER)
279          */
280         public String visit(OBJECT_IDENTIFIER data) {
281                 StringBuilder builder = new StringBuilder();
282                 if (data.value().size() > 0) {
283                         builder.append(data.value().get(0));
284                         for (int i = 1; i < data.value().size(); i++) {
285                                 builder.append('.').append(data.value().get(i));
286                         }
287                 }
288                 return builder.toString();
289         }
290
291         /*
292          * (non-Javadoc)
293          * 
294          * @see
295          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
296          * .builtin.RELATIVE_OID)
297          */
298         public String visit(RELATIVE_OID data) {
299                 return visit((OBJECT_IDENTIFIER) data);
300         }
301
302         /*
303          * (non-Javadoc)
304          * 
305          * @see
306          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
307          * .builtin.REAL)
308          */
309         public String visit(REAL data) {
310                 if (data.value().isInfinite()) {
311                         return TAG_START
312                                         + (data.value() == Double.POSITIVE_INFINITY ? REAL_PLUS_INFINITY
313                                                         : REAL_MINUS_INFINITY) + SINGLE_TAG_END;
314                 }
315                 if (data.value() == 0) {
316                         return REAL_ZERO;
317                 }
318                 return BigDecimal.valueOf(data.value()).stripTrailingZeros()
319                                 .toPlainString();
320         }
321
322         /*
323          * (non-Javadoc)
324          * 
325          * @see
326          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
327          * .builtin.ENUMERATED)
328          */
329         public String visit(ENUMERATED data) {
330                 return visit((INTEGER) data);
331         }
332
333         /*
334          * (non-Javadoc)
335          * 
336          * @see
337          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
338          * .builtin.ANY)
339          */
340         public String visit(ANY data) throws ASN1EncodingException {
341                 encodeImpl(data.value(),
342                                 data.value().specification().xmlTypeName(module));
343                 return null;
344         }
345
346         /*
347          * (non-Javadoc)
348          * 
349          * @see
350          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
351          * .builtin.CHOICE)
352          */
353         public String visit(CHOICE data) throws ASN1EncodingException {
354                 StringBuilder enclosure = builder;
355                 builder = new StringBuilder();
356                 encodeImpl(data.selectedValue(), data.selectedIdentifier());
357                 String result = builder.toString();
358                 builder = enclosure;
359                 return result;
360         }
361
362         /*
363          * (non-Javadoc)
364          * 
365          * @see
366          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
367          * .builtin.SEQUENCE_OF)
368          */
369         public String visit(SEQUENCE_OF<? extends ASN1Type> data)
370                         throws ASN1EncodingException {
371                 return processCollection(data);
372         }
373
374         /*
375          * (non-Javadoc)
376          * 
377          * @see
378          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
379          * .builtin.SEQUENCE)
380          */
381         public String visit(SEQUENCE data) throws ASN1EncodingException {
382                 return processConstructive(data);
383         }
384
385         /*
386          * (non-Javadoc)
387          * 
388          * @see
389          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
390          * .builtin.SET_OF)
391          */
392         public String visit(SET_OF<? extends ASN1Type> data)
393                         throws ASN1EncodingException {
394                 return processCollection(data);
395         }
396
397         /*
398          * (non-Javadoc)
399          * 
400          * @see
401          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
402          * .builtin.SET)
403          */
404         public String visit(SET data) throws ASN1EncodingException {
405                 return processConstructive(data);
406         }
407
408         /*
409          * (non-Javadoc)
410          * 
411          * @see
412          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
413          * .StringType)
414          */
415         public String visit(StringType data) throws ASN1EncodingException {
416                 try {
417                         return new String(XerStringEscapeUtil.escape(data.stringValue())
418                                         .getBytes("Shift_JIS"));
419                 } catch (UnsupportedEncodingException e) {
420                         throw new ASN1EncodingException();
421                 }
422         }
423
424         /*
425          * (non-Javadoc)
426          * 
427          * @see
428          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
429          * .TimeType)
430          */
431         public String visit(TimeType data) throws ASN1EncodingException {
432                 return data.stringValue();
433         }
434
435         /*
436          * (non-Javadoc)
437          * 
438          * @see
439          * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
440          * .UnknownType)
441          */
442         public String visit(UnknownType data) throws ASN1EncodingException {
443                 ASN1EncodingException ex = new ASN1EncodingException();
444                 ex.setMessage("Can't encode unknown type.", null, data.getClass(),
445                                 null, data);
446                 throw ex;
447         }
448
449         /**
450          * Encodes each element of the {@code ConstructiveType} data.
451          * 
452          * @param data
453          *            The data to be encoded.
454          * @throws ASN1EncodingException
455          *             When an error occurred while the encoding process.
456          */
457         private String processConstructive(ConstructiveType data)
458                         throws ASN1EncodingException {
459                 StringBuilder enclosure = builder;
460                 builder = new StringBuilder();
461                 for (ElementSpecification e : data.getElementTypeList()) {
462                         ASN1Type component = data.getComponent(e);
463                         if (component != null) {
464                                 encodeImpl(component, e.identifier());
465                         }
466                 }
467                 String result = builder.toString();
468                 builder = enclosure;
469                 return result;
470         }
471
472         /**
473          * Encodes each component of the {@code CollectionType} data.
474          * 
475          * @param data
476          *            The data to be encoded.
477          * @throws ASN1EncodingException
478          *             When an error occurred while the encoding process.
479          */
480         private String processCollection(CollectionType<? extends ASN1Type> data)
481                         throws ASN1EncodingException {
482                 StringBuilder enclosure = builder;
483                 builder = new StringBuilder();
484                 if (BOOLEAN.class.isAssignableFrom(data.componentType())
485                                 || ENUMERATED.class.isAssignableFrom(data.componentType())
486                                 || CHOICE.class.isAssignableFrom(data.componentType())) {
487                         for (ASN1Type e : data.collection()) {
488                                 builder.append(e.accept(this));
489                         }
490                 } else {
491                         for (ASN1Type e : data.collection()) {
492                                 encodeImpl(e, e.specification().xmlTypeName(module));
493                         }
494                 }
495                 String result = builder.toString();
496                 builder = enclosure;
497                 return result;
498         }
499
500 }