org.pentaho.reporting.libraries.designtime.swing.SerializedObjectContainer.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.reporting.libraries.designtime.swing.SerializedObjectContainer.java

Source

/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2017 Hitachi Vantara..  All rights reserved.
*/

package org.pentaho.reporting.libraries.designtime.swing;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.base.util.NullOutputStream;

import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class SerializedObjectContainer implements Serializable {
    public static class DebuggingObjectOutputStream extends ObjectOutputStream {

        private static final Field DEPTH_FIELD;

        static {
            try {
                DEPTH_FIELD = ObjectOutputStream.class.getDeclaredField("depth"); // NON-NLS
                DEPTH_FIELD.setAccessible(true);
            } catch (NoSuchFieldException e) {
                throw new AssertionError(e);
            }
        }

        private final List<Object> stack;

        /**
         * Indicates whether or not OOS has tried to write an IOException (presumably as the result of a serialization
         * error) to the stream.
         */
        private boolean broken;

        public DebuggingObjectOutputStream(final OutputStream out) throws IOException {
            super(out);
            broken = false;
            stack = new ArrayList<Object>();
            enableReplaceObject(true);
        }

        /**
         * Abuse {@code replaceObject()} as a hook to maintain our stack.
         */
        protected Object replaceObject(final Object o) {
            // ObjectOutputStream writes serialization
            // exceptions to the stream. Ignore
            // everything after that so we don't lose
            // the path to a non-serializable object. So
            // long as the user doesn't write an
            // IOException as the root object, we're OK.
            final int currentDepth = currentDepth();
            if (o instanceof IOException && currentDepth == 0) {
                broken = true;
            }
            if (!broken) {
                truncate(currentDepth);
                stack.add(o);
            }
            return o;
        }

        private void truncate(final int depth) {
            while (stack.size() > depth) {
                pop();
            }
        }

        private Object pop() {
            return stack.remove(stack.size() - 1);
        }

        /**
         * Returns a 0-based depth within the object graph of the current object being serialized.
         *
         * @return the current depth.
         */
        private int currentDepth() {
            try {
                final Integer oneBased = ((Integer) DEPTH_FIELD.get(this));
                return oneBased - 1;
            } catch (IllegalAccessException e) {
                throw new AssertionError(e);
            }
        }

        /**
         * Returns the path to the last object serialized. If an exception occurred, this should be the path to the
         * non-serializable object.
         *
         * @return Returns the current call stack.
         */
        public List<Object> getStack() {
            return stack;
        }
    }

    private static final Log logger = LogFactory.getLog(SerializedObjectContainer.class);
    private transient Object[] data;

    public SerializedObjectContainer(final Object[] data) {
        if (data == null) {
            throw new NullPointerException();
        }
        this.data = data.clone();

        // some paranoid checks ..
        try {
            final DebuggingObjectOutputStream objectOutputStream = new DebuggingObjectOutputStream(
                    new NullOutputStream());
            try {
                objectOutputStream.writeObject(this);
            } catch (NotSerializableException nse) {
                logger.debug("Non-Serializable object found @Path: " + objectOutputStream.getStack()); // NON-NLS
                throw nse;
            }
        } catch (IOException e) {
            throw new IllegalStateException("Object is not serializable.");
        }
    }

    public Object[] getData() {
        return data.clone();
    }

    private void writeObject(final ObjectOutputStream stream) throws IOException {
        stream.defaultWriteObject();
        final int count = data.length;
        stream.writeObject(data.getClass().getComponentType());
        stream.writeInt(count);
        for (int i = 0; i < count; i++) {
            final Object object = data[i];
            if (object != null && object instanceof Serializable) {
                stream.writeInt(i);
                stream.writeObject(object);
            } else {
                stream.writeInt(-1);
            }
        }
    }

    private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
        stream.defaultReadObject();
        final Class arrayType = (Class) stream.readObject();
        final int count = stream.readInt();
        data = (Object[]) Array.newInstance(arrayType, count);
        for (int i = 0; i < count; i++) {
            final int index = stream.readInt();
            if (index != -1) {
                data[i] = stream.readObject();
            }
        }
    }
}