sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1591695 - in /sis/trunk: ./ core/sis-build-helper/src/main/java/org/apache/sis/internal/doclet/ core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/ core/sis-feature/ core/sis-feature/src/main/java/org/apache/sis/feature/ co...
Date Thu, 01 May 2014 15:59:33 GMT
Author: desruisseaux
Date: Thu May  1 15:59:33 2014
New Revision: 1591695

URL: http://svn.apache.org/r1591695
Log:
Merge from the JDK6 branch.

Added:
    sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/Include.java
      - copied unchanged from r1591692, sis/branches/JDK6/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/Include.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java
      - copied unchanged from r1591692, sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttribute.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/package-info.java
      - copied unchanged from r1591692, sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/package-info.java
    sis/trunk/core/sis-feature/src/test/java/org/
      - copied from r1591692, sis/branches/JDK6/core/sis-feature/src/test/java/org/
Modified:
    sis/trunk/   (props changed)
    sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/doclet/Doclet.java
    sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/InlineTaglet.java
    sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/Preformat.java
    sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java
    sis/trunk/core/sis-feature/pom.xml
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java   (contents, props changed)
    sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java
    sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java
    sis/trunk/ide-project/NetBeans/nbproject/project.properties
    sis/trunk/pom.xml
    sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/ShapeFile.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java
    sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataOutput.java
    sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ByteArrayChannel.java
    sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataOutputTest.java

Propchange: sis/trunk/
------------------------------------------------------------------------------
  Merged /sis/branches/JDK8:r1590470-1591678
  Merged /sis/branches/JDK7:r1590477-1591680
  Merged /sis/branches/JDK6:r1590484-1591692

Modified: sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/doclet/Doclet.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/doclet/Doclet.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/doclet/Doclet.java [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/doclet/Doclet.java [UTF-8] Thu May  1 15:59:33 2014
@@ -33,7 +33,7 @@ import com.sun.tools.doclets.formats.htm
  * A doclet which delegates the work to the standard doclet, except for the {@code -stylesheet} option.
  * Rather than overwriting the standard stylesheet with the given one, this keep both the standard and
  * the specified stylesheets as separated files. The standard stylesheet will be renamed {@code standard.css}.
- * The stylesheet provided by the user shall contains an import stantement for the standard stylesheet.
+ * The stylesheet provided by the user shall contains an import statement for the standard stylesheet.
  *
  * <p>This class presumes that all CSS files are encoded in UTF-8.</p>
  *

Modified: sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/InlineTaglet.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/InlineTaglet.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/InlineTaglet.java [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/InlineTaglet.java [UTF-8] Thu May  1 15:59:33 2014
@@ -17,7 +17,11 @@
 package org.apache.sis.internal.taglet;
 
 import com.sun.javadoc.Tag;
+import com.sun.javadoc.RootDoc;
+import com.sun.javadoc.SourcePosition;
 import com.sun.tools.doclets.Taglet;
+import com.sun.tools.doclets.internal.toolkit.Configuration;
+import com.sun.tools.doclets.formats.html.ConfigurationImpl;
 
 
 /**
@@ -30,6 +34,13 @@ import com.sun.tools.doclets.Taglet;
  */
 abstract class InlineTaglet implements Taglet {
     /**
+     * Returns the doclet configuration.
+     */
+    private static synchronized Configuration getConfiguration() {
+        return ConfigurationImpl.getInstance();
+    }
+
+    /**
      * Constructs a default inline taglet.
      */
     InlineTaglet() {
@@ -122,4 +133,28 @@ abstract class InlineTaglet implements T
         }
         return buffer.toString();
     }
+
+    /**
+     * Prints a warning message.
+     */
+    static void printWarning(final SourcePosition position, final String message) {
+        final RootDoc root = getConfiguration().root;
+        if (root != null) {
+            root.printWarning(position, message);
+        } else {
+            System.err.println(message);
+        }
+    }
+
+    /**
+     * Prints an error message.
+     */
+    static void printError(final SourcePosition position, final String message) {
+        final RootDoc root = getConfiguration().root;
+        if (root != null) {
+            root.printError(position, message);
+        } else {
+            System.err.println(message);
+        }
+    }
 }

Modified: sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/Preformat.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/Preformat.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/Preformat.java [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/Preformat.java [UTF-8] Thu May  1 15:59:33 2014
@@ -19,7 +19,6 @@ package org.apache.sis.internal.taglet;
 import java.util.*;
 import com.sun.javadoc.Tag;
 import com.sun.tools.doclets.Taglet;
-import com.sun.tools.doclets.formats.html.ConfigurationImpl;
 
 
 /**
@@ -118,7 +117,7 @@ public final class Preformat extends Inl
         try {
             style = Style.valueOf(format);
         } catch (IllegalArgumentException e) {
-            ConfigurationImpl.getInstance().root.printWarning(tag.position(), "Unknown format: " + format);
+            printWarning(tag.position(), "Unknown format: " + format);
             style = Style.text;
         }
         /*

Modified: sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/taglet/SourceRepositoryURL.java [UTF-8] Thu May  1 15:59:33 2014
@@ -19,7 +19,6 @@ package org.apache.sis.internal.taglet;
 import java.util.Map;
 import com.sun.javadoc.Tag;
 import com.sun.tools.doclets.Taglet;
-import com.sun.tools.doclets.formats.html.ConfigurationImpl;
 
 
 /**
@@ -87,7 +86,7 @@ public final class SourceRepositoryURL e
                 url.append("/core/sis-referencing/src/test/resources/org/apache/sis/referencing");
             }
             else {
-                ConfigurationImpl.getInstance().root.printWarning(tag.position(), "Unknown keyword: " + keyword);
+                printWarning(tag.position(), "Unknown keyword: " + keyword);
             }
         }
         return url.toString();

Modified: sis/trunk/core/sis-feature/pom.xml
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/pom.xml?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/pom.xml (original)
+++ sis/trunk/core/sis-feature/pom.xml Thu May  1 15:59:33 2014
@@ -98,6 +98,19 @@ Representations of geographic features.
       <groupId>com.esri.geometry</groupId>
       <artifactId>esri-geometry-api</artifactId>
     </dependency>
+
+    <!-- Test dependencies -->
+    <dependency>
+      <groupId>org.opengis</groupId>
+      <artifactId>geoapi-conformance</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sis.core</groupId>
+      <artifactId>sis-utility</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractIdentifiedType.java [UTF-8] Thu May  1 15:59:33 2014
@@ -40,8 +40,8 @@ import org.apache.sis.internal.jdk7.Obje
  * will be replaced by references to the {@code IdentifiedType} interface.</div>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @since   0.4
- * @version 0.4
+ * @since   0.5
+ * @version 0.5
  * @module
  */
 public class AbstractIdentifiedType implements Serializable {
@@ -240,6 +240,18 @@ public class AbstractIdentifiedType impl
         return description;
     }
 
+    /*
+     * ISO 19109 properties omitted for now:
+     *
+     *   - constrainedBy : CharacterString
+     *
+     * Rational: a CharacterString is hardly programmatically usable. A Range would be better but too specific.
+     * We could follow the GeoAPI path and define a "restrictions : Filter" property. That would be more generic,
+     * but we are probably better to wait for Filter to be implemented in SIS.
+     *
+     * Reference: https://issues.apache.org/jira/browse/SIS-175
+     */
+
     /**
      * Returns a hash code value for this type.
      *

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultAttributeType.java [UTF-8] Thu May  1 15:59:33 2014
@@ -17,11 +17,12 @@
 package org.apache.sis.feature;
 
 import java.util.Map;
-import org.apache.sis.measure.Range;
+import org.opengis.util.GenericName;
+import org.apache.sis.util.Debug;
 import org.apache.sis.util.Classes;
-import org.apache.sis.util.Numbers;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.measure.NumberRange;
 
 import static org.apache.sis.util.ArgumentChecks.*;
 
@@ -44,11 +45,12 @@ import org.apache.sis.internal.jdk7.Obje
  * When such interface will be available, most references to {@code DefaultAttributeType} in the API
  * will be replaced by references to the {@code AttributeType} interface.</div>
  *
- * @param <T> The value type.
+ * @param <T> The type of attribute values.
  *
+ * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @since   0.4
- * @version 0.4
+ * @since   0.5
+ * @version 0.5
  * @module
  */
 public class DefaultAttributeType<T> extends AbstractIdentifiedType {
@@ -65,21 +67,6 @@ public class DefaultAttributeType<T> ext
     private final Class<T> valueClass;
 
     /**
-     * The minimum and maximum attribute value with their unit of measurement, or {@code null} if none.
-     * If this field is non-null, then <code>valueDomain.{@linkplain Range#getElementType() getElementType()}</code>
-     * shall be one of the following:
-     *
-     * <ul>
-     *   <li>If {@link #valueClass} is not an array, then the range element type shall be the same class.</li>
-     *   <li>If {@code valueClass} is an array, then the range element type shall be the wrapper of
-     *       <code>valueClass.{@linkplain Class#getComponentType() getComponentType()}</code>.</li>
-     * </ul>
-     *
-     * @see #getValueDomain()
-     */
-    private final Range<?> valueDomain;
-
-    /**
      * The default value for the attribute, or {@code null} if none.
      *
      * @see #getDefaultValue()
@@ -88,11 +75,8 @@ public class DefaultAttributeType<T> ext
 
     /**
      * The minimum/maximum number of occurrences of the property within its containing entity.
-     *
-     * @see #getMinimumOccurs()
-     * @see #getMaximumOccurs()
      */
-    private final int minimumOccurs, maximumOccurs;
+    private final NumberRange<Integer> cardinality;
 
     /**
      * Constructs an attribute type from the given properties. The properties map is given unchanged to
@@ -128,61 +112,30 @@ public class DefaultAttributeType<T> ext
      *   </tr>
      * </table>
      *
-     * {@section Domain of attribute values}
-     * If {@code valueDomain} argument is non-null, then it shall comply to the following conditions:
-     *
-     * <ul>
-     *   <li>The range shall be non-{@linkplain Range#isEmpty() empty}.</li>
-     *   <li><code>valueDomain.{@linkplain Range#getElementType() getElementType()}</code> shall be equals
-     *       to one of the following:
-     *     <ul>
-     *       <li>to {@code valueClass} if the later is not an array,</li>
-     *       <li>or to <code>{@linkplain Numbers#primitiveToWrapper(Class)
-     *           primitiveToWrapper}(valueClass.{@linkplain Class#getComponentType() getComponentType()})</code>
-     *           if {@code valueClass} is an array.</li>
-     *     </ul>
-     *   </li>
-     * </ul>
-     *
      * @param properties    The name and other properties to be given to this attribute type.
      * @param valueClass    The type of attribute values.
-     * @param valueDomain   The minimum value, maximum value and unit of measurement, or {@code null} if none.
      * @param defaultValue  The default value for the attribute, or {@code null} if none.
-     * @param minimumOccurs The minimum number of occurrences of the property within its containing entity.
-     * @param maximumOccurs The maximum number of occurrences of the property within its containing entity,
-     *                      or {@link Integer#MAX_VALUE} if none.
+     * @param cardinality   The minimum and maximum number of occurrences of the property within its containing entity,
+     *                      or {@code null} if there is no restriction.
      */
-    public DefaultAttributeType(final Map<String,?> properties, final Class<T> valueClass, final Range<?> valueDomain,
-            final T defaultValue, final int minimumOccurs, final int maximumOccurs)
+    public DefaultAttributeType(final Map<String,?> properties, final Class<T> valueClass, final T defaultValue,
+            NumberRange<Integer> cardinality)
     {
         super(properties);
         ensureNonNull("valueClass",   valueClass);
         ensureCanCast("defaultValue", valueClass, defaultValue);
-        if (minimumOccurs < 0 || minimumOccurs > maximumOccurs) {
-            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalRange_2, minimumOccurs, maximumOccurs));
-        }
-        if (valueDomain != null) {
-            Class<?> componentType = valueClass.getComponentType();
-            if (componentType != null) {
-                componentType = Numbers.primitiveToWrapper(componentType);
-            } else {
-                componentType = valueClass;
-            }
-            final Class<?> elementType = valueDomain.getElementType();
-            if (elementType != componentType) {
-                throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentClass_2,
-                        "valueDomain", "Range<" + Classes.getShortName(elementType) + '>'));
-            }
-            if (valueDomain.isEmpty()) {
-                throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalRange_2,
-                        valueDomain.getMinValue(), valueDomain.getMaxValue()));
+        if (cardinality == null) {
+            cardinality = NumberRange.createLeftBounded(0, true);
+        } else {
+            final Integer minValue = cardinality.getMinValue();
+            if (minValue == null || minValue < 0) {
+                throw new IllegalArgumentException(Errors.format(
+                        Errors.Keys.IllegalArgumentValue_2, "cardinality", cardinality));
             }
         }
-        this.valueClass    = valueClass;
-        this.valueDomain   = valueDomain;
-        this.defaultValue  = Numerics.cached(defaultValue);
-        this.minimumOccurs = minimumOccurs;
-        this.maximumOccurs = maximumOccurs;
+        this.valueClass   = valueClass;
+        this.defaultValue = Numerics.cached(defaultValue);
+        this.cardinality  = cardinality;
     }
 
     /**
@@ -195,23 +148,6 @@ public class DefaultAttributeType<T> ext
     }
 
     /**
-     * Returns the domain of values with their unit of measurement (if any), or {@code null} if none.
-     *
-     * <div class="note"><b>API note:</b> If this method returns a non-null value, then its type is either exactly
-     * {@code Range<T>}, or {@code Range<E>} where {@code <E>} is the {@linkplain Class#getComponentType() component
-     * type} of {@code <T>} (using wrapper classes for primitive types).</div>
-     *
-     * @return The domain of values, or {@code null}.
-     */
-    /* Implementation note: this method is final because the constructor performs various checks on range validity,
-     * and we can not express those rules in the method signature. If the user was allowed to override this method,
-     * there is no way we can ensure that the range element type still valid.
-     */
-    public final Range<?> getValueDomain() {
-        return valueDomain;
-    }
-
-    /**
      * Returns the default value for the attribute.
      * This value is used when an attribute is created and no value for it is specified.
      *
@@ -221,25 +157,27 @@ public class DefaultAttributeType<T> ext
         return defaultValue;
     }
 
-    /**
-     * Returns the minimum number of occurrences of the property within its containing entity.
-     * This value is always an integer greater than or equal to zero.
+    /*
+     * ISO 19109 properties omitted for now:
+     *
+     *   - valueDomain : CharacterString
      *
-     * @return The minimum number of occurrences of the property within its containing entity.
+     * Rational: a CharacterString is hardly programmatically usable. A Range would be better but too specific.
+     * We could follow the GeoAPI path and define a "restrictions : Filter" property. That would be more generic,
+     * but we are probably better to wait for Filter to be implemented in SIS.
+     *
+     * Reference: https://issues.apache.org/jira/browse/SIS-175
      */
-    public int getMinimumOccurs() {
-        return minimumOccurs;
-    }
 
     /**
-     * The maximum number of occurrences of the property within its containing entity.
-     * A value of {@link Integer#MAX_VALUE} means that the maximum number of occurrences is unbounded.
+     * Returns the minimum and maximum number of occurrences of the property within its containing entity.
+     * The bounds are always integer values greater than or equal to zero. The upper bounds may be {@code null}
+     * if there is no maximum number of occurrences.
      *
-     * @return The maximum number of occurrences of the property within its containing entity,
-     *         or {@link Integer#MAX_VALUE} if none.
+     * @return The minimum and maximum number of occurrences of the property within its containing entity.
      */
-    public int getMaximumOccurs() {
-        return maximumOccurs;
+    public NumberRange<Integer> getCardinality() {
+        return cardinality;
     }
 
     /**
@@ -249,8 +187,7 @@ public class DefaultAttributeType<T> ext
      */
     @Override
     public int hashCode() {
-        return super.hashCode() + valueClass.hashCode() + Objects.hashCode(valueDomain) +
-               31*(Objects.hashCode(defaultValue) + 31*(minimumOccurs + 31*maximumOccurs));
+        return super.hashCode() + valueClass.hashCode() + Objects.hashCode(defaultValue) + 31*cardinality.hashCode();
     }
 
     /**
@@ -265,12 +202,38 @@ public class DefaultAttributeType<T> ext
         }
         if (super.equals(obj)) {
             final DefaultAttributeType<?> that = (DefaultAttributeType<?>) obj;
-            return valueClass    == that.valueClass    &&
-                   minimumOccurs == that.minimumOccurs &&
-                   maximumOccurs == that.maximumOccurs &&
-                   Objects.equals(valueDomain,  that.valueDomain) &&
-                   Objects.equals(defaultValue, that.defaultValue);
+            return valueClass == that.valueClass &&
+                   Objects.equals(defaultValue, that.defaultValue) &&
+                   cardinality.equals(that.cardinality);
         }
         return false;
     }
+
+    /**
+     * Returns a string representation of this attribute type.
+     * The returned string is for debugging purpose and may change in any future SIS version.
+     *
+     * @return A string representation of this attribute type for debugging purpose.
+     */
+    @Debug
+    @Override
+    public String toString() {
+        return toString("AttributeType").toString();
+    }
+
+    /**
+     * Implementation of {@link #toString()} to be shared by {@link DefaultAttribute#toString()}.
+     */
+    final StringBuilder toString(final String typeName) {
+        final StringBuilder buffer = new StringBuilder(40).append(typeName).append('[');
+        final GenericName name = super.getName();
+        if (name != null) {
+            buffer.append('“');
+        }
+        buffer.append(name);
+        if (name != null) {
+            buffer.append("” : ");
+        }
+        return buffer.append(Classes.getShortName(valueClass)).append(']');
+    }
 }

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java [UTF-8] Thu May  1 15:59:33 2014
@@ -18,47 +18,157 @@ package org.apache.sis.feature;
 
 import java.util.Map;
 import java.util.HashMap;
-import com.esri.core.geometry.Geometry;
+import java.util.ConcurrentModificationException;
+import java.io.Serializable;
+import org.apache.sis.util.Debug;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.collection.Containers;
 
 // Related to JDK7
 import org.apache.sis.internal.jdk7.JDK7;
+import org.apache.sis.internal.jdk7.Objects;
 
 
 /**
- * Simple Feature class.
+ * An instance of {@linkplain DefaultFeatureType feature type} containing values for a real-world phenomena.
  *
  * @author  Travis L. Pinney
- * @since   0.4
- * @version 0.4
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
  * @module
  */
-public class DefaultFeature {
+public class DefaultFeature implements Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 6594295132544357870L;
+
+    /**
+     * Information about the feature (name, characteristics, <i>etc.</i>).
+     */
+    private final DefaultFeatureType type;
+
+    /**
+     * The properties (attributes, operations, feature associations) of this feature.
+     */
+    private final Map<String, DefaultAttribute<?>> properties;
+
+    /**
+     * Creates a new features.
+     *
+     * @param type Information about the feature (name, characteristics, <i>etc.</i>).
+     */
+    public DefaultFeature(final DefaultFeatureType type) {
+        ArgumentChecks.ensureNonNull("type", type);
+        this.type = type;
+        properties = new HashMap<String, DefaultAttribute<?>>(
+                Math.min(16, Containers.hashMapCapacity(type.getCharacteristics().size())));
+    }
+
+    /**
+     * Returns information about the feature (name, characteristics, <i>etc.</i>).
+     *
+     * @return Information about the feature.
+     */
+    public DefaultFeatureType getType() {
+        return type;
+    }
 
-    private Map<String, String> record;
-    private Geometry geom;
+    /**
+     * Returns the value of the attribute of the given name.
+     *
+     * @param  name The attribute name.
+     * @return The value for the given attribute, or {@code null} if none.
+     */
+    public Object getAttributeValue(final String name) {
+        final DefaultAttribute<?> attribute = properties.get(name);
+        if (attribute == null) {
+            final DefaultAttributeType<?> at = type.getProperty(name);
+            if (at == null) {
+                throw new IllegalArgumentException(propertyNotFound(name));
+            }
+            return at.getDefaultValue();
+        }
+        return attribute.getValue();
+    }
 
-    public Map<String, String> getRecord() {
-        return record;
+    /**
+     * Sets the value of the attribute of the given name.
+     *
+     * @param name  The attribute name.
+     * @param value The new value for the given attribute (may be {@code null}).
+     */
+    @SuppressWarnings("unchecked")
+    public void setAttributeValue(final String name, final Object value) {
+        DefaultAttribute<?> attribute = properties.get(name);
+        if (attribute == null) {
+            final DefaultAttributeType<?> at = type.getProperty(name);
+            if (at == null) {
+                throw new IllegalArgumentException(propertyNotFound(name));
+            }
+            if (Objects.equals(value, at.getDefaultValue())) {
+                return; // Avoid creating the attribute if not necessary.
+            }
+            attribute = new DefaultAttribute(at);
+            if (properties.put(name, attribute) != null) {
+                throw new ConcurrentModificationException();
+            }
+        }
+        ArgumentChecks.ensureCanCast(name, attribute.getType().getValueClass(), value);
+        ((DefaultAttribute) attribute).setValue(value);
     }
 
-    public void setRecord(HashMap<String, String> record) {
-        this.record = record;
+    /**
+     * Returns the error message for a property not found.
+     */
+    private String propertyNotFound(final String name) {
+        return Errors.format(Errors.Keys.PropertyNotFound_2, type.getName(), name);
     }
 
-    public Geometry getGeom() {
-        return geom;
+    /**
+     * Returns a hash code value for this feature.
+     *
+     * @return A hash code value.
+     */
+    @Override
+    public int hashCode() {
+        return type.hashCode() + 37 * properties.hashCode();
     }
 
-    public void setGeom(Geometry geom) {
-        this.geom = geom;
+    /**
+     * Compares this feature with the given object for equality.
+     *
+     * @return {@code true} if both objects are equal.
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (super.equals(obj)) {
+            final DefaultFeature that = (DefaultFeature) obj;
+            return type.equals(that.type) &&
+                   properties.equals(that.properties);
+        }
+        return false;
     }
 
+    /**
+     * Returns a string representation of this feature.
+     * The returned string is for debugging purpose and may change in any future SIS version.
+     *
+     * @return A string representation of this feature for debugging purpose.
+     */
+    @Debug
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         final String lineSeparator = JDK7.lineSeparator();
-        for (String s : this.record.keySet()) {
-            sb.append(s).append(": ").append(record.get(s)).append(lineSeparator);
+        for (final DefaultAttribute<?> attribute : properties.values()) {
+            sb.append(attribute.getType().getName()).append(": ").append(attribute.getValue()).append(lineSeparator);
         }
         return sb.toString();
     }

Propchange: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java
------------------------------------------------------------------------------
  Reverse-merged /sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java:r1515800-1515995
  Merged /sis/branches/JDK7/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java:r1515996-1591680
  Merged /sis/branches/JDK6/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java:r1516009-1591692
  Merged /sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeature.java:r1584960-1591678

Modified: sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] (original)
+++ sis/trunk/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java [UTF-8] Thu May  1 15:59:33 2014
@@ -19,10 +19,16 @@ package org.apache.sis.feature;
 import java.util.Map;
 import java.util.Set;
 import java.util.List;
+import java.util.HashMap;
+import java.util.Arrays;
 import java.util.Collections;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.lang.reflect.Field;
 import org.opengis.util.GenericName;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.collection.Containers;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.util.UnmodifiableArrayList;
 
@@ -40,13 +46,19 @@ import org.apache.sis.internal.util.Unmo
  * When such interface will be available, most references to {@code DefaultFeatureType} in the API
  * will be replaced by references to the {@code FeatureType} interface.</div>
  *
+ * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @since   0.4
- * @version 0.4
+ * @since   0.5
+ * @version 0.5
  * @module
  */
 public class DefaultFeatureType extends AbstractIdentifiedType {
     /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4357370600723922312L;
+
+    /**
      * If {@code true}, the feature type acts as an abstract super-type.
      *
      * @see #isAbstract()
@@ -65,6 +77,14 @@ public class DefaultFeatureType extends 
     private final List<DefaultAttributeType<?>> characteristics;
 
     /**
+     * A lookup table for fetching properties by name.
+     * This map shall not be modified after construction.
+     *
+     * @see #getProperty(String)
+     */
+    private final transient Map<String, DefaultAttributeType<?>> byName;
+
+    /**
      * Constructs a feature type from the given properties. The properties map is given unchanged to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
      * The following table is a reminder of main (not all) properties:
@@ -105,33 +125,64 @@ public class DefaultFeatureType extends 
      *        association role that carries characteristics of a feature type.
      */
     public DefaultFeatureType(final Map<String,?> properties, final boolean isAbstract,
-            final DefaultFeatureType[] superTypes, final DefaultAttributeType... characteristics)
+            final DefaultFeatureType[] superTypes, final DefaultAttributeType<?>... characteristics)
     {
         super(properties);
         ArgumentChecks.ensureNonNull("characteristics", characteristics);
         this.isAbstract = isAbstract;
         this.superTypes = (superTypes == null) ? Collections.<DefaultFeatureType>emptySet() :
                           CollectionsExt.<DefaultFeatureType>immutableSet(true, superTypes);
-        final DefaultAttributeType<?>[] copy = new DefaultAttributeType<?>[characteristics.length];
-        for (int i=0; i<characteristics.length; i++) {
-            copy[i] = characteristics[i];
-            ArgumentChecks.ensureNonNullElement("characteristics", i, copy);
-            /*
-             * Ensure there is no conflict in property names.
-             */
-            final GenericName name = copy[i].getName();
+        this.characteristics = (List) UnmodifiableArrayList.wrap(Arrays.copyOf(
+                characteristics, characteristics.length, DefaultAttributeType[].class));
+        byName = byName(this.characteristics);
+    }
+
+    /**
+     * Creates a (<var>name</var>, <var>property</var>) map from the given list.
+     * This is used only for performance purpose when searching for attributes.
+     *
+     * <p>As a side effect, this method checks for missing or duplicated names.</p>
+     *
+     * @param  characteristics The list to convert to a (<var>name</var>, <var>property</var>) map.
+     * @return The map of properties.
+     * @throws IllegalArgumentException if two properties have the same name.
+     */
+    private static Map<String, DefaultAttributeType<?>> byName(final List<DefaultAttributeType<?>> characteristics) {
+        final int length = characteristics.size();
+        final Map<String, DefaultAttributeType<?>> byName =
+                new HashMap<String, DefaultAttributeType<?>>(Containers.hashMapCapacity(length));
+        for (int i=0; i<length; i++) {
+            final DefaultAttributeType<?> c = characteristics.get(i);
+            ArgumentChecks.ensureNonNullElement("characteristics", i, c);
+            final GenericName name = c.getName();
             if (name == null) {
                 throw new IllegalArgumentException(Errors.format(
                         Errors.Keys.MissingValueForProperty_1, "characteristics[" + i + "].name"));
             }
-            for (int j=i; --j >= 0;) {
-                if (name.equals(copy[j].getName())) {
-                    throw new IllegalArgumentException(Errors.format(
-                            Errors.Keys.DuplicatedIdentifier_1, name));
-                }
+            final String s = name.toString();
+            if (byName.put(s, c) != null) {
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.DuplicatedIdentifier_1, s));
             }
         }
-        this.characteristics = UnmodifiableArrayList.wrap(copy);
+        return byName;
+    }
+
+    /**
+     * Invoked on deserialization for restoring the {@link #byName} map.
+     *
+     * @param  in The input stream from which to deserialize a feature 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 Field field = DefaultFeatureType.class.getDeclaredField("byName");
+            field.setAccessible(true);
+            field.set(this, byName(characteristics));
+        } catch (Exception e) { // ReflectiveOperationException on the JDK7 branch.
+            throw new AssertionError(e);
+        }
     }
 
     /**
@@ -165,4 +216,43 @@ public class DefaultFeatureType extends 
     public List<DefaultAttributeType<?>> getCharacteristics() {
         return characteristics;
     }
+
+    /**
+     * Returns the attribute, operation or association role for the given name.
+     *
+     * @param  name The name of the property to search.
+     * @return The property for the given name, or {@code null} if none.
+     */
+    final DefaultAttributeType<?> getProperty(final String name) {
+        return byName.get(name);
+    }
+
+    /**
+     * Returns a hash code value for this feature type.
+     *
+     * @return {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return super.hashCode() + superTypes.hashCode() + 37*superTypes.hashCode();
+    }
+
+    /**
+     * Compares this feature type with the given object for equality.
+     *
+     * @return {@inheritDoc}
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (super.equals(obj)) {
+            final DefaultFeatureType that = (DefaultFeatureType) obj;
+            return isAbstract == that.isAbstract &&
+                   superTypes.equals(that.superTypes) &&
+                   characteristics.equals(that.characteristics);
+        }
+        return false;
+    }
 }

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/MeasurementRange.java [UTF-8] Thu May  1 15:59:33 2014
@@ -51,7 +51,7 @@ import org.apache.sis.internal.jdk7.Obje
  *
  * @author  Martin Desruisseaux (IRD)
  * @since   0.3 (derived from geotk-2.4)
- * @version 0.3
+ * @version 0.5
  * @module
  *
  * @see RangeFormat
@@ -72,6 +72,7 @@ public class MeasurementRange<E extends 
 
     /**
      * Constructs a range of {@code float} values.
+     * This method may return a shared instance, at implementation choice.
      *
      * @param  minValue       The minimal value, or {@link Float#NEGATIVE_INFINITY} if none.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
@@ -83,13 +84,14 @@ public class MeasurementRange<E extends 
     public static MeasurementRange<Float> create(float minValue, boolean isMinIncluded,
                                                  float maxValue, boolean isMaxIncluded, Unit<?> unit)
     {
-        return new MeasurementRange<Float>(Float.class,
+        return unique(new MeasurementRange<Float>(Float.class,
                 valueOf("minValue", minValue, Float.NEGATIVE_INFINITY), isMinIncluded,
-                valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY), isMaxIncluded, unit);
+                valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY), isMaxIncluded, unit));
     }
 
     /**
      * Constructs a range of {@code double} values.
+     * This method may return a shared instance, at implementation choice.
      *
      * @param  minValue       The minimal value, or {@link Double#NEGATIVE_INFINITY} if none.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
@@ -101,9 +103,9 @@ public class MeasurementRange<E extends 
     public static MeasurementRange<Double> create(double minValue, boolean isMinIncluded,
                                                   double maxValue, boolean isMaxIncluded, Unit<?> unit)
     {
-        return new MeasurementRange<Double>(Double.class,
+        return unique(new MeasurementRange<Double>(Double.class,
                 valueOf("minValue", minValue, Double.NEGATIVE_INFINITY), isMinIncluded,
-                valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY), isMaxIncluded, unit);
+                valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY), isMaxIncluded, unit));
     }
 
     /**
@@ -111,6 +113,8 @@ public class MeasurementRange<E extends 
      * This method performs the same work than {@link NumberRange#createBestFit
      * NumberRange.createBestFit(…)} with an additional {@code unit} argument.
      *
+     * <p>This method may return a shared instance, at implementation choice.</p>
+     *
      * @param  minValue       The minimal value, or {@code null} if none.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
      * @param  maxValue       The maximal value, or {@code null} if none.
@@ -126,9 +130,12 @@ public class MeasurementRange<E extends 
     {
         final Class<? extends Number> type = Numbers.widestClass(
                 Numbers.narrowestClass(minValue), Numbers.narrowestClass(maxValue));
-        return (type == null) ? null :
-            new MeasurementRange(type, Numbers.cast(minValue, type), isMinIncluded,
-                                       Numbers.cast(maxValue, type), isMaxIncluded, unit);
+        if (type == null) {
+            return null;
+        }
+        return (MeasurementRange) unique(new MeasurementRange(type,
+                Numbers.cast(minValue, type), isMinIncluded,
+                Numbers.cast(maxValue, type), isMaxIncluded, unit));
     }
 
     /**

Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java [UTF-8] Thu May  1 15:59:33 2014
@@ -19,6 +19,7 @@ package org.apache.sis.measure;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.util.collection.WeakHashSet;
 
 
 /**
@@ -66,12 +67,22 @@ import org.apache.sis.internal.util.Nume
  * Other subclasses may or may not be immutable, at implementation choice. But implementors are encouraged
  * to make sure that all subclasses remain immutable for more predictable behavior.
  *
+ * {@section Shared instances}
+ * <i><b>Note:</b> following is implementation details provided for information purpose.
+ * The caching policy may change in any SIS version.</i>
+ *
+ * <p>All {@code create} static methods may return a shared instance. Those methods are preferred
+ * to the constructors when the range is expected to have a long lifetime, typically as instance
+ * given to {@linkplain org.apache.sis.parameter.DefaultParameterDescriptor parameter descriptor}
+ * or {@linkplain org.apache.sis.feature.DefaultAttributeType attribute type} constructor. Other
+ * methods do not check for shared instances, since the created object is often temporary.</p>
+ *
  * @param <E> The type of range elements as a subclass of {@link Number}.
  *
  * @author  Martin Desruisseaux (IRD)
  * @author  Jody Garnett (for parameterized type inspiration)
  * @since   0.3 (derived from geotk-2.4)
- * @version 0.3
+ * @version 0.5
  * @module
  *
  * @see RangeFormat
@@ -85,7 +96,21 @@ public class NumberRange<E extends Numbe
     private static final long serialVersionUID = -3198281191274903617L;
 
     /**
+     * The pool of ranges created by the {@code create(…)} methods.
+     */
+    @SuppressWarnings("unchecked")
+    private static final WeakHashSet<NumberRange<?>> POOL = new WeakHashSet<NumberRange<?>>((Class) NumberRange.class);
+
+    /**
+     * Returns a unique instance of the given range.
+     */
+    static <E extends Number & Comparable<? super E>, T extends NumberRange<E>> T unique(final T range) {
+        return POOL.unique(range);
+    }
+
+    /**
      * Constructs a range of {@code byte} values.
+     * This method may return a shared instance, at implementation choice.
      *
      * @param  minValue       The minimal value.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
@@ -96,13 +121,14 @@ public class NumberRange<E extends Numbe
     public static NumberRange<Byte> create(final byte minValue, final boolean isMinIncluded,
                                            final byte maxValue, final boolean isMaxIncluded)
     {
-        return new NumberRange<Byte>(Byte.class,
+        return unique(new NumberRange<Byte>(Byte.class,
                 Byte.valueOf(minValue), isMinIncluded,
-                Byte.valueOf(maxValue), isMaxIncluded);
+                Byte.valueOf(maxValue), isMaxIncluded));
     }
 
     /**
      * Constructs a range of {@code short} values.
+     * This method may return a shared instance, at implementation choice.
      *
      * @param  minValue       The minimal value.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
@@ -113,30 +139,34 @@ public class NumberRange<E extends Numbe
     public static NumberRange<Short> create(final short minValue, final boolean isMinIncluded,
                                             final short maxValue, final boolean isMaxIncluded)
     {
-        return new NumberRange<Short>(Short.class,
+        return unique(new NumberRange<Short>(Short.class,
                 Short.valueOf(minValue), isMinIncluded,
-                Short.valueOf(maxValue), isMaxIncluded);
+                Short.valueOf(maxValue), isMaxIncluded));
     }
 
     /**
      * Constructs a range of {@code int} values.
+     * This method may return a shared instance, at implementation choice.
      *
      * @param  minValue       The minimal value.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
      * @param  maxValue       The maximal value.
      * @param  isMaxIncluded  {@code true} if the maximal value is inclusive, or {@code false} if exclusive.
      * @return The new range of numeric values for the given endpoints.
+     *
+     * @see #createLeftBounded(int, boolean)
      */
     public static NumberRange<Integer> create(final int minValue, final boolean isMinIncluded,
                                               final int maxValue, final boolean isMaxIncluded)
     {
-        return new NumberRange<Integer>(Integer.class,
+        return unique(new NumberRange<Integer>(Integer.class,
                 Integer.valueOf(minValue), isMinIncluded,
-                Integer.valueOf(maxValue), isMaxIncluded);
+                Integer.valueOf(maxValue), isMaxIncluded));
     }
 
     /**
      * Constructs a range of {@code long} values.
+     * This method may return a shared instance, at implementation choice.
      *
      * @param  minValue       The minimal value.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
@@ -147,14 +177,15 @@ public class NumberRange<E extends Numbe
     public static NumberRange<Long> create(final long minValue, final boolean isMinIncluded,
                                            final long maxValue, final boolean isMaxIncluded)
     {
-        return new NumberRange<Long>(Long.class,
+        return unique(new NumberRange<Long>(Long.class,
                 Long.valueOf(minValue), isMinIncluded,
-                Long.valueOf(maxValue), isMaxIncluded);
+                Long.valueOf(maxValue), isMaxIncluded));
     }
 
     /**
      * Constructs a range of {@code float} values.
      * The values can not be {@link Float#NaN}.
+     * This method may return a shared instance, at implementation choice.
      *
      * @param  minValue       The minimal value, or {@link Float#NEGATIVE_INFINITY} if none.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
@@ -165,9 +196,9 @@ public class NumberRange<E extends Numbe
     public static NumberRange<Float> create(final float minValue, final boolean isMinIncluded,
                                             final float maxValue, final boolean isMaxIncluded)
     {
-        return new NumberRange<Float>(Float.class,
+        return unique(new NumberRange<Float>(Float.class,
                 valueOf("minValue", minValue, Float.NEGATIVE_INFINITY), isMinIncluded,
-                valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY), isMaxIncluded);
+                valueOf("maxValue", maxValue, Float.POSITIVE_INFINITY), isMaxIncluded));
     }
 
     /**
@@ -184,6 +215,7 @@ public class NumberRange<E extends Numbe
     /**
      * Constructs a range of {@code double} values.
      * The values can not be {@link Double#NaN}.
+     * This method may return a shared instance, at implementation choice.
      *
      * @param  minValue       The minimal value, or {@link Double#NEGATIVE_INFINITY} if none.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
@@ -194,9 +226,9 @@ public class NumberRange<E extends Numbe
     public static NumberRange<Double> create(final double minValue, final boolean isMinIncluded,
                                              final double maxValue, final boolean isMaxIncluded)
     {
-        return new NumberRange<Double>(Double.class,
+        return unique(new NumberRange<Double>(Double.class,
                 valueOf("minValue", minValue, Double.NEGATIVE_INFINITY), isMinIncluded,
-                valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY), isMaxIncluded);
+                valueOf("maxValue", maxValue, Double.POSITIVE_INFINITY), isMaxIncluded));
     }
 
     /**
@@ -211,6 +243,27 @@ public class NumberRange<E extends Numbe
     }
 
     /**
+     * Constructs a range of {@code int} values without upper bound.
+     * This method may return a shared instance, at implementation choice.
+     *
+     * <div class="note"><b>Note:</b> for creating left-bounded ranges of floating point values,
+     * use one of the {@code create(…)} methods with a {@code POSITIVE_INFINITY} constant.
+     * We do not provide variants for other integer types because this method is typically invoked for
+     * defining the {@linkplain org.apache.sis.feature.DefaultFeatureType cardinality of an attribute}.</div>
+     *
+     * @param  minValue       The minimal value.
+     * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
+     * @return The new range of numeric values from {@code minValue} to positive infinity.
+     *
+     * @see #create(int, boolean, int, boolean)
+     *
+     * @since 0.5
+     */
+    public static NumberRange<Integer> createLeftBounded(final int minValue, final boolean isMinIncluded) {
+        return unique(new NumberRange<Integer>(Integer.class, Integer.valueOf(minValue), isMinIncluded, null, false));
+    }
+
+    /**
      * Constructs a range using the smallest type of {@link Number} that can hold the
      * given values. The given numbers don't need to be of the same type since they will
      * be {@linkplain Numbers#cast(Number, Class) casted} as needed. More specifically
@@ -228,6 +281,8 @@ public class NumberRange<E extends Numbe
      *   <li>{@code NumberRange<Double>} If none of the above types is suitable.</li>
      * </ul>
      *
+     * This method may return a shared instance, at implementation choice.
+     *
      * @param  minValue       The minimal value, or {@code null} if none.
      * @param  isMinIncluded  {@code true} if the minimal value is inclusive, or {@code false} if exclusive.
      * @param  maxValue       The maximal value, or {@code null} if none.
@@ -240,9 +295,9 @@ public class NumberRange<E extends Numbe
     {
         final Class<? extends Number> type = Numbers.widestClass(
                 Numbers.narrowestClass(minValue), Numbers.narrowestClass(maxValue));
-        return (type == null) ? null :
-            new NumberRange(type, Numbers.cast(minValue, type), isMinIncluded,
-                                  Numbers.cast(maxValue, type), isMaxIncluded);
+        return (type == null) ? null : unique(new NumberRange(type,
+                Numbers.cast(minValue, type), isMinIncluded,
+                Numbers.cast(maxValue, type), isMaxIncluded));
     }
 
     /**
@@ -260,6 +315,7 @@ public class NumberRange<E extends Numbe
             return (NumberRange<N>) range;
         }
         // The constructor will ensure that the range element type is a subclass of Number.
+        // Do not invoke unique(NumberRange) because the returned range is often temporary.
         return new NumberRange<N>(range);
     }
 

Modified: sis/trunk/ide-project/NetBeans/nbproject/project.properties
URL: http://svn.apache.org/viewvc/sis/trunk/ide-project/NetBeans/nbproject/project.properties?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/ide-project/NetBeans/nbproject/project.properties [ISO-8859-1] (original)
+++ sis/trunk/ide-project/NetBeans/nbproject/project.properties [ISO-8859-1] Thu May  1 15:59:33 2014
@@ -81,7 +81,7 @@ jdom1.version        = 1.0
 jdom2.version        = 2.0.4
 jee.version          = 6.0
 osgi.version         = 5.0.0
-netcdf.version       = 4.3.20
+netcdf.version       = 4.3.21
 joda-time.version    = 2.0
 httpclient.version   = 3.1
 slf4j.version        = 1.6.4

Modified: sis/trunk/pom.xml
URL: http://svn.apache.org/viewvc/sis/trunk/pom.xml?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/pom.xml (original)
+++ sis/trunk/pom.xml Thu May  1 15:59:33 2014
@@ -396,8 +396,7 @@ Apache SIS is a free software, Java lang
          The last properties in this list depend on the Apache SIS branch.
        =================================================================== -->
   <properties>
-    <geoapi.version>3.0.0</geoapi.version>
-    <netcdf.version>4.3.20</netcdf.version>
+    <netcdf.version>4.3.21</netcdf.version>
     <findbugs.version>2.5.3</findbugs.version>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <website.encoding>UTF-8</website.encoding>
@@ -407,6 +406,7 @@ Apache SIS is a free software, Java lang
     <maven.compile.source>1.6</maven.compile.source>
     <maven.compile.target>1.6</maven.compile.target>
     <sis.plugin.version>${project.version}</sis.plugin.version>
+    <geoapi.version>3.0.0</geoapi.version>
   </properties>
 
   <profiles>

Modified: sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/ShapeFile.java
URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/ShapeFile.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/ShapeFile.java [UTF-8] (original)
+++ sis/trunk/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/ShapeFile.java [UTF-8] Thu May  1 15:59:33 2014
@@ -27,8 +27,12 @@ import java.util.Map;
 import com.esri.core.geometry.Point;
 import com.esri.core.geometry.Polygon;
 import com.esri.core.geometry.Polyline;
+import com.esri.core.geometry.Geometry;
 
+import org.apache.sis.measure.NumberRange;
 import org.apache.sis.feature.DefaultFeature;
+import org.apache.sis.feature.DefaultFeatureType;
+import org.apache.sis.feature.DefaultAttributeType;
 import org.apache.sis.storage.DataStoreException;
 
 
@@ -36,14 +40,15 @@ import org.apache.sis.storage.DataStoreE
  * Provides a ShapeFile Reader.
  *
  * @author  Travis L. Pinney
- * @since   0.4
- * @version 0.4
+ * @since   0.5
+ * @version 0.5
  * @module
  *
  * @see <a href="http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf">ESRI Shapefile Specification</a>
  * @see <a href="http://ulisse.elettra.trieste.it/services/doc/dbase/DBFstruct.htm">dBASE III File Structure</a>
  */
 public class ShapeFile {
+    private static final String GEOMETRY_NAME = "geometry";
 
     public int FileCode;   // big
     public int FileLength;  // big // The value for file length is the total length of the file in 16-bit words
@@ -140,6 +145,7 @@ public class ShapeFile {
             this.FDArray.add(fd);
             // loop until you hit the 0Dh field terminator
         }
+        final DefaultFeatureType featureType = getFeatureType(shpfile);
 
         df.get(); // should be 0d for field terminator
 
@@ -151,14 +157,13 @@ public class ShapeFile {
             data = new byte[4];
             rf.order(ByteOrder.LITTLE_ENDIAN);
             int ShapeType = rf.getInt();
-            DefaultFeature f = new DefaultFeature();
-            f.setRecord(new HashMap<String, String>());
+            final DefaultFeature f = new DefaultFeature(featureType);
 
             if (ShapeType == ShapeTypeEnum.Point.getValue()) {
                 double x = rf.getDouble();
                 double y = rf.getDouble();
                 Point pnt = new Point(x,y);
-                f.setGeom(pnt);
+                f.setAttributeValue(GEOMETRY_NAME, pnt);
 
             } else if (ShapeType == ShapeTypeEnum.Polygon.getValue()) {
                 double xmin = rf.getDouble();
@@ -186,7 +191,7 @@ public class ShapeFile {
                     ypnt = rf.getDouble();
                     poly.lineTo(xpnt, ypnt);
                 }
-                f.setGeom(poly);
+                f.setAttributeValue(GEOMETRY_NAME, poly);
 
             } else if (ShapeType == ShapeTypeEnum.PolyLine.getValue()) {
                 double xmin = rf.getDouble();
@@ -222,7 +227,7 @@ public class ShapeFile {
                     }
                 }
 
-                f.setGeom(ply);
+                f.setAttributeValue(GEOMETRY_NAME, ply);
 
             } else {
                 throw new DataStoreException("Unsupported shapefile type: " + this.ShapeType);
@@ -239,7 +244,7 @@ public class ShapeFile {
                 data = new byte[fd.getLength()];
                 df.get(data);
                 String value = new String(data);
-                f.getRecord().put(fd.getName(), value);
+                f.setAttributeValue(fd.getName(), value);
             }
 
             this.FeatureMap.put(RecordNumber, f);
@@ -251,6 +256,21 @@ public class ShapeFile {
         fis2.close();
     }
 
+    private DefaultFeatureType getFeatureType(final String name) {
+        final int n = FDArray.size();
+        final DefaultAttributeType<?>[] attributes = new DefaultAttributeType<?>[n + 1];
+        final NumberRange<Integer> cardinality = NumberRange.create(1, true, 1, true);
+        final Map<String,Object> properties = new HashMap<String,Object>(4);
+        for (int i=0; i<n; i++) {
+            properties.put(DefaultAttributeType.NAME_KEY, FDArray.get(i).getName());
+            attributes[i] = new DefaultAttributeType<String>(properties, String.class, null, cardinality);
+        }
+        properties.put(DefaultAttributeType.NAME_KEY, GEOMETRY_NAME);
+        attributes[n] = new DefaultAttributeType<Geometry>(properties, Geometry.class, null, cardinality);
+        properties.put(DefaultAttributeType.NAME_KEY, name);
+        return new DefaultFeatureType(properties, false, null, attributes);
+    }
+
     @Override
     public String toString() {
         StringBuilder s = new StringBuilder();

Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java
URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java [UTF-8] (original)
+++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataInput.java [UTF-8] Thu May  1 15:59:33 2014
@@ -560,7 +560,7 @@ public class ChannelDataInput extends Ch
             while ((length -= n) != 0) {
                 offset += n;
                 ensureBufferContains(dataSize); // Actually read as much data as possible.
-                view.position(0).limit(buffer.remaining() / dataSize);
+                view.rewind().limit(buffer.remaining() / dataSize);
                 transfer(offset, n = Math.min(view.remaining(), length));
                 skipInBuffer(n * dataSize);
             }

Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataOutput.java
URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataOutput.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataOutput.java [UTF-8] (original)
+++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ChannelDataOutput.java [UTF-8] Thu May  1 15:59:33 2014
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.internal.storage;
 
+import java.util.Arrays;
 import java.io.Flushable;
 import java.io.IOException;
 import java.nio.Buffer;
@@ -105,7 +106,7 @@ public class ChannelDataOutput extends C
         if (after > buffer.limit()) {
             /*
              * We will increase the limit for every new 'put' operation in order to maintain the number
-             * of valid bytes in the buffer. In the new limit would exceed the buffer capacity, then we
+             * of valid bytes in the buffer. If the new limit would exceed the buffer capacity, then we
              * need to write some bytes now.
              */
             if (after > capacity) {
@@ -479,7 +480,7 @@ public class ChannelDataOutput extends C
             while ((length -= n) != 0) {
                 offset += n;
                 ensureBufferAccepts(Math.min(length, view.capacity()) * dataSize);
-                view.position(0).limit(buffer.remaining() / dataSize);
+                view.rewind().limit(buffer.remaining() / dataSize);
                 transfer(offset, n = view.remaining());
                 skipInBuffer(n * dataSize);
             }
@@ -583,7 +584,25 @@ public class ChannelDataOutput extends C
     }
 
     /**
+     * Fills the buffer with the zero values from its position up to the limit.
+     * After this method call, the position is undetermined and shall be set to
+     * a new value by the caller.
+     */
+    private void clear() {
+        if (buffer.hasArray()) {
+            final int offset = buffer.arrayOffset();
+            Arrays.fill(buffer.array(), offset + buffer.position(), offset + buffer.limit(), (byte) 0);
+        } else {
+            while (buffer.hasRemaining()) {
+                buffer.put((byte) 0);
+            }
+        }
+    }
+
+    /**
      * Moves to the given position in the stream, relative to the stream position at construction time.
+     * If the given position is greater than the stream length, then the values of bytes between the
+     * previous stream length and the given position are unspecified. The limit is unchanged.
      *
      * @param  position The position where to move.
      * @throws IOException If the stream can not be moved to the given position.
@@ -605,6 +624,30 @@ public class ChannelDataOutput extends C
             flush();
             ((FileChannel) channel).position(channelOffset + position);
             bufferOffset = position;
+        } else if (p >= 0) {
+            /*
+             * Requested position is after the current buffer limit and
+             * we can not seek, so we have to pad with some zero values.
+             */
+            p -= buffer.limit();
+            flush(); // Also set the position to 0.
+            if (p <= buffer.capacity()) {
+                buffer.limit((int) p);
+                clear();
+                buffer.position((int) p);
+            } else {
+                buffer.clear();
+                clear();
+                do {
+                    if (channel.write(buffer) == 0) {
+                        onEmptyTransfer();
+                    }
+                    bufferOffset += buffer.position();
+                    p -= buffer.position();
+                    buffer.rewind();
+                } while (p > buffer.capacity());
+                buffer.limit((int) p).position((int) p);
+            }
         } else {
             // We can not move position beyond the buffered part.
             throw new IOException(Errors.format(Errors.Keys.StreamIsForwardOnly_1, filename));
@@ -619,7 +662,7 @@ public class ChannelDataOutput extends C
      */
     @Override
     public final void flush() throws IOException {
-        buffer.position(0);
+        buffer.rewind();
         writeFully();
         buffer.limit(0);
         clearBitOffset();
@@ -633,7 +676,7 @@ public class ChannelDataOutput extends C
     @Override
     final void flushAndSetPosition(final int position) throws IOException {
         final int limit = buffer.limit();
-        buffer.position(0).limit(position);
+        buffer.rewind().limit(position);
         writeFully();
         buffer.limit(limit);
     }

Modified: sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ByteArrayChannel.java
URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ByteArrayChannel.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ByteArrayChannel.java [UTF-8] (original)
+++ sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ByteArrayChannel.java [UTF-8] Thu May  1 15:59:33 2014
@@ -109,7 +109,7 @@ final strictfp class ByteArrayChannel im
      */
     public ByteArrayChannel position(final long newPosition) throws IOException {
         ensureOpen();
-        ArgumentChecks.ensureBetween("position", 0, limit, newPosition);
+        ArgumentChecks.ensureBetween("position", 0, data.length, newPosition);
         position = (int) newPosition;
         return this;
     }

Modified: sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataOutputTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataOutputTest.java?rev=1591695&r1=1591694&r2=1591695&view=diff
==============================================================================
--- sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataOutputTest.java [UTF-8] (original)
+++ sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ChannelDataOutputTest.java [UTF-8] Thu May  1 15:59:33 2014
@@ -24,6 +24,7 @@ import java.io.DataOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.InvalidMarkException;
+import java.nio.channels.ByteChannel;
 import javax.imageio.stream.ImageOutputStream;
 import org.apache.sis.test.DependsOnMethod;
 import org.junit.Test;
@@ -85,6 +86,20 @@ public strictfp class ChannelDataOutputT
     }
 
     /**
+     * May be invoked after {@link #initialize(String, int, int)} for replacing the seekable byte channel
+     * by a non-seekable one. Used for testing different code paths in {@link ChannelDataOutput}.
+     */
+    private void nonSeekableChannel() throws IOException {
+        final ByteChannel channel = (ByteChannel) testedStream.channel;
+        testedStream = new ChannelDataOutput(testedStream.filename, new ByteChannel() {
+            @Override public boolean isOpen()                                 {return channel.isOpen();}
+            @Override public int     read(ByteBuffer dst)  throws IOException {return channel.read(dst);}
+            @Override public int     write(ByteBuffer src) throws IOException {return channel.write(src);}
+            @Override public void    close()               throws IOException {channel.close();}
+        }, testedStream.buffer);
+    }
+
+    /**
      * Fills a stream with random data and compares the result with a reference output stream.
      * We allocate a small buffer for the {@code ChannelDataOutput} in order to force frequent
      * interactions between the buffer and the channel.
@@ -144,6 +159,42 @@ public strictfp class ChannelDataOutputT
     }
 
     /**
+     * Tests seeking ahead of buffer capacity.
+     *
+     * @throws IOException Should never happen.
+     */
+    @Test
+    @DependsOnMethod("testWriteAndSeek")
+    public void testSeekAhead() throws IOException {
+        testSeekAhead(true);
+        testSeekAhead(false);
+    }
+
+    /**
+     * Implementation of {@link #testSeekAhead()} method, testing two different code paths depending
+     * on the {@code seekable} argument value. Note: the two code paths are actually identical on JDK 6.
+     */
+    private void testSeekAhead(final boolean seekable) throws IOException {
+        initialize("testArgumentChecks", 48, 10);
+        if (!seekable) {
+            nonSeekableChannel();
+        }
+        for (int i=0; i<3; i++) {
+            final long v = random.nextLong();
+            referenceStream.writeLong(v);
+            testedStream.writeLong(v);
+        }
+        assertEquals("getStreamPosition()", 24, testedStream.getStreamPosition());
+        testedStream.seek(40); // Move 2 long ahead. Space shall be filled by 0.
+        referenceStream.writeLong(0);
+        referenceStream.writeLong(0);
+        final long v = random.nextLong();
+        referenceStream.writeLong(v);
+        testedStream.writeLong(v);
+        assertStreamContentEquals();
+    }
+
+    /**
      * Tests the argument checks performed by various methods. For example this method
      * tests {@link ChannelDataOutput#seek(long)} with an invalid seek position.
      *



Mime
View raw message