Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * JBoss, Home of Professional Open Source.
   * Copyright 2014 Red Hat, Inc., and individual contributors
   * as indicated by the @author tags.
   *
   * 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 org.jboss.marshalling.cloner;
 
 import java.io.File;
 import java.util.Map;
 import java.util.Set;
An object cloner which uses serialization methods to clone objects.
 
 class SerializingCloner implements ObjectCloner {
     private final CloneTable delegate;
     private final ObjectResolver objectResolver;
     private final ObjectResolver objectPreResolver;
     private final ClassCloner classCloner;
     private final int bufferSize;
 
     private final SerializableClassRegistry registry;

    
Create a new instance.

Parameters:
configuration the configuration to use
 
     SerializingCloner(final ClonerConfiguration configuration) {
        final CloneTable delegate = configuration.getCloneTable();
        this. = delegate == null ? . : delegate;
        final ObjectResolver objectResolver = configuration.getObjectResolver();
        this. = objectResolver == null ? Marshalling.nullObjectResolver() : objectResolver;
        final ObjectResolver objectPreResolver = configuration.getObjectPreResolver();
        this. = objectPreResolver == null ? Marshalling.nullObjectResolver() : objectPreResolver;
        final ClassCloner classCloner = configuration.getClassCloner();
        this. = classCloner == null ? . : classCloner;
        final SerializabilityChecker serializabilityChecker = configuration.getSerializabilityChecker();
        this. = serializabilityChecker == null ? . : serializabilityChecker;
        final int bufferSize = configuration.getBufferSize();
        this. = bufferSize < 1 ? 8192 : bufferSize;
         = SerializableClassRegistry.getInstance();
    }
    private final IdentityHashMap<ObjectObjectclones = new IdentityHashMap<ObjectObject>();
    public void reset() {
        synchronized (this) {
            .clear();
        }
    }
    public Object clone(final Object origthrows IOExceptionClassNotFoundException {
        synchronized (this) {
            boolean ok = false;
            try {
                final Object clone = clone(origtrue);
                ok = true;
                return clone;
            } finally {
                if (! ok) {
                    reset();
                }
            }
        }
    }
    private Object clone(final Object origfinal boolean replacethrows IOExceptionClassNotFoundException {
        if (orig == null) {
            return null;
        }
        if (Thread.currentThread().isInterrupted()) {
            throw new InterruptedIOException("Thread interrupted during cloning process");
        }
        final IdentityHashMap<ObjectObjectclones = this.;
        Object cached = clones.get(orig);
        if (cached != null) {
            return cached;
        }
        Object replaced = orig;
        replaced = .writeReplace(replaced);
        final ClassCloner classCloner = this.;
        if (replaced instanceof Class) {
            final Class<?> classObj = (Class<?>) replaced;
            final Class<?> clonedClass = Proxy.isProxyClass(classObj) ? classCloner.cloneProxy(classObj) : classCloner.clone(classObj);
            clones.put(replacedclonedClass);
            return clonedClass;
        }
        if ((cached = .clone(replacedthisclassCloner)) != null) {
            clones.put(replacedcached);
            return cached;
        }
        final Class<?> objClass = replaced.getClass();
        final SerializableClass info = .lookup(objClass);
        if (replace) {
            if (info.hasWriteReplace()) {
                replaced = info.callWriteReplace(replaced);
            }
            replaced = .writeReplace(replaced);
            if (replaced != orig) {
                Object clone = clone(replacedfalse);
                clones.put(origclone);
                return clone;
            }
        }
        if (orig instanceof Enum) {
            @SuppressWarnings("unchecked")
            final Class<? extends EnumcloneClass = ((Class<?>)clone(objClass)).asSubclass(Enum.class);
            if (cloneClass == objClass) {
                // same class means same enum constants
                return orig;
            } else {
                @SuppressWarnings("unchecked")
                final Class<? extends EnumcloneEnumClass;
                //the actual object class may be a sub class of the enum class
                final Class<?> enumClass = ((Enum<?>) orig).getDeclaringClass();
                if(enumClass == objClass) {
                    cloneEnumClass = cloneClass;
                } else{
                    cloneEnumClass = ((Class<?>)clone(enumClass)).asSubclass(Enum.class);
                }
                return Enum.valueOf(cloneEnumClass, ((Enum<?>) orig).name());
            }
        }
        final Class<?> clonedClass = (Class<?>) clone(objClass);
        if (Proxy.isProxyClass(objClass)) {
            return Proxy.newProxyInstance(clonedClass.getClassLoader(), clonedClass.getInterfaces(), (InvocationHandlerclone(getInvocationHandler(orig)));
        }
        if (.contains(objClass)) {
            return orig;
        }
        final boolean sameClass = objClass == clonedClass;
        if (objClass.isArray()) {
            Object simpleClone = simpleClone(origobjClass);
            if (simpleClone != nullreturn simpleClone;
            // must be an object array
            final Object[] origArray = (Object[]) orig;
            final int len = origArray.length;
            if (sameClass && len == 0) {
                clones.put(origorig);
                return orig;
            }
            if (.contains(objClass.getComponentType())) {
                final Object[] clone = origArray.clone();
                clones.put(origclone);
                return clone;
            }
            final Object[] clone;
            if (sameClass) {
                clone = origArray.clone();
            } else {
                clone = (Object[])Array.newInstance(clonedClass.getComponentType(), len);
            }
            // deep clone
            clones.put(origclone);
            for (int i = 0; i < leni++) {
                clone[i] = clone(origArray[i]);
            }
            return clone;
        }
        final SerializableClass cloneInfo = sameClass ? info : .lookup(clonedClass);
        // Now check the serializable types
        final Object clone;
        if (orig instanceof Externalizable) {
            final Externalizable externalizable = (Externalizableorig;
            clone = cloneInfo.callNoArgConstructor();
            clones.put(origclone);
            final Queue<Stepsteps = new ArrayDeque<Step>();
            final StepObjectOutput soo = new StepObjectOutput(steps);
            externalizable.writeExternal(soo);
            soo.doFinish();
            ((Externalizableclone).readExternal(new StepObjectInput(steps));
        } else if (.isSerializable(objClass)) {
            clone = cloneInfo.callNonInitConstructor();
            if (! (.isSerializable(clonedClass))) {
                throw new NotSerializableException(clonedClass.getName());
            }
            clones.put(origclone);
            initSerializableClone(originfoclonecloneInfo);
        } else {
            throw new NotSerializableException(objClass.getName());
        }
        replaced = clone;
        if (cloneInfo.hasReadResolve()) {
            replaced = cloneInfo.callReadResolve(replaced);
        }
        replaced = .readResolve(.readResolve(replaced));
        if (replaced != cloneclones.put(origreplaced);
        return replaced;
    }
    private void initSerializableClone(final Object origfinal SerializableClass origInfofinal Object clonefinal SerializableClass cloneInfothrows IOExceptionClassNotFoundException {
        final Class<?> cloneClass = cloneInfo.getSubjectClass();
        if (! .isSerializable(cloneClass)) {
            throw new NotSerializableException(cloneClass.getName());
        }
        final Class<?> cloneSuperClass = cloneClass.getSuperclass();
        final Class<?> origClass = origInfo.getSubjectClass();
        // first, init the serializable superclass, if any
        final Class<?> origSuperClass = origClass.getSuperclass();
        if (.isSerializable(origSuperClass) || .isSerializable(cloneSuperClass)) {
            initSerializableClone(orig.lookup(origSuperClass), clone.lookup(cloneSuperClass));
        }
        if (cloneClass != origClass && cloneClass != clone(origClass)) {
            if (cloneInfo.hasReadObjectNoData()) {
                cloneInfo.callReadObjectNoData(clone);
            }
            return;
        }
        
        if (! .isSerializable(origClass)) {
            if (cloneInfo.hasReadObjectNoData()) {
                cloneInfo.callReadObjectNoData(clone);
            }
            return;
        }
        final ClonerPutField fields = new ClonerPutField();
        fields.defineFields(origInfo);
        if (origInfo.hasWriteObject()) {
            final Queue<Stepsteps = new ArrayDeque<Step>();
            final StepObjectOutputStream stepObjectOutputStream = createStepObjectOutputStream(origfieldssteps);
            origInfo.callWriteObject(origstepObjectOutputStream);
            stepObjectOutputStream.flush();
            stepObjectOutputStream.doFinish();
            prepareFields(origfields);
            cloneFields(fields);
            if (cloneInfo.hasReadObject()) {
                cloneInfo.callReadObject(clonecreateStepObjectInputStream(clonecloneInfofieldssteps));
            } else {
                storeFields(cloneInfoclonefields);
            }
        } else {
            prepareFields(origfields);
            cloneFields(fields);
            if (cloneInfo.hasReadObject()) {
                cloneInfo.callReadObject(clonecreateStepObjectInputStream(clonecloneInfofieldsnew ArrayDeque<Step>()));
            } else {
                storeFields(cloneInfoclonefields);
            }
        }
    }
    private StepObjectInputStream createStepObjectInputStream(final Object clonefinal SerializableClass cloneInfofinal ClonerPutField fieldsfinal Queue<Stepstepsthrows IOException {
        try {
            return System.getSecurityManager() == null ? new StepObjectInputStream(stepsfieldsclonecloneInfo) : AccessController.doPrivileged(new PrivilegedExceptionAction<StepObjectInputStream>() {
                public StepObjectInputStream run() throws Exception {
                    return new StepObjectInputStream(stepsfieldsclonecloneInfo);
                }
            });
        } catch (PrivilegedActionException e) {
            try {
                throw e.getCause();
            } catch (IOException ioe) {
                throw ioe;
            } catch (RuntimeException re) {
                throw re;
            } catch (Error er) {
                throw er;
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    }
    private StepObjectOutputStream createStepObjectOutputStream(final Object origfinal ClonerPutField fieldsfinal Queue<Stepstepsthrows IOException {
        try {
            return System.getSecurityManager() == null ? new StepObjectOutputStream(stepsfieldsorig) : AccessController.doPrivileged(new PrivilegedExceptionAction<StepObjectOutputStream>() {
                public StepObjectOutputStream run() throws IOException {
                    return new StepObjectOutputStream(stepsfieldsorig);
                }
            });
        } catch (PrivilegedActionException e) {
            try {
                throw e.getCause();
            } catch (IOException ioe) {
                throw ioe;
            } catch (RuntimeException re) {
                throw re;
            } catch (Error er) {
                throw er;
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    }
    private void prepareFields(final Object subjectfinal ClonerPutField fieldsthrows InvalidObjectException {
        final Map<StringSerializableFielddefMap = fields.fieldDefMap;
        final Map<StringReadFieldmap = fields.fieldMap;
        try {
            for (String name : defMap.keySet()) {
                final SerializableField serializableField = defMap.get(name);
                final Field realField = serializableField.getField();
                if (realField != nullswitch (serializableField.getKind()) {
                    case map.put(namenew BooleanReadField(serializableFieldrealField.getBoolean(subject))); continue;
                    case :    map.put(namenew ByteReadField(serializableFieldrealField.getByte(subject))); continue;
                    case :    map.put(namenew CharReadField(serializableFieldrealField.getChar(subject))); continue;
                    case :  map.put(namenew DoubleReadField(serializableFieldrealField.getDouble(subject))); continue;
                    case :   map.put(namenew FloatReadField(serializableFieldrealField.getFloat(subject))); continue;
                    case :     map.put(namenew IntReadField(serializableFieldrealField.getInt(subject))); continue;
                    case :    map.put(namenew LongReadField(serializableFieldrealField.getLong(subject))); continue;
                    case :  map.put(namenew ObjectReadField(serializableFieldrealField.get(subject))); continue;
                    case :   map.put(namenew ShortReadField(serializableFieldrealField.getShort(subject))); continue;
                    defaultthrow new IllegalStateException();
                }
            }
        } catch (IllegalAccessException e) {
            throw new InvalidObjectException("Cannot access write field: " + e);
        }
    }
    private void cloneFields(final ClonerPutField fieldsthrows IOExceptionClassNotFoundException {
        final Map<StringSerializableFielddefMap = fields.fieldDefMap;
        final Map<StringReadFieldmap = fields.fieldMap;
        for (String name : defMap.keySet()) {
            final SerializableField field = defMap.get(name);
            if (field.getKind() == .) {
                map.put(namenew ObjectReadField(fieldclone(map.get(name).getObject())));
                continue;
            }
        }
    }
    private void storeFields(final SerializableClass cloneInfofinal Object clonefinal ClonerPutField fieldsthrows IOException {
        final Map<StringReadFieldmap = fields.fieldMap;
        try {
            for (SerializableField cloneField : cloneInfo.getFields()) {
                final String name = cloneField.getName();
                final ReadField field = map.get(name);
                final Field realField = cloneField.getField();
                if (realField != nullswitch (cloneField.getKind()) {
                    case realField.setBoolean(clonefield == null ? false : field.getBoolean()); continue;
                    case :    realField.setByte(clonefield == null ? 0 : field.getByte()); continue;
                    case :    realField.setChar(clonefield == null ? 0 : field.getChar()); continue;
                    case :  realField.setDouble(clonefield == null ? 0 : field.getDouble()); continue;
                    case :   realField.setFloat(clonefield == null ? 0 : field.getFloat()); continue;
                    case :     realField.setInt(clonefield == null ? 0 : field.getInt()); continue;
                    case :    realField.setLong(clonefield == null ? 0 : field.getLong()); continue;
                    case :  realField.set(clonefield == null ? null : field.getObject()); continue;
                    case :   realField.setShort(clonefield == null ? 0 : field.getShort()); continue;
                    defaultthrow new IllegalStateException();
                }
            }
        } catch (IllegalAccessException e) {
            throw new InvalidObjectException("Cannot access write field: " + e);
        }
    }
    private static Object simpleClone(final Object origfinal Class<?> objClass) {
        final int idx = .get(objClass, -1);
        switch (idx) {
            case 0: {
                final boolean[] booleans = (boolean[]) orig;
                return booleans.length == 0 ? orig : booleans.clone();
            }
            case 1: {
                final byte[] bytes = (byte[]) orig;
                return bytes.length == 0 ? orig : bytes.clone();
            }
            case 2: {
                final short[] shorts = (short[]) orig;
                return shorts.length == 0 ? orig : shorts.clone();
            }
            case 3: {
                final int[] ints = (int[]) orig;
                return ints.length == 0 ? orig : ints.clone();
            }
            case 4: {
                final long[] longs = (long[]) orig;
                return longs.length == 0 ? orig : longs.clone();
            }
            case 5: {
                final float[] floats = (float[]) orig;
                return floats.length == 0 ? orig : floats.clone();
            }
            case 6: {
                final double[] doubles = (double[]) orig;
                return doubles.length == 0 ? orig : doubles.clone();
            }
            case 7: {
                final char[] chars = (char[]) orig;
                return chars.length == 0 ? orig : chars.clone();
            }
            defaultreturn null// fall out
        }
    }
    private static InvocationHandler getInvocationHandler(final Object orig) {
        try {
            return (InvocationHandler.get(orig);
        } catch (IllegalAccessException e) {
            throw new IllegalAccessError(e.getMessage());
        }
    }
    private abstract static class Step {
    }
    private static final Set<Class<?>> UNCLONED;
    private static final IdentityIntMap<Class<?>> PRIMITIVE_ARRAYS;
    private static final Field proxyInvocationHandler;
    static {
        final Set<Class<?>> set = new HashSet<Class<?>>();
        // List of final, immutable, serializable JDK classes with no external references to mutable items
        // These items can be sent back by reference and the caller will never know
        set.add(Boolean.class);
        set.add(Byte.class);
        set.add(Short.class);
        set.add(Integer.class);
        set.add(Long.class);
        set.add(Float.class);
        set.add(Double.class);
        set.add(Character.class);
        set.add(String.class);
        set.add(StackTraceElement.class);
        set.add(BigInteger.class);
        set.add(BigDecimal.class);
        set.add(Pattern.class);
        set.add(File.class);
        set.add(Collections.emptyList().getClass());
        set.add(Collections.emptySet().getClass());
        set.add(Collections.emptyMap().getClass());
         = set;
        final IdentityIntMap<Class<?>> map = new IdentityIntMap<Class<?>>();
        // List of cloneable, non-extensible, serializable JDK classes with no external references to mutable items
        // These items can be deeply cloned by a single method call, without worrying about the classloader
        map.put(boolean[].class, 0);
        map.put(byte[].class, 1);
        map.put(short[].class, 2);
        map.put(int[].class, 3);
        map.put(long[].class, 4);
        map.put(float[].class, 5);
        map.put(double[].class, 6);
        map.put(char[].class, 7);
         = map;
         = AccessController.doPrivileged(new PrivilegedAction<Field>() {
            public Field run() {
                try {
                    final Field field = Proxy.class.getDeclaredField("h");
                    field.setAccessible(true);
                    return field;
                } catch (NoSuchFieldException e) {
                    throw new NoSuchFieldError(e.getMessage());
                }
            }
        });
    }
    class StepObjectOutput extends AbstractObjectOutput implements Marshaller {
        private final Queue<Stepsteps;
        private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        StepObjectOutput(final Queue<Stepstepsthrows IOException {
            super(SerializingCloner.this.);
            this. = steps;
            super.start(Marshalling.createByteOutput());
        }
        protected void doWriteObject(final Object objfinal boolean unsharedthrows IOException {
            super.flush();
            final ByteArrayOutputStream baos = ;
            if (baos.size() > 0) {
                .add(new ByteDataStep(baos.toByteArray()));
                baos.reset();
            }
            .add(new CloneStep(obj));
        }
        public void clearInstanceCache() throws IOException {
            throw new UnsupportedOperationException();
        }
        public void clearClassCache() throws IOException {
            throw new UnsupportedOperationException();
        }
        public void start(final ByteOutput byteOutputthrows IOException {
            throw new UnsupportedOperationException();
        }
        public void finish() throws IOException {
            throw new UnsupportedOperationException();
        }
        void doFinish() throws IOException {
            super.finish();
        }
        public void flush() throws IOException {
            super.flush();
            final ByteArrayOutputStream baos = ;
            if (baos.size() > 0) {
                .add(new ByteDataStep(baos.toByteArray()));
                baos.reset();
            }
        }
    }
        private final Queue<Stepsteps;
        private final ClonerPutField clonerPutField;
        private final Object subject;
        private final StepObjectOutput output;
        private StepObjectOutputStream(StepObjectOutput outputfinal Queue<Stepstepsfinal ClonerPutField clonerPutFieldfinal Object subjectthrows IOException {
            super(output);
            this. = output;
            this. = steps;
            this. = clonerPutField;
            this. = subject;
        }
        StepObjectOutputStream(final Queue<Stepstepsfinal ClonerPutField clonerPutFieldfinal Object subjectthrows IOException {
            this(new StepObjectOutput(steps), stepsclonerPutFieldsubject);
        }
        public void writeFields() throws IOException {
            if (! .isEmpty()) {
                throw new IllegalStateException("writeFields may not be called in this context");
            }
        }
        public PutField putFields() throws IOException {
            if (! .isEmpty()) {
                throw new IllegalStateException("putFields may not be called in this context");
            }
            return ;
        }
        public void defaultWriteObject() throws IOException {
            if (! .isEmpty()) {
                throw new IllegalStateException("defaultWriteObject may not be called in this context");
            }
            final Object subject = this.;
            final SerializingCloner.ClonerPutField fields = ;
            prepareFields(subjectfields);
        }
        void doFinish() throws IOException {
            .doFinish();
        }
    }
        private final ClonerPutField clonerPutField;
        private final Object clone;
        private final SerializableClass cloneInfo;
        StepObjectInputStream(final Queue<Stepstepsfinal ClonerPutField clonerPutFieldfinal Object clonefinal SerializableClass cloneInfothrows IOException {
            super(new StepObjectInput(steps));
            this. = clonerPutField;
            this. = clone;
            this. = cloneInfo;
        }
        public void defaultReadObject() throws IOExceptionClassNotFoundException {
            storeFields();
        }
        public GetField readFields() throws IOExceptionClassNotFoundException {
            return new GetField() {
                public ObjectStreamClass getObjectStreamClass() {
                    throw new UnsupportedOperationException();
                }
                public boolean defaulted(final String namethrows IOException {
                    final ReadField field = ..get(name);
                    return field == null || field.isDefaulted();
                }
                public boolean get(final String namefinal boolean valthrows IOException {
                    final ReadField field = ..get(name);
                    return field == null || field.isDefaulted() ? val : field.getBoolean();
                }
                public byte get(final String namefinal byte valthrows IOException {
                    final ReadField field = ..get(name);
                    return field == null || field.isDefaulted() ? val : field.getByte();
                }
                public char get(final String namefinal char valthrows IOException {
                    final ReadField field = ..get(name);
                    return field == null || field.isDefaulted() ? val : field.getChar();
                }
                public short get(final String namefinal short valthrows IOException {
                    final ReadField field = ..get(name);
                    return field == null || field.isDefaulted() ? val : field.getShort();
                }
                public int get(final String namefinal int valthrows IOException {
                    final ReadField field = ..get(name);
                    return field == null || field.isDefaulted() ? val : field.getInt();
                }
                public long get(final String namefinal long valthrows IOException {
                    final ReadField field = ..get(name);
                    return field == null || field.isDefaulted() ? val : field.getLong();
                }
                public float get(final String namefinal float valthrows IOException {
                    final ReadField field = ..get(name);
                    return field == null || field.isDefaulted() ? val : field.getFloat();
                }
                public double get(final String namefinal double valthrows IOException {
                    final ReadField field = ..get(name);
                    return field == null || field.isDefaulted() ? val : field.getDouble();
                }
                public Object get(final String namefinal Object valthrows IOException {
                    final ReadField field = ..get(name);
                    return field == null || field.isDefaulted() ? val : field.getObject();
                }
            };
        }
        public void registerValidation(final ObjectInputValidation objfinal int prioritythrows NotActiveExceptionInvalidObjectException {
        }
    }
    class ClonerPutField extends ObjectOutputStream.PutField {
        private final Map<StringSerializableFieldfieldDefMap = new HashMap<StringSerializableField>();
        private final Map<StringReadFieldfieldMap = new HashMap<StringReadField>();
        private SerializableField getField(final String namefinal Kind kind) {
            final SerializableField field = .get(name);
            if (field == null) {
                throw new IllegalArgumentException("No field named '" + name + "' could be found");
            }
            if (field.getKind() != kind) {
                throw new IllegalArgumentException("Field '" + name + "' is the wrong type (expected " + kind + ", got " + field.getKind() + ")");
            }
            return field;
        }
        private void defineFields(final SerializableClass clazz) {
            for (SerializableField field : clazz.getFields()) {
                .put(field.getName(), field);
            }
        }
        public void put(final String namefinal boolean val) {
            .put(namenew BooleanReadField(getField(name.), val));
        }
        public void put(final String namefinal byte val) {
            .put(namenew ByteReadField(getField(name.), val));
        }
        public void put(final String namefinal char val) {
            .put(namenew CharReadField(getField(name.), val));
        }
        public void put(final String namefinal short val) {
            .put(namenew ShortReadField(getField(name.), val));
        }
        public void put(final String namefinal int val) {
            .put(namenew IntReadField(getField(name.), val));
        }
        public void put(final String namefinal long val) {
            .put(namenew LongReadField(getField(name.), val));
        }
        public void put(final String namefinal float val) {
            .put(namenew FloatReadField(getField(name.), val));
        }
        public void put(final String namefinal double val) {
            .put(namenew DoubleReadField(getField(name.), val));
        }
        public void put(final String namefinal Object val) {
            .put(namenew ObjectReadField(getField(name.), val));
        }
        @Deprecated
        public void write(final ObjectOutput outthrows IOException {
            throw new UnsupportedOperationException();
        }
    }
    class StepObjectInput extends AbstractObjectInput implements Unmarshaller {
        private final Queue<Stepsteps;
        private Step current;
        private int idx;
        StepObjectInput(final Queue<Stepstepsthrows IOException {
            super();
            this. = steps;
             = steps.poll();
            super.start(new ByteInput() {
                public int read() throws IOException {
                    while (current != null) {
                        if (current instanceof ByteDataStep) {
                            final ByteDataStep step = (ByteDataStep) current;
                            final byte[] bytes = step.getBytes();
                            if (idx == bytes.length) {
                                current = steps.poll();
                                idx = 0;
                            } else {
                                final byte b = bytes[idx++];
                                return b & 0xff;
                            }
                        } else {
                            // an object is pending
                            return -1;
                        }
                    }
                    return -1;
                }
                public int read(final byte[] b) throws IOException {
                    return read(b, 0, b.length);
                }
                public int read(final byte[] b, int off, int len) throws IOException {
                    if (len == 0) return 0;
                    int t = 0;
                    while (current != null && len > 0) {
                        if (current instanceof ByteDataStep) {
                            final ByteDataStep step = (ByteDataStep) current;
                            final byte[] bytes = step.getBytes();
                            final int blen = bytes.length;
                            if (idx == blen) {
                                current = steps.poll();
                                idx = 0;
                            } else {
                                final int c = Math.min(blen - idx, len);
                                System.arraycopy(bytes, idx, b, off, c);
                                idx += c;
                                off += c;
                                len -= c;
                                t += c;
                                if (idx == blen) {
                                    current = steps.poll();
                                    idx = 0;
                                }
                            }
                        } else {
                            // an object is pending
                            return t == 0 ? -1 : t;
                        }
                    }
                    return t == 0 ? -1 : t;
                }
                public int available() throws IOException {
                    return current instanceof ByteDataStep ? ((ByteDataStep) current).getBytes().length - idx : 0;
                }
                public long skip(long n) throws IOException {
                    long t = 0;
                    while (current != null && n > 0) {
                        if (current instanceof ByteDataStep) {
                            final ByteDataStep step = (ByteDataStep) current;
                            final byte[] bytes = step.getBytes();
                            final int blen = bytes.length;
                            if (idx == blen) {
                                current = steps.poll();
                                idx = 0;
                            } else {
                                final int c = (int) Math.min((long) blen - idx, n);
                                idx += c;
                                n -= c;
                                if (idx == blen) {
                                    current = steps.poll();
                                    idx = 0;
                                }
                            }
                        } else {
                            // an object is pending
                            return t;
                        }
                    }
                    return t;
                }
                public void close() throws IOException {
                    current = null;
                }
            });
        }
        protected Object doReadObject(final boolean unsharedthrows ClassNotFoundExceptionIOException {
            Step step = ;
            while (step instanceof ByteDataStep) {
                step = .poll();
            }
            if (step == null) {
                 = null;
                throw new EOFException();
            }
             = .poll();
            // not really true, just IDEA being silly again
            @SuppressWarnings("UnnecessaryThis")
            final Object clone = SerializingCloner.this.clone(((CloneStepstep).getOrig());
            return clone;
        }
        public void finish() throws IOException {
            throw new UnsupportedOperationException();
        }
        public void start(final ByteInput byteInputthrows IOException {
            throw new UnsupportedOperationException();
        }
        public void clearInstanceCache() throws IOException {
            throw new UnsupportedOperationException();
        }
        public void clearClassCache() throws IOException {
            throw new UnsupportedOperationException();
        }
    }
    private static final class ByteDataStep extends Step {
        private final byte[] bytes;
        private ByteDataStep(final byte[] bytes) {
            this. = bytes;
        }
        byte[] getBytes() {
            return ;
        }
    }
    private static final class CloneStep extends Step {
        private final Object orig;
        private CloneStep(final Object orig) {
            this. = orig;
        }
        Object getOrig() {
            return ;
        }
    }