001: /*
002: * Copyright (C) 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 04. June 2006 by Joe Walnes
011: */
012: package com.thoughtworks.xstream.io.binary;
013:
014: import com.thoughtworks.xstream.converters.ErrorWriter;
015: import com.thoughtworks.xstream.io.HierarchicalStreamReader;
016: import com.thoughtworks.xstream.io.StreamException;
017:
018: import java.io.DataInputStream;
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.Map;
024:
025: /**
026: * A HierarchicalStreamReader that reads from a binary stream created by
027: * {@link BinaryStreamWriter}.
028: * <p/>
029: * <p>This produces
030: *
031: * @author Joe Walnes
032: * @see BinaryStreamReader
033: * @since 1.2
034: */
035: public class BinaryStreamReader implements HierarchicalStreamReader {
036:
037: private final DataInputStream in;
038: private final ReaderDepthState depthState = new ReaderDepthState();
039: private final IdRegistry idRegistry = new IdRegistry();
040:
041: private Token pushback;
042: private final Token.Formatter tokenFormatter = new Token.Formatter();
043:
044: public BinaryStreamReader(InputStream inputStream) {
045: in = new DataInputStream(inputStream);
046: moveDown();
047: }
048:
049: public boolean hasMoreChildren() {
050: return depthState.hasMoreChildren();
051: }
052:
053: public String getNodeName() {
054: return depthState.getName();
055: }
056:
057: public String getValue() {
058: return depthState.getValue();
059: }
060:
061: public String getAttribute(String name) {
062: return depthState.getAttribute(name);
063: }
064:
065: public String getAttribute(int index) {
066: return depthState.getAttribute(index);
067: }
068:
069: public int getAttributeCount() {
070: return depthState.getAttributeCount();
071: }
072:
073: public String getAttributeName(int index) {
074: return depthState.getAttributeName(index);
075: }
076:
077: public Iterator getAttributeNames() {
078: return depthState.getAttributeNames();
079: }
080:
081: public void moveDown() {
082: depthState.push();
083: Token firstToken = readToken();
084: switch (firstToken.getType()) {
085: case Token.TYPE_START_NODE:
086: depthState.setName(idRegistry.get(firstToken.getId()));
087: break;
088: default:
089: throw new StreamException("Expected StartNode");
090: }
091: while (true) {
092: Token nextToken = readToken();
093: switch (nextToken.getType()) {
094: case Token.TYPE_ATTRIBUTE:
095: depthState.addAttribute(idRegistry.get(nextToken
096: .getId()), nextToken.getValue());
097: break;
098: case Token.TYPE_VALUE:
099: depthState.setValue(nextToken.getValue());
100: break;
101: case Token.TYPE_END_NODE:
102: depthState.setHasMoreChildren(false);
103: pushBack(nextToken);
104: return;
105: case Token.TYPE_START_NODE:
106: depthState.setHasMoreChildren(true);
107: pushBack(nextToken);
108: return;
109: default:
110: throw new StreamException("Unexpected token "
111: + nextToken);
112: }
113: }
114: }
115:
116: public void moveUp() {
117: depthState.pop();
118: // We're done with this depth. Skip over all tokens until we get to the end.
119: int depth = 0;
120: slurp: while (true) {
121: Token nextToken = readToken();
122: switch (nextToken.getType()) {
123: case Token.TYPE_END_NODE:
124: if (depth == 0) {
125: break slurp;
126: } else {
127: depth--;
128: }
129: break;
130: case Token.TYPE_START_NODE:
131: depth++;
132: break;
133: default:
134: // Ignore other tokens
135: }
136: }
137: // Peek ahead to determine if there are any more kids at this level.
138: Token nextToken = readToken();
139: switch (nextToken.getType()) {
140: case Token.TYPE_END_NODE:
141: depthState.setHasMoreChildren(false);
142: break;
143: case Token.TYPE_START_NODE:
144: depthState.setHasMoreChildren(true);
145: break;
146: default:
147: throw new StreamException("Unexpected token " + nextToken);
148: }
149: pushBack(nextToken);
150: }
151:
152: private Token readToken() {
153: if (pushback == null) {
154: try {
155: Token token = tokenFormatter.read(in);
156: switch (token.getType()) {
157: case Token.TYPE_MAP_ID_TO_VALUE:
158: idRegistry.put(token.getId(), token.getValue());
159: return readToken(); // Next one please.
160: default:
161: return token;
162: }
163: } catch (IOException e) {
164: throw new StreamException(e);
165: }
166: } else {
167: Token result = pushback;
168: pushback = null;
169: return result;
170: }
171: }
172:
173: public void pushBack(Token token) {
174: if (pushback == null) {
175: pushback = token;
176: } else {
177: // If this happens, I've messed up :( -joe.
178: throw new Error("Cannot push more than one token back");
179: }
180: }
181:
182: public void close() {
183: try {
184: in.close();
185: } catch (IOException e) {
186: throw new StreamException(e);
187: }
188: }
189:
190: public HierarchicalStreamReader underlyingReader() {
191: return this ;
192: }
193:
194: public void appendErrors(ErrorWriter errorWriter) {
195: // TODO: When things go bad, it would be good to know where!
196: }
197:
198: private static class IdRegistry {
199:
200: private Map map = new HashMap();
201:
202: public void put(long id, String value) {
203: map.put(new Long(id), value);
204: }
205:
206: public String get(long id) {
207: String result = (String) map.get(new Long(id));
208: if (result == null) {
209: throw new StreamException("Unknown ID : " + id);
210: } else {
211: return result;
212: }
213: }
214: }
215:
216: }
|