blob: 86b74b333bb5549b7c49c632c81da7d41161e308 [file] [log] [blame]
/***************************************************************************
*
* Copyright 2003-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
/*
* @author Susantha Kumara (susantha@opensource.lk)
* @author Dasarath Weerathunga
*/
#include "XmlPullParser.hpp"
void XmlPullParser::relocateTokens(int ioffset)
{
int size= token->count();
for (int ii= 0; ii < size; ii++)
(*token)[ii].relocate(ioffset);
}
void XmlPullParser::shift()
{
memmove(buffer->get(), buffer->get()+offset, _next-offset);
_next -= offset;
last -= offset;
offset= 0;
}
int XmlPullParser::read()
{
if (_next == buffer->getSize()) {
if (offset > 0) {
relocateTokens(offset);
shift();
}
else {
char *p= buffer->get();
buffer->grow();
relocateTokens(p - buffer->get());
}
}
int c= s->read(buffer->get(), _next, buffer->getSize()-_next);
last += c;
return !c;
}
int XmlPullParser::nextCh(int eof)
{
if (_next > last && read())
if (eof)
return -1;
else
throw new XmlPullParserException();
//Update next
// FIXME:
//printf("%c", buffer->get()[_next]);
return buffer->get()[_next++];
//update next depending on the encoding type
}
char *XmlPullParser::lastCh()
{
return buffer->get()+_next-1;
}
void XmlPullParser::reset()
{
offset= _next;
token->trunc(0);
attr->trunc(0);
if (event == ETag || event == EmptyElemTag) {
DEPTH &l= depth->getLast();
if (l.count) {
NAMESPACE *p= ns->get()+l.first;
for (int ii= l.first; ii < l.total; ii++, p++) {
free(p->prefix);
free(p->uri);
}
ns->trunc(l.first);
}
depth->chip();
// printf("\ndepth=%d\n", depth->count());
}
name= -1;
_ns= -1;
}
void XmlPullParser::startToken()
{
TOKEN &t= token->append();
t.type= TOKEN::Unknown;
t.start= lastCh();
}
void XmlPullParser::endToken(int type)
{
TOKEN &t= token->getLast();
t.type= type;
t.end= lastCh()-1;
}
XmlPullParser::XmlPullParser(InputStream *pStream)
{
this->s= pStream;
state= S_1;
offset= 0;
last= -1;
_next= 0;
buffer= new Buffer<char>(1024);
token= new Array<TOKEN>(64);
ns= new Array<NAMESPACE>(64);
depth= new Array<DEPTH>(16);
attr= new Array<ATTR>(64);
}
XmlPullParser::~XmlPullParser()
{
delete buffer;
delete token;
delete ns;
delete depth;
delete attr;
}
int XmlPullParser::isS(int c)
{
return 0x9 == c || 0xA == c || 0xD == c || 0x20 == c;
}
int XmlPullParser::parseS(int c)
{
while (isS(c))
c= nextCh();
return c;
}
int XmlPullParser::parseEq(int c)
{
if ('=' == parseS(c))
return parseS(nextCh());
else
throw new XmlPullParserException();
}
int XmlPullParser::parseNameToken(int c)
{
startToken();
while (!(isS(c) || '/' == c || '?' == c || '=' == c || '>' == c)) {
if (':' == c) {
endToken(TOKEN::Prefix);
c= nextCh();
startToken();
}
else
c= nextCh();
}
endToken(TOKEN::Name);
return c;
}
int XmlPullParser::parseAttValueToken(int quote)
{
if ('\'' == quote || '"' == quote) {
int c= nextCh();
startToken();
while ('&' != c && '<' != c) {
if (c == quote) {
endToken(TOKEN::AttValue);
return nextCh();
}
c= nextCh();
}
}
throw new XmlPullParserException();
}
int XmlPullParser::parseAttribute(int c)
{
c= parseEq(parseNameToken(c));
c= parseAttValueToken(c);
return parseS(c);
}
void XmlPullParser::parseXMLDecl()
{
if (nextCh() == 'x' &&
nextCh() == 'm' &&
nextCh() == 'l') {
int c= parseAttribute(parseS(nextCh()));
if (c == 'e')
c= parseAttribute(c);
if (c == 's')
c= parseAttribute(c);
if (c == '?' && nextCh() == '>') {
event= XMLDecl;
return;
}
}
throw new XmlPullParserException();
}
void XmlPullParser::parseSTagOrEmptyElemTag(int c)
{
c= parseS(parseNameToken(c));
event= STag;
for (;;) {
if (c == '/') {
event= EmptyElemTag;
if (nextCh() == '>')
return;
else
throw new XmlPullParserException();
}
else
if (c == '>')
return;
else
c= parseAttribute(c);
}
}
void XmlPullParser::parseETag()
{
if (parseS(parseNameToken(nextCh())) == '>')
event= ETag;
else
throw new XmlPullParserException();
}
int XmlPullParser::parseCharData()
{
int c;
event= Content;
startToken();
do {
c= nextCh(-1);
if (-1 == c) {
endToken(TOKEN::CharData);
return 0;
}
} while ('<' != c);
endToken(TOKEN::CharData);
return c;
}
int XmlPullParser::parse()
{
do {
int c= nextCh(-1);
if (-1 == c)
return -1;
switch (state) {
case S_1:
if ('<' == c)
state= S_2;
else
state= S_0;
break;
case S_2:
if ('?' == c)
parseXMLDecl();
else
parseSTagOrEmptyElemTag(c);
state= S_3;
break;
case S_3:
if ('<' == c)
state= S_4;
else {
c= parseCharData();
if ('<' == c) {
state= S_4;
return event;
}
else if ('\0' == c)
state= S_3;
else
state= S_0;
}
break;
case S_4:
if ('/' == c) {
parseETag();
state= S_3;
}
else if ('!' == c)
state= S_0;
else if ('?' == c)
state= S_0;
else {
parseSTagOrEmptyElemTag(c);
state= S_3;
}
}
if (state == S_0)
throw new XmlPullParserException();
} while (state != S_3);
return event;
}
int XmlPullParser::next()
{
reset();
if (parse() != -1) {
if (STag == event || EmptyElemTag == event || ETag == event) {
TOKEN *p= token->get();
int ii= 0;
if (p->type == TOKEN::Prefix) {
_ns= 0xffff;
name= 1;
p += 2;
ii += 2;
}
else {
_ns = -1;
name= 0;
p++;
ii++;
}
if (ETag == event)
return event;
int count= token->count();
while (ii < count) {
if (p->type == TOKEN::Prefix) {
if (!p->compare("xmlns", 5)) {
NAMESPACE &n= ns->append();
p++;
n.prefix= p->toString();
p++;
n.uri= p->toString();
p++;
ii += 3;
}
else {
ATTR &a= attr->append();
a.prefix= ii++;
a.name= ii++;
a.value= ii++;
a.ns= -1;
p += 3;
}
}
else {
if (!p->compare("xmlns", 5)) {
NAMESPACE &n= ns->append();
n.prefix= NULL;
p++;
n.uri= p->toString();
p++;
ii += 2;
}
else {
ATTR &a= attr->append();
a.prefix= -1;
a.name= ii++;
a.value= ii++;
a.ns= -1;
p += 2;
}
}
}
if (depth->count()) {
DEPTH &l= depth->getLast();
DEPTH &m= depth->append();
m.first= l.first+l.count;
m.total= ns->count();
m.count= m.total-l.total;
}
else {
DEPTH &m= depth->append();
m.first= 0;
m.total= ns->count();
m.count= m.total;
}
_ns= resolve(_ns == -1 ? NULL : token->get());
}
else if (XMLDecl == event)
{
//Do not return this event. Process it for internal use only
//Get and return next event.
return next();
}
return event;
}
return -1;
}
int XmlPullParser::getAttributeCount()
{
if (STag != event && EmptyElemTag != event)
throw new XmlPullParserException();
return attr->count();
}
char *XmlPullParser::getAttributePrefix(int ii)
{
if (STag != event && EmptyElemTag != event)
throw new XmlPullParserException();
ii= (*attr)[ii].prefix;
if (ii == -1)
return NULL;
return (*token)[ii].toString();
}
char *XmlPullParser::getAttributeName(int ii)
{
if (STag != event && EmptyElemTag != event)
throw new XmlPullParserException();
ii= (*attr)[ii].name;
if (ii == -1)
return NULL;
return (*token)[ii].toString();
}
char *XmlPullParser::getAttributeValue(int ii)
{
if (STag != event && EmptyElemTag != event)
throw new XmlPullParserException();
ii= (*attr)[ii].value;
if (ii == -1)
return NULL;
return (*token)[ii].toString();
}
char *XmlPullParser::getAttributeNamespaceUri(int ii)
{
if (STag != event && EmptyElemTag != event)
throw new XmlPullParserException();
ATTR &t= (*attr)[ii];
if (t.ns == -1)
t.ns= resolve(t.prefix == -1 ? NULL : token->get()+t.prefix);
return (t.ns == -1) ? NULL : strdup((*ns)[t.ns].uri);
}
int XmlPullParser::resolve(TOKEN *prefix)
{
int ii= ns->count()-1;
NAMESPACE *p= &ns->getLast();
while (ii >= 0) {
if (prefix == NULL) {
if (p->prefix == NULL)
return ii;
}
else
if (p->prefix && !prefix->compare(p->prefix, strlen(p->prefix)))
return ii;
ii--;
p--;
}
//FIXME : Commented out following line because the parser fails when there are
// unqualified elements in xml which is IMO possible in SOAP - Susantha
// 11/11/2004
// throw new XmlPullParserException();
return -1; //not resolved
}
int XmlPullParser::getDepth()
{
return depth->count();
}
int XmlPullParser::getNamespaceCount(int ii)
{
if (ii > getDepth())
throw new XmlPullParserException();
else
return (*depth)[ii-1].total;
}
char *XmlPullParser::getNamespacePrefix(int ii)
{
if (ii > ns->count())
throw new XmlPullParserException();
else {
char *p= (*ns)[ii].prefix;
return p == NULL ? NULL : strdup(p);
}
}
char *XmlPullParser::getNamespaceUri(int ii)
{
if (ii > ns->count())
throw new XmlPullParserException();
else {
char *p= (*ns)[ii].uri;
return p == NULL ? NULL : strdup(p);
}
}
char *XmlPullParser::getName()
{
return (*token)[name].toString();
}
char *XmlPullParser::getCharData()
{
return (*token)[0].toString();
}
char *XmlPullParser::getNamespaceUri()
{
if (_ns == -1)
_ns= resolve(NULL);
else
if (_ns == 0xffff)
_ns= resolve(token->get());
return _ns == -1 ? NULL : strdup((*ns)[_ns].uri);
}
char* XmlPullParser::getNamespace4Prefix(const char *pcPrefix)
{
for (int ix=ns->count()-1; ix >= 0; ix--)
{
if ((*ns)[ix].prefix && (0 == strcmp(pcPrefix, (*ns)[ix].prefix)))
{
return strdup((*ns)[ix].uri);
}
}
return 0;
}
char* XmlPullParser::getPrefix4Namespace(const char *pcNs)
{
for (int ix=ns->count()-1; ix >= 0; ix--)
{
if ((*ns)[ix].uri && (0 == strcmp(pcNs, (*ns)[ix].uri)))
{
return strdup((*ns)[ix].prefix);
}
}
return 0;
}