001: /*
002: * Copyright (C) 2005, 2006 Joe Walnes.
003: * Copyright (C) 2006, 2007 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 24. April 2005 by Joe Walnes
011: */
012: package com.thoughtworks.xstream.io.xml;
013:
014: import java.util.Iterator;
015:
016: import com.thoughtworks.xstream.core.util.FastStack;
017: import com.thoughtworks.xstream.io.AttributeNameIterator;
018: import com.thoughtworks.xstream.io.HierarchicalStreamReader;
019:
020: /**
021: * Base class that contains common functionality across HierarchicalStreamReader implementations
022: * that need to read from a pull parser.
023: *
024: * @author Joe Walnes
025: * @author James Strachan
026: */
027: public abstract class AbstractPullReader extends AbstractXmlReader {
028:
029: protected static final int START_NODE = 1;
030: protected static final int END_NODE = 2;
031: protected static final int TEXT = 3;
032: protected static final int COMMENT = 4;
033: protected static final int OTHER = 0;
034:
035: private final FastStack elementStack = new FastStack(16);
036:
037: private final FastStack lookahead = new FastStack(4);
038: private final FastStack lookback = new FastStack(4);
039: private boolean marked;
040:
041: private static class Event {
042: int type;
043: String value;
044: }
045:
046: /**
047: * @since 1.2
048: */
049: protected AbstractPullReader(XmlFriendlyReplacer replacer) {
050: super (replacer);
051: }
052:
053: /**
054: * Pull the next event from the stream.
055: *
056: * <p>This MUST return {@link #START_NODE}, {@link #END_NODE}, {@link #TEXT}, {@link #COMMENT},
057: * {@link #OTHER} or throw {@link com.thoughtworks.xstream.io.StreamException}.</p>
058: *
059: * <p>The underlying pull parser will most likely return its own event types. These must be
060: * mapped to the appropriate events.</p>
061: */
062: protected abstract int pullNextEvent();
063:
064: /**
065: * Pull the name of the current element from the stream.
066: */
067: protected abstract String pullElementName();
068:
069: /**
070: * Pull the contents of the current text node from the stream.
071: */
072: protected abstract String pullText();
073:
074: public boolean hasMoreChildren() {
075: mark();
076: while (true) {
077: switch (readEvent().type) {
078: case START_NODE:
079: reset();
080: return true;
081: case END_NODE:
082: reset();
083: return false;
084: default:
085: continue;
086: }
087: }
088: }
089:
090: public void moveDown() {
091: int currentDepth = elementStack.size();
092: while (elementStack.size() <= currentDepth) {
093: move();
094: if (elementStack.size() < currentDepth) {
095: throw new RuntimeException(); // sanity check
096: }
097: }
098: }
099:
100: public void moveUp() {
101: int currentDepth = elementStack.size();
102: while (elementStack.size() >= currentDepth) {
103: move();
104: }
105: }
106:
107: private void move() {
108: switch (readEvent().type) {
109: case START_NODE:
110: elementStack.push(pullElementName());
111: break;
112: case END_NODE:
113: elementStack.pop();
114: break;
115: }
116: }
117:
118: private Event readEvent() {
119: if (marked) {
120: if (lookback.hasStuff()) {
121: return (Event) lookahead.push(lookback.pop());
122: } else {
123: return (Event) lookahead.push(readRealEvent());
124: }
125: } else {
126: if (lookback.hasStuff()) {
127: return (Event) lookback.pop();
128: } else {
129: return readRealEvent();
130: }
131: }
132: }
133:
134: private Event readRealEvent() {
135: Event event = new Event();
136: event.type = pullNextEvent();
137: if (event.type == TEXT) {
138: event.value = pullText();
139: } else if (event.type == START_NODE) {
140: event.value = pullElementName();
141: }
142: return event;
143: }
144:
145: public void mark() {
146: marked = true;
147: }
148:
149: public void reset() {
150: while (lookahead.hasStuff()) {
151: lookback.push(lookahead.pop());
152: }
153: marked = false;
154: }
155:
156: public String getValue() {
157: // we should collapse together any text which
158: // contains comments
159:
160: // lets only use a string buffer when we get 2 strings
161: // to avoid copying strings
162: String last = null;
163: StringBuffer buffer = null;
164:
165: mark();
166: Event event = readEvent();
167: while (true) {
168: if (event.type == TEXT) {
169: String text = event.value;
170: if (text != null && text.length() > 0) {
171: if (last == null) {
172: last = text;
173: } else {
174: if (buffer == null) {
175: buffer = new StringBuffer(last);
176: }
177: buffer.append(text);
178: }
179: }
180: } else if (event.type != COMMENT) {
181: break;
182: }
183: event = readEvent();
184: }
185: reset();
186: if (buffer != null) {
187: return buffer.toString();
188: } else {
189: return (last == null) ? "" : last;
190: }
191: }
192:
193: public Iterator getAttributeNames() {
194: return new AttributeNameIterator(this );
195: }
196:
197: public String getNodeName() {
198: return unescapeXmlName((String) elementStack.peek());
199: }
200:
201: public HierarchicalStreamReader underlyingReader() {
202: return this;
203: }
204:
205: }
|