| /* |
| * 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.Arrays; |
| import java.util.Locale; |
| import org.opengis.metadata.citation.Address; |
| import org.opengis.metadata.citation.Contact; |
| import org.opengis.metadata.citation.Citation; |
| import org.opengis.metadata.citation.ResponsibleParty; |
| import org.opengis.metadata.citation.PresentationForm; |
| import org.opengis.metadata.citation.Role; |
| import org.opengis.util.InternationalString; |
| import org.apache.sis.metadata.iso.citation.DefaultAddress; |
| import org.apache.sis.metadata.iso.citation.DefaultContact; |
| import org.apache.sis.metadata.iso.citation.DefaultCitation; |
| import org.apache.sis.metadata.iso.citation.DefaultResponsibleParty; |
| import org.apache.sis.metadata.iso.citation.DefaultOrganisation; |
| import org.apache.sis.metadata.iso.citation.DefaultIndividual; |
| import org.apache.sis.metadata.iso.citation.AbstractParty; |
| import org.apache.sis.util.collection.TableColumn; |
| import org.apache.sis.util.collection.TreeTable; |
| import org.apache.sis.test.DependsOnMethod; |
| import org.apache.sis.test.DependsOn; |
| import org.apache.sis.test.TestCase; |
| import org.junit.Test; |
| |
| import static org.apache.sis.test.MetadataAssert.*; |
| import static java.util.Collections.singleton; |
| |
| |
| /** |
| * Tests the {@link TreeNode} class. |
| * Unless otherwise specified, all tests use the {@link MetadataStandard#ISO_19115} constant. |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| * @version 0.8 |
| * @since 0.3 |
| * @module |
| */ |
| @DependsOn(TreeNodeChildrenTest.class) |
| public final strictfp class TreeNodeTest extends TestCase { |
| /** |
| * Creates a metadata hierarchy to be used for the tests. |
| * This method creates the following metadata: |
| * |
| * {@preformat text |
| * Citation |
| * ├─Title…………………………………………………………………………………………… Some title |
| * ├─Alternate title (1 of 2)………………………………………… First alternate title |
| * ├─Alternate title (2 of 2)………………………………………… Second alternate title |
| * ├─Edition……………………………………………………………………………………… Some edition |
| * ├─Cited responsible party (1 of 2) |
| * │ └─Organisation |
| * │ ├─Name…………………………………………………………………………… Some organisation |
| * │ └─Role…………………………………………………………………………… Distributor |
| * ├─Cited responsible party (2 of 2) |
| * │ └─Individual |
| * │ ├─Name…………………………………………………………………………… Some person of contact |
| * │ ├─Contact info |
| * │ │ └─Address |
| * │ │ └─Electronic mail address…… Some email |
| * │ └─Role…………………………………………………………………………… Point of contact |
| * ├─Presentation form (1 of 2)…………………………………… Map digital |
| * ├─Presentation form (2 of 2)…………………………………… map hardcopy |
| * └─Other citation details……………………………………………… Some other details |
| * } |
| */ |
| static DefaultCitation metadataWithHierarchy() { |
| final DefaultCitation citation = TreeNodeChildrenTest.metadataWithMultiOccurrences(); |
| AbstractParty party = new DefaultOrganisation("Some organisation", null, null, null); |
| DefaultResponsibleParty responsibility = new DefaultResponsibleParty(Role.DISTRIBUTOR); |
| responsibility.setParties(singleton(party)); |
| assertTrue(citation.getCitedResponsibleParties().add(responsibility)); |
| |
| // Add a second responsible party with deeper hierarchy. |
| final DefaultContact contact = new DefaultContact(); |
| final DefaultAddress address = new DefaultAddress(); |
| address.setElectronicMailAddresses(singleton("Some email")); |
| contact.setAddresses(singleton(address)); |
| party = new DefaultIndividual("Some person of contact", null, contact); |
| responsibility = new DefaultResponsibleParty(Role.POINT_OF_CONTACT); |
| responsibility.setParties(singleton(party)); |
| assertTrue(citation.getCitedResponsibleParties().add(responsibility)); |
| return citation; |
| } |
| |
| /** |
| * The policy to be given to {@link TreeTableView} constructor. |
| */ |
| private ValueExistencePolicy valuePolicy = ValueExistencePolicy.NON_EMPTY; |
| |
| /** |
| * Creates a node to be tested for the given metadata object and value policy. |
| */ |
| private <T extends AbstractMetadata> TreeNode create(final T metadata, final Class<? super T> baseType) { |
| final MetadataStandard standard = MetadataStandard.ISO_19115; |
| final TreeTableView table = new TreeTableView(standard, metadata, baseType, valuePolicy); |
| return (TreeNode) table.getRoot(); |
| } |
| |
| /** |
| * Tests the properties of the root node. |
| */ |
| @Test |
| public void testRootNode() { |
| final DefaultCitation citation = TreeNodeChildrenTest.metadataWithoutCollections(); |
| final TreeNode node = create(citation, Citation.class); |
| assertEquals("getName()", "Citation", node.getName()); |
| assertEquals("getIdentifier()", "CI_Citation", node.getIdentifier()); |
| assertEquals("baseType", Citation.class, node.baseType); |
| assertSame ("getUserObject()", citation, node.getUserObject()); |
| assertFalse ("isWritable()", node.isWritable()); |
| assertNull ("getParent()", node.getParent()); |
| assertFalse ("isLeaf()", node.isLeaf()); |
| |
| final TreeNodeChildren children = (TreeNodeChildren) node.getChildren(); |
| assertEquals("children.titleProperty", -1, children.titleProperty); |
| assertSame ("children.metadata", citation, children.metadata); |
| assertFalse ("children.isEmpty()", node.getChildren().isEmpty()); |
| assertSame ("children.parent", node, children.iterator().next().getParent()); |
| } |
| |
| /** |
| * Tests {@link TreeNode#getName()} on a metadata with only one entry in collections. |
| * Those names shall <em>not</em> contain numbering like <cite>"(1 of 2)"</cite>. |
| */ |
| @Test |
| @DependsOnMethod("testRootNode") // Because tested more basic methods than 'getValue(TableColumn)'. |
| public void testGetNameForSingleton() { |
| final DefaultCitation citation = TreeNodeChildrenTest.metadataWithSingletonInCollections(); |
| assertColumnContentEquals(create(citation, Citation.class), TableColumn.NAME, |
| "Citation", |
| "Title", |
| "Alternate title", |
| "Edition", |
| "Presentation form", |
| "Other citation details"); |
| } |
| |
| /** |
| * Tests {@link TreeNode#getName()} on a metadata with more than one entry in collections. |
| * Those names <em>shall</em> contain numbering like <cite>"(1 of 2)"</cite>. |
| */ |
| @Test |
| @DependsOnMethod("testGetNameForSingleton") |
| public void testGetNameForMultiOccurrences() { |
| final DefaultCitation citation = TreeNodeChildrenTest.metadataWithMultiOccurrences(); |
| assertColumnContentEquals(create(citation, Citation.class), TableColumn.NAME, |
| "Citation", |
| "Title", |
| "Alternate title (1 of 2)", |
| "Alternate title (2 of 2)", |
| "Edition", |
| "Presentation form (1 of 2)", |
| "Presentation form (2 of 2)", |
| "Other citation details"); |
| } |
| |
| /** |
| * Compares the result of the given getter method invoked on all nodes of {@link #metadataWithHierarchy()}. |
| * In the particular case of the {@link TableColumn#NAME}, international strings are replaced by unlocalized |
| * strings before comparisons. |
| * |
| * <p>If {@link #valuePolicy} is {@link ValueExistencePolicy#COMPACT}, then this method removes the elements at |
| * indices 0, 6 and 10 (if {@code offset} = 0) or 1, 7 and 11 (if {@code offset} = 1) from the {@code expected} |
| * array before to perform the comparison (note: actual indices vary according branches).</p> |
| * |
| * @param offset 0 if compact mode excludes the parent, or 1 if compact mode exclude the first child. |
| * @param column the column from which to get a value. |
| * @param expected the expected values. The first value is the result of the getter method |
| * applied on the given node, and all other values are the result of the |
| * getter method applied on the children, in iteration order. |
| */ |
| private void assertCitationContentEquals(final int offset, final TableColumn<?> column, final Object... expected) { |
| if (valuePolicy == ValueExistencePolicy.COMPACT) { |
| assertEquals(19, expected.length); |
| System.arraycopy(expected, 12+offset, expected, 11+offset, 7-offset); // Compact the "Individual" element. |
| System.arraycopy(expected, 8+offset, expected, 7+offset, 10-offset); // Compact the "Organisation" element. |
| System.arraycopy(expected, 1+offset, expected, offset, 16-offset); // Compact the "Title" element. |
| Arrays.fill(expected, 16, 19, null); |
| } |
| assertColumnContentEquals(create(metadataWithHierarchy(), Citation.class), column, expected); |
| } |
| |
| /** |
| * Tests {@link TreeNode#getName()} on a metadata with a deeper hierarchy. |
| */ |
| @Test |
| @DependsOnMethod("testGetNameForMultiOccurrences") |
| public void testGetNameForHierarchy() { |
| assertCitationContentEquals(1, TableColumn.NAME, |
| "Citation", |
| "Title", |
| "Alternate title (1 of 2)", |
| "Alternate title (2 of 2)", |
| "Edition", |
| "Cited responsible party (1 of 2)", |
| "Role", |
| "Party", |
| "Name", // In COMPACT mode, this value is associated to "Organisation" node. |
| "Cited responsible party (2 of 2)", |
| "Role", |
| "Party", |
| "Name", // In COMPACT mode, this value is associated to "Individual" node. |
| "Contact info", |
| "Address", |
| "Electronic mail address", |
| "Presentation form (1 of 2)", |
| "Presentation form (2 of 2)", |
| "Other citation details"); |
| } |
| |
| /** |
| * Tests {@link TreeNode#getIdentifier()} on a metadata with a hierarchy. |
| * Those names shall <em>not</em> contain numbering like <cite>"(1 of 2)"</cite>, even if the same |
| * identifiers are repeated. Those identifiers are not intended to be unique in a list of children. |
| * The repetition of the same identifier means that they shall be part of a collection. |
| */ |
| @Test |
| @DependsOnMethod("testGetNameForMultiOccurrences") // Because similar to names, which were tested progressively. |
| public void testGetIdentifier() { |
| assertCitationContentEquals(1, TableColumn.IDENTIFIER, |
| "CI_Citation", |
| "title", |
| "alternateTitle", |
| "alternateTitle", |
| "edition", |
| "citedResponsibleParty", |
| "role", |
| "party", |
| "name", // In COMPACT mode, this value is associated to "party" node. |
| "citedResponsibleParty", |
| "role", |
| "party", |
| "name", // In COMPACT mode, this value is associated to "party" node. |
| "contactInfo", |
| "address", |
| "electronicMailAddress", |
| "presentationForm", |
| "presentationForm", |
| "otherCitationDetails"); |
| } |
| |
| /** |
| * Tests {@link TreeNode#getIndex()} on a metadata with a hierarchy. |
| */ |
| @Test |
| @DependsOnMethod("testGetIdentifier") |
| public void testGetIndex() { |
| final Integer ZERO = 0; |
| final Integer ONE = 1; |
| skipCountCheck = true; // Because of the null value at the end of following array. |
| assertCitationContentEquals(1, TableColumn.INDEX, |
| null, // CI_Citation |
| null, // title |
| ZERO, // alternateTitle |
| ONE, // alternateTitle |
| null, // edition |
| ZERO, // citedResponsibleParty |
| null, // role |
| ZERO, // party (organisation) |
| null, // name — in COMPACT mode, this value is associated to "party" node. |
| ONE, // citedResponsibleParty |
| null, // role |
| ZERO, // party (individual) |
| null, // name — in COMPACT mode, this value is associated to "party" node. |
| ZERO, // contactInfo |
| ZERO, // address |
| ZERO, // electronicMailAddress |
| ZERO, // presentationForm |
| ONE, // presentationForm |
| null); // otherCitationDetails |
| } |
| |
| /** |
| * Tests getting the value of {@link TableColumn#TYPE} on a metadata with a hierarchy. |
| */ |
| @Test |
| @DependsOnMethod("testGetIdentifier") // Because if identifiers are wrong, we are looking at wrong properties. |
| public void testGetElementType() { |
| assertCitationContentEquals(0, TableColumn.TYPE, |
| Citation.class, |
| InternationalString.class, |
| InternationalString.class, |
| InternationalString.class, |
| InternationalString.class, |
| ResponsibleParty.class, |
| Role.class, |
| AbstractParty.class, // In COMPACT mode, value with be the one of "name" node instead. |
| InternationalString.class, // Name |
| ResponsibleParty.class, |
| Role.class, |
| AbstractParty.class, // In COMPACT mode, value with be the one of "name" node instead. |
| InternationalString.class, // Name |
| Contact.class, |
| Address.class, |
| String.class, |
| PresentationForm.class, |
| PresentationForm.class, |
| InternationalString.class); |
| } |
| |
| /** |
| * Tests {@link TreeNode#getValue(TableColumn)} for the value column. |
| */ |
| @Test |
| @DependsOnMethod("testGetIdentifier") // Because if identifiers are wrong, we are looking at wrong properties. |
| public void testGetValue() { |
| assertCitationContentEquals(0, TableColumn.VALUE, |
| null, // Citation |
| "Some title", |
| "First alternate title", |
| "Second alternate title", |
| "Some edition", |
| null, // ResponsibleParty |
| Role.DISTRIBUTOR, |
| null, // Party (organisation) |
| "Some organisation", |
| null, // ResponsibleParty |
| Role.POINT_OF_CONTACT, |
| null, // Party (individual) |
| "Some person of contact", |
| null, // Contact |
| null, // Address |
| "Some email", |
| PresentationForm.MAP_DIGITAL, |
| PresentationForm.MAP_HARDCOPY, |
| "Some other details"); |
| } |
| |
| /** |
| * Tests {@link TreeNode#newChild()}. |
| */ |
| @Test |
| @DependsOnMethod("testGetValue") |
| public void testNewChild() { |
| final DefaultCitation citation = metadataWithHierarchy(); |
| final TreeNode node = create(citation, Citation.class); |
| /* |
| * Ensure that we can not overwrite existing nodes. |
| */ |
| TreeTable.Node child = node.newChild(); |
| child.setValue(TableColumn.IDENTIFIER, "title"); |
| try { |
| child.setValue(TableColumn.VALUE, "A new title"); |
| fail("Attemps to overwrite an existing value shall fail."); |
| } catch (IllegalStateException e) { |
| assertTrue(e.getMessage().contains("title")); |
| } |
| /* |
| * Clear the title and try again. This time, it shall work. |
| */ |
| citation.setTitle(null); |
| child = node.newChild(); |
| child.setValue(TableColumn.IDENTIFIER, "title"); |
| child.setValue(TableColumn.VALUE, "A new title"); |
| assertTitleEquals("citation", "A new title", citation); |
| assertSame(citation.getTitle(), child.getValue(TableColumn.VALUE)); |
| /* |
| * Try adding a new element in a collection. |
| * Note that the code below imply a conversion from String to InternationalString. |
| */ |
| child = node.newChild(); |
| child.setValue(TableColumn.IDENTIFIER, "alternateTitle"); |
| child.setValue(TableColumn.VALUE, "Third alternate title"); |
| assertEquals(3, citation.getAlternateTitles().size()); |
| assertEquals("Third alternate title", child.getValue(TableColumn.VALUE).toString()); |
| } |
| |
| /** |
| * For disabling the check of child nodes count. |
| * This hack is specific to the branch using GeoAPI 3.0 (not needed on the branch using GeoAPI 4.0). |
| */ |
| private boolean skipCountCheck; |
| |
| /** |
| * Compares the result of the given getter method invoked on the given node, then invoked |
| * on all children of that given. In the particular case of the {@link TableColumn#NAME}, |
| * international strings are replaced by unlocalized strings before comparisons. |
| * |
| * @param node the node for which to test the children. |
| * @param column the column from which to get a value. |
| * @param expected the expected values. The first value is the result of the getter method |
| * applied on the given node, and all other values are the result of the |
| * getter method applied on the children, in iteration order. |
| */ |
| private void assertColumnContentEquals(final TreeNode node, |
| final TableColumn<?> column, final Object... expected) |
| { |
| int count = expected.length; |
| if (valuePolicy == ValueExistencePolicy.COMPACT) { |
| while (expected[count-1] == null) count--; |
| } |
| if (skipCountCheck) return; |
| assertEquals("Missing values in the tested metadata.", count, |
| assertColumnContentEquals(node, column, expected, 0)); |
| } |
| |
| /** |
| * Implementation of the above {@code assertGetterReturns}, to be invoked recursively. |
| * |
| * @return number of nodes found in the given metadata tree. |
| */ |
| private static int assertColumnContentEquals(final TreeTable.Node node, final TableColumn<?> column, |
| final Object[] expected, int index) |
| { |
| final Object actual = node.getValue(column); |
| Object unlocalized = actual; |
| if (unlocalized instanceof InternationalString) { |
| unlocalized = ((InternationalString) unlocalized).toString(Locale.ROOT); |
| } |
| assertEquals("values[" + index + ']', expected[index++], unlocalized); |
| for (final TreeTable.Node child : node.getChildren()) { |
| index = assertColumnContentEquals(child, column, expected, index); |
| } |
| assertSame("Value shall be stable.", actual, node.getValue(column)); |
| return index; |
| } |
| |
| /** |
| * Same tests but using {@link ValueExistencePolicy#COMPACT}. |
| * |
| * @see <a href="https://issues.apache.org/jira/browse/SIS-298">SIS-298</a> |
| */ |
| @Test |
| @DependsOnMethod({"testGetNameForHierarchy", "testGetIdentifier", "testGetIndex", "testGetElementType", "testGetValue"}) |
| public void testCompactPolicy() { |
| valuePolicy = ValueExistencePolicy.COMPACT; |
| testGetNameForHierarchy(); |
| testGetIdentifier(); |
| testGetIndex(); |
| testGetElementType(); |
| testGetValue(); |
| } |
| } |