sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1817597 [11/19] - in /sis/branches/ISO-19115-3: ./ application/ application/sis-console/ application/sis-console/src/main/artifact/ application/sis-console/src/main/artifact/lib/ application/sis-console/src/main/artifact/lib/darwin/ applic...
Date Sat, 09 Dec 2017 10:57:47 GMT
Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -27,18 +27,14 @@ import org.opengis.geometry.DirectPositi
 import org.opengis.referencing.cs.AxisDirection;    // For javadoc
 
 import org.apache.sis.util.Static;
-import org.apache.sis.util.Workaround;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.Constants;
 
-import static java.lang.Math.PI;
-import static java.lang.Math.abs;
 import static org.apache.sis.measure.UnitRegistry.SI;
 import static org.apache.sis.measure.UnitRegistry.ACCEPTED;
 import static org.apache.sis.measure.UnitRegistry.IMPERIAL;
 import static org.apache.sis.measure.UnitRegistry.OTHER;
 import static org.apache.sis.measure.UnitRegistry.PREFIXABLE;
-import static org.apache.sis.measure.SexagesimalConverter.EPS;
 
 
 /**
@@ -371,7 +367,7 @@ public final class Units extends Static
      * <table class="compact" summary="Related units" style="margin-left:30px; line-height:1.25">
      *   <tr><td>SI angle units:</td>   <td style="word-spacing:1em">{@link #MICRORADIAN}, <u><b>{@code RADIAN}</b></u>.</td></tr>
      *   <tr><td>In other systems:</td> <td style="word-spacing:1em">{@link #DEGREE}, {@link #ARC_MINUTE}, {@link #ARC_SECOND}, {@link #GRAD}.</td></tr>
-     *   <tr><td>Derived units:</td>    <td style="word-spacing:1em">{@link #STERADIAN}.</td></tr>
+     *   <tr><td>Derived units:</td>    <td style="word-spacing:1em">{@link #STERADIAN}, {@link #RADIANS_PER_SECOND}.</td></tr>
      * </table></div>
      *
      * @since 0.8
@@ -541,7 +537,7 @@ public final class Units extends Static
      * <div class="note"><p class="simpleTagLabel" style="margin-bottom:0">Related units:</p>
      * <table class="compact" summary="Related units" style="margin-left:30px; line-height:1.25">
      *   <tr><td>SI time units:</td>    <td style="word-spacing:1em">{@link #MILLISECOND}, <b>{@link #SECOND}</b>.</td></tr>
-     *   <tr><td>In other systems:</td> <td style="word-spacing:1em">{@link #MINUTE}, {@link #HOUR}, {@link #DAY}, <u>{@link WEEK}</u>, {@link #TROPICAL_YEAR}.</td></tr>
+     *   <tr><td>In other systems:</td> <td style="word-spacing:1em">{@link #MINUTE}, {@link #HOUR}, {@link #DAY}, <u>{@link #WEEK}</u>, {@link #TROPICAL_YEAR}.</td></tr>
      *   <tr><td>Derived units:</td>    <td style="word-spacing:1em">{@link #KILOMETRES_PER_HOUR}, {@link #HERTZ}.</td></tr>
      * </table></div>
      *
@@ -582,6 +578,19 @@ public final class Units extends Static
     public static final Unit<Frequency> HERTZ;
 
     /**
+     * The unit for angular velocity (rad/s).
+     * The identifier is EPSG:1035.
+     *
+     * <div class="note"><p class="simpleTagLabel" style="margin-bottom:0">Related units:</p>
+     * <table class="compact" summary="Related units" style="margin-left:30px; line-height:1.25">
+     *   <tr><td>Components:</td> <td style="word-spacing:0.5em">{@link #RADIAN} ∕ {@link #SECOND}</td></tr>
+     * </table></div>
+     *
+     * @since 0.8
+     */
+    public static final Unit<AngularVelocity> RADIANS_PER_SECOND;
+
+    /**
      * The SI derived unit for speed (m/s).
      * The unlocalized name is “metres per second” and the identifier is EPSG:1026.
      *
@@ -1020,10 +1029,9 @@ public final class Units extends Static
      * Salinity measured using PSS-78. While this is a dimensionless measurement, the {@code "psu"} symbol
      * is sometime added to PSS-78 measurement. However this is officially discouraged.
      *
-     * <p>If we make this field public in a future SIS version, we should consider introducing a
-     * {@code Salinity} quantity type.</p>
+     * @since 0.8
      */
-    static final Unit<Dimensionless> PSU;
+    public static final Unit<Salinity> PSU;
 
     /**
      * Sigma-level, used in oceanography. This is a way to measure a depth as a fraction of the sea floor depth.
@@ -1069,6 +1077,7 @@ public final class Units extends Static
         final UnitDimension temperature   = new UnitDimension('Θ');
         final UnitDimension amount        = new UnitDimension('N');
         final UnitDimension luminous      = new UnitDimension('J');
+        final UnitDimension frequency     = time.pow(-1);
         final UnitDimension area          = length.pow(2);
         final UnitDimension speed         = length.divide(time);
         final UnitDimension force         = mass.multiply(speed).divide(time);
@@ -1143,11 +1152,19 @@ public final class Units extends Static
         WEEK           = add(s, LinearConverter.scale( 7*24*60*60,      1), "wk",  OTHER,    (short) 0);
         TROPICAL_YEAR  = add(s, LinearConverter.scale(31556925445.0, 1000), "a",   OTHER,    (short) 1029);
         /*
-         * All Unit<Speed>.
+         * All Unit<Speed>, Unit<AngularVelocity> and Unit<ScaleRateOfChange>.
+         * The 'unityPerSecond' unit is not added to the registry because it is specific to the EPSG database,
+         * has no clear symbol and is easy to confuse with Hertz. We create that unit only for allowing us to
+         * create the "ppm/a" units.
          */
+        final SystemUnit<ScaleRateOfChange> unityPerSecond;
+        unityPerSecond = new SystemUnit<>(ScaleRateOfChange.class, frequency, null, OTHER, (short) 1036, null);
+        unityPerSecond.related(1);
         mps.related(1);
         METRES_PER_SECOND   = mps;
-        KILOMETRES_PER_HOUR = add(mps, LinearConverter.scale(10, 36), "km∕h", ACCEPTED, (short) 0);
+        KILOMETRES_PER_HOUR = add(mps, LinearConverter.scale(10, 36),     "km∕h",  ACCEPTED, (short) 0);
+        RADIANS_PER_SECOND  = add(AngularVelocity.class, null, frequency, "rad∕s", SI,       (short) 1035);
+        add(unityPerSecond, LinearConverter.scale(1, 31556925445E6),      "ppm∕a", OTHER,    (short) 1030);
         /*
          * All Unit<Pressure>.
          */
@@ -1177,8 +1194,9 @@ public final class Units extends Static
         GRAM         = add(kg, milli, "g", (byte) (ACCEPTED | PREFIXABLE), (short) 0);
         /*
          * Force, energy, electricity, magnetism and other units.
+         * Frequency must be defined after angular velocities.
          */
-        HERTZ      = add(Frequency.class,           Scalar.Frequency::new, time.pow(-1),                 "Hz",  (byte) (SI | PREFIXABLE), (short) 0);
+        HERTZ      = add(Frequency.class,           Scalar.Frequency::new, frequency,                    "Hz",  (byte) (SI | PREFIXABLE), (short) 0);
         NEWTON     = add(Force.class,               Scalar.Force::new,     force,                        "N",   (byte) (SI | PREFIXABLE), (short) 0);
         JOULE      = add(Energy.class,              Scalar.Energy::new,    energy,                       "J",   (byte) (SI | PREFIXABLE), (short) 0);
         WATT       = add(Power.class,               Scalar.Power::new,     power,                        "W",   (byte) (SI | PREFIXABLE), (short) 0);
@@ -1201,7 +1219,7 @@ public final class Units extends Static
          */
         PERCENT = add(one, centi,                                                    "%",     OTHER, (short) 0);
         PPM     = add(one, micro,                                                    "ppm",   OTHER, (short) 9202);
-        PSU     = add(Dimensionless.class, Scalar.Dimensionless::new, dimensionless, "psu",   OTHER, (short) 0);
+        PSU     = add(Salinity.class,      null,                      dimensionless, "psu",   OTHER, (short) 0);
         SIGMA   = add(Dimensionless.class, Scalar.Dimensionless::new, dimensionless, "sigma", OTHER, (short) 0);
         PIXEL   = add(Dimensionless.class, Scalar.Dimensionless::new, dimensionless, "px",    OTHER, (short) 0);
         UNITY   = UnitRegistry.init(one);  // Must be last in order to take precedence over all other units associated to UnitDimension.NONE.
@@ -1452,51 +1470,6 @@ public final class Units extends Static
     }
 
     /**
-     * Multiplies the given unit by the given factor. For example multiplying {@link #METRE}
-     * by 1000 gives {@link #KILOMETRE}. Invoking this method is equivalent to invoking
-     * {@link Unit#multiply(double)} except for the following:
-     *
-     * <ul>
-     *   <li>A small tolerance factor is applied for a few factors commonly used in GIS.
-     *       For example {@code multiply(RADIANS, 0.0174532925199...)} will return {@link #DEGREE}
-     *       even if the given numerical value is slightly different than {@linkplain Math#PI π}/180.
-     *       The tolerance factor and the set of units handled especially may change in future SIS versions.</li>
-     *   <li>This method tries to returns unique instances for some common units.</li>
-     * </ul>
-     *
-     * @param  <Q>     the quantity measured by the unit.
-     * @param  unit    the unit to multiply.
-     * @param  factor  the multiplication factor.
-     * @return the unit multiplied by the given factor.
-     *
-     * @deprecated Replaced by Apache SIS implementation of {@link Unit#multiply(double)}.
-     */
-    @Deprecated
-    @Workaround(library="JSR-275", version="0.9.3")
-    @SuppressWarnings("unchecked")
-    public static <Q extends Quantity<Q>> Unit<Q> multiply(Unit<Q> unit, final double factor) {
-        if (RADIAN.equals(unit)) {
-            if (abs(factor - (PI / 180)) <= (EPS * PI/180)) {
-                return (Unit<Q>) DEGREE;
-            }
-            if (abs(factor - (PI / 200)) <= (EPS * PI/200)) {
-                return (Unit<Q>) GRAD;
-            }
-        } else if (METRE.equals(unit)) {
-            if (abs(factor - 0.3048) <= (EPS * 0.3048)) {
-                return (Unit<Q>) FOOT;
-            }
-            if (abs(factor - (1200.0/3937)) <= (EPS * (1200.0/3937))) {
-                return (Unit<Q>) US_SURVEY_FOOT;
-            }
-        }
-        if (abs(factor - 1) > EPS) {
-            unit = unit.multiply(factor);
-        }
-        return unit;
-    }
-
-    /**
      * Returns the factor by which to multiply the standard unit in order to get the given unit.
      * The "standard" unit is usually the SI unit on which the given unit is based, as given by
      * {@link Unit#getSystemUnit()}.
@@ -1597,12 +1570,12 @@ public final class Units extends Static
      * is parsed as an integer and forwarded to the {@link #valueOfEPSG(int)} method.
      *
      * <div class="section">NetCDF unit symbols</div>
-     * The attributes in NetCDF files often merge the axis direction with the angular unit,
+     * The attributes in netCDF files often merge the axis direction with the angular unit,
      * as in {@code "degrees_east"} or {@code "degrees_north"}. This {@code valueOf} method
      * ignores those suffixes and unconditionally returns {@link #DEGREE} for all axis directions.
      * In particular, the units for {@code "degrees_west"} and {@code "degrees_east"}
      * do <strong>not</strong> have opposite sign.
-     * It is caller responsibility to handle the direction of axes associated to NetCDF units.
+     * It is caller responsibility to handle the direction of axes associated to netCDF units.
      *
      * @param  uom  the symbol to parse, or {@code null}.
      * @return the parsed symbol, or {@code null} if {@code uom} was null.

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/About.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/About.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/About.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/About.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -39,8 +39,6 @@ import java.text.DateFormat;
 import java.text.FieldPosition;
 import java.nio.file.Path;
 import java.nio.charset.Charset;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Exceptions;
@@ -382,7 +380,7 @@ fill:   for (int i=0; ; i++) {
                     if (sections.contains(PATHS)) {
                         nameKey = Vocabulary.Keys.DataDirectory;
                         try {
-                            value = AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getenv(DataDirectory.ENV));
+                            value = DataDirectory.getenv();
                         } catch (SecurityException e) {
                             value = e.toString();
                         }

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/InstallationResources.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -27,7 +27,7 @@ import java.io.BufferedReader;
  * {@code InstallationResources} can be used for downloading large files that may not be of interest
  * to every users, or data that are subject to more restricting terms of use than the Apache license.
  *
- * <div class="note"><b>Examples:</b><ul>
+ * <div class="note"><b>Examples:</b><ul class="verbose">
  * <li>The NADCON grid files provide <cite>datum shifts</cite> data for North America.
  *     Since those files are in the public domain, they could be bundled in Apache SIS.
  *     But the weight of those files (about 2.4 Mb) is unnecessary for users who do not live in North America.</li>
@@ -37,8 +37,18 @@ import java.io.BufferedReader;
  *     are more restrictive than the Apache license and require that we inform the users about those conditions.</li>
  * </ul></div>
  *
- * Some classes that depend on installation resources are:
- * {@link org.apache.sis.referencing.factory.sql.EPSGFactory}.
+ * Some authorities implemented in Apache SIS modules are listed below.
+ * In this list, {@code "Embedded"} is a pseudo-authority for an embedded database containing EPSG and other data.
+ * The embedded database is provided as a convenience for avoiding the need to define a {@code SIS_DATA} directory
+ * on the local machine.
+ *
+ * <table class="sis">
+ *   <caption>Authorities supported by Apache SIS</caption>
+ *   <tr><th>Authority</th>          <th>Provided by Maven module</th>                          <th>Used by class</th></tr>
+ *   <tr><td>{@code "EPSG"}</td>     <td>{@code org.apache.sis.non-free:sis-epsg}</td>          <td>{@link org.apache.sis.referencing.factory.sql.EPSGFactory}</td></tr>
+ *   <tr><td>{@code "Embedded"}</td> <td>{@code org.apache.sis.non-free:sis-embedded-data}</td> <td>All the above</td></tr>
+ * </table>
+ *
  * In order to allow those classes to discover which resources are available,
  * {@code InstallationResources} implementations shall be declared in the following file:
  *
@@ -47,13 +57,13 @@ import java.io.BufferedReader;
  * }
  *
  * Above registration is usually done automatically when extension modules are added on the classpath.
- * For example adding the {@code org.apache.sis.non-free:​sis-epsg} Maven dependency as documented on
+ * For example adding the {@code org.apache.sis.non-free:sis-epsg} Maven dependency as documented on
  * the <a href="http://sis.apache.org/epsg.html">Apache SIS web site</a> is the only step needed for
  * allowing Apache SIS to read the EPSG scripts (however SIS still needs an installation directory
  * for writing the database; see above-cited web page for more information).
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.7
  * @module
  */
@@ -69,9 +79,19 @@ public abstract class InstallationResour
      * The values recognized by SIS are listed below
      * (note that this list may be expanded in any future SIS versions):
      *
-     * <ul>
-     *   <li>{@code "EPSG"} for the EPSG geodetic dataset.</li>
-     * </ul>
+     * <table class="sis">
+     *   <caption>Authorities supported by Apache SIS</caption>
+     *   <tr><th>Authority</th>          <th>Resources</th></tr>
+     *   <tr><td>{@code "EPSG"}</td>     <td>SQL installation scripts for EPSG geodetic dataset.</td></tr>
+     *   <tr><td>{@code "Embedded"}</td> <td>Data source of embedded database containing EPSG and other resources.</td></tr>
+     * </table>
+     *
+     * <div class="note"><b>Note:</b>
+     * {@code "Embedded"} is a pseudo-authority for an embedded database containing EPSG and other data.
+     * This embedded database is provided by the {@code org.apache.sis.non-free:sis-embedded-data} module
+     * as a convenience for avoiding the need to define a {@code SIS_DATA} directory on the local machine.
+     * In this particular case, the resource is more for execution than for installation.
+     * </div>
      *
      * This method may return an empty set if this {@code InstallationResources} instance did not find the
      * resources (for example because of files not found) or does not have the permission to distribute them.
@@ -84,10 +104,19 @@ public abstract class InstallationResour
      * Returns the terms of use of the resources distributed by the specified authority, or {@code null} if none.
      * The terms of use can be returned in either plain text or HTML.
      *
-     * <div class="note"><b>Example:</b>
-     * For the {@code "EPSG"} authority, this method may return a copy of the
-     * <a href="http://www.epsg.org/TermsOfUse">http://www.epsg.org/TermsOfUse</a> page.
-     * </div>
+     * <table class="sis">
+     *   <caption>Licenses for some supported authorities</caption>
+     *   <tr>
+     *     <th>Authority</th>
+     *     <th>License</th>
+     *   </tr><tr>
+     *     <td>{@code "EPSG"}</td>
+     *     <td>A copy of the <a href="http://www.epsg.org/TermsOfUse">http://www.epsg.org/TermsOfUse</a> page.</td>
+     *   </tr><tr>
+     *     <td>{@code "Embedded"}</td>
+     *     <td>Above EPSG license.</td>
+     *   </tr>
+     * </table>
      *
      * @param  authority  one of the values returned by {@link #getAuthorities()}.
      * @param  locale     the preferred locale for the terms of use.
@@ -101,12 +130,18 @@ public abstract class InstallationResour
     /**
      * Returns the names of all resources of the specified authority that are distributed by this instance.
      * The resources will be used in the order they appear in the array.
+     * Examples:
      *
-     * <div class="note"><b>Example:</b>
-     * for the {@code "EPSG"} authority, this method may return the filenames of all SQL scripts to execute.
-     * One of the first script creates tables, followed by a script that populates tables with data,
-     * followed by a script that creates foreigner keys.
-     * </div>
+     * <ul class="verbose">
+     *   <li><b>{@code "EPSG"} authority:</b>
+     *     the resource names are the filenames of all SQL scripts to execute. One of the first script creates tables,
+     *     followed by a script that populates tables with data, followed by a script that creates foreigner keys.
+     *   </li>
+     *   <li><b>{@code "Embedded"} pseudo-authority:</b>
+     *     the database name, which is {@code "SpatialMetadata"}.
+     *     When embedded, this database is read-only.
+     *   </li>
+     * </ul>
      *
      * @param  authority  one of the values returned by {@link #getAuthorities()}.
      * @return the names of all resources of the given authority that are distributed by this instance.
@@ -116,6 +151,31 @@ public abstract class InstallationResour
     public abstract String[] getResourceNames(String authority) throws IOException;
 
     /**
+     * Returns an installation resource for the given authority, or {@code null} if not available.
+     * The return value may be an instance of any type, at implementation choice.
+     * This may be for example a {@link java.net.URL} referencing the actual resource.
+     *
+     * <p>The default implementation returns {@code null}. A null value means that the resource is fetched by
+     * {@link #openScript(String, int)} instead than this method. We do not return {@link java.net.URL} to text
+     * files in order to ensure that the file is opened with proper character encoding.</p>
+     *
+     * @param  authority  one of the values returned by {@link #getAuthorities()}.
+     * @param  index      index of the resource to get, from 0 inclusive to
+     *         <code>{@linkplain #getResourceNames(String) getResourceNames}(authority).length</code> exclusive.
+     * @return the resource as an URL or any other type (at implementation choice), or {@code null} if not available.
+     * @throws IllegalArgumentException if the given {@code authority} argument is not one of the expected values.
+     * @throws IndexOutOfBoundsException if the given {@code resource} argument is out of bounds.
+     * @throws IOException if an error occurred while fetching the resource.
+     *
+     * @see ClassLoader#getResource(String)
+     *
+     * @since 0.8
+     */
+    public Object getResource(String authority, int index) throws IOException {
+        return null;
+    }
+
+    /**
      * Returns a reader for the resources at the given index.
      * The resource may be a SQL script or any other resources readable as a text.
      * The returned {@link BufferedReader} instance shall be closed by the caller.

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -36,7 +36,7 @@ import org.apache.sis.internal.system.Mo
  * ({@link org.apache.sis.storage.DataStore}, <i>etc</i>).
  * {@code OptionKey}s are used for aspects that usually do not need to be configured, except in a few specialized cases.
  * For example most data file formats read by SIS do not require the user to specify the character encoding, since the
- * encoding it is often given in the file header or in the format specification. However if SIS may have to read plain
+ * encoding it is often given in the file header or in the format specification. However if SIS needs to read plain
  * text files <em>and</em> the default platform encoding is not suitable, then the user can specify the desired encoding
  * explicitely using the {@link #ENCODING} option.
  *
@@ -75,16 +75,6 @@ public class OptionKey<T> implements Ser
     private static final long serialVersionUID = -7580514229639750246L;
 
     /**
-     * The library to use for creating geometric objects at reading time.
-     * Some libraries are the Java Topology Suite (JTS), ESRI geometry API and Java2D.
-     * If this option is not specified, then a library will be selected automatically
-     * among the libraries available in the runtime environment.
-     *
-     * @since 0.8
-     */
-    public static final OptionKey<GeometryLibrary> GEOMETRY_LIBRARY = new OptionKey<>("GEOMETRY_LIBRARY", GeometryLibrary.class);
-
-    /**
      * The locale to use for locale-sensitive data. This option determines the language to use for writing
      * {@link org.apache.sis.util.iso.AbstractInternationalString international strings} when the target
      * storage support only one language. It may also control number and date patterns in some file formats
@@ -192,6 +182,16 @@ public class OptionKey<T> implements Ser
     public static final OptionKey<ByteBuffer> BYTE_BUFFER = new OptionKey<>("BYTE_BUFFER", ByteBuffer.class);
 
     /**
+     * The library to use for creating geometric objects at reading time.
+     * Some libraries are the Java Topology Suite (JTS), ESRI geometry API and Java2D.
+     * If this option is not specified, then a default library will be selected among
+     * the libraries available in the runtime environment.
+     *
+     * @since 0.8
+     */
+    public static final OptionKey<GeometryLibrary> GEOMETRY_LIBRARY = new OptionKey<>("GEOMETRY_LIBRARY", GeometryLibrary.class);
+
+    /**
      * The number of spaces to use for indentation when formatting text files in WKT or XML formats.
      * A value of {@value org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE} means to format the whole WKT
      * or XML document on a single line without line feeds or indentation.

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/package-info.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/package-info.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/setup/package-info.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -17,9 +17,13 @@
 
 /**
  * Provides information and some controls about SIS configuration.
+ * System-wide informations are provided by the {@link org.apache.sis.setup.About} class.
+ * Some controls, for example on the {@linkplain org.apache.sis.setup.GeometryLibrary geometry library} to use,
+ * is provided by a set of {@linkplain org.apache.sis.setup.OptionKey options} which can be used when a data store
+ * is created.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.3
  * @module
  */

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -67,7 +67,7 @@ import java.lang.reflect.Array;
  * objects.
  *
  * @author Martin Desruisseaux (IRD, Geomatys)
- * @version 0.4
+ * @version 0.8
  *
  * @see Arrays
  *
@@ -1684,6 +1684,33 @@ public final class ArraysExt extends Sta
     }
 
     /**
+     * Returns {@code true} if all values in the specified array are equal to the specified value,
+     * which may be {@code null}.
+     *
+     * @param  array  the array to check.
+     * @param  value  the expected value.
+     * @return {@code true} if all elements in the given array are equal to the given value.
+     *
+     * @since 0.8
+     */
+    public static boolean allEquals(final Object[] array, final Object value) {
+        if (value == null) {
+            for (int i=0; i<array.length; i++) {
+                if (array[i] != null) {
+                    return false;
+                }
+            }
+        } else {
+            for (int i=0; i<array.length; i++) {
+                if (!value.equals(array[i])) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
      * Returns {@code true} if all values in the specified array are equal to the specified value,
      * which may be {@link Double#NaN}.
      *

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -1200,7 +1200,7 @@ searchWordBreak:    while (true) {
      *
      *   <li>Next replace all occurrence of {@code '_'} by spaces in order to take in account
      *     an other common naming convention, which uses {@code '_'} as a word separator. This
-     *     convention is used by NetCDF attributes like {@code "project_name"}.</li>
+     *     convention is used by netCDF attributes like {@code "project_name"}.</li>
      *
      *   <li>Finally ensure that the first character is upper-case.</li>
      * </ol>

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.ArrayList;
 import java.util.Locale;
 import java.sql.SQLException;
+import java.nio.file.DirectoryIteratorException;
 import java.lang.reflect.InvocationTargetException;
 import org.opengis.util.InternationalString;
 import org.apache.sis.util.resources.Vocabulary;
@@ -211,6 +212,7 @@ public final class Exceptions extends St
      *   <li>It is an instance of {@link InvocationTargetException} (could be wrapping anything).</li>
      *   <li>It is an instance of {@link BackingStoreException} (typically wrapping a checked exception).</li>
      *   <li>It is an instance of {@link UncheckedIOException} (wrapping a {@link java.io.IOException}).</li>
+     *   <li>It is an instance of {@link DirectoryIteratorException} (wrapping a {@link java.io.IOException}).</li>
      *   <li>It is a parent type of the cause. For example some JDBC drivers wrap {@link SQLException}
      *       in other {@code SQLException} without additional information.</li>
      * </ul>
@@ -227,7 +229,8 @@ public final class Exceptions extends St
         if (exception != null) {
             while (exception instanceof InvocationTargetException ||
                    exception instanceof BackingStoreException ||
-                   exception instanceof UncheckedIOException)
+                   exception instanceof UncheckedIOException ||
+                   exception instanceof DirectoryIteratorException)
             {
                 final Throwable cause = exception.getCause();
                 if (!(cause instanceof Exception)) break;

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -37,12 +37,36 @@ import org.apache.sis.internal.system.Re
 
 // Branch-dependent imports
 import java.util.function.Supplier;
+import java.util.function.Function;
+import java.util.function.BiFunction;
 
 
 /**
- * A concurrent cache mechanism. This implementation is thread-safe and supports concurrency.
- * A cache entry can be locked when an object is in process of being created. The steps
- * are as below:
+ * A concurrent map capable to locks entries for which the value is in process of being computed.
+ * This map is intended for use as a cache, with a goal of avoiding to compute the same values twice.
+ * This implementation is thread-safe and supports concurrency.
+ * {@code Cache} is based on {@link ConcurrentHashMap} with the addition of three main capabilities:
+ *
+ * <ul>
+ *   <li>Lock an entry when its value is under computation in a thread.</li>
+ *   <li>Block other threads requesting the value of that particular entry until computation is completed.</li>
+ *   <li>Retain oldest values by soft or weak references instead of strong references.</li>
+ * </ul>
+ *
+ * The easiest way to use this class is to invoke {@link #computeIfAbsent computeIfAbsent(…)}
+ * or {@link #getOrCreate getOrCreate(…)} with lambda functions as below:
+ *
+ * {@preformat java
+ *     private final Cache<String,MyObject> cache = new Cache<String,MyObject>();
+ *
+ *     public MyObject getMyObject(String key) {
+ *         return cache.computeIfAbsent(key, (k) -> createMyObject(k));
+ *     }
+ * }
+ *
+ * Alternatively, one can handle explicitely the locks.
+ * This alternative sometime provides more flexibility, for example in exception handling.
+ * The steps are as below:
  *
  * <ol>
  *   <li>Check if the value is already available in the map.
@@ -53,34 +77,10 @@ import java.util.function.Supplier;
  *   <li>Otherwise compute the value, store the result and release the lock.</li>
  * </ol>
  *
- * The easiest way (except for exception handling) to use this class is to prepare a
- * {@link Callable} statement to be executed only if the object is not in the cache,
- * and to invoke the {@link #getOrCreate getOrCreate} method. Example:
- *
- * {@preformat java
- *     private final Cache<String,MyObject> cache = new Cache<String,MyObject>();
- *
- *     public MyObject getMyObject(final String key) throws MyCheckedException {
- *         try {
- *             return cache.getOrCreate(key, new Callable<MyObject>() {
- *                 MyObject call() throws FactoryException {
- *                     return createMyObject(key);
- *                 }
- *             });
- *         } catch (MyCheckedException | RuntimeException e) {
- *             throw e;
- *         } catch (Exception e) {
- *             throw new UndeclaredThrowableException(e);
- *         }
- *     }
- * }
- *
- * An alternative is to perform explicitly all the steps enumerated above. This alternative
- * avoid the creation of a temporary {@code Callable} statement which may never be executed,
- * and avoid the exception handling due to the {@code throws Exception} clause. Note that the
- * call to {@link Handler#putAndUnlock putAndUnlock} <strong>must</strong> be in the {@code finally}
- * block of a {@code try} block beginning immediately after the call to {@link #lock lock},
- * no matter what the result of the computation is (including {@code null}).
+ * Code example is shown below.
+ * Note that the call to {@link Handler#putAndUnlock putAndUnlock(…)} <strong>must</strong>
+ * be inside the {@code finally} block of a {@code try} block beginning immediately after the call
+ * to {@link #lock lock(…)}, no matter what the result of the computation is (including {@code null}).
  *
  * {@preformat java
  *     private final Cache<String,MyObject> cache = new Cache<String,MyObject>();
@@ -130,7 +130,8 @@ import java.util.function.Supplier;
  * <var>A</var>. If this rule is not meet, deadlock may occur randomly.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.4
+ * @author  Alexis Manin (Geomatys)
+ * @version 1.0
  *
  * @param <K>  the type of key objects.
  * @param <V>  the type of value objects.
@@ -138,11 +139,11 @@ import java.util.function.Supplier;
  * @since 0.3
  * @module
  */
-public class Cache<K,V> extends AbstractMap<K,V> {
+public class Cache<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V> {
     /**
-     * The map that contains the cached values. If a value is under the process of being
-     * calculated, then the value will be a temporary instance of {@link Handler}. The
-     * value may also be weak or soft {@link Reference} objects.
+     * The map that contains the cached values. If a value is in the process of being computed,
+     * then the value will be a temporary instance of {@link Handler}.
+     * The value may also be weak or soft {@link Reference} objects.
      */
     private final ConcurrentMap<K,Object> map;
 
@@ -224,11 +225,11 @@ public class Cache<K,V> extends Abstract
      */
     @Override
     public void clear() {
-        map.clear();
-        /*
-         * Do not update "costs" and "totalCost". Instead let adjustReferences(…)
-         * do its job, which needs to be done in a different thread.
-         */
+        synchronized (costs) {
+            map.clear();
+            costs.clear();
+            totalCost = 0;
+        }
     }
 
     /**
@@ -254,14 +255,126 @@ public class Cache<K,V> extends Abstract
     }
 
     /**
-     * Returns {@code true} if this map contains the specified key.
+     * Returns the value mapped to the given key in the cache, potentially waiting for computation to complete.
+     * This method is similar to {@link #peek(Object)} except that it blocks if the value is currently under
+     * computation in another thread.
      *
-     * @param  key  the key to check for existence.
-     * @return {@code true} if the given key still exist in this cache.
+     * @param  key  the key of the value to get.
+     * @return the value mapped to the given key, or {@code null} if none.
+     *
+     * @see #peek(Object)
+     * @see #containsKey(Object)
+     * @see #computeIfAbsent(Object, Function)
      */
     @Override
-    public boolean containsKey(final Object key) {
-        return map.containsKey(key);
+    public V get(final Object key) {
+        return valueOf(map.get(key));
+    }
+
+    /**
+     * Returns the value for the given key if it exists, or computes it otherwise.
+     * If a value already exists in the cache, then it is returned immediately.
+     * Otherwise the {@code creator.call()} method is invoked and its result is saved in this cache for future reuse.
+     *
+     * <div class="note"><b>Example:</b>
+     * the following example shows how this method can be used.
+     * In particular, it shows how to propagate {@code MyCheckedException}:
+     *
+     * {@preformat java
+     *     private final Cache<String,MyObject> cache = new Cache<String,MyObject>();
+     *
+     *     public MyObject getMyObject(final String key) throws MyCheckedException {
+     *         try {
+     *             return cache.getOrCreate(key, new Callable<MyObject>() {
+     *                 public MyObject call() throws MyCheckedException {
+     *                     return createMyObject(key);
+     *                 }
+     *             });
+     *         } catch (MyCheckedException | RuntimeException e) {
+     *             throw e;
+     *         } catch (Exception e) {
+     *             throw new UndeclaredThrowableException(e);
+     *         }
+     *     }
+     * }
+     * </div>
+     *
+     * This method is similar to {@link #computeIfAbsent(Object, Function)} except that it can propagate
+     * checked exceptions. If the {@code creator} function does not throw any checked exception, then
+     * invoking {@code computeIfAbsent(…)} is simpler.
+     *
+     * @param  key      the key for which to get the cached or created value.
+     * @param  creator  a method for creating a value, to be invoked only if no value are cached for the given key.
+     * @return the value for the given key, which may have been created as a result of this method call.
+     * @throws Exception if an exception occurred during the execution of {@code creator.call()}.
+     *
+     * @see #get(Object)
+     * @see #peek(Object)
+     * @see #computeIfAbsent(Object, Function)
+     */
+    public V getOrCreate(final K key, final Callable<? extends V> creator) throws Exception {
+        V value = peek(key);
+        if (value == null) {
+            final Handler<V> handler = lock(key);
+            try {
+                value = handler.peek();
+                if (value == null) {
+                    value = creator.call();
+                }
+            } finally {
+                handler.putAndUnlock(value);
+            }
+        }
+        return value;
+    }
+
+    /**
+     * Returns the value for the given key if it exists, or computes it otherwise.
+     * If a value already exists in the cache, then it is returned immediately.
+     * Otherwise the {@code creator.apply(Object)} method is invoked and its result
+     * is saved in this cache for future reuse.
+     *
+     * <div class="note"><b>Example:</b>
+     * below is the same code than {@link #getOrCreate(Object, Callable)} example,
+     * but without the need for any checked exception handling:
+     *
+     * {@preformat java
+     *     private final Cache<String,MyObject> cache = new Cache<String,MyObject>();
+     *
+     *     public MyObject getMyObject(final String key) {
+     *         return cache.computeIfAbsent(key, (k) -> createMyObject(k));
+     *     }
+     * }
+     * </div>
+     *
+     * This method is similar to {@link #getOrCreate(Object, Callable)}, but without checked exceptions.
+     *
+     * @param  key      the key for which to get the cached or created value.
+     * @param  creator  a method for creating a value, to be invoked only if no value are cached for the given key.
+     * @return the value already mapped to the key, or the newly computed value.
+     *
+     * @since 1.0
+     *
+     * @see #peek(Object)
+     * @see #containsKey(Object)
+     * @see #getOrCreate(Object, Callable)
+     * @see #computeIfPresent(Object, BiFunction)
+     */
+    @Override
+    public V computeIfAbsent(final K key, final Function<? super K, ? extends V> creator) {
+        V value = peek(key);
+        if (value == null) {
+            final Handler<V> handler = lock(key);
+            try {
+                value = handler.peek();
+                if (value == null) {
+                    value = creator.apply(key);
+                }
+            } finally {
+                handler.putAndUnlock(value);
+            }
+        }
+        return value;
     }
 
     /**
@@ -273,7 +386,19 @@ public class Cache<K,V> extends Abstract
     }
 
     /**
+     * Ensures that the given value is not an instance of a reserved type.
+     */
+    private static void ensureValidType(final Object value) throws IllegalArgumentException {
+        if (isReservedType(value)) {
+            throw new IllegalArgumentException(Errors.format(
+                    Errors.Keys.IllegalArgumentClass_2, "value", value.getClass()));
+        }
+    }
+
+    /**
      * Returns the value of the given object, unwrapping it if possible.
+     * If the value is under computation in another thread, this method
+     * will block until the computation is completed.
      */
     @SuppressWarnings("unchecked")
     private static <V> V valueOf(final Object value) {
@@ -291,76 +416,362 @@ public class Cache<K,V> extends Abstract
     }
 
     /**
-     * Puts the given value in cache.
+     * Returns the value of the given object if it is not under computation.
+     * This method is similar to {@link #valueOf(Object)} except that it does
+     * not block if the value is under computation.
+     */
+    @SuppressWarnings("unchecked")
+    private static <V> V immediateValueOf(final Object value) {
+        if (value instanceof Reference<?>) {
+            return ((Reference<V>) value).get();
+        }
+        if (value instanceof Handler<?>) {
+            return null;
+        }
+        return (V) value;
+    }
+
+    /**
+     * Notifies this {@code Cache} instance that an entry has changed. This methods adjusts
+     * cost calculation. This may cause some strong references to become weak references.
+     *
+     * @param  key    key of the entry that changed.
+     * @param  value  the new value. May be {@code null}.
+     */
+    final void notifyChange(final K key, final V value) {
+        DelayedExecutor.schedule(new Strong(key, value));
+    }
+
+    /**
+     * If no value is already mapped and no value is under computation for the given key, puts the given value
+     * in the cache. Otherwise returns the current value (potentially blocking until the computation finishes).
+     * A null {@code value} argument is equivalent to a no-op. Otherwise a {@code null} return value means that
+     * the given {@code value} has been stored in the {@code Cache}.
+     *
+     * @param  key    the key to associate with a value.
+     * @param  value  the value to associate with the given key if no value already exists, or {@code null}.
+     * @return the existing value mapped to the given key, or {@code null} if none existed before this method call.
+     *
+     * @see #get(Object)
+     * @see #computeIfAbsent(Object, Function)
      *
-     * @param  key    the key for which to set a value.
-     * @param  value  the value to store.
-     * @return the value previously stored at the given key, or {@code null} if none.
+     * @since 1.0
+     */
+    @Override
+    public V putIfAbsent(final K key, final V value) {
+        if (value == null) {
+            return null;
+        }
+        ensureValidType(value);
+        final Object previous = map.putIfAbsent(key, value);
+        if (previous == null) {
+            // A non-null value means that 'putIfAbsent' did nothing.
+            notifyChange(key, value);
+        }
+        return valueOf(previous);
+    }
+
+    /**
+     * Puts the given value in cache and immediately returns the old value.
+     * A null {@code value} argument removes the entry. If a different value is under computation in another thread,
+     * then the other thread may fail with an {@link IllegalStateException} unless {@link #isKeyCollisionAllowed()}
+     * returns {@code true}. For more safety, consider using {@link #putIfAbsent putIfAbsent(…)} instead.
+     *
+     * @param  key    the key to associate with a value.
+     * @param  value  the value to associate with the given key, or {@code null} for removing the mapping.
+     * @return the value previously mapped to the given key, or {@code null} if no value existed before this
+     *         method call or if the value was under computation in another thread.
+     *
+     * @see #get(Object)
+     * @see #putIfAbsent(Object, Object)
      */
     @Override
     public V put(final K key, final V value) {
-        if (isReservedType(value)) {
-            throw new IllegalArgumentException(Errors.format(
-                    Errors.Keys.IllegalArgumentClass_2, "value", value.getClass()));
+        ensureValidType(value);
+        final Object previous = (value != null) ? map.put(key, value) : map.remove(key);
+        if (previous != value) {
+            notifyChange(key, value);
+        }
+        return immediateValueOf(previous);
+    }
+
+    /**
+     * If the given key is mapped to any value, replaces that value with the given new value.
+     * Otherwise does nothing. A null {@code value} argument removes the entry.
+     * If a different value is under computation in another thread, then the other thread may fail with
+     * an {@link IllegalStateException} unless {@link #isKeyCollisionAllowed()} returns {@code true}.
+     *
+     * @param  key    key of the value to replace.
+     * @param  value  the new value to use in replacement of the previous one, or {@code null} for removing the mapping.
+     * @return the value previously mapped to the given key, or {@code null} if no value existed before this
+     *         method call or if the value was under computation in another thread.
+     *
+     * @see #replace(Object, Object, Object)
+     *
+     * @since 1.0
+     */
+    @Override
+    public V replace(final K key, final V value) {
+        ensureValidType(value);
+        final Object previous = (value != null) ? map.replace(key, value) : map.remove(key);
+        if (previous != null) {
+            // A null value means that 'replace' did nothing.
+            notifyChange(key, value);
         }
-        final Object previous;
-        if (value != null) {
-            previous = map.put(key, value);
-            DelayedExecutor.schedule(new Strong(key, value));
+        return immediateValueOf(previous);
+    }
+
+    /**
+     * If the given key is mapped to the given old value, replaces that value with the given new value.
+     * Otherwise does nothing. A null {@code value} argument removes the entry if the condition matches.
+     * If a value is under computation in another thread, then this method unconditionally returns {@code false}.
+     *
+     * @param  key       key of the value to replace.
+     * @param  oldValue  previous value expected to be mapped to the given key.
+     * @param  newValue  the new value to put if the condition matches, or {@code null} for removing the mapping.
+     * @return {@code true} if the value has been replaced, {@code false} otherwise.
+     *
+     * @since 1.0
+     */
+    @Override
+    public boolean replace(final K key, final V oldValue, final V newValue) {
+        ensureValidType(newValue);
+        final boolean done;
+        if (oldValue != null) {
+            done = (newValue != null) ? map.replace(key, oldValue, newValue) : map.remove(key, oldValue);
         } else {
-            previous = map.remove(key);
+            done = (newValue != null) && map.putIfAbsent(key, newValue) == null;
         }
-        return Cache.<V>valueOf(previous);
+        if (done) {
+            notifyChange(key, newValue);
+        }
+        return done;
     }
 
     /**
-     * Removes the value associated to the given key in the cache.
+     * Iterates over all entries in the cache and replaces their value with the one provided by the given function.
+     * If the function throws an exception, the iteration is stopped and the exception is propagated. If any value
+     * is under computation in other threads, then the iteration will block on that entry until its computation is
+     * completed.
      *
-     * @param  key  the key of the value to removed.
-     * @return the value that were associated to the given key, or {@code null} if none.
+     * @param  remapping  the function computing new values from the old ones.
+     *
+     * @since 1.0
      */
     @Override
-    public V remove(final Object key) {
-        return Cache.<V>valueOf(map.remove(key));
+    public void replaceAll(final BiFunction<? super K, ? super V, ? extends V> remapping) {
+        final ReplaceAdapter adapter = new ReplaceAdapter(remapping);
+        map.replaceAll(adapter);
+        Deferred.notifyChanges(this, adapter.changes);
     }
 
     /**
-     * Returns the value associated to the given key in the cache. This method is similar to
-     * {@link #peek} except that it blocks if the value is currently under computation in an
-     * other thread.
+     * Replaces the value mapped to the given key by a new value computed from the old value.
+     * If a value for the given key is under computation in another thread, then this method
+     * blocks until that computation is completed. This is equivalent to the work performed
+     * by {@link #replaceAll replaceAll(…)} but on a single entry.
      *
-     * @param  key  the key of the value to get.
-     * @return the value associated to the given key, or {@code null} if none.
+     * @param  key        key of the value to replace.
+     * @param  remapping  the function computing new values from the old ones.
+     * @return the new value associated with the given key.
+     *
+     * @see #computeIfAbsent(Object, Function)
+     *
+     * @since 1.0
      */
     @Override
-    public V get(final Object key) {
-        return Cache.<V>valueOf(map.get(key));
+    public V computeIfPresent(final K key, final BiFunction<? super K, ? super V, ? extends V> remapping) {
+        final ReplaceAdapter adapter = new ReplaceAdapter(remapping);
+        final Object value = map.computeIfPresent(key, adapter);
+        Deferred.notifyChanges(this, adapter.changes);
+        return valueOf(value);
     }
 
     /**
-     * Returns the value for the given key. If a value already exists in the cache, then it
-     * is returned immediately. Otherwise the {@code creator.call()} method is invoked and
-     * its result is saved in this cache for future reuse.
+     * Replaces the value mapped to the given key by a new value computed from the old value.
+     * If there is no value for the given key, then the "old value" is taken as {@code null}.
+     * If a value for the given key is under computation in another thread, then this method
+     * blocks until that computation is completed. This method is equivalent to
+     * {@link #computeIfPresent computeIfPresent(…)} except that a new value will be computed
+     * even if no value existed for the key before this method call.
      *
-     * @param  key      the key for which to get the cached or created value.
-     * @param  creator  a method for creating a value, to be invoked only if no value are cached for the given key.
-     * @return the value for the given key, which may have been created as a result of this method call.
-     * @throws Exception if an exception occurred during the execution of {@code creator.call()}.
+     * @param  key        key of the value to replace.
+     * @param  remapping  the function computing new values from the old ones, or from a {@code null} value.
+     * @return the new value associated with the given key.
+     *
+     * @see #computeIfAbsent(Object, Function)
+     *
+     * @since 1.0
      */
-    public V getOrCreate(final K key, final Callable<? extends V> creator) throws Exception {
-        V value = peek(key);
-        if (value == null) {
-            final Handler<V> handler = lock(key);
-            try {
-                value = handler.peek();
-                if (value == null) {
-                    value = creator.call();
+    @Override
+    public V compute(final K key, final BiFunction<? super K, ? super V, ? extends V> remapping) {
+        final ReplaceAdapter adapter = new ReplaceAdapter(remapping);
+        final Object value = map.compute(key, adapter);
+        Deferred.notifyChanges(this, adapter.changes);
+        return valueOf(value);
+    }
+
+    /**
+     * Maps the given value to the given key if no mapping existed before this method call,
+     * or computes a new value otherwise. If a value for the given key is under computation
+     * in another thread, then this method blocks until that computation is completed.
+     *
+     * @param  key        key of the value to replace.
+     * @param  value      the value to associate with the given key if no value already exists, or {@code null}.
+     * @param  remapping  the function computing a new value by merging the exiting value
+     *                    with the {@code value} argument given to this method.
+     * @return the new value associated with the given key.
+     *
+     * @since 1.0
+     */
+    @Override
+    public V merge(final K key, final V value, final BiFunction<? super V, ? super V, ? extends V> remapping) {
+        ensureValidType(value);
+
+        /** Similar to {@link Cache.ReplaceAdapter}, but adapted to the merge case. */
+        final class Adapter implements BiFunction<Object,Object,Object> {
+            /** Forwards {@link Cache#map} calls to the user-provided function. */
+            @Override public Object apply(final Object oldValue, final Object givenValue) {
+                final V toReplace = valueOf(oldValue);
+                final V newValue = remapping.apply(toReplace, valueOf(givenValue));
+                ensureValidType(newValue);
+                if (newValue != toReplace) {
+                    changes = new Deferred<>(key, newValue, changes);
                 }
-            } finally {
-                handler.putAndUnlock(value);
+                return newValue;
+            }
+
+            /** The new values for which to send notifications. */
+            Deferred<K,V> changes;
+        }
+        final Adapter adapter = new Adapter();
+        final Object newValue = map.merge(key, value, adapter);
+        Deferred.notifyChanges(this, adapter.changes);
+        return valueOf(newValue);
+    }
+
+    /**
+     * A callback for {@link Cache#map} which forwards the calls to the {@code remapping} function provided by user.
+     * Before to forward the calls, {@code ReplaceAdapter} verifies if the value is under computation. If yes, then
+     * this adapter block until the value is available for forwarding it to the user.
+     */
+    private final class ReplaceAdapter implements BiFunction<K,Object,Object> {
+        /** The new values for which to send notifications. */
+        private Deferred<K,V> changes;
+
+        /** The user-providing function. */
+        private final BiFunction<? super K, ? super V, ? extends V> remapping;
+
+        /** Creates a new adapter for the given user-provided function. */
+        ReplaceAdapter(final BiFunction<? super K, ? super V, ? extends V> remapping) {
+            this.remapping = remapping;
+        }
+
+        /** Forwards {@link Cache#map} calls to the user-provided function. */
+        @Override public Object apply(final K key, final Object oldValue) {
+            final V toReplace = valueOf(oldValue);
+            final V newValue = remapping.apply(key, toReplace);
+            ensureValidType(newValue);
+            if (newValue != toReplace) {
+                changes = new Deferred<>(key, newValue, changes);
+            }
+            return newValue;
+        }
+    }
+
+    /**
+     * Key-value pairs of new entries created during {@link Cache.ReplaceAdapter} execution, as a chained list.
+     * Calls to {@link Cache#notifyChange(Object, Object)} for those entries need to be deferred until operation
+     * on {@link Cache#map} completed because {@link Cache#adjustReferences(Object, Object)} needs the new values
+     * to be present in the map.
+     */
+    private static final class Deferred<K,V> {
+        private final K key;
+        private final V value;
+        private final Deferred<K,V> next;
+
+        /** Creates a new notification to be sent after the {@link Cache#map} operation completed. */
+        Deferred(final K key, final V value, final Deferred<K,V> next) {
+            this.key   = key;
+            this.value = value;
+            this.next  = next;
+        }
+
+        /** Sends all deferred notifications, starting with the given one. */
+        static <K,V> void notifyChanges(final Cache<K,V> cache, Deferred<K,V> entry) {
+            while (entry != null) {
+                cache.notifyChange(entry.key, entry.value);
+                entry = entry.next;
             }
         }
-        return value;
+    }
+
+    /**
+     * Removes the value mapped to the given key in the cache. If a value is under computation in another thread,
+     * then the other thread may fail with an {@link IllegalStateException} unless {@link #isKeyCollisionAllowed()}
+     * returns {@code true}. For more safety, consider using {@link #remove(Object, Object)} instead.
+     *
+     * @param  key  the key of the value to removed.
+     * @return the value previously mapped to the given key, or {@code null} if no value existed before this
+     *         method call or if the value was under computation in another thread.
+     *
+     * @see #get(Object)
+     * @see #remove(Object, Object)
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public V remove(final Object key) {
+        final Object oldValue = map.remove(key);
+        if (oldValue != null) {
+            notifyChange((K) key, null);
+        }
+        return immediateValueOf(oldValue);
+    }
+
+    /**
+     * If the given key is mapped to the given old value, removes that value. Otherwise does nothing.
+     * If a value is under computation in another thread, then this method unconditionally returns {@code false}.
+     *
+     * @param  key      key of the value to remove.
+     * @param  oldValue previous value expected to be mapped to the given key.
+     * @return {@code true} if the value has been removed, {@code false} otherwise.
+     *
+     * @see #get(Object)
+     *
+     * @since 1.0
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public boolean remove(final Object key, final Object oldValue) {
+        final boolean done = map.remove(key, oldValue);
+        if (done) {
+            notifyChange((K) key, null);
+        }
+        return done;
+    }
+
+    /**
+     * Returns {@code true} if this map contains the specified key.
+     * If the value is under computation in another thread, this method returns {@code true}
+     * without waiting for the computation result. This behavior is consistent with other
+     * {@code Map} methods in the following ways:
+     *
+     * <ul>
+     *   <li>{@link #get(Object)} blocks until the computation is completed.</li>
+     *   <li>{@link #put(Object, Object)} returns {@code null} for values under computation,
+     *       i.e. behaves as if keys are temporarily mapped to the {@code null} value until
+     *       the computation is completed.</li>
+     * </ul>
+     *
+     * @param  key  the key to check for existence.
+     * @return {@code true} if the given key is mapped to an existing value or a value under computation.
+     *
+     * @see #get(Object)
+     * @see #peek(Object)
+     */
+    @Override
+    public boolean containsKey(final Object key) {
+        return map.containsKey(key);
     }
 
     /**
@@ -370,6 +781,9 @@ public class Cache<K,V> extends Abstract
      *
      * @param  key  the key for which to get the cached value.
      * @return the cached value for the given key, or {@code null} if there is none.
+     *
+     * @see #get(Object)
+     * @see #lock(Object)
      */
     public V peek(final K key) {
         final Object value = map.get(key);
@@ -386,7 +800,7 @@ public class Cache<K,V> extends Abstract
             final V result = ref.get();
             if (result != null && map.replace(key, ref, result)) {
                 ref.clear();                        // Prevents the reference from being enqueued.
-                DelayedExecutor.schedule(new Strong(key, result));
+                notifyChange(key, result);
             }
             return result;
         }
@@ -397,9 +811,9 @@ public class Cache<K,V> extends Abstract
 
     /**
      * Invoked from the a background thread after a {@linkplain WeakReference weak}
-     * or {@linkplain SoftReference soft} reference has been replaced by a strong one. It will
-     * looks for older strong references to replace by weak references so that the total cost
-     * stay below the cost limit.
+     * or {@linkplain SoftReference soft} reference has been replaced by a strong one.
+     * It will looks for older strong references to replace by weak references so that
+     * the total cost stay below the cost limit.
      */
     private final class Strong extends DelayedRunnable.Immediate {
         private final K key;
@@ -479,7 +893,7 @@ public class Cache<K,V> extends Abstract
                      */
                     if (map.replace(key, ref, result)) {
                         ref.clear();                        // Prevents the reference from being enqueued.
-                        DelayedExecutor.schedule(new Strong(key, result));
+                        notifyChange(key, result);
                     }
                     return new Simple<>(result);
                 }
@@ -530,7 +944,7 @@ public class Cache<K,V> extends Abstract
             return work.new Wait();
         }
         /*
-         * A calculation has already been completed. Returns a wrapper
+         * A computation has already been completed. Returns a wrapper
          * which will just return the result without any processing.
          */
         assert !isReservedType(value) : value;
@@ -540,9 +954,8 @@ public class Cache<K,V> extends Abstract
     }
 
     /**
-     * The handler returned by {@link Cache#lock}, to be used for unlocking and storing the
-     * result. This handler should be used as below (note the {@code try} … {@code catch}
-     * blocks, which are <strong>mandatory</strong>):
+     * The handler returned by {@link Cache#lock}, to be used for unlocking and storing the result.
+     * This handler should be used as below (the {@code try} … {@code finally} statements are important):
      *
      * {@preformat java
      *     Value V = null;
@@ -593,12 +1006,12 @@ public class Cache<K,V> extends Abstract
 
     /**
      * A simple handler implementation wrapping an existing value. This implementation
-     * is used when the value has been fully calculated in an other thread before this
+     * is used when the value has been fully computed in an other thread before this
      * thread could start its work.
      */
     private final class Simple<V> implements Handler<V> {
         /**
-         * The result calculated in an other thread.
+         * The result computed in an other thread.
          */
         private final V value;
 
@@ -610,7 +1023,7 @@ public class Cache<K,V> extends Abstract
         }
 
         /**
-         * Returns the calculated value.
+         * Returns the computed value.
          */
         @Override
         public V peek() {
@@ -663,8 +1076,8 @@ public class Cache<K,V> extends Abstract
         }
 
         /**
-         * Waits for the completion of the value computation and returns this result. This
-         * method should be invoked only from an other thread than the one doing the calculation.
+         * Waits for the completion of the value computation and returns this result.
+         * This method should be invoked only from another thread than the one doing the computation.
          */
         @Override
         public V get() {
@@ -713,6 +1126,8 @@ public class Cache<K,V> extends Abstract
             }
             if (done) {
                 DelayedExecutor.schedule(this);
+            } else if (!isKeyCollisionAllowed()) {
+                throw new IllegalStateException(Errors.format(Errors.Keys.KeyCollision_1, key));
             }
         }
 
@@ -766,8 +1181,8 @@ public class Cache<K,V> extends Abstract
      * than the cost limit, then oldest strong references are replaced by weak references.
      */
     final void adjustReferences(final K key, final V value) {
-        int cost = cost(value);
-        synchronized (costs) {                          // Should not be needed, but done as a safety.
+        int cost = (value != null) ? cost(value) : 0;
+        synchronized (costs) {
             final Integer old = costs.put(key, cost);
             if (old != null) {
                 cost -= old;
@@ -779,7 +1194,7 @@ public class Cache<K,V> extends Abstract
                      * Converts the current entry from strong reference to weak/soft reference.
                      * We perform this conversion even if the entry is for the value just added
                      * to the cache, if it happen that the cost is higher than the maximal one.
-                     * That entry should not be garbage collected to early anyway because the
+                     * That entry should not be garbage collected too early anyway because the
                      * caller should still have a strong reference to the value he just created.
                      */
                     final Map.Entry<K,Integer> entry = it.next();

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -20,12 +20,21 @@ import java.util.Arrays;
 import java.util.AbstractList;
 import java.util.RandomAccess;
 import java.util.NoSuchElementException;
+import java.util.ConcurrentModificationException;
 import java.io.IOException;
 import java.io.Serializable;
 import java.io.ObjectOutputStream;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.ArgumentChecks;
 
+// Branch-dependent imports
+import java.util.Spliterator;
+import java.util.PrimitiveIterator;
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+import java.util.stream.IntStream;
+import java.util.stream.StreamSupport;
+
 
 /**
  * A list of unsigned integer values. This class packs the values in the minimal amount of bits
@@ -34,7 +43,8 @@ import org.apache.sis.util.ArgumentCheck
  * <p>This class is <strong>not</strong> thread-safe. Synchronizations (if wanted) are user's responsibility.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @author  Alexis Manin (Geomatys)
+ * @version 0.8
  *
  * @see org.apache.sis.math.Vector
  *
@@ -154,9 +164,12 @@ public class IntegerList extends Abstrac
      * the previous one, then the extra elements are initialized to 0.
      *
      * @param  size  the new size.
+     *
+     * @see #trimToSize()
      */
     public void resize(final int size) {
         ArgumentChecks.ensurePositive("size", size);
+        modCount++;
         if (size > this.size) {
             int base = this.size * bitCount;
             final int offset = base & OFFSET_MASK;
@@ -176,13 +189,14 @@ public class IntegerList extends Abstrac
 
     /**
      * Fills the list with the given value.
-     * Every existing values are overwritten from index 0 inclusive up to {@link #size} exclusive.
+     * Every existing values are overwritten from index 0 inclusive up to {@link #size()} exclusive.
      *
      * @param  value  the value to set.
      */
     @SuppressWarnings("fallthrough")
     public void fill(int value) {
         ArgumentChecks.ensureBetween("value", 0, mask, value);
+        modCount++;
         final long p;
         if (value == 0) {
             p = 0;                              // All bits set to 0.
@@ -210,6 +224,7 @@ public class IntegerList extends Abstrac
      */
     @Override
     public void clear() {
+        modCount++;
         size = 0;
     }
 
@@ -237,6 +252,7 @@ public class IntegerList extends Abstrac
      */
     public void addInt(final int value) throws IllegalArgumentException {
         ArgumentChecks.ensureBetween("value", 0, mask, value);
+        modCount++;
         final int last = size;
         final int length = length(++size);
         if (length > values.length) {
@@ -271,7 +287,8 @@ public class IntegerList extends Abstrac
 
     /**
      * Returns the element at the given index as the {@code int} primitive type.
-     * This argument does not check argument validity, since it is assumed already done.
+     * This argument does not check argument validity, since the verification is
+     * assumed already done.
      *
      * @param  index  the element index.
      * @return the value at the given index.
@@ -318,12 +335,14 @@ public class IntegerList extends Abstrac
     public void setInt(int index, int value) throws IndexOutOfBoundsException {
         ArgumentChecks.ensureValidIndex(size, index);
         ArgumentChecks.ensureBetween("value", 0, mask, value);
+        modCount++;
         setUnchecked(index, value);
     }
 
     /**
      * Sets the element at the given index as the {@code int} primitive type.
-     * This argument does not check argument validity, since it is assumed already done.
+     * This argument does not check argument validity, since the verification
+     * is assumed already done.
      *
      * @param  index  the element index.
      * @param  value  the value at the given index.
@@ -352,6 +371,7 @@ public class IntegerList extends Abstrac
     @Override
     public Integer remove(final int index) throws IndexOutOfBoundsException {
         final Integer old = get(index);
+        modCount++;
         removeRange(index, index+1);
         return old;
     }
@@ -364,6 +384,7 @@ public class IntegerList extends Abstrac
      */
     public int removeLast() throws NoSuchElementException {
         if (size != 0) {
+            modCount++;
             return getUnchecked(--size);
         }
         throw new NoSuchElementException();
@@ -423,7 +444,233 @@ public class IntegerList extends Abstrac
     }
 
     /**
+     * Returns an iterator over the elements in this list in increasing index order.
+     * The iterator is <cite>fail-fast</cite> and supports the remove operation.
+     *
+     * @return iterator over the integer values in this list.
+     *
+     * @since 0.8-jdk8
+     */
+    @Override
+    public PrimitiveIterator.OfInt iterator() {
+        return new PrimitiveSpliterator();
+    }
+
+    /**
+     * Returns an spliterator over the elements in this list in increasing index order.
+     * The iterator is <cite>fail-fast</cite>.
+     *
+     * @return spliterator over the integer values in this list.
+     *
+     * @since 0.8-jdk8
+     */
+    @Override
+    public Spliterator.OfInt spliterator() {
+        return new PrimitiveSpliterator();
+    }
+
+    /**
+     * Returns a stream of integers with this {@code IntegerList} as its source.
+     * This method is similar to {@link #stream()}, but does not box the values.
+     * The returned stream is <cite>fail-fast</cite>, meaning that any modification to the list
+     * while using the stream will cause a {@link ConcurrentModificationException} to be thrown.
+     *
+     * <p>The default implementation creates a parallel or sequential stream from {@link #spliterator()}.</p>
+     *
+     * @param parallel  {@code true} for a parallel stream, or {@code false} for a sequential stream.
+     * @return a stream of values in this list as primitive types.
+     *
+     * @since 0.8-jdk8
+     */
+    public IntStream stream(boolean parallel) {
+        return StreamSupport.intStream(spliterator(), parallel);
+    }
+
+    /**
+     * Same as {@link #spliterator()}, but without value boxing.
+     * This spliterator provides a fail-fast way to traverse list content, which means
+     * that any alteration to the list content causes a failure of the advance operation
+     * with a {@link ConcurrentModificationException}.
+     *
+     * <p>This implementation opportunistically provides an iterator implementation on
+     * integer values too, but only one of the {@code Iterator} or {@code Spliterator}
+     * API should be used on a given instance.</p>
+     */
+    private final class PrimitiveSpliterator implements Spliterator.OfInt, PrimitiveIterator.OfInt {
+        /**
+         * Index after the last element returned by this spliterator. This is initially {@link IntegerList#size},
+         * but may be set to a smaller value by call to {@link #trySplit()}.
+         */
+        private int stopAt;
+
+        /**
+         * Index of the next element to be returned.
+         */
+        private int nextIndex;
+
+        /**
+         * The {@link IntegerList#modCount} value as iterator construction time.
+         * Used for detecting modification in the backing list during traversal.
+         */
+        private int expectedModCount;
+
+        /**
+         * Index of the last elements removed by a {@link #remove()} operation.
+         * This is used for checking that {@code remove()} is not invoked twice
+         * before the next advance.
+         */
+        private int lastRemove;
+
+        /**
+         * Creates a new iterator for the whole content of the backing list.
+         */
+        PrimitiveSpliterator() {
+            expectedModCount = modCount;
+            stopAt           = size;
+        }
+
+        /**
+         * Creates the prefix spliterator in a call to {@link #trySplit()}.
+         *
+         * @param  suffix   the spliterator which will continue iteration after this spliterator.
+         * @param  startAt  index of the first element to be returned by this prefix spliterator.
+         */
+        private PrimitiveSpliterator(final PrimitiveSpliterator suffix, final int startAt) {
+            expectedModCount = suffix.expectedModCount;
+            stopAt           = suffix.nextIndex;
+            nextIndex        = startAt;
+        }
+
+        /**
+         * Declares that this split iterator does not return null elements, that all elements are
+         * traversed in a fixed order (which is increasing index values) and that {@link #size()}
+         * represents an exact count of elements.
+         */
+        @Override
+        public int characteristics() {
+            return NONNULL | ORDERED | SIZED | SUBSIZED;
+        }
+
+        /**
+         * Returns the exact number of values to be encountered by a {@code forEachRemaining(…)} traversal.
+         */
+        @Override
+        public long estimateSize() {
+            return stopAt - nextIndex;
+        }
+
+        /**
+         * @todo for now, we keep it simple and forbid parallelism. In the future,
+         *       we could use an approach as the one in java standard array lists.
+         */
+        @Override
+        public Spliterator.OfInt trySplit() {
+            final int startAt = nextIndex;
+            final int halfSize = (stopAt - startAt) >>> 1;
+            if (halfSize > 1) {
+                nextIndex += halfSize;
+                return new PrimitiveSpliterator(this, startAt);
+            }
+            return null;
+        }
+
+        /**
+         * Returns {@code true} if there is one more value to return. This method
+         * also ensures that no alteration has happened on the backing list since
+         * the spliterator creation.
+         */
+        @Override
+        public boolean hasNext() {
+            if (modCount == expectedModCount) {
+                return nextIndex < stopAt;
+            } else {
+                throw new ConcurrentModificationException();
+            }
+        }
+
+        /**
+         * Returns the next integer values in iterator order.
+         */
+        @Override
+        public int nextInt() {
+            if (hasNext()) {
+                return getUnchecked(nextIndex++);
+            } else {
+                throw new NoSuchElementException();
+            }
+        }
+
+        /**
+         * If a remaining element exists, performs the given action on it and returns {@code true}.
+         * Otherwise returns {@code false}.
+         */
+        @Override
+        public boolean tryAdvance(IntConsumer action) {
+            final boolean canAdvance = hasNext();
+            if (canAdvance) {
+                action.accept(getUnchecked(nextIndex++));
+            }
+            return canAdvance;
+        }
+
+        /**
+         * Performs the given action on all remaining elements. This implementation
+         * is shared by both {@code Iterator} and {@code Spliterator} interfaces.
+         */
+        @Override
+        public void forEachRemaining(final IntConsumer action) {
+            while (hasNext()) {
+                action.accept(getUnchecked(nextIndex++));
+            }
+        }
+
+        /**
+         * Performs the given action on all remaining elements. This implementation
+         * is shared by both {@code Iterator} and {@code Spliterator} interfaces.
+         */
+        @Override
+        public void forEachRemaining(final Consumer<? super Integer> action) {
+            if (action instanceof IntConsumer) {
+                forEachRemaining((IntConsumer) action);
+            } else while (hasNext()) {
+                action.accept(getUnchecked(nextIndex++));
+            }
+        }
+
+        /**
+         * Removes the last element returned by {@link #nextInt()}.
+         */
+        @Override
+        public void remove() {
+            if (nextIndex < lastRemove || nextIndex > stopAt) {
+                throw new IllegalStateException();
+            }
+            if (modCount != expectedModCount) {
+                throw new ConcurrentModificationException();
+            }
+            expectedModCount = ++modCount;
+            removeRange(nextIndex - 1, nextIndex);
+            lastRemove = --nextIndex;
+            stopAt--;
+        }
+    }
+
+    /**
+     * Invokes {@link #trimToSize()} before serialization in order to make the stream more compact.
+     *
+     * @param  out  the output stream where to serialize this list.
+     * @throws IOException if an I/O error occurred while writing.
+     */
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        trimToSize();
+        out.defaultWriteObject();
+    }
+
+    /**
      * Trims the capacity of this list to be its current size.
+     *
+     * @see #size()
+     * @see #resize(int)
      */
     public void trimToSize() {
         values = ArraysExt.resize(values, length(size));
@@ -442,18 +689,8 @@ public class IntegerList extends Abstrac
         } catch (CloneNotSupportedException e) {
             throw new AssertionError(e);
         }
-        clone.values = clone.values.clone();
+        clone.values = Arrays.copyOf(values, length(size));
+        clone.modCount = 0;
         return clone;
     }
-
-    /**
-     * Invokes {@link #trimToSize()} before serialization in order to make the stream more compact.
-     *
-     * @param  out  the output stream where to serialize this list.
-     * @throws IOException if an I/O error occurred while writing.
-     */
-    private void writeObject(final ObjectOutputStream out) throws IOException {
-        trimToSize();
-        out.defaultWriteObject();
-    }
 }

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/RangeSet.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/RangeSet.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/RangeSet.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/collection/RangeSet.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -1133,10 +1133,8 @@ public class RangeSet<E extends Comparab
             if (object instanceof Range<?>) {
                 @SuppressWarnings("unchecked")              // Type will actally be checked on the line after.
                 final Range<E> range = (Range<E>) object;
-                if (range.getElementType() == elementType) {
-                    if (!subRange.contains(range)) {
-                        return false;
-                    }
+                if (range.getElementType() == elementType && !subRange.contains(range)) {
+                    return false;
                 }
             }
             return RangeSet.this.contains(object);

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultLocalName.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultLocalName.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultLocalName.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultLocalName.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -91,10 +91,25 @@ public class DefaultLocalName extends Ab
      * The name, either as a {@link String} or an {@link InternationalString}.
      *
      * <div class="section">Note on JAXB annotation</div>
-     * The {@link XmlElement} annotation applied here is appropriate for subclasses only ({@link DefaultTypeName}
-     * and {@link DefaultMemberName}). It is <strong>not</strong> appropriate when (un)marshalling directly this
-     * {@code DefaultLocalName} class. In this later case, we will rather rely on the {@link String} conversion
-     * performed by {@link org.apache.sis.internal.jaxb.gco.GO_GenericName}.
+     * The {@link XmlElement} annotation applied here is appropriate for {@code TypeName} and {@code MemberName}
+     * subtypes only. It is <strong>not</strong> appropriate when (un)marshalling directly a {@code LocalName}.
+     * The distinction between the two cases is done by {@link org.apache.sis.internal.jaxb.gco.GO_LocalName},
+     * which replace the {@code LocalName} instance by an internal {@code NameValue} object (so the XML element
+     * declared here is never marshalled). Example:
+     *
+     * {@preformat xml
+     *   <gco:LocalName codeSpace=\"A code space\">A name in a scope</gco:LocalName>
+     * }
+     *
+     * versus
+     *
+     * {@preformat xml
+     *   <gco:TypeName>
+     *     <gco:aName>
+     *       <gco:CharacterString>An other local name</gco:CharacterString>
+     *     </gco:aName>
+     *   </gco:TypeName>
+     * }
      */
     @XmlJavaTypeAdapter(CharSequenceAdapter.class)
     @XmlElement(name = "aName", namespace = Namespaces.GCO)

Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java?rev=1817597&r1=1817596&r2=1817597&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java [UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java [UTF-8] Sat Dec  9 10:57:44 2017
@@ -104,19 +104,25 @@ public class DefaultRecord implements Re
      *
      * @since 0.8
      */
+    @SuppressWarnings("SuspiciousSystemArraycopy")
     public DefaultRecord(final Record record) {
         this(record.getRecordType());
-        for (final Map.Entry<MemberName,Integer> entry : definition.memberIndices().entrySet()) {
-            final MemberName name = entry.getKey();
-            final Object value = record.locate(name);
-            if (value != null) {
-                final int index = entry.getValue();
-                final Class<?> valueClass = definition.getValueClass(index);
-                if (valueClass != null && !valueClass.isInstance(value)) {
-                    throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyValueClass_3,
-                            name, valueClass, value.getClass()));
+        if (record instanceof DefaultRecord) {
+            final Object source = ((DefaultRecord) record).values;
+            System.arraycopy(source, 0, values, 0, Array.getLength(source));
+        } else {
+            for (final Map.Entry<MemberName,Integer> entry : definition.memberIndices().entrySet()) {
+                final MemberName name = entry.getKey();
+                final Object value = record.locate(name);
+                if (value != null) {
+                    final int index = entry.getValue();
+                    final Class<?> valueClass = definition.getValueClass(index);
+                    if (valueClass != null && !valueClass.isInstance(value)) {
+                        throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyValueClass_3,
+                                name, valueClass, value.getClass()));
+                    }
+                    Array.set(values, index, value);
                 }
-                Array.set(values, index, value);
             }
         }
     }



Mime
View raw message