/*
* @(#)StorableInput.java
*
* Project: JHotdraw - a GUI framework for technical drawings
* http://www.jhotdraw.org
* http://jhotdraw.sourceforge.net
* Copyright: (c) by the original author(s) and all contributors
* License: Lesser GNU Public License (LGPL)
* http://www.opensource.org/licenses/lgpl-license.html
*/
package org.jhotdraw.util;
import java.io.*;
import java.awt.Color;
import java.util.List;
/**
* An input stream that can be used to resurrect Storable objects.
* StorableInput preserves the object identity of the stored objects.
*
* @see Storable
* @see StorableOutput
*
* @version <$CURRENT_VERSION$>s
*/
public class StorableInput {
private StreamTokenizer fTokenizer;
private List fMap;
/**
* Initializes a Storable input with the given input stream.
*/
public StorableInput(InputStream stream) {
Reader r = new BufferedReader(new InputStreamReader(stream));
fTokenizer = new StreamTokenizer(r);
// include inner class separate in class names
fTokenizer.wordChars('$', '$');
fMap = CollectionsFactory.current().createList();
}
/**
* Reads and resurrects a Storable object from the input stream.
*/
public Storable readStorable() throws IOException {
Storable storable;
String s = readString();
if (s.equals("NULL")) {
return null;
}
if (s.equals("REF")) {
int ref = readInt();
return retrieve(ref);
}
storable = (Storable) makeInstance(s);
map(storable);
storable.read(this);
return storable;
}
/**
* Reads a string from the input stream.
*/
public String readString() throws IOException {
int token = fTokenizer.nextToken();
if (token == StreamTokenizer.TT_WORD || token == '"') {
return fTokenizer.sval;
}
String msg = "String expected in line: " + fTokenizer.lineno();
throw new IOException(msg);
}
/**
* Reads an int from the input stream.
*/
public int readInt() throws IOException {
int token = fTokenizer.nextToken();
if (token == StreamTokenizer.TT_NUMBER) {
return (int) fTokenizer.nval;
}
String msg = "Integer expected in line: " + fTokenizer.lineno();
IOException exception = new IOException(msg);
exception.printStackTrace();
throw new IOException(msg);
}
/**
* Reads an int from the input stream.
*/
public long readLong() throws IOException {
long token = fTokenizer.nextToken();
if (token == StreamTokenizer.TT_NUMBER) {
return (long)fTokenizer.nval;
}
String msg = "Long expected in line: " + fTokenizer.lineno();
IOException exception = new IOException(msg);
//exception.printStackTrace();
throw exception;
}
/**
* Reads a color from the input stream.
*/
public Color readColor() throws IOException {
return new Color(readInt(), readInt(), readInt());
}
/**
* Reads a double from the input stream.
*/
public double readDouble() throws IOException {
double value = 0.0;
int token = fTokenizer.nextToken();
if (token == StreamTokenizer.TT_NUMBER) {
// Allow for a bug in StreamTokenizer (JDK-bugs 4079180, 4146533 and 4638205), which
// causes the exponent to be ignored, then read in as a string as the next token.
// This is a nasty kludge and I think it could fail if a number
// without an exponent is followed by a string beginning with "E" or "e"
value = fTokenizer.nval;
int token2 = fTokenizer.nextToken();
if (token2 == StreamTokenizer.TT_WORD &&
(fTokenizer.sval.charAt(0) == 'E' || fTokenizer.sval.charAt(0) == 'e')) {
value = Double.valueOf(value+fTokenizer.sval).doubleValue();
}
else {
fTokenizer.pushBack();
}
return value;
}
else {
throw new IOException("Double expected in line: " + fTokenizer.lineno());
}
}
/**
* Reads a boolean from the input stream.
*/
public boolean readBoolean() throws IOException {
int token = fTokenizer.nextToken();
if (token == StreamTokenizer.TT_NUMBER) {
return ((int) fTokenizer.nval) == 1;
}
String msg = "Integer expected in line: " + fTokenizer.lineno();
throw new IOException(msg);
}
private Object makeInstance(String className) throws IOException {
try {
Class cl = Class.forName(className);
return cl.newInstance();
}
catch (NoSuchMethodError e) {
throw new IOException("Class " + className
+ " does not seem to have a no-arg constructor");
}
catch (ClassNotFoundException e) {
throw new IOException("No class: " + className);
}
catch (InstantiationException e) {
throw new IOException("Cannot instantiate: " + className);
}
catch (IllegalAccessException e) {
throw new IOException("Class (" + className + ") not accessible");
}
}
private void map(Storable storable) {
if (!fMap.contains(storable)) {
fMap.add(storable);
}
}
private Storable retrieve(int ref) {
return (Storable)fMap.get(ref);
}
}