blob: 17eba6609f2314b22f0d4f56f6b8e040f51f3452 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.ignite.internal.marshaller.optimized;
import java.io.Externalizable;
import java.io.IOException;
import java.io.NotActiveException;
import java.io.ObjectInputStream;
import java.io.ObjectInputValidation;
import java.io.ObjectStreamClass;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.util.GridUnsafe;
import org.apache.ignite.internal.util.io.GridDataInput;
import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.marshaller.MarshallerContext;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.ARRAY_LIST;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.BOOLEAN;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.BOOLEAN_ARR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.BYTE;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.BYTE_ARR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.CHAR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.CHAR_ARR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.CLS;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.DATE;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.DOUBLE;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.DOUBLE_ARR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.ENUM;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.EXTERNALIZABLE;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.FLOAT;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.FLOAT_ARR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.HANDLE;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.HASH_MAP;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.HASH_SET;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.HASH_SET_MAP_OFF;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.INT;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.INT_ARR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.JDK;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.LINKED_HASH_MAP;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.LINKED_HASH_SET;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.LINKED_LIST;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.LONG;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.LONG_ARR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.NULL;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.OBJ_ARR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.PROPS;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.PROXY;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.SERIALIZABLE;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.SHORT;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.SHORT_ARR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.STR;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.UUID;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.classDescriptor;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.setBoolean;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.setByte;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.setChar;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.setDouble;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.setFloat;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.setInt;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.setLong;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.setObject;
import static org.apache.ignite.internal.marshaller.optimized.OptimizedMarshallerUtils.setShort;
/**
* Optimized object input stream.
*/
class OptimizedObjectInputStream extends ObjectInputStream {
/** Dummy object for HashSet. */
private static final Object DUMMY = new Object();
/** */
private final HandleTable handles = new HandleTable(10);
/** */
private MarshallerContext ctx;
/** */
private OptimizedMarshallerIdMapper mapper;
/** */
private ClassLoader clsLdr;
/** */
private GridDataInput in;
/** */
private Object curObj;
/** */
private OptimizedClassDescriptor.ClassFields curFields;
/** */
private Class<?> curCls;
/** */
private ConcurrentMap<Class, OptimizedClassDescriptor> clsMap;
/**
* @param in Input.
* @throws IOException In case of error.
*/
OptimizedObjectInputStream(GridDataInput in) throws IOException {
this.in = in;
}
/**
* @param clsMap Class descriptors by class map.
* @param ctx Context.
* @param mapper ID mapper.
* @param clsLdr Class loader.
*/
void context(
ConcurrentMap<Class, OptimizedClassDescriptor> clsMap,
MarshallerContext ctx,
OptimizedMarshallerIdMapper mapper,
ClassLoader clsLdr)
{
this.clsMap = clsMap;
this.ctx = ctx;
this.mapper = mapper;
this.clsLdr = clsLdr;
}
/**
* @return Input.
*/
public GridDataInput in() {
return in;
}
/**
* @param in Input.
*/
public void in(GridDataInput in) {
this.in = in;
}
/** {@inheritDoc} */
@Override public void close() throws IOException {
reset();
ctx = null;
clsLdr = null;
clsMap = null;
}
/** {@inheritDoc} */
@SuppressWarnings("NonSynchronizedMethodOverridesSynchronizedMethod")
@Override public void reset() throws IOException {
in.reset();
handles.clear();
curObj = null;
curFields = null;
}
/** {@inheritDoc} */
@Override public Object readObjectOverride() throws ClassNotFoundException, IOException {
Object oldObj = curObj;
OptimizedClassDescriptor.ClassFields oldFields = curFields;
try {
return readObject0();
}
finally {
curObj = oldObj;
curFields = oldFields;
}
}
/**
* Reads object from stream.
*
* @return Object.
* @throws ClassNotFoundException If class was not found.
* @throws IOException In case of error.
*/
private Object readObject0() throws ClassNotFoundException, IOException {
curObj = null;
curFields = null;
byte ref = in.readByte();
switch (ref) {
case NULL:
return null;
case HANDLE:
return handles.lookup(readInt());
case JDK:
try {
return ctx.jdkMarshaller().unmarshal(this, clsLdr);
}
catch (IgniteCheckedException e) {
IOException ioEx = e.getCause(IOException.class);
if (ioEx != null)
throw ioEx;
else
throw new IOException("Failed to deserialize object with JDK marshaller.", e);
}
case BYTE:
return readByte();
case SHORT:
return readShort();
case INT:
return readInt();
case LONG:
return readLong();
case FLOAT:
return readFloat();
case DOUBLE:
return readDouble();
case CHAR:
return readChar();
case BOOLEAN:
return readBoolean();
case BYTE_ARR:
return readByteArray();
case SHORT_ARR:
return readShortArray();
case INT_ARR:
return readIntArray();
case LONG_ARR:
return readLongArray();
case FLOAT_ARR:
return readFloatArray();
case DOUBLE_ARR:
return readDoubleArray();
case CHAR_ARR:
return readCharArray();
case BOOLEAN_ARR:
return readBooleanArray();
case OBJ_ARR:
return readArray(readClass());
case STR:
return readString();
case UUID:
return readUuid();
case PROPS:
return readProperties();
case ARRAY_LIST:
return readArrayList();
case HASH_MAP:
return readHashMap(false);
case HASH_SET:
return readHashSet(HASH_SET_MAP_OFF);
case LINKED_LIST:
return readLinkedList();
case LINKED_HASH_MAP:
return readLinkedHashMap(false);
case LINKED_HASH_SET:
return readLinkedHashSet(HASH_SET_MAP_OFF);
case DATE:
return readDate();
case CLS:
return readClass();
case PROXY:
Class<?>[] intfs = new Class<?>[readInt()];
for (int i = 0; i < intfs.length; i++)
intfs[i] = readClass();
InvocationHandler ih = (InvocationHandler)readObject();
return Proxy.newProxyInstance(clsLdr != null ? clsLdr : U.gridClassLoader(), intfs, ih);
case ENUM:
case EXTERNALIZABLE:
case SERIALIZABLE:
int typeId = readInt();
OptimizedClassDescriptor desc = typeId == 0 ?
classDescriptor(clsMap, U.forName(readUTF(), clsLdr, ctx.classNameFilter()), ctx, mapper):
classDescriptor(clsMap, typeId, clsLdr, ctx, mapper);
curCls = desc.describedClass();
try {
return desc.read(this);
}
catch (IOException e){
throw new IOException("Failed to deserialize object [typeName=" +
desc.describedClass().getName() + ']', e);
}
default:
SB msg = new SB("Unexpected error occurred during unmarshalling");
if (curCls != null)
msg.a(" of an instance of the class: ").a(curCls.getName());
msg.a(". Check that all nodes are running the same version of Ignite and that all nodes have " +
"GridOptimizedMarshaller configured with identical optimized classes lists, if any " +
"(see setClassNames and setClassNamesPath methods). If your serialized classes implement " +
"java.io.Externalizable interface, verify that serialization logic is correct.");
throw new IOException(msg.toString());
}
}
/**
* @return Class.
* @throws ClassNotFoundException If class was not found.
* @throws IOException In case of other error.
*/
private Class<?> readClass() throws ClassNotFoundException, IOException {
int compTypeId = readInt();
return compTypeId == 0 ? U.forName(readUTF(), clsLdr) :
classDescriptor(clsMap, compTypeId, clsLdr, ctx, mapper).describedClass();
}
/**
* Reads array from this stream.
*
* @param compType Array component type.
* @return Array.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
@SuppressWarnings("unchecked")
<T> T[] readArray(Class<T> compType) throws ClassNotFoundException, IOException {
int len = in.readInt();
T[] arr = (T[])Array.newInstance(compType, len);
handles.assign(arr);
for (int i = 0; i < len; i++)
arr[i] = (T)readObject();
return arr;
}
/**
* Reads {@link UUID} from this stream.
*
* @return UUID.
* @throws IOException In case of error.
*/
UUID readUuid() throws IOException {
UUID uuid = new UUID(readLong(), readLong());
handles.assign(uuid);
return uuid;
}
/**
* Reads {@link Properties} from this stream.
*
* @return Properties.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
Properties readProperties() throws ClassNotFoundException, IOException {
Properties dflts = readBoolean() ? null : (Properties)readObject();
Properties props = new Properties(dflts);
int size = in.readInt();
for (int i = 0; i < size; i++)
props.setProperty(readUTF(), readUTF());
handles.assign(props);
return props;
}
/**
* Reads and sets all non-static and non-transient field values from this stream.
*
* @param obj Object.
* @param fieldOffs Field offsets.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
@SuppressWarnings("ForLoopReplaceableByForEach")
void readFields(Object obj, OptimizedClassDescriptor.ClassFields fieldOffs) throws ClassNotFoundException,
IOException {
for (int i = 0; i < fieldOffs.size(); i++) {
OptimizedClassDescriptor.FieldInfo t = fieldOffs.get(i);
try {
switch ((t.type())) {
case BYTE:
byte resByte = readByte();
if (t.field() != null)
setByte(obj, t.offset(), resByte);
break;
case SHORT:
short resShort = readShort();
if (t.field() != null)
setShort(obj, t.offset(), resShort);
break;
case INT:
int resInt = readInt();
if (t.field() != null)
setInt(obj, t.offset(), resInt);
break;
case LONG:
long resLong = readLong();
if (t.field() != null)
setLong(obj, t.offset(), resLong);
break;
case FLOAT:
float resFloat = readFloat();
if (t.field() != null)
setFloat(obj, t.offset(), resFloat);
break;
case DOUBLE:
double resDouble = readDouble();
if (t.field() != null)
setDouble(obj, t.offset(), resDouble);
break;
case CHAR:
char resChar = readChar();
if (t.field() != null)
setChar(obj, t.offset(), resChar);
break;
case BOOLEAN:
boolean resBoolean = readBoolean();
if (t.field() != null)
setBoolean(obj, t.offset(), resBoolean);
break;
case OTHER:
Object resObject = readObject();
if (t.field() != null)
setObject(obj, t.offset(), resObject);
}
}
catch (IOException e) {
throw new IOException("Failed to deserialize field [name=" + t.name() + ']', e);
}
}
}
/**
* Reads {@link Externalizable} object.
*
* @param constructor Constructor.
* @param readResolveMtd {@code readResolve} method.
* @return Object.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
Object readExternalizable(Constructor<?> constructor, Method readResolveMtd)
throws ClassNotFoundException, IOException {
Object obj;
try {
obj = constructor.newInstance();
}
catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
throw new IOException(e);
}
int handle = handles.assign(obj);
Externalizable extObj = ((Externalizable)obj);
extObj.readExternal(this);
if (readResolveMtd != null) {
try {
obj = readResolveMtd.invoke(obj);
handles.set(handle, obj);
}
catch (IllegalAccessException | InvocationTargetException e) {
throw new IOException(e);
}
}
return obj;
}
/**
* Reads serializable object.
*
* @param cls Class.
* @param mtds {@code readObject} methods.
* @param readResolveMtd {@code readResolve} method.
* @param fields class fields details.
* @return Object.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
@SuppressWarnings("ForLoopReplaceableByForEach")
Object readSerializable(Class<?> cls, List<Method> mtds, Method readResolveMtd,
OptimizedClassDescriptor.Fields fields) throws ClassNotFoundException, IOException {
Object obj;
try {
obj = GridUnsafe.allocateInstance(cls);
}
catch (InstantiationException e) {
throw new IOException(e);
}
int handle = handles.assign(obj);
for (int i = 0; i < mtds.size(); i++) {
Method mtd = mtds.get(i);
if (mtd != null) {
curObj = obj;
curFields = fields.fields(i);
try {
mtd.invoke(obj, this);
}
catch (IllegalAccessException | InvocationTargetException e) {
throw new IOException(e);
}
}
else
readFields(obj, fields.fields(i));
}
if (readResolveMtd != null) {
try {
obj = readResolveMtd.invoke(obj);
handles.set(handle, obj);
}
catch (IllegalAccessException | InvocationTargetException e) {
throw new IOException(e);
}
}
return obj;
}
/**
* Reads {@link ArrayList}.
*
* @return List.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
ArrayList<?> readArrayList() throws ClassNotFoundException, IOException {
int size = readInt();
ArrayList<Object> list = new ArrayList<>(size);
handles.assign(list);
for (int i = 0; i < size; i++)
list.add(readObject());
return list;
}
/**
* Reads {@link HashMap}.
*
* @param set Whether reading underlying map from {@link HashSet}.
* @return Map.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
HashMap<?, ?> readHashMap(boolean set) throws ClassNotFoundException, IOException {
int size = readInt();
float loadFactor = readFloat();
HashMap<Object, Object> map = new HashMap<>(size, loadFactor);
if (!set)
handles.assign(map);
for (int i = 0; i < size; i++) {
Object key = readObject();
Object val = !set ? readObject() : DUMMY;
map.put(key, val);
}
return map;
}
/**
* Reads {@link HashSet}.
*
* @param mapFieldOff Map field offset.
* @return Set.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
@SuppressWarnings("unchecked")
HashSet<?> readHashSet(long mapFieldOff) throws ClassNotFoundException, IOException {
try {
HashSet<Object> set = (HashSet<Object>)GridUnsafe.allocateInstance(HashSet.class);
handles.assign(set);
setObject(set, mapFieldOff, readHashMap(true));
return set;
}
catch (InstantiationException e) {
throw new IOException(e);
}
}
/**
* Reads {@link LinkedList}.
*
* @return List.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
LinkedList<?> readLinkedList() throws ClassNotFoundException, IOException {
int size = readInt();
LinkedList<Object> list = new LinkedList<>();
handles.assign(list);
for (int i = 0; i < size; i++)
list.add(readObject());
return list;
}
/**
* Reads {@link LinkedHashMap}.
*
* @param set Whether reading underlying map from {@link LinkedHashSet}.
* @return Map.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
LinkedHashMap<?, ?> readLinkedHashMap(boolean set) throws ClassNotFoundException, IOException {
int size = readInt();
float loadFactor = readFloat();
boolean accessOrder = readBoolean();
LinkedHashMap<Object, Object> map = new LinkedHashMap<>(size, loadFactor, accessOrder);
if (!set)
handles.assign(map);
for (int i = 0; i < size; i++) {
Object key = readObject();
Object val = !set ? readObject() : DUMMY;
map.put(key, val);
}
return map;
}
/**
* Reads {@link LinkedHashSet}.
*
* @param mapFieldOff Map field offset.
* @return Set.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
@SuppressWarnings("unchecked")
LinkedHashSet<?> readLinkedHashSet(long mapFieldOff) throws ClassNotFoundException, IOException {
try {
LinkedHashSet<Object> set = (LinkedHashSet<Object>)GridUnsafe.allocateInstance(LinkedHashSet.class);
handles.assign(set);
setObject(set, mapFieldOff, readLinkedHashMap(true));
return set;
}
catch (InstantiationException e) {
throw new IOException(e);
}
}
/**
* Reads {@link Date}.
*
* @return Date.
* @throws ClassNotFoundException If class not found.
* @throws IOException In case of error.
*/
Date readDate() throws ClassNotFoundException, IOException {
Date date = new Date(readLong());
handles.assign(date);
return date;
}
/**
* Reads array of {@code byte}s.
*
* @return Array.
* @throws IOException In case of error.
*/
byte[] readByteArray() throws IOException {
byte[] arr = in.readByteArray();
handles.assign(arr);
return arr;
}
/**
* Reads array of {@code short}s.
*
* @return Array.
* @throws IOException In case of error.
*/
short[] readShortArray() throws IOException {
short[] arr = in.readShortArray();
handles.assign(arr);
return arr;
}
/**
* Reads array of {@code int}s.
*
* @return Array.
* @throws IOException In case of error.
*/
int[] readIntArray() throws IOException {
int[] arr = in.readIntArray();
handles.assign(arr);
return arr;
}
/**
* Reads array of {@code long}s.
*
* @return Array.
* @throws IOException In case of error.
*/
long[] readLongArray() throws IOException {
long[] arr = in.readLongArray();
handles.assign(arr);
return arr;
}
/**
* Reads array of {@code float}s.
*
* @return Array.
* @throws IOException In case of error.
*/
float[] readFloatArray() throws IOException {
float[] arr = in.readFloatArray();
handles.assign(arr);
return arr;
}
/**
* Reads array of {@code double}s.
*
* @return Array.
* @throws IOException In case of error.
*/
double[] readDoubleArray() throws IOException {
double[] arr = in.readDoubleArray();
handles.assign(arr);
return arr;
}
/**
* Reads array of {@code char}s.
*
* @return Array.
* @throws IOException In case of error.
*/
char[] readCharArray() throws IOException {
char[] arr = in.readCharArray();
handles.assign(arr);
return arr;
}
/**
* Reads array of {@code boolean}s.
*
* @return Array.
* @throws IOException In case of error.
*/
boolean[] readBooleanArray() throws IOException {
boolean[] arr = in.readBooleanArray();
handles.assign(arr);
return arr;
}
/**
* Reads {@link String}.
*
* @return String.
* @throws IOException In case of error.
*/
public String readString() throws IOException {
String str = in.readUTF();
handles.assign(str);
return str;
}
/** {@inheritDoc} */
@Override public void readFully(byte[] b) throws IOException {
in.readFully(b);
}
/** {@inheritDoc} */
@Override public void readFully(byte[] b, int off, int len) throws IOException {
in.readFully(b, off, len);
}
/** {@inheritDoc} */
@Override public int skipBytes(int n) throws IOException {
return in.skipBytes(n);
}
/** {@inheritDoc} */
@Override public boolean readBoolean() throws IOException {
return in.readBoolean();
}
/** {@inheritDoc} */
@Override public byte readByte() throws IOException {
return in.readByte();
}
/** {@inheritDoc} */
@Override public int readUnsignedByte() throws IOException {
return in.readUnsignedByte();
}
/** {@inheritDoc} */
@Override public short readShort() throws IOException {
return in.readShort();
}
/** {@inheritDoc} */
@Override public int readUnsignedShort() throws IOException {
return in.readUnsignedShort();
}
/** {@inheritDoc} */
@Override public char readChar() throws IOException {
return in.readChar();
}
/** {@inheritDoc} */
@Override public int readInt() throws IOException {
return in.readInt();
}
/** {@inheritDoc} */
@Override public long readLong() throws IOException {
return in.readLong();
}
/** {@inheritDoc} */
@Override public float readFloat() throws IOException {
return in.readFloat();
}
/** {@inheritDoc} */
@Override public double readDouble() throws IOException {
return in.readDouble();
}
/** {@inheritDoc} */
@Override public int read() throws IOException {
return in.read();
}
/** {@inheritDoc} */
@Override public int read(byte[] b) throws IOException {
return in.read(b);
}
/** {@inheritDoc} */
@Override public int read(byte[] b, int off, int len) throws IOException {
return in.read(b, off, len);
}
/** {@inheritDoc} */
@SuppressWarnings("deprecation")
@Override public String readLine() throws IOException {
return in.readLine();
}
/** {@inheritDoc} */
@Override public String readUTF() throws IOException {
return in.readUTF();
}
/** {@inheritDoc} */
@Override public Object readUnshared() throws IOException, ClassNotFoundException {
return readObject();
}
/** {@inheritDoc} */
@Override public void defaultReadObject() throws IOException, ClassNotFoundException {
if (curObj == null)
throw new NotActiveException("Not in readObject() call.");
readFields(curObj, curFields);
}
/** {@inheritDoc} */
@Override public ObjectInputStream.GetField readFields() throws IOException, ClassNotFoundException {
if (curObj == null)
throw new NotActiveException("Not in readObject() call.");
return new GetFieldImpl(this);
}
/** {@inheritDoc} */
@Override public void registerValidation(ObjectInputValidation obj, int pri) {
// No-op.
}
/** {@inheritDoc} */
@Override public int available() throws IOException {
return -1;
}
/**
* Returns objects that were added to handles table.
* Used ONLY for test purposes.
*
* @return Handled objects.
*/
Object[] handledObjects() {
return handles.entries;
}
/**
* Lightweight identity hash table which maps objects to integer handles,
* assigned in ascending order.
*/
private static class HandleTable {
/** Array mapping handle -> object/exception (depending on status). */
private Object[] entries;
/** Number of handles in table. */
private int size;
/**
* Creates handle table with the given initial capacity.
*
* @param initCap Initial capacity.
*/
HandleTable(int initCap) {
entries = new Object[initCap];
}
/**
* Assigns next available handle to given object, and returns assigned
* handle.
*
* @param obj Object.
* @return Handle.
*/
int assign(Object obj) {
if (size >= entries.length)
grow();
entries[size] = obj;
return size++;
}
/**
* Assigns new object to existing handle. Old object is forgotten.
*
* @param handle Handle.
* @param obj Object.
*/
void set(int handle, Object obj) {
entries[handle] = obj;
}
/**
* Looks up and returns object associated with the given handle.
*
* @param handle Handle.
* @return Object.
*/
Object lookup(int handle) {
return entries[handle];
}
/**
* Resets table to its initial state.
*/
void clear() {
Arrays.fill(entries, 0, size, null);
size = 0;
}
/**
* Expands capacity of internal arrays.
*/
private void grow() {
int newCap = (entries.length << 1) + 1;
Object[] newEntries = new Object[newCap];
System.arraycopy(entries, 0, newEntries, 0, size);
entries = newEntries;
}
}
/**
* {@link GetField} implementation.
*/
private static class GetFieldImpl extends GetField {
/** Field info. */
private final OptimizedClassDescriptor.ClassFields fieldInfo;
/** Values. */
private final Object[] objs;
/**
* @param in Stream.
* @throws IOException In case of error.
* @throws ClassNotFoundException If class not found.
*/
@SuppressWarnings("ForLoopReplaceableByForEach")
private GetFieldImpl(OptimizedObjectInputStream in) throws IOException, ClassNotFoundException {
fieldInfo = in.curFields;
objs = new Object[fieldInfo.size()];
for (int i = 0; i < fieldInfo.size(); i++) {
OptimizedClassDescriptor.FieldInfo t = fieldInfo.get(i);
Object obj = null;
switch (t.type()) {
case BYTE:
obj = in.readByte();
break;
case SHORT:
obj = in.readShort();
break;
case INT:
obj = in.readInt();
break;
case LONG:
obj = in.readLong();
break;
case FLOAT:
obj = in.readFloat();
break;
case DOUBLE:
obj = in.readDouble();
break;
case CHAR:
obj = in.readChar();
break;
case BOOLEAN:
obj = in.readBoolean();
break;
case OTHER:
obj = in.readObject();
}
objs[i] = obj;
}
}
/** {@inheritDoc} */
@Override public ObjectStreamClass getObjectStreamClass() {
throw new UnsupportedOperationException();
}
/** {@inheritDoc} */
@Override public boolean defaulted(String name) throws IOException {
return objs[fieldInfo.getIndex(name)] == null;
}
/** {@inheritDoc} */
@Override public boolean get(String name, boolean dflt) throws IOException {
return value(name, dflt);
}
/** {@inheritDoc} */
@Override public byte get(String name, byte dflt) throws IOException {
return value(name, dflt);
}
/** {@inheritDoc} */
@Override public char get(String name, char dflt) throws IOException {
return value(name, dflt);
}
/** {@inheritDoc} */
@Override public short get(String name, short dflt) throws IOException {
return value(name, dflt);
}
/** {@inheritDoc} */
@Override public int get(String name, int dflt) throws IOException {
return value(name, dflt);
}
/** {@inheritDoc} */
@Override public long get(String name, long dflt) throws IOException {
return value(name, dflt);
}
/** {@inheritDoc} */
@Override public float get(String name, float dflt) throws IOException {
return value(name, dflt);
}
/** {@inheritDoc} */
@Override public double get(String name, double dflt) throws IOException {
return value(name, dflt);
}
/** {@inheritDoc} */
@Override public Object get(String name, Object dflt) throws IOException {
return value(name, dflt);
}
/**
* @param name Field name.
* @param dflt Default value.
* @return Value.
*/
@SuppressWarnings("unchecked")
private <T> T value(String name, T dflt) {
return objs[fieldInfo.getIndex(name)] != null ? (T)objs[fieldInfo.getIndex(name)] : dflt;
}
}
}