Search in sources :

Example 1 with AbstractIdentifiedType

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());
}
Also used : DefaultFeatureType(org.apache.sis.feature.DefaultFeatureType) AbstractIdentifiedType(org.apache.sis.feature.AbstractIdentifiedType) DefaultFeatureTypeTest(org.apache.sis.feature.DefaultFeatureTypeTest) Test(org.junit.Test) DependsOnMethod(org.apache.sis.test.DependsOnMethod)

Example 2 with AbstractIdentifiedType

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());
}
Also used : DefaultFeatureType(org.apache.sis.feature.DefaultFeatureType) AbstractIdentifiedType(org.apache.sis.feature.AbstractIdentifiedType) DefaultFeatureTypeTest(org.apache.sis.feature.DefaultFeatureTypeTest) Test(org.junit.Test) DependsOnMethod(org.apache.sis.test.DependsOnMethod)

Example 3 with AbstractIdentifiedType

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;
}
Also used : FactoryException(org.opengis.util.FactoryException) DefaultFeatureType(org.apache.sis.feature.DefaultFeatureType) CorruptedObjectException(org.apache.sis.util.CorruptedObjectException) AbstractIdentifiedType(org.apache.sis.feature.AbstractIdentifiedType)

Example 4 with AbstractIdentifiedType

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());
}
Also used : Geometry(com.esri.core.geometry.Geometry) DefaultFeatureType(org.apache.sis.feature.DefaultFeatureType) AbstractIdentifiedType(org.apache.sis.feature.AbstractIdentifiedType) DefaultFeatureTypeTest(org.apache.sis.feature.DefaultFeatureTypeTest) Test(org.junit.Test) DependsOnMethod(org.apache.sis.test.DependsOnMethod)

Example 5 with AbstractIdentifiedType

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());
            }
        }
    }
}
Also used : Set(java.util.Set) HashMap(java.util.HashMap) AbstractIdentifiedType(org.apache.sis.feature.AbstractIdentifiedType) GenericName(org.opengis.util.GenericName) DefaultAssociationRole(org.apache.sis.feature.DefaultAssociationRole) AbstractOperation(org.apache.sis.feature.AbstractOperation) DefaultAttributeType(org.apache.sis.feature.DefaultAttributeType) HashMap(java.util.HashMap) Map(java.util.Map)

Aggregations

AbstractIdentifiedType (org.apache.sis.feature.AbstractIdentifiedType)6 DefaultFeatureType (org.apache.sis.feature.DefaultFeatureType)4 DefaultFeatureTypeTest (org.apache.sis.feature.DefaultFeatureTypeTest)3 DependsOnMethod (org.apache.sis.test.DependsOnMethod)3 Test (org.junit.Test)3 GenericName (org.opengis.util.GenericName)2 Geometry (com.esri.core.geometry.Geometry)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 Set (java.util.Set)1 AbstractOperation (org.apache.sis.feature.AbstractOperation)1 DefaultAssociationRole (org.apache.sis.feature.DefaultAssociationRole)1 DefaultAttributeType (org.apache.sis.feature.DefaultAttributeType)1 CorruptedObjectException (org.apache.sis.util.CorruptedObjectException)1 FactoryException (org.opengis.util.FactoryException)1 InternationalString (org.opengis.util.InternationalString)1