PtsPrimitiveParser.java
上传用户:szyujian
上传日期:2016-09-20
资源大小:320k
文件大小:23k
源码类别:

android开发

开发平台:

C/C++

  1. /*
  2.  * Copyright (C) 2007 Esmertec AG.
  3.  * Copyright (C) 2007 The Android Open Source Project
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package com.android.im.imps;
  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.util.ArrayList;
  23. import java.util.HashMap;
  24. import java.util.Map.Entry;
  25. import java.util.regex.Matcher;
  26. import java.util.regex.Pattern;
  27. import com.android.im.imps.Primitive.TransactionMode;
  28. /**
  29.  * PTS/SMS encoded IMPS messages parser. Only response transactions and
  30.  * server initiated requests are supported.
  31.  */
  32. public class PtsPrimitiveParser implements PrimitiveParser {
  33.     // WVaaBBcccDD <parameters>
  34.     //   aa - version number; 12 for 1.2, 13 for 1.3; "XX" for version discovery
  35.     //   BB - message type, case insensitive
  36.     //   ccc - transaction id in range 0-999 without preceding zero
  37.     //   DD - multiple SMSes identifier
  38.     private static final Pattern sPreamplePattern =
  39.         Pattern.compile("\AWV(\d{2})(\p{Alpha}{2})(\d{1,3})(\p{Alpha}{2})?(\z| .*)");
  40.     private char mReadBuf[] = new char[256];
  41.     private StringBuilder mStringBuf = new StringBuilder();
  42.     private int mPos;
  43.     private static int UNCERTAIN_GROUP_SIZE = -1;
  44.     public Primitive parse(InputStream in) throws ParserException, IOException {
  45.         // assuming PTS data is always short
  46.         BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
  47.         mStringBuf.setLength(0);
  48.         mPos = 0;
  49.         int len;
  50.         while ((len = reader.read(mReadBuf)) != -1) {
  51.             mStringBuf.append(mReadBuf, 0, len);
  52.         }
  53.         return parsePrim();
  54.     }
  55.     private Primitive parsePrim() throws ParserException
  56.     {
  57.         Matcher m = sPreamplePattern.matcher(mStringBuf);
  58.         if (!m.matches()) {
  59.             throw new ParserException("Invalid PTS encoded message");
  60.         }
  61.         Primitive p = new Primitive();
  62.         // TODO: handle WV version in m.group(1)
  63.         String type = m.group(2).toUpperCase();
  64.         String transactionType = PtsCodes.getTransaction(type);
  65.         if (transactionType == null) {
  66.             throw new ParserException("Unrecognized transaction code " + type);
  67.         }
  68.         p.setContentElement(transactionType);
  69.         if (PtsCodes.isServerRequestCode(type)) {
  70.             p.setTransactionMode(TransactionMode.Request);
  71.         } else {
  72.             p.setTransactionMode(TransactionMode.Response);
  73.         }
  74.         p.setTransactionId(m.group(3));
  75.         mPos = m.start(5);
  76.         if (mPos < mStringBuf.length()) {
  77.             match(' ');
  78.             HashMap<String, ParamValue> params = parseParams();
  79.             for (Entry<String, ParamValue> param : params.entrySet()) {
  80.                 translateParam(p, param.getKey(), param.getValue());
  81.             }
  82.         }
  83.         return p;
  84.     }
  85.     private static HashMap<String, Integer> sInfoElemTypeMap;
  86.     private static final int ELEM_OTHER_SIMPLE         = 0;
  87.     private static final int ELEM_SESSION_ID           = 1;
  88.     private static final int ELEM_RESULT               = 2;
  89.     private static final int ELEM_ALL_FUNCTIONS        = 3;
  90.     private static final int ELEM_NOT_AVAIL_FUNCS      = 4;
  91.     private static final int ELEM_CAPABILITY_LIST      = 5;
  92.     private static final int ELEM_CONTACT_LIST         = 6;
  93.     private static final int ELEM_DEFAULT_CONTACT_LIST = 7;
  94.     private static final int ELEM_USER_NICK_LIST       = 8;
  95.     private static final int ELEM_CONTACT_LIST_PROPS   = 9;
  96.     private static final int ELEM_PRESENCE             = 10;
  97.     /*
  98.     private static final int ELEM_RESULT_CLIST  = 3;
  99.     private static final int ELEM_RESULT_DOMAIN = 4;
  100.     private static final int ELEM_RESULT_GROUP  = 5;
  101.     private static final int ELEM_RESULT_MSGID  = 6;
  102.     private static final int ELEM_RESULT_SCRNAME = 7;
  103.     private static final int ELEM_RESULT_USER   = 8;
  104.     */
  105.     static {
  106.         sInfoElemTypeMap = new HashMap<String, Integer>();
  107.         sInfoElemTypeMap.put(PtsCodes.SessionID, ELEM_SESSION_ID);
  108.         sInfoElemTypeMap.put(PtsCodes.Status, ELEM_RESULT);
  109.         sInfoElemTypeMap.put(PtsCodes.NotAvailableFunctions, ELEM_NOT_AVAIL_FUNCS);
  110.         sInfoElemTypeMap.put(PtsCodes.AllFunctions, ELEM_ALL_FUNCTIONS);
  111.         sInfoElemTypeMap.put(PtsCodes.AgreedCapabilityList, ELEM_CAPABILITY_LIST);
  112.         sInfoElemTypeMap.put(PtsCodes.ContactList, ELEM_CONTACT_LIST);
  113.         sInfoElemTypeMap.put(PtsCodes.DefaultContactList, ELEM_DEFAULT_CONTACT_LIST);
  114.         sInfoElemTypeMap.put(PtsCodes.UserNickList, ELEM_USER_NICK_LIST);
  115.         sInfoElemTypeMap.put(PtsCodes.ContactListProps, ELEM_CONTACT_LIST_PROPS);
  116.         sInfoElemTypeMap.put(PtsCodes.Presence, ELEM_PRESENCE);
  117.     }
  118.     private static void translateParam(Primitive p, String elemCode,
  119.             ParamValue elemValue) throws ParserException {
  120.         int type;
  121.         elemCode = elemCode.toUpperCase();
  122.         // FIXME: Should be refactored when we had concrete situation of the null value case
  123.         if (elemValue == null) {
  124.             throw new ParserException("Parameter " + elemCode + " must have value.");
  125.         }
  126.         if (sInfoElemTypeMap.containsKey(elemCode)) {
  127.             type = sInfoElemTypeMap.get(elemCode);
  128.             /*
  129.             if (type == ELEM_RESULT_CLIST && p.getType().equals(ImpsTags.Login_Response)) {
  130.                 // Fix up DigestSchema which shares a same code with
  131.                 // ContactListID. It appears only in Login_Response.
  132.                 type = ELEM_OTHER_SIMPLE;
  133.             }
  134.             */
  135.         } else {
  136.             type = ELEM_OTHER_SIMPLE;
  137.         }
  138.         switch (type) {
  139.         case ELEM_SESSION_ID:
  140.             if (elemValue.mStrValue == null) {
  141.                 throw new ParserException("Element SessionID must have string value!");
  142.             }
  143.             if (p.getType().equals(ImpsTags.Login_Response)) {
  144.                 p.addElement(ImpsTags.SessionID, elemValue.mStrValue);
  145.             } else {
  146.                 p.setSession(elemValue.mStrValue);
  147.             }
  148.             break;
  149.         case ELEM_RESULT:
  150.             // ST=<StatusCode>
  151.             // ST=(<StatusCode>,<Description>)
  152.             PrimitiveElement result = p.addElement(ImpsTags.Result);
  153.             if (elemValue.mStrValue != null) {
  154.                 result.addChild(ImpsTags.Code, elemValue.mStrValue);
  155.             } else {
  156.                 checkGroupValue(elemValue.mValueGroup, 2);
  157.                 result.addChild(ImpsTags.Code, elemValue.mValueGroup.get(0).mStrValue);
  158.                 result.addChild(ImpsTags.Description, elemValue.mValueGroup.get(1).mStrValue);
  159.             }
  160.             break;
  161.         case ELEM_ALL_FUNCTIONS:
  162.         case ELEM_NOT_AVAIL_FUNCS:
  163.             p.addElement(translateServiceTree(elemCode, elemValue));
  164.             break;
  165.         case ELEM_CAPABILITY_LIST:
  166.             p.addElement(translateCapabilityList(elemValue));
  167.             break;
  168.         case ELEM_CONTACT_LIST:
  169.             if (elemValue.mStrValue != null) {
  170.                 p.addElement(ImpsTags.ContactList, elemValue.mStrValue);
  171.             } else {
  172.                 checkGroupValue(elemValue.mValueGroup, UNCERTAIN_GROUP_SIZE);
  173.                 for (ParamValue value : elemValue.mValueGroup) {
  174.                     p.addElement(ImpsTags.ContactList, value.mStrValue);
  175.                 }
  176.             }
  177.             break;
  178.         case ELEM_DEFAULT_CONTACT_LIST:
  179.             if (elemValue.mStrValue == null) {
  180.                 throw new ParserException("Deafult Contact List must have string value!");
  181.             }
  182.             p.addElement(ImpsTags.DefaultContactList, elemValue.mStrValue);
  183.             break;
  184.         case ELEM_USER_NICK_LIST:
  185.         {
  186.             checkGroupValue(elemValue.mValueGroup, UNCERTAIN_GROUP_SIZE);
  187.             PrimitiveElement nicklistElem = p.addElement(ImpsTags.NickList);
  188.             int groupSize = elemValue.mValueGroup.size();
  189.             for (int i = 0; i < groupSize; i++) {
  190.                 ArrayList<ParamValue> valueGroup = elemValue.mValueGroup.get(i).mValueGroup;
  191.                 checkGroupValue(valueGroup, 2);
  192.                 String nickname = valueGroup.get(0).mStrValue;
  193.                 String address  = valueGroup.get(1).mStrValue;
  194.                 if (nickname == null || address == null) {
  195.                     throw new ParserException("Null value found for NickName: " + nickname
  196.                             + "-" + address);
  197.                 }
  198.                 PrimitiveElement nicknameElem = nicklistElem.addChild(ImpsTags.NickName);
  199.                 nicknameElem.addChild(ImpsTags.Name, "".equals(nickname) ? null : nickname);
  200.                 nicknameElem.addChild(ImpsTags.UserID, address);
  201.             }
  202.         }
  203.             break;
  204.         case ELEM_CONTACT_LIST_PROPS:
  205.         {
  206.             checkGroupValue(elemValue.mValueGroup, UNCERTAIN_GROUP_SIZE);
  207.             PrimitiveElement propertiesElem = p.addElement(ImpsTags.ContactListProperties);
  208.             int groupSize = elemValue.mValueGroup.size();
  209.             for (int i = 0; i < groupSize; i++) {
  210.                 ArrayList<ParamValue> valueGroup = elemValue.mValueGroup.get(i).mValueGroup;
  211.                 checkGroupValue(valueGroup, 2);
  212.                 String name  = valueGroup.get(0).mStrValue;
  213.                 String value = valueGroup.get(1).mStrValue;
  214.                 if (name == null || value == null) {
  215.                     throw new ParserException("Null value found for property: " + name + "-" + value);
  216.                 }
  217.                 if (PtsCodes.DisplayName.equals(name)) {
  218.                     name = ImpsConstants.DisplayName;
  219.                 } else if (PtsCodes.Default.equals(name)) {
  220.                     name = ImpsConstants.Default;
  221.                 } else {
  222.                     throw new ParserException("Unrecognized property " + name);
  223.                 }
  224.                 PrimitiveElement propertyElem = propertiesElem.addChild(ImpsTags.Property);
  225.                 propertyElem.addChild(ImpsTags.Name, name);
  226.                 propertyElem.addChild(ImpsTags.Value, value);
  227.             }
  228.         }
  229.             break;
  230.         case ELEM_PRESENCE:
  231.             //PR=(<UserID>[,<PresenceSubList>])
  232.             //PR=((<UserID>[,<PresenceSubList>]),(<UserID>[,<PresenceSubList>]))
  233.             checkGroupValue(elemValue.mValueGroup, UNCERTAIN_GROUP_SIZE);
  234.             if (elemValue.mValueGroup.size() == 1) {
  235.                 // PR=(<UserID>)
  236.                 ParamValue value = elemValue.mValueGroup.get(0);
  237.                 if (value.mStrValue != null) {
  238.                     p.addElement(ImpsTags.Presence).addChild(ImpsTags.UserID, value.mStrValue);
  239.                 } else {
  240.                     // workaround for OZ server
  241.                     p.addElement(translatePresence(value.mValueGroup));
  242.                 }
  243.             } else {
  244.                 if (elemValue.mValueGroup.get(0).mStrValue == null) {
  245.                     // PR=((<UserID>[,<PresenceSubList>]),(<UserID>[,<PresenceSubList>]))
  246.                     int groupSize = elemValue.mValueGroup.size();
  247.                     for (int i = 0; i < groupSize; i++) {
  248.                         ParamValue value = elemValue.mValueGroup.get(i);
  249.                         if (value.mStrValue != null) {
  250.                             p.addElement(ImpsTags.Presence).addChild(ImpsTags.UserID, value.mStrValue);
  251.                         } else {
  252.                             p.addElement(translatePresence(value.mValueGroup));
  253.                         }
  254.                     }
  255.                 } else {
  256.                     // PR=(<UserID>,<PresenceSubList>)
  257.                     p.addElement(translatePresence(elemValue.mValueGroup));
  258.                 }
  259.             }
  260.             break;
  261.         case ELEM_OTHER_SIMPLE:
  262.             p.addElement(translateSimpleElem(elemCode, elemValue));
  263.             break;
  264.         default:
  265.             throw new ParserException("Unsupported element " + elemValue);
  266.         }
  267.     }
  268.     private static PrimitiveElement translatePresence(ArrayList<ParamValue> valueGroup)
  269.             throws ParserException {
  270.         checkGroupValue(valueGroup, UNCERTAIN_GROUP_SIZE);
  271.         PrimitiveElement presence = new PrimitiveElement(ImpsTags.Presence);
  272.         if (valueGroup.get(0).mStrValue == null) {
  273.             throw new ParserException("UserID must have string value!");
  274.         }
  275.         presence.addChild(ImpsTags.UserID, valueGroup.get(0).mStrValue);
  276.         if (valueGroup.size() > 1) {
  277.             // has presence sub list
  278.             presence.addChild(translatePresenceSubList(valueGroup.get(1)));
  279.         }
  280.         return presence;
  281.     }
  282.     private static PrimitiveElement translatePresenceSubList(ParamValue value)
  283.             throws ParserException {
  284.         checkGroupValue(value.mValueGroup, UNCERTAIN_GROUP_SIZE);
  285.         PrimitiveElement presenceSubList = new PrimitiveElement(ImpsTags.PresenceSubList);
  286.         int groupSize = value.mValueGroup.size();
  287.         for (int i = 0; i < groupSize; i++) {
  288.             ParamValue v = value.mValueGroup.get(i);
  289.             if (v.mStrValue != null) {
  290.                 throw new ParserException("Unexpected string value for presence attribute");
  291.             }
  292.             presenceSubList.addChild(translatePresenceAttribute(v.mValueGroup));
  293.         }
  294.         return presenceSubList;
  295.     }
  296.     // <attribute>[,<qualifier>][,<value>]
  297.     // <attribute>[,<qualifier>,<sub-attribute>]
  298.     private static PrimitiveElement translatePresenceAttribute(
  299.             ArrayList<ParamValue> valueGroup) throws ParserException {
  300.         String type = valueGroup.get(0).mStrValue;
  301.         if (type == null) {
  302.             return null;
  303.         }
  304.         String tag = PtsCodes.getPresenceAttributeElement(type);
  305.         if (tag == null) {
  306.             return null;
  307.         }
  308.         PrimitiveElement paElem = new PrimitiveElement(tag);
  309.         if (valueGroup.size() == 2) {
  310.             // no qualifier
  311.             translateAttributeValue(paElem, valueGroup.get(1), false);
  312.         }else if (valueGroup.size() == 3) {
  313.             // has qualifier, and it should has no group value
  314.             ParamValue qualifierValue = valueGroup.get(1);
  315.             if (qualifierValue.mStrValue == null) {
  316.                 throw new ParserException("Qualifier value can't be group value!");
  317.             }
  318.             if (!"".equals(qualifierValue.mStrValue)) {
  319.                 paElem.addChild(ImpsTags.Qualifier, qualifierValue.mStrValue);
  320.             }
  321.             translateAttributeValue(paElem, valueGroup.get(2), true);
  322.         } else {
  323.             return null;
  324.         }
  325.         return paElem;
  326.     }
  327.     private static void translateAttributeValue(PrimitiveElement paElem,
  328.             ParamValue v, boolean hasQualifier) throws ParserException {
  329.         if (v.mStrValue == null) {
  330.             // sub-attribute as value
  331.             checkGroupValue(v.mValueGroup, UNCERTAIN_GROUP_SIZE);
  332.             if (v.mValueGroup.get(0).mStrValue != null) {
  333.                 paElem.addChild(translatePresenceAttribute(v.mValueGroup));
  334.             } else {
  335.                 int groupSize = v.mValueGroup.size();
  336.                 for (int i = 0; i < groupSize; i++) {
  337.                     ParamValue value = v.mValueGroup.get(i);
  338.                     if (value.mStrValue != null) {
  339.                         throw new ParserException("Presence Attribute value error!");
  340.                     }
  341.                     checkGroupValue(value.mValueGroup, UNCERTAIN_GROUP_SIZE);
  342.                     paElem.addChild(translatePresenceAttribute(value.mValueGroup));
  343.                 }
  344.             }
  345.         } else {
  346.             // single simple value
  347.             if (hasQualifier) {
  348.                 paElem.addChild(ImpsTags.PresenceValue, PtsCodes.getPAValue(v.mStrValue));
  349.             } else {
  350.                 paElem.setContents(PtsCodes.getPAValue(v.mStrValue));
  351.             }
  352.         }
  353.     }
  354.     private static void checkGroupValue(ArrayList<ParamValue> valueGroup,
  355.             int expectedGroupSize) throws ParserException {
  356.         if (valueGroup == null
  357.                 || (expectedGroupSize != UNCERTAIN_GROUP_SIZE
  358.                         && valueGroup.size() != expectedGroupSize)) {
  359.             throw new ParserException("Invalid group value!");
  360.         }
  361.         int groupSize = valueGroup.size();
  362.         for (int i = 0; i < groupSize; i++) {
  363.             if (valueGroup.get(i) == null) {
  364.                 throw new ParserException("Invalid group value!");
  365.             }
  366.         }
  367.     }
  368.     private static PrimitiveElement translateCapabilityList(ParamValue elemValue)
  369.             throws ParserException {
  370.         PrimitiveElement elem = new PrimitiveElement(ImpsTags.AgreedCapabilityList);
  371.         ArrayList<ParamValue> params = elemValue.mValueGroup;
  372.         if (params != null) {
  373.             checkGroupValue(params, UNCERTAIN_GROUP_SIZE);
  374.             int paramsSize = params.size();
  375.             for (int i = 0; i < paramsSize; i++) {
  376.                 ArrayList<ParamValue> capElemGroup = params.get(i).mValueGroup;
  377.                 checkGroupValue(capElemGroup, 2);
  378.                 String capElemCode = capElemGroup.get(0).mStrValue;
  379.                 String capElemName;
  380.                 if (capElemCode == null
  381.                         || (capElemName = PtsCodes.getCapElement(capElemCode)) == null) {
  382.                     throw new ParserException("Unknown capability element "
  383.                             + capElemCode);
  384.                 }
  385.                 String capElemValue = capElemGroup.get(1).mStrValue;
  386.                 if (capElemValue == null) {
  387.                     throw new ParserException("Illegal capability value for "
  388.                             + capElemCode);
  389.                 }
  390.                 capElemValue = PtsCodes.getCapValue(capElemValue);
  391.                 elem.addChild(capElemName, capElemValue);
  392.             }
  393.         }
  394.         return elem;
  395.     }
  396.     private static PrimitiveElement translateServiceTree(String elemCode,
  397.             ParamValue elemValue) throws ParserException {
  398.         String elemName = PtsCodes.getElement(elemCode);
  399.         PrimitiveElement elem = new PrimitiveElement(elemName);
  400.         // TODO: translate the service tree.
  401.         return elem;
  402.     }
  403.     private static PrimitiveElement translateSimpleElem(String elemCode, ParamValue value)
  404.             throws ParserException {
  405.         String elemName = PtsCodes.getElement(elemCode);
  406.         if (elemName == null) {
  407.             throw new ParserException("Unrecognized parameter " + elemCode);
  408.         }
  409.         PrimitiveElement elem = new PrimitiveElement(elemName);
  410.         if (value.mStrValue != null) {
  411.             elem.setContents(value.mStrValue);
  412.         } else {
  413.             throw new ParserException("Don't know how to handle parameters for "
  414.                     + elemName);
  415.         }
  416.         return elem;
  417.     }
  418.     private HashMap<String, ParamValue> parseParams() throws ParserException {
  419.         int pos = mPos;
  420.         StringBuilder buf = mStringBuf;
  421.         int len = buf.length();
  422.         HashMap<String, ParamValue> ret = new HashMap<String, ParamValue>();
  423.         String paramName;
  424.         ParamValue paramValue;
  425.         while (pos < len) {
  426.             int nameStart = pos;
  427.             while (pos < len) {
  428.                 char ch = buf.charAt(pos);
  429.                 if (ch == ' ' || ch == '=') {
  430.                     break;
  431.                 }
  432.                 pos++;
  433.             }
  434.             if (nameStart == pos) {
  435.                 throw new ParserException("Missing parameter name near " + pos);
  436.             }
  437.             paramName = buf.substring(nameStart, pos);
  438.             if (pos < len && buf.charAt(pos) == '=') {
  439.                 pos++;
  440.                 mPos = pos;
  441.                 paramValue = parseParamValue();
  442.                 pos = mPos;
  443.             } else {
  444.                 paramValue = null;
  445.             }
  446.             ret.put(paramName, paramValue);
  447.             if (pos < len) {
  448.                 // more parameters ahead
  449.                 match(' ');
  450.                 pos = mPos;
  451.             }
  452.         }
  453.         return ret;
  454.     }
  455.     private ParamValue parseParamValue() throws ParserException {
  456.         int pos = mPos;
  457.         StringBuilder buf = mStringBuf;
  458.         int len = buf.length();
  459.         if (pos == len) {
  460.             throw new ParserException("Missing parameter value near " + pos);
  461.         }
  462.         ParamValue value = new ParamValue();
  463.         char ch = buf.charAt(pos);
  464.         if (ch == '(') {
  465.             // value list
  466.             pos++;
  467.             ArrayList<ParamValue> valueGroup = new ArrayList<ParamValue>();
  468.             while (pos < len) {
  469.                 mPos = pos;
  470.                 valueGroup.add(parseParamValue());
  471.                 pos = mPos;
  472.                 if (pos == len) {
  473.                     throw new ParserException("Unexpected parameter end");
  474.                 }
  475.                 if (buf.charAt(pos) != ',') {
  476.                     break;
  477.                 }
  478.                 pos++;
  479.             }
  480.             mPos = pos;
  481.             match(')');
  482.             if (valueGroup.isEmpty()) {
  483.                 throw new ParserException("Empty value group near " + mPos);
  484.             }
  485.             value.mValueGroup = valueGroup;
  486.         } else {
  487.             // single value
  488.             if (ch == '"') {
  489.                 // quoted value
  490.                 pos++;
  491.                 StringBuilder escapedValue = new StringBuilder();
  492.                 boolean quotedEnd = false;
  493.                 while (pos < len) {
  494.                     ch = buf.charAt(pos);
  495.                     pos++;
  496.                     if (ch == '"') {
  497.                         if (pos < len && buf.charAt(pos) == '"') {
  498.                             // "doubled" quote
  499.                             pos++;
  500.                         } else {
  501.                             quotedEnd = true;
  502.                             break;
  503.                         }
  504.                     }
  505.                     escapedValue.append(ch);
  506.                 }
  507.                 if (!quotedEnd) {
  508.                     throw new ParserException("Unexpected quoted parameter end");
  509.                 }
  510.                 value.mStrValue = escapedValue.toString();
  511.             } else {
  512.                 int valueStart = pos;
  513.                 while (pos < len) {
  514.                     ch = buf.charAt(pos);
  515.                     if (ch == ',' || ch == ')' || ch == ' ') {
  516.                         break;
  517.                     }
  518.                     if (""(=&".indexOf(ch) != -1) {
  519.                         throw new ParserException("Special character " + ch
  520.                                 + " must be quoted");
  521.                     }
  522.                     pos++;
  523.                 }
  524.                 value.mStrValue = buf.substring(valueStart, pos);
  525.             }
  526.             mPos = pos;
  527.         }
  528.         return value;
  529.     }
  530.     private void match(char c) throws ParserException {
  531.         if (mStringBuf.charAt(mPos) != c) {
  532.             throw new ParserException("Expected " + c + " at pos " + mPos);
  533.         }
  534.         mPos++;
  535.     }
  536.     /**
  537.      * Detect if this short message is a PTS encoded WV-primitive.
  538.      */
  539.     public static boolean isPtsPrimitive(CharSequence msg)
  540.     {
  541.         if (msg == null) {
  542.             return false;
  543.         }
  544.         Matcher m = sPreamplePattern.matcher(msg);
  545.         return m.matches();
  546.     }
  547.     static final class ParamValue {
  548.         public String mStrValue;
  549.         public ArrayList<ParamValue> mValueGroup;
  550.     }
  551. }