| /*************************************************************************** |
| * |
| * 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; |
| } |