package com.schneeloch.bostonbusmap_library.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.schneeloch.bostonbusmap_library.data.Path;
import com.schneeloch.bostonbusmap_library.data.RouteConfig;
public class Box implements IBox {
private final DataInputStream inputStream;
private final DataOutputStream outputStream;
private final ByteArrayOutputStream innerOutputStream;
private static final byte IS_NULL = 1;
private static final byte IS_NOT_NULL = 0;
private final byte[] single = new byte[1];
private final BiMap<String, Integer> sharedStringTable = HashBiMap.create();
public Box(byte[] input)
{
if (input == null)
{
innerOutputStream = new ByteArrayOutputStream();
outputStream = new DataOutputStream(innerOutputStream);
inputStream = null;
}
else
{
outputStream = null;
innerOutputStream = null;
inputStream = new DataInputStream(new ByteArrayInputStream(input));
}
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeBytes(byte[])
*/
@Override
public void writeBytes(byte[] b) throws IOException
{
showProgress("writeBytes");
if (b == null)
{
writeByte((byte) IS_NULL);
}
else
{
writeByte((byte) IS_NOT_NULL);
writeInt(b.length);
outputStream.write(b);
}
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeInt(int)
*/
@Override
public void writeInt(int i) throws IOException
{
showProgress("writeInt");
outputStream.writeInt(i);
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readInt()
*/
@Override
public int readInt() throws IOException
{
showProgress("readInt");
return inputStream.readInt();
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeShort(short)
*/
@Override
public void writeShort(short s) throws IOException
{
showProgress("writeShort");
outputStream.writeShort(s);
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readShort()
*/
@Override
public short readShort() throws IOException
{
showProgress("readShort");
return inputStream.readShort();
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readBytes()
*/
@Override
public byte[] readBytes() throws IOException
{
showProgress("readBytes");
byte b = readByte();
if (b == IS_NOT_NULL)
{
int len = readInt();
byte[] ret = new byte[len];
inputStream.read(ret, 0, len);
return ret;
}
else
{
return null;
}
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readStringUnique()
*/
@Override
public String readStringUnique() throws IOException
{
showProgress("readStringUnique");
return inputStream.readUTF();
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeStringUnique(java.lang.String)
*/
@Override
public void writeStringUnique(String s) throws IOException
{
showProgress("writeStringUnique");
outputStream.writeUTF(s);
}
private static final int NULL_STRING = -1;
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readString()
*/
@Override
public String readString() throws IOException
{
showProgress("readString");
int index = inputStream.readInt();
if (index == NULL_STRING)
{
return null;
}
String s;
if (sharedStringTable.containsValue(index) == false)
{
//new string
s = inputStream.readUTF();
sharedStringTable.put(s, index);
}
else
{
s = sharedStringTable.inverse().get(index);
}
return s;
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeString(java.lang.String)
*/
@Override
public void writeString(String s) throws IOException {
showProgress("writeStringUnique");
if (s == null)
{
outputStream.writeInt(NULL_STRING);
}
else
{
Integer index = sharedStringTable.get(s);
if (null == index)
{
//new string
int newIndex = sharedStringTable.size();
sharedStringTable.put(s, newIndex);
outputStream.writeInt(newIndex);
outputStream.writeUTF(s);
}
else
{
//existing string
outputStream.writeInt(index);
}
}
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readByte()
*/
@Override
public byte readByte() throws IOException
{
showProgress("readByte");
inputStream.read(single, 0, 1);
return single[0];
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeByte(byte)
*/
@Override
public void writeByte(byte b) throws IOException
{
showProgress("writeByte");
single[0] = b;
outputStream.write(single, 0, 1);
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeFakeStringMap()
*/
@Override
public void writeFakeStringMap() throws IOException {
writeByte(IS_NOT_NULL);
writeInt(0);
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readFakeStringMap()
*/
@Override
public void readFakeStringMap() throws IOException
{
byte b = readByte();
if (b == IS_NOT_NULL)
{
int size = readInt();
for (int i = 0; i < size; i++)
{
readString();
readString();
}
}
}
/*public void writeStringKeyValue(ArrayList<String> keys, ArrayList<String> values) throws IOException
{
showProgress("writeStringMap");
if (keys == null || values == null)
{
//this never happens
writeByte(IS_NULL);
}
else
{
writeByte(IS_NOT_NULL);
int size = keys.size();
writeInt(keys.size());
for (int i = 0; i < size; i++)
{
writeString(keys.get(i));
writeString(values.get(i));
}
}
}*/
private static final String inbound = "Inbound";
private static final String outbound = "Outbound";
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeStringMap(java.util.Map)
*/
@Override
public void writeStringMap(Map<String, String> map) throws IOException
{
showProgress("writeStringMap");
if (map == null)
{
writeByte(IS_NULL);
}
else
{
writeByte(IS_NOT_NULL);
writeInt(map.size());
for (String s : map.keySet())
{
writeString(s);
writeString(map.get(s));
}
}
}
/* public void readStringMap(Map<String, String> map) throws IOException
{
showProgress("readStringMap(map)");
byte b = readByte();
if (b == IS_NULL)
{
//do nothing
return;
}
else
{
int size = readInt();
for (int i = 0; i < size; i++)
{
String key = readString();
String value = readString();
map.put(key, value);
}
}
}
public Map<String, String> readStringMap() throws IOException
{
showProgress("readStringMap");
byte b = readByte();
if (b == IS_NULL)
{
//do nothing
return Collections.emptyMap();
}
else
{
int size = readInt();
MyHashMap<String, String> map = new MyHashMap<String, String>(size);
for (int i = 0; i < size; i++)
{
String key = readString();
String value = readString();
map.put(key, value);
}
return map;
}
}
*/
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writePathsList(com.schneeloch.bostonbusmap_library.data.Path[])
*/
@Override
public void writePathsList(Path[] paths) throws IOException {
showProgress("writePathsMap");
int size = paths.length;
writeInt(size);
for (Path path : paths)
{
path.serialize(this);
}
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readPathsList()
*/
@Override
public Path[] readPathsList(int color) throws IOException {
showProgress("readPathsMap");
if (!isOutput()) {
//TODO: this actually means it's input, but the input was null
int size = readInt();
ArrayList<Path> paths = new ArrayList<Path>(size);
for (int i = 0; i < size; i++)
{
Path value = new Path(this, color);
paths.add(value);
}
return paths.toArray(RouteConfig.nullPaths);
}
else
{
return new Path[0];
}
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeDouble(double)
*/
@Override
public void writeDouble(double d) throws IOException {
showProgress("writeDouble");
outputStream.writeDouble(d);
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readDouble()
*/
@Override
public double readDouble() throws IOException
{
showProgress("readDouble");
return inputStream.readDouble();
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeFloat(float)
*/
@Override
public void writeFloat(float f) throws IOException
{
showProgress("writeFloat");
outputStream.writeFloat(f);
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readFloat()
*/
@Override
public float readFloat() throws IOException
{
showProgress("readFloat");
return inputStream.readFloat();
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeBoolean(boolean)
*/
@Override
public void writeBoolean(boolean b) throws IOException
{
showProgress("writeBoolean");
outputStream.writeBoolean(b);
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readBoolean()
*/
@Override
public boolean readBoolean() throws IOException
{
showProgress("readBoolean");
return inputStream.readBoolean();
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeLong(long)
*/
@Override
public void writeLong(long i) throws IOException {
showProgress("writeLong");
outputStream.writeLong(i);
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readLong()
*/
@Override
public long readLong() throws IOException
{
showProgress("readLong");
return inputStream.readLong();
}
private void showProgress(String string) {
/*if (outputStream != null)
{
progress.add(string + " " + outputStream.size());
}
else
{
progress.add(string);
}*/
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#getBlob()
*/
@Override
public byte[] getBlob() throws IOException {
outputStream.flush();
outputStream.close();
return innerOutputStream.toByteArray();
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#writeStrings(java.util.ArrayList)
*/
@Override
public void writeStrings(ArrayList<String> routes) throws IOException {
writeInt(routes.size());
for (String route : routes)
{
writeString(route);
}
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#readStrings()
*/
@Override
public ArrayList<String> readStrings() throws IOException
{
int size = readInt();
ArrayList<String> ret = new ArrayList<String>(size);
for (int i = 0; i < size; i++)
{
ret.add(readString());
}
return ret;
}
/* (non-Javadoc)
* @see com.schneeloch.bostonbusmap_library.util.IBox2#isOutput()
*/
@Override
public boolean isOutput() {
return inputStream == null;
}
@Override
public boolean isEmpty() {
// TODO: double check. But usually if we're using this it should be empty
return false;
}
public static IBox emptyBox() {
return new IBox() {
@Override
public void writeStrings(ArrayList<String> routes) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeStringUnique(String s) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeStringMap(Map<String, String> map) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeString(String s) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeShort(short s) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writePathsList(Path[] paths) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeLong(long i) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeInt(int i) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeFloat(float f) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeFakeStringMap() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeDouble(double d) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeBytes(byte[] b) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeByte(byte b) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void writeBoolean(boolean b) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public ArrayList<String> readStrings() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public String readStringUnique() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public String readString() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public short readShort() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public Path[] readPathsList(int color) throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public long readLong() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public int readInt() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public float readFloat() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public void readFakeStringMap() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public double readDouble() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public byte[] readBytes() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public byte readByte() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public boolean readBoolean() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public boolean isOutput() {
return false;
}
@Override
public byte[] getBlob() throws IOException {
throw new RuntimeException("Unimplemented");
}
@Override
public boolean isEmpty() {
return true;
}
};
}
}