use of org.opengis.util.ScopedName in project sis by apache.
the class GO_GenericName method getValue.
/**
* Returns the {@code LocalName} or {@code ScopedName} to marshal. Returns {@code null} if the name
* is a {@link TypeName} or a {@link MemberName}, in order to use {@link #getName()} instead.
* Example:
*
* {@preformat xml
* <gml:alias>
* <gco:LocalName codeSpace=\"A code space\">A name in a scope</gco:LocalName>
* </gml:alias>
* }
*
* @return the code for the current name, or {@code null} if none.
*/
@XmlElementRef
public final NameValue getValue() {
final GenericName name = this.name;
final NameValue code;
if (name instanceof LocalName) {
if (name instanceof TypeName || name instanceof MemberName) {
return null;
} else if (FilterByVersion.LEGACY_METADATA.accept()) {
code = new NameValue.Local();
} else {
// ISO 19115-3:2016 does not seem to define gco:LocalName anymore.
code = new NameValue.Scoped();
}
} else if (name instanceof ScopedName) {
code = new NameValue.Scoped();
} else {
return null;
}
code.setName(name);
return code;
}
use of org.opengis.util.ScopedName 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.ScopedName in project sis by apache.
the class TypeBuilder method forName.
/**
* Returns the element of the given name in the given list. The given name does not need to contains
* all elements of a {@link ScopedName}; it can be only the tip (for example {@code "myName"} instead
* of {@code "myScope:myName"}) provided that ignoring the name head does not create ambiguity.
*
* @param types the collection where to search for an element of the given name.
* @param name name of the element to search.
* @return element of the given name, or {@code null} if none were found.
* @throws IllegalArgumentException if the given name is ambiguous.
*/
@SuppressWarnings("null")
final <E extends TypeBuilder> E forName(final List<E> types, final String name) {
// Best type found so far.
E best = null;
// If two types are found at the same depth, the other type.
E ambiguity = null;
// Number of path elements that we had to ignore in the GenericName.
int depth = Integer.MAX_VALUE;
for (final E type : types) {
GenericName candidate = type.getName();
for (int d = 0; candidate != null; d++) {
if (name.equals(candidate.toString())) {
if (d < depth) {
best = type;
ambiguity = null;
depth = d;
break;
}
if (d == depth) {
ambiguity = type;
break;
}
}
if (!(candidate instanceof ScopedName))
break;
candidate = ((ScopedName) candidate).tail();
}
}
if (ambiguity != null) {
throw new IllegalArgumentException(errors().getString(Errors.Keys.AmbiguousName_3, best.getName(), ambiguity.getName(), name));
}
return best;
}
use of org.opengis.util.ScopedName in project sis by apache.
the class DefaultNameSpace method name.
/**
* Represents the identifier of this namespace. Namespace identifiers shall be
* {@linkplain AbstractName#toFullyQualifiedName() fully-qualified names} where
* the following condition holds:
*
* {@preformat java
* assert name.scope().isGlobal() == true;
* }
*
* @return the identifier of this namespace.
*/
@Override
public GenericName name() {
final int depth;
synchronized (this) {
if (path != null) {
return path;
}
depth = depth(this);
final DefaultLocalName[] names = new DefaultLocalName[depth];
DefaultNameSpace scan = this;
for (int i = depth; --i >= 0; ) {
names[i] = new DefaultLocalName(scan.parent, scan.name);
scan = scan.parent;
}
assert depth(scan) == 0 || scan.isGlobal();
path = DefaultScopedName.create(UnmodifiableArrayList.wrap(names));
GenericName truncated = path;
for (int i = depth; --i >= 0; ) {
names[i].fullyQualified = truncated;
truncated = (truncated instanceof ScopedName) ? ((ScopedName) truncated).path() : null;
}
}
/*
* At this point the name is created and ready to be returned. As an optimization,
* defines the name of parents now in order to share subarea of the array we just
* created. The goal is to have less objects in memory.
*/
AbstractName truncated = path;
DefaultNameSpace scan = parent;
while (scan != null && !scan.isGlobal()) {
/*
* If we have a parent, then depth >= 2 and consequently the name is a ScopedName.
* Actually it should be an instance of DefaultScopedName - we known that since we
* created it ourself with the DefaultScopedName.create(...) method call - and we
* know that its tail() implementation creates instance of AbstractName. Given all
* the above, none of the casts on the line below should ever fails, unless there
* is bug in this package.
*/
truncated = (AbstractName) ((ScopedName) truncated).path();
synchronized (scan) {
if (scan.path == null || scan.path.arraySize() < depth) {
scan.path = truncated;
}
}
scan = scan.parent;
}
return path;
}
use of org.opengis.util.ScopedName in project sis by apache.
the class NamesTest method testCreateScopedName.
/**
* Tests {@link Names#createScopedName(GenericName, String, CharSequence)}.
*/
@Test
@DependsOnMethod("testCreateLocalName")
public void testCreateScopedName() {
final LocalName scope = Names.createLocalName("Apache", null, "sis");
final ScopedName name = Names.createScopedName(scope, null, "identifier");
assertSame("path()", scope, name.path());
assertEquals("tail()", "identifier", name.tail().toString());
assertEquals("toString()", "sis:identifier", name.toString());
assertEquals("full", "Apache:sis:identifier", name.toFullyQualifiedName().toString());
assertEquals("tail().full", "Apache:sis:identifier", name.tail().toFullyQualifiedName().toString());
}
Aggregations