/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2008-2012, Open Source Geospatial Foundation (OSGeo) * (C) 2009-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.image.io.mosaic; import java.util.Set; import java.util.Collection; import java.util.LinkedHashSet; import java.util.NoSuchElementException; import java.util.Objects; import java.io.IOException; import java.io.PrintStream; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import javax.imageio.spi.ImageReaderSpi; import org.geotoolkit.coverage.grid.ImageGeometry; /** * Redirects all method calls to two {@linkplain TileManager tile managers} and compares their * results. This is used during testings and assertions only. For this reasons, comparison * failures throw {@link AssertionError}. * * @author Martin Desruisseaux (Geomatys) * @version 3.02 * * @since 2.5 * @module */ final class ComparedTileManager extends TileManager { /** * For cross-version compatibility during serialization. */ private static final long serialVersionUID = -6028433158360279586L; /** * The tile managers to compare. */ final TileManager first, second; /** * Creates a comparator the grid and tree tile managers for the given tiles. */ ComparedTileManager(final Tile[] tiles) throws IOException { second = new GridTileManager(tiles); // Must be created before TreeTileManager. first = new TreeTileManager(tiles); } /** * Creates a comparator between the given tile managers. */ ComparedTileManager(final TileManager first, final TileManager second) { this.first = first; this.second = second; } /** * Ensures that the given objects are equals. Throws an {@link AssertionError} * on failure since this class is used for testing purpose and assertions only. * * @param o1 The first object to compare. * @param o2 The second object to compare. * @return The first object if both are equal. * @throws AssertionError if the given objects are not equal. */ private <T> T assertEqual(final T o1, final T o2) throws AssertionError { if (!Objects.equals(o1, o2)) { throw new AssertionError( first.getClass().getSimpleName() + '=' + o1 + ", " + second.getClass().getSimpleName() + '=' + o2); } return o1; } /** * Verifies that the given set equals. If they are not, the differences and printed * to the {@linkplain System#err standard error stream}. */ private boolean equal(final Collection<Tile> c1, final Collection<Tile> c2) { return equals(first, c1, c2) & equals(second, c2, c1); // NOSONAR: Really "&", not "&&". } /** * Implementation helper for {@link #equal}. */ private static boolean equals(TileManager manager, Collection<Tile> c1, Collection<Tile> c2) { final Set<Tile> remainding = new LinkedHashSet<>(c1); /* * Following loop is equivalent to remainding.removeAll(c2), but we do not want to invoke * the later because the default AbstractSet.removeAll(Collection) implementation invokes * c2.contains(Tile) repetively for every elements in c1. This is efficient when c2 is a * Set. But since our c2 collection is often an List, this leads to O(n²) computation cost. */ for (final Tile tile : c2) { remainding.remove(tile); } if (remainding.isEmpty()) { return true; } final PrintStream err = System.err; err.print("Additional tiles from "); err.print(manager.getClass().getSimpleName()); err.println(':'); err.println(Tile.toString(remainding, 100)); /* * We limit the formatting to a small number of tiles (100) because this method is * expected to be invoked only with small collections. Futhermore we don't want to * flood stderr because the difference are printed without stopping the program. */ return false; } /** * {@inheritDoc} */ @Override public void setGridToCRS(AffineTransform gridToCRS) throws IllegalStateException, IOException { first .setGridToCRS(gridToCRS); second.setGridToCRS(gridToCRS); } /** * {@inheritDoc} */ @Override void setGridGeometry(final ImageGeometry geometry) { first .setGridGeometry(geometry); second.setGridGeometry(geometry); } /** * {@inheritDoc} */ @Override public ImageGeometry getGridGeometry() throws IOException { return assertEqual(first.getGridGeometry(), second.getGridGeometry()); } /** * {@inheritDoc} */ @Override Rectangle getRegion() throws IOException { return assertEqual(first.getRegion(), second.getRegion()); } /** * {@inheritDoc} */ @Override Dimension getTileSize() throws IOException { return assertEqual(first.getTileSize(), second.getTileSize()); } /** * {@inheritDoc} */ @Override boolean isImageTiled() throws IOException { return assertEqual(first.isImageTiled(), second.isImageTiled()); } /** * {@inheritDoc} */ @Override public Set<ImageReaderSpi> getImageReaderSpis() throws IOException { return assertEqual(first.getImageReaderSpis(), second.getImageReaderSpis()); } /** * {@inheritDoc} */ @Override public Tile createGlobalTile(ImageReaderSpi provider, Object input, int imageIndex) throws NoSuchElementException, IOException { return assertEqual(first.createGlobalTile(provider, input, imageIndex), second.createGlobalTile(provider, input, imageIndex)); } /** * {@inheritDoc} */ @Override public Collection<Tile> getTiles() throws IOException { final Collection<Tile> tiles = first.getTiles(); if (!equal(tiles, second.getTiles())) { // We don't use the assert statement because we want the above line // to be executed in all cases, since it prints mismatchs as warnings. throw new AssertionError(); } return tiles; } /** * {@inheritDoc} */ @Override public Collection<Tile> getTiles(Rectangle region, Dimension subsampling, boolean subsamplingChangeAllowed) throws IOException { final Dimension copy = new Dimension(subsampling); final Collection<Tile> tiles = first.getTiles(region, subsampling, subsamplingChangeAllowed); if (equal(tiles, second.getTiles(region, copy, subsamplingChangeAllowed))) { assertEqual(subsampling, copy); } return tiles; } /** * {@inheritDoc} */ @Override public boolean intersects(Rectangle region, Dimension subsampling) throws IOException { return assertEqual(first.intersects(region, subsampling), second.intersects(region, subsampling)); } /** * Returns a hash code value for this tile manager. */ @Override public int hashCode() { return (int) serialVersionUID ^ (first.hashCode() + 31 * second.hashCode()); } /** * Compares this tile manager with the specified object for equality. * * @param object The object to compare with. * @return If the given object is equal to this manager. */ @Override public boolean equals(final Object object) { if (object instanceof ComparedTileManager) { final ComparedTileManager that = (ComparedTileManager) object; return Objects.equals(this.first, that.first) && Objects.equals(this.second, that.second); } return false; } }