sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1677036 - in /sis/branches/JDK8/core/sis-feature/src: main/java/org/apache/sis/feature/ test/java/org/apache/sis/feature/
Date Thu, 30 Apr 2015 18:01:55 GMT
Author: desruisseaux
Date: Thu Apr 30 18:01:54 2015
New Revision: 1677036

URL: http://svn.apache.org/r1677036
Log:
Feature: initial support of parameterless operations used as "virtual attribute".
Existing tests continue to pass, new tests still need to be written for the new capability.

Added:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractOperation.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAssociation.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/NoOperation.java

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java?rev=1677036&r1=1677035&r2=1677036&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractFeature.java
[UTF-8] Thu Apr 30 18:01:54 2015
@@ -37,6 +37,7 @@ import org.opengis.feature.Feature;
 import org.opengis.feature.FeatureType;
 import org.opengis.feature.FeatureAssociation;
 import org.opengis.feature.FeatureAssociationRole;
+import org.opengis.feature.Operation;
 
 
 /**
@@ -71,7 +72,7 @@ import org.opengis.feature.FeatureAssoci
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.5
+ * @version 0.6
  * @module
  *
  * @see DefaultFeatureType#newInstance()
@@ -118,7 +119,10 @@ public abstract class AbstractFeature im
     }
 
     /**
-     * Returns the property (attribute, operation or association) of the given name.
+     * Returns the property (attribute, feature association or operation result) of the given
name.
+     * If the property type is a parameterless {@linkplain AbstractOperation operation},
then this
+     * method may return the result of {@linkplain AbstractOperation#apply executing} the
operation
+     * on this feature, at implementation choice.
      *
      * <div class="note"><b>Tip:</b> This method returns the property <em>instance</em>.
If only the property
      * <em>value</em> is desired, then {@link #getPropertyValue(String)} is preferred
since it gives to SIS a
@@ -135,7 +139,7 @@ public abstract class AbstractFeature im
     public abstract Property getProperty(final String name) throws IllegalArgumentException;
 
     /**
-     * Sets the property (attribute, operation or association).
+     * Sets the property (attribute or feature association).
      * The given property shall comply to the following conditions:
      *
      * <ul>
@@ -188,7 +192,8 @@ public abstract class AbstractFeature im
      *
      * @param  name The name of the property to create.
      * @return A {@code Property} of the given name.
-     * @throws IllegalArgumentException If the given argument is not an attribute or association
name of this feature.
+     * @throws IllegalArgumentException If the given argument is not the name of an attribute
or
+     *         feature association of this feature.
      */
     final Property createProperty(final String name) throws IllegalArgumentException {
         final PropertyType pt = type.getProperty(name);
@@ -201,6 +206,45 @@ public abstract class AbstractFeature im
         }
     }
 
+    /**
+     * Executes the parameterless operation of the given name and returns its result.
+     */
+    final Property getOperationResult(final String name) {
+        /*
+         * The (Operation) cast below should never fail (unless the DefaultFeatureType in
not really immutable,
+         * which would be a contract violation) because all callers shall ensure that this
method is invoked in
+         * a context where the following assertion holds.
+         */
+        assert DefaultFeatureType.OPERATION_INDEX.equals(((DefaultFeatureType) type).indices().get(name))
: name;
+        return ((Operation) type.getProperty(name)).apply(this, null);
+    }
+
+    /**
+     * Executes the parameterless operation of the given name and returns the value of its
result.
+     */
+    final Object getOperationValue(final String name) {
+        final Property result = getOperationResult(name);
+        if (result instanceof Attribute<?>) {
+            return getAttributeValue((Attribute<?>) result);
+        } else if (result instanceof FeatureAssociation) {
+            return getAssociationValue((FeatureAssociation) result);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Executes the parameterless operation of the given name and sets the value of its result.
+     */
+    final void setOperationValue(final String name, final Object value) {
+        final Property result = getOperationResult(name);
+        if (result != null) {
+            setPropertyValue(result, value);
+        } else {
+            throw new IllegalStateException(Errors.format(Errors.Keys.CanNotSetPropertyValue_1,
name));
+        }
+    }
+
     /**
      * Returns the default value to be returned by {@link #getPropertyValue(String)}
      * for the property of the given name.

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractOperation.java?rev=1677036&r1=1677035&r2=1677036&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractOperation.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractOperation.java
[UTF-8] Thu Apr 30 18:01:54 2015
@@ -22,13 +22,14 @@ import org.opengis.parameter.GeneralPara
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.ParameterValueGroup;
 import org.apache.sis.referencing.IdentifiedObjects;
-import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Debug;
 
 // Branch-dependent imports
 import java.util.Objects;
 import java.util.function.BiFunction;
+import org.opengis.feature.Attribute;
 import org.opengis.feature.Feature;
+import org.opengis.feature.FeatureAssociation;
 import org.opengis.feature.IdentifiedType;
 import org.opengis.feature.Operation;
 import org.opengis.feature.Property;
@@ -46,6 +47,9 @@ import org.opengis.feature.Property;
  * <div class="note"><b>Example:</b> a mutator operation may raise the
height of a dam. This changes
  * may affect other properties like the watercourse and the reservoir associated with the
dam.</div>
  *
+ * The value is computed, or the operation is executed, by {@link #apply(Feature, ParameterValueGroup)}.
+ * If the value is modifiable, new value can be set by call to {@link Attribute#setValue(Object)}.
+ *
  * <div class="warning"><b>Warning:</b> this class is experimental and
may change after we gained more
  * experience on this aspect of ISO 19109.</div>
  *
@@ -60,33 +64,16 @@ public abstract class AbstractOperation
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = 6300319108116735764L;
-
-    /**
-     * A description of the input parameters.
-     */
-    private final ParameterDescriptorGroup parameters;
-
-    /**
-     * The type of the result, or {@code null} if none.
-     */
-    private final IdentifiedType result;
+    private static final long serialVersionUID = -179930765502963170L;
 
     /**
      * Constructs an operation from the given properties. The identification map is given
unchanged to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
      *
      * @param identification The name and other information to be given to this operation.
-     * @param parameters     A description of the input parameters.
-     * @param result         The type of the result, or {@code null} if none.
      */
-    public AbstractOperation(final Map<String,?> identification,
-            final ParameterDescriptorGroup parameters, final IdentifiedType result)
-    {
+    public AbstractOperation(final Map<String,?> identification) {
         super(identification);
-        ArgumentChecks.ensureNonNull("parameters", parameters);
-        this.parameters = parameters;
-        this.result     = result;
     }
 
     /**
@@ -95,9 +82,7 @@ public abstract class AbstractOperation
      * @return Description of the input parameters.
      */
     @Override
-    public ParameterDescriptorGroup getParameters() {
-        return parameters;
-    }
+    public abstract ParameterDescriptorGroup getParameters();
 
     /**
      * Returns the expected result type, or {@code null} if none.
@@ -105,9 +90,7 @@ public abstract class AbstractOperation
      * @return The type of the result, or {@code null} if none.
      */
     @Override
-    public IdentifiedType getResult() {
-        return result;
-    }
+    public abstract IdentifiedType getResult();
 
     /**
      * Executes the operation on the specified feature with the specified parameters.
@@ -127,10 +110,13 @@ public abstract class AbstractOperation
      * <div class="note"><b>Analogy:</b>
      * if we compare {@code Operation} to {@link Method} in the Java language, then this
method is equivalent
      * to {@link Method#apply(Object, Object...)}. The {@code Feature} argument is equivalent
to {@code this}
-     * in the Java language.</div>
+     * in the Java language, and may be {@code null} if the operation does not need a feature
instance
+     * (like static methods in the Java language).</div>
      *
      * @param  feature    The feature on which to execute the operation.
+     *                    Can be {@code null} if the operation does not need feature instance.
      * @param  parameters The parameters to use for executing the operation.
+     *                    Can be {@code null} if the operation does not take any parameters.
      * @return The operation result, or {@code null} if this operation does not produce any
result.
      */
     @Override
@@ -138,16 +124,20 @@ public abstract class AbstractOperation
 
     /**
      * Returns a hash code value for this operation.
+     * The default implementation computes a hash code from the {@linkplain #getParameters()
parameters}
+     * and {@linkplain #getResult() result type}.
      *
      * @return {@inheritDoc}
      */
     @Override
     public int hashCode() {
-        return super.hashCode() + parameters.hashCode() + Objects.hashCode(result);
+        return super.hashCode() + Objects.hashCode(getParameters()) + Objects.hashCode(getResult());
     }
 
     /**
      * Compares this operation with the given object for equality.
+     * The default implementation compares the {@linkplain #getParameters() parameters}
+     * and {@linkplain #getResult() result type}.
      *
      * @return {@inheritDoc}
      */
@@ -158,8 +148,8 @@ public abstract class AbstractOperation
         }
         if (super.equals(obj)) {
             final AbstractOperation that = (AbstractOperation) obj;
-            return parameters.equals(that.parameters) &&
-                   Objects.equals(result, that.result);
+            return Objects.equals(getParameters(), that.getParameters()) &&
+                   Objects.equals(getResult(),     that.getResult());
         }
         return false;
     }
@@ -183,13 +173,14 @@ public abstract class AbstractOperation
             buffer.append('”');
         }
         String separator = " (";
-        for (final GeneralParameterDescriptor param : parameters.descriptors()) {
+        for (final GeneralParameterDescriptor param : getParameters().descriptors()) {
             buffer.append(separator).append(IdentifiedObjects.toString(param.getName()));
             separator = ", ";
         }
         if (separator == ", ") { // Identity comparaison is okay here.
             buffer.append(')');
         }
+        final IdentifiedType result = getResult();
         if (result != null) {
             buffer.append(" : ").append(result.getName());
         }

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=1677036&r1=1677035&r2=1677036&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultFeatureType.java
[UTF-8] Thu Apr 30 18:01:54 2015
@@ -31,6 +31,7 @@ import org.opengis.util.NameFactory;
 import org.opengis.util.LocalName;
 import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
+import org.opengis.parameter.ParameterDescriptorGroup;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.collection.Containers;
@@ -44,6 +45,7 @@ import org.opengis.feature.AttributeType
 import org.opengis.feature.Feature;
 import org.opengis.feature.FeatureType;
 import org.opengis.feature.FeatureAssociationRole;
+import org.opengis.feature.Operation;
 
 
 /**
@@ -177,6 +179,7 @@ public class DefaultFeatureType extends
     /**
      * Indices of properties in an array of properties similar to {@link #properties},
      * but excluding operations. This map includes the properties from the super-types.
+     * Parameterless operations (to be handled in a special way) are identified by index
-1.
      *
      * The size of this map may be smaller than the {@link #byName} size.
      * This map shall not be modified after construction.
@@ -184,6 +187,12 @@ public class DefaultFeatureType extends
     private transient Map<String, Integer> indices;
 
     /**
+     * Value in {@link #indices} map for parameterless operations. Those operations are not
stored
+     * in feature instances, but can be handled as virtual attributes computed on-the-fly.
+     */
+    static final Integer OPERATION_INDEX = -1;
+
+    /**
      * Constructs a feature type from the given properties. The identification map is given
unchanged to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
      * The following table is a reminder of main (not all) recognized map entries:
@@ -292,6 +301,7 @@ public class DefaultFeatureType extends
          * in account only the most specific ones.
          */
         isSimple = true;
+        int index = 0;
         int mandatory = 0; // Count of mandatory properties.
         for (final Map.Entry<String,PropertyType> entry : byName.entrySet()) {
             final int minimumOccurs, maximumOccurs;
@@ -305,11 +315,14 @@ public class DefaultFeatureType extends
                 maximumOccurs = ((FeatureAssociationRole) property).getMaximumOccurs();
                 isSimple = false;
             } else {
+                if (isParameterlessOperation(property)) {
+                    indices.put(entry.getKey(), OPERATION_INDEX);
+                }
                 continue; // For feature operations, maximumOccurs is implicitly 0.
             }
             if (maximumOccurs != 0) {
                 isSimple &= (maximumOccurs == 1);
-                indices.put(entry.getKey(), indices.size());
+                indices.put(entry.getKey(), index++);
                 if (minimumOccurs != 0) {
                     mandatory++;
                 }
@@ -339,7 +352,9 @@ public class DefaultFeatureType extends
                 final String tip = entry.getKey();
                 if (byName.putIfAbsent(tip, property) == null) {
                     // This block is skipped if there is properties named "tip" and "head:tip".
-                    if (indices.put(tip, indices.get(property.getName().toString())) != null)
{
+                    // The 'indices' value may be null if the property is an operation.
+                    final Integer value = indices.get(property.getName().toString());
+                    if (value != null && indices.put(tip, value) != null) {
                         throw new AssertionError(tip);  // Should never happen.
                     }
                 }
@@ -490,6 +505,20 @@ public class DefaultFeatureType extends
         return resolved;
     }
 
+    /**
+     * Returns {@code true} if the given property type stands for a parameterless operation
which return a result.
+     *
+     * @see #OPERATION_INDEX
+     */
+    private static boolean isParameterlessOperation(final PropertyType type) {
+        if (type instanceof Operation) {
+            final ParameterDescriptorGroup parameters = ((Operation) type).getParameters();
+            return ((parameters == null) || parameters.descriptors().isEmpty())
+                   && ((Operation) type).getResult() != null;
+        }
+        return false;
+    }
+
 
     // -------- END OF CONSTRUCTORS ------------------------------------------------------------------------------
 

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java?rev=1677036&r1=1677035&r2=1677036&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DefaultOperation.java
[UTF-8] Thu Apr 30 18:01:54 2015
@@ -19,6 +19,7 @@ package org.apache.sis.feature;
 import java.util.Map;
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterDescriptorGroup;
+import org.apache.sis.util.ArgumentChecks;
 
 // Branch-dependent imports
 import org.opengis.feature.Feature;
@@ -27,7 +28,7 @@ import org.opengis.feature.Property;
 
 
 /**
- * @deprecated Renamed {@link AbstractOperation}.
+ * @deprecated Replaced by {@link AbstractOperation}.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
@@ -42,6 +43,16 @@ public class DefaultOperation extends Ab
     private static final long serialVersionUID = 6300319108116735764L;
 
     /**
+     * A description of the input parameters.
+     */
+    private final ParameterDescriptorGroup parameters;
+
+    /**
+     * The type of the result, or {@code null} if none.
+     */
+    private final IdentifiedType result;
+
+    /**
      * Constructs an operation from the given properties. The identification map is given
unchanged to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
      *
@@ -52,7 +63,30 @@ public class DefaultOperation extends Ab
     public DefaultOperation(final Map<String,?> identification,
             final ParameterDescriptorGroup parameters, final IdentifiedType result)
     {
-        super(identification, parameters, result);
+        super(identification);
+        ArgumentChecks.ensureNonNull("parameters", parameters);
+        this.parameters = parameters;
+        this.result     = result;
+    }
+
+    /**
+     * Returns a description of the input parameters.
+     *
+     * @return Description of the input parameters.
+     */
+    @Override
+    public ParameterDescriptorGroup getParameters() {
+        return parameters;
+    }
+
+    /**
+     * Returns the expected result type, or {@code null} if none.
+     *
+     * @return The type of the result, or {@code null} if none.
+     */
+    @Override
+    public IdentifiedType getResult() {
+        return result;
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java?rev=1677036&r1=1677035&r2=1677036&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
[UTF-8] Thu Apr 30 18:01:54 2015
@@ -38,7 +38,7 @@ import org.opengis.feature.FeatureAssoci
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Marc le Bihan
  * @since   0.5
- * @version 0.5
+ * @version 0.6
  * @module
  *
  * @see SparseFeature
@@ -58,7 +58,7 @@ final class DenseFeature extends Abstrac
     private final Map<String, Integer> indices;
 
     /**
-     * The properties (attributes, operations, feature associations) of this feature.
+     * The properties (attributes or feature associations) in this feature.
      *
      * Conceptually, values in this array are {@link Property} instances. However at first
we will store only
      * the property <em>values</em>, and convert to an array of type {@code Property[]}
only when at least one
@@ -78,10 +78,12 @@ final class DenseFeature extends Abstrac
     }
 
     /**
-     * Returns the index for the property of the given name.
+     * Returns the index for the property of the given name, or {@link DefaultFeatureType#OPERATION_INDEX}
+     * if the property is a parameterless operation.
      *
      * @param  name The property name.
-     * @return The index for the property of the given name.
+     * @return The index for the property of the given name,
+     *         or a negative value if the property is a parameterless operation.
      * @throws IllegalArgumentException If the given argument is not a property name of this
feature.
      */
     private int getIndex(final String name) throws IllegalArgumentException {
@@ -102,17 +104,22 @@ final class DenseFeature extends Abstrac
     @Override
     public Property getProperty(final String name) throws IllegalArgumentException {
         ArgumentChecks.ensureNonNull("name", name);
-        final int index = getIndex(name); // Invoked first because this method checks name
validity.
-
-        // Are the properties currently initialized? If not, wrap the values we can find.
+        final int index = getIndex(name);
+        if (index < 0) {
+            return getOperationResult(name);
+        }
+        /*
+         * Are the Property instances currently initialized? If not, wrap the values we can
find.
+         * This is a all-or-nothing converion (we do not wrap only the requested property)
+         * for avoiding the additional complexity of remembering which values were wrapped.
+         */
         if (!(properties instanceof Property[])) {
             wrapValuesInProperties();
         }
-
-        // Find the wanted property.
+        /*
+         * Find the wanted property. If the property still have a null value, we create it
from its type.
+         */
         Property property = ((Property[]) properties)[index];
-
-        // If the property still have a null value, we create it, but we can only tell its
type.
         if (property == null) {
             property = createProperty(name);
             properties[index] = property;
@@ -135,6 +142,10 @@ final class DenseFeature extends Abstrac
         if (!(properties instanceof Property[])) {
             wrapValuesInProperties();
         }
+        /*
+         * Following index should never be OPERATION_INDEX (a negative value) because the
call
+         * to 'verifyPropertyType(name, property)' shall have rejected all Operation types.
+         */
         properties[indices.get(name)] = property;
     }
 
@@ -167,8 +178,12 @@ final class DenseFeature extends Abstrac
     @Override
     public Object getPropertyValue(final String name) throws IllegalArgumentException {
         ArgumentChecks.ensureNonNull("name", name);
+        final int index = getIndex(name);
+        if (index < 0) {
+            return getOperationValue(name);
+        }
         if (properties != null) {
-            final Object element = properties[getIndex(name)];
+            final Object element = properties[index];
             if (element != null) {
                 if (!(properties instanceof Property[])) {
                     return element; // Most common case.
@@ -196,6 +211,10 @@ final class DenseFeature extends Abstrac
     public void setPropertyValue(final String name, Object value) throws IllegalArgumentException
{
         ArgumentChecks.ensureNonNull("name", name);
         final int index = getIndex(name);
+        if (index < 0) {
+            setOperationValue(name, value);
+            return;
+        }
         if (properties == null) {
             final int n = indices.size();
             properties = (value != null) ? new Object[n] : new Property[n];

Added: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java?rev=1677036&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java
(added)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/LinkOperation.java
[UTF-8] Thu Apr 30 18:01:54 2015
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.feature;
+
+import java.util.Map;
+import java.util.HashMap;
+import org.opengis.metadata.Identifier;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.apache.sis.parameter.DefaultParameterDescriptorGroup;
+import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.util.ArgumentChecks;
+
+// Branch-dependent imports
+import org.opengis.feature.Feature;
+import org.opengis.feature.IdentifiedType;
+import org.opengis.feature.Property;
+
+
+/**
+ * A link operation, which is like a redirection or an alias.
+ * The operation acts like a reference to another property.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+final class LinkOperation extends AbstractOperation {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 765096861589501215L;
+
+    /**
+     * Creates a parameter descriptor in the Apache SIS namespace. This convenience method
shall
+     * not be in public API, because users should define operations in their own namespace.
+     *
+     * <div class="note"><b>Note:</b>
+     * this method is shared by other operations in this package, but is declared here in
order to delay
+     * {@link org.apache.sis.parameter} classes loading until we need to instantiate an operation
like this
+     * {@code LinkOperation}. Since {@code LinkOperation} is very light and often used, the
cost for other
+     * operations of loading this class is considered negligible.</div>
+     */
+    static ParameterDescriptorGroup parameters(final String name, final int minimumOccurs,
+            final ParameterDescriptor<?>... parameters)
+    {
+        final Map<String,Object> properties = new HashMap<>(4);
+        properties.put(ParameterDescriptorGroup.NAME_KEY, name);
+        properties.put(Identifier.AUTHORITY_KEY, Citations.SIS);
+        return new DefaultParameterDescriptorGroup(properties, minimumOccurs, 1);
+    }
+
+    /**
+     * The parameter descriptor for the "Link" operation, which does not take any parameter.
+     */
+    private static final ParameterDescriptorGroup EMPTY_PARAMS = parameters("Link", 1);
+
+    /**
+     * The type of the result.
+     */
+    private final IdentifiedType result;
+
+    /**
+     * The name of the referenced attribute or feature association.
+     */
+    final String propertyName;
+
+    /**
+     * Creates a new link to the given attribute or association.
+     *
+     * @param identification The name of the link, together with optional information.
+     * @param propertyType   The referenced attribute or feature association.
+     */
+    LinkOperation(final Map<String, ?> identification, final IdentifiedType propertyType)
{
+        super(identification);
+        result = propertyType;
+        propertyName = propertyType.getName().toString();
+    }
+
+    /**
+     * Returns a description of the input parameters.
+     */
+    @Override
+    public ParameterDescriptorGroup getParameters() {
+        return EMPTY_PARAMS;
+    }
+
+    /**
+     * Returns the expected result type.
+     */
+    @Override
+    public IdentifiedType getResult() {
+        return result;
+    }
+
+    /**
+     * Returns the property from the referenced attribute of feature association.
+     *
+     * @param  feature    The feature from which to get the property.
+     * @param  parameters Ignored.
+     * @return The property from the given feature.
+     */
+    @Override
+    public Property apply(final Feature feature, final ParameterValueGroup parameters) {
+        ArgumentChecks.ensureNonNull("feature", feature);
+        return feature.getProperty(propertyName);
+    }
+}

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

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

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAssociation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAssociation.java?rev=1677036&r1=1677035&r2=1677036&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAssociation.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAssociation.java
[UTF-8] Thu Apr 30 18:01:54 2015
@@ -129,16 +129,18 @@ final class MultiValuedAssociation exten
     /**
      * Sets the feature values. All previous values are replaced by the given collection.
      *
-     * @param values The new values.
+     * @param newValues The new values.
      */
     @Override
-    public void setValues(final Collection<? extends Feature> values) {
-        ArgumentChecks.ensureNonNull("values", values);
-        final FeatureType base = role.getValueType();
-        this.values.clear();
-        for (final Feature value : values) {
-            ensureValid(base, value.getType());
-            this.values.add(value);
+    public void setValues(final Collection<? extends Feature> newValues) {
+        if (newValues != values) {
+            ArgumentChecks.ensureNonNull("values", newValues);  // The parameter name in
public API is "values".
+            final FeatureType base = role.getValueType();
+            values.clear();
+            for (final Feature value : newValues) {
+                ensureValid(base, value.getType());
+                values.add(value);
+            }
         }
     }
 

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java?rev=1677036&r1=1677035&r2=1677036&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
[UTF-8] Thu Apr 30 18:01:54 2015
@@ -139,13 +139,15 @@ final class MultiValuedAttribute<V> exte
     /**
      * Sets the attribute values. All previous values are replaced by the given collection.
      *
-     * @param values The new values.
+     * @param newValues The new values.
      */
     @Override
-    public void setValues(final Collection<? extends V> values) {
-        ArgumentChecks.ensureNonNull("values", values);
-        this.values.clear();
-        this.values.addAll(values);
+    public void setValues(final Collection<? extends V> newValues) {
+        if (newValues != values) {
+            ArgumentChecks.ensureNonNull("values", newValues);  // The parameter name in
public API is "values".
+            values.clear();
+            values.addAll(newValues);
+        }
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java?rev=1677036&r1=1677035&r2=1677036&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
[UTF-8] Thu Apr 30 18:01:54 2015
@@ -24,12 +24,11 @@ import org.opengis.metadata.quality.Data
 import org.apache.sis.internal.util.Cloner;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CorruptedObjectException;
+import org.apache.sis.util.resources.Errors;
 
 // Branch-dependent imports
 import org.opengis.feature.Property;
-import org.opengis.feature.PropertyType;
 import org.opengis.feature.Attribute;
-import org.opengis.feature.FeatureType;
 import org.opengis.feature.FeatureAssociation;
 
 
@@ -42,7 +41,7 @@ import org.opengis.feature.FeatureAssoci
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
- * @version 0.5
+ * @version 0.6
  * @module
  *
  * @see DenseFeature
@@ -52,7 +51,7 @@ final class SparseFeature extends Abstra
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = -4486200659005766093L;
+    private static final long serialVersionUID = 2954323576287152427L;
 
     /**
      * A {@link #valuesKind} flag meaning that the {@link #properties} map contains raw values.
@@ -70,7 +69,17 @@ final class SparseFeature extends Abstra
     private static final byte CORRUPTED = 2;
 
     /**
-     * The properties (attributes, operations, feature associations) of this feature.
+     * The map of property names to keys in the {@link #properties} map. This map is a reference
to the
+     * {@link DefaultFeatureType#indices} map (potentially shared by many feature instances)
and shall
+     * not be modified.
+     *
+     * <p>We use those indices as {@link #properties} keys instead than using directly
the property names
+     * in order to resolve aliases.</p>
+     */
+    private final Map<String, Integer> indices;
+
+    /**
+     * The properties (attributes or feature associations) in this feature.
      *
      * Conceptually, values in this map are {@link Property} instances. However at first
we will store
      * only the property <em>values</em>, and build the full {@code Property}
objects only if they are
@@ -79,7 +88,7 @@ final class SparseFeature extends Abstra
      *
      * @see #valuesKind
      */
-    private HashMap<String, Object> properties;
+    private HashMap<Integer, Object> properties;
 
     /**
      * {@link #PROPERTIES} if the values in the {@link #properties} map are {@link Property}
instances,
@@ -96,24 +105,56 @@ final class SparseFeature extends Abstra
      *
      * @param type Information about the feature (name, characteristics, <i>etc.</i>).
      */
-    public SparseFeature(final FeatureType type) {
+    public SparseFeature(final DefaultFeatureType type) {
         super(type);
+        indices = type.indices();
         properties = new HashMap<>();
     }
 
     /**
+     * Returns the index for the property of the given name, or {@link DefaultFeatureType#OPERATION_INDEX}
+     * if the property is a parameterless operation.
+     *
+     * @param  name The property name.
+     * @return The index for the property of the given name,
+     *         or a negative value if the property is a parameterless operation.
+     * @throws IllegalArgumentException If the given argument is not a property name of this
feature.
+     */
+    private int getIndex(final String name) throws IllegalArgumentException {
+        final Integer index = indices.get(name);
+        if (index != null) {
+            return index;
+        }
+        throw new IllegalArgumentException(Errors.format(Errors.Keys.PropertyNotFound_2,
getName(), name));
+    }
+
+    /**
+     * Returns the property name at the given index.
+     * Current implementation is inefficient, but this method should rarely be invoked.
+     */
+    private String nameOf(final Integer index) {
+        for (final Map.Entry<String, Integer> entry : indices.entrySet()) {
+            if (index.equals(entry.getValue())) {
+                return entry.getKey();
+            }
+        }
+        // Should never reach this point.
+        throw new AssertionError(index);
+    }
+
+    /**
      * Ensures that the {@link #properties} map contains {@link Property} instances instead
than
      * property values. The conversion, if needed, will be performed at most once per feature.
      */
-    private void ensurePropertyMap() {
+    private void requireMapOfProperties() {
         if (valuesKind != PROPERTIES) {
             if (!properties.isEmpty()) { // The map is typically empty when this method is
first invoked.
                 if (valuesKind != VALUES) {
                     throw new CorruptedObjectException(getName());
                 }
                 valuesKind = CORRUPTED;
-                for (final Map.Entry<String, Object> entry : properties.entrySet())
{
-                    final String key   = entry.getKey();
+                for (final Map.Entry<Integer, Object> entry : properties.entrySet())
{
+                    final String key = nameOf(entry.getKey());
                     final Object value = entry.getValue();
                     if (entry.setValue(createProperty(key, value)) != value) {
                         throw new ConcurrentModificationException(key);
@@ -134,7 +175,7 @@ final class SparseFeature extends Abstra
     @Override
     public Property getProperty(final String name) throws IllegalArgumentException {
         ArgumentChecks.ensureNonNull("name", name);
-        ensurePropertyMap();
+        requireMapOfProperties();
         return getPropertyInstance(name);
     }
 
@@ -144,10 +185,14 @@ final class SparseFeature extends Abstra
      */
     private Property getPropertyInstance(final String name) throws IllegalArgumentException
{
         assert valuesKind == PROPERTIES : valuesKind;
-        Property property = (Property) properties.get(name);
+        final Integer index = getIndex(name);
+        if (index < 0) {
+            return getOperationResult(name);
+        }
+        Property property = (Property) properties.get(index);
         if (property == null) {
             property = createProperty(name);
-            replace(name, null, property);
+            replace(index, null, property);
         }
         return property;
     }
@@ -164,8 +209,12 @@ final class SparseFeature extends Abstra
         ArgumentChecks.ensureNonNull("property", property);
         final String name = property.getName().toString();
         verifyPropertyType(name, property);
-        ensurePropertyMap();
-        properties.put(name, property);
+        requireMapOfProperties();
+        /*
+         * Following index should never be OPERATION_INDEX (a negative value) because the
call
+         * to 'verifyPropertyType(name, property)' shall have rejected all Operation types.
+         */
+        properties.put(indices.get(name), property);
     }
 
     /**
@@ -178,7 +227,11 @@ final class SparseFeature extends Abstra
     @Override
     public Object getPropertyValue(final String name) throws IllegalArgumentException {
         ArgumentChecks.ensureNonNull("name", name);
-        final Object element = properties.get(name);
+        final Integer index = getIndex(name);
+        if (index < 0) {
+            return getOperationValue(name);
+        }
+        final Object element = properties.get(index);
         if (element != null) {
             if (valuesKind == VALUES) {
                 return element; // Most common case.
@@ -191,7 +244,7 @@ final class SparseFeature extends Abstra
             } else {
                 throw new CorruptedObjectException(getName());
             }
-        } else if (properties.containsKey(name)) {
+        } else if (properties.containsKey(index)) {
             return null; // Null has been explicitely set.
         } else {
             return getDefaultValue(name);
@@ -209,8 +262,13 @@ final class SparseFeature extends Abstra
     @Override
     public void setPropertyValue(final String name, final Object value) throws IllegalArgumentException
{
         ArgumentChecks.ensureNonNull("name", name);
+        final Integer index = getIndex(name);
+        if (index < 0) {
+            setOperationValue(name, value);
+            return;
+        }
         if (valuesKind == VALUES) {
-            final Object previous = properties.put(name, value);
+            final Object previous = properties.put(index, value);
             /*
              * Slight optimization:  if we replaced a previous value of the same class, then
we can skip the
              * checks for name and type validity since those checks have been done previously.
But if we add
@@ -222,7 +280,7 @@ final class SparseFeature extends Abstra
                     toStore = verifyPropertyValue(name, value);
                 } finally {
                     if (toStore != value) {
-                        replace(name, value, toStore);
+                        replace(index, value, toStore);
                     }
                 }
             }
@@ -236,13 +294,13 @@ final class SparseFeature extends Abstra
     /**
      * Sets a value in the {@link #properties} map.
      *
-     * @param name     The name of the property to set.
+     * @param index    The key of the property to set.
      * @param oldValue The old value, used for verification purpose.
      * @param newValue The new value.
      */
-    private void replace(final String name, final Object oldValue, final Object newValue)
{
-        if (properties.put(name, newValue) != oldValue) {
-            throw new ConcurrentModificationException(name);
+    private void replace(final Integer index, final Object oldValue, final Object newValue)
{
+        if (properties.put(index, newValue) != oldValue) {
+            throw new ConcurrentModificationException(nameOf(index));
         }
     }
 
@@ -255,8 +313,8 @@ final class SparseFeature extends Abstra
     public DataQuality quality() {
         if (valuesKind == VALUES) {
             final Validator v = new Validator(ScopeCode.FEATURE);
-            for (final PropertyType pt : type.getProperties(true)) {
-                v.validateAny(pt, properties.get(pt.getName().toString()));
+            for (final Map.Entry<String, Integer> entry : indices.entrySet()) {
+                v.validateAny(type.getProperty(entry.getKey()), properties.get(entry.getValue()));
             }
             return v.quality;
         }
@@ -281,14 +339,14 @@ final class SparseFeature extends Abstra
     @SuppressWarnings("unchecked")
     public SparseFeature clone() throws CloneNotSupportedException {
         final SparseFeature clone = (SparseFeature) super.clone();
-        clone.properties = (HashMap<String,Object>) clone.properties.clone();
+        clone.properties = (HashMap<Integer,Object>) clone.properties.clone();
         switch (clone.valuesKind) {
             default:        throw new AssertionError(clone.valuesKind);
             case CORRUPTED: throw new CorruptedObjectException(clone.getName());
             case VALUES:    break; // Nothing to do.
             case PROPERTIES: {
                 final Cloner cloner = new Cloner();
-                for (final Map.Entry<String,Object> entry : clone.properties.entrySet())
{
+                for (final Map.Entry<Integer,Object> entry : clone.properties.entrySet())
{
                     final Property property = (Property) entry.getValue();
                     if (property instanceof Cloneable) {
                         entry.setValue(cloner.clone(property));

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/NoOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/NoOperation.java?rev=1677036&r1=1677035&r2=1677036&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/NoOperation.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/NoOperation.java
[UTF-8] Thu Apr 30 18:01:54 2015
@@ -38,6 +38,16 @@ import org.opengis.feature.Property;
 @SuppressWarnings("serial")
 final strictfp class NoOperation extends AbstractOperation {
     /**
+     * A description of the input parameters.
+     */
+    private final ParameterDescriptorGroup parameters;
+
+    /**
+     * The type of the result, or {@code null} if none.
+     */
+    private final IdentifiedType result;
+
+    /**
      * Constructs an operation from the given properties. The identification map is given
unchanged to
      * the {@linkplain AbstractIdentifiedType#AbstractIdentifiedType(Map) super-class constructor}.
      *
@@ -48,7 +58,29 @@ final strictfp class NoOperation extends
     NoOperation(final Map<String,?> identification,
             final ParameterDescriptorGroup parameters, final IdentifiedType result)
     {
-        super(identification, parameters, result);
+        super(identification);
+        this.parameters = parameters;
+        this.result     = result;
+    }
+
+    /**
+     * Returns a description of the input parameters.
+     *
+     * @return Description of the input parameters.
+     */
+    @Override
+    public ParameterDescriptorGroup getParameters() {
+        return parameters;
+    }
+
+    /**
+     * Returns the expected result type, or {@code null} if none.
+     *
+     * @return The type of the result, or {@code null} if none.
+     */
+    @Override
+    public IdentifiedType getResult() {
+        return result;
     }
 
     /**



Mime
View raw message