1/* libs/graphics/ports/SkXMLParser_expat.cpp 2** 3** Copyright 2006, 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 18#include "SkXMLParser.h" 19#include "SkChunkAlloc.h" 20#include "SkString.h" 21#include "SkStream.h" 22 23#include "expat.h" 24 25static inline char* dupstr(SkChunkAlloc& chunk, const char src[], size_t len) 26{ 27 SkASSERT(src); 28 char* dst = (char*)chunk.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType); 29 30 memcpy(dst, src, len); 31 dst[len] = 0; 32 return dst; 33} 34 35static inline int count_pairs(const char** p) 36{ 37 const char** start = p; 38 while (*p) 39 { 40 SkASSERT(p[1] != NULL); 41 p += 2; 42 } 43 return (p - start) >> 1; 44} 45 46struct Data { 47 Data() : fAlloc(2048), fState(NORMAL) {} 48 49 XML_Parser fParser; 50 SkXMLPullParser::Curr* fCurr; 51 SkChunkAlloc fAlloc; 52 53 enum State { 54 NORMAL, 55 MISSED_START_TAG, 56 RETURN_END_TAG 57 }; 58 State fState; 59 const char* fEndTag; // if state is RETURN_END_TAG 60}; 61 62static void XMLCALL start_proc(void *data, const char *el, const char **attr) 63{ 64 Data* p = (Data*)data; 65 SkXMLPullParser::Curr* c = p->fCurr; 66 SkChunkAlloc& alloc = p->fAlloc; 67 68 c->fName = dupstr(alloc, el, strlen(el)); 69 70 int n = count_pairs(attr); 71 SkXMLPullParser::AttrInfo* info = (SkXMLPullParser::AttrInfo*)alloc.alloc(n * sizeof(SkXMLPullParser::AttrInfo), 72 SkChunkAlloc::kThrow_AllocFailType); 73 c->fAttrInfoCount = n; 74 c->fAttrInfos = info; 75 76 for (int i = 0; i < n; i++) 77 { 78 info[i].fName = dupstr(alloc, attr[0], strlen(attr[0])); 79 info[i].fValue = dupstr(alloc, attr[1], strlen(attr[1])); 80 attr += 2; 81 } 82 83 c->fEventType = SkXMLPullParser::START_TAG; 84 XML_StopParser(p->fParser, true); 85} 86 87static void XMLCALL end_proc(void *data, const char *el) 88{ 89 Data* p = (Data*)data; 90 SkXMLPullParser::Curr* c = p->fCurr; 91 92 if (c->fEventType == SkXMLPullParser::START_TAG) 93 { 94 /* if we get here, we were called with a start_tag immediately 95 followed by this end_tag. The caller will only see the end_tag, 96 so we set a flag to notify them of the missed start_tag 97 */ 98 p->fState = Data::MISSED_START_TAG; 99 100 SkASSERT(c->fName != NULL); 101 SkASSERT(strcmp(c->fName, el) == 0); 102 } 103 else 104 c->fName = dupstr(p->fAlloc, el, strlen(el)); 105 106 c->fEventType = SkXMLPullParser::END_TAG; 107 XML_StopParser(p->fParser, true); 108} 109 110#include <ctype.h> 111 112static bool isws(const char s[]) 113{ 114 for (; *s; s++) 115 if (!isspace(*s)) 116 return false; 117 return true; 118} 119 120static void XMLCALL text_proc(void* data, const char* text, int len) 121{ 122 Data* p = (Data*)data; 123 SkXMLPullParser::Curr* c = p->fCurr; 124 125 c->fName = dupstr(p->fAlloc, text, len); 126 c->fIsWhitespace = isws(c->fName); 127 128 c->fEventType = SkXMLPullParser::TEXT; 129 XML_StopParser(p->fParser, true); 130} 131 132////////////////////////////////////////////////////////////////////////// 133 134struct SkXMLPullParser::Impl { 135 Data fData; 136 void* fBuffer; 137 size_t fBufferLen; 138}; 139 140static void reportError(XML_Parser parser) 141{ 142 XML_Error code = XML_GetErrorCode(parser); 143 int lineNumber = XML_GetCurrentLineNumber(parser); 144 const char* msg = XML_ErrorString(code); 145 146 printf("-------- XML error [%d] on line %d, %s\n", code, lineNumber, msg); 147} 148 149bool SkXMLPullParser::onInit() 150{ 151 fImpl = new Impl; 152 153 XML_Parser p = XML_ParserCreate(NULL); 154 SkASSERT(p); 155 156 fImpl->fData.fParser = p; 157 fImpl->fData.fCurr = &fCurr; 158 159 XML_SetElementHandler(p, start_proc, end_proc); 160 XML_SetCharacterDataHandler(p, text_proc); 161 XML_SetUserData(p, &fImpl->fData); 162 163 size_t len = fStream->read(NULL, 0); 164 fImpl->fBufferLen = len; 165 fImpl->fBuffer = sk_malloc_throw(len); 166 fStream->rewind(); 167 size_t len2 = fStream->read(fImpl->fBuffer, len); 168 return len2 == len; 169} 170 171void SkXMLPullParser::onExit() 172{ 173 sk_free(fImpl->fBuffer); 174 XML_ParserFree(fImpl->fData.fParser); 175 delete fImpl; 176 fImpl = NULL; 177} 178 179SkXMLPullParser::EventType SkXMLPullParser::onNextToken() 180{ 181 if (Data::RETURN_END_TAG == fImpl->fData.fState) 182 { 183 fImpl->fData.fState = Data::NORMAL; 184 fCurr.fName = fImpl->fData.fEndTag; // restore name from (below) save 185 return SkXMLPullParser::END_TAG; 186 } 187 188 fImpl->fData.fAlloc.reuse(); 189 190 XML_Parser p = fImpl->fData.fParser; 191 XML_Status status; 192 193 status = XML_ResumeParser(p); 194 195CHECK_STATUS: 196 switch (status) { 197 case XML_STATUS_OK: 198 return SkXMLPullParser::END_DOCUMENT; 199 200 case XML_STATUS_ERROR: 201 if (XML_GetErrorCode(p) != XML_ERROR_NOT_SUSPENDED) 202 { 203 reportError(p); 204 return SkXMLPullParser::ERROR; 205 } 206 status = XML_Parse(p, (const char*)fImpl->fBuffer, fImpl->fBufferLen, true); 207 goto CHECK_STATUS; 208 209 case XML_STATUS_SUSPENDED: 210 if (Data::MISSED_START_TAG == fImpl->fData.fState) 211 { 212 // return a start_tag, and clear the flag so we return end_tag next 213 SkASSERT(SkXMLPullParser::END_TAG == fCurr.fEventType); 214 fImpl->fData.fState = Data::RETURN_END_TAG; 215 fImpl->fData.fEndTag = fCurr.fName; // save this pointer 216 return SkXMLPullParser::START_TAG; 217 } 218 break; 219 } 220 return fCurr.fEventType; 221} 222 223