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.
153 public int encode(ASN1Type data) throws ASN1EncodingException {
154 if (module == null) {
155 module = data.specification().module();
157 builder = new StringBuilder();
158 if (!prologIsEmpty) {
159 builder.append(XML_PROLOG);
161 encodeImpl(data, data.specification().xmlTypeName(module));
163 byte[] result = builder.toString().getBytes("UTF-8");
165 return result.length;
166 } catch (Exception e) {
167 ASN1EncodingException ex = new ASN1EncodingException();
168 ex.setMessage("Failed to write result to stream.", e, null, null,
175 * Encodes the source data to xml documents and writes it to the
179 * The ASN.1 data to be encoded.
181 * The xml tag name to be set.
182 * @throws ASN1EncodingException
183 * When an error occurred while the encoding process.
185 private void encodeImpl(ASN1Type data, String xmlTagName)
186 throws ASN1EncodingException {
187 String contents = data.accept(this);
188 if (contents == null) {
190 } else if (contents.length() == 0) {
191 builder.append(TAG_START).append(xmlTagName).append(SINGLE_TAG_END);
193 builder.append(TAG_START).append(xmlTagName).append(TAG_END);
194 builder.append(contents);
195 builder.append(END_TAG_START).append(xmlTagName).append(TAG_END);
203 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
207 public String visit(BOOLEAN data) {
208 return TAG_START + (data.value() ? BOOLEAN_TRUE : BOOLEAN_FALSE)
216 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
220 public String visit(INTEGER data) {
221 String numberId = data.identifier();
222 if (numberId != null) {
223 return TAG_START + numberId + SINGLE_TAG_END;
225 return data.value().toString();
233 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
234 * .builtin.BIT_STRING)
237 public String visit(BIT_STRING data) {
238 if (data.hasNamedBits()) {
239 StringBuilder builder = new StringBuilder();
240 for (int i = 0; i < data.size(); i++) {
242 String name = data.nameOfBit(i);
244 builder.append(TAG_START).append(name)
245 .append(SINGLE_TAG_END);
249 return builder.toString();
251 return new BinString(data.value()).string();
259 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
260 * .builtin.OCTET_STRING)
263 public String visit(OCTET_STRING data) {
264 return new HexString(data.value()).string();
271 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
275 public String visit(NULL data) {
283 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
284 * .builtin.OBJECT_IDENTIFIER)
287 public String visit(OBJECT_IDENTIFIER data) {
288 StringBuilder builder = new StringBuilder();
289 if (data.value().size() > 0) {
290 builder.append(data.value().get(0));
291 for (int i = 1; i < data.value().size(); i++) {
292 builder.append('.').append(data.value().get(i));
295 return builder.toString();
302 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
303 * .builtin.RELATIVE_OID)
306 public String visit(RELATIVE_OID data) {
307 return visit((OBJECT_IDENTIFIER) data);
314 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
318 public String visit(REAL data) {
319 if (data.value().isInfinite()) {
321 + (data.value() == Double.POSITIVE_INFINITY ? REAL_PLUS_INFINITY
322 : REAL_MINUS_INFINITY) + SINGLE_TAG_END;
324 if (data.value() == 0) {
327 return BigDecimal.valueOf(data.value()).stripTrailingZeros()
335 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
336 * .builtin.ENUMERATED)
339 public String visit(ENUMERATED data) {
340 return visit((INTEGER) data);
347 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
351 public String visit(ANY data) throws ASN1EncodingException {
352 encodeImpl(data.value(),
353 data.value().specification().xmlTypeName(module));
361 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
365 public String visit(CHOICE data) throws ASN1EncodingException {
366 StringBuilder enclosure = builder;
367 builder = new StringBuilder();
368 encodeImpl(data.selectedValue(), data.selectedIdentifier());
369 String result = builder.toString();
378 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
379 * .builtin.SEQUENCE_OF)
382 public String visit(SEQUENCE_OF<? extends ASN1Type> data)
383 throws ASN1EncodingException {
384 return processCollection(data);
391 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
395 public String visit(SEQUENCE data) throws ASN1EncodingException {
396 return processConstructive(data);
403 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
407 public String visit(SET_OF<? extends ASN1Type> data)
408 throws ASN1EncodingException {
409 return processCollection(data);
416 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
420 public String visit(SET data) throws ASN1EncodingException {
421 return processConstructive(data);
428 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
432 public String visit(StringType data) throws ASN1EncodingException {
434 return new String(XerStringEscapeUtil.escape(data.stringValue())
435 .getBytes("Shift_JIS"));
436 } catch (UnsupportedEncodingException e) {
437 throw new ASN1EncodingException();
445 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
449 public String visit(TimeType data) throws ASN1EncodingException {
450 return data.stringValue();
457 * jp.bitmeister.asn1.processor.ASN1Visitor#visit(jp.bitmeister.asn1.type
461 public String visit(UnknownType data) throws ASN1EncodingException {
462 ASN1EncodingException ex = new ASN1EncodingException();
463 ex.setMessage("Can't encode unknown type.", null, data.getClass(),
469 * Encodes each element of the {@code ConstructiveType} data.
472 * The data to be encoded.
473 * @throws ASN1EncodingException
474 * When an error occurred while the encoding process.
476 private String processConstructive(ConstructiveType data)
477 throws ASN1EncodingException {
478 StringBuilder enclosure = builder;
479 builder = new StringBuilder();
480 for (ElementSpecification e : data.getElementTypeList()) {
481 ASN1Type component = data.getComponent(e);
482 if (component != null) {
483 encodeImpl(component, e.identifier());
486 String result = builder.toString();
492 * Encodes each component of the {@code CollectionType} data.
495 * The data to be encoded.
496 * @throws ASN1EncodingException
497 * When an error occurred while the encoding process.
499 private String processCollection(CollectionType<? extends ASN1Type> data)
500 throws ASN1EncodingException {
501 StringBuilder enclosure = builder;
502 builder = new StringBuilder();
503 if (BOOLEAN.class.isAssignableFrom(data.componentType())
504 || ENUMERATED.class.isAssignableFrom(data.componentType())
505 || CHOICE.class.isAssignableFrom(data.componentType())) {
506 for (ASN1Type e : data.collection()) {
507 builder.append(e.accept(this));
510 for (ASN1Type e : data.collection()) {
511 encodeImpl(e, e.specification().xmlTypeName(module));
514 String result = builder.toString();