/*
* Copyright 2015 MiLaboratory.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.milaboratory.primitivio;
import java.io.*;
import java.util.ArrayList;
public final class PrimitivI implements DataInput, AutoCloseable {
final DataInput input;
final SerializersManager manager;
final ArrayList<Object> references = new ArrayList<>(), putKnownAfterReset = new ArrayList<>();
int knownReferencesCount = 0;
int depth = 0;
public PrimitivI(InputStream input) {
this(new DataInputStream(input),
new SerializersManager());
}
public PrimitivI(DataInput input) {
this(input, new SerializersManager());
}
public PrimitivI(DataInput input, SerializersManager manager) {
this.input = input;
this.manager = manager;
}
public SerializersManager getSerializersManager() {
return manager;
}
public void putKnownReference(Object ref) {
if (depth > 0) {
putKnownAfterReset.add(ref);
} else {
references.add(ref);
++knownReferencesCount;
}
}
public void readReference(Object ref) {
int id = readVarInt();
if (id != references.size())
throw new RuntimeException("wrong reference id.");
references.add(ref);
}
private void reset() {
for (int i = references.size() - 1; i >= knownReferencesCount; --i)
references.remove(i);
if (!putKnownAfterReset.isEmpty()) {
for (Object ref : putKnownAfterReset)
putKnownReference(ref);
putKnownAfterReset.clear();
}
}
public <T> T readObject(Class<T> type) {
Serializer serializer = manager.getSerializer(type);
if (serializer.isReference()) {
int id = readVarInt();
if (id == PrimitivO.NULL_ID) {
return null;
} else if (id == PrimitivO.NEW_OBJECT_ID) {
boolean readReferenceAfter = !serializer.handlesReference();
++depth;
try {
T obj = (T) serializer.read(this);
if (readReferenceAfter)
readReference(obj);
return obj;
} finally {
--depth;
if (depth == 0)
reset();
}
} else {
Object obj = references.get(id - PrimitivO.ID_OFFSET);
if (!type.isInstance(obj))
throw new RuntimeException("Wrong file format.");
return (T) obj;
}
} else {
++depth;
try {
return (T) serializer.read(this);
} finally {
--depth;
if (depth == 0)
reset();
}
}
}
public long readVarLongZigZag() {
return Util.zigZagDecodeLong(readVarLong());
}
public long readVarLong() {
long value = 0, tmp;
int shift = 0;
do {
tmp = readByte();
value |= (tmp & 0x7F) << (shift);
shift += 7;
} while ((tmp & 0x80) != 0);
return value;
}
public int readVarIntZigZag() {
return Util.zigZagDecodeInt(readVarInt());
}
public int readVarInt() {
int value = 0, tmp;
int shift = 0;
do {
tmp = readByte();
value |= (tmp & 0x7F) << (shift);
shift += 7;
} while ((tmp & 0x80) != 0);
return value;
}
@Override
public void readFully(byte[] b) {
try {
input.readFully(b);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void readFully(byte[] b, int off, int len) {
try {
input.readFully(b, off, len);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public int skipBytes(int n) {
try {
return input.skipBytes(n);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean readBoolean() {
try {
return input.readBoolean();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public byte readByte() {
try {
return input.readByte();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public int readUnsignedByte() {
try {
return input.readUnsignedByte();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public short readShort() {
try {
return input.readShort();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public int readUnsignedShort() {
try {
return input.readUnsignedShort();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public char readChar() {
try {
return input.readChar();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public int readInt() {
try {
return input.readInt();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public long readLong() {
try {
return input.readLong();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public float readFloat() {
try {
return input.readFloat();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public double readDouble() {
try {
return input.readDouble();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public String readLine() {
throw new UnsupportedOperationException();
}
@Override
public String readUTF() {
try {
return input.readUTF();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() {
try {
if (input instanceof Closeable)
((Closeable) input).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}