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.codec.xer;
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;
29 import java.io.OutputStream;
30 import java.io.UnsupportedEncodingException;
31 import java.math.BigDecimal;
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;
63 * XER (XML Encoding Rules) encoder.
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
74 * @author WATANABE, Jun. <jwat at bitmeister.jp>
79 public class XerEncoder implements ASN1Encoder,
80 ASN1Visitor<String, ASN1EncodingException> {
82 private Class<? extends ASN1Module> module;
84 private OutputStream out;
86 private StringBuilder builder;
88 private boolean prologIsEmpty = false;
91 * Instantiates a {@code XEREncoder}.
94 * The {@code OutputStream} that encoded octets will be written.
96 public XerEncoder(OutputStream out) {
101 * Instantiates a {@code XEREncoder}.
104 * The ASN.1 module used for encoding.
106 * The {@code OutputStream} that encoded octets will be written.
108 public XerEncoder(Class<? extends ASN1Module> module, OutputStream out) {
110 this.module = module;
114 * Instantiates a {@code XEREncoder}.
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
122 public XerEncoder(OutputStream out, boolean prologIsEmpty) {
124 this.prologIsEmpty = prologIsEmpty;
128 * Instantiates a {@code XEREncoder}.
131 * The ASN.1 module used for encoding.
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
138 public XerEncoder(Class<? extends ASN1Module> module, OutputStream out, boolean prologIsEmpty) {
140 this.prologIsEmpty = prologIsEmpty;
144 * Encodes an ASN.1 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.
152 public int encode(ASN1Type data) throws ASN1EncodingException {
153 if (module == null) {
154 module = data.specification().module();
156 builder = new StringBuilder();
157 if (!prologIsEmpty) {
158 builder.append(XML_PROLOG);
160 encodeImpl(data, data.specification().xmlTypeName(module));
162 byte[] result = builder.toString().getBytes("UTF-8");
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,
174 * Encodes the source data to xml documents and writes it to the
178 * The ASN.1 data to be encoded.
180 * The xml tag name to be set.
181 * @throws ASN1EncodingException
182 * When an error occurred while the encoding process.
184 private void encodeImpl(ASN1Type data, String xmlTagName)
185 throws ASN1EncodingException {
186 String contents = data.accept(this);
187 if (contents == null) {
189 } else if (contents.length() == 0) {
190 builder.append(TAG_START).append(xmlTagName).append(SINGLE_TAG_END);
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);
202 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
205 public String visit(BOOLEAN data) {
206 return TAG_START + (data.value() ? BOOLEAN_TRUE : BOOLEAN_FALSE)
214 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
217 public String visit(INTEGER data) {
218 String numberId = data.identifier();
219 if (numberId != null) {
220 return TAG_START + numberId + SINGLE_TAG_END;
222 return data.value().toString();
230 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
231 * .builtin.BIT_STRING)
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++) {
238 String name = data.nameOfBit(i);
240 builder.append(TAG_START).append(name)
241 .append(SINGLE_TAG_END);
245 return builder.toString();
247 return new BinString(data.value()).string();
255 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
256 * .builtin.OCTET_STRING)
258 public String visit(OCTET_STRING data) {
259 return new HexString(data.value()).string();
266 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
269 public String visit(NULL data) {
277 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
278 * .builtin.OBJECT_IDENTIFIER)
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));
288 return builder.toString();
295 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
296 * .builtin.RELATIVE_OID)
298 public String visit(RELATIVE_OID data) {
299 return visit((OBJECT_IDENTIFIER) data);
306 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
309 public String visit(REAL data) {
310 if (data.value().isInfinite()) {
312 + (data.value() == Double.POSITIVE_INFINITY ? REAL_PLUS_INFINITY
313 : REAL_MINUS_INFINITY) + SINGLE_TAG_END;
315 if (data.value() == 0) {
318 return BigDecimal.valueOf(data.value()).stripTrailingZeros()
326 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
327 * .builtin.ENUMERATED)
329 public String visit(ENUMERATED data) {
330 return visit((INTEGER) data);
337 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
340 public String visit(ANY data) throws ASN1EncodingException {
341 encodeImpl(data.value(),
342 data.value().specification().xmlTypeName(module));
350 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
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();
366 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
367 * .builtin.SEQUENCE_OF)
369 public String visit(SEQUENCE_OF<? extends ASN1Type> data)
370 throws ASN1EncodingException {
371 return processCollection(data);
378 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
381 public String visit(SEQUENCE data) throws ASN1EncodingException {
382 return processConstructive(data);
389 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
392 public String visit(SET_OF<? extends ASN1Type> data)
393 throws ASN1EncodingException {
394 return processCollection(data);
401 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
404 public String visit(SET data) throws ASN1EncodingException {
405 return processConstructive(data);
412 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
415 public String visit(StringType data) throws ASN1EncodingException {
417 return new String(XerStringEscapeUtil.escape(data.stringValue())
418 .getBytes("Shift_JIS"));
419 } catch (UnsupportedEncodingException e) {
420 throw new ASN1EncodingException();
428 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
431 public String visit(TimeType data) throws ASN1EncodingException {
432 return data.stringValue();
439 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
442 public String visit(UnknownType data) throws ASN1EncodingException {
443 ASN1EncodingException ex = new ASN1EncodingException();
444 ex.setMessage("Can't encode unknown type.", null, data.getClass(),
450 * Encodes each element of the {@code ConstructiveType} data.
453 * The data to be encoded.
454 * @throws ASN1EncodingException
455 * When an error occurred while the encoding process.
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());
467 String result = builder.toString();
473 * Encodes each component of the {@code CollectionType} data.
476 * The data to be encoded.
477 * @throws ASN1EncodingException
478 * When an error occurred while the encoding process.
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));
491 for (ASN1Type e : data.collection()) {
492 encodeImpl(e, e.specification().xmlTypeName(module));
495 String result = builder.toString();