blob: 143f1f0593767630a4b750c383241e6c11e115c2 [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.referencing;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Locale;
import org.apache.sis.referencing.datum.AbstractDatum;
import org.apache.sis.xml.bind.Context;
import org.apache.sis.xml.bind.referencing.Code;
import static org.apache.sis.metadata.iso.citation.Citations.EPSG;
// Test dependencies
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import static org.junit.jupiter.api.Assertions.*;
import org.opengis.test.Validators;
import org.apache.sis.test.TestCase;
import static org.apache.sis.test.Assertions.assertMessageContains;
import static org.apache.sis.test.Assertions.assertSerializedEquals;
import static org.apache.sis.test.TestUtilities.getSingleton;
import static org.apache.sis.referencing.Assertions.assertRemarksEquals;
// Specific to the main and geoapi-3.1 branches:
import org.opengis.referencing.ReferenceIdentifier;
/**
* Tests the {@link AbstractIdentifiedObject} class.
*
* @author Martin Desruisseaux (IRD, Geomatys)
*/
public final class AbstractIdentifiedObjectTest extends TestCase {
/**
* Creates a new test case.
*/
public AbstractIdentifiedObjectTest() {
}
/**
* Creates a map of properties to be given to the {@link AbstractIdentifiedObject} constructor.
* The values in the map are consistent with the values expected by the {@link #validate} method.
*
* @param identifiers the value for the {@code "identifiers"} property.
*/
private static Map<String,Object> properties(final Set<ReferenceIdentifier> identifiers) {
final var properties = new HashMap<String,Object>(8);
assertNull(properties.put("name", "GRS 1980"));
assertNull(properties.put("identifiers", identifiers.toArray(ReferenceIdentifier[]::new)));
assertNull(properties.put("codespace", "EPSG"));
assertNull(properties.put("version", "8.3"));
assertNull(properties.put("alias", "International 1979"));
assertNull(properties.put("remarks", "Adopted by IUGG 1979 Canberra"));
assertNull(properties.put("remarks_fr", "Adopté par IUGG 1979 Canberra"));
return properties;
}
/**
* Validates the given object created by the test methods.
*
* @param object the object to validate.
* @param identifiers the expected value of {@link AbstractIdentifiedObject#getIdentifiers()}.
* @param gmlID the expected value of {@link AbstractIdentifiedObject#getID()}.
* @return the value of {@link AbstractIdentifiedObject#getIdentifier()}.
*/
private static ReferenceIdentifier validate(final AbstractIdentifiedObject object,
final Set<ReferenceIdentifier> identifiers, final String gmlID)
{
Validators.validate(object);
final var name = object.getName();
assertEquals("GRS 1980", name.getCode(), "name");
assertEquals("EPSG", name.getCodeSpace(), "codespace");
assertEquals("8.3", name.getVersion(), "version");
assertEquals("International 1979", getSingleton(object.getAlias()).toString(), "aliases");
assertEquals(name, getSingleton(object.getNames()), "names");
assertEquals(identifiers, object.getIdentifiers(), "identifiers");
assertEquals(gmlID, object.getID(), "ID");
assertRemarksEquals("Adopted by IUGG 1979 Canberra", object, Locale.ENGLISH);
assertRemarksEquals("Adopté par IUGG 1979 Canberra", object, Locale.FRENCH);
final Code code = object.getIdentifier();
return (code != null) ? code.getIdentifier() : null;
}
/**
* Tests the {@link AbstractIdentifiedObject#AbstractIdentifiedObject(Map)} constructor without name.
* This is invalid and should throw an exception.
*/
@Test
@SuppressWarnings("ResultOfObjectAllocationIgnored")
public void testMissingName() {
final var properties = new HashMap<String,Object>(4);
assertNull(properties.put(AbstractIdentifiedObject.REMARKS_KEY, "Not a name."));
final Executable test = () -> new AbstractIdentifiedObject(properties);
/*
* The message may be in any language, but shall
* contain at least the missing property name.
*/
IllegalArgumentException exception;
exception = assertThrows(IllegalArgumentException.class, test, "Should not allow unnamed object.");
assertMessageContains(exception, "name");
// Try again, with error messages forced to English.
assertNull(properties.put(AbstractIdentifiedObject.LOCALE_KEY, Locale.US));
exception = assertThrows(IllegalArgumentException.class, test, "Should not allow unnamed object.");
assertEquals("Missing value for “name” property.", exception.getMessage());
// "code" with String value is accepted as well.
assertNull(properties.put("code", "Test"));
assertEquals("Test", new AbstractIdentifiedObject(properties).getName().getCode());
}
/**
* Tests the {@link AbstractIdentifiedObject#AbstractIdentifiedObject(Map)} constructor without identifier.
* This method compares the property values against the expected values.
*/
@Test
public void testWithoutIdentifier() {
final var identifiers = Set.<ReferenceIdentifier>of();
final var object = new AbstractIdentifiedObject(properties(identifiers));
final var gmlId = validate(object, identifiers, "GRS1980");
assertNull(gmlId);
}
/**
* Tests the {@link AbstractIdentifiedObject#AbstractIdentifiedObject(Map)} constructor
* with only one identifier. The methods of interest for this test are:
*
* <ul>
* <li>{@link AbstractIdentifiedObject#getIdentifiers()}</li>
* <li>{@link AbstractIdentifiedObject#getIdentifier()}</li>
* <li>{@link AbstractIdentifiedObject#getID()}</li>
* </ul>
*/
@Test
public void testWithSingleIdentifier() {
final var identifier = new ImmutableIdentifier(null, "EPSG", "7019");
final var identifiers = Set.<ReferenceIdentifier>of(identifier);
final var object = new AbstractIdentifiedObject(properties(identifiers));
final var gmlId = validate(object, identifiers, "epsg-7019");
assertNotNull( gmlId);
assertEquals ("EPSG", gmlId.getCodeSpace());
assertEquals ("7019", gmlId.getCode());
}
/**
* Tests the {@link AbstractIdentifiedObject#AbstractIdentifiedObject(Map)} constructor
* with more than one identifier. This method also tries a different identifier implementation
* than the one used by {@link #testWithSingleIdentifier()}.
*/
@Test
public void testWithManyIdentifiers() {
final var identifiers = new LinkedHashSet<ReferenceIdentifier>(4);
assertTrue(identifiers.add(new NamedIdentifier(EPSG, "7019")));
assertTrue(identifiers.add(new NamedIdentifier(EPSG, "IgnoreMe")));
final var object = new AbstractIdentifiedObject(properties(identifiers));
final var gmlId = validate(object, identifiers, "epsg-7019");
assertNotNull( gmlId);
assertEquals ("EPSG", gmlId.getCodeSpace());
assertEquals ("7019", gmlId.getCode());
}
/**
* Tests {@link AbstractIdentifiedObject#getIdentifier()} with a sub-type of {@code AbstractIdentifiedObject}.
* The use of a subtype will allow {@code getIdentifier()} to build a URN and {@code getId()} to know what to
* insert between {@code "epsg-"} and the code.
*/
@Test
public void testAsSubtype() {
final var identifier = new NamedIdentifier(EPSG, "7019");
final var identifiers = Set.<ReferenceIdentifier>of(identifier);
final var object = new AbstractDatum(properties(identifiers));
final var gmlId = validate(object, identifiers, "epsg-datum-7019");
assertNotNull( gmlId);
assertEquals ("EPSG", gmlId.getCodeSpace());
assertEquals ("7019", gmlId.getCode());
}
/**
* Tests two {@code AbstractIdentifiedObject} declaring the same identifier in the same XML document.
* The {@code getID()} method should detect the collision and select different identifier.
*/
@Test
public void testIdentifierCollision() {
final var properties = new HashMap<String,Object>(4);
assertNull(properties.put("name", "GRS 1980"));
assertNull(properties.put("identifiers", new NamedIdentifier(EPSG, "7019")));
final var o1 = new AbstractIdentifiedObject(properties);
final var o2 = new AbstractIdentifiedObject(properties);
final var o3 = new AbstractIdentifiedObject(properties);
final var o4 = new AbstractIdentifiedObject(properties);
final var context = new Context(0, null, null, null, null, null, null, null, null, null, null);
try {
final String c1, c2, c3, c4;
assertEquals("epsg-7019", c1 = o1.getID());
assertEquals("GRS1980", c2 = o2.getID());
assertEquals("GRS1980-1", c3 = o3.getID());
assertEquals("GRS1980-2", c4 = o4.getID());
assertSame (c1, o1.getID()); // Verify that values are remembered.
assertSame (c2, o2.getID());
assertSame (c3, o3.getID());
assertSame (c4, o4.getID());
} finally {
context.finish();
}
}
/**
* Tests serialization.
*/
@Test
public void testSerialization() {
final var identifiers = Set.<ReferenceIdentifier>of();
final var object = new AbstractIdentifiedObject(properties(identifiers));
final var actual = assertSerializedEquals(object);
assertNotSame(object, actual);
assertNull(validate(actual, identifiers, "GRS1980"), "gmlId");
}
}