/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2008, Open Source Geospatial Foundation (OSGeo) * * 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.geotools.image.io.mosaic; import java.util.Set; import java.util.Collection; import java.util.LinkedHashSet; import java.util.NoSuchElementException; 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.geotools.util.Utilities; import org.geotools.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, comparaison * failures throw {@link AssertionError}. * * @since 2.5 * @source $URL$ * @version $Id$ * @author Martin Desruisseaux */ final class ComparedTileManager extends TileManager { /** * For cross-version compatibility during serialization. */ private static final long serialVersionUID = -6028433158360279586L; /** * The tile manager. */ private 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 (!Utilities.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); // 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<Tile>(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 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} */ 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() + 37 * 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 equals to this manager. */ @Override public boolean equals(final Object object) { if (object instanceof ComparedTileManager) { final ComparedTileManager that = (ComparedTileManager) object; return Utilities.equals(this.first, that.first) && Utilities.equals(this.second, that.second); } return false; } }