| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.metadata; |
| |
| import java.util.Locale; |
| import java.util.Iterator; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import org.opengis.annotation.Obligation; |
| import org.opengis.util.InternationalString; |
| import org.opengis.metadata.citation.Citation; |
| import org.apache.sis.util.collection.TreeTable; |
| import org.apache.sis.util.collection.TableColumn; |
| import org.apache.sis.metadata.iso.citation.DefaultCitation; |
| import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox; |
| import org.apache.sis.xml.NilReason; |
| |
| // Test dependencies |
| import org.junit.jupiter.api.Test; |
| import static org.junit.jupiter.api.Assertions.*; |
| import org.apache.sis.test.TestCase; |
| import static org.apache.sis.test.Assertions.assertMultilinesEquals; |
| import static org.apache.sis.test.TestUtilities.toTreeStructure; |
| import static org.apache.sis.test.TestUtilities.formatMetadata; |
| |
| |
| /** |
| * Tests the {@link TreeTableView} class. |
| * Unless otherwise specified, all tests use the {@link MetadataStandard#ISO_19115} constant. |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| */ |
| public final class TreeTableViewTest extends TestCase { |
| /** |
| * Creates a new test case. |
| */ |
| public TreeTableViewTest() { |
| } |
| |
| /** |
| * Creates a table to be tested for the given value policy. |
| */ |
| private static TreeTableView create(final ValueExistencePolicy valuePolicy) { |
| return new TreeTableView(MetadataStandard.ISO_19115, TreeNodeTest.metadataWithHierarchy(), Citation.class, valuePolicy); |
| } |
| |
| /** |
| * The expected string representation of the tree created by {@link #create(ValueExistencePolicy)} |
| * with {@link ValueExistencePolicy#NON_EMPTY}. |
| */ |
| private static final String EXPECTED = |
| "Citation………………………………………………………………………………………………… Some title\n" + |
| " ├─Alternate title (1 of 2)…………………………………………… First alternate title\n" + |
| " ├─Alternate title (2 of 2)…………………………………………… Second alternate title\n" + |
| " ├─Edition………………………………………………………………………………………… Some edition\n" + |
| " ├─Cited responsible party (1 of 2)\n" + |
| " │ ├─Role……………………………………………………………………………………… Distributor\n" + |
| " │ └─Organisation………………………………………………………………… Some organisation\n" + |
| " ├─Cited responsible party (2 of 2)\n" + |
| " │ ├─Role……………………………………………………………………………………… Point of contact\n" + |
| " │ └─Individual……………………………………………………………………… Some person of contact\n" + |
| " │ └─Contact info\n" + |
| " │ └─Address\n" + |
| " │ └─Electronic mail address…… Some email\n" + |
| " ├─Presentation form (1 of 2)……………………………………… Map digital\n" + |
| " ├─Presentation form (2 of 2)……………………………………… Map hardcopy\n" + |
| " └─Other citation details………………………………………………… Some other details\n"; |
| |
| /** |
| * Tests {@link TreeTableView#toString()}. |
| * Since the result is locale-dependent, we cannot compare against an exact string. |
| * We will only compare the beginning of each line. |
| */ |
| @Test |
| public void testToString() { |
| final TreeTableView metadata = create(ValueExistencePolicy.COMPACT); |
| assertFalse(metadata.getColumns().contains(MetadataColumn.NIL_REASON)); |
| assertMultilinesEquals(EXPECTED, formatMetadata(metadata)); // Locale-independent |
| assertArrayEquals(toTreeStructure(EXPECTED), toTreeStructure(metadata.toString())); // Locale-dependent. |
| } |
| |
| /** |
| * Verifies most columns in the tree table. All nil reasons are null. |
| */ |
| @Test |
| public void testGetValues() { |
| final TreeTableView metadata = create(ValueExistencePolicy.NON_NULL); |
| assertTrue(metadata.getColumns().contains(MetadataColumn.NIL_REASON)); |
| verify(metadata.getRoot(), "Some title", null); |
| } |
| |
| /** |
| * Verifies columns in the tree table with some non-null nil reasons. |
| */ |
| @Test |
| public void testNilReasons() { |
| final TreeTableView metadata = create(ValueExistencePolicy.NON_NULL); |
| assertTrue(metadata.getColumns().contains(MetadataColumn.NIL_REASON)); |
| final var citation = (DefaultCitation) metadata.getRoot().getUserObject(); |
| citation.setTitle(NilReason.TEMPLATE.createNilObject(InternationalString.class)); |
| verify(metadata.getRoot(), null, NilReason.TEMPLATE); |
| } |
| |
| /** |
| * Verifies the values of the given root node and some of its children. |
| * This method modifies the metadata for also testing some nil values. |
| * |
| * @param node root node to verify. |
| * @param title expected citation title, or {@code null} if it is expected to be missing. |
| * @param titleNR if the title is missing, the expected reason why. |
| */ |
| private void verify(TreeTableView.Node node, final String title, final NilReason titleNR) { |
| assertEquals("CI_Citation", node.getValue(TableColumn.IDENTIFIER)); |
| assertNull ( node.getValue(TableColumn.INDEX)); |
| assertEquals("Citation", node.getValue(TableColumn.NAME)); |
| assertEquals(Citation.class, node.getValue(TableColumn.TYPE)); |
| assertNull ( node.getValue(TableColumn.OBLIGATION)); |
| assertNull ( node.getValue(TableColumn.VALUE)); |
| assertNull ( node.getValue(MetadataColumn.NIL_REASON)); |
| /* |
| * The first child of a Citation object should be the title. |
| * Verify the title value, type, obligation, etc. |
| */ |
| Iterator<TreeTable.Node> it = node.getChildren().iterator(); |
| node = it.next(); |
| assertEquals("title", node.getValue(TableColumn.IDENTIFIER)); |
| assertNull ( node.getValue(TableColumn.INDEX)); |
| assertEquals("Title", node.getValue(TableColumn.NAME)); |
| assertEquals(InternationalString.class, node.getValue(TableColumn.TYPE)); |
| assertEquals(Obligation.MANDATORY, node.getValue(TableColumn.OBLIGATION)); |
| assertI18nEq(title, node.getValue(TableColumn.VALUE)); |
| assertEquals(titleNR, node.getValue(MetadataColumn.NIL_REASON)); |
| /* |
| * Declare the title as missing and verify that the change has been applied. |
| */ |
| node.setValue(MetadataColumn.NIL_REASON, NilReason.MISSING); |
| assertEquals(NilReason.MISSING, node.getValue(MetadataColumn.NIL_REASON)); |
| assertNull(node.getValue(TableColumn.VALUE)); |
| /* |
| * The second child of the Citation use in this test should be an alternate title. |
| * This property is a collection with two elements. Check the first one. |
| */ |
| node = it.next(); |
| assertEquals("alternateTitle", node.getValue(TableColumn.IDENTIFIER)); |
| assertEquals(0, node.getValue(TableColumn.INDEX)); |
| assertI18nEq("Alternate title (1 of 2)", node.getValue(TableColumn.NAME)); |
| assertEquals(InternationalString.class, node.getValue(TableColumn.TYPE)); |
| assertEquals(Obligation.OPTIONAL, node.getValue(TableColumn.OBLIGATION)); |
| assertI18nEq("First alternate title", node.getValue(TableColumn.VALUE)); |
| assertNull ( node.getValue(MetadataColumn.NIL_REASON)); |
| /* |
| * Set the first element to nil, then check that the second element has not been impacted. |
| * Contrarily to the previous test, this test modifies a collection elements instead of the |
| * property as a whole. |
| */ |
| node.setValue(MetadataColumn.NIL_REASON, NilReason.INAPPLICABLE); |
| assertEquals(NilReason.INAPPLICABLE, node.getValue(MetadataColumn.NIL_REASON)); |
| assertNull(node.getValue(TableColumn.VALUE)); |
| node = it.next(); |
| assertI18nEq("Second alternate title", node.getValue(TableColumn.VALUE)); |
| assertNull(node.getValue(MetadataColumn.NIL_REASON)); |
| } |
| |
| /** |
| * Verifies the value of the given international string in English. |
| */ |
| private static void assertI18nEq(final String expected, Object text) { |
| if (text instanceof InternationalString i18n) { |
| text = i18n.toString(Locale.ENGLISH); |
| } |
| assertEquals(expected, text); |
| } |
| |
| /** |
| * Tests serialization. |
| * |
| * @throws Exception if an error occurred during the serialization process. |
| */ |
| @Test |
| public void testSerialization() throws Exception { |
| final Object original = create(ValueExistencePolicy.COMPACT); |
| final Object deserialized; |
| final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); |
| try (ObjectOutputStream out = new ObjectOutputStream(buffer)) { |
| out.writeObject(original); |
| } |
| // Now reads the object we just serialized. |
| final byte[] data = buffer.toByteArray(); |
| try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data))) { |
| deserialized = in.readObject(); |
| } |
| assertMultilinesEquals(EXPECTED, formatMetadata((TreeTableView) deserialized)); |
| } |
| |
| /** |
| * Tests formatting a tree containing a remark. We use a geographic bounding box crossing the anti-meridian. |
| * In this test the longitude value and the remarks and separated by "……" characters, but this is because we |
| * use the default {@link org.apache.sis.util.collection.TreeTableFormat}. When using {@link MetadataFormat} |
| * specialization, the formatting is a little bit different |
| */ |
| @Test |
| public void testRemarks() { |
| final DefaultGeographicBoundingBox bbox = new DefaultGeographicBoundingBox(170, -160, -30, 40); |
| final String text = formatMetadata(bbox.asTreeTable()); |
| assertMultilinesEquals( |
| "Geographic bounding box\n" + |
| " ├─West bound longitude…… 170°E\n" + |
| " ├─East bound longitude…… 160°W…… Bounding box crosses the antimeridian.\n" + // See method javadoc. |
| " ├─South bound latitude…… 30°S\n" + |
| " ├─North bound latitude…… 40°N\n" + |
| " └─Extent type code……………… True\n", text); |
| } |
| } |