/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2010-2012, Open Source Geospatial Foundation (OSGeo) * (C) 2010-2012, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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. */ package org.geotoolkit.internal.image.io; import java.util.Arrays; import java.io.IOException; import javax.imageio.stream.ImageInputStream; import org.geotoolkit.lang.Debug; import org.geotoolkit.lang.Decorator; import org.apache.sis.util.logging.Logging; /** * An image input stream which checks if the stream has been closed. The check is performed * at garbage-collection time. This class is used only for debugging purpose, as a way to * trace the place where the input stream may not have been properly closed. * * @author Martin Desruisseaux (Geomatys) * @version 3.14 * * @since 3.14 * @module */ @Debug @Decorator(ImageInputStream.class) public final class CheckedImageInputStream extends ImageInputStreamProxy { /** * {@code true} if {@link #close()} has been invoked. */ private volatile boolean isClosed; /** * The creator of this input stream, used in order to format a log message if needed. */ private final StackTraceElement[] creator; /** * Creates a new instance wrapping the given stream. * * @param in The image input stream to wrap. */ private CheckedImageInputStream(final ImageInputStream in) { super(in); StackTraceElement[] creator = Thread.currentThread().getStackTrace(); for (int i=0; i<creator.length; i++) { final String c = creator[i].getClassName(); if (!c.equals("java.lang.Thread") && !c.equals("org.geotoolkit.internal.image.io.CheckedImageInputStream")) { creator = Arrays.copyOfRange(creator, i, creator.length); break; } } this.creator = creator; } /** * Creates a new instance wrapping the given stream, or returns {@code null} * if the given instance was null. * * @param in The image input stream to wrap, or {@code null}. * @return The wrapped image input stream, or {@code null}. */ public static ImageInputStream wrap(ImageInputStream in) { if (in != null && !(in instanceof CheckedImageInputStream)) { in = new CheckedImageInputStream(in); } return in; } /** * Returns {@code true} if the given input stream is null, or is a valid * {@link CheckedImageInputStream}. This method is used only for assertions purpose. * * @param in The input stream to check. * @return {@code true} if the given input stream is valid. */ public static boolean isValid(final ImageInputStream in) { return (in == null) || (in instanceof CheckedImageInputStream && !((CheckedImageInputStream) in).isClosed); } /** * Closes the stream. */ @Override public void close() throws IOException { isClosed = true; super.close(); } /** * Checks if the input stream has been closed. */ @Override protected void finalize() { if (!isClosed) { final StringBuilder buffer = new StringBuilder("ImageInputStream created below has not been closed:\n"); for (final StackTraceElement element : creator) { buffer.append(" ").append(element).append('\n'); } Logging.getLogger("org.geotoolkit.image.io").warning(buffer.toString()); } } }