blob: a70df46294e4a3406bfc007661b01ed9e2ad32ff [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.feature;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.sis.test.DependsOn;
import static org.apache.sis.test.Assert.*;
/**
* Tests some default method implementations provided in {@link AbstractFeature}.
* This test also has the side-effect of testing {@link Validator} with attribute
* instances that are not {@link AbstractAttribute}.
*
* <p>This class inherits all tests defined in {@link FeatureTestCase}.</p>
*
* @author Martin Desruisseaux (Geomatys)
* @version 0.8
* @since 0.8
* @module
*/
@DependsOn({
DenseFeatureTest.class,
SparseFeatureTest.class
})
public final strictfp class AbstractFeatureTest extends FeatureTestCase {
/**
* A feature implementation on top of {@link AbstractFeature}. This class has more code than strictly necessary
* since we need to reproduce some of the verifications performed by the Apache SIS supported implementations
* in order to get the tests to pass.
*/
@SuppressWarnings("serial")
private static final class CustomFeature extends AbstractFeature implements Cloneable {
/**
* All property values. For this test we use a {@code java.util.Map}. However users who want to provide
* their own {@link AbstractFeature} implementation should consider to use plain Java fields instead.
* If a feature backed by a {@code java.util.Map} is really wanted, then {@link SparseFeature} should
* be considered.
*/
private HashMap<String,Object> values = new HashMap<>();
/**
* Creates a new feature of the given type. This constructor adds immediately the default values into
* the {@link #values} map and a modifiable list (even if empty) for all properties that are collections.
*/
CustomFeature(final DefaultFeatureType type) {
super(type);
for (final AbstractIdentifiedType pt : type.getProperties(true)) {
if (pt instanceof DefaultAttributeType<?>) {
Object value = ((DefaultAttributeType<?>) pt).getDefaultValue();
if (isMultiValued(pt)) {
value = new ArrayList<>(PropertyView.singletonOrEmpty(value));
}
if (value != null) {
values.put(pt.getName().toString(), value);
}
}
}
}
/**
* Returns {@code true} if the given property can contains more than one value.
*/
private static boolean isMultiValued(final AbstractIdentifiedType pt) {
return (pt instanceof DefaultAttributeType<?>) && (((DefaultAttributeType<?>) pt).getMaximumOccurs() > 1);
}
/**
* Returns the value for the property of the given name, or {@code null} if none.
* This method does not verify if it should return the default value or a collection.
* In this simple implementation, those verifications are done at construction time.
*
* <p>A robust implementation would verify that the given name is legal.
* For this test, be skip that verification.</p>
*/
@Override
public Object getPropertyValue(final String name) {
return values.get(name);
}
/**
* Sets the value for the property of the given name. In order to allow the tests to pass,
* we need to reproduce in this method some of the verifications performed by the
* {@link SingletonAttribute} and {@link MultiValuedAttribute} implementations.
*/
@Override
public void setPropertyValue(final String name, Object value) {
final AbstractIdentifiedType type = getType().getProperty(name);
final boolean isMultiValued = isMultiValued(type);
if (isMultiValued && !(value instanceof Collection<?>)) {
value = new ArrayList<>(PropertyView.singletonOrEmpty(value));
}
if (value != null) {
final Class<?> base;
if (type instanceof DefaultAttributeType<?>) {
base = ((DefaultAttributeType<?>) type).getValueClass();
} else {
base = FeatureType.class;
}
for (final Object element : (isMultiValued ? (Iterable<?>) value : PropertyView.singletonOrEmpty(value))) {
if (!base.isInstance(element)) {
throw new ClassCastException("Can not cast " + value.getClass() + " to " + base + " in " + name + '.');
}
}
}
values.put(name, value);
}
/**
* Returns a close of this feature.
*/
@Override
@SuppressWarnings("unchecked")
public CustomFeature clone() {
final CustomFeature c;
try {
c = (CustomFeature) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
c.values = (HashMap<String,Object>) c.values.clone();
return c;
}
}
/**
* Relax the requirements about {@link AbstractFeature#getProperty(String)} to return the exact same instance.
*/
@Override
boolean assertSameProperty(final String name, final Property expected, final boolean modified) {
final Property actual = (Property) feature.getProperty(name);
assertSame("name", expected.getName(), actual.getName());
if (!modified) {
assertSame("value", expected.getValue(), actual.getValue());
}
return actual == expected;
}
/**
* Creates a new feature for the given type.
*/
@Override
final AbstractFeature createFeature(final DefaultFeatureType type) {
return new CustomFeature(type);
}
/**
* Clones the {@link #feature} instance.
*/
@Override
final AbstractFeature cloneFeature() throws CloneNotSupportedException {
return ((CustomFeature) feature).clone();
}
// Inherit all tests from the super-class.
}