1 /*
2  * Copyright (C) 2009 Google Inc.  All rights reserved.
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 
17 package com.google.polo.wire.xml;
18 
19 import com.google.polo.exception.PoloException;
20 import com.google.polo.exception.ProtocolErrorException;
21 import com.google.polo.json.JSONArray;
22 import com.google.polo.json.JSONException;
23 import com.google.polo.json.JSONObject;
24 import com.google.polo.json.XML;
25 import com.google.polo.pairing.PoloUtil;
26 import com.google.polo.pairing.message.ConfigurationAckMessage;
27 import com.google.polo.pairing.message.ConfigurationMessage;
28 import com.google.polo.pairing.message.EncodingOption;
29 import com.google.polo.pairing.message.EncodingOption.EncodingType;
30 import com.google.polo.pairing.message.OptionsMessage;
31 import com.google.polo.pairing.message.OptionsMessage.ProtocolRole;
32 import com.google.polo.pairing.message.PairingRequestAckMessage;
33 import com.google.polo.pairing.message.PairingRequestMessage;
34 import com.google.polo.pairing.message.PoloMessage;
35 import com.google.polo.pairing.message.PoloMessage.PoloMessageType;
36 import com.google.polo.pairing.message.SecretAckMessage;
37 import com.google.polo.pairing.message.SecretMessage;
38 
39 /**
40  * A collection of methods to convert {@link PoloMessage}s to and from XML
41  * format.
42  * <p>
43  * This wire format was specified by a third party; it uses a proprietary
44  * 64-byte message header/delimiter, and message internals are inconsistent with
45  * the protocol buffer in several places.
46  */
47 public class XmlMessageBuilder {
48 
49   /*
50    * Status types.
51    * NOTE(mikey): These do not match the values defined by
52    * OuterMessage.MessageType in polo.proto.
53    */
54 
55   public static final int STATUS_OK = 1;
56   public static final int STATUS_ERROR = 2;
57 
58   /*
59    * Key names for XML versions of messages.
60    */
61 
62   // OuterMessage XML key names
63   private static final String OUTER_FIELD_TYPE = "msg_type";
64   private static final String OUTER_FIELD_STATUS = "status";
65   private static final String OUTER_FIELD_MSG_ID = "msg_id";
66   private static final String OUTER_FIELD_PAYLOAD = "pairing_msg";
67 
68   // PairingRequestMessage XML key names
69   private static final String PAIRING_REQUEST_FIELD_PROTOCOL_VERSION =
70       "proto_version";
71 
72   // OptionsMessage XML key names
73   private static final String OPTIONS_FIELD_PREFERRED_ROLE = "pref_role";
74   private static final String OPTIONS_FIELD_OUTPUT_ENCODINGS = "out_encodings";
75   private static final String OPTIONS_FIELD_INPUT_ENCODINGS = "in_encodings";
76 
77   // ConfigurationMessage XML key names
78   private static final String CONFIG_FIELD_CLIENT_ROLE = "role";
79 
80   // EncodingOption XML key names
81   private static final String ENCODING_FIELD_TYPE = "type";
82   private static final String ENCODING_FIELD_SYMBOL_LENGTH = "min_length";
83   private static final String ENCODING_FIELD_MAX_LENGTH = "max_length";
84   private static final String ENCODING_SUBFIELD_ENCODING = "encoding";
85 
86   // SecretMessage XML key names
87   private static final String SECRET_FIELD_SECRET = "bytes";
88 
89   // Payload container names
90   private static final String MESSAGE_CONTAINER_NAME_PAIRING_REQUEST =
91       "pairing_req";
92   private static final String MESSAGE_CONTAINER_NAME_PAIRING_REQUEST_ACK =
93       "pairing_req_ack";
94   private static final String MESSAGE_CONTAINER_NAME_OPTIONS = "config_options";
95   private static final String MESSAGE_CONTAINER_NAME_CONFIG = "config";
96   private static final String MESSAGE_CONTAINER_NAME_SECRET = "secret";
97   private static final String PAIRING_REQUEST_FIELD_SERVICE_NAME = "svc_name";
98   private static final String PAIRING_REQUEST_FIELD_CLIENT_NAME = "client_name";
99   private static final String PAIRING_REQUEST_ACK_FIELD_SERVER_NAME =
100       "server_name";
101 
102   //
103   // Encoding types -- these do not match polo.proto's enum.
104   //
105 
106   public static final int ENCODING_TYPE_NUMERIC = 1;
107   public static final int ENCODING_TYPE_HEXADECIMAL = 2;
108   public static final int ENCODING_TYPE_ALPHANUMERIC = 3;
109   public static final int ENCODING_TYPE_QRCODE = 4;
110 
111   /**
112    * Cache of the last message id header value received.  The value should be
113    * copied to any response.
114    */
115   private String mLastMessageId;
116 
XmlMessageBuilder()117   public XmlMessageBuilder() {
118     mLastMessageId = null;
119   }
120 
121   /**
122    * Builds a {@link PoloMessage} from the XML version of the outer message.
123    *
124    * @param outerXml        the outermost XML string
125    * @return  a new {@link PoloMessage}
126    * @throws PoloException  on error parsing the message
127    */
outerXMLToPoloMessage(String outerXml)128   PoloMessage outerXMLToPoloMessage(String outerXml) throws PoloException {
129     JSONObject outerMessage;
130     try {
131       outerMessage = XML.toJSONObject(outerXml);
132     } catch (JSONException e) {
133         throw new PoloException(e);
134     }
135 
136     JSONObject payload;
137     PoloMessageType messageType;
138     try {
139       payload = outerMessage.getJSONObject(OUTER_FIELD_PAYLOAD);
140 
141       int status = payload.getInt(OUTER_FIELD_STATUS);
142       if (status != STATUS_OK) {
143         throw new ProtocolErrorException("Peer reported an error.");
144       }
145 
146       int msgIntVal = payload.getInt(OUTER_FIELD_TYPE);
147       messageType = PoloMessageType.fromIntVal(msgIntVal);
148     } catch (JSONException e) {
149       throw new PoloException("Bad outer message.", e);
150     }
151 
152     if (outerMessage.has("msg_id")) {
153       try {
154         mLastMessageId = outerMessage.getString("msg_id");
155       } catch (JSONException e) {
156       }
157     } else {
158       mLastMessageId = null;
159     }
160 
161     switch (messageType) {
162       case PAIRING_REQUEST:
163         return getPairingRequest(payload);
164       case PAIRING_REQUEST_ACK:
165         return getPairingRequestAck(payload);
166       case OPTIONS:
167         return getOptionsMessage(payload);
168       case CONFIGURATION:
169         return getConfigMessage(payload);
170       case CONFIGURATION_ACK:
171         return getConfigAckMessage(payload);
172       case SECRET:
173         return getSecretMessage(payload);
174       case SECRET_ACK:
175         return getSecretAckMessage(payload);
176       default:
177         return null;
178     }
179 
180   }
181 
182   /*
183    * Methods to convert XML inner messages to PoloMessage instances.
184    *
185    * NOTE(mikey): These methods are implemented in terms of JSONObject
186    * as a convenient way to represent hierarchical key->(dict|list|value)
187    * structures.
188    *
189    * Note that these methods are very similar to those found in
190    * JsonWireAdapter.  However, the XML wire format was specified with slight
191    * differences compared to the protocol buffer definition.  For example,
192    * in the OptionsMessage, encodings are wrapped in an "<options>" container.
193    *
194    * Also, many fields names have slight differences compared to the names in
195    * the protocol buffer (for example, in PairingRequestMessage, "service_name"
196    * is called "svc_name".)
197    */
198 
199   /**
200    * Generates a new {@link PairingRequestMessage} from a JSON payload.
201    *
202    * @param  body           the JSON payload
203    * @return  the new message
204    * @throws PoloException  on error parsing the {@link JSONObject}
205    */
getPairingRequest(JSONObject body)206   PairingRequestMessage getPairingRequest(JSONObject body)
207       throws PoloException {
208     try {
209       JSONObject jsonObj = body.getJSONObject(
210           MESSAGE_CONTAINER_NAME_PAIRING_REQUEST);
211       String serviceName = jsonObj.getString(
212           PAIRING_REQUEST_FIELD_SERVICE_NAME);
213       String clientName = null;
214       if (jsonObj.has(PAIRING_REQUEST_FIELD_CLIENT_NAME)) {
215         clientName = jsonObj.getString(PAIRING_REQUEST_FIELD_CLIENT_NAME);
216       }
217       return new PairingRequestMessage(serviceName, clientName);
218     } catch (JSONException e) {
219       throw new PoloException("Malformed message.", e);
220     }
221   }
222 
223   /**
224    * Generates a new {@link PairingRequestAckMessage} from a JSON payload.
225    *
226    * @param  body           the JSON payload
227    * @return                the new message
228    * @throws PoloException  on error parsing the {@link JSONObject}
229    */
getPairingRequestAck(JSONObject body)230   PairingRequestAckMessage getPairingRequestAck(JSONObject body)
231       throws PoloException {
232     try {
233       JSONObject jsonObj = body.getJSONObject(
234           MESSAGE_CONTAINER_NAME_PAIRING_REQUEST_ACK);
235       String serverName = null;
236       if (jsonObj.has(PAIRING_REQUEST_ACK_FIELD_SERVER_NAME)) {
237         serverName = jsonObj.getString(PAIRING_REQUEST_ACK_FIELD_SERVER_NAME);
238       }
239       return new PairingRequestAckMessage(serverName);
240     } catch (JSONException e) {
241       throw new PoloException("Malformed message.", e);
242     }
243   }
244 
245   /**
246    * Generates a new {@link OptionsMessage} from a JSON payload.
247    *
248    * @param  body           the JSON payload
249    * @return                the new message
250    * @throws PoloException  on error parsing the {@link JSONObject}
251    */
getOptionsMessage(JSONObject body)252   OptionsMessage getOptionsMessage(JSONObject body) throws PoloException {
253     OptionsMessage options = new OptionsMessage();
254     JSONObject jsonOptions;
255     try {
256       jsonOptions = body.getJSONObject(MESSAGE_CONTAINER_NAME_OPTIONS);
257 
258       JSONObject inEnc = jsonOptions.getJSONObject(
259           OPTIONS_FIELD_INPUT_ENCODINGS);
260       JSONObject outEnc = jsonOptions.getJSONObject(
261           OPTIONS_FIELD_OUTPUT_ENCODINGS);
262 
263       // Input encodings
264       JSONArray inEncodings = new JSONArray();
265       try {
266         inEncodings = inEnc.getJSONArray(ENCODING_SUBFIELD_ENCODING);
267       } catch (JSONException e) {
268         if (inEnc.has(ENCODING_SUBFIELD_ENCODING)) {
269           JSONObject enc = inEnc.getJSONObject(ENCODING_SUBFIELD_ENCODING);
270           inEncodings.put(enc);
271         }
272       }
273 
274       for (int i = 0; i < inEncodings.length(); i++) {
275         JSONObject enc = inEncodings.getJSONObject(i);
276         options.addInputEncoding(getEncodingOption(enc));
277       }
278 
279       // Output encodings
280       JSONArray outEncodings = new JSONArray();
281       try {
282         outEncodings = outEnc.getJSONArray(ENCODING_SUBFIELD_ENCODING);
283       } catch (JSONException e) {
284         if (outEnc.has(ENCODING_SUBFIELD_ENCODING)) {
285           JSONObject enc = outEnc.getJSONObject(ENCODING_SUBFIELD_ENCODING);
286           outEncodings.put(enc);
287         }
288       }
289 
290       for (int i = 0; i < outEncodings.length(); i++) {
291         JSONObject enc = outEncodings.getJSONObject(i);
292         options.addOutputEncoding(getEncodingOption(enc));
293       }
294 
295       // Role
296       ProtocolRole role = ProtocolRole.fromIntVal(
297           jsonOptions.getInt(OPTIONS_FIELD_PREFERRED_ROLE));
298       options.setProtocolRolePreference(role);
299     } catch (JSONException e) {
300       throw new PoloException("Malformed message.", e);
301     }
302 
303     return options;
304   }
305 
306   /**
307    * Generates a new {@link ConfigurationMessage} from a JSON payload.
308    *
309    * @param  body           the JSON payload
310    * @return                the new message
311    * @throws PoloException  on error parsing the {@link JSONObject}
312    */
getConfigMessage(JSONObject body)313   ConfigurationMessage getConfigMessage(JSONObject body)
314   throws PoloException {
315     try {
316       EncodingOption encoding = getEncodingOption(
317           body.getJSONObject(MESSAGE_CONTAINER_NAME_CONFIG)
318               .getJSONObject(ENCODING_SUBFIELD_ENCODING));
319       ProtocolRole role = ProtocolRole.fromIntVal(
320           body.getJSONObject(MESSAGE_CONTAINER_NAME_CONFIG)
321               .getInt(CONFIG_FIELD_CLIENT_ROLE));
322       return new ConfigurationMessage(encoding, role);
323     } catch (JSONException e) {
324       throw new PoloException("Malformed message.", e);
325     }
326   }
327 
328   /**
329    * Generates a new {@link ConfigurationAckMessage} from a JSON payload.
330    *
331    * @param  body           the JSON payload
332    * @return                the new message
333    */
getConfigAckMessage(JSONObject body)334   ConfigurationAckMessage getConfigAckMessage(JSONObject body) {
335     return new ConfigurationAckMessage();
336   }
337 
338   /**
339    * Generates a new {@link SecretMessage} from a JSON payload.
340    *
341    * @param  body           the JSON payload
342    * @return                the new message
343    * @throws PoloException  on error parsing the {@link JSONObject}
344    */
getSecretMessage(JSONObject body)345   SecretMessage getSecretMessage(JSONObject body) throws PoloException {
346     String secret;
347     try {
348       secret = body.getJSONObject(MESSAGE_CONTAINER_NAME_SECRET)
349           .getString(SECRET_FIELD_SECRET);
350     } catch (JSONException e) {
351       throw new PoloException("Malformed message.", e);
352     }
353     byte[] secretBytes = PoloUtil.hexStringToBytes(secret);
354     return new SecretMessage(secretBytes);
355   }
356 
357   /**
358    * Generates a new {@link SecretAckMessage} from a JSON payload.
359    *
360    * @param body  the JSON payload
361    * @return      the new message
362    */
getSecretAckMessage(JSONObject body)363   SecretAckMessage getSecretAckMessage(JSONObject body) {
364     return new SecretAckMessage(null);
365   }
366 
367   /**
368    * Generates a new {@link EncodingOption} from a JSON sub-dictionary.
369    *
370    * @param  option         the JSON sub-dictionary describing the option
371    * @return                the new {@link EncodingOption}
372    * @throws JSONException  on error parsing the {@link JSONObject}
373    */
getEncodingOption(JSONObject option)374   EncodingOption getEncodingOption(JSONObject option) throws JSONException {
375     int length = option.getInt(ENCODING_FIELD_SYMBOL_LENGTH);
376     int intType = option.getInt(ENCODING_FIELD_TYPE);
377     EncodingType type = encodingTypeFromIntValue(intType);
378     return new EncodingOption(type, length);
379   }
380 
381   /**
382    * Converts a {@link PoloMessage} to an XML string.
383    *
384    * @param message         the message to convert
385    * @return                the same message, as translated to XML
386    */
poloMessageToXML(PoloMessage message)387   public String poloMessageToXML(PoloMessage message) {
388     try {
389       if (message instanceof PairingRequestMessage) {
390         return toXML((PairingRequestMessage) message);
391       } else if (message instanceof PairingRequestAckMessage) {
392         return toXML((PairingRequestAckMessage) message);
393       } else if (message instanceof OptionsMessage) {
394         return toXML((OptionsMessage) message);
395       } else if (message instanceof ConfigurationMessage) {
396         return toXML((ConfigurationMessage) message);
397       } else if (message instanceof ConfigurationAckMessage) {
398         return toXML((ConfigurationAckMessage) message);
399       } else if (message instanceof SecretMessage) {
400         return toXML((SecretMessage) message);
401       } else if (message instanceof SecretAckMessage) {
402         return toXML((SecretAckMessage) message);
403       }
404       return null;
405     } catch (JSONException e) {
406       e.printStackTrace();
407       return "";
408     }
409 
410   }
411 
412   /**
413    * Generates a String corresponding to a full wire message (wrapped in
414    * an outer message) for the given payload.
415    */
getOuterXML(PoloMessage message, int status)416   public String getOuterXML(PoloMessage message, int status) {
417     StringBuffer out = new StringBuffer();
418 
419 
420     out.append("<" + OUTER_FIELD_PAYLOAD + ">\n");
421 
422     // status
423     out.append("<" + OUTER_FIELD_STATUS + ">");
424     out.append(status);
425     out.append("</" + OUTER_FIELD_STATUS + ">\n");
426 
427     // msg_id (optional)
428     if (mLastMessageId != null) {
429       out.append("<" + OUTER_FIELD_MSG_ID + ">");
430       out.append(mLastMessageId);
431       out.append("</" + OUTER_FIELD_MSG_ID + ">\n");
432     }
433 
434     // payload
435     if (message != null) {
436       int msgType = message.getType().getAsInt();
437       out.append("<" + OUTER_FIELD_TYPE + ">");
438       out.append(msgType);
439       out.append("</" + OUTER_FIELD_TYPE + ">\n");
440 
441       out.append(poloMessageToXML(message));
442       out.append("\n");
443     }
444 
445     out.append("</" + OUTER_FIELD_PAYLOAD + ">\n");
446     return out.toString();
447   }
448 
449 
450 
451   /**
452    * Generates an error payload corresponding to an outer message with an
453    * error code in the status field.  The error code is determined by the type
454    * of the exception.
455    *
456    * @param exception       the {@link Exception} to use to determine the error
457    *                        code
458    * @return                a string outer message
459    * @throws PoloException  on error building the message
460    */
getErrorXML(Exception exception)461   public String getErrorXML(Exception exception)
462       throws PoloException {
463     return getOuterXML(null, STATUS_ERROR);
464   }
465 
466   /**
467    * Translates a {@link PairingRequestMessage} to an XML string.
468    *
469    * @throws JSONException  on error generating the {@link String}.
470    */
toXML(PairingRequestMessage message)471   String toXML(PairingRequestMessage message) throws JSONException {
472     JSONObject jsonObj = new JSONObject();
473     JSONObject pairingReq = new JSONObject();
474     jsonObj.put(MESSAGE_CONTAINER_NAME_PAIRING_REQUEST, pairingReq);
475     pairingReq.put(PAIRING_REQUEST_FIELD_SERVICE_NAME,
476         message.getServiceName());
477     if (message.hasClientName()) {
478       pairingReq.put(PAIRING_REQUEST_FIELD_CLIENT_NAME,
479           message.getServiceName());
480     }
481     pairingReq.put(PAIRING_REQUEST_FIELD_PROTOCOL_VERSION, 1);
482     return XML.toString(jsonObj);
483   }
484 
485   /**
486    * Translates a {@link PairingRequestAckMessage} to an XML string.
487    *
488    * @throws JSONException  on error generating the {@link String}.
489    */
toXML(PairingRequestAckMessage message)490   String toXML(PairingRequestAckMessage message) throws JSONException {
491     JSONObject jsonObj = new JSONObject();
492     JSONObject pairingReq = new JSONObject();
493     jsonObj.put(MESSAGE_CONTAINER_NAME_PAIRING_REQUEST_ACK, pairingReq);
494     if (message.hasServerName()) {
495       jsonObj.put(PAIRING_REQUEST_ACK_FIELD_SERVER_NAME,
496           message.getServerName());
497     }
498     pairingReq.put(PAIRING_REQUEST_FIELD_PROTOCOL_VERSION, 1);
499     return XML.toString(jsonObj);
500   }
501 
502   /**
503    * Translates a {@link OptionsMessage} to an XML string.
504    *
505    * @throws JSONException  on error generating the {@link String}.
506    */
toXML(OptionsMessage message)507  String toXML(OptionsMessage message) throws JSONException {
508     JSONObject jsonObj = new JSONObject();
509     JSONObject options = new JSONObject();
510 
511     JSONObject inEncs = new JSONObject();
512     JSONArray inEncsArray = new JSONArray();
513     for (EncodingOption encoding : message.getInputEncodingSet()) {
514       inEncsArray.put(encodingToJson(encoding));
515     }
516     inEncs.put(ENCODING_SUBFIELD_ENCODING, inEncsArray);
517     options.put(OPTIONS_FIELD_INPUT_ENCODINGS, inEncs);
518 
519     JSONObject outEncs = new JSONObject();
520     JSONArray outEncsArray = new JSONArray();
521     for (EncodingOption encoding : message.getOutputEncodingSet()) {
522       outEncsArray.put(encodingToJson(encoding));
523     }
524     outEncs.put(ENCODING_SUBFIELD_ENCODING, outEncsArray);
525     options.put(OPTIONS_FIELD_OUTPUT_ENCODINGS, outEncs);
526 
527     options.put(OPTIONS_FIELD_PREFERRED_ROLE,
528         message.getProtocolRolePreference().ordinal());
529     jsonObj.put(MESSAGE_CONTAINER_NAME_OPTIONS, options);
530     return XML.toString(jsonObj);
531   }
532 
533   /**
534    * Translates a {@link ConfigurationMessage} to an XML string.
535    *
536    * @throws JSONException  on error generating the {@link String}.
537    */
toXML(ConfigurationMessage message)538   String toXML(ConfigurationMessage message) throws JSONException {
539     JSONObject jsonObj = new JSONObject();
540     JSONObject config = new JSONObject();
541     JSONObject encoding = encodingToJson(message.getEncoding());
542     config.put(ENCODING_SUBFIELD_ENCODING, encoding);
543     config.put(CONFIG_FIELD_CLIENT_ROLE, message.getClientRole().ordinal());
544     jsonObj.put(MESSAGE_CONTAINER_NAME_CONFIG, config);
545     return XML.toString(jsonObj);
546   }
547 
548   /**
549    * Translates a {@link ConfigurationAckMessage} to an XML string.
550    */
toXML(ConfigurationAckMessage message)551  String toXML(ConfigurationAckMessage message) {
552     return "";
553   }
554 
555  /**
556   * Translates a {@link SecretMessage} to an XML string.
557   *
558   * @throws JSONException  on error generating the {@link String}.
559   */
toXML(SecretMessage message)560   String toXML(SecretMessage message) throws JSONException {
561     JSONObject jsonObj = new JSONObject();
562     JSONObject secret = new JSONObject();
563     String bytesStr = PoloUtil.bytesToHexString(message.getSecret());
564     secret.put(SECRET_FIELD_SECRET, bytesStr);
565     jsonObj.put(MESSAGE_CONTAINER_NAME_SECRET, secret);
566     return XML.toString(jsonObj);
567   }
568 
569   /**
570    * Translates a {@link SecretAckMessage} to an XML string.
571    */
toXML(SecretAckMessage message)572   String toXML(SecretAckMessage message) {
573     return "";
574   }
575 
576   /**
577    * Translates a {@link EncodingOption} to a {@link JSONObject}.
578    *
579    * @throws JSONException  on error generating the {@link JSONObject}
580    */
encodingToJson(EncodingOption encoding)581   JSONObject encodingToJson(EncodingOption encoding) throws JSONException {
582     JSONObject result = new JSONObject();
583     int intType = encodingTypeToIntVal(encoding.getType());
584     result.put(ENCODING_FIELD_TYPE, intType);
585     result.put(ENCODING_FIELD_SYMBOL_LENGTH, encoding.getSymbolLength());
586     result.put(ENCODING_FIELD_MAX_LENGTH, encoding.getSymbolLength());
587     return result;
588   }
589 
590   /**
591    * Converts an {@link EncodingType} to the numeric value used on the wire.
592    * <p>
593    * Note that in this implementation, the values used on the wire do not match
594    * those returned by {@link EncodingType#getAsInt()}, hence the extra method.
595    *
596    * @param type  the {@link EncodingType}
597    * @return      an integer representation
598    */
encodingTypeToIntVal(EncodingType type)599   private static int encodingTypeToIntVal(EncodingType type) {
600     switch (type) {
601       case ENCODING_ALPHANUMERIC:
602         return ENCODING_TYPE_ALPHANUMERIC;
603       case ENCODING_NUMERIC:
604         return ENCODING_TYPE_NUMERIC;
605       case ENCODING_HEXADECIMAL:
606         return ENCODING_TYPE_HEXADECIMAL;
607       case ENCODING_QRCODE:
608         return ENCODING_TYPE_QRCODE;
609       case ENCODING_UNKNOWN:
610       default:
611         return 0;
612     }
613   }
614 
615   /**
616    * Converts a numeric value used on the wire to the corresponding
617    * {@link EncodingType}.
618    * <p>
619    * Note that in this implementation, the values used on the wire do not match
620    * those returned by {@link EncodingType#getAsInt()}, hence the extra method.
621    *
622    * @param intType  the value used on the wire
623    * @return         the corresponding {@link EncodingType}
624    */
encodingTypeFromIntValue(int intType)625   private static EncodingType encodingTypeFromIntValue(int intType) {
626     EncodingType type = EncodingType.ENCODING_UNKNOWN;
627     switch (intType) {
628       case ENCODING_TYPE_ALPHANUMERIC:
629         type = EncodingType.ENCODING_ALPHANUMERIC;
630         break;
631       case ENCODING_TYPE_NUMERIC:
632         type = EncodingType.ENCODING_NUMERIC;
633         break;
634       case ENCODING_TYPE_HEXADECIMAL:
635         type = EncodingType.ENCODING_HEXADECIMAL;
636         break;
637       case ENCODING_TYPE_QRCODE:
638         type = EncodingType.ENCODING_QRCODE;
639         break;
640       default:
641         type = EncodingType.ENCODING_UNKNOWN;
642       break;
643     }
644     return type;
645   }
646 
647 }
648