/*
* Funambol is a mobile platform developed by Funambol, Inc.
* Copyright (C) 2009 Funambol, Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*
* You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
* 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by Funambol" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by Funambol".
*/
package com.funambol.syncml.spds;
import java.util.Vector;
import java.util.Date;
import java.util.Calendar;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParser;
//import org.xmlpull.v1.XmlPullParserFactory;
import com.funambol.org.kxml2.io.KXmlParser;
import com.funambol.org.kxml2.wap.Wbxml;
import com.funambol.org.kxml2.wap.WbxmlParser;
import com.funambol.syncml.protocol.*;
import com.funambol.util.XmlUtil;
import com.funambol.util.Log;
/**
* This class is meant to provide a SyncML parser. Such a parser reads a SyncML
* message and builds a representation of this message based on the objects
* provided by the com.funambol.syncml.protocol objects.
* The implementation is based on KXml and relies on its XmlPull interface. As
* such it is capable of parsing both XML and WBXML.
* At the moment the implementation is restricted to some components of the
* SyncML message. In particular the DevInf section.
* This parser performs a relaxed parsing, allowing unknown tokens to be parsed.
* These tokens are simply skipped and they are not reflected into the SyncML
* representation.
*/
public class SyncMLParser {
private static final String TAG_LOG = "SyncMLParser";
private boolean wbxml = true;
// This flag turns on super verbosity mode to trace parser execution
// This is a development only feature. Leave it false on standard build.
private boolean verbose = false;
public SyncMLParser(boolean wbxml) {
this.wbxml = wbxml;
}
public SyncML parse(byte message[]) throws SyncMLParserException {
/*
// KXml minimal version does not have the factory parser,
// therefore we need direct instantiation
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = factory.newPullParser();
*/
XmlPullParser parser;
if (wbxml) {
parser = com.funambol.org.kxml2.wap.syncml.SyncML.createParser();
} else {
parser = new KXmlParser();
try {
parser.setFeature("NORMALIZE", false);
} catch (Exception e) {
Log.error(TAG_LOG, "Parser does not support unnormalized mode");
}
}
if (Log.isLoggable(Log.TRACE)) {
Log.trace(TAG_LOG, "parse");
}
SyncHdr header = null;
SyncBody body = null;
try {
ByteArrayInputStream is = new ByteArrayInputStream(message);
parser.setInput(is, "UTF-8");
// Begin parsing
nextSkipSpaces(parser);
// If the first tag is not the SyncML start tag, then this is an
// invalid message
require(parser, parser.START_TAG, null, SyncML.TAG_SYNCML);
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_SYNCHDR.equals(tagName)) {
header = parseHeader(parser);
} else if (SyncML.TAG_SYNCBODY.equals(tagName)) {
body = parseSyncBody(parser);
} else {
String msg = "Error parsing. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_SYNCML);
} catch (Exception e) {
Log.error(TAG_LOG, "Error parsing command", e);
throw new SyncMLParserException("Cannot parse command: " + e.toString());
}
SyncML syncML = new SyncML();
if (header != null) {
syncML.setSyncHdr(header);
}
if (body != null) {
syncML.setSyncBody(body);
}
return syncML;
}
public DevInf parseXMLDevInf(String message) throws SyncMLParserException {
XmlPullParser parser = new KXmlParser();
try {
parser.setFeature("NORMALIZE", false);
} catch (Exception e) {
Log.error(TAG_LOG, "Parser does not support unnormalized mode");
}
try {
ByteArrayInputStream is = new ByteArrayInputStream(message.getBytes("UTF-8"));
parser.setInput(is, "UTF-8");
// Begin parsing
nextSkipSpaces(parser);
// If the first tag is not the SyncML start tag, then this is an
// invalid message
require(parser, parser.START_TAG, null, SyncML.TAG_DEVINF);
return parseDevInf(parser);
} catch (Exception e) {
Log.error(TAG_LOG, "Cannot parse dev inf", e);
throw new SyncMLParserException("Cannot parse dev inf");
}
}
private void parseFinal(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
nextSkipSpaces(parser);
require(parser, parser.END_TAG, null, SyncML.TAG_FINAL);
}
private void parseNoResp(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
nextSkipSpaces(parser);
require(parser, parser.END_TAG, null, SyncML.TAG_NORESP);
}
private SyncHdr parseHeader(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseHeader");
VerDTD verDTD = null;
String verProto = null;
String sessionID = null;
String msgId = null;
Source source = null;
Target target = null;
Meta meta = null;
String respUri = null;
Cred cred = null;
boolean noResp = false;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_VERDTD.equals(tagName)) {
String verDTDvalue = parseSimpleStringTag(parser, SyncML.TAG_VERDTD);
verDTD = new VerDTD(verDTDvalue);
} else if (SyncML.TAG_VERPROTO.equals(tagName)) {
verProto = parseSimpleStringTag(parser, SyncML.TAG_VERPROTO);
} else if (SyncML.TAG_SESSIONID.equals(tagName)) {
sessionID = parseSimpleStringTag(parser, SyncML.TAG_SESSIONID);
} else if (SyncML.TAG_MSGID.equals(tagName)) {
msgId = parseSimpleStringTag(parser, SyncML.TAG_MSGID);
} else if (SyncML.TAG_SOURCE.equals(tagName)) {
source = parseSource(parser);
} else if (SyncML.TAG_TARGET.equals(tagName)) {
target = parseTarget(parser);
} else if (SyncML.TAG_META.equals(tagName)) {
meta = parseMeta(parser);
} else if (SyncML.TAG_RESPURI.equals(tagName)) {
respUri = parseSimpleStringTag(parser, SyncML.TAG_RESPURI);
} else if (SyncML.TAG_CRED.equals(tagName)) {
cred = parseCred(parser);
} else if (SyncML.TAG_NORESP.equals(tagName)) {
parseNoResp(parser);
noResp = true;
} else {
String msg = "Error parsing header tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_SYNCHDR);
SyncHdr hdr = new SyncHdr();
if (verDTD != null) {
hdr.setVerDTD(verDTD);
}
if (verProto != null) {
hdr.setVerProto(verProto);
}
if (sessionID != null) {
hdr.setSessionID(sessionID);
}
if (msgId != null) {
hdr.setMsgID(msgId);
}
if (source != null) {
hdr.setSource(source);
}
if (target != null) {
hdr.setTarget(target);
}
if (respUri != null) {
hdr.setRespURI(respUri);
}
if (cred != null) {
hdr.setCred(cred);
}
if (noResp) {
hdr.setNoResp(new Boolean(true));
}
if (meta != null) {
hdr.setMeta(meta);
}
return hdr;
}
private SyncBody parseSyncBody(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseSyncBody");
Vector commands = new Vector();
boolean lastMsg = false;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_STATUS.equals(tagName)) {
Status status = parseStatus(parser);
commands.addElement(status);
} else if (SyncML.TAG_SYNC.equals(tagName)) {
Sync sync = parseSync(parser);
commands.addElement(sync);
} else if (SyncML.TAG_RESULTS.equals(tagName)) {
Results results = parseResults(parser);
commands.addElement(results);
} else if (SyncML.TAG_ALERT.equals(tagName)) {
Alert alert = parseAlert(parser);
commands.addElement(alert);
} else if (SyncML.TAG_GET.equals(tagName)) {
Get get = parseGet(parser);
commands.addElement(get);
} else if (SyncML.TAG_PUT.equals(tagName)) {
Put put = parsePut(parser);
commands.addElement(put);
} else if (SyncML.TAG_FINAL.equals(tagName)) {
parseFinal(parser);
lastMsg = true;
} else if (SyncML.TAG_MAP.equals(tagName)) {
Map map = parseMap(parser);
commands.addElement(map);
} else {
String msg = "Error parsing sync item tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_SYNCBODY);
SyncBody syncBody = new SyncBody();
syncBody.setCommands(commands);
syncBody.setFinalMsg(new Boolean(lastMsg));
return syncBody;
}
private Status parseStatus(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseStatus");
String cmdId = null;
String msgRef = null;
String cmdRef = null;
String cmd = null;
Data data = null;
Vector targetRefs = new Vector();
Vector sourceRefs = new Vector();
Vector items = new Vector();
Chal chal = null;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_CMDID.equals(tagName)) {
cmdId = parseSimpleStringTag(parser, SyncML.TAG_CMDID);
} else if (SyncML.TAG_MSGREF.equals(tagName)) {
msgRef = parseSimpleStringTag(parser, SyncML.TAG_MSGREF);
} else if (SyncML.TAG_TARGETREF.equals(tagName)) {
TargetRef targetRef = parseTargetRef(parser);
targetRefs.addElement(targetRef);
} else if (SyncML.TAG_SOURCEREF.equals(tagName)) {
SourceRef sourceRef = parseSourceRef(parser);
sourceRefs.addElement(sourceRef);
} else if (SyncML.TAG_ITEM.equals(tagName)) {
Item item = parseSyncItem(parser);
items.addElement(item);
} else if (SyncML.TAG_CMDREF.equals(tagName)) {
cmdRef = parseSimpleStringTag(parser, SyncML.TAG_CMDREF);
} else if (SyncML.TAG_CMD.equals(tagName)) {
cmd = parseSimpleStringTag(parser, SyncML.TAG_CMD);
} else if (SyncML.TAG_DATA.equals(tagName)) {
String dataVal = parseSimpleStringTag(parser, SyncML.TAG_DATA);
data = Data.newInstance(dataVal);
} else if (SyncML.TAG_CHAL.equals(tagName)) {
chal = parseChal(parser);
} else {
String msg = "Error parsing sync item tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_STATUS);
Status status = Status.newInstance();
if (cmdId != null) {
status.setCmdID(cmdId);
}
if (msgRef != null) {
status.setMsgRef(msgRef);
}
if (sourceRefs.size() > 0) {
status.setSourceRef(sourceRefs);
}
if (targetRefs.size() > 0) {
status.setTargetRef(targetRefs);
}
if (items.size() > 0) {
status.setItems(items);
}
if (cmdRef != null) {
status.setCmdRef(cmdRef);
}
if (cmd != null) {
status.setCmd(cmd);
}
if (data != null) {
status.setData(data);
}
if (chal != null) {
status.setChal(chal);
}
return status;
}
private Map parseMap(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseMap");
Source source = null;
Target target = null;
String cmdId = null;
Vector mappings = new Vector();
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_SOURCE.equals(tagName)) {
source = parseSource(parser);
} else if (SyncML.TAG_CMDID.equals(tagName)) {
cmdId = parseSimpleStringTag(parser, SyncML.TAG_CMDID);
} else if (SyncML.TAG_TARGET.equals(tagName)) {
target = parseTarget(parser);
} else if (SyncML.TAG_MAPITEM.equals(tagName)) {
MapItem mapItem = parseMapItem(parser);
mappings.addElement(mapItem);
} else {
String msg = "Error parsing chal element. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
Map map = new Map();
if (cmdId != null) {
map.setCmdID(cmdId);
}
if (source != null) {
map.setSource(source);
}
if (target != null) {
map.setTarget(target);
}
if (mappings.size() > 0) {
map.setMapItems(mappings);
}
return map;
}
private MapItem parseMapItem(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseMapItem");
Source source = null;
Target target = null;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_SOURCE.equals(tagName)) {
source = parseSource(parser);
} else if (SyncML.TAG_TARGET.equals(tagName)) {
target = parseTarget(parser);
} else {
String msg = "Error parsing chal element. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
MapItem mapItem = new MapItem();
if (source != null) {
mapItem.setSource(source);
}
if (target != null) {
mapItem.setTarget(target);
}
return mapItem;
}
private Chal parseChal(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseChal");
Meta meta = null;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_META.equals(tagName)) {
meta = parseMeta(parser);
} else if (SyncML.TAG_CMDID.equals(tagName)) {
String cmdId = parseSimpleStringTag(parser, SyncML.TAG_CMDID);
} else {
String msg = "Error parsing chal element. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
Chal chal = new Chal();
if (meta != null) {
chal.setMeta(meta);
}
return chal;
}
private Cred parseCred(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseCred");
Meta meta = null;
Data data = null;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_META.equals(tagName)) {
meta = parseMeta(parser);
} else if (SyncML.TAG_DATA.equals(tagName)) {
String dataVal = parseSimpleStringTag(parser, SyncML.TAG_DATA);
data = Data.newInstance(dataVal);
} else {
String msg = "Error parsing chal element. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
Cred cred = new Cred();
if (meta != null) {
cred.setMeta(meta);
}
if (data != null) {
cred.setData(data);
}
return cred;
}
private Get parseGet(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseGet");
Meta meta = null;
Cred cred = null;
String lang = null;
boolean noResp = false;
String cmdId = null;
Vector items = new Vector();
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_META.equals(tagName)) {
meta = parseMeta(parser);
} else if (SyncML.TAG_CRED.equals(tagName)) {
cred = parseCred(parser);
} else if (SyncML.TAG_LANG.equals(tagName)) {
lang = parseSimpleStringTag(parser, SyncML.TAG_LANG);
} else if (SyncML.TAG_NORESP.equals(tagName)) {
parseNoResp(parser);
noResp = true;
} else if (SyncML.TAG_CMDID.equals(tagName)) {
cmdId = parseSimpleStringTag(parser, SyncML.TAG_CMDID);
} else if (SyncML.TAG_ITEM.equals(tagName)) {
Item item = parseSyncItem(parser);
items.addElement(item);
} else {
String msg = "Error parsing get element. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
Get get = new Get();
if (meta != null) {
get.setMeta(meta);
}
if (cred != null) {
get.setCred(cred);
}
if (lang != null) {
get.setLang(lang);
}
if (cmdId != null) {
get.setCmdID(cmdId);
}
get.setNoResp(new Boolean(noResp));
if (items.size() > 0) {
get.setItems(items);
}
return get;
}
private Put parsePut(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parsePut");
Meta meta = null;
Cred cred = null;
String lang = null;
boolean noResp = false;
String cmdId = null;
Vector items = new Vector();
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_META.equals(tagName)) {
meta = parseMeta(parser);
} else if (SyncML.TAG_CRED.equals(tagName)) {
cred = parseCred(parser);
} else if (SyncML.TAG_LANG.equals(tagName)) {
lang = parseSimpleStringTag(parser, SyncML.TAG_LANG);
} else if (SyncML.TAG_NORESP.equals(tagName)) {
parseNoResp(parser);
noResp = true;
} else if (SyncML.TAG_CMDID.equals(tagName)) {
cmdId = parseSimpleStringTag(parser, SyncML.TAG_CMDID);
} else if (SyncML.TAG_ITEM.equals(tagName)) {
Item item = parseSyncItem(parser);
items.addElement(item);
} else {
String msg = "Error parsing get element. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
Put put = new Put();
if (meta != null) {
put.setMeta(meta);
}
if (cred != null) {
put.setCred(cred);
}
if (lang != null) {
put.setLang(lang);
}
if (cmdId != null) {
put.setCmdID(cmdId);
}
if (items.size() > 0) {
put.setItems(items);
}
put.setNoResp(new Boolean(noResp));
return put;
}
private Alert parseAlert(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseAlert");
String cmdId = null;
int alertCode = 0;
Vector items = new Vector();
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_CMDID.equals(tagName)) {
cmdId = parseSimpleStringTag(parser, SyncML.TAG_CMDID);
} else if (SyncML.TAG_DATA.equals(tagName)) {
Data data = parseItemData(parser);
String dataVal = null;
if (data.getData() != null) {
dataVal = data.getData();
} else if (data.getBinData() != null) {
// This is not a real binary data
dataVal = new String(data.getBinData());
}
try {
alertCode = Integer.parseInt(dataVal);
} catch (Exception e) {
throw new SyncMLParserException("Invalid alert code: " + dataVal);
}
} else if (SyncML.TAG_ITEM.equals(tagName)) {
Item item = parseAlertItem(parser);
items.addElement(item);
} else {
String msg = "Error parsing sync item tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_ALERT);
Alert alert = new Alert();
if (cmdId != null) {
alert.setCmdID(cmdId);
}
alert.setData(alertCode);
if (items.size() > 0) {
alert.setItems(items);
}
return alert;
}
private Item parseAlertItem(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseAlertItem");
Target target = null;
Source source = null;
Meta meta = null;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_TARGET.equals(tagName)) {
target = parseTarget(parser);
} else if (SyncML.TAG_SOURCE.equals(tagName)) {
source = parseSource(parser);
} else if (SyncML.TAG_META.equals(tagName)) {
meta = parseMeta(parser);
} else {
String msg = "Error parsing sync item tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_ITEM);
Item item = Item.newInstance();
if (target != null) {
item.setTarget(target);
}
if (source != null) {
item.setSource(source);
}
if (meta != null) {
item.setMeta(meta);
}
return item;
}
private Sync parseSync(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseSync");
String cmdId = null;
Target target = null;
Source source = null;
Vector commands = new Vector();
Long numChanges = null;
boolean noResp = false;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_CMDID.equals(tagName)) {
cmdId = parseSimpleStringTag(parser, SyncML.TAG_CMDID);
} else if (SyncML.TAG_SOURCE.equals(tagName)) {
source = parseSource(parser);
} else if (SyncML.TAG_TARGET.equals(tagName)) {
target = parseTarget(parser);
} else if (SyncML.TAG_ADD.equals(tagName)) {
SyncMLCommand command = parseCommand(parser, SyncML.TAG_ADD);
commands.addElement(command);
} else if (SyncML.TAG_REPLACE.equals(tagName)) {
SyncMLCommand command = parseCommand(parser, SyncML.TAG_REPLACE);
commands.addElement(command);
} else if (SyncML.TAG_DELETE.equals(tagName)) {
SyncMLCommand command = parseCommand(parser, SyncML.TAG_DELETE);
commands.addElement(command);
} else if (SyncML.TAG_NUMBEROFCHANGES.equals(tagName)) {
long numChangesValue = parseSimpleLongTag(parser, SyncML.TAG_NUMBEROFCHANGES);
numChanges = new Long(numChangesValue);
} else if (SyncML.TAG_NORESP.equals(tagName)) {
parseNoResp(parser);
noResp = true;
} else {
String msg = "Error parsing sync item tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_SYNC);
Sync sync = new Sync();
if (cmdId != null) {
sync.setCmdID(cmdId);
}
if (numChanges != null) {
sync.setNumberOfChanges(numChanges);
}
if (commands.size() > 0) {
sync.setCommands(commands);
}
if (source != null) {
sync.setSource(source);
}
if (target != null) {
sync.setTarget(target);
}
if (noResp) {
sync.setNoResp(new Boolean(true));
}
return sync;
}
public SyncMLCommand parseCommand(XmlPullParser parser, String name) throws SyncMLParserException,
IOException,
XmlPullParserException
{
verboseLog("parseCommand");
Vector items = new Vector();
String cmdId = null;
Meta meta = null;
boolean noResp = false;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_CMDID.equals(tagName)) {
cmdId = parseSimpleStringTag(parser, SyncML.TAG_CMDID);
} else if (SyncML.TAG_ITEM.equals(tagName)) {
Item item = parseSyncItem(parser);
items.addElement(item);
} else if (SyncML.TAG_META.equals(tagName)) {
meta = parseMeta(parser);
} else if (SyncML.TAG_NORESP.equals(tagName)) {
parseNoResp(parser);
noResp = true;
} else {
String msg = "Error parsing command tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, name);
SyncMLCommand cmd = SyncMLCommand.newInstance(name);
cmd.setCmdId(cmdId);
if (meta != null) {
cmd.setMeta(meta);
}
if (items.size() > 0) {
cmd.setItems(items);
}
if (noResp) {
cmd.setNoResp(true);
}
return cmd;
}
private Item parseSyncItem(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseSyncItem");
Source source = null;
Target target = null;
Data data = null;
SourceParent sourceParent = null;
TargetParent targetParent = null;
boolean hasMoreData = false;
Meta meta = null;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_TARGET.equals(tagName)) {
target = parseTarget(parser);
} else if (SyncML.TAG_SOURCE.equals(tagName)) {
source = parseSource(parser);
} else if (SyncML.TAG_TARGET_PARENT.equals(tagName)) {
targetParent = parseTargetParent(parser);
} else if (SyncML.TAG_SOURCE_PARENT.equals(tagName)) {
sourceParent = parseSourceParent(parser);
} else if (SyncML.TAG_MORE_DATA.equals(tagName)) {
parseSimpleStringTag(parser, SyncML.TAG_MORE_DATA);
hasMoreData = true;
} else if (SyncML.TAG_DATA.equals(tagName)) {
data = parseItemData(parser);
} else if (SyncML.TAG_META.equals(tagName)) {
meta = parseMeta(parser);
} else {
String msg = "Error parsing sync item tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_ITEM);
Item item = Item.newInstance();
if (source != null) {
item.setSource(source);
}
if (target != null) {
item.setTarget(target);
}
if (data != null) {
item.setData(data);
}
if (hasMoreData) {
item.setMoreData(new Boolean(hasMoreData));
}
if (sourceParent != null) {
item.setSourceParent(sourceParent);
}
if (targetParent != null) {
item.setTargetParent(targetParent);
}
if (meta != null) {
item.setMeta(meta);
}
return item;
}
private Target parseTarget(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseTarget");
nextSkipSpaces(parser);
// TODO handle filter
String locUri = null;
String locName = null;
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_LOC_URI.equals(tagName)) {
locUri = parseSimpleStringTag(parser, SyncML.TAG_LOC_URI);
} else if (SyncML.TAG_LOC_NAME.equals(tagName)) {
locName = parseSimpleStringTag(parser, SyncML.TAG_LOC_NAME);
} else {
String msg = "Error parsing target item tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_TARGET);
Target target = Target.newInstance();
if (locUri != null) {
target.setLocURI(locUri);
}
if (locName != null) {
target.setLocName(locName);
}
return target;
}
private Source parseSource(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseSource");
nextSkipSpaces(parser);
// TODO handle filter
String locUri = null;
String locName = null;
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_LOC_URI.equals(tagName)) {
locUri = parseSimpleStringTag(parser, SyncML.TAG_LOC_URI);
} else if (SyncML.TAG_LOC_NAME.equals(tagName)) {
locName = parseSimpleStringTag(parser, SyncML.TAG_LOC_NAME);
} else {
String msg = "Error parsing target item tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_SOURCE);
Source source = Source.newInstance();
if (locUri != null) {
source.setLocURI(locUri);
}
if (locName != null) {
source.setLocName(locName);
}
return source;
}
private TargetParent parseTargetParent(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseTargetParent");
nextSkipSpaces(parser);
// We expect the LocURI only
require(parser, parser.START_TAG, null, SyncML.TAG_LOC_URI);
String locUri = parseSimpleStringTag(parser, SyncML.TAG_LOC_URI);
nextSkipSpaces(parser);
require(parser, parser.END_TAG, null, SyncML.TAG_TARGET_PARENT);
TargetParent targetParent = new TargetParent(locUri);
return targetParent;
}
private SourceParent parseSourceParent(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseSourceParent");
nextSkipSpaces(parser);
// We expect the LocURI only
require(parser, parser.START_TAG, null, SyncML.TAG_LOC_URI);
String locUri = parseSimpleStringTag(parser, SyncML.TAG_LOC_URI);
nextSkipSpaces(parser);
require(parser, parser.END_TAG, null, SyncML.TAG_SOURCE_PARENT);
SourceParent sourceParent = SourceParent.newInstance();
sourceParent.setLocURI(locUri);
return sourceParent;
}
private Data parseItemData(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseItemData");
boolean done = false;
String textData = null;
byte binData[] = null;
Anchor anchor = null;
DevInf devInf = null;
StringBuffer preamble = new StringBuffer();
// Use the nextToken here to catch binary data (Wbxml OPAQUE)
parser.nextToken();
if (parser.getEventType() == parser.CDSECT) {
// This can only happen in XML, so we can ignore binary data and
// grab the content directly (note that this can only be a leaf, so
// this must be textData)
textData = parser.getText();
// Advance to the next token
parser.nextToken();
} else {
while(parser.getEventType() == parser.IGNORABLE_WHITESPACE ||
parser.getEventType() == parser.TEXT ||
parser.getEventType() == parser.ENTITY_REF)
{
preamble.append(parser.getText());
parser.nextToken();
}
textData = preamble.toString();
if (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_ANCHOR.equals(tagName)) {
anchor = parseAnchor(parser);
} else if (SyncML.TAG_DEVINF.equals(tagName)) {
devInf = parseDevInf(parser);
} else {
throw new SyncMLParserException("Unkonw tag in data: " + tagName);
}
nextSkipSpaces(parser);
} else if (parser.getEventType() == WbxmlParser.WAP_EXTENSION) {
// This is binary data
binData = parseBinaryData(parser);
} else if (parser.getEventType() == parser.TEXT) {
textData = parseTextData(parser, textData);
// We expect text plain data. Since this text is not contained
// in CDATA section, we must unescape it
if (!wbxml) {
textData = XmlUtil.unescapeXml(textData);
}
}
}
// We require the END DATA tag here
require(parser, parser.END_TAG, null, SyncML.TAG_DATA);
Data data;
if (anchor != null) {
data = Data.newInstance(anchor);
} else if (devInf != null) {
data = Data.newInstance(devInf);
} else if (binData != null) {
data = Data.newInstance(binData);
} else {
data = Data.newInstance(textData);
}
return data;
}
private byte[] parseBinaryData(XmlPullParser parser) throws XmlPullParserException,
SyncMLParserException,
IOException
{
verboseLog("parseBinaryData");
byte binData[] = null;
// We support the OPAQUE as binary data
if (parser instanceof WbxmlParser) {
WbxmlParser wbxmlParser = (WbxmlParser)parser;
int wapId = wbxmlParser.getWapCode();
if (wapId == Wbxml.OPAQUE) {
binData = (byte[])wbxmlParser.getWapExtensionData();
} else {
throw new SyncMLParserException("Cannot parse WAP EXTENSION " + wapId);
}
} else {
throw new SyncMLParserException("Cannot parse binary data in XML");
}
// Advance to the next token
nextSkipSpaces(parser);
return binData;
}
private String parseTextData(XmlPullParser parser, String preamble) throws XmlPullParserException,
SyncMLParserException,
IOException
{
verboseLog("parseTextData");
StringBuffer value = new StringBuffer(preamble);
String v = null;
while (parser.getEventType() != parser.END_TAG) {
// Now fetch the rest of the data tag
parser.nextToken();
if (parser.getEventType() == parser.TEXT ||
parser.getEventType() == parser.IGNORABLE_WHITESPACE ||
parser.getEventType() == parser.ENTITY_REF)
{
v = parser.getText();
value.append(v);
} else if (parser.getEventType() != parser.END_TAG) {
throw new SyncMLParserException("Unexpected event: " + parser.getEventType());
}
}
// Try to avoid redundant memory usage.
if (v != null && v.length() == value.length()) {
return v;
} else if (v == null) {
return preamble;
} else {
return value.toString();
}
}
private Anchor parseAnchor(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseAnchor");
String last = null;
String next = null;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_LAST.equals(tagName)) {
last = parseSimpleStringTag(parser, SyncML.TAG_LAST);
} else if (SyncML.TAG_NEXT.equals(tagName)) {
next = parseSimpleStringTag(parser, SyncML.TAG_NEXT);
} else {
String msg = "Error parsing anchor tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_ANCHOR);
Anchor anchor = new Anchor(last, next);
return anchor;
}
/**
* Parse the results section of the server response. At the moment this
* method assumes this section contains only the server device info. This is
* not true in general, but this is currently a limitation. In the future it
* will be made more general.
*
* @param results is the results section of the server response. This value
* shall not contain the <Results> tag
* @return a Results object representing the XML element
* @throws SyncMLParserException if the text cannot be parser properly. Note
* that if the text contains unknown tags, they are simply skipped, but if
* it has malformed xml, an exception is thrown.
*/
public Results parseResults(XmlPullParser parser) throws SyncMLParserException,
IOException,
XmlPullParserException
{
String cmdId = null;
String msgRef = null;
String cmdRef = null;
Meta meta = null;
Vector items = new Vector();
TargetRef tgtRef = null;
SourceRef srcRef = null;
verboseLog("parseResults");
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_CMDID.equals(tagName)) {
cmdId = parseSimpleStringTag(parser, SyncML.TAG_CMDID);
} else if (SyncML.TAG_MSGREF.equals(tagName)) {
msgRef = parseSimpleStringTag(parser, SyncML.TAG_MSGREF);
} else if (SyncML.TAG_CMDREF.equals(tagName)) {
cmdRef = parseSimpleStringTag(parser, SyncML.TAG_CMDREF);
} else if (SyncML.TAG_META.equals(tagName)) {
meta = parseMeta(parser);
} else if (SyncML.TAG_ITEM.equals(tagName)) {
DevInfItem devInfItem = parseDevInfItem(parser);
items.addElement(devInfItem);
} else if (SyncML.TAG_TARGETREF.equals(tagName)) {
tgtRef = parseTargetRef(parser);
} else if (SyncML.TAG_SOURCEREF.equals(tagName)) {
srcRef = parseSourceRef(parser);
} else {
String msg = "Error parsing device info tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_RESULTS);
Results results = new Results();
if (cmdId != null) {
results.setCmdID(cmdId);
}
if (msgRef != null) {
results.setMsgRef(msgRef);
}
if (cmdRef != null) {
results.setCmdRef(cmdRef);
}
if (meta != null) {
results.setMeta(meta);
}
if (items.size() > 0) {
results.setItems(items);
}
if (tgtRef != null) {
results.setTargetRef(tgtRef);
}
if (srcRef != null) {
results.setSourceRef(srcRef);
}
return results;
}
private Meta parseMeta(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseMeta");
String type = null;
String format = null;
Anchor anchor = null;
NextNonce nextNonce = null;
Long size = null;
Long maxMsgSize = null;
Long maxObjSize = null;
MetInf metInf = null;
String version = null;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_TYPE.equals(tagName)) {
type = parseSimpleStringTag(parser, SyncML.TAG_TYPE);
} else if (SyncML.TAG_FORMAT.equals(tagName)) {
format = parseSimpleStringTag(parser, SyncML.TAG_FORMAT);
} else if (SyncML.TAG_NEXTNONCE.equals(tagName)) {
String nextNonceVal = parseSimpleStringTag(parser, SyncML.TAG_NEXTNONCE);
nextNonce = new NextNonce(nextNonceVal);
} else if (SyncML.TAG_ANCHOR.equals(tagName)) {
anchor = parseAnchor(parser);
} else if (SyncML.TAG_SIZE.equals(tagName)) {
long sizeVal = parseSimpleLongTag(parser, SyncML.TAG_SIZE);
size = new Long(sizeVal);
} else if (SyncML.TAG_MAXMSGSIZE.equals(tagName)) {
long sizeVal = parseSimpleLongTag(parser, SyncML.TAG_MAXMSGSIZE);
maxMsgSize = new Long(sizeVal);
} else if (SyncML.TAG_MAXOBJSIZE.equals(tagName)) {
long sizeVal = parseSimpleLongTag(parser, SyncML.TAG_MAXOBJSIZE);
maxObjSize = new Long(sizeVal);
} else if (SyncML.TAG_METAINF.equals(tagName)) {
metInf = parseMetaInf(parser);
} else if (SyncML.TAG_VERSION.equals(tagName)) {
version = parseSimpleStringTag(parser, SyncML.TAG_VERSION);
} else {
String msg = "Error parsing META tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_META);
Meta meta = Meta.newInstance();
// If the metainf is specified as an attribute, then this object is
// allowed to be null
if (metInf == null) {
metInf = MetInf.newInstance();
}
if (type != null) {
metInf.setType(type);
}
if (format != null) {
metInf.setFormat(format);
}
if (nextNonce != null) {
metInf.setNextNonce(nextNonce);
}
meta.setMetInf(metInf);
if (anchor != null) {
meta.setAnchor(anchor);
}
if (size != null) {
meta.setSize(size);
}
if (maxMsgSize != null) {
meta.setMaxMsgSize(maxMsgSize);
}
if (maxObjSize != null) {
meta.setMaxObjSize(maxObjSize);
}
if (version != null) {
meta.setVersion(version);
}
return meta;
}
private MetInf parseMetaInf(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
String type = null;
String format = null;
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_TYPE.equals(tagName)) {
type = parseSimpleStringTag(parser, SyncML.TAG_TYPE);
} else if (SyncML.TAG_FORMAT.equals(tagName)) {
format = parseSimpleStringTag(parser, SyncML.TAG_FORMAT);
} else {
String msg = "Error parsing META tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_METAINF);
MetInf metaInf = MetInf.newInstance();
if (format != null) {
metaInf.setFormat(format);
}
if (type != null) {
metaInf.setType(type);
}
return metaInf;
}
private DevInfItem parseDevInfItem(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
DevInf devInf = null;
Meta meta = null;
Source source = null;
Target target = null;
verboseLog("parseDevInfItem");
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_SOURCE.equals(tagName)) {
source = parseItemSource(parser);
} else if (SyncML.TAG_DATA.equals(tagName)) {
if (source != null && SyncML.DEVINF12.equals(source.getLocURI())) {
devInf = parseDevInfData(parser);
} else {
parseData(parser);
}
} else if (SyncML.TAG_META.equals(tagName)) {
meta = parseMeta(parser);
} else if (SyncML.TAG_TARGET.equals(tagName)) {
target = parseTarget(parser);
} else {
String msg = "Error parsing ITEM tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_ITEM);
DevInfItem item = new DevInfItem();
if (devInf != null) {
item.setDevInf(devInf);
}
if (meta != null) {
item.setMeta(meta);
}
if (source != null) {
item.setSource(source);
}
if (target != null) {
item.setTarget(target);
}
return item;
}
private Source parseItemSource(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException {
verboseLog("parseItemSource");
nextSkipSpaces(parser);
Source source = Source.newInstance();
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_LOCURI.equals(tagName)) {
String locUri = parseSimpleStringTag(parser, SyncML.TAG_LOCURI);
source.setLocURI(locUri);
} else if (SyncML.TAG_LOCNAME.equals(tagName)) {
String locName = parseSimpleStringTag(parser, SyncML.TAG_LOCNAME);
source.setLocName(locName);
} else {
String msg = "Error parsing ITEM tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_SOURCE);
return source;
}
private DevInf parseDevInfData(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException {
verboseLog("parseDevInfData");
DevInf devInf = null;
// Depending on the WBXML generator, the devinf can be contained in an
// OPAQUE section or can be just regular tokens. Here we handle both
// cases. If it is an opaque we read it completely and then parse its
// content.
parser.nextToken();
// Skip things we are not interested at
while(parser.getEventType() == parser.IGNORABLE_WHITESPACE ||
parser.getEventType() == parser.ENTITY_REF)
{
parser.nextToken();
}
if (parser.getEventType() == WbxmlParser.WAP_EXTENSION) {
// The whole devinf is embedded in a single TEXT string, but
// it can be encoded as XML or WBXML. We use the first byte to
// discriminate the two cases.
byte binData[] = parseBinaryData(parser);
XmlPullParser devInfParser;
if (binData[0] == 2 || binData[0] == 3) {
// DevInf is in WBXML
devInfParser = com.funambol.org.kxml2.wap.syncml.SyncML.createDevInfParser();
} else {
// DevInf is in XML
devInfParser = new KXmlParser();
}
ByteArrayInputStream is = new ByteArrayInputStream(binData);
devInfParser.setInput(is, "UTF-8");
// Start parsing the DevInf
nextSkipSpaces(devInfParser);
require(devInfParser, parser.START_TAG, null, SyncML.TAG_DEVINF);
devInf = parseDevInf(devInfParser);
} else if (wbxml && parser.getEventType() == parser.TEXT) {
// The whole devinf is embedded in a single TEXT string
XmlPullParser devInfParser = new KXmlParser();
byte binData[] = null;
try {
binData = parser.getText().getBytes("UTF-8");
} catch (Exception e) {
Log.error(TAG_LOG, "Cannot convert dev inf to UTF-8 encoding", e);
throw new SyncMLParserException("Cannot convert dev inf to UTF-8");
}
ByteArrayInputStream is = new ByteArrayInputStream(binData);
devInfParser.setInput(is, "UTF-8");
// Start parsing the DevInf
nextSkipSpaces(devInfParser);
require(devInfParser, parser.START_TAG, null, SyncML.TAG_DEVINF);
devInf = parseDevInf(devInfParser);
// Move ahead
nextSkipSpaces(parser);
} else {
if (parser.getEventType() == parser.TEXT) {
nextSkipSpaces(parser);
}
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_DEVINF.equals(tagName)) {
devInf = parseDevInf(parser);
} else {
String msg = "Error parsing dev inf data tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
}
require(parser, parser.END_TAG, null, SyncML.TAG_DATA);
return devInf;
}
private void parseData(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException {
// We skip everything until we find the DATA closure tag
// TODO: we shall return the data somehow
skipUnknownToken(parser, SyncML.TAG_DATA);
}
/**
* Parse a Device Info section.
* @param parser is the parser
*/
private DevInf parseDevInf(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
// In general the item parsing depends on its type
verboseLog("parseDevInf");
DevInf devInf = new DevInf();
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_VERDTD.equals(tagName)) {
parseVerDTD(parser, devInf);
} else if (SyncML.TAG_DEVINFMAN.equals(tagName)) {
String man = parseSimpleStringTag(parser, SyncML.TAG_DEVINFMAN);
devInf.setMan(man);
} else if (SyncML.TAG_DEVINFMOD.equals(tagName)) {
String mod = parseSimpleStringTag(parser, SyncML.TAG_DEVINFMOD);
devInf.setMod(mod);
} else if (SyncML.TAG_DEVINFOEM.equals(tagName)) {
String oem = parseSimpleStringTag(parser, SyncML.TAG_DEVINFOEM);
devInf.setOEM(oem);
} else if (SyncML.TAG_DEVINFFWV.equals(tagName)) {
String fwv = parseSimpleStringTag(parser, SyncML.TAG_DEVINFFWV);
devInf.setFwV(fwv);
} else if (SyncML.TAG_DEVINFSWV.equals(tagName)) {
String swv = parseSimpleStringTag(parser, SyncML.TAG_DEVINFSWV);
devInf.setSwV(swv);
} else if (SyncML.TAG_DEVINFHWV.equals(tagName)) {
String hwv = parseSimpleStringTag(parser, SyncML.TAG_DEVINFHWV);
devInf.setHwV(hwv);
} else if (SyncML.TAG_DEVINFDEVID.equals(tagName)) {
String devId = parseSimpleStringTag(parser, SyncML.TAG_DEVINFDEVID);
devInf.setDevID(devId);
} else if (SyncML.TAG_DEVINFDEVTYP.equals(tagName)) {
String devTyp = parseSimpleStringTag(parser, SyncML.TAG_DEVINFDEVTYP);
devInf.setDevTyp(devTyp);
} else if (SyncML.TAG_DEVINFUTC.equals(tagName)) {
parseDevInfUtc(parser, devInf);
} else if (SyncML.TAG_DEVINFLO.equals(tagName)) {
parseDevInfLo(parser, devInf);
} else if (SyncML.TAG_DEVINFNC.equals(tagName)) {
parseDevInfNc(parser, devInf);
} else if (SyncML.TAG_DEVINFDATASTORE.equals(tagName)) {
DataStore ds = parseDevInfDataStore(parser, devInf);
devInf.addDataStore(ds);
} else if (SyncML.TAG_EXT.equals(tagName)) {
Vector exts = parseExt(parser);
devInf.addExts(exts);
} else {
String msg = "Error parsing ITEM tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_DEVINF);
return devInf;
}
private void parseVerDTD(XmlPullParser parser, DevInf devInf)
throws XmlPullParserException,
IOException,
SyncMLParserException
{
String verDtd = parseSimpleStringTag(parser, SyncML.TAG_VERDTD);
VerDTD ver = new VerDTD(verDtd);
devInf.setVerDTD(ver);
}
private void parseDevInfUtc(XmlPullParser parser, DevInf devInf)
throws XmlPullParserException,
IOException, SyncMLParserException {
devInf.setUTC(true);
nextSkipSpaces(parser);
require(parser, parser.END_TAG, null, SyncML.TAG_DEVINFUTC);
}
private void parseDevInfLo(XmlPullParser parser, DevInf devInf)
throws XmlPullParserException,
IOException, SyncMLParserException
{
verboseLog("parseDevInfLo");
devInf.setSupportLargeObjs(true);
nextSkipSpaces(parser);
require(parser, parser.END_TAG, null, SyncML.TAG_DEVINFLO);
}
private void parseDevInfNc(XmlPullParser parser, DevInf devInf)
throws XmlPullParserException,
IOException, SyncMLParserException
{
verboseLog("parseDevInfNc");
devInf.setSupportNumberOfChanges(true);
nextSkipSpaces(parser);
require(parser, parser.END_TAG, null, SyncML.TAG_DEVINFNC);
}
private DataStore parseDevInfDataStore(XmlPullParser parser, DevInf devInf)
throws SyncMLParserException,
XmlPullParserException,
IOException
{
verboseLog("parseDevInfDataStore");
DataStore ds = new DataStore();
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_SOURCEREF.equals(tagName)) {
SourceRef sourceRef = parseSourceRef(parser);
ds.setSourceRef(sourceRef);
} else if (SyncML.TAG_DISPLAYNAME.equals(tagName)) {
String displayName = parseSimpleStringTag(parser, SyncML.TAG_DISPLAYNAME);
ds.setDisplayName(displayName);
} else if (SyncML.TAG_MAXGUIDSIZE.equals(tagName)) {
long size = parseSimpleLongTag(parser, SyncML.TAG_MAXGUIDSIZE);
ds.setMaxGUIDSize(size);
} else if (SyncML.TAG_RX.equals(tagName)) {
parseRxs(parser, ds);
} else if (SyncML.TAG_RXPREF.equals(tagName)) {
parseRxPref(parser, ds);
} else if (SyncML.TAG_TX.equals(tagName)) {
parseTxs(parser, ds);
} else if (SyncML.TAG_TXPREF.equals(tagName)) {
parseTxPref(parser, ds);
} else if (SyncML.TAG_SYNCCAP.equals(tagName)) {
SyncCap cap = parseSyncCap(parser);
ds.setSyncCap(cap);
} else if (SyncML.TAG_CTCAP.equals(tagName)) {
CTCap cap = parseCTCap(parser);
ds.addCTCap(cap);
} else if (SyncML.TAG_DSMEM.equals(tagName)) {
DSMem dsMem = parseDSMem(parser);
ds.setDSMem(dsMem);
} else if (SyncML.TAG_DATASTOREHS.equals(tagName)) {
parseDevInfHs(parser, devInf);
} else {
String msg = "Error parsing DATA STORE tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_DEVINFDATASTORE);
return ds;
}
private SourceRef parseSourceRef(XmlPullParser parser) throws SyncMLParserException,
XmlPullParserException,
IOException
{
verboseLog("parseSourceRef");
String name = parseSimpleStringTag(parser, SyncML.TAG_SOURCEREF);
SourceRef sr = SourceRef.newInstance();
sr.setValue(name);
return sr;
}
private TargetRef parseTargetRef(XmlPullParser parser) throws SyncMLParserException,
XmlPullParserException,
IOException
{
verboseLog("parseTargetRef");
String name = parseSimpleStringTag(parser, SyncML.TAG_TARGETREF);
TargetRef tr = TargetRef.newInstance();
tr.setValue(name);
return tr;
}
private void parseRxs(XmlPullParser parser, DataStore ds)
throws SyncMLParserException,
XmlPullParserException,
IOException
{
verboseLog("parseRxs");
CTInfo ctInfo = parseCTInfo(parser);
ds.addRxs(ctInfo);
require(parser, parser.END_TAG, null, SyncML.TAG_RX);
}
private void parseRxPref(XmlPullParser parser, DataStore ds)
throws SyncMLParserException,
XmlPullParserException,
IOException
{
verboseLog("parseRxPref");
CTInfo ctInfo = parseCTInfo(parser);
ds.setRxPref(ctInfo);
require(parser, parser.END_TAG, null, SyncML.TAG_RXPREF);
}
private void parseTxs(XmlPullParser parser, DataStore ds)
throws SyncMLParserException,
XmlPullParserException,
IOException
{
verboseLog("parseTxs");
CTInfo ctInfo = parseCTInfo(parser);
ds.addTxs(ctInfo);
require(parser, parser.END_TAG, null, SyncML.TAG_TX);
}
private void parseTxPref(XmlPullParser parser, DataStore ds)
throws SyncMLParserException,
XmlPullParserException,
IOException
{
verboseLog("parseTxPref");
CTInfo ctInfo = parseCTInfo(parser);
ds.setTxPref(ctInfo);
require(parser, parser.END_TAG, null, SyncML.TAG_TXPREF);
}
private CTInfo parseCTInfo(XmlPullParser parser) throws SyncMLParserException,
XmlPullParserException,
IOException
{
verboseLog("parseCTInfo");
CTInfo ctInfo = new CTInfo();
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_CTTYPE.equals(tagName)) {
String type = parseSimpleStringTag(parser, SyncML.TAG_CTTYPE);
ctInfo.setCTType(type);
} else if (SyncML.TAG_VERCT.equals(tagName)) {
String ver = parseSimpleStringTag(parser, SyncML.TAG_VERCT);
ctInfo.setVerCT(ver);
} else {
String msg = "Error parsing CTINFO tag. Skipping Unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
return ctInfo;
}
private DSMem parseDSMem(XmlPullParser parser) throws SyncMLParserException,
XmlPullParserException,
IOException {
DSMem dsMem = new DSMem();
verboseLog("parseDSMem");
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_SHAREDMEM.equals(tagName)) {
parseSimpleStringTag(parser, SyncML.TAG_SHAREDMEM);
dsMem.setSharedMem(true);
} else if (SyncML.TAG_MAXMEM.equals(tagName)) {
long maxMem = parseSimpleLongTag(parser, SyncML.TAG_MAXMEM);
dsMem.setMaxMem(maxMem);
} else if (SyncML.TAG_MAXID.equals(tagName)) {
long maxId = parseSimpleLongTag(parser, SyncML.TAG_MAXID);
dsMem.setMaxID(maxId);
} else {
String msg = "Error parsing DSMEM tag. Skipping Unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_DSMEM);
return dsMem;
}
private SyncCap parseSyncCap(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseSyncCap");
SyncCap syncCap = new SyncCap();
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_SYNCTYPE.equals(tagName)) {
SyncType type = parseSyncType(parser);
syncCap.addSyncType(type);
} else {
String msg = "Error parsing DATA STORE tag. Unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_SYNCCAP);
return syncCap;
}
private CTCap parseCTCap(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseCTCap");
CTCap ctCap = new CTCap();
CTInfo ctInfo = new CTInfo();
nextSkipSpaces(parser);
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_CTTYPE.equals(tagName)) {
String type = parseSimpleStringTag(parser, SyncML.TAG_CTTYPE);
ctInfo.setCTType(type);
} else if (SyncML.TAG_VERCT.equals(tagName)) {
String ver = parseSimpleStringTag(parser, SyncML.TAG_VERCT);
ctInfo.setVerCT(ver);
} else if (SyncML.TAG_PROPERTY.equals(tagName)) {
Property property = parseProperty(parser);
ctCap.addProperty(property);
} else {
String msg = "Error parsing CTINFO tag. Skipping Unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
ctCap.setCTInfo(ctInfo);
require(parser, parser.END_TAG, null, SyncML.TAG_CTCAP);
return ctCap;
}
private void parseDevInfHs(XmlPullParser parser, DevInf devInf)
throws XmlPullParserException,
IOException {
verboseLog("parseDevInfHs");
devInf.setSupportHierarchicalSync(true);
parser.next();
require(parser, parser.END_TAG, null, SyncML.TAG_DATASTOREHS);
}
private Property parseProperty(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseProperty");
nextSkipSpaces(parser);
Property property = new Property();
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_PROPNAME.equals(tagName)) {
String name = parseSimpleStringTag(parser, SyncML.TAG_PROPNAME);
property.setPropName(name);
} else if (SyncML.TAG_MAXSIZE.equals(tagName)) {
long maxSize = parseSimpleLongTag(parser, SyncML.TAG_MAXSIZE);
} else if (SyncML.TAG_VALENUM.equals(tagName)) {
String val = parseSimpleStringTag(parser, SyncML.TAG_VALENUM);
property.addValEnum(val);
} else if (SyncML.TAG_MAXOCCUR.equals(tagName)) {
long maxOccur = parseSimpleLongTag(parser, SyncML.TAG_MAXOCCUR);
property.setMaxOccur((int)maxOccur);
} else if (SyncML.TAG_PROPPARAM.equals(tagName)) {
PropParam param = parsePropertyParam(parser);
property.addPropParam(param);
} else if (SyncML.TAG_DATATYPE.equals(tagName)) {
String val = parseSimpleStringTag(parser, SyncML.TAG_DATATYPE);
property.setDataType(val);
} else if (SyncML.TAG_DISPLAYNAME.equals(tagName)) {
String val = parseSimpleStringTag(parser, SyncML.TAG_DISPLAYNAME);
property.setDisplayName(val);
} else {
String msg = "Error parsing PROPERTY tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_PROPERTY);
return property;
}
private PropParam parsePropertyParam(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parsePropertyParam");
nextSkipSpaces(parser);
PropParam propParam = new PropParam();
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_PARAMNAME.equals(tagName)) {
String name = parseSimpleStringTag(parser, SyncML.TAG_PARAMNAME);
propParam.setParamName(name);
} else if (SyncML.TAG_DISPLAYNAME.equals(tagName)) {
String displayName = parseSimpleStringTag(parser, SyncML.TAG_DISPLAYNAME);
propParam.setDisplayName(displayName);
} else if (SyncML.TAG_VALENUM.equals(tagName)) {
String val = parseSimpleStringTag(parser, SyncML.TAG_VALENUM);
propParam.addValEnum(val);
} else if (SyncML.TAG_DATATYPE.equals(tagName)) {
String dataType = parseSimpleStringTag(parser, SyncML.TAG_DATATYPE);
propParam.setDataType(dataType);
} else {
String msg = "Error parsing EXT tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_PROPPARAM);
return propParam;
}
private SyncType parseSyncType(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseSyncType");
long type = parseSimpleLongTag(parser, SyncML.TAG_SYNCTYPE);
SyncType syncType = new SyncType((int)type);
return syncType;
}
private String parseSimpleStringTag(XmlPullParser parser, String tag) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseSimpleStringTag");
String value = "";
parser.next();
if (parser.getEventType() == parser.TEXT) {
value = parser.getText();
// We expect text plain data. Since this text is not contained
// in CDATA section, we must unescape it
value = XmlUtil.unescapeXml(value);
parser.next();
} else if (parser.getEventType() == parser.CDSECT) {
value = parser.getText();
parser.next();
} else if (parser.getEventType() == WbxmlParser.WAP_EXTENSION) {
verboseLog("string value in WAP EXTENSION");
// We support the OPAQUE as binary data
if (parser instanceof WbxmlParser) {
WbxmlParser wbxmlParser = (WbxmlParser)parser;
int wapId = wbxmlParser.getWapCode();
if (wapId == Wbxml.OPAQUE) {
byte binData[] = (byte[])wbxmlParser.getWapExtensionData();
// this cannot be a real binary value!
value = new String(binData);
verboseLog("binary value is:" + value);
} else {
verboseLog("unknown wap extension: " + wapId);
throw new SyncMLParserException("Unknown wapId " + wapId);
}
}
}
require(parser, parser.END_TAG, null, tag);
return value;
}
private long parseSimpleLongTag(XmlPullParser parser, String tag) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseSimpleLongTag");
String value = parseSimpleStringTag(parser, tag);
try {
long l = Long.parseLong(value);
return l;
} catch (Exception e) {
String msg = "Error while parsing long " + e.toString();
SyncMLParserException pe = new SyncMLParserException(msg);
throw pe;
}
}
private Vector parseExt(XmlPullParser parser) throws XmlPullParserException,
IOException,
SyncMLParserException
{
verboseLog("parseExt");
Vector exts = new Vector();
nextSkipSpaces(parser);
Ext ext = null;
while (parser.getEventType() == parser.START_TAG) {
String tagName = parser.getName();
if (SyncML.TAG_XNAM.equals(tagName)) {
String name = parseSimpleStringTag(parser, SyncML.TAG_XNAM);
ext = new Ext();
ext.setXNam(name);
exts.addElement(ext);
} else if (SyncML.TAG_XVAL.equals(tagName)) {
String value = parseSimpleStringTag(parser, SyncML.TAG_XVAL);
if (ext == null) {
String msg = "Error parsing EXT tag. Found value without name. Skipping it"
+ tagName;
Log.error(TAG_LOG, msg);
} else {
ext.addXVal(value);
}
} else {
String msg = "Error parsing EXT tag. Skipping unexpected token: " + tagName;
Log.error(TAG_LOG, msg);
skipUnknownToken(parser, tagName);
}
nextSkipSpaces(parser);
}
require(parser, parser.END_TAG, null, SyncML.TAG_EXT);
return exts;
}
private void require(XmlPullParser parser, int type, String namespace,
String name) throws XmlPullParserException
{
if (type != parser.getEventType()
|| (namespace != null && !namespace.equals(parser.getNamespace()))
|| (name != null && !name.equals(parser.getName())))
{
StringBuffer desc = new StringBuffer();
desc.append("Expected ").append(parser.TYPES[ type ]).append(parser.getPositionDescription())
.append(" -- Found ").append(parser.TYPES[parser.getEventType()]);
throw new XmlPullParserException(desc.toString());
}
}
private void nextSkipSpaces(XmlPullParser parser) throws SyncMLParserException,
XmlPullParserException,
IOException {
int eventType = parser.next();
if (eventType == parser.TEXT) {
if (!parser.isWhitespace()) {
String t = parser.getText();
if (t.length() > 0) {
Log.error(TAG_LOG, "Unexpected text: " + t);
throw new SyncMLParserException("Unexpected text: " + t);
}
}
parser.next();
}
}
private void skipUnknownToken(XmlPullParser parser, String tagName)
throws SyncMLParserException,
XmlPullParserException,
IOException
{
/*
// Skip this subtree
parser.skipSubTree();
// Now we are positioned on the end tag
require(parser, parser.END_TAG, null, tagName);
parser.next();
*/
do {
parser.next();
} while (parser.getEventType() != parser.END_TAG || !tagName.equals(parser.getName()));
}
private void verboseLog(String msg) {
if (verbose) {
if (Log.isLoggable(Log.TRACE)) {
Log.trace(TAG_LOG, msg);
}
}
}
}