summaryrefslogtreecommitdiff
path: root/ports/SkXMLPullParser_expat.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ports/SkXMLPullParser_expat.cpp')
-rw-r--r--ports/SkXMLPullParser_expat.cpp213
1 files changed, 213 insertions, 0 deletions
diff --git a/ports/SkXMLPullParser_expat.cpp b/ports/SkXMLPullParser_expat.cpp
new file mode 100644
index 00000000..44a3c7f8
--- /dev/null
+++ b/ports/SkXMLPullParser_expat.cpp
@@ -0,0 +1,213 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkXMLParser.h"
+#include "SkChunkAlloc.h"
+#include "SkString.h"
+#include "SkStream.h"
+
+#include "expat.h"
+
+static inline char* dupstr(SkChunkAlloc& chunk, const char src[], size_t len)
+{
+ SkASSERT(src);
+ char* dst = (char*)chunk.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
+
+ memcpy(dst, src, len);
+ dst[len] = 0;
+ return dst;
+}
+
+static inline int count_pairs(const char** p)
+{
+ const char** start = p;
+ while (*p)
+ {
+ SkASSERT(p[1] != NULL);
+ p += 2;
+ }
+ return (p - start) >> 1;
+}
+
+struct Data {
+ Data() : fAlloc(2048), fState(NORMAL) {}
+
+ XML_Parser fParser;
+ SkXMLPullParser::Curr* fCurr;
+ SkChunkAlloc fAlloc;
+
+ enum State {
+ NORMAL,
+ MISSED_START_TAG,
+ RETURN_END_TAG
+ };
+ State fState;
+ const char* fEndTag; // if state is RETURN_END_TAG
+};
+
+static void XMLCALL start_proc(void *data, const char *el, const char **attr)
+{
+ Data* p = (Data*)data;
+ SkXMLPullParser::Curr* c = p->fCurr;
+ SkChunkAlloc& alloc = p->fAlloc;
+
+ c->fName = dupstr(alloc, el, strlen(el));
+
+ int n = count_pairs(attr);
+ SkXMLPullParser::AttrInfo* info = (SkXMLPullParser::AttrInfo*)alloc.alloc(n * sizeof(SkXMLPullParser::AttrInfo),
+ SkChunkAlloc::kThrow_AllocFailType);
+ c->fAttrInfoCount = n;
+ c->fAttrInfos = info;
+
+ for (int i = 0; i < n; i++)
+ {
+ info[i].fName = dupstr(alloc, attr[0], strlen(attr[0]));
+ info[i].fValue = dupstr(alloc, attr[1], strlen(attr[1]));
+ attr += 2;
+ }
+
+ c->fEventType = SkXMLPullParser::START_TAG;
+ XML_StopParser(p->fParser, true);
+}
+
+static void XMLCALL end_proc(void *data, const char *el)
+{
+ Data* p = (Data*)data;
+ SkXMLPullParser::Curr* c = p->fCurr;
+
+ if (c->fEventType == SkXMLPullParser::START_TAG)
+ {
+ /* if we get here, we were called with a start_tag immediately
+ followed by this end_tag. The caller will only see the end_tag,
+ so we set a flag to notify them of the missed start_tag
+ */
+ p->fState = Data::MISSED_START_TAG;
+
+ SkASSERT(c->fName != NULL);
+ SkASSERT(strcmp(c->fName, el) == 0);
+ }
+ else
+ c->fName = dupstr(p->fAlloc, el, strlen(el));
+
+ c->fEventType = SkXMLPullParser::END_TAG;
+ XML_StopParser(p->fParser, true);
+}
+
+#include <ctype.h>
+
+static bool isws(const char s[])
+{
+ for (; *s; s++)
+ if (!isspace(*s))
+ return false;
+ return true;
+}
+
+static void XMLCALL text_proc(void* data, const char* text, int len)
+{
+ Data* p = (Data*)data;
+ SkXMLPullParser::Curr* c = p->fCurr;
+
+ c->fName = dupstr(p->fAlloc, text, len);
+ c->fIsWhitespace = isws(c->fName);
+
+ c->fEventType = SkXMLPullParser::TEXT;
+ XML_StopParser(p->fParser, true);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+struct SkXMLPullParser::Impl {
+ Data fData;
+ void* fBuffer;
+ size_t fBufferLen;
+};
+
+static void reportError(XML_Parser parser)
+{
+ XML_Error code = XML_GetErrorCode(parser);
+ int lineNumber = XML_GetCurrentLineNumber(parser);
+ const char* msg = XML_ErrorString(code);
+
+ printf("-------- XML error [%d] on line %d, %s\n", code, lineNumber, msg);
+}
+
+bool SkXMLPullParser::onInit()
+{
+ fImpl = new Impl;
+
+ XML_Parser p = XML_ParserCreate(NULL);
+ SkASSERT(p);
+
+ fImpl->fData.fParser = p;
+ fImpl->fData.fCurr = &fCurr;
+
+ XML_SetElementHandler(p, start_proc, end_proc);
+ XML_SetCharacterDataHandler(p, text_proc);
+ XML_SetUserData(p, &fImpl->fData);
+
+ size_t len = fStream->getLength();
+ fImpl->fBufferLen = len;
+ fImpl->fBuffer = sk_malloc_throw(len);
+ fStream->rewind();
+ size_t len2 = fStream->read(fImpl->fBuffer, len);
+ return len2 == len;
+}
+
+void SkXMLPullParser::onExit()
+{
+ sk_free(fImpl->fBuffer);
+ XML_ParserFree(fImpl->fData.fParser);
+ delete fImpl;
+ fImpl = NULL;
+}
+
+SkXMLPullParser::EventType SkXMLPullParser::onNextToken()
+{
+ if (Data::RETURN_END_TAG == fImpl->fData.fState)
+ {
+ fImpl->fData.fState = Data::NORMAL;
+ fCurr.fName = fImpl->fData.fEndTag; // restore name from (below) save
+ return SkXMLPullParser::END_TAG;
+ }
+
+ fImpl->fData.fAlloc.reset();
+
+ XML_Parser p = fImpl->fData.fParser;
+ XML_Status status;
+
+ status = XML_ResumeParser(p);
+
+CHECK_STATUS:
+ switch (status) {
+ case XML_STATUS_OK:
+ return SkXMLPullParser::END_DOCUMENT;
+
+ case XML_STATUS_ERROR:
+ if (XML_GetErrorCode(p) != XML_ERROR_NOT_SUSPENDED)
+ {
+ reportError(p);
+ return SkXMLPullParser::ERROR;
+ }
+ status = XML_Parse(p, (const char*)fImpl->fBuffer, fImpl->fBufferLen, true);
+ goto CHECK_STATUS;
+
+ case XML_STATUS_SUSPENDED:
+ if (Data::MISSED_START_TAG == fImpl->fData.fState)
+ {
+ // return a start_tag, and clear the flag so we return end_tag next
+ SkASSERT(SkXMLPullParser::END_TAG == fCurr.fEventType);
+ fImpl->fData.fState = Data::RETURN_END_TAG;
+ fImpl->fData.fEndTag = fCurr.fName; // save this pointer
+ return SkXMLPullParser::START_TAG;
+ }
+ break;
+ }
+ return fCurr.fEventType;
+}