Author: desruisseaux
Date: Wed Apr 20 13:01:55 2016
New Revision: 1740141
URL: http://svn.apache.org/viewvc?rev=1740141&view=rev
Log:
Complete (for now) FeatureTypeBuilder refactoring.
All 'addProperty(...)' methods have been replaced by a smaller amount of
'addFoo(...)'
methods, which return a Property builder on which the caller can invoke setter
methods.
Removed:
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/AttributeTypeBuilder.java
sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/internal/feature/AttributeTypeBuilderTest.java
Modified:
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Builder.java
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FeatureTypeBuilder.java
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/NameConvention.java
sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/internal/feature/FeatureTypeBuilderTest.java
sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/internal/feature/NameConventionTest.java
sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/TestRunner.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXConstants.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXReader.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GPXWriter100.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/gpx/GroupPointsAsPolylineOperation.java
sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXReaderTest.java
sis/branches/JDK8/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/gpx/GPXWriterTest.java
Modified:
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java?rev=1740141&r1=1740140&r2=1740141&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/FeatureOperations.java
[UTF-8] Wed Apr 20 13:01:55 2016
@@ -187,7 +187,7 @@ public final class FeatureOperations ext
* forwarded to the corresponding single property.
*
* @param identification the name and other information to be given to
the operation.
- * @param delimiter the characters to use a delimiter between
each single property value.
+ * @param delimiter the characters to use as delimiter between
each single property value.
* @param prefix characters to use at the beginning of the
concatenated string, or {@code null} if none.
* @param suffix characters to use at the end of the
concatenated string, or {@code null} if none.
* @param singleAttributes identification of the single attributes (or
operations producing attributes) to concatenate.
Modified:
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Builder.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Builder.java?rev=1740141&r1=1740140&r2=1740141&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Builder.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Builder.java
[UTF-8] Wed Apr 20 13:01:55 2016
@@ -19,29 +19,30 @@ package org.apache.sis.internal.feature;
import java.util.HashMap;
import java.util.Map;
import org.opengis.util.GenericName;
-import org.apache.sis.feature.DefaultFeatureType;
+import org.apache.sis.feature.AbstractIdentifiedType;
+import org.apache.sis.util.resources.Vocabulary;
import org.apache.sis.util.ArgumentChecks;
-// Branch-dependent imports
-import org.opengis.feature.IdentifiedType;
-
/**
* Base class of feature and attribute builders.
* This base class provide the method needed for filling the {@code
identification} map.
*
+ * @param <T> the builder subclass. It is subclass responsibility to ensure
that {@code this}
+ * is assignable to {@code <T>}; this {@code Builder} class can not
verify that.
+ *
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
* @since 0.7
* @version 0.7
* @module
*/
-abstract class Builder {
+abstract class Builder<T extends Builder<T>> {
/**
* The feature name, definition, designation and description.
* The name is mandatory; all other information are optional.
*/
- final Map<String,Object> identification = new HashMap<>();
+ final Map<String,Object> identification = new HashMap<>(4);
/**
* Creates a new builder instance.
@@ -52,49 +53,65 @@ abstract class Builder {
/**
* Resets this builder to its initial state. After invocation of this
method,
* this builder is in the same state than after construction.
+ *
+ * @return {@code this} for allowing method calls chaining.
*/
- public void clear() {
+ @SuppressWarnings("unchecked")
+ public T clear() {
identification.clear();
+ return (T) this;
}
/**
- * Sets this builder state to properties inferred from the given type.
+ * Creates a generic name from the given scope and local part.
+ * An empty scope means no scope. A {@code null} scope means the
+ * {@linkplain FeatureTypeBuilder#setDefaultScope(String) default scope}.
+ *
+ * @param scope the scope of the name to create, or {@code null} if
the name is local.
+ * @param localPart the local part of the generic name (can not be {@code
null}).
*/
- final void copy(final IdentifiedType template) {
- clear();
- setName (template.getName());
- setDescription(template.getDescription());
- setDefinition (template.getDefinition());
- setDesignation(template.getDesignation());
- }
+ abstract GenericName name(String scope, String localPart);
/**
- * Sets the feature type name as a simple string without scope.
- * The name will be an instance of {@link org.opengis.util.LocalName}.
+ * Sets the feature type name as a simple string with the default scope.
+ * The default scope is the value specified by the last call to
+ * {@link FeatureTypeBuilder#setDefaultScope(String)}.
+ *
+ * <p>The name will be an instance of {@link org.opengis.util.LocalName}
if no default scope
+ * has been specified, or an instance of {@link
org.opengis.util.ScopedName} otherwise.</p>
*
* <p>This convenience method creates a {@link GenericName} instance,
* then delegates to {@link #setName(GenericName)}.</p>
*
- * @param localPart the local part of the generic name (can not be {@code
null}).
+ * @param localPart the local part of the generic name (can not be
{@code null}).
+ * @return {@code this} for allowing method calls chaining.
*/
- public void setName(String localPart) {
- ArgumentChecks.ensureNonNull("localPart", localPart);
- setName(name(null, localPart));
+ public T setName(String localPart) {
+ ArgumentChecks.ensureNonEmpty("localPart", localPart);
+ return setName(name(null, localPart));
}
/**
* Sets the feature type name as a string in the given scope.
- * If the given scope is non-null, then the name will be an instance of
{@link org.opengis.util.ScopedName}.
+ * If a {@linkplain FeatureTypeBuilder#setDefaultScope(String) default
scope} was specified,
+ * this method override it.
+ *
+ * <p>The name will be an instance of {@link org.opengis.util.LocalName}
if the given scope
+ * is {@code null} or empty, or an instance of {@link
org.opengis.util.ScopedName} otherwise.</p>
*
* <p>This convenience method creates a {@link GenericName} instance,
* then delegates to {@link #setName(GenericName)}.</p>
*
- * @param scope the scope of the name to create, or {@code null} if
the name is local.
- * @param localPart the local part of the generic name (can not be {@code
null}).
- */
- public void setName(String scope, String localPart) {
- ArgumentChecks.ensureNonNull("localPart", localPart);
- setName(name(scope, localPart));
+ * @param scope the scope of the name to create, or {@code null} if
the name is local.
+ * @param localPart the local part of the generic name (can not be
{@code null}).
+ * @return {@code this} for allowing method calls chaining.
+ */
+ public T setName(String scope, String localPart) {
+ ArgumentChecks.ensureNonEmpty("localPart", localPart);
+ if (scope == null) {
+ scope = ""; // For preventing the
use of default scope.
+ }
+ return setName(name(scope, localPart));
}
/**
@@ -105,13 +122,16 @@ abstract class Builder {
* all {@code setName(…)} convenience methods in this builder delegate to
this method.
* Consequently this method can be used as a central place where to
control the creation of all names.</div>
*
- * @param name the generic name (can not be {@code null}).
+ * @param name the generic name (can not be {@code null}).
+ * @return {@code this} for allowing method calls chaining.
*
- * @see DefaultFeatureType#NAME_KEY
+ * @see AbstractIdentifiedType#NAME_KEY
*/
- public void setName(GenericName name) {
+ @SuppressWarnings("unchecked")
+ public T setName(GenericName name) {
ArgumentChecks.ensureNonNull("name", name);
- identification.put(DefaultFeatureType.NAME_KEY, name);
+ identification.put(AbstractIdentifiedType.NAME_KEY, name);
+ return (T) this;
}
/**
@@ -121,49 +141,58 @@ abstract class Builder {
* @return the current {@code FeatureType} name, or {@code null} if the
name has not yet been specified.
*/
public GenericName getName() {
- return (GenericName) identification.get(DefaultFeatureType.NAME_KEY);
+ return (GenericName)
identification.get(AbstractIdentifiedType.NAME_KEY);
}
/**
- * Sets optional information beyond that required for concise definition
of the element.
- * The description may assist in understanding the feature scope and
application.
- *
- * @param description information beyond that required for concise
definition of the element, or {@code null} if none.
- *
- * @see DefaultFeatureType#DESCRIPTION_KEY
+ * Returns the name to use for displaying error messages.
*/
- public void setDescription(CharSequence description) {
- identification.put(DefaultFeatureType.DESCRIPTION_KEY, description);
+ final String getDisplayName() {
+ final GenericName name = getName();
+ return (name != null) ? name.toString() :
Vocabulary.getResources(identification).getString(Vocabulary.Keys.Unnamed);
}
/**
- * Sets a natural language designator for the element.
- * This can be used as an alternative to the {@linkplain #getName() name}
in user interfaces.
+ * Sets a concise definition of the element.
*
- * @param designation a natural language designator for the element, or
{@code null} if none.
+ * @param definition a concise definition of the element, or {@code null}
if none.
+ * @return {@code this} for allowing method calls chaining.
*
- * @see DefaultFeatureType#DESIGNATION_KEY
+ * @see AbstractIdentifiedType#DEFINITION_KEY
*/
- public void setDesignation(CharSequence designation) {
- identification.put(DefaultFeatureType.DESIGNATION_KEY, designation);
+ @SuppressWarnings("unchecked")
+ public T setDefinition(CharSequence definition) {
+ identification.put(AbstractIdentifiedType.DEFINITION_KEY, definition);
+ return (T) this;
}
/**
- * Sets a concise definition of the element.
+ * Sets a natural language designator for the element.
+ * This can be used as an alternative to the {@linkplain #getName() name}
in user interfaces.
*
- * @param definition a concise definition of the element, or {@code null}
if none.
+ * @param designation a natural language designator for the element, or
{@code null} if none.
+ * @return {@code this} for allowing method calls chaining.
*
- * @see DefaultFeatureType#DEFINITION_KEY
+ * @see AbstractIdentifiedType#DESIGNATION_KEY
*/
- public void setDefinition(CharSequence definition) {
- identification.put(DefaultFeatureType.DEFINITION_KEY, definition);
+ @SuppressWarnings("unchecked")
+ public T setDesignation(CharSequence designation) {
+ identification.put(AbstractIdentifiedType.DESIGNATION_KEY,
designation);
+ return (T) this;
}
/**
- * Creates a generic name from the given scope and local part.
+ * Sets optional information beyond that required for concise definition
of the element.
+ * The description may assist in understanding the feature scope and
application.
*
- * @param scope the scope of the name to create, or {@code null} if
the name is local.
- * @param localPart the local part of the generic name (can not be {@code
null}).
+ * @param description information beyond that required for concise
definition of the element, or {@code null} if none.
+ * @return {@code this} for allowing method calls chaining.
+ *
+ * @see AbstractIdentifiedType#DESCRIPTION_KEY
*/
- abstract GenericName name(String scope, String localPart);
+ @SuppressWarnings("unchecked")
+ public T setDescription(CharSequence description) {
+ identification.put(AbstractIdentifiedType.DESCRIPTION_KEY,
description);
+ return (T) this;
+ }
}
Modified:
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FeatureTypeBuilder.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FeatureTypeBuilder.java?rev=1740141&r1=1740140&r2=1740141&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FeatureTypeBuilder.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/FeatureTypeBuilder.java
[UTF-8] Wed Apr 20 13:01:55 2016
@@ -18,15 +18,12 @@ package org.apache.sis.internal.feature;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opengis.util.GenericName;
import org.opengis.util.NameFactory;
-import org.opengis.util.ScopedName;
import org.opengis.util.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.feature.AbstractOperation;
@@ -35,15 +32,16 @@ import org.apache.sis.feature.DefaultAtt
import org.apache.sis.feature.DefaultFeatureType;
import org.apache.sis.feature.FeatureOperations;
import org.apache.sis.internal.system.DefaultFactories;
+import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.util.NullArgumentException;
+import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.ArgumentChecks;
-
-import static org.apache.sis.internal.feature.NameConvention.*;
+import org.apache.sis.util.ArraysExt;
// Branch-dependent imports
import org.opengis.feature.AttributeType;
-import org.opengis.feature.FeatureAssociationRole;
+import org.opengis.feature.Feature;
import org.opengis.feature.FeatureType;
-import org.opengis.feature.Operation;
import org.opengis.feature.PropertyType;
@@ -60,23 +58,24 @@ import org.opengis.feature.PropertyType;
* @since 0.7
* @version 0.7
* @module
+ *
+ * @see org.apache.sis.parameter.ParameterBuilder
*/
-public class FeatureTypeBuilder extends Builder {
+public class FeatureTypeBuilder extends Builder<FeatureTypeBuilder> {
/**
* The factory to use for creating names.
*/
private final NameFactory nameFactory;
/**
- * The feature properties. Entries in this map are added by invoking one
of the
- * {@code addProperty(…)} methods defined in this class.
+ * Builders for the properties of this feature.
*/
- private final Map<GenericName,PropertyType> properties = new
LinkedHashMap<>();
+ private final List<Property<?>> properties;
/**
* The parent of the feature to create. By default, new features have no
parent.
*/
- private final List<FeatureType> superTypes = new ArrayList<>();
+ private final List<FeatureType> superTypes;
/**
* Whether the feature type is abstract. The default value is {@code
false}.
@@ -84,10 +83,31 @@ public class FeatureTypeBuilder extends
private boolean isAbstract;
/**
- * If {@link #idAttributes} is non-null, an optional prefix to insert
before the
- * {@linkplain FeatureOperations#compound compound key} named {@code
"@id"}.
+ * The default scope to use when {@link #name(String, String)} is invoked
with a null scope.
+ *
+ * @see #setDefaultScope(String)
+ */
+ private String defaultScope;
+
+ /**
+ * The default minimum number of property values.
+ *
+ * @see #setDefaultCardinality(int, int)
+ */
+ private int defaultMinimumOccurs;
+
+ /**
+ * The default maximum number of property values.
+ *
+ * @see #setDefaultCardinality(int, int)
+ */
+ private int defaultMaximumOccurs;
+
+ /**
+ * If {@link #idAttributes} is non-null, an optional prefix or suffix to
insert before
+ * or after the {@linkplain FeatureOperations#compound compound key} named
{@code "@id"}.
*/
- private String idPrefix;
+ private String idPrefix, idSuffix;
/**
* If {@link #idAttributes} is non-null and contains more than one value,
the separator to insert between
@@ -96,556 +116,643 @@ public class FeatureTypeBuilder extends
private String idDelimiter;
/**
- * The names of attributes to use in a {@linkplain
FeatureOperations#compound compound key} named {@code "@id"},
- * or {@code null} if none. If this array contains only one name and
{@link #idPrefix} is null, then {@code "@id"}
- * will be a {@linkplain FeatureOperations#link link} to the attribute
named {@code idAttributes[0]}.
+ * The attributes to use in a {@linkplain FeatureOperations#compound
compound key} named {@code "@id"},
+ * or {@code null} if none. If this array contains only one property and
{@link #idPrefix} is null,
+ * then {@code "@id"} will be a {@linkplain FeatureOperations#link link}
to {@code idAttributes[0]}.
*/
- private GenericName[] idAttributes;
+ private final List<Property<?>> idAttributes;
/**
- * The name of the default geometry attribute, or {@code null} if none.
+ * The default geometry attribute, or {@code null} if none.
+ *
+ * @see NameConvention#DEFAULT_GEOMETRY_PROPERTY
*/
- private GenericName defGeomAttribute;
+ private Property<?> defaultGeometry;
/**
- * Creates a new builder instance.
+ * Creates a new builder instance using the default name factory.
*/
public FeatureTypeBuilder() {
- nameFactory = DefaultFactories.forBuildin(NameFactory.class);
+ this(DefaultFactories.forBuildin(NameFactory.class));
}
/**
- * Resets this builder to its initial state. After invocation of this
method,
- * this builder is in the same state than after construction.
+ * Creates a new builder instance using the given name factory.
+ *
+ * @param factory the factory to use for creating names.
*/
- @Override
- public void clear() {
- super.clear();
- properties.clear();
- superTypes.clear();
- isAbstract = false;
- idPrefix = null;
- idDelimiter = null;
- idAttributes = null;
- defGeomAttribute = null;
+ public FeatureTypeBuilder(final NameFactory factory) {
+ nameFactory = factory;
+ properties = new ArrayList<>();
+ superTypes = new ArrayList<>();
+ idAttributes = new ArrayList<>();
+ idDelimiter = ":";
+ defaultMinimumOccurs = 1;
+ defaultMaximumOccurs = 1;
}
/**
- * Sets this builder state to properties inferred from the given feature
type.
- * This method fetches the {@code FeatureType} name, definition,
designation, description, super-types,
- * abstract flag, and list of properties from the given instance.
- *
- * <p>This method is useful when an existing instance is to be used as a
template.</p>
+ * Resets this builder to its initial state. After invocation of this
method,
+ * this builder is in the same state than after construction.
*
- * @param template feature type to copy parameters from.
+ * @return {@code this} for allowing method calls chaining.
*/
- public void copy(final FeatureType template) {
- super.copy(template);
- setAbstract(template.isAbstract());
- Set<? extends FeatureType> parents = template.getSuperTypes();
- setSuperTypes(parents.toArray(new FeatureType[parents.size()]));
- for (PropertyType pt : template.getProperties(false)) {
- addProperty(pt);
- }
+ @Override
+ public FeatureTypeBuilder clear() {
+ super.clear();
+ properties .clear();
+ superTypes .clear();
+ idAttributes.clear();
+ idDelimiter = ":";
+ idPrefix = null;
+ idSuffix = null;
+ isAbstract = false;
+ defaultGeometry = null;
+ defaultMinimumOccurs = 1;
+ defaultMaximumOccurs = 1;
+ return this;
}
/**
* Sets whether the feature type is abstract.
* If this method is not invoked, then the default value is {@code false}.
*
- * @param isAbstract whether the feature type is abstract.
+ * @param isAbstract whether the feature type is abstract.
+ * @return {@code this} for allowing method calls chaining.
*/
- public void setAbstract(final boolean isAbstract) {
+ public FeatureTypeBuilder setAbstract(final boolean isAbstract) {
this.isAbstract = isAbstract;
+ return this;
}
/**
* Sets the parent types (or super-type) from which to inherit properties.
* If this method is not invoked, then the default value is to have no
parent.
*
- * @param parents the parent types from which to inherit properties, or
an empty array if none.
+ * @param parents the parent types from which to inherit properties, or
an empty array if none.
+ * @return {@code this} for allowing method calls chaining.
*/
- public void setSuperTypes(final FeatureType... parents) {
+ public FeatureTypeBuilder setSuperTypes(final FeatureType... parents) {
ArgumentChecks.ensureNonNull("parents", parents);
superTypes.clear();
superTypes.addAll(Arrays.asList(parents));
+ return this;
}
/**
- * Define an id operation composed of the given attributes.
+ * Sets the scope to use by default when {@link #setName(String)} is
invoked.
*
- * @param prefix generated id prefix
- * @param separator generated id separator between attribute values
- * @param attributes attributes used in the id operation
+ * @param scope the new default scope, or {@code null} if none.
+ * @return {@code this} for allowing method calls chaining.
*/
- public void setIdOperation(final String prefix, final String separator,
final GenericName... attributes) {
- idPrefix = prefix;
- idDelimiter = separator;
- idAttributes = attributes.clone();
- properties.put(ID_PROPERTY, null); //
Add placeholder
+ public FeatureTypeBuilder setDefaultScope(final String scope) {
+ defaultScope = scope;
+ return this;
}
/**
- * Define a default geometry link operation.
+ * Sets the default minimum and maximum number of property values.
+ * Those defaults will applied to newly created attributes or associations,
+ * for example in next calls to {@link #addAttribute(Class)}.
*
- * @param attribute referenced attribute
- */
- public void setDefaultGeometryOperation(final GenericName attribute) {
- defGeomAttribute = attribute;
- properties.put(DEFAULT_GEOMETRY_PROPERTY, null); //
Add placeholder
- properties.put(ENVELOPE_PROPERTY, null);
- }
-
- /**
- * Add a new property to the feature type.
- * Property will have a minimum and maximum occurrence of one, no
characteristics
- * and no default value.
+ * <p>If this method is not invoked, then the default cardinality is [1 …
1].</p>
*
- * @param localPart property generic name tip part
- * @param valueClass property value class
- * @return created property type
- */
- public <V> AttributeType<V> addProperty(final String localPart, final
Class<V> valueClass) {
- return addProperty(name(null, localPart), valueClass);
- }
-
- /**
- * Add a new property to the feature type.
- * Property will have a minimum and maximum occurrence of one, no
characteristics
- * and no default value.
- *
- * @param scope property generic name scope part
- * @param localPart property generic name tip part
- * @param valueClass property value class
- * @return created property type
- */
- public <V> AttributeType<V> addProperty(final String scope, final String
localPart, final Class<V> valueClass) {
- return addProperty(name(scope, localPart), valueClass);
- }
-
- /**
- * Add a new property to the feature type.
- * Property will have a minimum and maximum occurrence of one, no
characteristics
- * and no default value.
+ * @param minimumOccurs new default minimum number of property values.
+ * @param maximumOccurs new default maximum number of property values.
+ * @return {@code this} for allowing method calls chaining.
*
- * @param name property name
- * @param valueClass property value class
- * @return created property type
+ * @see Property#setCardinality(int, int)
*/
- public <V> AttributeType<V> addProperty(final GenericName name, final
Class<V> valueClass) {
- return addProperty(name, valueClass, (V) null);
+ public FeatureTypeBuilder setDefaultCardinality(final int minimumOccurs,
final int maximumOccurs) {
+ if (minimumOccurs < 0 || maximumOccurs < minimumOccurs) {
+ throw new
IllegalArgumentException(errors().getString(Errors.Keys.IllegalRange_2,
minimumOccurs, maximumOccurs));
+ }
+ defaultMinimumOccurs = minimumOccurs;
+ defaultMaximumOccurs = maximumOccurs;
+ return this;
}
/**
- * Add a new property to the feature type.
- * Property will have a minimum and maximum occurrence of one, no default
value
- * and the given {@code CoordinateReferenceSystem} characteristic.
+ * Sets the prefix, suffix and delimiter to use when formatting a compound
identifier made of two or more attributes.
+ * The strings specified to this method will be used only if {@link
#addIdentifier(Class)} is invoked more than once.
*
- * @param localPart property generic name tip part
- * @param valueClass property value class
- * @param crs property {@code CoordinateReferenceSystem} characteristic
- * @return created property type
- */
- public <V> AttributeType<V> addProperty(final String localPart, final
Class<V> valueClass, final CoordinateReferenceSystem crs) {
- return addProperty(null, localPart, valueClass, crs);
- }
-
- /**
- * Add a new property to the feature type.
- * Property will have a minimum and maximum occurrence of one, no default
value
- * and the given {@code CoordinateReferenceSystem} characteristic.
+ * <p>If this method is not invoked, then the default values are the
{@code ":"} delimiter and no prefix or suffix.</p>
*
- * @param scope property generic name scope part
- * @param localPart property generic name tip part
- * @param valueClass property value class
- * @param crs property {@code CoordinateReferenceSystem} characteristic
- * @return created property type
- */
- public <V> AttributeType<V> addProperty(final String scope, final String
localPart,
- final Class<V> valueClass, final CoordinateReferenceSystem crs)
- {
- return addProperty(name(scope, localPart), valueClass, crs);
- }
-
- /**
- * Add a new property to the feature type.
- * Property will have a minimum and maximum occurrence of one, no default
value
- * and the given {@code CoordinateReferenceSystem} characteristic.
+ * @param delimiter the characters to use as delimiter between each
single property value.
+ * @param prefix characters to use at the beginning of the
concatenated string, or {@code null} if none.
+ * @param suffix characters to use at the end of the concatenated
string, or {@code null} if none.
+ * @return {@code this} for allowing method calls chaining.
*
- * @param name property name
- * @param valueClass property value class
- * @param crs property {@code CoordinateReferenceSystem} characteristic
- * @return created property type
+ * @see java.util.StringJoiner
+ * @see FeatureOperations#compound(Map, String, String, String,
PropertyType...)
*/
- public <V> AttributeType<V> addProperty(final GenericName name, final
Class<V> valueClass, final CoordinateReferenceSystem crs) {
- return addProperty(name, valueClass, null, crs);
+ public FeatureTypeBuilder setIdentifierDelimiters(final String delimiter,
final String prefix, final String suffix) {
+ ArgumentChecks.ensureNonEmpty("delimiter", delimiter);
+ idDelimiter = delimiter;
+ idPrefix = prefix;
+ idSuffix = suffix;
+ return this;
}
/**
- * Add a new property to the feature type.
- * Property will have a minimum and maximum occurrence of one and no
characteristics.
+ * Creates a new {@code AttributeType} builder for values of the given
class which will be used as identifiers.
+ * An arbitrary amount of attributes can be specified as identifiers:
*
- * @param scope property generic name scope part
- * @param localPart property generic name tip part
- * @param valueClass property value class
- * @param defaultValue property default value
- * @return created property type
- */
- public <V> AttributeType<V> addProperty(final String scope, final String
localPart,
- final Class<V> valueClass, final V defaultValue)
- {
- return addProperty(name(scope, localPart), valueClass, defaultValue,
null);
- }
-
- /**
- * Add a new property to the feature type.
- * Property will have a minimum and maximum occurrence of one and the given
- * {@code CoordinateReferenceSystem} characteristic.
+ * <ul>
+ * <li>If this method is never invoked, no attribute is marked as
feature identifier.</li>
+ * <li>If this method is invoked exactly once, then a new attribute is
created in the same way than
+ * {@link #addAttribute(Class)} and a synthetic attribute named
{@code "@id"} will be created as
+ * a {@linkplain FeatureOperations#link link} to the new
attribute.</li>
+ * <li>If this method is invoked more than once, then new attributes are
created in the same way than
+ * {@link #addAttribute(Class)} and a synthetic attribute named
{@code "@id"} will be created as
+ * a {@linkplain FeatureOperations#compound compound key} made of
all identifiers.</li>
+ * </ul>
*
- * @param scope property generic name scope part
- * @param localPart property generic name tip part
- * @param valueClass property value class
- * @param defaultValue property default value
- * @param crs property {@code CoordinateReferenceSystem} characteristic
- * @return created property type
- */
- public <V> AttributeType<V> addProperty(final String scope, final String
localPart,
- final Class<V> valueClass, final V defaultValue, final
CoordinateReferenceSystem crs)
- {
- return addProperty(name(scope, localPart), valueClass, 1, 1,
defaultValue, crs);
- }
-
- /**
- * Add a new property to the feature type.
- * Property will have a minimum and maximum occurrence of one and no
characteristics.
+ * Callers shall invoke at least one of the {@code Property.setName(…)}
methods on the returned instance.
+ * All other methods are optional.
*
- * @param name property name
- * @param valueClass property value class
- * @param defaultValue property default value
- * @return created property type
+ * @param <V> the compile-time value of {@code valueClass} argument.
+ * @param valueClass the class of attribute values.
+ * @return a builder for an {@code AttributeType}.
*/
- public <V> AttributeType<V> addProperty(final GenericName name, final
Class<V> valueClass, final V defaultValue) {
- return addProperty(name, valueClass, 1, 1, defaultValue);
+ public <V> Property<V> addIdentifier(final Class<V> valueClass) {
+ ensureAttributeType(valueClass);
+ final Property<V> property = new Property<>(valueClass);
+ idAttributes.add(property);
+ properties.add(property);
+ return property;
}
/**
- * Add a new property to the feature type.
- * Property will have a minimum and maximum occurrence of one and the given
- * {@code CoordinateReferenceSystem} characteristic.
- *
- * @param name property name
- * @param valueClass property value class
- * @param defaultValue property default value
- * @param crs property {@code CoordinateReferenceSystem} characteristic
- * @return created property type
- */
- public <V> AttributeType<V> addProperty(final GenericName name, final
Class<V> valueClass,
- final V defaultValue, final CoordinateReferenceSystem crs)
- {
- return addProperty(name, valueClass, 1, 1, defaultValue, crs);
+ * Creates a new {@code AttributeType} builder for a geometry which will
be flagged as the default geometry.
+ * Callers shall invoke at least one of the {@code Property.setName(…)}
methods on the returned instance.
+ * All other methods are optional.
+ *
+ * @param <V> the compile-time value of {@code valueClass} argument.
+ * @param valueClass the geometry class of attribute values.
+ * @return a builder for an {@code AttributeType} which will contain the
default geometry.
+ * @throws IllegalArgumentException if the given type is not a supported
geometry type.
+ * @throws IllegalStateException if a default geometry has already been
specified to this builder.
+ */
+ public <V> Property<V> addDefaultGeometry(final Class<V> valueClass) {
+ ensureAttributeType(valueClass);
+ if (!Geometries.isKnownType(valueClass)) {
+ throw new
IllegalArgumentException(errors().getString(Errors.Keys.UnsupportedImplementation_1,
valueClass));
+ }
+ if (defaultGeometry != null) {
+ throw new
IllegalStateException(errors().getString(Errors.Keys.PropertyAlreadyExists_2,
+ getDisplayName(),
NameConvention.DEFAULT_GEOMETRY_PROPERTY));
+ }
+ final Property<V> property = new Property<>(valueClass);
+ defaultGeometry = property;
+ properties.add(property);
+ return property;
}
/**
- * Add a new property to the feature type.
- *
- *
- * @param localPart property generic name tip part
- * @param valueClass property value class
- * @param minimumOccurs property minimum number of occurrences
- * @param maximumOccurs property maximum number of occurrences
- * @param defaultValue property default value
- * @return created property type
- */
- public <V> AttributeType<V> addProperty(final String localPart, final
Class<V> valueClass,
- final int minimumOccurs, final int maximumOccurs, final V
defaultValue)
- {
- return addProperty(name(null, localPart), valueClass, minimumOccurs,
maximumOccurs, defaultValue);
+ * Creates a new {@code AttributeType} builder for values of the given
class.
+ * Callers shall invoke at least one of the {@code Property.setName(…)}
methods on the returned instance.
+ * All other methods are optional.
+ *
+ * <p>Usage example:</p>
+ * {@preformat java
+ * builder.addAttribute(String.class).setName("City");
+ * }
+ *
+ * @param <V> the compile-time value of {@code valueClass} argument.
+ * @param valueClass the class of attribute values.
+ * @return a builder for an {@code AttributeType}.
+ */
+ public <V> Property<V> addAttribute(final Class<V> valueClass) {
+ ensureAttributeType(valueClass);
+ final Property<V> property = new Property<>(valueClass);
+ properties.add(property);
+ return property;
}
/**
- * Add a new property to the feature type.
- *
- * @param scope property generic name scope part
- * @param localPart property generic name tip part
- * @param valueClass property value class
- * @param minimumOccurs property minimum number of occurrences
- * @param maximumOccurs property maximum number of occurrences
- * @param defaultValue property default value
- * @return created property type
+ * Ensures that the given value class is not null and not assignable to
{@code Feature}.
+ * We disallow {@code Feature.class} because those type shall be handled
as associations
+ * instead than attributes.
*/
- public <V> AttributeType<V> addProperty(final String scope, final String
localPart, final Class<V> valueClass,
- final int minimumOccurs, final int maximumOccurs, final V
defaultValue)
- {
- return addProperty(name(scope, localPart), valueClass, minimumOccurs,
maximumOccurs, defaultValue);
+ private void ensureAttributeType(final Class<?> valueClass) {
+ if (valueClass == null) {
+ throw new
NullArgumentException(errors().getString(Errors.Keys.NullArgument_1,
"valueClass"));
+ }
+ if (Feature.class.isAssignableFrom(valueClass)) {
+ throw new
IllegalArgumentException(errors().getString(Errors.Keys.IllegalArgumentValue_2,
"valueClass", valueClass));
+ }
}
/**
- * Add a new property to the feature type.
+ * Creates a new {@code FeatureAssociationRole} builder for features of
the given type.
+ * Callers shall invoke at least one of the {@code Property.setName(…)}
methods on the returned instance.
+ * All other methods are optional.
*
- * @param scope property generic name scope part
- * @param localPart property generic name tip part
- * @param valueClass property value class
- * @param minimumOccurs property minimum number of occurrences
- * @param maximumOccurs property maximum number of occurrences
- * @param defaultValue property default value
- * @param crs property {@code CoordinateReferenceSystem} characteristic
- * @return created property type
+ * @param type the type of feature values.
+ * @return a builder for a {@code FeatureAssociationRole}.
*/
- public <V> AttributeType<V> addProperty(final String scope, final String
localPart, final Class<V> valueClass,
- final int minimumOccurs, final int maximumOccurs, final V
defaultValue, final CoordinateReferenceSystem crs)
- {
- return addProperty(name(scope, localPart), valueClass, minimumOccurs,
maximumOccurs, defaultValue, crs);
+ public Property<Feature> addAssociation(final FeatureType type) {
+ ArgumentChecks.ensureNonNull("type", type);
+ final Property<Feature> property = new Property<>(Feature.class,
FeatureType.class, type);
+ properties.add(property);
+ return property;
}
/**
- * Add a new property to the feature type.
- *
- * @param name property name
- * @param valueClass property value class
- * @param minimumOccurs property minimum number of occurrences
- * @param maximumOccurs property maximum number of occurrences
- * @param defaultValue property default value
- * @return created property type
- */
- public <V> AttributeType<V> addProperty(final GenericName name, final
Class<V> valueClass,
- final int minimumOccurs, final int maximumOccurs, final V
defaultValue)
- {
- return addProperty(name, valueClass, minimumOccurs, maximumOccurs,
defaultValue,null);
+ * Creates a new {@code FeatureAssociationRole} builder for features of a
type of the given name.
+ * This method can be invoked as an alternative to {@link
#addAssociation(FeatureType)} when the
+ * {@code FeatureType} instance is not yet available because of cyclic
dependency.
+ *
+ * @param type the name of the type of feature values.
+ * @return a builder for a {@code FeatureAssociationRole}.
+ */
+ public Property<Feature> addAssociation(final GenericName type) {
+ ArgumentChecks.ensureNonNull("type", type);
+ final Property<Feature> property = new Property<>(Feature.class,
GenericName.class, type);
+ properties.add(property);
+ return property;
}
/**
- * Add a new property to the feature type.
+ * Describes one property of the {@code FeatureType} to be built by the
enclosing {@code FeatureTypeBuilder}.
+ * A different instance of {@code Property} exists for each property to
describe. Those instances are created by:
*
- * @param name property name
- * @param valueClass property value class
- * @param minimumOccurs property minimum number of occurrences
- * @param maximumOccurs property maximum number of occurrences
- * @param defaultValue property default value
- * @param crs property {@code CoordinateReferenceSystem} characteristic
- * @return created property type
- */
- public <V> AttributeType<V> addProperty(final GenericName name, final
Class<V> valueClass,
- final int minimumOccurs, final int maximumOccurs, final V
defaultValue, final CoordinateReferenceSystem crs)
- {
- final AttributeType<V> att;
- if (crs != null) {
- final AttributeType<CoordinateReferenceSystem> qualifier = new
DefaultAttributeType<>(
- Collections.singletonMap(DefaultFeatureType.NAME_KEY,
CRS_CHARACTERISTIC),
- CoordinateReferenceSystem.class, 1, 1, crs);
- att = new DefaultAttributeType<>(
- Collections.singletonMap(DefaultFeatureType.NAME_KEY, name),
- valueClass, minimumOccurs, maximumOccurs, defaultValue,
qualifier);
- } else {
- att = new DefaultAttributeType<>(
- Collections.singletonMap(DefaultFeatureType.NAME_KEY, name),
- valueClass, minimumOccurs, maximumOccurs, defaultValue);
+ * <ul>
+ * <li>{@link FeatureTypeBuilder#addIdentifier(Class)}</li>
+ * <li>{@link FeatureTypeBuilder#addDefaultGeometry(Class)}</li>
+ * <li>{@link FeatureTypeBuilder#addAttribute(Class)}</li>
+ * <li>{@link FeatureTypeBuilder#addAssociation(FeatureType)}</li>
+ * <li>{@link FeatureTypeBuilder#addAssociation(GenericName)}</li>
+ * </ul>
+ *
+ * @param <V> the class of property values.
+ */
+ public final class Property<V> extends Builder<Property<V>> {
+ /**
+ * The class of property values. Can not be changed after construction
+ * because this value determines the parameterized type {@code <V>}.
+ */
+ private final Class<V> valueClass;
+
+ /**
+ * The default value for the property, or {@code null} if none.
+ */
+ private V defaultValue;
+
+ /**
+ * The minimum number of property values.
+ * The default value is 1, unless otherwise specified by {@link
#setDefaultCardinality(int, int)}.
+ *
+ * @see #setCardinality(int, int)
+ */
+ private int minimumOccurs;
+
+ /**
+ * The maximum number of property values. The default value is 1.
+ * The default value is 1, unless otherwise specified by {@link
#setDefaultCardinality(int, int)}.
+ *
+ * @see #setCardinality(int, int)
+ */
+ private int maximumOccurs;
+
+ /**
+ * Builders for the characteristics associated to the attribute.
+ */
+ private final List<Characteristic<?>> characteristics;
+
+ /**
+ * Creates a new {@code AttributeType} or {@code Operation} builder
for values of the given class.
+ *
+ * @param valueClass the class of property values.
+ */
+ Property(final Class<V> valueClass) {
+ this.valueClass = valueClass;
+ minimumOccurs = defaultMinimumOccurs;
+ maximumOccurs = defaultMaximumOccurs;
+ characteristics = new ArrayList<>();
+ }
+
+ /**
+ * Creates a new {@code AssociationRole} builder for values of the
given type.
+ * This constructor arbitrarily stores the feature type as an unnamed
characteristic of this property.
+ *
+ * @param valueClass shall be {@code Feature.class}.
+ * @param typeClass shall be either {@code FeatureType.class} or
{@code GenericName.class}.
+ * @param type the type of associated features.
+ */
+ <C> Property(final Class<V> valueClass, final Class<C> typeClass,
final C type) {
+ this.valueClass = valueClass;
+ minimumOccurs = defaultMinimumOccurs;
+ maximumOccurs = defaultMaximumOccurs;
+ characteristics = Collections.singletonList(new
Characteristic<>(typeClass).setDefaultValue(type));
+ }
+
+ /**
+ * Delegates the creation of a new name to the enclosing builder.
+ */
+ @Override
+ final GenericName name(final String scope, final String localPart) {
+ return FeatureTypeBuilder.this.name(scope, localPart);
+ }
+
+ /**
+ * Sets the minimum and maximum number of property values. Those
numbers must be equal or greater than zero.
+ *
+ * <p>If this method is not invoked, then the default values are the
cardinality specified by the last call
+ * to {@link #setDefaultCardinality(int, int)} at the time this {@code
Property} instance has been created.
+ * If the later method has not invoked neither, then the default
cardinality is [1 … 1].</p>
+ *
+ * @param minimumOccurs new minimum number of property values.
+ * @param maximumOccurs new maximum number of property values.
+ * @return {@code this} for allowing method calls chaining.
+ */
+ public Property<V> setCardinality(final int minimumOccurs, final int
maximumOccurs) {
+ if (minimumOccurs < 0 || maximumOccurs < minimumOccurs) {
+ throw new
IllegalArgumentException(errors().getString(Errors.Keys.IllegalRange_2,
minimumOccurs, maximumOccurs));
+ }
+ this.minimumOccurs = minimumOccurs;
+ this.maximumOccurs = maximumOccurs;
+ return this;
+ }
+
+ /**
+ * Sets the default value for the property.
+ *
+ * @param defaultValue default property value, or {@code null} if
none.
+ * @return {@code this} for allowing method calls chaining.
+ */
+ public Property<V> setDefaultValue(final V defaultValue) {
+ this.defaultValue = defaultValue;
+ return this;
+ }
+
+ /**
+ * Sets an enumeration of valid values for this attribute.
+ *
+ * <p>This is a convenience method for {@link
#addCharacteristic(Class)} with a value
+ * of type {@link Set} and a conventional name.</p>
+ *
+ * @param values valid values.
+ * @return {@code this} for allowing method calls chaining.
+ * @throws UnsupportedOperationException if this property does not
support characteristics.
+ *
+ * @see NameConvention#VALID_VALUES_CHARACTERISTIC
+ */
+ @SafeVarargs
+ public final Property<V> setValidValues(final V... values) {
+ return
setCharacteristic(NameConvention.VALID_VALUES_CHARACTERISTIC,
+ Set.class, CollectionsExt.immutableSet(false, values));
+ }
+
+ /**
+ * Sets the maximal length that characterizes the {@link CharSequence}
values of this attribute.
+ * While this characteristic can be applied to any kind of attribute,
it is meaningful only with
+ * character sequences.
+ *
+ * <p>This is a convenience method for {@link
#addCharacteristic(Class)} with a value
+ * of type {@link Integer} and a conventional name.</p>
+ *
+ * @param length maximal length of {@link CharSequence} attribute
values, or {@code null}.
+ * @return {@code this} for allowing method calls chaining.
+ * @throws UnsupportedOperationException if this property does not
support length characteristics.
+ *
+ * @see NameConvention#MAXIMAL_LENGTH_CHARACTERISTIC
+ */
+ public Property<V> setMaximalLengthCharacteristic(final Integer
length) {
+ return
setCharacteristic(NameConvention.MAXIMAL_LENGTH_CHARACTERISTIC, Integer.class,
length);
+ }
+
+ /**
+ * Sets the coordinate reference system that characterizes the values
of this attribute.
+ * While this characteristic can be applied to any kind of attribute,
it is meaningful
+ * only with georeferenced values like geometries or coverages.
+ *
+ * <p>This is a convenience method for {@link
#addCharacteristic(Class)} with a value
+ * of type {@link CoordinateReferenceSystem} and a conventional
name.</p>
+ *
+ * @param crs coordinate reference system associated to attribute
values, or {@code null}.
+ * @return {@code this} for allowing method calls chaining.
+ * @throws UnsupportedOperationException if this property does not
support CRS characteristics.
+ *
+ * @see NameConvention#CRS_CHARACTERISTIC
+ */
+ public Property<V> setCRSCharacteristic(final
CoordinateReferenceSystem crs) {
+ return setCharacteristic(NameConvention.CRS_CHARACTERISTIC,
CoordinateReferenceSystem.class, crs);
+ }
+
+ /**
+ * Implementation of all setter methods for characteristics.
+ *
+ * @throws UnsupportedOperationException if this property does not
support characteristics.
+ */
+ private <C> Property<V> setCharacteristic(final GenericName name,
final Class<C> type, final C value) {
+ for (final Characteristic<?> characteristic : characteristics) {
+ if
(name.equals(characteristic.identification.get(DefaultAttributeType.NAME_KEY)))
{
+ characteristic.set(value);
+ return this;
+ }
+ }
+ addCharacteristic(type).setDefaultValue(value).setName(name);
+ return this;
}
- addProperty(att);
- return att;
- }
- /**
- * Add a custom property to the feature type.
- *
- * @param property user defined property type
- * @return created property type
- */
- public PropertyType addProperty(final PropertyType property) {
- properties.put(property.getName(), property);
- return property;
+ /**
+ * Adds another attribute type that describes this attribute type.
+ * See <cite>"Attribute characterization"</cite> in {@link
DefaultAttributeType} Javadoc for more information.
+ *
+ * <p>Usage example:</p>
+ * {@preformat java
+ * attribute.addCharacteristic(Unit.class).setName("Unit of
measurement").setDefaultValue(SI.CELSIUS);
+ * }
+ *
+ * Callers shall invoke at least one of the {@code
Characteristic.setName(…)} methods on the returned instance.
+ * All other methods are optional.
+ *
+ * @param <C> the compile-time type of {@code type} argument.
+ * @param type the class of characteristic values.
+ * @return a builder for a characteristic of this attribute.
+ * @throws UnsupportedOperationException if this property does not
support characteristics.
+ */
+ public <C> Characteristic<C> addCharacteristic(final Class<C> type) {
+ if (valueClass == Feature.class) {
+ throw new
UnsupportedOperationException(errors().getString(Errors.Keys.IllegalOperationForValueClass_1,
valueClass));
+ }
+ ArgumentChecks.ensureNonNull("type", type);
+ final Characteristic<C> characteristic = new
Characteristic<>(type);
+ characteristics.add(characteristic);
+ return characteristic;
+ }
+
+ /**
+ * Creates a new property type from the current setting.
+ */
+ final PropertyType build() {
+ final PropertyType property;
+ if (valueClass == Feature.class) {
+ final Object type =
CollectionsExt.first(characteristics).defaultValue;
+ if (type instanceof FeatureType) {
+ property = new DefaultAssociationRole(identification,
(FeatureType) type, minimumOccurs, maximumOccurs);
+ } else {
+ property = new DefaultAssociationRole(identification,
(GenericName) type, minimumOccurs, maximumOccurs);
+ }
+ } else {
+ final AttributeType<?>[] chrts = new
AttributeType<?>[characteristics.size()];
+ for (int i=0; i<chrts.length; i++) {
+ chrts[i] = characteristics.get(i).build();
+ }
+ property = new DefaultAttributeType<>(identification,
valueClass, minimumOccurs, maximumOccurs, defaultValue, chrts);
+ }
+ return property;
+ }
}
/**
- * Add a new association to the feature type.
- * Association will have a minimum occurrence of zero and maximum
occurrence of one.
+ * Describes one characteristic of the {@code AttributeType} to be built
by the enclosing {@code FeatureTypeBuilder}.
+ * A different instance of {@code Characteristic} exists for each
characteristic to describe.
+ * Those instances are created by:
*
- * @param scope property generic name scope part
- * @param localPart property generic name tip part
- * @param type associated feature type
- * @return created association
- */
- public FeatureAssociationRole addAssociation(final String scope, final
String localPart, final FeatureType type) {
- return addAssociation(scope, localPart, type,0,1);
- }
-
- /**
- * Add a new association to the feature type.
- * Association will have a minimum occurrence of zero and maximum
occurrence of one.
+ * <ul>
+ * <li>{@link Property#addCharacteristic(Class)}</li>
+ * </ul>
*
- * @param name property name
- * @param type associated feature type
- * @return created association
+ * @param <V> the class of characteristic values.
*/
- public FeatureAssociationRole addAssociation(final GenericName name, final
FeatureType type) {
- return addAssociation(name, type,0,1);
- }
+ public final class Characteristic<V> extends Builder<Characteristic<V>> {
+ /**
+ * The class of attribute values. Can not be changed after construction
+ * because this value determines the parameterized type {@code <V>}.
+ */
+ private final Class<V> valueClass;
- /**
- * Add a new association to the feature type.
- *
- * @param scope property generic name scope part
- * @param localPart property generic name tip part
- * @param type associated feature type
- * @param minimumOccurs property minimum number of occurrences
- * @param maximumOccurs property maximum number of occurrences
- * @return created association
- */
- public FeatureAssociationRole addAssociation(final String scope, final
String localPart, final FeatureType type,
- final int minimumOccurs, final int maximumOccurs)
- {
- return addAssociation(name(scope, localPart), type,minimumOccurs,
maximumOccurs);
- }
+ /**
+ * The default value for the attribute, or {@code null} if none.
+ */
+ V defaultValue;
- /**
- * Add a new association to the feature type.
- *
- * @param name property name
- * @param type associated feature type
- * @param minimumOccurs property minimum number of occurrences
- * @param maximumOccurs property maximum number of occurrences
- * @return created association
- */
- public FeatureAssociationRole addAssociation(final GenericName name, final
FeatureType type,
- final int minimumOccurs, final int maximumOccurs)
- {
- final FeatureAssociationRole role = new DefaultAssociationRole(
- Collections.singletonMap(DefaultFeatureType.NAME_KEY, name),
type, minimumOccurs, maximumOccurs);
- addProperty(role);
- return role;
- }
+ /**
+ * Creates a new characteristic builder for values of the given class.
+ *
+ * @param valueClass the class of characteristic values.
+ */
+ Characteristic(final Class<V> valueClass) {
+ this.valueClass = valueClass;
+ }
- /**
- * Add a custom properties to the feature type.
- *
- * @param properties user defined property types
- */
- public void addProperties(final PropertyType ... properties) {
- for (PropertyType pt : properties) {
- this.properties.put(pt.getName(), pt);
+ /**
+ * Delegates the creation of a new name to the enclosing builder.
+ */
+ @Override
+ final GenericName name(final String scope, final String localPart) {
+ return FeatureTypeBuilder.this.name(scope, localPart);
}
- }
- /**
- * Add a custom properties to the feature type.
- *
- * @param properties user defined property types
- */
- public void addProperties(final Collection<? extends PropertyType>
properties) {
- for (PropertyType pt : properties) {
- this.properties.put(pt.getName(), pt);
+ /**
+ * Sets the default value with check of the value class.
+ */
+ final void set(final Object value) {
+ setDefaultValue(valueClass.cast(value));
}
- }
- /**
- * Remove a property from the feature type.
- *
- * @param name property name
- * @return removed property, can be null if property was not found
- */
- public PropertyType removeProperty(final String name) {
- return properties.remove(valueOf(name));
- }
+ /**
+ * Sets the default value for the characteristic.
+ *
+ * @param value characteristic default value, or {@code null} if
none.
+ * @return {@code this} for allowing method calls chaining.
+ */
+ public Characteristic<V> setDefaultValue(final V value) {
+ defaultValue = value;
+ return this;
+ }
- /**
- * Remove a property from the feature type.
- *
- * @param name property name
- * @return removed property, can be null if property was not found
- */
- public PropertyType removeProperty(final GenericName name) {
- return properties.remove(name);
+ /**
+ * Creates a new characteristic from the current setting.
+ */
+ final AttributeType<V> build() {
+ return new DefaultAttributeType<>(identification, valueClass, 0,
1, defaultValue);
+ }
}
/**
- * Build feature type
+ * 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.
*
- * @return FeatureType
+ * @return the new feature type.
+ * @throws IllegalStateException if the feature type contains incompatible
+ * {@linkplain Property#setCRSCharacteristic CRS characteristics}.
*/
- public DefaultFeatureType build() throws IllegalArgumentException {
- //build id property
- if (idAttributes != null) {
- //check id properties exist
- for (GenericName n : idAttributes) {
- if (!properties.containsKey(n)) {
- throw new IllegalArgumentException("Property "+n+" used in
id does not exist");
- }
- }
-
- String prefix = idPrefix;
- if (idPrefix==null) {
- prefix = getName().tip().toString();
- }
-
- final Operation att = FeatureOperations.compound(
- Collections.singletonMap(AbstractOperation.NAME_KEY,
ID_PROPERTY),
- idDelimiter, prefix, null, properties.get(idAttributes));
- properties.put(ID_PROPERTY, att);
- }
- //build default geometry property
- if (defGeomAttribute != null) {
- if (!properties.containsKey(defGeomAttribute)) {
- throw new IllegalArgumentException("Property
"+defGeomAttribute+" used in default geometry does not exist");
+ public FeatureType build() throws IllegalStateException {
+ int numSynthetic; // Number of
synthetic properties to be generated.
+ int numSpecified = properties.size(); // Number of
explicitely specified properties.
+ final PropertyType[] identifierTypes;
+ if (idAttributes.isEmpty()) {
+ identifierTypes = null;
+ numSynthetic = 0;
+ } else {
+ identifierTypes = new PropertyType[idAttributes.size()];
+ numSynthetic = 1; // Reserve one
slot for the synthetic property "@id".
+ }
+ if (defaultGeometry != null) {
+ numSynthetic += 2; // One slot for
"@defaultGeometry" and one for "@envelope".
+ }
+ PropertyType[] propertyTypes = new PropertyType[numSynthetic +
numSpecified];
+ for (int i=0,j=numSynthetic; i<numSpecified; i++, j++) {
+ final Property<?> builder = properties.get(i);
+ final PropertyType instance = builder.build();
+ propertyTypes[j] = instance;
+ final int id = idAttributes.indexOf(builder);
+ if (id >= 0) {
+ identifierTypes[id] = instance;
}
- final PropertyType geomAtt = properties.get(defGeomAttribute);
- final CoordinateReferenceSystem crs = null; // TODO
NameConvention.getCoordinateReferenceSystem(geomAtt);
- final Operation att = FeatureOperations.link(
- Collections.singletonMap(AbstractOperation.NAME_KEY,
DEFAULT_GEOMETRY_PROPERTY), geomAtt);
- properties.put(DEFAULT_GEOMETRY_PROPERTY, att);
-
- final Operation boundAtt;
- try {
- boundAtt =
FeatureOperations.envelope(Collections.singletonMap(AbstractOperation.NAME_KEY,
ENVELOPE_PROPERTY),
- crs, properties.values().toArray(new
PropertyType[properties.size()]));
- } catch (FactoryException e) {
- throw new IllegalStateException(e);
+ /*
+ * If there is a default geometry, add a link named "@geometry" to
that geometry. It may happen
+ * that the property created by the user is already named
"@geometry", in which case will will
+ * avoid to duplicate the property by removing the second
occurrence.
+ */
+ if (builder == defaultGeometry) {
+ final PropertyType geom;
+ if
(NameConvention.DEFAULT_GEOMETRY_PROPERTY.equals(instance.getName())) {
+ propertyTypes = ArraysExt.remove(propertyTypes, j--, 1);
+ geom = instance;
+ } else {
+ geom =
FeatureOperations.link(name(NameConvention.DEFAULT_GEOMETRY_PROPERTY),
instance);
+ }
+ propertyTypes[numSynthetic - 1] = geom;
}
- properties.put(ENVELOPE_PROPERTY, boundAtt);
-
}
-
- verifyOperations();
-
- return new DefaultFeatureType(
- identification,
- isAbstract,
- superTypes.toArray(new FeatureType[superTypes.size()]),
- properties.values().toArray(new
PropertyType[properties.size()]));
+ /*
+ * Create the "envelope" operation only after we created all other
properties, except "@id" which is not needed
+ * for envelope. It is okay if the 'propertyTypes' array still
contains null elements like the "@id" one, since
+ * FeatureOperations.envelope(…) constructor will ignore any property
which is not for a value.
+ */
+ if (defaultGeometry != null) try {
+ propertyTypes[numSynthetic - 2] =
FeatureOperations.envelope(name(NameConvention.ENVELOPE_PROPERTY), null,
propertyTypes);
+ } catch (FactoryException e) {
+ throw new IllegalStateException(e);
+ }
+ if (identifierTypes != null) {
+ propertyTypes[0] =
FeatureOperations.compound(name(NameConvention.ID_PROPERTY), idDelimiter,
idPrefix, idSuffix, identifierTypes);
+ }
+ return new DefaultFeatureType(identification, isAbstract,
superTypes.toArray(new FeatureType[superTypes.size()]), propertyTypes);
}
/**
- * Check operations have the required properties.
- *
- * @throws IllegalArgumentException if some properties are missing for an
operation.
+ * Helper method for creating identification info of synthetic attributes.
*/
- private void verifyOperations() throws IllegalArgumentException {
- for (PropertyType pt : properties.values()) {
- if (pt instanceof AbstractOperation) {
- final Set<String> dependencies =
((AbstractOperation)pt).getDependencies();
- depLoop:
- for (String dep : dependencies) {
- for (GenericName gn : properties.keySet()) {
- if (match(gn, dep)) continue depLoop;
- }
- throw new IllegalArgumentException("Operation
"+pt.getName().toString()+" requiere property "+dep+" but this property is
missing.");
- }
-
- }
- }
-
+ private static Map<String,?> name(final GenericName name) {
+ return Collections.singletonMap(AbstractOperation.NAME_KEY, name);
}
-
////////////////////////////////////////////////////////////////////////////
- // Name utils
//////////////////////////////////////////////////////////////
-
////////////////////////////////////////////////////////////////////////////
-
/**
- *
- * @param parsedNames mandatory
- * @return GenericName
+ * Invoked by {@link Builder} for creating new {@code LocalName} or {@code
GenericName} instances.
*/
@Override
- final GenericName name(final String scope, final String localPart) {
+ final GenericName name(String scope, final String localPart) {
if (scope == null) {
+ scope = defaultScope;
+ }
+ if (scope == null || scope.isEmpty()) {
return nameFactory.createLocalName(null, localPart);
} else {
return nameFactory.createGenericName(null, scope, localPart);
@@ -653,82 +760,9 @@ public class FeatureTypeBuilder extends
}
/**
- * Parse a string value that can be expressed in 2 different forms :
- * JSR-283 extended form : {uri}localpart
- * Separator form : uri:localpart
- *
- * if the given string do not match any, then a Name with no namespace
will be
- * created and the localpart will be the given string.
- *
- * @param candidate String to convert to a geoneric name
- * @return Name
- */
- private GenericName valueOf(final String candidate) {
-
- if (candidate.startsWith("{")) {
- //name is in extended form
- return toSessionNamespaceFromExtended(candidate);
- }
-
- int index = candidate.lastIndexOf(':');
-
- if (index <= 0) {
- return name(null, candidate);
- } else {
- final String uri = candidate.substring(0,index);
- final String name =
candidate.substring(index+1,candidate.length());
- return name(uri, name);
- }
-
- }
-
- private GenericName toSessionNamespaceFromExtended(final String candidate)
{
- final int index = candidate.indexOf('}');
-
- if (index < 0) throw new IllegalArgumentException("Invalide extended
form : "+ candidate);
-
- final String uri = candidate.substring(1, index);
- final String name = candidate.substring(index+1, candidate.length());
-
- return name(uri, name);
- }
-
- private static String toExpandedString(final GenericName name) {
- String ns = getNamespace(name);
- if (ns == null) {
- return name.tip().toString();
- } else {
- return '{' + ns + '}' + name.tip();
- }
- }
-
- /**
- * Tests that the given string representation matches the given name.
- * String can be written with only the local part or in extendedform or JCR
- * extended form.
- *
- * @param name expected generic name
- * @param candidate name to test
- * @return true if the string match the name
+ * Returns the resources for error messages.
*/
- private static boolean match(final GenericName name, final String
candidate) {
- if (candidate.startsWith("{")) {
- //candidate is in extended form
- return candidate.equals(toExpandedString(name));
- }
-
- final int index = candidate.lastIndexOf(':');
-
- if (index <= 0) {
- return candidate.equals(name.tip().toString());
- } else {
- final String uri = candidate.substring(0,index);
- final String local =
candidate.substring(index+1,candidate.length());
- return uri.equals(getNamespace(name)) &&
local.equals(name.tip().toString());
- }
- }
-
- private static String getNamespace(final GenericName name) {
- return (name instanceof ScopedName) ?
((ScopedName)name).path().toString() : null;
+ final Errors errors() {
+ return Errors.getResources(identification);
}
}
Modified:
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/NameConvention.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/NameConvention.java?rev=1740141&r1=1740140&r2=1740141&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/NameConvention.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/internal/feature/NameConvention.java
[UTF-8] Wed Apr 20 13:01:55 2016
@@ -136,6 +136,8 @@ public final class NameConvention extend
*
* <p>The {@linkplain
org.apache.sis.feature.DefaultAttributeType#getValueClass() value class} should
be
* {@link org.opengis.referencing.crs.CoordinateReferenceSystem}.</p>
+ *
+ * @see #getCRSCharacteristic(Property)
*/
public static final LocalName CRS_CHARACTERISTIC;
@@ -148,6 +150,8 @@ public final class NameConvention extend
*
* <p>The {@linkplain
org.apache.sis.feature.DefaultAttributeType#getValueClass() value class} should
be
* {@link Integer}.</p>
+ *
+ * @see #getMaximalLengthCharacteristic(Property)
*/
public static final LocalName MAXIMAL_LENGTH_CHARACTERISTIC;
@@ -165,7 +169,7 @@ public final class NameConvention extend
NAMESPACE = factory.createGenericName(null,
"Apache", Constants.SIS);
NameSpace ns = factory.createNameSpace(NAMESPACE,
null);
ID_PROPERTY = factory.createLocalName(ns,
"@identifier");
- DEFAULT_GEOMETRY_PROPERTY = factory.createLocalName(ns,
"@defaultGeometry");
+ DEFAULT_GEOMETRY_PROPERTY = factory.createLocalName(ns,
"@geometry");
ENVELOPE_PROPERTY = factory.createLocalName(ns,
"@envelope");
CRS_CHARACTERISTIC = factory.createLocalName(ns, "@crs");
MAXIMAL_LENGTH_CHARACTERISTIC = factory.createLocalName(ns,
"@maximalLength");
@@ -248,8 +252,10 @@ public final class NameConvention extend
* @return The Coordinate Reference System characteristic of the given
attribute, or {@code null} if none.
* @throws ClassCastException if {@link #CRS_CHARACTERISTIC} has been
found but is associated
* to an object which is not a {@link CoordinateReferenceSystem}
instance.
+ *
+ * @see
org.apache.sis.internal.feature.FeatureTypeBuilder.Property#setCRSCharacteristic(CoordinateReferenceSystem)
*/
- public static CoordinateReferenceSystem getCrsCharacteristic(final
Property attribute) {
+ public static CoordinateReferenceSystem getCRSCharacteristic(final
Property attribute) {
return (CoordinateReferenceSystem) getCharacteristic(attribute,
CRS_CHARACTERISTIC.toString());
}
@@ -273,6 +279,8 @@ public final class NameConvention extend
* @return The Coordinate Reference System characteristic of the given
attribute, or {@code null} if none.
* @throws ClassCastException if {@link #MAXIMAL_LENGTH_CHARACTERISTIC}
has been found but is associated
* to an object which is not an {@link Integer} instance.
+ *
+ * @see
org.apache.sis.internal.feature.FeatureTypeBuilder.Property#setMaximalLengthCharacteristic(Integer)
*/
public static Integer getMaximalLengthCharacteristic(final Property
attribute) {
return (Integer) getCharacteristic(attribute,
MAXIMAL_LENGTH_CHARACTERISTIC.toString());