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.io.StreamException;
015:
016: import java.io.DataOutput;
017: import java.io.IOException;
018: import java.io.DataInput;
019:
020: /**
021: * Represents the Tokens stored in the binary stream used by
022: * {@link BinaryStreamReader} and {@link BinaryStreamWriter}.
023: * <p/>
024: * <p>A token consists of a type and (depending on this type)
025: * it may additionally have an ID (positive long number)
026: * and/or a value (String).</p>
027: * <p/>
028: * <p>The first byte of the token represents how many subsequent
029: * bytes are used by the ID.
030: *
031: * @author Joe Walnes
032: * @see BinaryStreamReader
033: * @see BinaryStreamWriter
034: * @since 1.2
035: */
036: public abstract class Token {
037:
038: private static final byte TYPE_MASK = 0x7;
039: public static final byte TYPE_VERSION = 0x1;
040: public static final byte TYPE_MAP_ID_TO_VALUE = 0x2;
041: public static final byte TYPE_START_NODE = 0x3;
042: public static final byte TYPE_END_NODE = 0x4;
043: public static final byte TYPE_ATTRIBUTE = 0x5;
044: public static final byte TYPE_VALUE = 0x6;
045:
046: private static final byte ID_MASK = 0x38;
047: private static final byte ID_ONE_BYTE = 0x08;
048: private static final byte ID_TWO_BYTES = 0x10;
049: private static final byte ID_FOUR_BYTES = 0x18;
050: private static final byte ID_EIGHT_BYTES = 0x20;
051:
052: private final byte type;
053:
054: protected long id = -1;
055: protected String value;
056:
057: public Token(byte type) {
058: this .type = type;
059: }
060:
061: public byte getType() {
062: return type;
063: }
064:
065: public long getId() {
066: return id;
067: }
068:
069: public String getValue() {
070: return value;
071: }
072:
073: public String toString() {
074: return getClass().getName() + " [id=" + id + ", value='"
075: + value + "']";
076: }
077:
078: public boolean equals(Object o) {
079: if (this == o)
080: return true;
081: if (o == null || getClass() != o.getClass())
082: return false;
083:
084: final Token token = (Token) o;
085:
086: if (id != token.id)
087: return false;
088: if (type != token.type)
089: return false;
090: return !(value != null ? !value.equals(token.value)
091: : token.value != null);
092:
093: }
094:
095: public int hashCode() {
096: int result;
097: result = type;
098: result = 29 * result + (int) (id ^ (id >>> 32));
099: result = 29 * result + (value != null ? value.hashCode() : 0);
100: return result;
101: }
102:
103: public abstract void writeTo(DataOutput out, byte idType)
104: throws IOException;
105:
106: public abstract void readFrom(DataInput in, byte idType)
107: throws IOException;
108:
109: protected void writeId(DataOutput out, long id, byte idType)
110: throws IOException {
111: if (id < 0) {
112: throw new IOException("id must not be negative " + id);
113: }
114: switch (idType) {
115: case ID_ONE_BYTE:
116: out.writeByte((byte) id + Byte.MIN_VALUE);
117: break;
118: case ID_TWO_BYTES:
119: out.writeShort((short) id + Short.MIN_VALUE);
120: break;
121: case ID_FOUR_BYTES:
122: out.writeInt((int) id + Integer.MIN_VALUE);
123: break;
124: case ID_EIGHT_BYTES:
125: out.writeLong(id + Long.MIN_VALUE);
126: break;
127: default:
128: throw new Error("Unknown idType " + idType);
129: }
130: }
131:
132: protected void writeString(DataOutput out, String string)
133: throws IOException {
134: out.writeUTF(string);
135: }
136:
137: protected long readId(DataInput in, byte idType) throws IOException {
138: switch (idType) {
139: case ID_ONE_BYTE:
140: return in.readByte() - Byte.MIN_VALUE;
141: case ID_TWO_BYTES:
142: return in.readShort() - Short.MIN_VALUE;
143: case ID_FOUR_BYTES:
144: return in.readInt() - Integer.MIN_VALUE;
145: case ID_EIGHT_BYTES:
146: return in.readLong() - Long.MIN_VALUE;
147: default:
148: throw new Error("Unknown idType " + idType);
149: }
150: }
151:
152: protected String readString(DataInput in) throws IOException {
153: return in.readUTF();
154: }
155:
156: public static class Formatter {
157:
158: public void write(DataOutput out, Token token)
159: throws IOException {
160: long id = token.getId();
161: byte idType;
162: if (id <= Byte.MAX_VALUE - Byte.MIN_VALUE) {
163: idType = ID_ONE_BYTE;
164: } else if (id <= Short.MAX_VALUE - Short.MIN_VALUE) {
165: idType = ID_TWO_BYTES;
166: } else if (id <= (long) Integer.MAX_VALUE
167: - (long) Integer.MIN_VALUE) { // cast to long to prevent overflo
168: idType = ID_FOUR_BYTES;
169: } else {
170: idType = ID_EIGHT_BYTES;
171: }
172: out.write(token.getType() + idType);
173: token.writeTo(out, idType);
174: }
175:
176: public Token read(DataInput in) throws IOException {
177: byte nextByte = in.readByte();
178: byte type = (byte) (nextByte & TYPE_MASK);
179: byte idType = (byte) (nextByte & ID_MASK);
180: Token token = contructToken(type);
181: token.readFrom(in, idType);
182: return token;
183: }
184:
185: private Token contructToken(byte type) {
186: switch (type) {
187: case Token.TYPE_START_NODE:
188: return new StartNode();
189: case Token.TYPE_MAP_ID_TO_VALUE:
190: return new MapIdToValue();
191: case Token.TYPE_ATTRIBUTE:
192: return new Attribute();
193: case Token.TYPE_END_NODE:
194: return new EndNode();
195: case Token.TYPE_VALUE:
196: return new Value();
197: default:
198: throw new StreamException("Unknown token type");
199: }
200: }
201: }
202:
203: public static class MapIdToValue extends Token {
204:
205: public MapIdToValue(long id, String value) {
206: super (TYPE_MAP_ID_TO_VALUE);
207: this .id = id;
208: this .value = value;
209: }
210:
211: public MapIdToValue() {
212: super (TYPE_MAP_ID_TO_VALUE);
213: }
214:
215: public void writeTo(DataOutput out, byte idType)
216: throws IOException {
217: writeId(out, id, idType);
218: writeString(out, value);
219: }
220:
221: public void readFrom(DataInput in, byte idType)
222: throws IOException {
223: id = readId(in, idType);
224: value = readString(in);
225: }
226:
227: }
228:
229: public static class StartNode extends Token {
230:
231: public StartNode(long id) {
232: super (TYPE_START_NODE);
233: this .id = id;
234: }
235:
236: public StartNode() {
237: super (TYPE_START_NODE);
238: }
239:
240: public void writeTo(DataOutput out, byte idType)
241: throws IOException {
242: writeId(out, id, idType);
243: }
244:
245: public void readFrom(DataInput in, byte idType)
246: throws IOException {
247: id = readId(in, idType);
248: }
249:
250: }
251:
252: public static class EndNode extends Token {
253:
254: public EndNode() {
255: super (TYPE_END_NODE);
256: }
257:
258: public void writeTo(DataOutput out, byte idType) {
259: }
260:
261: public void readFrom(DataInput in, byte idType) {
262: }
263:
264: }
265:
266: public static class Attribute extends Token {
267:
268: public Attribute(long id, String value) {
269: super (TYPE_ATTRIBUTE);
270: this .id = id;
271: this .value = value;
272: }
273:
274: public Attribute() {
275: super (TYPE_ATTRIBUTE);
276: }
277:
278: public void writeTo(DataOutput out, byte idType)
279: throws IOException {
280: writeId(out, id, idType);
281: writeString(out, value);
282: }
283:
284: public void readFrom(DataInput in, byte idType)
285: throws IOException {
286: this .id = readId(in, idType);
287: this .value = readString(in);
288: }
289:
290: }
291:
292: public static class Value extends Token {
293:
294: public Value(String value) {
295: super (TYPE_VALUE);
296: this .value = value;
297: }
298:
299: public Value() {
300: super (TYPE_VALUE);
301: }
302:
303: public void writeTo(DataOutput out, byte idType)
304: throws IOException {
305: writeString(out, value);
306: }
307:
308: public void readFrom(DataInput in, byte idType)
309: throws IOException {
310: value = readString(in);
311: }
312:
313: }
314:
315: }
|