/*
* $Id: Unserializer.java,v 1.20 2002/09/16 08:05:02 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.core;
import anvil.script.Context;
import java.util.ArrayList;
/**
*
* Class for wrapping byte array and pointer to it.
*
* @author Jani Lehtim�ki
* @version $Revision: 1.20 $
*/
public final class Unserializer
{
byte[] _data;
int _length;
int _pos;
private Context _context;
private ArrayList _registered = null;
private boolean _unicode = true;
protected Unserializer(Context context, byte[] data)
throws UnserializationException
{
_context = context;
_data = data;
_length = data.length;
_pos = 0;
_unicode = consumeIf('!');
}
protected Unserializer(Context context, byte[] data, int begin, int end)
throws UnserializationException
{
_context = context;
_data = data;
_length = end;
_pos = begin;
_unicode = consumeIf('!');
}
public Context getContext() throws UnserializationException
{
Context context = _context;
if (context == null) {
_context = context = Context.getInstance();
}
return context;
}
public void register(Object o)
{
if (_registered == null) {
_registered = new ArrayList();
}
_registered.add(o);
}
public void close()
{
if (_registered != null) {
_registered.clear();
_registered = null;
}
}
public int peek()
{
if (_pos < _length) {
return _data[_pos];
} else {
return -1;
}
}
public void back()
{
if (_pos > 0) {
_pos--;
}
}
public String getView()
{
return new String(_data, _pos, _length - _pos);
}
public int get() throws UnserializationException
{
if (_pos < _length) {
return _data[_pos++];
} else {
throw new UnserializationException();
}
}
public void consume(int ch) throws UnserializationException
{
if (_pos < _length) {
if (_data[_pos++] == ch) {
return;
}
}
throw new UnserializationException();
}
public Object consumeRegisteredIf() throws UnserializationException
{
if (_pos < _length) {
if (_data[_pos] == '#') {
_pos++;
return _registered.get((int)getLong());
}
return null;
} else {
throw new UnserializationException();
}
}
public boolean consumeIf(int ch) throws UnserializationException
{
if (_pos < _length) {
if (_data[_pos] == ch) {
_pos++;
return true;
}
return false;
}
throw new UnserializationException();
}
/**
* Helper method which actually does the serialization.
*
* @return Unserialized value
* @exception UnserializationException If data has been corrupted
*/
public final Any unserialize()
throws UnserializationException
{
byte[] data = _data;
if (_pos >= _length) {
throw new UnserializationException();
}
switch(data[_pos++]) {
case (byte)'!': // unicode marker, first byte in stream
case (byte)'[': // stackframe, appears only with closure
throw new UnserializationException("Invalid tag");
case (byte)'#':
return (Any)_registered.get((int)getLong());
case (byte)'-':
return Any.NEG_INF;
case (byte)'+':
return Any.INF;
case (byte)'n':
return Any.NULL;
case (byte)'u':
return Any.UNDEFINED;
case (byte)'t':
return Any.TRUE;
case (byte)'f':
return Any.FALSE;
case (byte)'i':
return ObjectPool.createLong(getLong());
case (byte)'d':
return new AnyDouble(getDouble());
case (byte)'s':
{
AnyString str = new AnyString(getUTF16String());
register(str);
return str;
}
case (byte)'b':
return AnyBinary.unserialize(this);
case (byte)'B':
{
AnyBuffer buffer = new AnyBuffer(new StringBuffer(getUTF16String()));
register(buffer);
return buffer;
}
case (byte)'l':
return AnyTuple.unserialize(this);
case (byte)'L':
return AnyList.unserialize(this);
case (byte)'a':
return Array.unserialize(this);
case (byte)'o':
return AnyClass.unserialize(this);
case (byte)'x':
return AnyAbstractClass.unserialize(this);
case (byte)'p':
return AnyPattern.unserialize(this);
case (byte)'r':
return AnyRange.unserialize(this);
case (byte)'m':
return AnyMap.unserialize(this);
case (byte)'q':
return AnyLocalRef.unserialize(this);
case (byte)'Q':
return AnyEscapedLocalRef.unserialize(this);
case (byte)'R':
return AnyClassRef.unserialize(this);
case (byte)'F':
return anvil.core.io.AnyFile.unserialize(this);
case (byte)'U':
return anvil.core.net.AnyURL.unserialize(this);
case (byte)'I':
return anvil.core.net.AnyInetAddress.unserialize(this);
case (byte)'C':
return anvil.core.time.AnyCalendar.unserialize(this);
case (byte)'M':
return anvil.core.runtime.AnyScope.unserialize(this);
case (byte)'c':
return anvil.core.runtime.AnyFunction.unserialize(this);
case (byte)'D':
return anvil.core.runtime.AnyFunction.unserializeDelegate(this);
case (byte)'Z':
return anvil.core.runtime.AnyFunction.unserializeClosure(this);
case (byte)'N':
return anvil.core.runtime.AnyNamespace.unserialize(this);
case (byte)'T':
return anvil.core.runtime.AnyType.unserialize(this);
case (byte)'H':
return anvil.core.runtime.AnyDoc.unserialize(this);
case (byte)'P':
return anvil.core.runtime.AnyPermission.unserialize(this);
case (byte)'A':
return anvil.core.arrays.AnyArray.unserialize(this);
default:
throw new UnserializationException("Invalid tag");
}
}
/**
* Helper method for unserializing String.
* This method uses deprecated API when instantiating new Strings
* for the purposes of efficiency.
*
* @return Unserialized String
* @exception UnserializationException If data has been corrupted
*/
public final String getUTF16String()
throws UnserializationException
{
if (_unicode) {
int length = (int)getLong();
if (length < 0) {
throw new UnserializationException();
}
byte[] data = _data;
int pos = _pos;
if ((pos + length*2) > _length) {
throw new UnserializationException();
}
char[] array = new char[length];
for(int i=0; i<length; i++) {
array[i] = (char)((data[pos++]<<8) | (data[pos++] & 0xff));
}
_pos = pos;
return new String(array);
} else {
return getBinaryString();
}
}
public final String getBinaryString()
throws UnserializationException
{
int length = (int)getLong();
if (length < 0) {
throw new UnserializationException();
}
byte[] data = _data;
int pos = _pos;
if ((pos + length) > _length) {
throw new UnserializationException();
}
_pos = pos + length;
return new String(data, 0, pos, length);
}
/**
* Helper method for unserializing integer.
*
* @return Unserialized integer
* @exception UnserializationException If data has been corrupted
*/
public final long getLong() throws UnserializationException
{
byte[] data = _data;
int length = _length;
int pos = _pos;
long number = 0;
long multiply = 1;
boolean negative = false;
int begin;
int ch;
if ((pos+1) >= length) {
throw new UnserializationException();
}
if (data[pos] == '-') {
negative = true;
pos++;
}
begin = pos;
while(pos < length) {
ch = data[pos++];
if (ch<'0' || ch>'9') {
break;
}
}
if (pos - begin <= 1) {
throw new UnserializationException();
}
for(int p=pos-2; p>=begin; p--) {
number += ((((long)data[p])-'0') * multiply);
multiply *= 10;
}
_pos = pos;
return negative ? -number : number;
}
/**
* Helper method for unserializing Double.
*
* @return Unserialized Double
* @exception UnserializationException If data has been corrupted
*/
public final double getDouble() throws UnserializationException
{
byte[] data = _data;
int length = _length;
int pos = _pos;
if ((pos+2) >= length) {
throw new UnserializationException();
}
int begin = pos;
while(pos < length) {
if (data[pos++]==';') {
break;
}
}
_pos = pos;
try {
return Double.parseDouble(new String(data, 0, begin, pos - 1 - begin));
} catch (NumberFormatException e) {
throw new UnserializationException();
}
}
/**
* Helper method for unserializing float.
*
* @return Unserialized float
* @exception UnserializationException If data has been corrupted
*/
public final float getFloat() throws UnserializationException
{
byte[] data = _data;
int length = _length;
int pos = _pos;
if ((pos+2) >= length) {
throw new UnserializationException();
}
int begin = pos;
while(pos < length) {
if (data[pos++]==';') {
break;
}
}
_pos = pos;
try {
return Float.parseFloat(new String(data, 0, begin, pos - 1 - begin));
} catch (NumberFormatException e) {
throw new UnserializationException();
}
}
public char getCharUTF16() throws UnserializationException
{
if ((_pos+2) >= _length) {
throw new UnserializationException();
}
byte[] data = _data;
return (char)((data[_pos++]<<8) | (data[_pos++] & 0xff));
}
}