use of org.opengis.util.GenericName in project sis by apache.
the class CoordinateOperationMethods method writeIdentification.
/**
* Writes identification info about the given method.
* This method writes the following information:
*
* <ul>
* <li>EPSG codes</li>
* <li>Aliases</li>
* <li>Domain of validity</li>
* </ul>
*/
private void writeIdentification(final OperationMethod method) throws IOException {
final int table = openTag("table class=\"info\"");
/*
* ──────────────── EPSG IDENTIFIERS ────────────────────────────────────
*/
final StringBuilder buffer = new StringBuilder();
for (final ReferenceIdentifier id : method.getIdentifiers()) {
if (Constants.EPSG.equalsIgnoreCase(id.getCodeSpace())) {
if (buffer.length() != 0) {
buffer.append(", ");
}
final boolean isDeprecated = isDeprecated(id);
if (isDeprecated) {
buffer.append("<del>");
}
buffer.append(id.getCode());
if (isDeprecated) {
buffer.append("</del>");
}
}
}
if (buffer.length() != 0) {
final int tr = openTag("tr");
println("th", "EPSG code:");
println("td", buffer);
closeTags(tr);
}
/*
* ──────────────── ALIASES ─────────────────────────────────────────────
*/
buffer.setLength(0);
for (final GenericName alias : method.getAlias()) {
if (buffer.length() != 0) {
buffer.append(", ");
}
final GenericName head = alias.head();
if (head == alias || Constants.EPSG.equalsIgnoreCase(head.toString())) {
buffer.append(alias.tip());
} else {
buffer.append("<span class=\"non-epsg\">").append(head).append(":</span>").append("<code>").append(alias.tip()).append("</code>");
}
}
if (buffer.length() != 0) {
final int tr = openTag("tr");
println("th", "Aliases:");
println("td", buffer);
closeTags(tr);
}
/*
* ──────────────── DOMAIN OF VALIDITY ──────────────────────────────────
*/
buffer.setLength(0);
final DefaultGeographicBoundingBox domain = getDomainOfValidity(method);
if (domain != null) {
openTag("tr");
println("th", "Domain of validity:");
println("td", buffer.append(new Latitude(domain.getSouthBoundLatitude())).append(" to ").append(new Latitude(domain.getNorthBoundLatitude())).append(" and ").append(new Longitude(domain.getWestBoundLongitude())).append(" to ").append(new Longitude(domain.getEastBoundLongitude())));
}
closeTags(table);
}
use of org.opengis.util.GenericName in project sis by apache.
the class MetadataBuilder method addFeatureType.
/**
* Adds descriptions for the given feature.
* Storage location is:
*
* <ul>
* <li>{@code metadata/contentInfo/featureTypes/featureTypeName}</li>
* <li>{@code metadata/contentInfo/featureTypes/featureInstanceCount}</li>
* </ul>
*
* This method returns the feature name for more convenient chaining with
* {@link org.apache.sis.storage.FeatureNaming#add FeatureNaming.add(…)}.
* Note that the {@link FeatureCatalogBuilder} subclasses can also be used for that chaining.
*
* @param type the feature type to add, or {@code null} for no-operation.
* @param occurrences number of instances of the given feature type, or {@code null} if unknown.
* @return the name of the added feature, or {@code null} if none.
*
* @see FeatureCatalogBuilder#define(DefaultFeatureType)
*/
public final GenericName addFeatureType(final DefaultFeatureType type, final Integer occurrences) {
if (type != null) {
final GenericName name = type.getName();
if (name != null) {
final DefaultFeatureTypeInfo info = new DefaultFeatureTypeInfo(name);
if (occurrences != null) {
info.setFeatureInstanceCount(shared(occurrences));
}
addIfNotPresent(featureDescription().getFeatureTypeInfo(), info);
return name;
}
}
return null;
}
use of org.opengis.util.GenericName in project sis by apache.
the class AbstractOperation method toString.
/**
* Returns a string representation of this operation.
* The returned string is for debugging purpose and may change in any future SIS version.
*
* @return a string representation of this operation for debugging purpose.
*/
@Debug
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder(40).append(Classes.getShortClassName(this)).append('[');
final GenericName name = getName();
if (name != null) {
buffer.append('“');
}
buffer.append(name);
if (name != null) {
buffer.append('”');
}
final AbstractIdentifiedType result = getResult();
if (result != null) {
final Object type;
if (result instanceof DefaultAttributeType<?>) {
type = Classes.getShortName(((DefaultAttributeType<?>) result).getValueClass());
} else {
type = result.getName();
}
buffer.append(" : ").append(type);
}
try {
formatResultFormula(buffer.append("] = "));
} catch (IOException e) {
// Should never happen since we write in a StringBuilder.
throw new UncheckedIOException(e);
}
return buffer.toString();
}
use of org.opengis.util.GenericName in project sis by apache.
the class DefaultFeatureType method computeTransientFields.
/**
* Computes transient fields ({@link #assignableTo}, {@link #byName}, {@link #indices}, {@link #isSimple}).
*
* <p>As a side effect, this method checks for missing or duplicated names.</p>
*
* @param properties same content as {@link #properties} (may be the reference to the same list), but
* optionally in a temporarily modifiable list if we want to allow removal of duplicated values.
* See {@code scanPropertiesFrom(FeatureType, Collection)} javadoc for more explanation.
* @throws IllegalArgumentException if two properties have the same name.
*/
private void computeTransientFields(final List<AbstractIdentifiedType> properties) {
final int capacity = Containers.hashMapCapacity(properties.size());
byName = new LinkedHashMap<>(capacity);
indices = new LinkedHashMap<>(capacity);
assignableTo = new HashSet<>(4);
assignableTo.add(super.getName());
scanPropertiesFrom(this, properties);
allProperties = UnmodifiableArrayList.wrap(byName.values().toArray(new AbstractIdentifiedType[byName.size()]));
/*
* Now check if the feature is simple/complex or dense/sparse. We perform this check after we finished
* to create the list of all properties, because some properties may be overridden and we want to take
* in account only the most specific ones.
*/
isSimple = true;
int index = 0;
// Count of mandatory properties.
int mandatory = 0;
for (final Map.Entry<String, AbstractIdentifiedType> entry : byName.entrySet()) {
final int minimumOccurs, maximumOccurs;
final AbstractIdentifiedType property = entry.getValue();
if (property instanceof DefaultAttributeType<?>) {
// Other SIS branches check for AttributeType instead.
minimumOccurs = ((DefaultAttributeType<?>) property).getMinimumOccurs();
maximumOccurs = ((DefaultAttributeType<?>) property).getMaximumOccurs();
isSimple &= (minimumOccurs == maximumOccurs);
} else if (property instanceof FieldType) {
// TODO: check for AssociationRole instead (after GeoAPI upgrade).
minimumOccurs = ((FieldType) property).getMinimumOccurs();
maximumOccurs = ((FieldType) property).getMaximumOccurs();
isSimple = false;
} else {
if (isParameterlessOperation(property)) {
indices.put(entry.getKey(), OPERATION_INDEX);
}
// For feature operations, maximumOccurs is implicitly 0.
continue;
}
if (maximumOccurs != 0) {
isSimple &= (maximumOccurs == 1);
indices.put(entry.getKey(), index++);
if (minimumOccurs != 0) {
mandatory++;
}
}
}
/*
* If some properties use long name of the form "head:tip", creates short aliases containing only the "tip"
* name for convenience, provided that it does not create ambiguity. If a short alias could map to two or
* more properties, then that alias is not added.
*
* In the 'aliases' map below, null values will be assigned to ambiguous short names.
*/
final Map<String, AbstractIdentifiedType> aliases = new LinkedHashMap<>();
for (final AbstractIdentifiedType property : allProperties) {
GenericName name = property.getName();
while (name instanceof ScopedName) {
// Safety against broken implementations.
if (name == (name = ((ScopedName) name).tail()))
break;
String key = name.toString();
// Safety against broken implementations.
if (key == null || (key = key.trim()).isEmpty())
break;
aliases.put(key, aliases.containsKey(key) ? null : property);
}
}
for (final Map.Entry<String, AbstractIdentifiedType> entry : aliases.entrySet()) {
final AbstractIdentifiedType property = entry.getValue();
if (property != null) {
final String tip = entry.getKey();
if (byName.putIfAbsent(tip, property) == null) {
/*
* This block is skipped if there is properties named "tip" and "head:tip".
* The 'indices' value may be null if the property is an operation.
*/
final Integer value = indices.get(property.getName().toString());
if (value != null && indices.put(tip, value) != null) {
// Should never happen.
throw new AssertionError(tip);
}
}
}
}
/*
* Trim the collections. Especially useful when the collections have less that 2 elements.
*/
byName = CollectionsExt.compact(byName);
indices = CollectionsExt.compact(indices);
assignableTo = CollectionsExt.unmodifiableOrCopy(assignableTo);
/*
* Rational for choosing whether the feature is sparse: By default, java.util.HashMap implementation creates
* an internal array of length 16 (see HashMap.DEFAULT_INITIAL_CAPACITY). In addition, the HashMap instance
* itself consumes approximatively 8 "words" in memory. Consequently there is no advantage in using HashMap
* unless the number of properties is greater than 16 + 8 (note: we could specify a smaller initial capacity,
* but the memory consumed by each internal Map.Entry quickly exceed the few saved words). Next, the default
* HashMap threshold is 0.75, so there is again no advantage in using HashMap if we do not expect at least 25%
* of unused properties. Our current implementation arbitrarily sets the threshold to 50%.
*/
final int n = indices.size();
isSparse = (n > 24) && (mandatory <= n / 2);
}
use of org.opengis.util.GenericName in project sis by apache.
the class DefaultAssociationRoleTest method testCyclicAssociation.
/**
* Tests {@code DefaultFeatureType.isAssignableFrom(FeatureType)} and {@code DefaultFeatureType.equals(Object)}
* on a feature type having a bidirectional association to an other feature. This test will fall in an infinite
* loop if the implementation does not have proper guard against infinite recursivity.
*/
@Test
@DependsOnMethod("testBidirectionalAssociation")
public void testCyclicAssociation() {
final NameFactory factory = DefaultFactories.forBuildin(NameFactory.class);
final GenericName nameOfA = factory.createTypeName(null, "A");
final GenericName nameOfB = factory.createTypeName(null, "B");
final GenericName nameOfC = factory.createTypeName(null, "C");
final GenericName nameOfD = factory.createTypeName(null, "D");
/*
* Associations defined only by the FeatureType name.
*/
final DefaultAssociationRole toB = new DefaultAssociationRole(singletonMap(NAME_KEY, "toB"), nameOfB, 1, 1);
final DefaultAssociationRole toC = new DefaultAssociationRole(singletonMap(NAME_KEY, "toC"), nameOfC, 1, 1);
final DefaultAssociationRole toD = new DefaultAssociationRole(singletonMap(NAME_KEY, "toD"), nameOfD, 1, 1);
final DefaultFeatureType typeA = createType(nameOfA, null, toB);
final DefaultFeatureType typeB = createType(nameOfB, null, toC);
final DefaultFeatureType typeC = createType(nameOfC, null, toD);
/*
* Association defined with real FeatureType instance, except for an association to itself.
* Construction of this FeatureType shall cause the resolution of all above FeatureTypes.
*/
final DefaultAssociationRole toAr = new DefaultAssociationRole(singletonMap(NAME_KEY, "toA"), typeA, 1, 1);
final DefaultAssociationRole toBr = new DefaultAssociationRole(singletonMap(NAME_KEY, toB.getName()), typeB, 1, 1);
final DefaultAssociationRole toCr = new DefaultAssociationRole(singletonMap(NAME_KEY, toC.getName()), typeC, 1, 1);
final DefaultFeatureType typeD = createType(nameOfD, null, toAr, toBr, toCr, toD);
/*
* Verify the property given to the constructors. There is no reason for those properties
* to change as they are not the instances to be replaced by the name resolutions, but we
* verify them as a paranoiac check.
*/
assertSame("A.properties", toB, getSingleton(typeA.getProperties(false)));
assertSame("B.properties", toC, getSingleton(typeB.getProperties(false)));
assertSame("C.properties", toD, getSingleton(typeC.getProperties(false)));
assertSame("D.properties", toAr, typeD.getProperty("toA"));
assertSame("D.properties", toBr, typeD.getProperty("toB"));
assertSame("D.properties", toCr, typeD.getProperty("toC"));
assertSame("D.properties", toD, typeD.getProperty("toD"));
/*
* CORE OF THIS TEST: verify that the values of toB, toC and toD have been replaced by the actual
* FeatureType instances. Also verify that as a result, toB.equals(toBr) and toC.equals(toCr).
*/
assertSame("toA", typeA, toAr.getValueType());
assertSame("toB", typeB, toBr.getValueType());
assertSame("toB", typeB, toB.getValueType());
assertSame("toC", typeC, toCr.getValueType());
assertSame("toC", typeC, toC.getValueType());
assertSame("toD", typeD, toD.getValueType());
assertEquals("toB", toB, toBr);
assertEquals("toC", toC, toCr);
/*
* Other equality tests, mostly for verifying that we do not fall in an infinite loop here.
*/
assertFalse("equals", typeA.equals(typeD));
assertFalse("equals", typeD.equals(typeA));
assertFalse("equals", typeB.equals(typeC));
assertFalse("equals", typeC.equals(typeB));
assertFalse("hashCode", typeA.hashCode() == typeB.hashCode());
assertFalse("hashCode", typeC.hashCode() == typeD.hashCode());
}
Aggregations