| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.feature.builder; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Objects; |
| import java.lang.reflect.Array; |
| import org.opengis.util.GenericName; |
| import org.opengis.referencing.crs.CoordinateReferenceSystem; |
| import org.apache.sis.feature.DefaultAttributeType; |
| import org.apache.sis.feature.FeatureOperations; |
| import org.apache.sis.internal.util.CollectionsExt; |
| import org.apache.sis.internal.util.SetOfUnknownSize; |
| import org.apache.sis.internal.util.AbstractIterator; |
| import org.apache.sis.internal.feature.AttributeConvention; |
| import org.apache.sis.internal.feature.Geometries; |
| import org.apache.sis.internal.feature.Resources; |
| import org.apache.sis.util.resources.Errors; |
| import org.apache.sis.util.Classes; |
| import org.apache.sis.util.ObjectConverters; |
| import org.apache.sis.util.UnconvertibleObjectException; |
| |
| // Branch-dependent imports |
| import org.opengis.feature.AttributeType; |
| |
| |
| /** |
| * Describes one {@code AttributeType} which will be part of the feature type to be built by |
| * a {@code FeatureTypeBuilder}. An attribute can be for example a city name, a temperature |
| * (together with its units of measurement and uncertainty if desired) or a geometric shape. |
| * Attribute types contain the following information: |
| * |
| * <ul> |
| * <li>the name — a unique name which can be defined within a scope (or namespace).</li> |
| * <li>the definition — a concise definition of the element.</li> |
| * <li>the designation — a natural language designator for the element for user interfaces.</li> |
| * <li>the description — information beyond that required for concise definition of the element.</li> |
| * <li>the value class — often {@link String}, {@link Float} or {@link com.esri.core.geometry.Geometry}. |
| * Must be specified at {@linkplain FeatureTypeBuilder#addAttribute(Class) construction time}.</li> |
| * <li>a default value — to be used when an attribute instance does not provide an explicit value.</li> |
| * <li>characteristics — for example the units of measurement for all attributes of the same type.</li> |
| * <li>multiplicity — the minimum and maximum occurrences of attribute values.</li> |
| * </ul> |
| * |
| * @author Johann Sorel (Geomatys) |
| * @author Martin Desruisseaux (Geomatys) |
| * @version 0.8 |
| * |
| * @param <V> the class of attribute values. |
| * |
| * @see FeatureTypeBuilder#addAttribute(Class) |
| * @see org.apache.sis.feature.DefaultAttributeType |
| * |
| * @since 0.8 |
| * @module |
| */ |
| public final class AttributeTypeBuilder<V> extends PropertyTypeBuilder { |
| /** |
| * 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; |
| |
| /** |
| * The default value for the attribute, or {@code null} if none. |
| */ |
| private V defaultValue; |
| |
| /** |
| * Whether this attribute will be used in a {@linkplain FeatureOperations#compound compound key} named |
| * {@code "sis:identifier"}. If only one attribute has this flag and {@link FeatureTypeBuilder#idPrefix} and |
| * {@code isSuffix} are null, then {@code "sis:identifier"} will be a {@linkplain FeatureOperations#link link} |
| * to {@code idAttributes[0]}. |
| * |
| * @see #addRole(AttributeRole) |
| */ |
| private boolean isIdentifier; |
| |
| /** |
| * Builders for the characteristics associated to the attribute. |
| */ |
| final List<CharacteristicTypeBuilder<?>> characteristics; |
| |
| /** |
| * The attribute type created by this builder, or {@code null} if not yet created. |
| * This field must be cleared every time that a setter method is invoked on this builder. |
| */ |
| private transient AttributeType<V> property; |
| |
| /** |
| * Creates a new builder initialized to the values of the given builder. |
| * This constructor is for {@link #setValueClass(Class)} implementation only. |
| * |
| * @throws UnconvertibleObjectException if the default value can not be converted to the given class. |
| */ |
| private AttributeTypeBuilder(final AttributeTypeBuilder<?> builder, final Class<V> valueClass) |
| throws UnconvertibleObjectException |
| { |
| super(builder); |
| this.valueClass = valueClass; |
| defaultValue = ObjectConverters.convert(builder.defaultValue, valueClass); |
| isIdentifier = builder.isIdentifier; |
| characteristics = builder.characteristics; |
| } |
| |
| /** |
| * Creates a new {@code AttributeType} builder for values of the given class. |
| * |
| * @param owner the builder of the {@code FeatureType} for which to add the attribute. |
| * @param valueClass the class of attribute values. |
| */ |
| AttributeTypeBuilder(final FeatureTypeBuilder owner, final Class<V> valueClass) { |
| super(owner); |
| this.valueClass = valueClass; |
| characteristics = new ArrayList<>(); |
| } |
| |
| /** |
| * Creates a new {@code AttributeType} builder initialized to the values of an existing attribute. |
| * |
| * @param owner the builder of the {@code FeatureType} for which to add the attribute. |
| */ |
| AttributeTypeBuilder(final FeatureTypeBuilder owner, final AttributeType<V> template) { |
| super(owner); |
| property = template; |
| minimumOccurs = template.getMinimumOccurs(); |
| maximumOccurs = template.getMaximumOccurs(); |
| valueClass = template.getValueClass(); |
| defaultValue = template.getDefaultValue(); |
| final Map<String, AttributeType<?>> tc = template.characteristics(); |
| characteristics = new ArrayList<>(tc.size()); |
| for (final AttributeType<?> c : tc.values()) { |
| characteristics.add(new CharacteristicTypeBuilder<>(this, c)); |
| } |
| initialize(template); |
| } |
| |
| /** |
| * If the {@code AttributeType} created by the last call to {@link #build()} has been cached, |
| * clears that cache. This method must be invoked every time that a setter method is invoked. |
| */ |
| @Override |
| final void clearCache() { |
| property = null; |
| super.clearCache(); |
| } |
| |
| /** |
| * Returns a default name to use if the user did not specified a name. The first letter will be changed to |
| * lower case (unless the name looks like an acronym) for compliance with Java convention on attribute names. |
| */ |
| @Override |
| final String getDefaultName() { |
| return Classes.getShortName(valueClass); |
| } |
| |
| /** |
| * Sets the {@code AttributeType} name as a generic name. |
| * If another name was defined before this method call, that previous value will be discarded. |
| * |
| * @return {@code this} for allowing method calls chaining. |
| */ |
| @Override |
| public AttributeTypeBuilder<V> setName(final GenericName name) { |
| super.setName(name); |
| return this; |
| } |
| |
| /** |
| * Sets the {@code AttributeType} name as a simple string (local name). |
| * The namespace will be the value specified by the last call to {@link FeatureTypeBuilder#setNameSpace(CharSequence)}, |
| * but that namespace will not be visible in the {@linkplain org.apache.sis.util.iso.DefaultLocalName#toString() |
| * string representation} unless the {@linkplain org.apache.sis.util.iso.DefaultLocalName#toFullyQualifiedName() |
| * fully qualified name} is requested. |
| * |
| * <p>This convenience method creates a {@link org.opengis.util.LocalName} instance from |
| * the given {@code CharSequence}, then delegates to {@link #setName(GenericName)}.</p> |
| * |
| * @return {@code this} for allowing method calls chaining. |
| */ |
| @Override |
| public AttributeTypeBuilder<V> setName(final CharSequence localPart) { |
| super.setName(localPart); |
| return this; |
| } |
| |
| /** |
| * Sets the {@code AttributeType} name as a string in the given scope. |
| * The {@code components} array must contain at least one element. |
| * The last component (the {@linkplain org.apache.sis.util.iso.DefaultScopedName#tip() tip}) will be sufficient |
| * in many cases for calls to the {@link org.apache.sis.feature.AbstractFeature#getProperty(String)} method. |
| * The other elements before the last one are optional and can be used for resolving ambiguity. |
| * They will be visible as the name {@linkplain org.apache.sis.util.iso.DefaultScopedName#path() path}. |
| * |
| * <div class="note"><b>Example:</b> |
| * a call to {@code setName("A", "B", "C")} will create a "A:B:C" name. |
| * An attribute built with this name can be obtained from a feature by a call to {@code feature.getProperty("C")} |
| * if there is no ambiguity, or otherwise by a call to {@code feature.getProperty("B:C")} (if non-ambiguous) or |
| * {@code feature.getProperty("A:B:C")}.</div> |
| * |
| * In addition to the path specified by the {@code components} array, the name may also contain |
| * a namespace specified by the last call to {@link FeatureTypeBuilder#setNameSpace(CharSequence)}. |
| * But contrarily to the specified components, the namespace will not be visible in the name |
| * {@linkplain org.apache.sis.util.iso.DefaultScopedName#toString() string representation} unless the |
| * {@linkplain org.apache.sis.util.iso.DefaultScopedName#toFullyQualifiedName() fully qualified name} is requested. |
| * |
| * <p>This convenience method creates a {@link org.opengis.util.LocalName} or {@link org.opengis.util.ScopedName} |
| * instance depending on whether the {@code names} array contains exactly 1 element or more than 1 element, then |
| * delegates to {@link #setName(GenericName)}.</p> |
| * |
| * @return {@code this} for allowing method calls chaining. |
| */ |
| @Override |
| public AttributeTypeBuilder<V> setName(final CharSequence... components) { |
| super.setName(components); |
| return this; |
| } |
| |
| /** |
| * Sets the minimum number of attribute values. If the given number is greater than the |
| * {@linkplain #getMaximumOccurs() maximal number} of attribute values, than the maximum |
| * is also set to that value. |
| * |
| * @param occurs the new minimum number of attribute values. |
| * @return {@code this} for allowing method calls chaining. |
| */ |
| @Override |
| public AttributeTypeBuilder<V> setMinimumOccurs(final int occurs) { |
| super.setMinimumOccurs(occurs); |
| return this; |
| } |
| |
| /** |
| * Sets the maximum number of attribute values. If the given number is less than the |
| * {@linkplain #getMinimumOccurs() minimal number} of attribute values, than the minimum |
| * is also set to that value. {@link Integer#MAX_VALUE} means that there is no maximum. |
| * |
| * @param occurs the new maximum number of attribute values. |
| * @return {@code this} for allowing method calls chaining. |
| */ |
| @Override |
| public AttributeTypeBuilder<V> setMaximumOccurs(final int occurs) { |
| super.setMaximumOccurs(occurs); |
| return this; |
| } |
| |
| /** |
| * Returns the class of attribute values. |
| * |
| * @return the class of attribute values. |
| * |
| * @see #setValueClass(Class) |
| */ |
| public Class<V> getValueClass() { |
| return valueClass; |
| } |
| |
| /** |
| * Sets the class of attribute values. Callers <strong>must</strong> use the builder returned by this method |
| * instead of {@code this} builder after this method call, since the returned builder may be a new instance. |
| * |
| * @param <N> the compile-time value of the {@code type} argument. |
| * @param type the new class of attribute values. |
| * @return the attribute builder — <em>not necessarily this instance.</em> |
| * @throws UnconvertibleObjectException if the {@linkplain #getDefaultValue() default value} |
| * can not be converted to the given {@code <N>} class. |
| * |
| * @see #getValueClass() |
| */ |
| @SuppressWarnings("unchecked") |
| public <N> AttributeTypeBuilder<N> setValueClass(final Class<N> type) throws UnconvertibleObjectException { |
| final FeatureTypeBuilder owner = owner(); |
| ensureNonNull("type", type); |
| if (type == valueClass) { |
| return (AttributeTypeBuilder<N>) this; |
| } |
| final AttributeTypeBuilder<N> newb = new AttributeTypeBuilder<>(this, type); |
| for (final CharacteristicTypeBuilder<?> c : characteristics) { |
| c.owner(newb); |
| } |
| owner.replace(this, newb); |
| dispose(); |
| return newb; |
| } |
| |
| /** |
| * Returns the default value for the attribute, or {@code null} if none. |
| * |
| * @return the default attribute value, or {@code null} if none. |
| * |
| * @see #setDefaultValue(Object) |
| */ |
| public V getDefaultValue() { |
| return defaultValue; |
| } |
| |
| /** |
| * Sets the default value for the attribute. |
| * |
| * @param value default attribute value, or {@code null} if none. |
| * @return {@code this} for allowing method calls chaining. |
| * |
| * @see #getDefaultValue() |
| */ |
| public AttributeTypeBuilder<V> setDefaultValue(final V value) { |
| if (!Objects.equals(defaultValue, value)) { |
| defaultValue = value; |
| clearCache(); |
| } |
| return this; |
| } |
| |
| /** |
| * Returns an enumeration of valid values for the attribute, or an empty array if none. |
| * This convenience method returns the value of the characteristic set by {@link #setValidValues(Object...)}. |
| * |
| * @return valid values for the attribute, or an empty array if none. |
| */ |
| @SuppressWarnings("unchecked") |
| public V[] getValidValues() { |
| final Collection<?> c = CollectionsExt.nonNull((Collection<?>) |
| getCharacteristic(AttributeConvention.VALID_VALUES_CHARACTERISTIC)); |
| final V[] values = (V[]) Array.newInstance(valueClass, c.size()); |
| int index = 0; |
| for (final Object value : c) { |
| values[index++] = (V) value; // ArrayStoreException if 'value' is not the expected type. |
| } |
| return values; |
| } |
| |
| /** |
| * Sets an enumeration of valid values for the 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. |
| * |
| * @see #characteristics() |
| * @see AttributeConvention#VALID_VALUES_CHARACTERISTIC |
| */ |
| @SafeVarargs |
| public final AttributeTypeBuilder<V> setValidValues(final V... values) { |
| return setCharacteristic(AttributeConvention.VALID_VALUES_CHARACTERISTIC, |
| Set.class, CollectionsExt.immutableSet(false, values)); |
| } |
| |
| /** |
| * Returns the maximal length that characterizes the {@link CharSequence} values of this attribute. |
| * This convenience method returns the value of the characteristic set by {@link #setMaximalLength(Integer)}. |
| * |
| * @return the maximal length of {@link CharSequence} attribute values, or {@code null}. |
| */ |
| public Integer getMaximalLength() { |
| return (Integer) getCharacteristic(AttributeConvention.MAXIMAL_LENGTH_CHARACTERISTIC); |
| } |
| |
| /** |
| * 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. |
| * |
| * @see #characteristics() |
| * @see AttributeConvention#MAXIMAL_LENGTH_CHARACTERISTIC |
| */ |
| public AttributeTypeBuilder<V> setMaximalLength(final Integer length) { |
| return setCharacteristic(AttributeConvention.MAXIMAL_LENGTH_CHARACTERISTIC, Integer.class, length); |
| } |
| |
| /** |
| * Returns the coordinate reference system associated to attribute values. |
| * This convenience method returns the value of the characteristic set by {@link #setCRS(CoordinateReferenceSystem)}. |
| * |
| * @return the coordinate reference system associated to attribute values, or {@code null}. |
| */ |
| public CoordinateReferenceSystem getCRS() { |
| return (CoordinateReferenceSystem) getCharacteristic(AttributeConvention.CRS_CHARACTERISTIC); |
| } |
| |
| /** |
| * 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. |
| * |
| * @see #characteristics() |
| * @see AttributeConvention#CRS_CHARACTERISTIC |
| */ |
| public AttributeTypeBuilder<V> setCRS(final CoordinateReferenceSystem crs) { |
| return setCharacteristic(AttributeConvention.CRS_CHARACTERISTIC, CoordinateReferenceSystem.class, crs); |
| } |
| |
| /** |
| * Implementation of all getter methods for characteristics. |
| */ |
| private Object getCharacteristic(final GenericName name) { |
| for (final CharacteristicTypeBuilder<?> characteristic : characteristics) { |
| if (name.equals(characteristic.getName())) { |
| return characteristic.getDefaultValue(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Implementation of all setter methods for characteristics. |
| */ |
| private <C> AttributeTypeBuilder<V> setCharacteristic(final GenericName name, final Class<C> type, final C value) { |
| for (final CharacteristicTypeBuilder<?> characteristic : characteristics) { |
| if (name.equals(characteristic.getName())) { |
| characteristic.set(value); |
| clearCache(); |
| return this; |
| } |
| } |
| addCharacteristic(type).setDefaultValue(value).setName(name); |
| return this; |
| } |
| |
| /** |
| * Returns the builder for the characteristic of the given name. The given name does not need to contains |
| * all elements of a {@link org.opengis.util.ScopedName}; it is okay to specify only the tip (for example |
| * {@code "myName"} instead of {@code "myScope:myName"}) provided that ignoring the name head does not |
| * create ambiguity. |
| * |
| * @param name name of the characteristic to search. |
| * @return characteristic of the given name, or {@code null} if none. |
| * @throws IllegalArgumentException if the given name is ambiguous. |
| * |
| * @see #characteristics() |
| */ |
| public CharacteristicTypeBuilder<?> getCharacteristic(final String name) { |
| return forName(characteristics, name, true); |
| } |
| |
| /** |
| * 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(Units.CELSIUS); |
| * } |
| * |
| * The default characteristic name is the name of the given type, but callers should invoke one |
| * of the {@code CharacteristicTypeBuilder.setName(…)} methods on the returned instance with a better name. |
| * |
| * @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. |
| * |
| * @see #characteristics() |
| */ |
| public <C> CharacteristicTypeBuilder<C> addCharacteristic(final Class<C> type) { |
| ensureNonNull("type", type); |
| final CharacteristicTypeBuilder<C> characteristic = new CharacteristicTypeBuilder<>(this, type); |
| characteristics.add(characteristic); |
| clearCache(); |
| return characteristic; |
| } |
| |
| /** |
| * Adds another attribute type that describes this attribute type, using an existing one as a template. |
| * See <cite>"Attribute characterization"</cite> in {@link DefaultAttributeType} Javadoc for more information. |
| * |
| * @param <C> the compile-time type of values in the {@code template} argument. |
| * @param template an existing attribute type to use as a template. |
| * @return a builder for a characteristic of this attribute, initialized with the values of the given template. |
| * |
| * @see #characteristics() |
| */ |
| public <C> CharacteristicTypeBuilder<C> addCharacteristic(final AttributeType<C> template) { |
| ensureNonNull("template", template); |
| final CharacteristicTypeBuilder<C> characteristic = new CharacteristicTypeBuilder<>(this, template); |
| characteristics.add(characteristic); |
| clearCache(); |
| return characteristic; |
| } |
| |
| /** |
| * Returns a view of all characteristics added to the {@code AttributeType} to build. |
| * The returned list is <cite>live</cite>: changes in this builder are reflected in that list and conversely. |
| * However the returned list allows only {@linkplain List#remove(Object) remove} operations; |
| * new characteristics can be added only by calls to one of the {@code set/addCharacteristic(…)} methods. |
| * |
| * @return a live list over the characteristics declared to this builder. |
| * |
| * @see #getCharacteristic(String) |
| * @see #addCharacteristic(Class) |
| * @see #addCharacteristic(AttributeType) |
| * @see #setValidValues(Object...) |
| * @see #setCRS(CoordinateReferenceSystem) |
| */ |
| public List<CharacteristicTypeBuilder<?>> characteristics() { |
| return new RemoveOnlyList<>(characteristics); |
| } |
| |
| /** |
| * Returns the roles that the attribute play in the pre-defined operations managed by {@code AttributeTypeBuilder}. |
| * The set returned by this method is <cite>live</cite>: additions or removal on that set are reflected back on |
| * this builder, and conversely. |
| * |
| * @return the roles that the attribute play in the pre-defined operations managed by {@code AttributeTypeBuilder}. |
| */ |
| public Set<AttributeRole> roles() { |
| return new SetOfUnknownSize<AttributeRole>() { |
| @Override public Iterator<AttributeRole> iterator() {return new RoleIter();} |
| @Override public boolean add(AttributeRole role) {return addRole(role);} |
| }; |
| } |
| |
| /** |
| * The iterator returned by the {@link AttributeTypeBuilder#roles()} set. |
| */ |
| private final class RoleIter extends AbstractIterator<AttributeRole> { |
| /** |
| * Index of the next {@code AttributeRole} to return. |
| */ |
| private int index; |
| |
| /** |
| * Prepares the next {@code AttributeRole} on which to iterate and returns |
| * {@code true} if such {@code AttributeRole} has been found. |
| */ |
| @Override |
| @SuppressWarnings("fallthrough") |
| public boolean hasNext() { |
| if (next == null) { |
| switch (index) { |
| case 0: { |
| if (isIdentifier) { |
| next = AttributeRole.IDENTIFIER_COMPONENT; |
| break; |
| } |
| index++; // Fall through for testing the case for next 'index' value. |
| } |
| case 1: { |
| if (owner().defaultGeometry == AttributeTypeBuilder.this) { |
| next = AttributeRole.DEFAULT_GEOMETRY; |
| break; |
| } |
| index++; // Fall through for testing the case for next 'index' value. |
| } |
| default: { |
| return false; |
| } |
| } |
| index++; |
| } |
| return true; |
| } |
| |
| /** |
| * Removes the element returned by the last {@link #next()} method. |
| */ |
| @Override |
| public void remove() { |
| switch (index) { |
| case 1: isIdentifier = false; break; |
| case 2: owner().defaultGeometry = null; break; |
| default: throw new IllegalStateException(); |
| } |
| } |
| } |
| |
| /** |
| * Flags this attribute as an input of one of the pre-defined operations managed by {@code AttributeTypeBuilder}. |
| * Invoking this method is equivalent to invoking <code>{@linkplain #roles()}.add(role)</code>. |
| * |
| * @param role the role to add to the attribute (shall not be null). |
| * @return {@code true} if the given role has been added to the attribute. |
| */ |
| public boolean addRole(final AttributeRole role) { |
| final FeatureTypeBuilder owner = owner(); |
| ensureNonNull("role", role); |
| switch (role) { |
| case IDENTIFIER_COMPONENT: { |
| if (!isIdentifier) { |
| isIdentifier = true; |
| owner.identifierCount++; |
| owner.clearCache(); // The change does not impact this attribute itself. |
| return true; |
| } |
| break; |
| } |
| case DEFAULT_GEOMETRY: { |
| if (owner.defaultGeometry != this) { |
| if (!Geometries.isKnownType(valueClass)) { |
| throw new IllegalStateException(errors().getString(Errors.Keys.UnsupportedImplementation_1, valueClass)); |
| } |
| if (owner.defaultGeometry != null) { |
| throw new IllegalStateException(resources().getString(Resources.Keys.PropertyAlreadyExists_2, |
| owner.getDisplayName(), AttributeConvention.GEOMETRY_PROPERTY)); |
| } |
| owner.defaultGeometry = this; |
| owner.clearCache(); // The change does not impact this attribute itself. |
| return true; |
| } |
| break; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns {@code true} if {@link AttributeRole#IDENTIFIER_COMPONENT} has been associated to this attribute. |
| */ |
| @Override |
| boolean isIdentifier() { |
| return isIdentifier; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public AttributeTypeBuilder<V> setDefinition(final CharSequence definition) { |
| super.setDefinition(definition); |
| return this; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public AttributeTypeBuilder<V> setDesignation(final CharSequence designation) { |
| super.setDesignation(designation); |
| return this; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public AttributeTypeBuilder<V> setDescription(final CharSequence description) { |
| super.setDescription(description); |
| return this; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public AttributeTypeBuilder<V> setDeprecated(final boolean deprecated) { |
| super.setDeprecated(deprecated); |
| return this; |
| } |
| |
| /** |
| * Appends a text inside the value returned by {@link #toString()}, before the closing bracket. |
| */ |
| @Override |
| final void toStringInternal(final StringBuilder buffer) { |
| buffer.append(" : ").append(Classes.getShortName(valueClass)); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void remove() { |
| if (isIdentifier) { |
| isIdentifier = false; |
| owner().identifierCount--; // Owner should never be null since we set 'isIdentifier' to false. |
| } |
| super.remove(); |
| } |
| |
| /** |
| * Builds the attribute type from the information specified to this builder. |
| * If a type has already been built and this builder state has not changed since the type creation, |
| * then the previously created {@code AttributeType} instance is returned. |
| * |
| * <div class="note"><b>Example:</b> |
| * the following lines of code add a "name" attribute to a "City" feature, then get the corresponding |
| * {@code AttributeType<String>} instance. If no setter method is invoked on the builder of the "name" |
| * attribute after those lines, then the {@code name} variable below will reference the same instance |
| * than the "name" attribute in the {@code city} type. |
| * |
| * {@preformat java |
| * FeatureTypeBuilder builder = new FeatureTypeBuilder().setName("City"); |
| * AttributeType<String> name = builder.addAttribute(String.class).setName("name").build(); |
| * FeatureType city = builder.build(); |
| * |
| * assert city.getProperty("name") == name : "AttributeType instance should be the same."; |
| * } |
| * |
| * Note that {@code city.getProperty("name")} returns {@code AttributeType<?>}, |
| * i.e. the {@linkplain #getValueClass() value class} is lost at compile-time. |
| * By comparison, this {@code build()} method has a more accurate return type. |
| * </div> |
| * |
| * @return the attribute type. |
| */ |
| @Override |
| public AttributeType<V> build() { |
| if (property == null) { |
| 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; |
| } |
| } |