sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1720014 [1/2] - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/ sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ sis-utility/src/main/java/org/apache/sis/util/c...
Date Mon, 14 Dec 2015 21:09:23 GMT
Author: desruisseaux
Date: Mon Dec 14 21:09:23 2015
New Revision: 1720014

URL: http://svn.apache.org/viewvc?rev=1720014&view=rev
Log:
Begin the port of EPSGFactory.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AxisName.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java   (with props)
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java   (with props)
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/collection/IntegerListTest.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DatumShiftTransform.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java?rev=1720014&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java [UTF-8] Mon Dec 14 21:09:23 2015
@@ -0,0 +1,391 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.factory.sql;
+
+import java.util.LinkedHashMap;
+import java.io.Serializable;
+import java.io.ObjectStreamException;
+import java.sql.ResultSet;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.PreparedStatement;
+import java.sql.Statement;
+import java.lang.ref.Reference;
+import org.opengis.referencing.operation.Projection;
+import org.opengis.util.NoSuchIdentifierException;
+import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.internal.util.AbstractMap;
+import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.util.collection.IntegerList;
+import org.apache.sis.util.logging.Logging;
+
+
+/**
+ * A map of EPSG authority codes as keys and object names as values.
+ * This map requires a living connection to the EPSG database.
+ *
+ * <p>Serialization of this class stores a copy of all authority codes.
+ * The serialization does not preserve any connection to the database.</p>
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+final class AuthorityCodes extends AbstractMap<String,String> implements Serializable, AutoCloseable {
+    /**
+     * For compatibility with different versions.
+     */
+    private static final long serialVersionUID = 6118171679321975503L;
+
+    /**
+     * Highest code value (inclusive) that this {@code AuthorityCodes} support during iterations.
+     * This is based on the upper value of the highest range of codes once used by EPSG.
+     */
+    private static final int MAX_CODE = 69999999;
+
+    /**
+     * Index in the {@link #sql} and {@link #statements} arrays.
+     */
+    private static final int ALL = 0, ONE = 1;
+
+    /**
+     * The factory which is the owner of this map. One purpose of this field is to prevent garbage collection
+     * of that factory as long as this map is in use. This is required because {@link EPSGFactory#finalize()}
+     * closes the JDBC connections.
+     */
+    private final transient EPSGFactory factory;
+
+    /**
+     * The interface of referencing objects for which this map contains the code.
+     * May be a super-interface of the type specified to the constructor.
+     */
+    final Class<?> type;
+
+    /**
+     * {@code true} if {@link #type} is assignable to {@link Projection}.
+     */
+    private final transient boolean isProjection;
+
+    /**
+     * The SQL commands that this {@code AuthorityCodes} may need to execute.
+     * In this array:
+     *
+     * <ul>
+     *   <li>{@code sql[ALL]} is a statement for querying all codes.</li>
+     *   <li>{@code sql[ONE]} is a statement for querying a single code.
+     *       This statement is similar to {@code sql[ALL]} with the addition of a {@code WHERE} clause.</li>
+     * </ul>
+     */
+    private final transient String[] sql = new String[2];
+
+    /**
+     * The JDBC statements for the SQL commands in the {@link #sql} array, created when first needed.
+     * All usages of those statements shall be synchronized on the {@linkplain #factory}.
+     * This array will also be stored in {@link CloseableReference} for closing the statements
+     * when the garbage collector detected that {@code AuthorityCodes} is no longer in use.
+     */
+    private final transient Statement[] statements = new Statement[2];
+
+    /**
+     * The result of {@code statements[ALL]}, created only if requested.
+     * The codes will be queried at most once and cached in the {@link #codes} list.
+     *
+     * <p>Note that if this result set is not closed explicitely, it will be closed implicitly when
+     * {@code statements[ALL]} will be closed. This is because JDBC specification said that closing
+     * a statement also close its result set.</p>
+     */
+    private transient ResultSet results;
+
+    /**
+     * A cache of integer codes. Created only if the user wants to iterate over all codes or asked for the map size.
+     */
+    private transient IntegerList codes;
+
+    /**
+     * Creates a new map of authority codes for the specified type.
+     *
+     * @param  connection The connection to the EPSG database.
+     * @param  table      The table to query.
+     * @param  type       The type to query.
+     * @param  factory    The factory originator.
+     */
+    AuthorityCodes(final Connection connection, final TableInfo table, final Class<?> type, final EPSGFactory factory) {
+        this.factory = factory;
+        /*
+         * Build the SQL query for fetching the codes of all object. It is of the form:
+         *
+         *     SELECT code FROM table ORDER BY code;
+         */
+        final StringBuilder buffer = new StringBuilder(100);
+        final int columnNameStart = buffer.append("SELECT ").length();
+        final int columnNameEnd = buffer.append(table.codeColumn).length();
+        buffer.append(" FROM ").append(table.table);
+        boolean hasWhere = false;
+        Class<?> tableType = table.type;
+        if (table.typeColumn != null) {
+            for (int i=0; i<table.subTypes.length; i++) {
+                final Class<?> candidate = table.subTypes[i];
+                if (candidate.isAssignableFrom(type)) {
+                    buffer.append(" WHERE (").append(table.typeColumn)
+                          .append(" LIKE '").append(table.typeNames[i]).append("%'");
+                    hasWhere = true;
+                    tableType = candidate;
+                    break;
+                }
+            }
+            if (hasWhere) {
+                buffer.append(')');
+            }
+        }
+        final int conditionStart = buffer.length();
+        buffer.append(" ORDER BY ").append(table.codeColumn);
+        sql[ALL] = factory.adaptSQL(buffer.toString());
+        /*
+         * Build the SQL query for fetching the name of a single object for a given code.
+         * This query will also be used for testing object existence. It is of the form:
+         *
+         *     SELECT name FROM table WHERE code = ?
+         */
+        buffer.setLength(conditionStart);
+        if (table.nameColumn != null) {
+            buffer.replace(columnNameStart, columnNameEnd, table.nameColumn);
+        }
+        buffer.append(hasWhere ? " AND " : " WHERE ").append(table.codeColumn).append(" = ?");
+        sql[ONE] = factory.adaptSQL(buffer.toString());
+        /*
+         * Other information opportunistically computed from above search.
+         */
+        this.type = tableType;
+        isProjection = Projection.class.isAssignableFrom(tableType);
+    }
+
+    /**
+     * Creates a weak reference to this map. That reference will also be in charge of closing the JDBC statements
+     * if they were not closed.
+     */
+    final Reference<AuthorityCodes> createReference() {
+        return new CloseableReference<>(this, factory, statements);
+    }
+
+    /**
+     * Returns {@code true} if the specified code should be included in this map.
+     */
+    private boolean filter(final int code) throws SQLException {
+        assert Thread.holdsLock(factory);
+        if (!isProjection) {
+            return true;
+        }
+        try {
+            return factory.isProjection(code);
+        } catch (NoSuchIdentifierException e) {
+            /*
+             * This is not a fatal error since we can consider that the CRS is not a projection if we did not found it.
+             * However since this exception should never happen, there is probably a problem with the database content.
+             * Logs a warning pretending to come from the EPSGFactory.getAuthorityCodes() method since the later is the
+             * public facade by which the user can iterate over the entries in this AuthorityCodes map.
+             */
+            Logging.unexpectedException(Logging.getLogger(Loggers.CRS_FACTORY), EPSGFactory.class, "getAuthorityCodes", e);
+            return false;
+        }
+    }
+
+    /**
+     * Returns the code at the given index, or -1 if the index is out of bounds.
+     *
+     * @param  index index of the code to fetch.
+     * @return The code at the given index, or -1 if out of bounds.
+     * @throws SQLException if an error occurred while querying the database.
+     */
+    private int getCodeAt(final int index) throws SQLException {
+        int code;
+        synchronized (factory) {
+            if (codes == null) {
+                codes = new IntegerList(100, MAX_CODE);
+                results = (statements[ALL] = factory.connection.createStatement()).executeQuery(sql[ALL]);
+                sql[ALL] = null;                // Not needed anymore.
+            }
+            int more = index - codes.size();    // Positive as long as we need more data.
+            if (more < 0) {
+                code = codes.getInt(index);     // Get a previously cached value.
+            } else {
+                final ResultSet r = results;
+                if (r == null) {
+                    code = -1;                  // Already reached iteration end in a previous call.
+                } else do {
+                    if (!r.next()) {
+                        results = null;
+                        r.close();
+                        statements[ALL].close();
+                        statements[ALL] = null;
+                        return -1;
+                    }
+                    code = r.getInt(1);
+                    if (filter(code)) {
+                        codes.addInt(code);
+                        more--;
+                    }
+                } while (more >= 0);
+            }
+        }
+        return code;
+    }
+
+    /**
+     * Returns {@code true} if this map contains no element.
+     * This method fetches at most one row instead of counting all rows.
+     */
+    @Override
+    public boolean isEmpty() {
+        try {
+            return getCodeAt(0) >= 0;
+        } catch (SQLException exception) {
+            throw factoryFailure(exception);
+        }
+    }
+
+    /**
+     * Counts the number of elements in this map.
+     */
+    @Override
+    public int size() {
+        try {
+            getCodeAt(Integer.MAX_VALUE);       // Force counting all elements, if not already done.
+        } catch (SQLException exception) {
+            throw factoryFailure(exception);
+        }
+        return codes.size();
+    }
+
+    /**
+     * Returns the description associated to the given authority code, or {@code null} if none.
+     *
+     * @param  code  The code for which to get the description. May be a string or an integer.
+     * @return The description for the given code, or {@code null} if none.
+     */
+    @Override
+    public String get(final Object code) {
+        if (code != null) {
+            final int n;
+            if (code instanceof Number) {
+                n = ((Number) code).intValue();
+            } else try {
+                n = Integer.parseInt(code.toString());
+            } catch (NumberFormatException e) {
+                return null;    // Okay by this method contract (the given key does not exist in this map).
+            }
+            try {
+                synchronized (factory) {
+                    if (filter(n)) {
+                        PreparedStatement statement = (PreparedStatement) statements[ONE];
+                        if (statement == null) {
+                            statements[ONE] = statement = factory.connection.prepareStatement(sql[ONE]);
+                            sql[ONE] = null;    // Not needed anymore.
+                        }
+                        statement.setInt(1, n);
+                        try (ResultSet results = statement.executeQuery()) {
+                            while (results.next()) {
+                                String name = results.getString(1);
+                                if (name != null) {
+                                    return name;
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (SQLException exception) {
+                throw factoryFailure(exception);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns an iterator over the entries.
+     */
+    @Override
+    public EntryIterator<String,String> entryIterator() {
+        return new EntryIterator<String,String>() {
+            /** Index of current position. */
+            private int index = -1;
+
+            /** The authority code at current position of the iterator, or -1 if we reached iteration end. */
+            private int code;
+
+            /** Moves to the next element in the iteration. */
+            @Override protected boolean next() {
+                try {
+                    code = getCodeAt(++index);
+                } catch (SQLException exception) {
+                    throw factoryFailure(exception);
+                }
+                return code >= 0;
+            }
+
+            /** Returns the key at the current iterator position. */
+            @Override protected String getKey() {
+                return String.valueOf(code);
+            }
+
+            /**
+             * Returns pseudo-value at the current iterator position. We do not query the real value because it
+             * is costly and useless in the context where this method is used. This is because the users should
+             * never see the map directly, but only the key set.
+             */
+            @Override protected String getValue() {
+                return "";
+            }
+        };
+    }
+
+    /**
+     * Invoked when a SQL statement can not be executed, or the result retrieved.
+     */
+    private BackingStoreException factoryFailure(final SQLException exception) {
+        return new BackingStoreException(exception.getLocalizedMessage(), exception);
+    }
+
+    /**
+     * Returns a serializable copy of this set. This method is invoked automatically during serialization.
+     * The serialized map of authority codes is disconnected from the underlying database.
+     */
+    protected Object writeReplace() throws ObjectStreamException {
+        return new LinkedHashMap<>(this);
+    }
+
+    /**
+     * Closes the JDBC statement used by the {@code AuthorityCodes}. Note that if this method is never invoked,
+     * {@link CloseableReference} will perform the same work after the garbage collector detected that this map
+     * is not referenced anymore.
+     */
+    @Override
+    public void close() throws SQLException {
+        SQLException exception = null;
+        synchronized (factory) {
+            if (results != null) try {
+                results.close();
+            } catch (SQLException e) {
+                exception = e;
+            }
+            results = null;
+            exception = CloseableReference.close(statements, exception);
+        }
+        if (exception != null) {
+            throw exception;
+        }
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AuthorityCodes.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AxisName.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AxisName.java?rev=1720014&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AxisName.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AxisName.java [UTF-8] Mon Dec 14 21:09:23 2015
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.factory.sql;
+
+import java.util.Objects;
+import org.apache.sis.util.Debug;
+
+
+/**
+ * A (name, description) pair for a coordinate system axis.
+ *
+ * @author  Martin Desruisseaux (IRD)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+final class AxisName {
+    /**
+     * The coordinate system axis name (never {@code null}).
+     */
+    final String name;
+
+    /**
+     * The coordinate system axis description, or {@code null} if none.
+     */
+    final String description;
+
+    /**
+     * Creates a new coordinate system axis name.
+     */
+    AxisName(final String name, final String description) {
+        this.name = name;
+        this.description = description;
+    }
+
+    /**
+     * Returns a hash code for this object.
+     */
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    /**
+     * Compares this name with the specified object for equality.
+     */
+    @Override
+    public boolean equals(final Object object) {
+        if (object instanceof AxisName) {
+            final AxisName that = (AxisName) object;
+            return name.equals(that.name) && Objects.equals(description, that.description);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a string representation of this object, for debugging purpose only.
+     */
+    @Debug
+    @Override
+    public String toString() {
+        return name;
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/AxisName.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java?rev=1720014&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java [UTF-8] Mon Dec 14 21:09:23 2015
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.factory.sql;
+
+
+/**
+ * Private structure for {@link EPSGFactory#createBursaWolfParameters} usage.
+ *
+ * @author  Martin Desruisseaux (IRD)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+final class BursaWolfInfo {
+    /**
+     * The value of {@code CO.COORD_OP_CODE}.
+     */
+    final int operation;
+
+    /**
+     * The value of {@code CO.COORD_OP_METHOD_CODE}.
+     */
+    final int method;
+
+    /**
+     * The value of {@code CRS1.DATUM_CODE}.
+     */
+    final String target;
+
+    /**
+     * Fills a structure with the specified values.
+     */
+    BursaWolfInfo(final int operation, final int method, final String target) {
+        this.operation = operation;
+        this.method    = method;
+        this.target    = target;
+    }
+
+    /**
+     * MUST returns the operation code. This is required by {@link EPSGFactory#sort(Object[])}.
+     */
+    @Override
+    public String toString() {
+        return String.valueOf(operation);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/BursaWolfInfo.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java?rev=1720014&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java [UTF-8] Mon Dec 14 21:09:23 2015
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.factory.sql;
+
+import java.lang.ref.WeakReference;
+import java.sql.SQLException;
+import java.sql.Statement;
+import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.internal.system.ReferenceQueueConsumer;
+import org.apache.sis.util.Disposable;
+import org.apache.sis.util.logging.Logging;
+
+
+/**
+ * Closes JDBC resources when {@link AuthorityCodes} is garbage collected.
+ * Those weak references are stored in the {@link EPSGFactory#authorityCodes} map.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+final class CloseableReference<T> extends WeakReference<T> implements Disposable {
+    /**
+     * The EPSG factory, used for synchronization lock.
+     */
+    private final EPSGFactory factory;
+
+    /**
+     * The statements to close. Statements will be closed in reverse order, with null elements ignored.
+     * A synchronization lock will be hold on the array.
+     */
+    private final Statement[] statements;
+
+    /**
+     * Creates a new phantom reference which will close the given statements
+     * when the given referenced object will be garbage collected.
+     */
+    CloseableReference(final T ref, final EPSGFactory factory, final Statement[] statements) {
+        super(ref, ReferenceQueueConsumer.QUEUE);
+        this.statements = statements;
+        this.factory = factory;
+    }
+
+    /**
+     * Invoked indirectly by the garbage collector.
+     */
+    @Override
+    public void dispose() {
+        SQLException exception;
+        synchronized (factory) {
+            exception = close(statements, null);
+        }
+        if (exception != null) {
+            /*
+             * There is nothing we can do here. It is not even worth to throw an unchecked exception because
+             * this method is invoked from a background thread, so the exception would not reach user's code.
+             * Pretend that the logging come from AuthorityCodes because it is closer to a public API (or at
+             * least, easier to guess that it is related to the EPSGFactory.getAuthorityCodes() method).
+             */
+            Logging.unexpectedException(Logging.getLogger(Loggers.CRS_FACTORY), AuthorityCodes.class, "close", exception);
+        }
+    }
+
+    /**
+     * Implementation of {@link #dispose()} as a static method for sharing with {@link AuthorityCodes#close()}.
+     * It is caller's responsibility to invoke this method from a synchronization lock on the factory.
+     */
+    static SQLException close(final Statement[] statements, SQLException exception) {
+        for (int i=statements.length; --i >= 0;) {
+            final Statement s = statements[i];
+            statements[i] = null;
+            if (s != null) try {
+                s.close();
+            } catch (SQLException e) {
+                if (exception == null) {
+                    exception = e;
+                } else {
+                    exception.addSuppressed(e);
+                }
+            }
+        }
+        return exception;
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/CloseableReference.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java?rev=1720014&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] Mon Dec 14 21:09:23 2015
@@ -0,0 +1,637 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.factory.sql;
+
+import java.util.Set;
+import java.util.Map;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.IdentityHashMap;
+import java.util.Collections;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.sql.SQLException;
+import java.lang.ref.Reference;
+import java.text.DateFormat;
+import java.net.URI;
+import java.net.URISyntaxException;
+import javax.measure.unit.Unit;
+import javax.measure.unit.SI;
+import javax.measure.unit.NonSI;
+import javax.measure.converter.ConversionException;
+
+import org.opengis.util.NameSpace;
+import org.opengis.util.NameFactory;
+import org.opengis.util.InternationalString;
+import org.opengis.util.FactoryException;
+import org.opengis.util.NoSuchIdentifierException;
+import org.opengis.parameter.*;
+import org.opengis.referencing.cs.*;
+import org.opengis.referencing.crs.*;
+import org.opengis.referencing.datum.*;
+import org.opengis.referencing.operation.*;
+import org.opengis.referencing.IdentifiedObject;
+import org.opengis.metadata.citation.Citation;
+import org.opengis.metadata.citation.OnLineFunction;
+import org.opengis.metadata.quality.PositionalAccuracy;
+import org.opengis.referencing.NoSuchAuthorityCodeException;
+import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.metadata.iso.DefaultIdentifier;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.apache.sis.metadata.iso.citation.DefaultOnlineResource;
+import org.apache.sis.referencing.datum.BursaWolfParameters;
+import org.apache.sis.referencing.factory.GeodeticAuthorityFactory;
+import org.apache.sis.referencing.factory.ConcurrentAuthorityFactory;
+import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.resources.Messages;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.Disposable;
+import org.apache.sis.util.Version;
+import org.apache.sis.measure.Units;
+
+
+/**
+ * A geodetic object factory backed by the EPSG database tables.
+ * The EPSG database is freely available at <a href="http://www.epsg.org">http://www.epsg.org</a>.
+ * Current version of this class requires EPSG database version 6.6 or above.
+ *
+ * <p>This factory accepts names as well as numerical identifiers.
+ * For example <cite>"NTF (Paris) / France I"</cite> and {@code "27581"} both fetch the same object.
+ * However, names may be ambiguous since the same name may be used for more than one object.
+ * This is the case of <cite>"WGS 84"</cite> for instance.
+ * If such an ambiguity is found, an exception will be thrown.
+ * This behavior can be changed by overriding the {@link #isPrimaryKey(String)} method.</p>
+ *
+ * <p>This factory does not cache the result of {@code createFoo(String)} methods.
+ * Asking for the same object twice will cause the EPSG database to be queried again.
+ * For caching, this factory should be wrapped in {@link ConcurrentAuthorityFactory}.</p>
+ *
+ * <p>Because the primary distribution format for the EPSG database is MS-Access, this class uses
+ * SQL statements formatted for the MS-Access syntax. For usage with an other database software,
+ * a dialect-specific subclass must be used.</p>
+ *
+ * @author  Yann Cézard (IRD)
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @author  Rueben Schulz (UBC)
+ * @author  Matthias Basler
+ * @author  Andrea Aime (TOPP)
+ * @author  Johann Sorel (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ *
+ * @see <a href="http://sis.apache.org/book/tables/CoordinateReferenceSystems.html">List of authority codes</a>
+ */
+public abstract class EPSGFactory extends GeodeticAuthorityFactory implements CRSAuthorityFactory,
+        CSAuthorityFactory, DatumAuthorityFactory, CoordinateOperationAuthorityFactory, Disposable
+{
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    //////                                                                                 ///////
+    //////   HARD CODED VALUES (other than SQL statements) RELATIVE TO THE EPSG DATABASE   ///////
+    //////                                                                                 ///////
+    //////////////////////////////////////////////////////////////////////////////////////////////
+
+    // See org.apache.sis.measure.Units.valueOfEPSG(int) for hard-code units from EPSG codes.
+
+    /**
+     * Sets a Bursa-Wolf parameter from an EPSG parameter.
+     *
+     * @param  parameters The Bursa-Wolf parameters to modify.
+     * @param  code       The EPSG code for a parameter from the [PARAMETER_CODE] column.
+     * @param  value      The value of the parameter from the [PARAMETER_VALUE] column.
+     * @param  unit       The unit of the parameter value from the [UOM_CODE] column.
+     * @throws FactoryException if the code is unrecognized.
+     */
+    private static void setBursaWolfParameter(final BursaWolfParameters parameters,
+            final int code, double value, final Unit<?> unit) throws FactoryException
+    {
+        Unit<?> target = unit;
+        if (code >= 8605) {
+            if      (code <= 8607) target = SI   .METRE;
+            else if (code <= 8710) target = NonSI.SECOND_ANGLE;
+            else if (code == 8611) target = Units.PPM;
+        }
+        if (target != unit) try {
+            value = unit.getConverterToAny(target).convert(value);
+        } catch (ConversionException e) {
+            throw new FactoryException(Errors.format(Errors.Keys.IncompatibleUnit_1, unit), e);
+        }
+        switch (code) {
+            case 8605: parameters.tX = value; break;
+            case 8606: parameters.tY = value; break;
+            case 8607: parameters.tZ = value; break;
+            case 8608: parameters.rX = value; break;
+            case 8609: parameters.rY = value; break;
+            case 8610: parameters.rZ = value; break;
+            case 8611: parameters.dS = value; break;
+            default: throw new FactoryException(Errors.format(Errors.Keys.UnexpectedParameter_1, code));
+        }
+    }
+    // Datum shift operation methods
+    /** First Bursa-Wolf method. */ private static final int BURSA_WOLF_MIN_CODE = 9603;
+    /** Last Bursa-Wolf method.  */ private static final int BURSA_WOLF_MAX_CODE = 9607;
+    /** Rotation frame method.   */ private static final int ROTATION_FRAME_CODE = 9607;
+
+    /**
+     * List of tables and columns to test for codes values. Those tables are used by the {@link #createObject(String)}
+     * method in order to detect which of the following methods should be invoked for a given code:
+     *
+     * {@link #createCoordinateReferenceSystem(String)}
+     * {@link #createCoordinateSystem(String)}
+     * {@link #createDatum(String)}
+     * {@link #createEllipsoid(String)}
+     * {@link #createUnit(String)}
+     *
+     * The order is significant: it is the key for a {@code switch} statement.
+     *
+     * @see #createObject(String)
+     * @see #lastObjectType
+     */
+    private static final TableInfo[] TABLES_INFO = {
+        new TableInfo(CoordinateReferenceSystem.class,
+                "[Coordinate Reference System]",
+                "COORD_REF_SYS_CODE",
+                "COORD_REF_SYS_NAME",
+                "COORD_REF_SYS_KIND",
+                new Class<?>[] { ProjectedCRS.class, GeographicCRS.class, GeocentricCRS.class,
+                                 VerticalCRS.class,  CompoundCRS.class,   EngineeringCRS.class},
+                new String[]   {"projected",        "geographic",        "geocentric",
+                                "vertical",         "compound",          "engineering"}),
+
+        new TableInfo(CoordinateSystem.class,
+                "[Coordinate System]",
+                "COORD_SYS_CODE",
+                "COORD_SYS_NAME",
+                "COORD_SYS_TYPE",
+                new Class<?>[] { CartesianCS.class, EllipsoidalCS.class, SphericalCS.class, VerticalCS.class},
+                new String[]   {"Cartesian",       "ellipsoidal",       "spherical",       "vertical"}),
+                               //Really upper-case C.
+        new TableInfo(CoordinateSystemAxis.class,
+                "[Coordinate Axis] AS CA INNER JOIN [Coordinate Axis Name] AS CAN" +
+                                 " ON CA.COORD_AXIS_NAME_CODE=CAN.COORD_AXIS_NAME_CODE",
+                "COORD_AXIS_CODE",
+                "COORD_AXIS_NAME"),
+
+        new TableInfo(Datum.class,
+                "[Datum]",
+                "DATUM_CODE",
+                "DATUM_NAME",
+                "DATUM_TYPE",
+                new Class<?>[] { GeodeticDatum.class, VerticalDatum.class, EngineeringDatum.class},
+                new String[]   {"geodetic",          "vertical",          "engineering"}),
+
+        new TableInfo(Ellipsoid.class,
+                "[Ellipsoid]",
+                "ELLIPSOID_CODE",
+                "ELLIPSOID_NAME"),
+
+        new TableInfo(PrimeMeridian.class,
+                "[Prime Meridian]",
+                "PRIME_MERIDIAN_CODE",
+                "PRIME_MERIDIAN_NAME"),
+
+        new TableInfo(CoordinateOperation.class,
+                "[Coordinate_Operation]",
+                "COORD_OP_CODE",
+                "COORD_OP_NAME",
+                "COORD_OP_TYPE",
+                new Class<?>[] { Projection.class, Conversion.class, Transformation.class},
+                new String[]   {"conversion",     "conversion",     "transformation"}),
+                // Note: Projection is handle in a special way.
+
+        new TableInfo(OperationMethod.class,
+                "[Coordinate_Operation Method]",
+                "COORD_OP_METHOD_CODE",
+                "COORD_OP_METHOD_NAME"),
+
+        new TableInfo(ParameterDescriptor.class,
+                "[Coordinate_Operation Parameter]",
+                "PARAMETER_CODE",
+                "PARAMETER_NAME"),
+
+        new TableInfo(Unit.class,
+                "[Unit of Measure]",
+                "UOM_CODE",
+                "UNIT_OF_MEAS_NAME")
+    };
+
+    ///////////////////////////////////////////////////////////////////////////////
+    ////////                                                               ////////
+    ////////                    END OF HARD CODED VALUES                   ////////
+    ////////                                                               ////////
+    ////////    NOTE: 'createFoo(...)' methods may still have hard-coded   ////////
+    ////////    values (others than SQL statements) in 'equalsIgnoreCase'  ////////
+    ////////    expressions.                                               ////////
+    ///////////////////////////////////////////////////////////////////////////////
+
+
+
+
+    /**
+     * The name for the transformation accuracy metadata.
+     */
+    private static final InternationalString TRANSFORMATION_ACCURACY =
+            Vocabulary.formatInternational(Vocabulary.Keys.TransformationAccuracy);
+
+    /**
+     * The authority for this database. Will be created only when first needed. This authority will contain
+     * the database version in the {@linkplain Citation#getEdition() edition} attribute, together with the
+     * {@linkplain Citation#getEditionDate() edition date}.
+     */
+    private Citation authority;
+
+    /**
+     * Last object type returned by {@link #createObject(String)}, or -1 if none.
+     * This type is an index in the {@link #TABLES_INFO} array and is strictly for {@link #createObject} internal use.
+     */
+    private int lastObjectType = -1;
+
+    /**
+     * The last table in which object name were looked for.
+     * This is for internal use by {@link #toPrimaryKey} only.
+     */
+    private String lastTableForName;
+
+    /**
+     * The calendar instance for creating {@link Date} objects from a year (the "epoch" in datum definition).
+     * We use the UTC timezone, which may not be quite accurate. But there is no obvious timezone for "epoch",
+     * and the "epoch" is approximative anyway.
+     *
+     * @see #getCalendar()
+     */
+    private Calendar calendar;
+
+    /**
+     * The object to use for parsing dates, created when first needed. This is used for
+     * parsing the origin of temporal datum. This is an Apache SIS specific extension.
+     */
+    private DateFormat dateFormat;
+
+    /**
+     * A pool of prepared statements. Keys are {@link String} objects related to their originating method
+     * (for example "Ellipsoid" for {@link #createEllipsoid(String)}).
+     *
+     * <div class="note"><b>Note:</b>
+     * it is okay to use {@link IdentityHashMap} instead of {@link HashMap} because the keys will always be
+     * the exact same object, namely the hard-coded argument given to calls to {@link #prepareStatement} in
+     * this class.</div>
+     */
+    private final Map<String,PreparedStatement> statements = new IdentityHashMap<>();
+
+    /**
+     * The set of authority codes for different types. This map is used by the {@link #getAuthorityCodes(Class)}
+     * method as a cache for returning the set created in a previous call.
+     *
+     * <p>Note that this {@code EPSGFactory} instance can not be disposed as long as this map is not empty, since
+     * {@link AuthorityCodes} caches some SQL statements and consequently require the {@linkplain #connection} to
+     * be open. This is why we use weak references rather than hard ones, in order to know when no
+     * {@link AuthorityCodes} are still in use.</p>
+     *
+     * <p>The {@link CloseableReference#dispose()} method takes care of closing the statements used by the map.
+     * The {@link AuthorityCodes} reference in this map is then cleared by the garbage collector.
+     * The {@link #canDispose()} method checks if there is any remaining live reference in this map,
+     * and returns {@code false} if some are found (thus blocking the call to {@link #dispose()}
+     * by the {@link ConcurrentAuthorityFactory} timer).</p>
+     */
+    private final Map<Class<?>, Reference<AuthorityCodes>> authorityCodes = new HashMap<>();
+
+    /**
+     * Cache for axis names. This service is not provided by {@link CachingAuthorityFactory}
+     * since {@link AxisName} objects are particular to the EPSG database.
+     *
+     * @see #getAxisName(int)
+     */
+    private final Map<Integer,AxisName> axisNames = new HashMap<>();
+
+    /**
+     * Cache for the number of dimensions of coordinate systems. This service is not provided by
+     * {@link CachingAuthorityFactory} since the number of dimension is used internally in this class.
+     *
+     * @see #getDimensionForCS(int)
+     */
+    private final Map<Integer,Integer> csDimensions = new HashMap<>();
+
+    /**
+     * Cache for whether conversions are projections. This service is not provided by {@link CachingAuthorityFactory}
+     * since the check for conversion type is used internally in this class.
+     *
+     * @see #isProjection(int)
+     */
+    private final Map<Integer,Boolean> isProjection = new HashMap<>();
+
+    /**
+     * Cache the positional accuracies. Most coordinate operation use a small set of accuracy values.
+     *
+     * @see #getAccuracy(double)
+     */
+    private final Map<Double,PositionalAccuracy> accuracies = new HashMap<>();
+
+    /**
+     * Pool of naming systems, used for caching. There is usually few of them (about 15).
+     *
+     * @see #createProperties(String, String, String, String, boolean)
+     */
+    private final Map<Integer,NameSpace> scopes = new HashMap<>();
+
+    /**
+     * The properties to be given the objects to construct.
+     * Reused every time {@code createProperties(…)} is invoked.
+     */
+    private final Map<String,Object> properties = new HashMap<>();
+
+    /**
+     * A safety guard for preventing never-ending loops in recursive calls to {@link #createDatum(String)}.
+     * This is used by {@link #createBursaWolfParameters(String, ResultSet)}, which need to create a target datum.
+     * The target datum could have its own Bursa-Wolf parameters, with one of them pointing again to the source datum.
+     */
+    private final Set<Integer> safetyGuard = new HashSet<>();
+
+    /**
+     * The {@link ConcurrentAuthorityFactory} that supply caching for all {@code createFoo(String)} methods,
+     * or {@code this} if none.
+     */
+    GeodeticAuthorityFactory buffered = this;
+
+    /**
+     * The connection to the EPSG database. This connection is specified at {@linkplain #EPSGFactory construction time}
+     * and closed by the {@link #dispose()} method, or when this {@code EPSGFactory} instance is garbage collected.
+     */
+    protected final Connection connection;
+
+    /**
+     * Creates a factory using the given connection. The connection will be {@linkplain Connection#close() closed}
+     * when this factory will be {@linkplain #dispose() disposed}.
+     *
+     * @param connection The connection to the underlying EPSG database.
+     */
+    public EPSGFactory(final Connection connection, final NameFactory nameFactory) {
+        super(nameFactory);
+        this.connection = connection;
+        ArgumentChecks.ensureNonNull("connection", connection);
+    }
+
+    /**
+     * Returns the calendar to use for reading dates in the database.
+     */
+    @SuppressWarnings("ReturnOfDateField")
+    private Calendar getCalendar() {
+        if (calendar == null) {
+            calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.CANADA);
+            // Canada locale is closer to ISO than US.
+        }
+        return calendar;
+    }
+
+    /**
+     * Returns the authority for this EPSG database. The returned citation contains the database version
+     * in the {@linkplain Citation#getEdition() edition} attribute, together with date of last update in
+     * the {@linkplain Citation#getEditionDate() edition date}.
+     */
+    @Override
+    public synchronized Citation getAuthority() {
+        if (authority == null) {
+            final DefaultCitation c = new DefaultCitation("EPSG Geodetic Parameter Dataset");
+            c.setIdentifiers(Collections.singleton(new DefaultIdentifier("EPSG")));
+            try {
+                /*
+                 * Get the most recent version number from the history table. We get the date in local timezone
+                 * instead then UTC because the date is for information purpose only, and the local timezone is
+                 * more likely to be shown nicely (without artificial hours) to the user.
+                 */
+                final String query = adaptSQL("SELECT VERSION_NUMBER, VERSION_DATE FROM [Version History]" +
+                                              " ORDER BY VERSION_DATE DESC, VERSION_HISTORY_CODE DESC");
+                String version = null;
+                try (Statement statement = connection.createStatement();
+                     ResultSet result = statement.executeQuery(query))
+                {
+                    while (result.next()) {
+                        version = result.getString(1);
+                        final Date date = result.getDate(2);                            // Local timezone.
+                        if (version != null && date != null) {                          // Paranoiac check.
+                            c.setEdition(new SimpleInternationalString(version));
+                            c.setEditionDate(date);
+                            break;
+                        }
+                    }
+                }
+                /*
+                 * Add some hard-coded links to EPSG resources, and finally add the JDBC driver name and version number.
+                 * The list last OnlineResource looks like:
+                 *
+                 *    Linkage:      jdbc:derby:/my/path/to/SIS_DATA/Metadata
+                 *    Function:     Connection
+                 *    Description:  EPSG dataset version 8.8 on “Apache Derby Embedded JDBC Driver” version 10.12.
+                 */
+                final DatabaseMetaData metadata  = connection.getMetaData();
+addURIs:        for (int i=0; ; i++) {
+                    String url;
+                    OnLineFunction function;
+                    InternationalString description = null;
+                    switch (i) {
+                        case 0: url = "http://epsg-registry.org/"; function = OnLineFunction.SEARCH; break;
+                        case 1: url = "http://www.epsg.org/"; function = OnLineFunction.DOWNLOAD; break;
+                        case 2: {
+                            url = metadata.getURL();
+                            function = OnLineFunction.valueOf("CONNECTION");
+                            description = Messages.formatInternational(Messages.Keys.DataBase_4, "EPSG", version,
+                                    metadata.getDatabaseProductName(),
+                                    Version.valueOf(metadata.getDatabaseMajorVersion(),
+                                                    metadata.getDatabaseMinorVersion()));
+                            break;
+                        }
+                        default: break addURIs;     // Finished adding all URIs.
+                    }
+                    final DefaultOnlineResource r = new DefaultOnlineResource();
+                    try {
+                        r.setLinkage(new URI(url));
+                    } catch (URISyntaxException exception) {
+                        unexpectedException("getAuthority", exception);
+                    }
+                    r.setFunction(function);
+                    r.setDescription(description);
+                    c.getOnlineResources().add(r);
+                }
+            } catch (SQLException exception) {
+                unexpectedException("getAuthority", exception);
+            } finally {
+                c.freeze();
+                authority = c;
+            }
+        }
+        return authority;
+    }
+
+    /**
+     * Returns the set of authority codes of the given type.
+     * This returned set may keep a connection to the EPSG database,
+     * so the set can execute efficiently idioms like the following one:
+     *
+     * {@preformat java
+     *     getAuthorityCodes(type).containsAll(others)
+     * }
+     *
+     * The returned set should not be referenced for a long time, as it may prevent this factory to release
+     * JDBC resources. If the set of codes is needed for a long time, their values should be copied in another
+     * collection object.
+     *
+     * @param  type The spatial reference objects type (may be {@code Object.class}).
+     * @return The set of authority codes for spatial reference objects of the given type (may be an empty set).
+     * @throws FactoryException if access to the underlying database failed.
+     */
+    @Override
+    public Set<String> getAuthorityCodes(final Class<? extends IdentifiedObject> type) throws FactoryException {
+        return getCodeMap(type).keySet();
+    }
+
+    /**
+     * Returns a map of EPSG authority codes as keys and object names as values.
+     */
+    private synchronized Map<String,String> getCodeMap(final Class<?> type) throws FactoryException {
+        Reference<AuthorityCodes> reference = authorityCodes.get(type);
+        if (reference != null) {
+            AuthorityCodes existing = reference.get();
+            if (existing != null) {
+                return existing;
+            }
+        }
+        Map<String,String> result = Collections.emptyMap();
+        for (final TableInfo table : TABLES_INFO) {
+            /*
+             * We test 'isAssignableFrom' in the two ways for catching the following use cases:
+             *
+             *  - table.type.isAssignableFrom(type)
+             *    is for the case where a table is for CoordinateReferenceSystem while the user type is some subtype
+             *    like GeographicCRS. The GeographicCRS need to be queried into the CoordinateReferenceSystem table.
+             *    An additional filter will be applied inside the AuthorityCodes class implementation.
+             *
+             *  - type.isAssignableFrom(table.type)
+             *    is for the case where the user type is IdentifiedObject or Object, in which case we basically want
+             *    to iterate through every tables.
+             */
+            if (table.type.isAssignableFrom(type) || type.isAssignableFrom(table.type)) {
+                /*
+                 * Maybe an instance already existed but was not found above because the user specified some
+                 * implementation class instead of an interface class. Before to return a newly created map,
+                 * check again in the cached maps using the type computed by AuthorityCodes itself.
+                 */
+                AuthorityCodes codes = new AuthorityCodes(connection, table, type, this);
+                reference = authorityCodes.get(codes.type);
+                if (reference != null) {
+                    AuthorityCodes existing = reference.get();
+                    if (existing != null) {
+                        codes = existing;
+                    } else {
+                        reference = null;   // The weak reference is no longer valid.
+                    }
+                }
+                if (reference == null) {
+                    reference = codes.createReference();
+                    authorityCodes.put(codes.type, reference);
+                }
+                if (type != codes.type) {
+                    authorityCodes.put(type, reference);
+                }
+                /*
+                 * We now have the codes for a single type. Append with the codes of previous types, if any.
+                 * This usually happen only if the user asked for the IdentifiedObject type. Of course this
+                 * break all our effort to query the data only when first needed, but the user should ask
+                 * for more specific types.
+                 */
+                if (result.isEmpty()) {
+                    result = codes;
+                } else {
+                    if (result instanceof AuthorityCodes) {
+                        result = new LinkedHashMap<>(result);
+                    }
+                    result.putAll(codes);
+                    try {
+                        codes.close();
+                    } catch (SQLException e) {  // Not a fatal exception for this method since we got the data.
+                        unexpectedException("getAuthorityCodes", e);
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Gets a description of the object corresponding to a code.
+     *
+     * @param  code Value allocated by authority.
+     * @return A description of the object, or {@code null} if the object corresponding to the specified {@code code}
+     *         has no description.
+     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
+     * @throws FactoryException if the query failed for some other reason.
+     */
+    @Override
+    public InternationalString getDescriptionText(final String code) throws NoSuchAuthorityCodeException, FactoryException {
+        final String primaryKey = trimAuthority(code);
+        for (final TableInfo table : TABLES_INFO) {
+            final String text = getCodeMap(table.type).get(primaryKey);
+            if (text != null) {
+                return (table.nameColumn != null) ? new SimpleInternationalString(text) : null;
+            }
+        }
+        throw noSuchAuthorityCode(IdentifiedObject.class, code);
+    }
+
+    final boolean isProjection(final int code) throws NoSuchIdentifierException, SQLException {
+        return false;
+    }
+
+    /**
+     * Invoked when a new {@link PreparedStatement} is about to be created from a SQL string.
+     * Since the <a href="http://www.epsg.org">EPSG database</a> is available primarily in MS-Access format,
+     * SQL statements are formatted using a syntax specific to this particular database software
+     * (for example "{@code SELECT * FROM [Coordinate Reference System]}").
+     * When a subclass targets another database vendor, it must overrides this method in order to adapt the SQL syntax.
+     *
+     * <div class="note"><b>Example</b>
+     * a subclass connecting to a <cite>PostgreSQL</cite> database would replace the watching braces
+     * ({@code '['} and {@code ']'}) by the quote character ({@code '"'}).</div>
+     *
+     * The default implementation returns the given statement unchanged.
+     *
+     * @param  statement The statement in MS-Access syntax.
+     * @return The SQL statement adapted to the syntax of the target database.
+     */
+    protected String adaptSQL(final String statement) {
+        return statement;
+    }
+
+    /**
+     * Logs a warning about an unexpected but non-fatal exception.
+     *
+     * @param method    The source method.
+     * @param exception The exception to log.
+     */
+    private static void unexpectedException(final String method, final Exception exception) {
+        Logging.unexpectedException(Logging.getLogger(Loggers.CRS_FACTORY), EPSGFactory.class, method, exception);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java?rev=1720014&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java [UTF-8] Mon Dec 14 21:09:23 2015
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.factory.sql;
+
+
+/**
+ * Information about a specific table. The MS-Access dialect of SQL is assumed;
+ * it will be translated into ANSI SQL later by {@link EPSGFactory#adaptSQL(String)} if needed.
+ *
+ * @author  Martin Desruisseaux (IRD)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+final class TableInfo {
+    /**
+     * The class of object to be created.
+     */
+    final Class<?> type;
+
+    /**
+     * The table name for SQL queries. May contains a {@code "JOIN"} clause.
+     */
+    final String table;
+
+    /**
+     * Column name for the code (usually with the {@code "_CODE"} suffix).
+     */
+    final String codeColumn;
+
+    /**
+     * Column name for the name (usually with the {@code "_NAME"} suffix), or {@code null}.
+     */
+    final String nameColumn;
+
+    /**
+     * Column type for the type (usually with the {@code "_TYPE"} suffix), or {@code null}.
+     */
+    final String typeColumn;
+
+    /**
+     * Sub-interfaces of {@link #type} to handle, or {@code null} if none.
+     */
+    final Class<?>[] subTypes;
+
+    /**
+     * Names of {@link #subTypes} in the database, or {@code null} if none.
+     */
+    final String[] typeNames;
+
+    /**
+     * Stores information about a specific table.
+     */
+    TableInfo(final Class<?> type, final String table,
+              final String codeColumn, final String nameColumn)
+    {
+        this(type, table, codeColumn, nameColumn, null, null, null);
+    }
+
+    /**
+     * Stores information about a specific table.
+     */
+    TableInfo(final Class<?> type,
+              final String table, final String codeColumn, final String nameColumn,
+              final String typeColumn, final Class<?>[] subTypes, final String[] typeNames)
+    {
+        this.type       = type;
+        this.table      = table;
+        this.codeColumn = codeColumn;
+        this.nameColumn = nameColumn;
+        this.typeColumn = typeColumn;
+        this.subTypes   = subTypes;
+        this.typeNames  = typeNames;
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/TableInfo.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DatumShiftTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DatumShiftTransform.java?rev=1720014&r1=1720013&r2=1720014&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DatumShiftTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DatumShiftTransform.java [UTF-8] Mon Dec 14 21:09:23 2015
@@ -44,8 +44,8 @@ import java.util.Objects;
  * to geographic coordinates, to more complex transformations involving conversions to geocentric coordinates and/or
  * interpolations in a {@linkplain DatumShiftGrid datum shift grid}. The simple cases like adding a constant offset
  * are handled by other {@code MathTransform} implementations like {@link LinearTransform}.
- * This {@code DatumShiftTransform} base class is only for transformation methods that can not be represented
- * by a concatenation of other {@code MathTransform} implementations.</p>
+ * More complex methods are subclasses of this {@code DatumShiftTransform} base class, but users should not assume
+ * that this is the case of every transforms performing a datum shift.</p>
  *
  * <div class="section">Datum shift methods overview</div>
  * The two CRS's ellipsoids have slightly different scale and rotation in space, and their center are located in

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java?rev=1720014&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java [UTF-8] Mon Dec 14 21:09:23 2015
@@ -0,0 +1,458 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.util.collection;
+
+import java.util.Arrays;
+import java.util.AbstractList;
+import java.util.RandomAccess;
+import java.util.NoSuchElementException;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.ArgumentChecks;
+
+
+/**
+ * A list of unsigned integer values. This class packs the values in the minimal amount of bits
+ * required for storing unsigned integers of the given {@linkplain #maximalValue() maximal value}.
+ *
+ * <p>This class is <strong>not</strong> thread-safe. Synchronizations (if wanted) are user's responsibility.</p>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.7
+ * @version 0.7
+ * @module
+ */
+public class IntegerList extends AbstractList<Integer> implements RandomAccess, Serializable, Cloneable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 1241962316404811189L;
+
+    /**
+     * The size of the primitive type used for the {@link #values} array.
+     */
+    private static final int VALUE_SIZE = Long.SIZE;
+
+    /**
+     * The shift to apply on {@code index} in order to produce a result equivalent to {@code index} / {@value #VALUE_SIZE}.
+     * The following relation must hold: {@code (1 <<< BASE_SHIFT) == VALUE_SIZE}.
+     */
+    private static final int BASE_SHIFT = 6;
+
+    /**
+     * The mask to apply on {@code index} in order to produce a result equivalent to {@code index} % {@value #VALUE_SIZE}.
+     */
+    private static final int OFFSET_MASK = VALUE_SIZE - 1;
+
+    /**
+     * The packed values. We use the {@code long} type instead of {@code int} since 64 bits machines are common now.
+     */
+    private long[] values;
+
+    /**
+     * The bit count for values.
+     */
+    private final int bitCount;
+
+    /**
+     * The mask computed as {@code (1 << bitCount) - 1}.
+     */
+    private final int mask;
+
+    /**
+     * The list size. Initially 0.
+     */
+    private int size;
+
+    /**
+     * Creates an initially empty list with the given initial capacity.
+     *
+     * @param initialCapacity The initial capacity.
+     * @param maximalValue The maximal value to be allowed, inclusive.
+     */
+    public IntegerList(int initialCapacity, int maximalValue) {
+        this(initialCapacity, maximalValue, false);
+    }
+
+    /**
+     * Creates a new list with the given initial size.
+     * The value of all elements are initialized to 0.
+     *
+     * @param initialCapacity The initial capacity.
+     * @param maximalValue The maximal value to be allowed, inclusive.
+     * @param fill If {@code true}, the initial {@linkplain #size() size} is set to the initial capacity
+     *        with all values set to 0.
+     */
+    public IntegerList(final int initialCapacity, int maximalValue, final boolean fill) {
+        ArgumentChecks.ensureStrictlyPositive("initialCapacity", initialCapacity);
+        ArgumentChecks.ensureStrictlyPositive("maximalValue",    maximalValue);
+        int bitCount = 0;
+        do {
+            bitCount++;
+            maximalValue >>>= 1;
+        } while (maximalValue != 0);
+        this.bitCount = bitCount;
+        mask = (1 << bitCount) - 1;
+        values = new long[length(initialCapacity)];
+        if (fill) {
+            size = initialCapacity;
+        }
+    }
+
+    /**
+     * Returns the array length required for holding a list of the given size.
+     *
+     * @param size The list size.
+     * @return The array length for holding a list of the given size.
+     */
+    private int length(int size) {
+        size *= bitCount;
+        int length = size >>> BASE_SHIFT;
+        if ((size & OFFSET_MASK) != 0) {
+            length++;
+        }
+        return length;
+    }
+
+    /**
+     * Returns the maximal value that can be stored in this list.
+     * May be slightly higher than the value given to the constructor.
+     *
+     * @return The maximal value, inclusive.
+     */
+    public int maximalValue() {
+        return mask;
+    }
+
+    /**
+     * Returns the current number of values in this list.
+     *
+     * @return The number of values.
+     */
+    @Override
+    public int size() {
+        return size;
+    }
+
+    /**
+     * Sets the list size to the given value. If the new size is lower than previous size,
+     * then the elements after the new size are discarded. If the new size is greater than
+     * the previous one, then the extra elements are initialized to 0.
+     *
+     * @param size The new size.
+     */
+    public void resize(final int size) {
+        ArgumentChecks.ensurePositive("size", size);
+        if (size > this.size) {
+            int base = this.size * bitCount;
+            final int offset = base & OFFSET_MASK;
+            base >>>= BASE_SHIFT;
+            if (offset != 0 && base < values.length) {
+                values[base] &= (1L << offset) - 1;
+                base++;
+            }
+            final int length = length(size);
+            Arrays.fill(values, base, Math.min(length, values.length), 0L);
+            if (length > values.length) {
+                values = Arrays.copyOf(values, length);
+            }
+        }
+        this.size = size;
+    }
+
+    /**
+     * Fills the list with the given value.
+     * 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);
+        final long p;
+        if (value == 0) {
+            p = 0;                              // All bits set to 0.
+        } else if (value == mask) {
+            p = -1L;                            // All bits set to 1.
+        } else switch (bitCount) {
+            case  1: value |= (value << 1);     // Fall through
+            case  2: value |= (value << 2);     // Fall through
+            case  4: value |= (value << 4);     // Fall through
+            case  8: value |= (value << 8);     // Fall through
+            case 16: value |= (value << 16);    // Fall through
+            case 32: p = (value & 0xFFFFFFFFL) | ((long) value << 32); break;
+            default: {    // General case (unoptimized)
+                for (int i=0; i<size; i++) {
+                    setUnchecked(i, value);
+                }
+                return;
+            }
+        }
+        Arrays.fill(values, 0, length(size), p);
+    }
+
+    /**
+     * Discards all elements in this list.
+     */
+    @Override
+    public void clear() {
+        size = 0;
+    }
+
+    /**
+     * Adds the given element to this list.
+     *
+     * @param  value The value to add.
+     * @return Always {@code true}.
+     * @throws NullPointerException if the given value is null.
+     * @throws IllegalArgumentException if the given value is out of bounds.
+     */
+    @Override
+    public boolean add(final Integer value) throws IllegalArgumentException {
+        addInt(value);
+        return true;
+    }
+
+    /**
+     * Adds the given element as the {@code int} primitive type.
+     *
+     * @param  value The value to add.
+     * @throws IllegalArgumentException if the given value is out of bounds.
+     *
+     * @see #removeLast()
+     */
+    public void addInt(final int value) throws IllegalArgumentException {
+        ArgumentChecks.ensureBetween("value", 0, mask, value);
+        final int last = size;
+        final int length = length(++size);
+        if (length > values.length) {
+            values = Arrays.copyOf(values, 2*values.length);
+        }
+        setUnchecked(last, value);
+    }
+
+    /**
+     * Returns the element at the given index.
+     *
+     * @param  index The element index.
+     * @return The value at the given index.
+     * @throws IndexOutOfBoundsException if the given index is out of bounds.
+     */
+    @Override
+    public Integer get(final int index) throws IndexOutOfBoundsException {
+        return getInt(index);
+    }
+
+    /**
+     * Returns the element at the given index as the {@code int} primitive type.
+     *
+     * @param  index The element index.
+     * @return The value at the given index.
+     * @throws IndexOutOfBoundsException if the given index is out of bounds.
+     */
+    public int getInt(final int index) throws IndexOutOfBoundsException {
+        ArgumentChecks.ensureValidIndex(size, index);
+        return getUnchecked(index);
+    }
+
+    /**
+     * 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.
+     *
+     * @param  index The element index.
+     * @return The value at the given index.
+     */
+    private int getUnchecked(int index) {
+        index *= bitCount;
+        int base   = index >>> BASE_SHIFT;
+        int offset = index & OFFSET_MASK;
+        int value  = (int) (values[base] >>> offset);
+        offset = VALUE_SIZE - offset;
+        if (offset < bitCount) {
+            final int high = (int) values[++base];
+            value |= (high << offset);
+        }
+        value &= mask;
+        return value;
+    }
+
+    /**
+     * Sets the element at the given index.
+     *
+     * @param  index The element index.
+     * @param  value The value at the given index.
+     * @return The previous value at the given index.
+     * @throws IndexOutOfBoundsException if the given index is out of bounds.
+     * @throws IllegalArgumentException if the given value is out of bounds.
+     * @throws NullPointerException if the given value is null.
+     */
+    @Override
+    public Integer set(final int index, final Integer value) throws IndexOutOfBoundsException {
+        final Integer old = get(index);
+        setInt(index, value);
+        return old;
+    }
+
+    /**
+     * Sets the element at the given index as the {@code int} primitive type.
+     *
+     * @param  index The element index.
+     * @param  value The value at the given index.
+     * @throws IndexOutOfBoundsException if the given index is out of bounds.
+     * @throws IllegalArgumentException if the given value is out of bounds.
+     */
+    public void setInt(int index, int value) throws IndexOutOfBoundsException {
+        ArgumentChecks.ensureValidIndex(size, index);
+        ArgumentChecks.ensureBetween("value", 0, mask, value);
+        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.
+     *
+     * @param index The element index.
+     * @param value The value at the given index.
+     */
+    private void setUnchecked(int index, int value) {
+        index *= bitCount;
+        int base   = index >>> BASE_SHIFT;
+        int offset = index & OFFSET_MASK;
+        values[base] &= ~(((long) mask) << offset);
+        values[base] |= ((long) value) << offset;
+        offset = VALUE_SIZE - offset;
+        if (offset < bitCount) {
+            value >>>= offset;
+            values[++base] &= ~(((long) mask) >>> offset);
+            values[base] |= value;
+        }
+    }
+
+    /**
+     * Removes the element at the given index.
+     *
+     * @param  index The index of the element to remove.
+     * @return The previous value of the element at the given index.
+     * @throws IndexOutOfBoundsException if the given index is out of bounds.
+     */
+    @Override
+    public Integer remove(final int index) throws IndexOutOfBoundsException {
+        final Integer old = get(index);
+        removeRange(index, index+1);
+        return old;
+    }
+
+    /**
+     * Retrieves and remove the last element of this list.
+     *
+     * @return The tail of this list.
+     * @throws NoSuchElementException if this list is empty.
+     */
+    public int removeLast() throws NoSuchElementException {
+        if (size != 0) {
+            return getUnchecked(--size);
+        }
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * Removes all values in the given range of index.
+     * Shifts any succeeding elements to the left (reduces their index).
+     *
+     * @param lower Index of the first element to remove, inclusive.
+     * @param upper Index after the last element to be removed.
+     */
+    @Override
+    protected void removeRange(int lower, int upper) {
+        ArgumentChecks.ensureValidIndexRange(size, lower, upper);
+        int lo = lower * bitCount;
+        int hi = upper * bitCount;
+        final int offset = (lo & OFFSET_MASK);
+        if (offset == (hi & OFFSET_MASK)) {
+            /*
+             * Optimisation for a special case which can be handled by a call
+             * to System.arracopy, which is much faster than our loop.
+             */
+            lo >>>= BASE_SHIFT;
+            hi >>>= BASE_SHIFT;
+            final long mask = (1L << offset) - 1;
+            final long save = values[lo] & mask;
+            System.arraycopy(values, hi, values, lo, length(size) - hi);
+            values[lo] = (values[lo] & ~mask) | save;
+        } else {
+            /*
+             * The general case, when the packed values after the range
+             * removal don't have the same offset than the original values.
+             */
+            while (upper < size) {
+                setUnchecked(lower++, getUnchecked(upper++));
+            }
+        }
+        this.size -= (upper - lower);
+    }
+
+    /**
+     * Returns the occurrence of the given value in this list.
+     *
+     * @param  value The value to search for.
+     * @return The number of time the given value occurs in this list.
+     */
+    public int occurrence(final int value) {
+        int count = 0;
+        final int size = this.size;
+        for (int i=0; i<size; i++) {
+            if (getUnchecked(i) == value) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Trims the capacity of this list to be its current size.
+     */
+    public void trimToSize() {
+        values = ArraysExt.resize(values, length(size));
+    }
+
+    /**
+     * Returns a clone of this list.
+     *
+     * @return A clone of this list.
+     */
+    @Override
+    public IntegerList clone() {
+        final IntegerList clone;
+        try {
+            clone = (IntegerList) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError(e);
+        }
+        clone.values = clone.values.clone();
+        return clone;
+    }
+
+    /**
+     * Invokes {@link #trimToSize()} before serialization in order to make the stream more compact.
+     */
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        trimToSize();
+        out.defaultWriteObject();
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java?rev=1720014&r1=1720013&r2=1720014&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java [UTF-8] Mon Dec 14 21:09:23 2015
@@ -87,6 +87,11 @@ public final class Messages extends Inde
         public static final short CreatedNamedObject_2 = 16;
 
         /**
+         * {0} dataset version {1} on “{2}” version {3}.
+         */
+        public static final short DataBase_4 = 28;
+
+        /**
          * The {0} environment variable is defined, but the given “{1}” value is not an existing
          * directory.
          */

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties?rev=1720014&r1=1720013&r2=1720014&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties [ISO-8859-1] Mon Dec 14 21:09:23 2015
@@ -20,6 +20,7 @@ ConformanceMeansDatumShift      = This r
 ConstantProjParameterValue_1    = This parameter is shown for completeness, but should never have a value different than {0} for this projection.
 CreatedNamedObject_2            = Created an instance of \u2018{0}\u2019 named \u201c{1}\u201d.
 CreatedIdentifiedObject_3       = Created an instance of \u2018{0}\u2019 named \u201c{1}\u201d with \u201c{2}\u201d identifier.
+DataBase_4                      = {0} dataset version {1} on \u201c{2}\u201d version {3}.
 DataDirectory_2                 = Environment variable {0} specifies the \u201c{1}\u201d data directory.
 DataDirectoryDoesNotExist_2     = The {0} environment variable is defined, but the given \u201c{1}\u201d value is not an existing directory.
 DataDirectoryNotAccessible_2    = Apache SIS is not authorized to access the \u201c{1}\u201d sub-directory in the directory given by the {0} environment variable.

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties?rev=1720014&r1=1720013&r2=1720014&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties [ISO-8859-1] Mon Dec 14 21:09:23 2015
@@ -27,6 +27,7 @@ ConformanceMeansDatumShift      = Ce r\u
 ConstantProjParameterValue_1    = Ce param\u00e8tre est montr\u00e9 pour \u00eatre plus complet, mais sa valeur ne devrait jamais \u00eatre diff\u00e9rente de {0} pour cette projection.
 CreatedNamedObject_2            = Cr\u00e9ation d\u2019une instance de \u2018{0}\u2019 nomm\u00e9e \u00ab\u202f{1}\u202f\u00bb.
 CreatedIdentifiedObject_3       = Cr\u00e9ation d\u2019une instance de \u2018{0}\u2019 nomm\u00e9e \u00ab\u202f{1}\u202f\u00bb avec l\u2019identifiant \u00ab\u202f{2}\u202f\u00bb.
+DataBase_4                      = Base de donn\u00e9es {0} version {1} sur \u00ab\u202f{2}\u202f\u00bb version {3}.
 DataDirectory_2                 = La variable environnementale {0} sp\u00e9cifie le r\u00e9pertoire de donn\u00e9es \u00ab\u202f{1}\u202f\u00bb.
 DataDirectoryDoesNotExist_2     = La variable environnementale {0} est bien d\u00e9finie, mais sa valeur \u00ab\u202f{1}\u202f\u00bb n\u2019est pas un r\u00e9pertoire existant.
 DataDirectoryNotAccessible_2    = Apache SIS n\u2019a pas l\u2019autorisation d\u2019acc\u00e9der au sous-r\u00e9pertoire \u00ab\u202f{1}\u202f\u00bb dans le r\u00e9pertoire donn\u00e9 par la variable environnementale {0}.




Mime
View raw message