/*******************************************************************************
* Copyright (c) 2004, 2006
* Thomas Hallgren, Kenneth Olwing, Mitch Sonies
* Pontus Rydin, Nils Unden, Peer Torngren
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the individual
* copyright holders listed above, as Initial Contributors under such license.
* The text of such license is available at www.eclipse.org.
*******************************************************************************/
package org.eclipse.buckminster.p4.internal;
import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StreamCorruptedException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PythonInputStream extends BufferedInputStream implements PythonStreamConstants
{
private final byte[] m_buf = new byte[32];
private boolean m_atEnd = false;
public PythonInputStream(InputStream stream)
{
super(stream);
}
public boolean isAtEnd()
{
return m_atEnd;
}
/**
* Read the next object from the stream. Note that <code>null</code> is a valid return value that does <b>not</b>
* imply end of file.
*
* @return The next object on the stream or the special object {@link #EOF} to denote that the end of the input is
* reached.
* @throws IOException
* if some other error occurs during reading.
*/
@SuppressWarnings("fallthrough")
public Object readObject() throws IOException
{
if(m_atEnd)
return null;
int token = this.read();
switch(token)
{
case -1:
m_atEnd = true;
// Fall through
case TYPE_NULL:
return null;
case TYPE_STRING:
return this.readString();
case TYPE_INT:
return new Integer(this.readInt32());
case TYPE_INT64:
return new Long(this.readInt64());
case TYPE_FLOAT:
return new Double(this.readDouble());
case TYPE_DICT:
return this.readMap();
case TYPE_LIST:
return this.readList();
case TYPE_TUPLE:
return this.readTuple();
}
throw new StreamCorruptedException();
}
public Map<String, String> readStringMap() throws IOException
{
if(m_atEnd)
return null;
int token = this.read();
switch(token)
{
case -1:
m_atEnd = true;
return null;
case TYPE_DICT:
return this.readStrings();
}
throw new StreamCorruptedException();
}
private double readDouble() throws IOException
{
int len = this.read();
if(len <= 0 || len > 31)
throw new StreamCorruptedException();
this.readFully(m_buf, 0, len);
return Double.parseDouble(new String(m_buf, 0, len, "US-ASCII")); //$NON-NLS-1$
}
private void readFully(byte[] b, int off, int len) throws IOException
{
while(len > 0)
{
int n = this.read(b, off, len);
if(n < 0)
throw new EOFException();
off += n;
len -= n;
}
}
private int readInt32() throws IOException
{
byte[] bytes = m_buf;
this.readFully(bytes, 0, 4);
return ((bytes[3] << 24) + ((bytes[2] & 255) << 16) + ((bytes[1] & 255) << 8) + ((bytes[0] & 255) << 0));
}
private long readInt64() throws IOException
{
byte[] bytes = m_buf;
this.readFully(bytes, 0, 8);
return (((long)bytes[7] << 56) + ((long)(bytes[6] & 255) << 48) + ((long)(bytes[5] & 255) << 40)
+ ((long)(bytes[4] & 255) << 32) + ((long)(bytes[3] & 255) << 24) + ((bytes[2] & 255) << 16)
+ ((bytes[1] & 255) << 8) + ((bytes[0] & 255) << 0));
}
private List<?> readList() throws IOException
{
int top = this.readInt32();
if(top <= 0)
return Collections.emptyList();
ArrayList<Object> list = new ArrayList<Object>(top);
while(--top >= 0)
{
list.add(this.readObject());
if(m_atEnd)
throw new StreamCorruptedException();
}
return list;
}
private Map<?, ?> readMap() throws IOException
{
Object key = this.readObject();
if(m_atEnd)
throw new StreamCorruptedException();
if(key == null)
return Collections.emptyMap();
HashMap<Object, Object> map = new HashMap<Object, Object>();
do
{
map.put(key, this.readObject());
if(m_atEnd)
throw new StreamCorruptedException();
key = this.readObject();
if(m_atEnd)
throw new StreamCorruptedException();
} while(key != null);
return map;
}
private String readString() throws IOException
{
int len = this.readInt32();
byte[] bytes = new byte[len];
this.readFully(bytes, 0, len);
return new String(bytes, ENCODING);
}
private Map<String, String> readStrings() throws IOException
{
Object key = this.readObject();
if(m_atEnd)
throw new StreamCorruptedException();
if(key == null)
return Collections.emptyMap();
HashMap<String, String> map = new HashMap<String, String>();
do
{
Object val = this.readObject();
if(val == null || m_atEnd)
throw new StreamCorruptedException();
map.put(key.toString(), val.toString());
key = this.readObject();
if(m_atEnd)
throw new StreamCorruptedException();
} while(key != null);
return map;
}
private Object[] readTuple() throws IOException
{
int top = this.readInt32();
Object[] tuple = new Object[top];
for(int idx = 0; idx < top; ++idx)
{
tuple[idx] = this.readObject();
if(m_atEnd)
throw new StreamCorruptedException();
}
return tuple;
}
}