use of org.apache.sis.feature.AbstractIdentifiedType in project sis by apache.
the class FeatureTypeBuilderTest method testAddAnonymousIdentifier.
/**
* Tests {@link FeatureTypeBuilder#addAttribute(Class)} where one attribute is an identifier that already has
* the {@code "sis:identifier"} name. This is called "anonymous" because identifiers with an explicit name in
* the data file should use that name instead in the feature type.
*/
@Test
@DependsOnMethod("testAddIdentifierAndGeometry")
public void testAddAnonymousIdentifier() {
final FeatureTypeBuilder builder = new FeatureTypeBuilder();
assertSame(builder, builder.setName("City"));
builder.addAttribute(String.class).setName(AttributeConvention.IDENTIFIER_PROPERTY).addRole(AttributeRole.IDENTIFIER_COMPONENT);
builder.addAttribute(Integer.class).setName("population");
final DefaultFeatureType type = builder.build();
final Iterator<? extends AbstractIdentifiedType> it = type.getProperties(true).iterator();
final AbstractIdentifiedType a0 = it.next();
final AbstractIdentifiedType a1 = it.next();
assertFalse("properties count", it.hasNext());
assertEquals("name", AttributeConvention.IDENTIFIER_PROPERTY, a0.getName());
assertEquals("type", String.class, ((DefaultAttributeType<?>) a0).getValueClass());
assertEquals("name", "population", a1.getName().toString());
assertEquals("type", Integer.class, ((DefaultAttributeType<?>) a1).getValueClass());
}
use of org.apache.sis.feature.AbstractIdentifiedType in project sis by apache.
the class FeatureTypeBuilderTest method testAddAnonymousGeometry.
/**
* Tests {@link FeatureTypeBuilder#addAttribute(Class)} where one attribute is a geometry that already has
* the {@code "sis:geometry"} name. This is called "anonymous" because geometries with an explicit name in
* the data file should use that name instead in the feature type.
*/
@Test
@DependsOnMethod("testAddIdentifierAndGeometry")
public void testAddAnonymousGeometry() {
final FeatureTypeBuilder builder = new FeatureTypeBuilder();
assertSame(builder, builder.setName("City"));
builder.addAttribute(Point.class).setName(AttributeConvention.GEOMETRY_PROPERTY).addRole(AttributeRole.DEFAULT_GEOMETRY);
builder.addAttribute(Integer.class).setName("population");
final DefaultFeatureType type = builder.build();
final Iterator<? extends AbstractIdentifiedType> it = type.getProperties(true).iterator();
final AbstractIdentifiedType a0 = it.next();
final AbstractIdentifiedType a1 = it.next();
final AbstractIdentifiedType a2 = it.next();
assertFalse("properties count", it.hasNext());
assertEquals("name", AttributeConvention.ENVELOPE_PROPERTY, a0.getName());
assertEquals("name", AttributeConvention.GEOMETRY_PROPERTY, a1.getName());
assertEquals("type", Point.class, ((DefaultAttributeType<?>) a1).getValueClass());
assertEquals("name", "population", a2.getName().toString());
assertEquals("type", Integer.class, ((DefaultAttributeType<?>) a2).getValueClass());
}
use of org.apache.sis.feature.AbstractIdentifiedType in project sis by apache.
the class FeatureTypeBuilder method build.
/**
* Builds the feature type from the information and properties specified to this builder.
* One of the {@code setName(…)} methods must have been invoked before this {@code build()} method (mandatory).
* All other methods are optional, but some calls to a {@code add} method are usually needed.
*
* <div class="warning"><b>Warning:</b> In a future SIS version, the return type may be changed to the
* {@code org.opengis.feature.FeatureType} interface. This change is pending GeoAPI revision.</div>
*
* <p>If a feature type has already been built and this builder state has not changed since the
* feature type creation, then the previously created {@code FeatureType} instance is returned.</p>
*
* @return the feature type.
* @throws IllegalStateException if the builder contains inconsistent information.
*
* @see #clear()
*/
@Override
public DefaultFeatureType build() throws IllegalStateException {
if (feature == null) {
/*
* Creates an initial array of property types with up to 3 slots reserved for sis:identifier, sis:geometry
* and sis:envelope operations. At first we presume that there is always an identifier. The identifier slot
* will be removed later if there is none.
*/
// Number of explicitely specified properties.
final int numSpecified = properties.size();
// Number of synthetic properties that may be generated.
int numSynthetic;
int envelopeIndex = -1;
int geometryIndex = -1;
final AbstractIdentifiedType[] identifierTypes;
if (identifierCount == 0) {
numSynthetic = 0;
identifierTypes = null;
} else {
numSynthetic = 1;
identifierTypes = new AbstractIdentifiedType[identifierCount];
}
if (defaultGeometry != null) {
envelopeIndex = numSynthetic++;
if (!AttributeConvention.GEOMETRY_PROPERTY.equals(defaultGeometry.getName())) {
geometryIndex = numSynthetic++;
}
}
final AbstractIdentifiedType[] propertyTypes = new AbstractIdentifiedType[numSynthetic + numSpecified];
int propertyCursor = numSynthetic;
int identifierCursor = 0;
for (int i = 0; i < numSpecified; i++) {
final PropertyTypeBuilder builder = properties.get(i);
final AbstractIdentifiedType instance = builder.build();
propertyTypes[propertyCursor] = instance;
/*
* Collect the attributes to use as identifier components while we loop over all properties.
* A NullPointerException or an ArrayIndexOutOfBoundsException in this block would mean that
* identifierCount field has not been updated correctly by an addRole(AttributeRole) method.
*/
if (builder.isIdentifier()) {
identifierTypes[identifierCursor++] = instance;
}
/*
* If there is a default geometry, add a link named "sis:geometry" to that geometry.
* It may happen that the property created by the user is already named "sis:geometry",
* in which case we will avoid to duplicate the property.
*/
if (builder == defaultGeometry && geometryIndex >= 0) {
if (propertyTypes[geometryIndex] != null) {
/*
* Assuming that there is no bug in our implementation, this error could happen if the user
* has modified this FeatureTypeBuilder in another thread during this build() execution.
*/
throw new CorruptedObjectException();
}
propertyTypes[geometryIndex] = FeatureOperations.link(name(AttributeConvention.GEOMETRY_PROPERTY), instance);
}
propertyCursor++;
}
/*
* Create the "envelope" operation only after we created all other properties.
* Actually it is okay if the 'propertyTypes' array still contains null elements not needed for envelope calculation
* like "sis:identifier", since FeatureOperations.envelope(…) constructor ignores any property which is not for a value.
*/
if (envelopeIndex >= 0)
try {
propertyTypes[envelopeIndex] = FeatureOperations.envelope(name(AttributeConvention.ENVELOPE_PROPERTY), null, propertyTypes);
} catch (FactoryException e) {
throw new IllegalStateException(e);
}
/*
* If a synthetic identifier need to be created, create it now as the first property.
* It may happen that the user provided a single identifier component already named
* "sis:identifier", in which case we avoid to duplicate the property.
*/
if (identifierTypes != null) {
if (identifierCursor != identifierTypes.length) {
/*
* Assuming that there is no bug in our implementation, this error could happen if the user
* has modified this FeatureTypeBuilder in another thread during this build() execution.
*/
throw new CorruptedObjectException();
}
if (AttributeConvention.IDENTIFIER_PROPERTY.equals(identifierTypes[0].getName())) {
if (identifierCursor > 1) {
throw new IllegalStateException(Resources.format(Resources.Keys.PropertyAlreadyExists_2, getDisplayName(), AttributeConvention.IDENTIFIER_PROPERTY));
}
System.arraycopy(propertyTypes, 1, propertyTypes, 0, --propertyCursor);
} else {
propertyTypes[0] = FeatureOperations.compound(name(AttributeConvention.IDENTIFIER_PROPERTY), idDelimiter, idPrefix, idSuffix, identifierTypes);
}
}
feature = new DefaultFeatureType(identification(), isAbstract(), superTypes.toArray(new DefaultFeatureType[superTypes.size()]), ArraysExt.resize(propertyTypes, propertyCursor));
}
return feature;
}
use of org.apache.sis.feature.AbstractIdentifiedType in project sis by apache.
the class FeatureTypeBuilderTest method testAddIdentifierAndGeometry.
/**
* Tests {@link FeatureTypeBuilder#addAttribute(Class)} where one property is an identifier
* and another property is the geometry.
*/
@Test
@DependsOnMethod("testAddAttribute")
public void testAddIdentifierAndGeometry() {
final FeatureTypeBuilder builder = new FeatureTypeBuilder();
assertSame(builder, builder.setName("scope", "test"));
assertSame(builder, builder.setIdentifierDelimiters("-", "pref.", null));
builder.addAttribute(String.class).setName("name").addRole(AttributeRole.IDENTIFIER_COMPONENT);
builder.addAttribute(Geometry.class).setName("shape").setCRS(HardCodedCRS.WGS84).addRole(AttributeRole.DEFAULT_GEOMETRY);
final DefaultFeatureType type = builder.build();
assertEquals("name", "scope:test", type.getName().toString());
assertFalse("isAbstract", type.isAbstract());
final Iterator<? extends AbstractIdentifiedType> it = type.getProperties(true).iterator();
final AbstractIdentifiedType a0 = it.next();
final AbstractIdentifiedType a1 = it.next();
final AbstractIdentifiedType a2 = it.next();
final AbstractIdentifiedType a3 = it.next();
final AbstractIdentifiedType a4 = it.next();
assertFalse("properties count", it.hasNext());
assertEquals("name", AttributeConvention.IDENTIFIER_PROPERTY, a0.getName());
assertEquals("name", AttributeConvention.ENVELOPE_PROPERTY, a1.getName());
assertEquals("name", AttributeConvention.GEOMETRY_PROPERTY, a2.getName());
assertEquals("name", "name", a3.getName().toString());
assertEquals("name", "shape", a4.getName().toString());
}
use of org.apache.sis.feature.AbstractIdentifiedType in project sis by apache.
the class FeatureTypeBuilder method initialize.
/**
* Initializes this builder to the value of the given type.
* The caller is responsible to invoke {@link #clear()} (if needed) before this method.
*/
private void initialize(final DefaultFeatureType template) {
super.initialize(template);
feature = template;
isAbstract = template.isAbstract();
superTypes.addAll(template.getSuperTypes());
/*
* For each attribute and association, wrap those properties in a builder.
* For each operation, wrap them in pseudo-builder only if the operation
* is not one of the operations automatically generated by this builder.
*/
final Map<String, Set<AttributeRole>> propertyRoles = new HashMap<>();
for (final AbstractIdentifiedType property : template.getProperties(false)) {
PropertyTypeBuilder builder;
if (property instanceof DefaultAttributeType<?>) {
builder = new AttributeTypeBuilder<>(this, (DefaultAttributeType<?>) property);
} else if (property instanceof DefaultAssociationRole) {
builder = new AssociationRoleBuilder(this, (DefaultAssociationRole) property);
} else {
// Do not create OperationWrapper now - see below.
builder = null;
}
/*
* If the property name is one of our (Apache SIS specific) conventional names, try to reconstitute
* the attribute roles that caused FeatureTypeBuilder to produce such property. Those roles usually
* need to be applied on the source properties used for calculating the current property. There is
* usually at most one role for each source property, but we nevertheless allow an arbitrary amount.
*/
final AttributeRole role;
final GenericName name = property.getName();
if (AttributeConvention.IDENTIFIER_PROPERTY.equals(name)) {
role = AttributeRole.IDENTIFIER_COMPONENT;
} else if (AttributeConvention.GEOMETRY_PROPERTY.equals(name)) {
role = AttributeRole.DEFAULT_GEOMETRY;
} else if (AttributeConvention.ENVELOPE_PROPERTY.equals(name)) {
// If "sis:envelope" is an operation, skip it completely.
// It will be recreated if a default geometry exists.
role = null;
} else {
if (builder == null) {
// For all unknown operation, wrap as-is.
builder = new OperationWrapper(this, property);
}
role = null;
}
if (role != null) {
final Set<AttributeRole> rc = Collections.singleton(role);
if (property instanceof AbstractOperation) {
for (final String dependency : ((AbstractOperation) property).getDependencies()) {
propertyRoles.merge(dependency, rc, AttributeRole::merge);
}
} else {
propertyRoles.merge(name.toString(), rc, AttributeRole::merge);
}
}
if (builder != null) {
properties.add(builder);
}
}
/*
* At this point we finished to collect information about the attribute roles.
* Now assign those roles to the attribute builders. Note that some roles may
* be ignored if we didn't found a suitable builder. The roles inference done
* in this constructor is only a "best effort".
*/
if (!propertyRoles.isEmpty()) {
for (final Map.Entry<String, Set<AttributeRole>> entry : propertyRoles.entrySet()) {
final PropertyTypeBuilder property = forName(properties, entry.getKey());
if (property instanceof AttributeTypeBuilder<?>) {
((AttributeTypeBuilder<?>) property).roles().addAll(entry.getValue());
}
}
}
}
Aggregations