sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1762965 - in /sis/branches/JDK8: core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-feature/src/main/java/org/apache/sis/feature/builder/ core/sis-utility/src/main/java/org/apache/sis/internal/util/ core/sis-utility/src/test/j...
Date Fri, 30 Sep 2016 21:59:36 GMT
Author: desruisseaux
Date: Fri Sep 30 21:59:36 2016
New Revision: 1762965

URL: http://svn.apache.org/viewvc?rev=1762965&view=rev
Log:
First draft of a convenience class for managing FeatureTypes in DataStore implementations.

Added:
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/GenericNameMap.java
  (with props)
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalNameException.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/CollectionsExtTest.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryDataTransfer.java

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java?rev=1762965&r1=1762964&r2=1762965&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
[UTF-8] Fri Sep 30 21:59:36 2016
@@ -381,7 +381,7 @@ public class DefaultFeatureType extends
         }
         /*
          * If some properties use long name of the form "head:tip", creates short aliases
containing only the "tip"
-         * name for convenience, provided that it does not create ambiguity. If an short
alias could map to two or
+         * name for convenience, provided that it does not create ambiguity.  If a short
alias could map to two or
          * more properties, then this alias is not added.
          *
          * In the 'aliases' map below, null values will be assigned to ambiguous short names.

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java?rev=1762965&r1=1762964&r2=1762965&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/builder/TypeBuilder.java
[UTF-8] Fri Sep 30 21:59:36 2016
@@ -34,6 +34,7 @@ import org.apache.sis.util.Debug;
 // Branch-dependent imports
 import java.util.Objects;
 import org.opengis.feature.IdentifiedType;
+import org.opengis.feature.PropertyNotFoundException;
 
 
 /**
@@ -360,7 +361,7 @@ public abstract class TypeBuilder implem
             }
         }
         if (ambiguity != null) {
-            throw new IllegalArgumentException(errors().getString(
+            throw new PropertyNotFoundException(errors().getString(
                     Errors.Keys.AmbiguousName_3, best.getName(), ambiguity.getName(), name));
         }
         return best;

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java?rev=1762965&r1=1762964&r2=1762965&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
[UTF-8] Fri Sep 30 21:59:36 2016
@@ -677,18 +677,49 @@ public final class CollectionsExt extend
         final List<V> singleton = Collections.singletonList(value);
         List<V> values = map.put(key, singleton);
         if (values == null) {
-            return singleton;
+            return singleton;               // This is the most common case.
         }
         if (values.size() <= 1) {
             values = new ArrayList<>(values);
-            if (map.put(key, values) != singleton) {
-                throw new ConcurrentModificationException();
-            }
+        }
+        if (map.put(key, values) != singleton) {
+            throw new ConcurrentModificationException();
         }
         values.add(value);
         return values;
     }
 
+    /**
+     * Removes a value in a pseudo multi-values map. The multi-values map is simulated by
a map of lists.
+     * If more than one occurrence of the given value is found in the list, only the first
occurrence is
+     * removed. If the list become empty after this method call, that list is removed from
the map.
+     *
+     * @param  <K>    the type of key elements in the map.
+     * @param  <V>    the type of value elements in the lists.
+     * @param  map    the multi-values map where to remove an element.
+     * @param  key    the key of the element to remove. Can be null if the given map supports
null keys.
+     * @param  value  the value of the element to remove. Can be null.
+     * @return list of remaining elements after the removal, or {@code null} if no list is
mapped to the given key.
+     */
+    public static <K,V> List<V> removeFromMultiValuesMap(final Map<K,List<V>>
map, final K key, final V value) {
+        List<V> values = map.get(key);
+        if (values != null) {
+            final boolean isEmpty;
+            switch (values.size()) {
+                case 0:  isEmpty = true; break;
+                case 1:  isEmpty = Objects.equals(value, values.get(0)); break;
+                default: isEmpty = values.remove(value) && values.isEmpty(); break;
+            }
+            if (isEmpty) {
+                if (map.remove(key) != values) {
+                    throw new ConcurrentModificationException();
+                }
+                values = Collections.emptyList();
+            }
+        }
+        return values;
+    }
+
     /**
      * Creates a (<cite>name</cite>, <cite>element</cite>) mapping
for the given collection of elements.
      * If the name of an element is not all lower cases, then this method also adds an entry
for the

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/CollectionsExtTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/CollectionsExtTest.java?rev=1762965&r1=1762964&r2=1762965&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/CollectionsExtTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/CollectionsExtTest.java
[UTF-8] Fri Sep 30 21:59:36 2016
@@ -96,27 +96,32 @@ public final strictfp class CollectionsE
     }
 
     /**
-     * Tests {@link CollectionsExt#addToMultiValuesMap(Map, Object, Object)}.
+     * Tests {@link CollectionsExt#addToMultiValuesMap(Map, Object, Object)}, then
+     * opportunistically tests {@link CollectionsExt#removeFromMultiValuesMap(Map, Object,
Object)},
      */
     @Test
-    public void testAddToMultiValuesMap() {
+    public void testAddAndRemoveToMultiValuesMap() {
         final Map<String, List<Integer>> map = new LinkedHashMap<>();
         final Integer A1 = 2;
         final Integer A2 = 4;
         final Integer B1 = 3;
         final Integer B2 = 6;
         final Integer B3 = 9;
-        assertArrayEquals(new Integer[] {A1},
-                CollectionsExt.addToMultiValuesMap(map, "A", A1).toArray());
-        assertArrayEquals(new Integer[] {B1},
-                CollectionsExt.addToMultiValuesMap(map, "B", B1).toArray());
-        assertArrayEquals(new Integer[] {B1, B2},
-                CollectionsExt.addToMultiValuesMap(map, "B", B2).toArray());
-        assertArrayEquals(new Integer[] {A1, A2},
-                CollectionsExt.addToMultiValuesMap(map, "A", A2).toArray());
-        assertArrayEquals(new Integer[] {B1, B2, B3},
-                CollectionsExt.addToMultiValuesMap(map, "B", B3).toArray());
-        assertArrayEquals(new String[] {"A", "B"}, map.keySet().toArray());
+        assertArrayEquals(new Integer[] {A1},         CollectionsExt.addToMultiValuesMap(map,
"A", A1).toArray());
+        assertArrayEquals(new Integer[] {B1},         CollectionsExt.addToMultiValuesMap(map,
"B", B1).toArray());
+        assertArrayEquals(new Integer[] {B1, B2},     CollectionsExt.addToMultiValuesMap(map,
"B", B2).toArray());
+        assertArrayEquals(new Integer[] {A1, A2},     CollectionsExt.addToMultiValuesMap(map,
"A", A2).toArray());
+        assertArrayEquals(new Integer[] {B1, B2, B3}, CollectionsExt.addToMultiValuesMap(map,
"B", B3).toArray());
+        assertArrayEquals(new String[]  {"A", "B"},   map.keySet().toArray());
+        assertArrayEquals(new Integer[] {A1, A2},     map.get("A").toArray());
+        assertArrayEquals(new Integer[] {B1, B2, B3}, map.get("B").toArray());
+
+        assertNull(                                   CollectionsExt.removeFromMultiValuesMap(map,
"C", A2));
+        assertArrayEquals(new Integer[] {A1},         CollectionsExt.removeFromMultiValuesMap(map,
"A", A2).toArray());
+        assertArrayEquals(new Integer[] {B1, B3},     CollectionsExt.removeFromMultiValuesMap(map,
"B", B2).toArray());
+        assertArrayEquals(new Integer[] {},           CollectionsExt.removeFromMultiValuesMap(map,
"A", A1).toArray());
+        assertArrayEquals(new String[]  {"B"},        map.keySet().toArray());
+        assertArrayEquals(new Integer[] {B1, B3},     map.get("B").toArray());
     }
 
     /**

Added: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/GenericNameMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/GenericNameMap.java?rev=1762965&view=auto
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/GenericNameMap.java
(added)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/GenericNameMap.java
[UTF-8] Fri Sep 30 21:59:36 2016
@@ -0,0 +1,179 @@
+/*
+ * 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.internal.storage;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ConcurrentModificationException;
+import org.opengis.util.GenericName;
+import org.opengis.util.ScopedName;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.storage.IllegalNameException;
+
+
+/**
+ * Helper class for mapping {@link GenericName} instances and their aliases to arbitrary
objects.
+ * The objects can be read and written using a {@link String} representation of their name,
+ * or an alias when there is no ambiguity.
+ *
+ * <div class="section">Synchronization</div>
+ * This class is not thread-safe. Synchronization, if desired, shall be done by the caller.
+ * The caller is typically a {@link org.apache.sis.storage.DataStore} implementation.
+ *
+ * @param <E> the type of elements associated with the names.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+public final class GenericNameMap<E> {
+    /**
+     * All aliases found for all names given to the {@link #add(GenericName, Object)} method.
+     * Keys are aliases (never the explicitely given names) and values are names for which
the key is an alias.
+     * Each {@code List<GenericName>} instance contains exactly one name if there is
no ambiguity.
+     * If the list contains more than one name, this means that the alias is ambiguous.
+     */
+    private final Map<String, List<GenericName>> aliases;
+
+    /**
+     * The user-specified values associated to names and aliases. If a value is absent, it
may means either that the
+     * given name does not exist, or that the given name is an ambiguous alias. Those two
cases can be distinguished
+     * by checking if the key exists in the {@link #aliases} map.
+     */
+    private final Map<String,E> values;
+
+    /**
+     * Creates a new "{@code GenericName} to object" mapping.
+     */
+    public GenericNameMap() {
+        aliases = new HashMap<>();
+        values  = new HashMap<>();
+    }
+
+    /**
+     * Returns the value associated to the given name.
+     *
+     * @param  name  the name for which to get a value.
+     * @return value associated to the given object.
+     * @throws IllegalNameException if the given name was not found or is ambiguous.
+     */
+    public E get(final String name) throws IllegalNameException {
+        final E value = values.get(name);
+        if (value != null) {
+            return value;
+        }
+        final List<GenericName> nc = aliases.get(name);
+        final String message;
+        if (nc == null) {
+            message = Errors.format(Errors.Keys.ElementNotFound_1, name);
+        } else if (nc.size() >= 2) {
+            message = Errors.format(Errors.Keys.AmbiguousName_3, nc.get(0), nc.get(1), name);
+        } else {
+            return null;    // Name was explicitely associated to null value (actually not
allowed by current API).
+        }
+        throw new IllegalNameException(message);
+    }
+
+    /**
+     * Adds a value for the given name if none exist.
+     * If a previous value already exists for the given name, then an exception is thrown.
+     *
+     * @param  name   the name for which to add a value.
+     * @param  value  the value to add (can not be null).
+     * @throws IllegalNameException if another element is already registered for the given
name.
+     */
+    public void add(final GenericName name, final E value) throws IllegalNameException {
+        ArgumentChecks.ensureNonNull("name",  name);
+        ArgumentChecks.ensureNonNull("value", value);
+        String key = name.toString();
+        if (values.putIfAbsent(key, value) != null) {
+            throw new IllegalNameException(Errors.format(Errors.Keys.ElementAlreadyPresent_1,
key));
+        }
+        GenericName tail = name;
+        while (tail instanceof ScopedName) {
+            tail = ((ScopedName) tail).tail();
+            key = tail.toString();
+            if (CollectionsExt.addToMultiValuesMap(aliases, key, name).size() > 1) {
+                /*
+                 * If there is more than one GenericName for the same alias, we have an ambiguity.
+                 * Remove any value associated to that alias. The 'get' method in this class
will
+                 * know that the value is missing because of ambiguous name.
+                 */
+                values.remove(key);
+            } else if (values.put(key, value) != null) {
+                /*
+                 * If no previous GenericName existed for that alias, then no value should
exist for that alias.
+                 * If a previous value existed, then (assuming that we do not have a bug
in our algorithm) this
+                 * object changed in a concurrent thread during this method execution.  We
do not try to detect
+                 * all such errors, but this check is an easy one to perform opportunistically.
+                 */
+                throw new ConcurrentModificationException();
+            }
+        }
+    }
+
+    /**
+     * Removes the value associated to the given name.
+     * If no value is associated to the given name, then this method does nothing.
+     *
+     * @param  name  the name for which to remove value.
+     * @return {@code true} if the value was removed, or {@code false} if no value was defined
for the given name.
+     * @throws IllegalNameException if inconsistency are found between the given name and
the one which was given
+     *         to the {@link #add(GenericName, Object)} method. An example of inconsistency
is a name having the
+     *         same string representation, but for which {@link ScopedName#tail()} returns
different values.
+     */
+    public boolean remove(GenericName name) throws IllegalNameException {
+        ArgumentChecks.ensureNonNull("name",  name);
+        final String key = name.toString();
+        if (values.remove(key) == null) {
+            return false;
+        }
+        boolean error = false;
+        while (name instanceof ScopedName) {
+            name = ((ScopedName) name).tail();
+            String tail = name.toString();
+            final List<GenericName> remaining = CollectionsExt.removeFromMultiValuesMap(aliases,
tail, name);
+            /*
+             * The list of remaining GenericNames may be empty, but should never be null
unless the tail is
+             * inconsistent with the one found by the 'add(GenericName, Object) method. 
Otherwise if there
+             * is exactly one remaining GenericName, then the alias is not ambiguous anymore
for that name.
+             */
+            error |= (remaining == null);
+            if (remaining != null && remaining.size() == 1) {
+                final String select = remaining.get(0).toString();
+                /*
+                 * Extra check in case a GenericName implementation provides a broken equals(Object)
method:
+                 * If the remaining name is the name that we were supposed to remove (when
comparing String
+                 * representations instead than GenericName instances), then do not re-put
the value.
+                 */
+                if (!key.equals(select)) {
+                    error |= (values.putIfAbsent(tail, values.get(select)) != null);
+                } else if (aliases.remove(tail) != remaining) {
+                    throw new ConcurrentModificationException();
+                }
+            }
+        }
+        if (error) {
+            throw new IllegalNameException();
+        }
+        return true;
+    }
+}

Propchange: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/GenericNameMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/GenericNameMap.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryDataTransfer.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryDataTransfer.java?rev=1762965&r1=1762964&r2=1762965&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryDataTransfer.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MemoryDataTransfer.java
[UTF-8] Fri Sep 30 21:59:36 2016
@@ -94,7 +94,7 @@ final class MemoryDataTransfer implement
     /**
      * Delegates to the actual implementation.
      */
-    @Override public String filename()                  {return filename();}
+    @Override public String filename()                  {return reader.filename();}
     @Override public int    dataSizeShift()             {return reader.dataSizeShift();}
     @Override public Object dataArray()                 {return reader.dataArray();}
     @Override public Buffer view()                      {return reader.view();}

Added: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalNameException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalNameException.java?rev=1762965&view=auto
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalNameException.java
(added)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalNameException.java
[UTF-8] Fri Sep 30 21:59:36 2016
@@ -0,0 +1,73 @@
+/*
+ * 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.storage;
+
+
+/**
+ * Thrown when an invalid name is used for identifying a coverage, a feature or other kind
of element in a data store.
+ * A name may be invalid because no coverage or feature exists in the {@link DataStore} for
that name,
+ * or because the name is ambiguous (in which case the exception message should explain why),
+ * or because the name of a new {@linkplain org.apache.sis.feature.DefaultFeatureType feature
type}
+ * to add in the {@code DataStore} conflicts with the name of an existing feature type.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+public class IllegalNameException extends DataStoreException {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 2435437568097737351L;
+
+    /**
+     * Creates an exception with no cause and no details message.
+     */
+    public IllegalNameException() {
+        super();
+    }
+
+    /**
+     * Creates an exception with the specified details message.
+     *
+     * @param message  the detail message.
+     */
+    public IllegalNameException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Creates an exception with the specified cause and no details message.
+     *
+     * @param cause  the cause for this exception.
+     */
+    public IllegalNameException(final Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Creates an exception with the specified details message and cause.
+     *
+     * @param message  the detail message.
+     * @param cause    the cause for this exception.
+     */
+    public IllegalNameException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}

Propchange: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalNameException.java
------------------------------------------------------------------------------
    svn:eol-style = native

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



Mime
View raw message