+++ /dev/null
-/*--------------------------------------------------------------------------\r
- * Copyright 2009 Taro L. Saito\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- *--------------------------------------------------------------------------*/\r
-//--------------------------------------\r
-// XerialJ\r
-//\r
-// XMLStreamWalker.java\r
-// Since: Mar 30, 2009 5:27:15 PM\r
-//\r
-// $URL: http://www.xerial.org/svn/project/XerialJ/trunk/xerial-core/src/main/java/org/xerial/util/xml/XMLStreamReader.java $\r
-// $Author: leo $\r
-//--------------------------------------\r
-package org.xerial.util.xml;\r
-\r
-import static org.xmlpull.v1.XmlPullParser.*;\r
-\r
-import java.io.IOException;\r
-import java.io.Reader;\r
-\r
-import org.xerial.core.XerialErrorCode;\r
-import org.xerial.core.XerialException;\r
-import org.xerial.util.ArrayDeque;\r
-import org.xerial.util.Deque;\r
-import org.xerial.util.tree.TreeEvent;\r
-import org.xerial.util.tree.TreeStreamReader;\r
-import org.xerial.util.tree.TreeEvent.EventType;\r
-import org.xerial.util.xml.impl.TreeEventQueue;\r
-import org.xerial.util.xml.pullparser.PullParserUtil;\r
-import org.xmlpull.v1.XmlPullParser;\r
-import org.xmlpull.v1.XmlPullParserException;\r
-\r
-/**\r
- * {@link TreeStreamReader} of XML data\r
- * \r
- * @author leo\r
- * \r
- */\r
-public class XMLStreamReader implements TreeStreamReader\r
-{\r
- private final XmlPullParser pullParser;\r
- private final Deque<StringBuilder> textStack = new ArrayDeque<StringBuilder>();\r
- private final static StringBuilder EMPTY_STRING = new StringBuilder(0);\r
- private int TEXT_BUFFER_MAX = 8192;\r
-\r
- private int parseState = START_DOCUMENT;\r
- private boolean convertValueAttribute = false;\r
-\r
- private final TreeEventQueue eventQueue = new TreeEventQueue();\r
-\r
- public XMLStreamReader(Reader reader)\r
- {\r
- if (reader == null)\r
- throw new IllegalArgumentException("XML reader is null");\r
-\r
- pullParser = PullParserUtil.newParser(reader);\r
-\r
- }\r
-\r
- public void useValueAttributeAsNodeValue(boolean enable)\r
- {\r
- this.convertValueAttribute = enable;\r
- }\r
-\r
- public TreeEvent peekNext() throws XerialException\r
- {\r
- if (!eventQueue.isEmpty())\r
- {\r
- return eventQueue.peekFirst();\r
- }\r
-\r
- if (parseState == END_DOCUMENT)\r
- return null;\r
-\r
- readNext();\r
-\r
- return peekNext();\r
- }\r
-\r
- public TreeEvent next() throws XerialException\r
- {\r
- if (!eventQueue.isEmpty())\r
- {\r
- return eventQueue.pop();\r
- }\r
-\r
- if (parseState == END_DOCUMENT)\r
- return null;\r
-\r
- readNext();\r
-\r
- return next();\r
- }\r
-\r
- public void readNext() throws XerialException\r
- {\r
- if (parseState == END_DOCUMENT)\r
- return;\r
-\r
- try\r
- {\r
- parseState = pullParser.next();\r
-\r
- switch (parseState)\r
- {\r
- case START_TAG:\r
- {\r
- textStack.addLast(EMPTY_STRING);\r
- String tagName = pullParser.getName();\r
- String immediateNodeValue = null;\r
-\r
- Deque<TreeEvent> startEventQueue = new ArrayDeque<TreeEvent>(pullParser.getAttributeCount() + 1);\r
- // read attributes\r
- for (int i = 0; i < pullParser.getAttributeCount(); i++)\r
- {\r
- String attributeName = pullParser.getAttributeName(i);\r
- String attributeValue = pullParser.getAttributeValue(i);\r
-\r
- // assign the value attribute as a node value of the start tag \r
- if (convertValueAttribute && attributeName.equals("value"))\r
- {\r
- immediateNodeValue = attributeValue;\r
- continue;\r
- }\r
-\r
- startEventQueue.addLast(TreeEvent.newVisitEvent(attributeName, attributeValue));\r
- startEventQueue.addLast(TreeEvent.newLeaveEvent(attributeName));\r
- }\r
-\r
- // push a new start tag event to the front of the queue\r
- startEventQueue.addFirst(TreeEvent.newVisitEvent(tagName, immediateNodeValue));\r
- eventQueue.push(startEventQueue);\r
-\r
- // pre-fetch the next event\r
- readNext();\r
- }\r
- break;\r
- case END_TAG:\r
- {\r
- if (textStack.getLast() == EMPTY_STRING)\r
- {\r
- eventQueue.push(TreeEvent.newLeaveEvent(pullParser.getName()));\r
- }\r
- else\r
- {\r
- StringBuilder textBuffer = textStack.getLast();\r
- if (!eventQueue.isEmpty() && eventQueue.peekLast().event == EventType.VISIT)\r
- {\r
- // attach the text value to the the previous visit event\r
- eventQueue.replaceLast(TreeEvent.newVisitEvent(pullParser.getName(), sanitize(textBuffer\r
- .toString())));\r
- }\r
- else\r
- reportTextEvent(textBuffer);\r
-\r
- eventQueue.push(TreeEvent.newLeaveEvent(pullParser.getName()));\r
- }\r
- textStack.removeLast();\r
- }\r
- break;\r
- case TEXT:\r
- {\r
- String textData = pullParser.getText();\r
- StringBuilder textBuffer = textStack.getLast();\r
-\r
- if (textData.length() <= 0)\r
- break;\r
-\r
- if (textBuffer == EMPTY_STRING)\r
- {\r
- textBuffer = replaceLastTextBuffer();\r
- }\r
- else if (textBuffer.length() + textData.length() > TEXT_BUFFER_MAX)\r
- {\r
- // add the previous text data to the event queue\r
- reportTextEvent(textBuffer);\r
-\r
- // replace the text buffer\r
- textBuffer = replaceLastTextBuffer();\r
- }\r
- textBuffer.append(textData);\r
-\r
- boolean needPrefetch = eventQueue.isEmpty() ? false : eventQueue.peekLast().event == EventType.VISIT;\r
- if (needPrefetch)\r
- readNext();\r
- }\r
- break;\r
- default:\r
- // do nothing\r
- break;\r
- }\r
-\r
- }\r
- catch (XmlPullParserException e)\r
- {\r
- throw new XerialException(XMLErrorCode.PARSE_ERROR, e);\r
- }\r
- catch (IOException e)\r
- {\r
- throw new XerialException(XerialErrorCode.IO_EXCEPTION, e);\r
- }\r
-\r
- }\r
-\r
- private StringBuilder replaceLastTextBuffer()\r
- {\r
- textStack.removeLast();\r
- StringBuilder textBuffer = new StringBuilder();\r
- textStack.addLast(textBuffer);\r
- return textBuffer;\r
- }\r
-\r
- private String sanitize(String s)\r
- {\r
- return s.trim();\r
- }\r
-\r
- private void reportTextEvent(StringBuilder buffer)\r
- {\r
- reportTextEvent(buffer.toString());\r
- }\r
-\r
- private void reportTextEvent(String textData)\r
- {\r
- textData = sanitize(textData);\r
-\r
- if (textData.length() > 0)\r
- eventQueue.push(TreeEvent.newTextEvent(eventQueue.getContextNodeName(), textData));\r
- }\r
-\r
-}\r