/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2009-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.metadata; import java.util.List; import java.util.Arrays; import java.util.HashSet; import javax.imageio.ImageReader; import org.opengis.metadata.Identifier; import org.opengis.metadata.acquisition.Instrument; import org.opengis.metadata.extent.GeographicBoundingBox; import org.opengis.coverage.grid.RectifiedGrid; import org.junit.*; import org.apache.sis.test.DependsOn; import static org.geotoolkit.test.Assert.*; import static org.geotoolkit.test.Commons.*; import static org.geotoolkit.image.io.metadata.SpatialMetadataFormat.GEOTK_FORMAT_NAME; /** * Tests {@link MetadataNodeAccessor}. * * @author Martin Desruisseaux (Geomatys) * @version 3.16 * * @since 3.06 */ @DependsOn(SpatialMetadataFormatTest.class) public final strictfp class MetadataNodeAccessorTest extends org.geotoolkit.test.TestBase { /** * Tests the accessor with some properties defined under the {@code "ImageDescription"} node. */ @Test public void testImageDescription() { final SpatialMetadata metadata = new SpatialMetadata(SpatialMetadataFormat.getImageInstance(GEOTK_FORMAT_NAME)); assertMultilinesEquals("The metadata should initially contains only the root node.", GEOTK_FORMAT_NAME + "\n", metadata.toString()); /* * Ensure that the metadata is initially empty and that * attempts to access attributes do not throw an exception. */ final MetadataNodeAccessor accessor = new MetadataNodeAccessor(metadata, null, "ImageDescription", null); assertEquals("ImageDescription", accessor.name()); assertEquals("Initially empty metadata should have no child.", 0, accessor.childCount()); assertNull(accessor.getAttribute("imagingCondition")); assertNull(accessor.getAttributeAsDouble("cloudCoverPercentage")); assertMultilinesEquals("MetadataNodeAccessor constructor should have created its node.", GEOTK_FORMAT_NAME + "\n" + "└───ImageDescription\n", metadata.toString()); /* * Define a few values conform to the structure declared * in SpatialMetadataFormat.getImageInstance(null). */ accessor.setAttribute("imagingCondition", "cloud"); accessor.setAttribute("cloudCoverPercentage", 20.0); /* * Check the value that we have set. */ assertEquals("cloud", accessor.getAttribute("imagingCondition")); assertEquals(Double.valueOf(20), accessor.getAttributeAsDouble("cloudCoverPercentage")); assertMultilinesEquals(decodeQuotes( GEOTK_FORMAT_NAME + "\n" + "└───ImageDescription\n" + "    ├───imagingCondition=“cloud”\n" + "    └───cloudCoverPercentage=“20.0”\n"), metadata.toString()); /* * Ensure that attempt to select a child thrown an exception, * since this accessor does not declare any children. */ try { accessor.selectChild(0); fail("Selecting a child should be an illegal operation."); } catch (IndexOutOfBoundsException e) { // This is the expected exception. } } /** * Tests the accessor with some properties defined under the {@code "OffsetVectors"} node. */ @Test public void testOffsetVectors() { testOffsetVectors("OffsetVector"); } /** * Same tests than {@link #testOffsetVectors()}, but without explicit specification * of the child name. */ @Test public void testAutoDetectChilds() { testOffsetVectors("#auto"); } /** * Tests the accessor with some properties defined under the {@code "OffsetVectors"} node. * The child name shall be either {@code "OffsetVector"} or {@code "#auto"}. */ private void testOffsetVectors(final String childName) { final SpatialMetadata metadata = new SpatialMetadata(SpatialMetadataFormat.getImageInstance(GEOTK_FORMAT_NAME)); assertMultilinesEquals("The metadata should initially contains only the root node.", GEOTK_FORMAT_NAME + "\n", metadata.toString()); /* * Ensure that the metadata is initially empty and that * attempts to access attributes do not throw an exception. */ final MetadataNodeAccessor accessor = new MetadataNodeAccessor(metadata, null, "RectifiedGridDomain/OffsetVectors", childName); assertEquals("OffsetVectors", accessor.name()); assertMultilinesEquals("MetadataNodeAccessor constructor should have created its node.", GEOTK_FORMAT_NAME + "\n" + "└───RectifiedGridDomain\n" + "    └───OffsetVectors\n", metadata.toString()); /* * Define a few values conform to the structure declared * in SpatialMetadataFormat.getImageInstance(null). */ accessor.selectChild(accessor.appendChild()); assertNull(accessor.getAttributeAsDoubles("values", false)); accessor.setAttribute("values", new double[] {2, 5, 8}); accessor.selectChild(accessor.appendChild()); assertNull(accessor.getAttributeAsDoubles("values", false)); accessor.setAttribute("values", new double[] {3, 1, 4}); /* * Check the value that we have set. */ accessor.selectParent(); assertNull(accessor.getAttributeAsDoubles("values", false)); accessor.selectChild(0); assertTrue(Arrays.equals(accessor.getAttributeAsDoubles("values", false), new double[] {2, 5, 8})); accessor.selectChild(1); assertTrue(Arrays.equals(accessor.getAttributeAsDoubles("values", false), new double[] {3, 1, 4})); assertMultilinesEquals(decodeQuotes( GEOTK_FORMAT_NAME + "\n" + "└───RectifiedGridDomain\n" + "    └───OffsetVectors\n" + "        ├───OffsetVector\n" + "        │   └───values=“2.0 5.0 8.0”\n" + "        └───OffsetVector\n" + "            └───values=“3.0 1.0 4.0”\n"), metadata.toString()); } /** * Tests the accessor with a non-existent attribute under the {@code "ImageDescription"} node. * Actually nothing special happen except that the resulting {@code IIOMetadata} is invalid, * but this is not {@link MetadataNodeAccessor} job to verify that. */ @Test public void testNonExistentAttribute() { final SpatialMetadata metadata = new SpatialMetadata(SpatialMetadataFormat.getImageInstance(GEOTK_FORMAT_NAME)); final MetadataNodeAccessor accessor = new MetadataNodeAccessor(metadata, null, "ImageDescription", null); accessor.setAttribute("cloudCoverPercentage", 20.0); // For comparison purpose. accessor.setAttribute("inexistent", 10.0); } /** * Tests the {@link MetadataNodeAccessor#listPaths} static method. */ @Test public void testListPaths() { /* * Simpliest cases: the element below is known to appear only once. */ final SpatialMetadataFormat STREAM = SpatialMetadataFormat.getStreamInstance(GEOTK_FORMAT_NAME); List<String> paths = MetadataNodeAccessor.listPaths(STREAM, GeographicBoundingBox.class); assertEquals(Arrays.asList("DiscoveryMetadata/Extent/GeographicElement"), paths); /* * Simpliest case again, but deeper path. Note that it cross a collection (Instruments). */ paths = MetadataNodeAccessor.listPaths(STREAM, Identifier.class); assertEquals(Arrays.asList("AcquisitionMetadata/Platform/Instruments/Instrument/Identifier"), paths); /* * The element below appears more than once. * We don't consider elements order. */ paths = MetadataNodeAccessor.listPaths(SpatialMetadataFormat.getImageInstance(GEOTK_FORMAT_NAME), Identifier.class); assertEquals(new HashSet<>(Arrays.asList("ImageDescription/ImageQualityCode", "ImageDescription/ProcessingLevelCode")), new HashSet<>(paths)); /* * More tricky case: 'Instrument' is the type of elements in a collection. * But we want the path to the whole collection. */ paths = MetadataNodeAccessor.listPaths(STREAM, Instrument.class); assertEquals(Arrays.asList("AcquisitionMetadata/Platform/Instruments"), paths); } /** * Tests the {@link MetadataNodeAccessor} constructors which locate the path * automatically from the given type, searching in the whole tree. */ @Test public void testAutomaticLocation() { SpatialMetadata metadata = new SpatialMetadata(SpatialMetadataFormat.getStreamInstance(GEOTK_FORMAT_NAME)); MetadataNodeAccessor accessor = new MetadataNodeAccessor(metadata, "#auto", GeographicBoundingBox.class); accessor.setAttribute("inclusion", true); assertMultilinesEquals(decodeQuotes( GEOTK_FORMAT_NAME + "\n" + "└───DiscoveryMetadata\n" + "    └───Extent\n" + "        └───GeographicElement\n" + "            └───inclusion=“true”\n"), metadata.toString()); /* * Following should fails because of ambiguity (there is many identifier). */ metadata = new SpatialMetadata(SpatialMetadataFormat.getImageInstance(GEOTK_FORMAT_NAME)); try { accessor = new MetadataNodeAccessor(metadata, "#auto", Identifier.class); fail(accessor.toString()); } catch (IllegalArgumentException e) { // This is the expected exception. } } /** * Tests the {@link MetadataNodeAccessor} constructors which locate the path * automatically from the given type, forcing a search in the fallback. * * @since 3.16 */ @Test public void testAutomaticLocationInFallback() { SpatialMetadata metadata = new SpatialMetadata(SpatialMetadataFormat.getStreamInstance(GEOTK_FORMAT_NAME)); MetadataNodeAccessor accessor = new MetadataNodeAccessor(metadata, "#auto", GeographicBoundingBox.class); assertEquals("GeographicElement", accessor.name()); accessor.setAttribute("inclusion", true); /* * The stream metadata should not know anything about the RectifiedGrid interface. * Note that we will ask exactly the same interface later. */ try { accessor = new MetadataNodeAccessor(metadata, "#auto", RectifiedGrid.class); fail("Should not find " + accessor); } catch (IllegalArgumentException e) { // This is the expected exception. assertTrue(e.getMessage().contains("RectifiedGrid")); } /* * Creates an image metadata which will use the above stream metadata as a fallback. * This is not a recommanded practice (especially since both metadata format share * the same name). We are just too lazy for creating a new metadata format, and the * approach used here is sufficient for this test. */ metadata = new SpatialMetadata(false, (ImageReader) null, metadata); accessor = new MetadataNodeAccessor(metadata, "#auto", RectifiedGrid.class); assertEquals("RectifiedGridDomain", accessor.name()); /* * Now ask a metadata which should work only if the MetadataNodeAccessor has been * able to search in the fallback chain. */ accessor = new MetadataNodeAccessor(metadata, "#auto", GeographicBoundingBox.class); assertEquals("GeographicElement", accessor.name()); } }