blob: ad31dae2439127be819abb3c2d00ce836da790bb [file] [log] [blame]
/*
* 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();
}
}