All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.clementlevallois.utils.DebuggingOutputStream Maven / Gradle / Ivy

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package net.clementlevallois.utils;

/**
 *
 * @author LEVALLOIS
 */
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.ArrayList;

/**
 *
 * @author LEVALLOIS
 */
public class DebuggingOutputStream
        extends ObjectOutputStream {

    private static final Field DEPTH_FIELD;

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

    final List stack
            = new ArrayList();

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

    /**
     *
     * @param out
     * @throws IOException
     */
    public DebuggingOutputStream(OutputStream out) throws IOException {
        super(out);
        enableReplaceObject(true);
    }

    /**
     * Abuse {@code replaceObject()} as a hook to maintain our stack.
     * @param o
     */
    protected Object replaceObject(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.
        int currentDepth = currentDepth();
        if (o instanceof IOException
                && currentDepth == 0) {
            broken = true;
        }
        if (!broken) {
            truncate(currentDepth);
            stack.add(o);
        }
        return o;
    }

    private void truncate(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.
     */
    private int currentDepth() {
        try {
            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 
     */
    public List getStack() {
        return stack;
    }
}