sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1639258 - in /sis/branches/JDK8/core/sis-feature/src: main/java/org/apache/sis/feature/ test/java/org/apache/sis/feature/
Date Thu, 13 Nov 2014 07:16:07 GMT
Author: desruisseaux
Date: Thu Nov 13 07:16:06 2014
New Revision: 1639258

URL: http://svn.apache.org/r1639258
Log:
Initial implementation of AttributeType.characterizedBy association.
This association is part of ISO 19109, and will be useful for providing information about accuracy of measurement.
Corresponding association has not yet been added to Attribute.

Added:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java   (with props)
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAssociation.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertySingleton.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAssociation.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAssociation.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -51,7 +51,7 @@ import org.opengis.feature.FeatureAssoci
  *
  * @see DefaultAssociationRole
  */
-public abstract class AbstractAssociation extends Field<Feature> implements FeatureAssociation, Serializable {
+public abstract class AbstractAssociation extends Field<Feature> implements FeatureAssociation, Cloneable, Serializable {
     /**
      * For cross-version compatibility.
      */
@@ -233,4 +233,19 @@ public abstract class AbstractAssociatio
             }
         });
     }
+
+    /**
+     * Returns a copy of this association.
+     * The default implementation returns a <em>shallow</em> copy:
+     * the association {@linkplain #getValue() value} is <strong>not</strong> cloned.
+     * However subclasses may choose to do otherwise.
+     *
+     * @return A clone of this association.
+     * @throws CloneNotSupportedException if this association can not be cloned.
+     *         The default implementation never throw this exception. However subclasses may throw it.
+     */
+    @Override
+    public AbstractAssociation clone() throws CloneNotSupportedException {
+        return (AbstractAssociation) super.clone();
+    }
 }

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -59,7 +59,7 @@ import org.opengis.feature.AttributeType
  *
  * @see DefaultAttributeType
  */
-public abstract class AbstractAttribute<V> extends Field<V> implements Attribute<V>, Serializable {
+public abstract class AbstractAttribute<V> extends Field<V> implements Attribute<V>, Cloneable, Serializable {
     /**
      * For cross-version compatibility.
      */
@@ -276,4 +276,21 @@ public abstract class AbstractAttribute<
     public String toString() {
         return FieldType.toString("Attribute", type, Classes.getShortName(type.getValueClass()), getValues().iterator());
     }
+
+    /**
+     * Returns a copy of this attribute.
+     * The default implementation returns a <em>shallow</em> copy:
+     * the attribute {@linkplain #getValue() value} is <strong>not</strong> cloned.
+     * However subclasses may choose to do otherwise.
+     *
+     * @return A clone of this attribute.
+     * @throws CloneNotSupportedException if this attribute can not be cloned.
+     *         The default implementation never throw this exception. However subclasses may throw it,
+     *         for example on attempt to clone the attribute value.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public AbstractAttribute<V> clone() throws CloneNotSupportedException {
+        return (AbstractAttribute<V>) super.clone();
+    }
 }

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -288,4 +288,28 @@ public class AbstractIdentifiedType impl
         }
         return false;
     }
+
+    /**
+     * Returns the string representation of the given name, making sure that the name is non-null
+     * and the string non-empty. This method is used for checking argument validity.
+     *
+     * @param name      The name for which to get the string representation.
+     * @param container The feature or attribute which contains the named characteristics.
+     * @param argument  The name of the argument ({@code "properties"} or {@code "characterizedBy"}).
+     * @param index     Index of the characteristics having the given name.
+     * @throws IllegalArgumentException if the given name is null or have an empty string representation.
+     */
+    static String toString(final GenericName name, final IdentifiedType container, final String argument, final int index) {
+        short key = Errors.Keys.MissingValueForProperty_1;
+        if (name != null) {
+            final String s = name.toString();
+            if (!s.isEmpty()) {
+                return s;
+            }
+            key = Errors.Keys.EmptyProperty_1;
+        }
+        final StringBuilder b = new StringBuilder(40).append("Type[“").append(container.getName()).append("”].")
+                .append(argument).append('[').append(index).append("].name");
+        throw new IllegalArgumentException(Errors.format(key, b.toString()));
+    }
 }

Added: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java?rev=1639258&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java (added)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -0,0 +1,319 @@
+/*
+ * 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;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.AbstractMapEntry;
+
+// Branch-dependent imports
+import org.opengis.feature.Attribute;
+
+
+/**
+ * Implementation of {@link AbstractAttribute#characteristics()} map.
+ * This map holds only the attribute characteristics which have been explicitely set or requested.
+ *
+ * <p>This implementation has one behavioral difference compared to the familiar {@code Map} invariants:
+ * a call to the {@link #get(Object)} method creates a new {@code Attribute} instance if the given key
+ * is valid and no instance existed previously for that key, thus increasing this {@code Map} size by one.
+ * If this behavior is not desired, then caller should check {@link #containsKey(Object)} first.</p>
+ *
+ * <div class="note"><b>Note:</b>
+ * Such departures are unusual, but exists also in the JDK. For example {@link java.util.WeakHashMap} too
+ * may change its size as a result of read-only methods. Those maps behave as though an unknown thread is
+ * silently adding or removing entries.
+ * </div>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ */
+final class CharacteristicMap extends AbstractMap<String,Attribute<?>> {
+    /**
+     * The attribute source for which to provide characteristics.
+     */
+    private final Attribute<?> source;
+
+    /**
+     * Characteristics of the {@code source} attribute, created when first needed.
+     */
+    Attribute<?>[] characterizedBy;
+
+    /**
+     * Description of the attribute characteristics.
+     */
+    final CharacteristicTypeMap types;
+
+    /**
+     * Creates an initially empty map of attribute characteristics.
+     *
+     * @param source The attribute which is characterized by {@code characterizedBy}.
+     * @param characterizedBy Description of the characteristics of {@code source}.
+     */
+    CharacteristicMap(final Attribute<?> source, final CharacteristicTypeMap types) {
+        this.source = source;
+        this.types  = types;
+    }
+
+    /**
+     * Returns {@code false} if this map contains at least one characteristic.
+     */
+    @Override
+    public boolean isEmpty() {
+        if (characterizedBy != null) {
+            for (final Attribute<?> attribute : characterizedBy) {
+                if (attribute != null) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns the number of attribute characteristics.
+     */
+    @Override
+    public int size() {
+        int n = 0;
+        if (characterizedBy != null) {
+            for (final Attribute<?> attribute : characterizedBy) {
+                if (attribute != null) {
+                    n++;
+                }
+            }
+        }
+        return n;
+    }
+
+    /**
+     * Returns {@code true} if this map contains an attribute characteristic for the given name.
+     */
+    @Override
+    public boolean containsKey(final Object key) {
+        if (characterizedBy != null) {
+            final Integer index = types.indices.get(key);
+            if (index != null) {
+                return characterizedBy[index] != null;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the attribute characteristic for the given name, or {@code null} if none. If no characteristic
+     * exist for the given key and that key is valid, then this method will silently creates a new instance.
+     * The intend is to allow callers to set a characteristic values using the following pattern:
+     *
+     * {@preformat java
+     *     Attribute<?> characteristic = attribute.characteristics().get("accuracy");
+     *     Features.cast(characteristic, Double.class).setValue(0.1);
+     * }
+     */
+    @Override
+    public Attribute<?> get(final Object key) {
+        final Integer index = types.indices.get(key);
+        if (index == null) {
+            return null;
+        }
+        if (characterizedBy == null) {
+            characterizedBy = new Attribute<?>[types.characterizedBy.length];
+        }
+        Attribute<?> attribute = characterizedBy[index];
+        if (attribute == null) {
+            attribute = AbstractAttribute.create(types.characterizedBy[index]);
+            characterizedBy[index] = attribute;
+        }
+        return attribute;
+    }
+
+    /**
+     * Removes the attribute characteristic for the given name.
+     */
+    @Override
+    public Attribute<?> remove(final Object key) {
+        if (characterizedBy != null) {
+            final Integer index = types.indices.get(key);
+            if (index != null) {
+                final Attribute<?> previous = characterizedBy[index];
+                characterizedBy[index] = null;
+                return previous;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Sets the attribute characteristic for the given name.
+     */
+    @Override
+    public Attribute<?> put(final String key, final Attribute<?> value) {
+        ArgumentChecks.ensureNonNull("value", value);
+        final Integer index = types.indices.get(key);
+        if (index == null) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.PropertyNotFound_2, source.getName(), key));
+        }
+        if (!types.characterizedBy[index].equals(value.getType())) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedPropertyType_1, key));
+        }
+        if (characterizedBy == null) {
+            characterizedBy = new Attribute<?>[types.characterizedBy.length];
+        }
+        final Attribute<?> previous = characterizedBy[index];
+        characterizedBy[index] = value;
+        return previous;
+    }
+
+    /**
+     * Returns the set of entries in this map.
+     */
+    @Override
+    public Set<Map.Entry<String, Attribute<?>>> entrySet() {
+        return (characterizedBy != null) ? new Entries() : Collections.emptySet();
+    }
+
+    /**
+     * The set of entries in the {@link CharacteristicMap}.
+     */
+    private final class Entries extends AbstractSet<Map.Entry<String,Attribute<?>>> {
+        /** Creates a new set of entries. */
+        Entries() {
+        }
+
+        /** Returns the number of entries. */
+        @Override
+        public int size() {
+            return CharacteristicMap.this.size();
+        }
+
+        /** Returns an iterator over the entries. */
+        @Override
+        public Iterator<Map.Entry<String, Attribute<?>>> iterator() {
+            return new Iter();
+        }
+    }
+
+    /**
+     * Iterator over the {@link CharacteristicMap} entries.
+     */
+    private final class Iter implements Iterator<Map.Entry<String, Attribute<?>>> {
+        /** Index of the next element to return in the iteration. */
+        private int index;
+
+        /** The next element to return, or {@code null} if we reached the end of iteration. */
+        private Attribute<?> next;
+
+        /** Index of the element returned by the last call to {@link #next()}, or -1 if none. */
+        private int previous;
+
+        /** Creates a new iterator. */
+        Iter() {
+            move();
+        }
+
+        /** Advances {@link #index} to the next attribute to return. */
+        private void move() {
+            previous = -1;
+            while (index < characterizedBy.length) {
+                next = characterizedBy[index];
+                if (next != null) return;
+                index++;
+            }
+            next = null;
+        }
+
+        /** Returns {@code true} if there is more entries in the iteration. */
+        @Override
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        /** Creates and return the next entry. */
+        @Override
+        public Map.Entry<String, Attribute<?>> next() {
+            if (hasNext()) {
+                return new Entry(previous = index++, next);
+            } else {
+                throw new NoSuchElementException();
+            }
+        }
+
+        /** Removes the last element returned by {@link #next()}. */
+        @Override
+        public void remove() {
+            if (previous >= 0) {
+                characterizedBy[previous] = null;
+                previous = -1;
+            } else {
+                throw new IllegalStateException();
+            }
+        }
+    }
+
+    /**
+     * An entry returned by the {@link CharacteristicMap#entrySet()} iterator.
+     * The key and value are never null, even in case of concurrent modification.
+     * This entry supports the {@link #setValue(Attribute)} operation.
+     */
+    private final class Entry extends AbstractMapEntry<String, Attribute<?>> {
+        /** Index of the attribute characteristics represented by this entry. */
+        private final int index;
+
+        /** The current attribute value, which is guaranteed to be non-null. */
+        private Attribute<?> value;
+
+        /** Creates a new entry for the characteristic at the given index. */
+        Entry(final int index, final Attribute<?> value) {
+            this.index = index;
+            this.value = value;
+        }
+
+        /** Returns the name of the attribute characteristic. */
+        @Override
+        public String getKey() {
+            return types.names[index];
+        }
+
+        /** Returns the attribute characteristic (never {@code null}). */
+        @Override
+        public Attribute<?> getValue() {
+            return value;
+        }
+
+        /** Sets the attribute characteristic. */
+        @Override
+        public Attribute<?> setValue(final Attribute<?> value) {
+            ArgumentChecks.ensureNonNull("value", value);
+            if (!types.characterizedBy[index].equals(value.getType())) {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedPropertyType_1, getKey()));
+            }
+            final Attribute<?> previous = this.value;
+            characterizedBy[index] = value;
+            this.value = value;
+            return previous;
+        }
+    }
+}

Propchange: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java?rev=1639258&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java (added)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -0,0 +1,191 @@
+/*
+ * 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;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.HashMap;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.util.collection.Containers;
+import org.apache.sis.util.resources.Errors;
+
+import static org.apache.sis.util.ArgumentChecks.ensureNonNullElement;
+
+// Branch-dependent imports
+import org.opengis.feature.AttributeType;
+
+
+/**
+ * Implementation of the map returned by {@link DefaultAttributeType#characteristics()}.
+ * Information provided by this implementation are also used by {@link CharacteristicMap}.
+ *
+ * <p><b>Comparison with standard hash map:</b>
+ * The straightforward approach would be to store the attributes directly as values in a standard {@code HashMap}.
+ * But instead of that, we store attributes in an array and the array indices in a {@code HashMap}. This level of
+ * indirection is useless if we consider only the {@link DefaultAttributeType#characteristics()} method, since a
+ * standard {@code HashMap<String,AttributeType>} would work as well or better. However this level of indirection
+ * become useful for {@link CharacteristicMap} (the map returned by {@link DefaultAttribute#characteristics()}),
+ * since it allows a more efficient storage. We do this effort because some applications may create a very large
+ * amount of attribute instances.</p>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ */
+final class CharacteristicTypeMap extends AbstractMap<String,AttributeType<?>> {
+    /**
+     * Characteristics of an other attribute type (the {@code source} attribute given to the constructor).
+     * This array shall not be modified.
+     */
+    final AttributeType<?>[] characterizedBy;
+
+    /**
+     * Name of the {@code characterizedBy} attribute types, used only during iteration over map entries.
+     * This array shall not be modified.
+     */
+    final String[] names;
+
+    /**
+     * The names of attribute types listed in the {@link #characterizedBy} array,
+     * together where their index in the array. This map shall not be modified.
+     */
+    final Map<String,Integer> indices;
+
+    /**
+     * Creates a new map for the given attribute characteristics.
+     *
+     * @param  source The attribute which is characterized by {@code characterizedBy}.
+     * @param  characterizedBy Characteristics of {@code source}. Should not be empty.
+     * @throws IllegalArgumentException if two characteristics have the same name.
+     */
+    CharacteristicTypeMap(final AttributeType<?> source, final AttributeType<?>[] characterizedBy) {
+        this.characterizedBy = characterizedBy.clone();
+        names = new String[characterizedBy.length];
+        int index = 0;
+        final Map<String,Integer> indices = new HashMap<>(Containers.hashMapCapacity(characterizedBy.length));
+        for (int i=0; i<characterizedBy.length; i++) {
+            final AttributeType<?> attribute = characterizedBy[i];
+            ensureNonNullElement("characterizedBy", i, attribute);
+            final String name = AbstractIdentifiedType.toString(attribute.getName(), source, "characterizedBy", i);
+            names[index] = name;
+            if (indices.put(name, index++) != null) {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.DuplicatedIdentifier_1, name));
+            }
+        }
+        this.indices = CollectionsExt.compact(indices);
+    }
+
+    /**
+     * Returns the number of attribute characteristics.
+     */
+    @Override
+    public int size() {
+        return characterizedBy.length;
+    }
+
+    /**
+     * Returns {@code true} if this map contains an attribute characteristic of the given name.
+     */
+    @Override
+    public boolean containsKey(final Object key) {
+        return indices.containsKey(key);
+    }
+
+    /**
+     * Returns {@code true} if this map contains the given attribute characteristic.
+     */
+    @Override
+    public boolean containsValue(final Object key) {
+        for (final AttributeType<?> type : characterizedBy) {
+            if (type.equals(key)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the attribute characteristic for the given name, or {@code null} if none.
+     */
+    @Override
+    public AttributeType<?> get(final Object key) {
+        final Integer index = indices.get(key);
+        return (index != null) ? characterizedBy[index] : null;
+    }
+
+    /**
+     * Returns the set of entries in this map.
+     */
+    @Override
+    public Set<Entry<String, AttributeType<?>>> entrySet() {
+        return new Entries();
+    }
+
+    /**
+     * The set of entries in the {@link CharacteristicTypeMap}.
+     */
+    private final class Entries extends AbstractSet<Entry<String,AttributeType<?>>> {
+        /** Creates a new set of entries. */
+        Entries() {
+        }
+
+        /** Returns the number of entries. */
+        @Override
+        public int size() {
+            return CharacteristicTypeMap.this.size();
+        }
+
+        /** Returns an iterator over the entries. */
+        @Override
+        public Iterator<Entry<String, AttributeType<?>>> iterator() {
+            return new Iter();
+        }
+    }
+
+    /**
+     * Iterator over the {@link CharacteristicTypeMap} entries.
+     */
+    private final class Iter implements Iterator<Entry<String, AttributeType<?>>> {
+        /** Index of the next element to return in the iteration. */
+        private int index;
+
+        /** Creates a new iterator. */
+        Iter() {
+        }
+
+        /** Returns {@code true} if there is more entries in the iteration. */
+        @Override
+        public boolean hasNext() {
+            return index < characterizedBy.length;
+        }
+
+        /** Creates and return the next entry. */
+        @Override
+        public Entry<String, AttributeType<?>> next() {
+            if (hasNext()) {
+                return new SimpleImmutableEntry<>(names[index], characterizedBy[index++]);
+            } else {
+                throw new NoSuchElementException();
+            }
+        }
+    }
+}

Propchange: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicTypeMap.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -17,6 +17,11 @@
 package org.apache.sis.feature;
 
 import java.util.Map;
+import java.util.Collections;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.IOException;
+import java.io.InvalidObjectException;
 import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
 import org.apache.sis.util.Debug;
@@ -38,7 +43,9 @@ import org.opengis.feature.AttributeType
  *
  * <div class="note"><b>Note:</b>
  * Compared to the Java language, {@code AttributeType} is equivalent to {@link java.lang.reflect.Field}
- * while {@code FeatureType} is equivalent to {@link Class}.</div>
+ * while {@code FeatureType} is equivalent to {@link Class}.
+ * Attribute characterization (discussed below) is similar to {@link java.lang.annotation.Annotation}.
+ * </div>
  *
  * {@section Value type}
  * Attributes can be used for both spatial and non-spatial properties.
@@ -52,6 +59,28 @@ import org.opengis.feature.AttributeType
  *   <tr><td>Horizontal accuracy</td> <td>{@link org.opengis.metadata.quality.PositionalAccuracy}</td></tr>
  * </table>
  *
+ * {@section Attribute characterization}
+ * An {@code Attribute} can be characterized by other attributes. For example an attribute that carries a measurement
+ * (e.g. air temperature) may have another attribute that holds the measurement accuracy (e.g. ±0.1°C).
+ * The accuracy value is often constant for all instances of that attribute
+ * (e.g. for all temperature measurements in the same file), but this is not mandatory.
+ * Such accuracy could be stored as an ordinary, independent, attribute (like an other column in a table),
+ * but storing accuracy as a <cite>characteristic</cite> of the measurement attribute instead
+ * provides the following advantages:
+ *
+ * <ul>
+ *   <li>The same characteristic name (e.g. “accuracy”) can be used for different attributes
+ *       (e.g. “temperature”, “humidity”, <i>etc.</i>) since all characteristics are local to their attribute.</li>
+ *   <li>A reference to an attribute gives also access to its characteristics. For example any method expecting
+ *       an {@code Attribute} argument, when given a measurement, can also get its accuracy in same time.</li>
+ *   <li>In the common case of a {@linkplain DefaultFeatureType#isSimple() simple feature} with characteristics
+ *       that are constants, declaring them as attribute characteristics allows to specify the constants only once.</li>
+ * </ul>
+ *
+ * Constant values of characteristics are given by their {@linkplain #getDefaultValue() default value}.
+ * It is still possible for any specific {@code Attribute} instance to specify their own value,
+ * but {@linkplain DefaultFeatureType#isSimple() simple feature} usually don't do that.
+ *
  * {@section Immutability and thread safety}
  * Instances of this class are immutable if all properties ({@link GenericName} and {@link InternationalString}
  * instances) and all arguments (e.g. {@code defaultValue}) given to the constructor are also immutable.
@@ -75,7 +104,7 @@ public class DefaultAttributeType<V> ext
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = 8215784957556648553L;
+    private static final long serialVersionUID = -817024213677735239L;
 
     /**
      * The class that describe the type of attribute values.
@@ -92,6 +121,14 @@ public class DefaultAttributeType<V> ext
     private final V defaultValue;
 
     /**
+     * Other attribute types that describes this attribute type, or {@code null} if none.
+     * This is used for attributes of attribute (e.g. accuracy of a position).
+     *
+     * @see #characteristics()
+     */
+    private transient CharacteristicTypeMap characteristics;
+
+    /**
      * Constructs an attribute type from the given properties. The identification map is given unchanged to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
      * The following table is a reminder of main (not all) recognized map entries:
@@ -125,21 +162,59 @@ public class DefaultAttributeType<V> ext
      *   </tr>
      * </table>
      *
-     * @param identification The name and other information to be given to this attribute type.
-     * @param valueClass     The type of attribute values.
-     * @param minimumOccurs  The minimum number of occurrences of the attribute within its containing entity.
-     * @param maximumOccurs  The maximum number of occurrences of the attribute within its containing entity,
-     *                       or {@link Integer#MAX_VALUE} if there is no restriction.
-     * @param defaultValue   The default value for the attribute, or {@code null} if none.
+     * @param identification  The name and other information to be given to this attribute type.
+     * @param valueClass      The type of attribute values.
+     * @param minimumOccurs   The minimum number of occurrences of the attribute within its containing entity.
+     * @param maximumOccurs   The maximum number of occurrences of the attribute within its containing entity,
+     *                        or {@link Integer#MAX_VALUE} if there is no restriction.
+     * @param defaultValue    The default value for the attribute, or {@code null} if none.
+     * @param characterizedBy Other attribute types that describes this attribute type (can be {@code null} for none).
+     *                        For example if this new {@code DefaultAttributeType} describes a measurement,
+     *                        then {@code characterizedBy} could holds the measurement accuracy.
+     *                        See "<cite>Attribute characterization</cite>" in class Javadoc for more information.
      */
     public DefaultAttributeType(final Map<String,?> identification, final Class<V> valueClass,
-            final int minimumOccurs, final int maximumOccurs, final V defaultValue)
+            final int minimumOccurs, final int maximumOccurs, final V defaultValue,
+            final AttributeType<?>... characterizedBy)
     {
         super(identification, minimumOccurs, maximumOccurs);
         ensureNonNull("valueClass",   valueClass);
         ensureCanCast("defaultValue", valueClass, defaultValue);
-        this.valueClass   = valueClass;
-        this.defaultValue = Numerics.cached(defaultValue);
+        this.valueClass      = valueClass;
+        this.defaultValue    = Numerics.cached(defaultValue);
+        if (characterizedBy != null && characterizedBy.length != 0) {
+            characteristics = new CharacteristicTypeMap(this, characterizedBy);
+        }
+    }
+
+    /**
+     * Invoked on serialization for saving the {@link #characteristics} field.
+     *
+     * @param  out The output stream where to serialize this attribute type.
+     * @throws IOException If an I/O error occurred while writing.
+     */
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+        out.writeObject(characteristics != null ? characteristics.characterizedBy : null);
+    }
+
+    /**
+     * Invoked on deserialization for restoring the {@link #characteristics} field.
+     *
+     * @param  in The input stream from which to deserialize an attribute type.
+     * @throws IOException If an I/O error occurred while reading or if the stream contains invalid data.
+     * @throws ClassNotFoundException If the class serialized on the stream is not on the classpath.
+     */
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        try {
+            final AttributeType<?>[] characterizedBy = (AttributeType<?>[]) in.readObject();
+            if (characterizedBy != null) {
+                characteristics = new CharacteristicTypeMap(this, characterizedBy);
+            }
+        } catch (RuntimeException e) { // At least ClassCastException, NullPointerException and IllegalArgumentException.
+            throw (IOException) new InvalidObjectException(e.getMessage()).initCause(e);
+        }
     }
 
     /**
@@ -205,13 +280,31 @@ public class DefaultAttributeType<V> ext
     }
 
     /**
+     * Other attribute types that describes this attribute type.
+     * See "<cite>Attribute characterization</cite>" in class Javadoc for more information.
+     *
+     * <div class="note"><b>Example:</b>
+     * An attribute that carries a measurement (e.g. air temperature) may have another attribute that holds the
+     * measurement accuracy. The accuracy is often constant for all measurements in a file, but not necessarily.
+     * If the accuracy is a constant, then the characteristics {@linkplain #getDefaultValue() default value}
+     * shall hold that constant.
+     * </div>
+     *
+     * @return Other attribute types that describes this attribute type, or an empty set if none.
+     */
+    public Map<String,AttributeType<?>> characteristics() {
+        return (characteristics != null) ? characteristics : Collections.emptyMap();
+    }
+
+    /**
      * Returns a hash code value for this attribute type.
      *
      * @return {@inheritDoc}
      */
     @Override
     public int hashCode() {
-        return super.hashCode() + valueClass.hashCode() + Objects.hashCode(defaultValue);
+        return super.hashCode() + valueClass.hashCode() + Objects.hashCode(defaultValue)
+               + Objects.hashCode(characteristics);
     }
 
     /**
@@ -227,7 +320,8 @@ public class DefaultAttributeType<V> ext
         if (super.equals(obj)) {
             final DefaultAttributeType<?> that = (DefaultAttributeType<?>) obj;
             return valueClass == that.valueClass &&
-                   Objects.equals(defaultValue, that.defaultValue);
+                   Objects.equals(defaultValue, that.defaultValue) &&
+                   Objects.equals(characteristics, that.characteristics);
         }
         return false;
     }

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -354,7 +354,7 @@ public class DefaultFeatureType extends 
         int index = -1;
         for (final PropertyType property : source.getProperties(false)) {
             ArgumentChecks.ensureNonNullElement("properties", ++index, property);
-            final String name = toString(property.getName(), source, index);
+            final String name = toString(property.getName(), source, "properties", index);
             final PropertyType previous = byName.put(name, property);
             if (previous != null) {
                 if (!isAssignableIgnoreName(previous, property)) {
@@ -385,33 +385,6 @@ public class DefaultFeatureType extends 
     }
 
     /**
-     * Returns the string representation of the given name, making sure that the name is non-null
-     * and the string non-empty. This method is used for checking argument validity.
-     *
-     * <p>{@code this} shall be the instance in process of being created, not any other instance.</p>
-     *
-     * @param name   The name for which to get the string representation.
-     * @param source The feature which contains the property (typically {@code this}).
-     * @param index  Index of the property having the given name.
-     */
-    private String toString(final GenericName name, final FeatureType source, final int index) {
-        short key = Errors.Keys.MissingValueForProperty_1;
-        if (name != null) {
-            final String s = name.toString();
-            if (!s.isEmpty()) {
-                return s;
-            }
-            key = Errors.Keys.EmptyProperty_1;
-        }
-        final StringBuilder b = new StringBuilder(30);
-        if (source != this) {
-            b.append(source.getName()).append('.');
-        }
-        throw new IllegalArgumentException(Errors.format(key,
-                b.append("properties[").append(index).append("].name").toString()));
-    }
-
-    /**
      * If an associated feature type is a placeholder for a {@code FeatureType} to be defined later,
      * replaces the placeholder by the actual instance if available. Otherwise do nothing.
      *

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAssociation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAssociation.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAssociation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAssociation.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -49,7 +49,7 @@ import org.opengis.feature.FeatureAssoci
  *
  * @see DefaultAssociationRole
  */
-final class MultiValuedAssociation extends AbstractAssociation implements Cloneable {
+final class MultiValuedAssociation extends AbstractAssociation {
     /**
      * For cross-version compatibility.
      */
@@ -144,14 +144,11 @@ final class MultiValuedAssociation exten
 
     /**
      * Returns a copy of this association.
-     * The default implementation returns a <em>shallow</em> copy:
+     * This implementation returns a <em>shallow</em> copy:
      * the association {@linkplain #getValues() values} are <strong>not</strong> cloned.
-     * However subclasses may choose to do otherwise.
      *
      * @return A clone of this association.
      * @throws CloneNotSupportedException if this association can not be cloned.
-     *         The default implementation never throw this exception. However subclasses may throw it,
-     *         for example on attempt to clone the association values.
      */
     @Override
     @SuppressWarnings("unchecked")

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -50,7 +50,7 @@ import org.opengis.feature.AttributeType
  *
  * @see DefaultAttributeType
  */
-final class MultiValuedAttribute<V> extends AbstractAttribute<V> implements Cloneable {
+final class MultiValuedAttribute<V> extends AbstractAttribute<V> {
     /**
      * For cross-version compatibility.
      */
@@ -150,18 +150,15 @@ final class MultiValuedAttribute<V> exte
 
     /**
      * Returns a copy of this attribute.
-     * The default implementation returns a <em>shallow</em> copy:
+     * This implementation returns a <em>shallow</em> copy:
      * the attribute {@linkplain #getValues() values} are <strong>not</strong> cloned.
-     * However subclasses may choose to do otherwise.
      *
      * @return A clone of this attribute.
      * @throws CloneNotSupportedException if this attribute can not be cloned.
-     *         The default implementation never throw this exception. However subclasses may throw it,
-     *         for example on attempt to clone the attribute values.
      */
     @Override
     @SuppressWarnings("unchecked")
-    public MultiValuedAttribute<V> clone() throws CloneNotSupportedException {
+    public AbstractAttribute<V> clone() throws CloneNotSupportedException {
         final MultiValuedAttribute<V> clone = (MultiValuedAttribute<V>) super.clone();
         clone.values = (CheckedArrayList<V>) clone.values.clone();
         return clone;

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertySingleton.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertySingleton.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertySingleton.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/PropertySingleton.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.feature;
 
+import java.util.List;
 import java.util.AbstractList;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
@@ -186,6 +187,37 @@ final class PropertySingleton<V> extends
     }
 
     /**
+     * Same contract than {@link AbstractList}, just slightly more efficient for this particular class.
+     */
+    @Override
+    public int hashCode() {
+        final V element = property.getValue();
+        final int hashCode = (element != null) ? 31 + element.hashCode() : 1;
+        assert hashCode == super.hashCode() : hashCode;
+        return hashCode;
+    }
+
+    /**
+     * Same contract than {@link AbstractList}, just slightly more efficient for this particular class.
+     */
+    @Override
+    public boolean equals(final Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (other instanceof List<?>) {
+            final V element = property.getValue();
+            if (element == null) {
+                return ((List<?>) other).isEmpty();
+            } else {
+                final Iterator<?> it = ((List<?>) other).iterator();
+                return it.hasNext() && element.equals(it.next()) && !it.hasNext();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns an iterator over the unique element in this list.
      */
     @Override

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAssociation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAssociation.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAssociation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAssociation.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -41,7 +41,7 @@ import org.opengis.feature.FeatureAssoci
  *
  * @see DefaultAssociationRole
  */
-final class SingletonAssociation extends AbstractAssociation implements Cloneable {
+final class SingletonAssociation extends AbstractAssociation {
     /**
      * For cross-version compatibility.
      */
@@ -102,21 +102,6 @@ final class SingletonAssociation extends
     }
 
     /**
-     * Returns a copy of this association.
-     * The default implementation returns a <em>shallow</em> copy:
-     * the association {@linkplain #getValue() value} is <strong>not</strong> cloned.
-     * However subclasses may choose to do otherwise.
-     *
-     * @return A clone of this association.
-     * @throws CloneNotSupportedException if this association can not be cloned.
-     *         The default implementation never throw this exception. However subclasses may throw it.
-     */
-    @Override
-    public SingletonAssociation clone() throws CloneNotSupportedException {
-        return (SingletonAssociation) super.clone();
-    }
-
-    /**
      * Returns a hash code value for this association.
      *
      * @return A hash code value.

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -43,7 +43,7 @@ import org.opengis.feature.AttributeType
  *
  * @see DefaultAttributeType
  */
-final class SingletonAttribute<V> extends AbstractAttribute<V> implements Cloneable {
+final class SingletonAttribute<V> extends AbstractAttribute<V> {
     /**
      * For cross-version compatibility.
      */
@@ -100,23 +100,6 @@ final class SingletonAttribute<V> extend
     }
 
     /**
-     * Returns a copy of this attribute.
-     * The default implementation returns a <em>shallow</em> copy:
-     * the attribute {@linkplain #getValue() value} is <strong>not</strong> cloned.
-     * However subclasses may choose to do otherwise.
-     *
-     * @return A clone of this attribute.
-     * @throws CloneNotSupportedException if this attribute can not be cloned.
-     *         The default implementation never throw this exception. However subclasses may throw it,
-     *         for example on attempt to clone the attribute value.
-     */
-    @Override
-    @SuppressWarnings("unchecked")
-    public SingletonAttribute<V> clone() throws CloneNotSupportedException {
-        return (SingletonAttribute<V>) super.clone();
-    }
-
-    /**
      * Returns a hash code value for this attribute.
      *
      * @return A hash code value.

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/MultiValuedAttributeTest.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -140,7 +140,7 @@ public final strictfp class MultiValuedA
     @DependsOnMethod("testEquals")
     public void testClone() throws CloneNotSupportedException {
         final MultiValuedAttribute<Integer> a1 = population();
-        final MultiValuedAttribute<Integer> a2 = a1.clone();
+        final    AbstractAttribute<Integer> a2 = a1.clone();
         assertNotSame(a1, a2);
         SingletonAttributeTest.testEquals(a1, a2);
     }

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java?rev=1639258&r1=1639257&r2=1639258&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/SingletonAttributeTest.java [UTF-8] Thu Nov 13 07:16:06 2014
@@ -157,7 +157,7 @@ public final strictfp class SingletonAtt
     @DependsOnMethod("testEquals")
     public void testClone() throws CloneNotSupportedException {
         final SingletonAttribute<Integer> a1 = population();
-        final SingletonAttribute<Integer> a2 = a1.clone();
+        final  AbstractAttribute<Integer> a2 = a1.clone();
         assertNotSame(a1, a2);
         testEquals(a1, a2);
     }



Mime
View raw message