You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2016/04/15 23:12:14 UTC

svn commit: r1739369 - in /sis/branches/JDK8/core/sis-feature/src: main/java/org/apache/sis/feature/AbstractAttribute.java main/java/org/apache/sis/feature/StringJoinOperation.java test/java/org/apache/sis/feature/StringJoinOperationTest.java

Author: desruisseaux
Date: Fri Apr 15 21:12:14 2016
New Revision: 1739369

URL: http://svn.apache.org/viewvc?rev=1739369&view=rev
Log:
Add test, bug fixes and formatting.

Modified:
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
    sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java
    sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/StringJoinOperationTest.java

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java?rev=1739369&r1=1739368&r2=1739369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/AbstractAttribute.java [UTF-8] Fri Apr 15 21:12:14 2016
@@ -103,7 +103,7 @@ public abstract class AbstractAttribute<
     /**
      * Creates a new attribute of the given type.
      *
-     * @param type Information about the attribute (base Java class, domain of values, <i>etc.</i>).
+     * @param type  information about the attribute (base Java class, domain of values, <i>etc.</i>).
      *
      * @see #create(AttributeType)
      */
@@ -115,9 +115,9 @@ public abstract class AbstractAttribute<
      * Creates a new attribute of the given type initialized to the
      * {@linkplain DefaultAttributeType#getDefaultValue() default value}.
      *
-     * @param  <V>  The type of attribute values.
-     * @param  type Information about the attribute (base Java class, domain of values, <i>etc.</i>).
-     * @return The new attribute.
+     * @param  <V>   the type of attribute values.
+     * @param  type  information about the attribute (base Java class, domain of values, <i>etc.</i>).
+     * @return the new attribute.
      *
      * @see DefaultAttributeType#newInstance()
      */
@@ -132,9 +132,9 @@ public abstract class AbstractAttribute<
      * Creates a new attribute of the given type initialized to the given value.
      * Note that a {@code null} value may not be the same as the default value.
      *
-     * @param  <V>   The type of attribute values.
-     * @param  type  Information about the attribute (base Java class, domain of values, <i>etc.</i>).
-     * @param  value The initial value (may be {@code null}).
+     * @param  <V>    the type of attribute values.
+     * @param  type   information about the attribute (base Java class, domain of values, <i>etc.</i>).
+     * @param  value  the initial value (may be {@code null}).
      * @return The new attribute.
      */
     static <V> AbstractAttribute<V> create(final AttributeType<V> type, final Object value) {
@@ -147,8 +147,8 @@ public abstract class AbstractAttribute<
     /**
      * Invoked on serialization for saving the {@link #characteristics} field.
      *
-     * @param  out The output stream where to serialize this attribute.
-     * @throws IOException If an I/O error occurred while writing.
+     * @param  out  the output stream where to serialize this attribute.
+     * @throws IOException if an I/O error occurred while writing.
      */
     private void writeObject(final ObjectOutputStream out) throws IOException {
         out.defaultWriteObject();
@@ -164,9 +164,9 @@ public abstract class AbstractAttribute<
     /**
      * Invoked on deserialization for restoring the {@link #characteristics} field.
      *
-     * @param  in The input stream from which to deserialize an attribute.
-     * @throws IOException If an I/O error occurred while reading or if the stream contains invalid data.
-     * @throws ClassNotFoundException If the class serialized on the stream is not on the classpath.
+     * @param  in  the input stream from which to deserialize an attribute.
+     * @throws IOException if an I/O error occurred while reading or if the stream contains invalid data.
+     * @throws ClassNotFoundException if the class serialized on the stream is not on the classpath.
      */
     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
         in.defaultReadObject();
@@ -185,7 +185,7 @@ public abstract class AbstractAttribute<
      * Returns the name of this attribute as defined by its {@linkplain #getType() type}.
      * This convenience method delegates to {@link AttributeType#getName()}.
      *
-     * @return The attribute name specified by its type.
+     * @return the attribute name specified by its type.
      */
     @Override
     public GenericName getName() {
@@ -195,7 +195,7 @@ public abstract class AbstractAttribute<
     /**
      * Returns information about the attribute (base Java class, domain of values, <i>etc.</i>).
      *
-     * @return Information about the attribute.
+     * @return information about the attribute.
      */
     @Override
     public AttributeType<V> getType() {
@@ -207,7 +207,7 @@ public abstract class AbstractAttribute<
      * the common case where the {@linkplain DefaultAttributeType#getMaximumOccurs() maximum number}
      * of attribute values is restricted to 1 or 0.
      *
-     * @return The attribute value (may be {@code null}).
+     * @return the attribute value (may be {@code null}).
      * @throws MultiValuedPropertyException if this attribute contains more than one value.
      *
      * @see AbstractFeature#getPropertyValue(String)
@@ -223,7 +223,7 @@ public abstract class AbstractAttribute<
      * <p>The default implementation returns a collection which will delegate its work to
      * {@link #getValue()} and {@link #setValue(Object)}.</p>
      *
-     * @return The attribute values in a <cite>live</cite> collection.
+     * @return the attribute values in a <cite>live</cite> collection.
      */
     @Override
     public Collection<V> getValues() {
@@ -239,12 +239,14 @@ public abstract class AbstractAttribute<
      * and also because some rules may be temporarily broken while constructing a feature.
      * A more exhaustive verification can be performed by invoking the {@link #quality()} method.
      *
-     * @param value The new value, or {@code null} for removing all values from this attribute.
+     * @param  value  the new value, or {@code null} for removing all values from this attribute.
+     * @throws InvalidPropertyValueException if this method verifies argument validity and the given value
+     *         does not met the attribute constraints.
      *
      * @see AbstractFeature#setPropertyValue(String, Object)
      */
     @Override
-    public abstract void setValue(final V value);
+    public abstract void setValue(final V value) throws InvalidPropertyValueException;
 
     /**
      * Sets the attribute values. All previous values are replaced by the given collection.
@@ -252,7 +254,7 @@ public abstract class AbstractAttribute<
      * <p>The default implementation ensures that the given collection contains at most one element,
      * then delegates to {@link #setValue(Object)}.</p>
      *
-     * @param  values The new values.
+     * @param  values  the new values.
      * @throws InvalidPropertyValueException if the given collection contains too many elements.
      */
     @Override
@@ -331,7 +333,7 @@ public abstract class AbstractAttribute<
      *     }</li>
      * </ol>
      *
-     * @return Other attribute types that describes this attribute type, or an empty map if none.
+     * @return other attribute types that describes this attribute type, or an empty map if none.
      *
      * @see DefaultAttributeType#characteristics()
      */
@@ -341,7 +343,7 @@ public abstract class AbstractAttribute<
         if (characteristics == null) {
             characteristics = newCharacteristicsMap();
         }
-        return characteristics;     // Intentionally modifiable
+        return characteristics;                                 // Intentionally modifiable
     }
 
     /**
@@ -436,7 +438,7 @@ public abstract class AbstractAttribute<
      * }
      * </div>
      *
-     * @return Reports on all constraint violations found.
+     * @return reports on all constraint violations found.
      *
      * @see AbstractFeature#quality()
      */
@@ -456,7 +458,7 @@ public abstract class AbstractAttribute<
      *     └─ characteristics: units=°C, accuracy=0.1
      * }
      *
-     * @return A string representation of this attribute for debugging purpose.
+     * @return a string representation of this attribute for debugging purpose.
      */
     @Debug
     @Override
@@ -481,7 +483,7 @@ public abstract class AbstractAttribute<
      * are <strong>not</strong> cloned.
      * However subclasses may choose to do otherwise.
      *
-     * @return A clone of this attribute.
+     * @return a clone of this attribute.
      * @throws CloneNotSupportedException if this attribute, the {@linkplain #getValue() value}
      *         or one of its {@linkplain #characteristics() characteristics} can not be cloned.
      */

Modified: sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java?rev=1739369&r1=1739368&r2=1739369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/main/java/org/apache/sis/feature/StringJoinOperation.java [UTF-8] Fri Apr 15 21:12:14 2016
@@ -305,10 +305,10 @@ final class StringJoinOperation extends
          * parsed, then this method does not store any property value ("all or nothing" behavior).
          *
          * @param  value  the concatenated string.
-         * @throws UnconvertibleObjectException if one of the attribute values can not be parsed to the expected type.
+         * @throws InvalidPropertyValueException if one of the attribute values can not be parsed to the expected type.
          */
         @Override
-        public void setValue(final String value) throws UnconvertibleObjectException {
+        public void setValue(final String value) throws InvalidPropertyValueException {
             final int endAt = value.length() - suffix.length();
             final boolean prefixMatches = value.startsWith(prefix);
             if (!prefixMatches || !value.endsWith(suffix)) {
@@ -316,7 +316,7 @@ final class StringJoinOperation extends
                         getName(),
                         prefixMatches ? 1 : 0,              // For "{1,choice,0#begin|1#end}" in message format.
                         prefixMatches ? suffix : prefix,
-                        prefixMatches ? CharSequences.token(value, 0) : value.substring(Math.max(0, endAt))));
+                        prefixMatches ? value.substring(Math.max(0, endAt)) : CharSequences.token(value, 0)));
             }
             /*
              * We do not use the regex split for avoiding possible reserved regex characters,
@@ -358,7 +358,7 @@ final class StringJoinOperation extends
                     element = new StringBuilder(element.length() - 1)
                             .append(element, 0, i).append(element, i+1, element.length()).toString();
                     if (i < element.length()) {
-                        if (element.indexOf(i) == ESCAPE) {
+                        if (element.charAt(i) == ESCAPE) {
                             i++;
                         } else {
                             assert element.regionMatches(i, delimiter, 0, delimiter.length()) : element;
@@ -371,8 +371,11 @@ final class StringJoinOperation extends
                  * If we have more values than expected, continue the parsing but without storing the values.
                  * The intend is to get the correct count of values for error reporting.
                  */
-                if (!element.isEmpty() && count < values.length) {
+                if (!element.isEmpty() && count < values.length) try {
                     values[count] = converters[count].apply(element);
+                } catch (UnconvertibleObjectException e) {
+                    throw new InvalidPropertyValueException(Errors.format(
+                            Errors.Keys.CanNotAssign_2, attributeNames[count], element), e);
                 }
                 count++;
                 upper += delimiter.length();

Modified: sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/StringJoinOperationTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/StringJoinOperationTest.java?rev=1739369&r1=1739368&r2=1739369&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/StringJoinOperationTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-feature/src/test/java/org/apache/sis/feature/StringJoinOperationTest.java [UTF-8] Fri Apr 15 21:12:14 2016
@@ -27,12 +27,14 @@ import static org.junit.Assert.*;
 
 // Branch-dependent imports
 import org.opengis.feature.PropertyType;
+import org.opengis.feature.InvalidPropertyValueException;
 
 
 /**
  * Tests {@link StringJoinOperation}.
  *
  * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
  * @since   0.7
  * @version 0.7
  * @module
@@ -52,12 +54,16 @@ public final strictfp class StringJoinOp
      *   <li>{@code summary} as string join of {@code name} and {@code age} attributes.</li>
      * </ul>
      *
+     * The operation uses {@code "<<:"} and {@code ":>>"} as prefix and suffix respectively
+     * avoid avoiding confusion if a code spelled the variable name (e.g. {@code prefix})
+     * instead of using it.
+     *
      * @return The feature for a person.
      */
     private static DefaultFeatureType person() {
         final PropertyType nameType = new DefaultAttributeType<>(name("name"), String.class, 1, 1, null);
         final PropertyType ageType  = new DefaultAttributeType<>(name("age"), Integer.class, 1, 1, null);
-        final PropertyType cmpType  = FeatureOperations.compound(name("concat"), "/", "prefix:", ":suffix", nameType, ageType);
+        final PropertyType cmpType  = FeatureOperations.compound(name("concat"), "/", "<<:", ":>>", nameType, ageType);
         return new DefaultFeatureType(name("person"), false, null, nameType, ageType, cmpType);
     }
 
@@ -70,6 +76,7 @@ public final strictfp class StringJoinOp
 
     /**
      * Tests {@code StringJoinOperation.Result.getValue()} on sparse and dense features.
+     * This test does not use the {@code '\'} escape character.
      */
     @Test
     public void testGetValue() {
@@ -82,17 +89,18 @@ public final strictfp class StringJoinOp
      * Executes the {@link #testGetValue()} on the given feature, which is either sparse or dense.
      */
     private static void testGetValue(final AbstractFeature feature) {
-        assertEquals("prefix:/:suffix", feature.getPropertyValue("concat"));
+        assertEquals("<<:/:>>", feature.getPropertyValue("concat"));
 
         feature.setPropertyValue("name", "marc");
-        assertEquals("prefix:marc/:suffix", feature.getPropertyValue("concat"));
+        assertEquals("<<:marc/:>>", feature.getPropertyValue("concat"));
 
         feature.setPropertyValue("age", 21);
-        assertEquals("prefix:marc/21:suffix", feature.getPropertyValue("concat"));
+        assertEquals("<<:marc/21:>>", feature.getPropertyValue("concat"));
     }
 
     /**
      * Tests {@code StringJoinOperation.Result.setValue(String)} on sparse and dense features.
+     * This test does not use the {@code '\'} escape character.
      */
     @Test
     @DependsOnMethod("testGetValue")
@@ -106,9 +114,65 @@ public final strictfp class StringJoinOp
      * Executes the {@link #testSetValue()} on the given feature, which is either sparse or dense.
      */
     private static void testSetValue(final AbstractFeature feature) {
-        feature.setPropertyValue("concat", "prefix:emile/37:suffix");
+        feature.setPropertyValue("concat", "<<:emile/37:>>");
         assertEquals("name",   "emile", feature.getPropertyValue("name"));
         assertEquals("age",         37, feature.getPropertyValue("age"));
-        assertEquals("concat", "prefix:emile/37:suffix", feature.getPropertyValue("concat"));
+        assertEquals("concat", "<<:emile/37:>>", feature.getPropertyValue("concat"));
+    }
+
+    /**
+     * Tests {@code getValue()} and {@code setValue(String)} with values that contains the {@code '\'}
+     * escape character.
+     */
+    @Test
+    @DependsOnMethod({"testGetValue", "testSetValue"})
+    public void testEscapeCharacter() {
+        final DenseFeature feature = new DenseFeature(person());
+        feature.setPropertyValue("name", "marc/emile\\julie");
+        feature.setPropertyValue("age", 30);
+        assertEquals("<<:marc\\/emile\\\\julie/30:>>", feature.getPropertyValue("concat"));
+
+        feature.setPropertyValue("concat", "<<:emile\\\\julie\\/marc/:>>");
+        assertEquals("name", "emile\\julie/marc", feature.getPropertyValue("name"));
+        assertNull  ("age", feature.getPropertyValue("age"));
+    }
+
+    /**
+     * Verifies that proper exceptions are thrown in case of illegal argument.
+     */
+    @Test
+    public void testIllegalArgument() {
+        final DenseFeature feature = new DenseFeature(person());
+        try {
+            feature.setPropertyValue("concat", "((:marc/21:>>");
+            fail("Should fail because of mismatched prefix.");
+        } catch (InvalidPropertyValueException e) {
+            String message = e.getMessage();
+            assertTrue(message, message.contains("<<:"));
+            assertTrue(message, message.contains("(("));
+        }
+        try {
+            feature.setPropertyValue("concat", "<<:marc/21:))");
+            fail("Should fail because of mismatched suffix.");
+        } catch (InvalidPropertyValueException e) {
+            String message = e.getMessage();
+            assertTrue(message, message.contains(":>>"));
+            assertTrue(message, message.contains("))"));
+        }
+        try {
+            feature.setPropertyValue("concat", "<<:marc/21/julie:>>");
+            fail("Should fail because of too many components.");
+        } catch (InvalidPropertyValueException e) {
+            String message = e.getMessage();
+            assertTrue(message, message.contains("<<:marc/21/julie:>>"));
+        }
+        try {
+            feature.setPropertyValue("concat", "<<:marc/julie:>>");
+            fail("Should fail because of unparsable number.");
+        } catch (InvalidPropertyValueException e) {
+            String message = e.getMessage();
+            assertTrue(message, message.contains("julie"));
+            assertTrue(message, message.contains("age"));
+        }
     }
 }