Author: desruisseaux
Date: Fri Mar 27 00:31:14 2015
New Revision: 1669486
URL: http://svn.apache.org/r1669486
Log:
Referencing: call to ContextualParameters.completeTransform(...) mark the
parameters as unmodifiable.
This method is invoked after we finished to build the ContextualParameters and
need to keep the reference.
Added:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java
(with props)
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/UnmodifiableParameterValueTest.java
(with props)
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java?rev=1669486&r1=1669485&r2=1669486&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java
[UTF-8] Fri Mar 27 00:31:14 2015
@@ -103,11 +103,11 @@ import java.nio.file.Path;
* Consequently, the above-cited methods provide single points that subclasses
can override
* for modifying the behavior of all getter and setter methods.
*
- * @param <T> The value type.
+ * @param <T> The type of the value stored in this parameter.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @since 0.4
- * @version 0.4
+ * @version 0.6
* @module
*
* @see DefaultParameterDescriptor
@@ -134,7 +134,7 @@ public class DefaultParameterValue<T> ex
private T value;
/**
- * The unit of measure for the value, or {@code null} if it doesn't apply.
+ * The unit of measure for the value, or {@code null} if it does not apply.
* Except for the constructors, the {@link #equals(Object)} and the {@link
#hashCode()} methods,
* this field shall be read only by {@link #getUnit()} and written by
{@link #setValue(Object, Unit)}.
*/
@@ -159,6 +159,9 @@ public class DefaultParameterValue<T> ex
* object is not cloned.
*
* @param parameter The parameter to copy values from.
+ *
+ * @see #clone()
+ * @see #unmodifiable(ParameterValue)
*/
public DefaultParameterValue(final ParameterValue<T> parameter) {
ensureNonNull("parameter", parameter);
@@ -758,6 +761,9 @@ public class DefaultParameterValue<T> ex
/**
* Returns a clone of this parameter value.
+ *
+ * @see #DefaultParameterValue(ParameterValue)
+ * @see #unmodifiable(ParameterValue)
*/
@Override
@SuppressWarnings("unchecked")
@@ -770,6 +776,39 @@ public class DefaultParameterValue<T> ex
}
/**
+ * Returns an unmodifiable implementation of the given parameter value.
+ * This method shall be used only with:
+ *
+ * <ul>
+ * <li>immutable {@linkplain #getDescriptor() descriptor},</li>
+ * <li>immutable or null {@linkplain #getUnit() unit}, and</li>
+ * <li>immutable or {@linkplain Cloneable cloneable} parameter
{@linkplain #getValue() value}.</li>
+ * </ul>
+ *
+ * If the parameter value implements the {@code Cloneable} interface and
has a public {@code clone()} method,
+ * then that value will be cloned every time the {@link #getValue()}
method is invoked.
+ *
+ * <div class="section">Instances sharing</div>
+ * If this method is invoked more than once with equal {@linkplain
#getDescriptor() descriptor},
+ * {@linkplain #getValue() value} and {@linkplain #getUnit() unit}, then
this method will return
+ * the same {@code DefaultParameterValue} instance on a <cite>best
effort</cite> basis.
+ *
+ * <div class="note"><b>Rational:</b>
+ * the same parameter value is often used in many different coordinate
operations. For example all <cite>Universal
+ * Transverse Mercator</cite> (UTM) projections use the same scale factor
(0.9996) and false easting (500000 metres).
+ * </div>
+ *
+ * @param <T> The type of the value stored in the given parameter.
+ * @param parameter The parameter to make unmodifiable, or {@code null}.
+ * @return An unmodifiable implementation of the given parameter, or
{@code null} if the given parameter was null.
+ *
+ * @since 0.6
+ */
+ public static <T> DefaultParameterValue<T> unmodifiable(final
ParameterValue<T> parameter) {
+ return UnmodifiableParameterValue.create(parameter);
+ }
+
+ /**
* Formats this parameter as a <cite>Well Known Text</cite> {@code
Parameter[…]} element.
* Example:
*
Added:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java?rev=1669486&view=auto
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java
(added)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java
[UTF-8] Fri Mar 27 00:31:14 2015
@@ -0,0 +1,121 @@
+/*
+ * 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.parameter;
+
+import javax.measure.unit.Unit;
+import org.opengis.parameter.ParameterValue;
+import org.apache.sis.internal.util.Cloner;
+import org.apache.sis.util.collection.WeakHashSet;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * A parameter value which can not be modified. This implementation shall be
used only with:
+ *
+ * <ul>
+ * <li>immutable {@linkplain #getDescriptor() descriptor},</li>
+ * <li>immutable or null {@linkplain #getUnit() unit}, and</li>
+ * <li>immutable or {@linkplain Cloneable cloneable} parameter {@linkplain
#getValue() value}.</li>
+ * </ul>
+ *
+ * If the parameter value implements the {@code Cloneable} interface and has a
public {@code clone()} method,
+ * then that value will be cloned every time the {@link #getValue()} method is
invoked.
+ *
+ * <div class="section">Instances sharing</div>
+ * If the {@link #create(ParameterValue)} method is invoked more than once
with equal descriptor, value and unit,
+ * then the method will return the same {@code UnmodifiableParameterValue}
instance on a <cite>best effort</cite>
+ * basis.
+ *
+ * <div class="note"><b>Rational:</b>
+ * the same parameter value is often used in many different coordinate
operations. For example all <cite>Universal
+ * Transverse Mercator</cite> (UTM) projections use the same scale factor
(0.9996) and false easting (500000 metres).
+ * </div>
+ *
+ * @param <T> The type of the value stored in this parameter.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.6
+ * @version 0.6
+ * @module
+ */
+final class UnmodifiableParameterValue<T> extends DefaultParameterValue<T> {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = -4760030766220872555L;
+
+ /**
+ * Pool of parameter instances created in this running JVM.
+ * See class javadoc for a rational about why we use a pool.
+ */
+ @SuppressWarnings("rawtypes")
+ private static final WeakHashSet<UnmodifiableParameterValue> POOL =
+ new WeakHashSet<>(UnmodifiableParameterValue.class);
+
+ /**
+ * Creates a new parameter with the same value than the given one.
+ */
+ private UnmodifiableParameterValue(final ParameterValue<T> value) {
+ super(value);
+ }
+
+ /**
+ * Returns an unmodifiable implementation of the given parameter value.
+ * See class javadoc for more information.
+ *
+ * @param <T> The type of the value stored in the given parameter.
+ * @param parameter The parameter to make unmodifiable, or {@code null}.
+ * @return An unmodifiable implementation of the given parameter, or
{@code null} if the given parameter was null.
+ */
+ static <T> UnmodifiableParameterValue<T> create(final ParameterValue<T>
parameter) {
+ if (parameter == null || parameter instanceof
UnmodifiableParameterValue<?>) {
+ return (UnmodifiableParameterValue<T>) parameter;
+ } else {
+ return POOL.unique(new UnmodifiableParameterValue<>(parameter));
+ }
+ }
+
+ /**
+ * If the value is cloneable, clones it before to return it.
+ */
+ @Override
+ public T getValue() {
+ T value = super.getValue();
+ if (value instanceof Cloneable) try {
+ value =
getDescriptor().getValueClass().cast(Cloner.cloneIfPublic(value));
+ } catch (CloneNotSupportedException e) {
+ throw new
UnsupportedOperationException(Errors.format(Errors.Keys.CloneNotSupported_1,
value.getClass()), e);
+ }
+ return value;
+ }
+
+ /**
+ * Do not allow modification of the parameter value.
+ */
+ @Override
+ protected void setValue(final Object value, final Unit<?> unit) {
+ throw new
UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1,
getClass()));
+ }
+
+ /**
+ * Returns a modifiable copy of this parameter.
+ */
+ @Override
+ public DefaultParameterValue<T> clone() {
+ return new DefaultParameterValue<>(this);
+ }
+}
Propchange:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java?rev=1669486&r1=1669485&r2=1669486&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
[UTF-8] Fri Mar 27 00:31:14 2015
@@ -54,7 +54,7 @@ import java.util.Objects;
* {@linkplain ContextualParameters#normalizeGeographicInputs normalize}
affine transform.</li>
*
* <li>On output, the {@link #transform(double[],int,double[],int,boolean)
transform(…)} method returns
- * (<var>x</var>, <var>y</var>) values on a sphere or ellipse having a
semi-major axis length of 1.
+ * (<var>x</var>, <var>y</var>) values on a sphere or ellipse having a
semi-major axis length (<var>a</var>) of 1.
* The multiplication by the scale factor (<var>k</var>₀) and the
translation by false easting (FE) and false
* northing (FN) are applied by the {@linkplain
ContextualParameters#denormalizeCartesianOutputs denormalize}
* affine transform.</li>
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java?rev=1669486&r1=1669485&r2=1669486&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java
[UTF-8] Fri Mar 27 00:31:14 2015
@@ -17,7 +17,9 @@
package org.apache.sis.referencing.operation.transform;
import java.util.List;
+import java.util.ArrayList;
import java.util.Objects;
+import java.util.Collections;
import java.io.Serializable;
import org.opengis.util.FactoryException;
import org.opengis.referencing.operation.MathTransform;
@@ -32,7 +34,9 @@ import org.opengis.referencing.operation
import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix;
import org.apache.sis.internal.referencing.WKTUtilities;
import org.apache.sis.internal.util.DoubleDouble;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
import org.apache.sis.parameter.Parameterized;
+import org.apache.sis.parameter.DefaultParameterValue;
import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
import
org.apache.sis.referencing.operation.matrix.NoninvertibleMatrixException;
@@ -119,7 +123,7 @@ public class ContextualParameters extend
* The parameters that represents the sequence of transforms as a whole.
The parameter values may take effect
* in either the {@linkplain #normalization(boolean)
normalize/denormalize} transforms or in the kernel.
*
- * @see #getParameterDescriptors()
+ * @see #getDescriptor()
*/
private final ParameterDescriptorGroup descriptor;
@@ -127,7 +131,7 @@ public class ContextualParameters extend
* The affine transform to be applied before (<cite>normalize</cite>) and
after (<cite>denormalize</cite>)
* the kernel operation. On {@code ContextualParameters} construction,
those affines are initially identity
* transforms, to be modified in-place by callers of {@link
#normalization(boolean)}.
- * After {@link #createConcatenatedTransform(MathTransformFactory,
MathTransform)} has been invoked,
+ * After {@link #completeTransform(MathTransformFactory, MathTransform)}
has been invoked,
* they are typically (but not necessarily) replaced by the {@link
LinearTransform} instance itself.
*
* @see #normalization(boolean)
@@ -135,6 +139,12 @@ public class ContextualParameters extend
private Matrix normalize, denormalize;
/**
+ * The list of parameter values. This list is modifiable after
construction, but is replaced by an
+ * unmodifiable list after {@link #completeTransform(MathTransformFactory,
MathTransform)} has been invoked.
+ */
+ private List<GeneralParameterValue> values;
+
+ /**
* Creates a new group of parameters for the given non-linear coordinate
operation method.
* The {@linkplain
org.apache.sis.referencing.operation.DefaultOperationMethod#getParameters()
method parameters}
* shall apply to the <cite>normalize</cite> → <cite>non-linear
kernel</cite> → <cite>denormalize</cite> sequence
@@ -148,6 +158,15 @@ public class ContextualParameters extend
descriptor = method.getParameters();
normalize = linear("sourceDimensions", method.getSourceDimensions());
denormalize = linear("targetDimensions", method.getTargetDimensions());
+ values = new ArrayList<>(descriptor.descriptors().size()); //
See isModifiable() below.
+ }
+
+ /**
+ * Returns {@code true} if this parameter group is modifiable, or {@code
false} if it has been made
+ * unmodifiable by a call to {@link
#completeTransform(MathTransformFactory, MathTransform)}.
+ */
+ private boolean isModifiable() {
+ return values.getClass() == ArrayList.class;
}
/**
@@ -181,13 +200,20 @@ public class ContextualParameters extend
}
/**
- * Returns an unmodifiable view of all parameter values in this group.
+ * Returns an unmodifiable list containing all parameters in this group.
+ * Callers should not attempt to modify the parameter values.
*
* @return All parameter values.
*/
@Override
public List<GeneralParameterValue> values() {
- throw new UnsupportedOperationException("Not supported yet."); // TODO
+ if (isModifiable()) {
+ return Collections.unmodifiableList(values);
+ } else {
+ // Already unmodifiable, no need to protect against changes.
+ assert (values instanceof UnmodifiableArrayList<?>);
+ return values;
+ }
}
/**
@@ -203,6 +229,17 @@ public class ContextualParameters extend
}
/**
+ * Ensures that this instance if modifiable.
+ *
+ * @throws IllegalStateException if this {@code ContextualParameter} has
been made unmodifiable.
+ */
+ private void ensureModifiable() throws IllegalStateException {
+ if (!isModifiable()) {
+ throw new
IllegalStateException(Errors.format(Errors.Keys.UnmodifiableObject_1,
getClass()));
+ }
+ }
+
+ /**
* Prepends a normalization step before the non-linear kernel, which will
convert ordinate values
* in the two first dimensions from degrees to radians. Before this
conversion, the normalization
* can optionally subtract the given λ₀ value (in degrees) from the
longitude.
@@ -215,8 +252,10 @@ public class ContextualParameters extend
* @param λ0 Longitude of the central meridian, in degrees.
* @return The normalization affine transform as a matrix.
* Callers can change that matrix directly if they want to apply
additional normalization operations.
+ * @throws IllegalStateException if this {@code ContextualParameter} has
been made unmodifiable.
*/
public MatrixSIS normalizeGeographicInputs(final double λ0) {
+ ensureModifiable();
/*
* In theory the check for (λ0 != 0) is useless. However Java has a
notion of negative zero, and we want
* to avoid negative zeros because we do not want them to appear in
WKT formatting of matrix elements.
@@ -227,7 +266,7 @@ public class ContextualParameters extend
offset = new DoubleDouble(-λ0);
offset.multiply(toRadians);
}
- final MatrixSIS normalize = normalization(true);
+ final MatrixSIS normalize = (MatrixSIS) this.normalize; // Must be
the same instance, not a copy.
normalize.concatenate(0, toRadians, offset);
normalize.concatenate(1, toRadians, null);
return normalize;
@@ -247,10 +286,12 @@ public class ContextualParameters extend
* @param ty The false northing (FN).
* @return The denormalization affine transform as a matrix.
* Callers can change that matrix directly if they want to apply
additional denormalization operations.
+ * @throws IllegalStateException if this {@code ContextualParameter} has
been made unmodifiable.
*/
public MatrixSIS denormalizeCartesianOutputs(double scale, double tx,
double ty) {
+ ensureModifiable();
final DoubleDouble s = new DoubleDouble(scale);
- final MatrixSIS denormalize = normalization(false);
+ final MatrixSIS denormalize = (MatrixSIS) this.denormalize; // Must
be the same instance, not a copy.
denormalize.concatenate(0, s, tx);
denormalize.concatenate(1, s, ty);
return denormalize;
@@ -266,12 +307,19 @@ public class ContextualParameters extend
* @return The requested normalize ({@code true}) or denormalize ({@code
false}) affine transform.
*/
public final MatrixSIS normalization(final boolean norm) {
- return MatrixSIS.castOrCopy(norm ? normalize : denormalize);
+ final Matrix m = norm ? normalize : denormalize;
+ if (isModifiable()) {
+ return (MatrixSIS) m; // Must be the same instance, not a
copy.
+ } else {
+ return Matrices.copy(m); // Should be protected against changes.
+ }
}
/**
- * Creates a chain of {@linkplain ConcatenatedTransform concatenated
transforms} from the
- * <cite>normalize</cite> transform, the given kernel and the
<cite>denormalize</cite> transform.
+ * Marks this {@code ContextualParameter} as unmodifiable and creates the
+ * <cite>normalize</cite> → {@code kernel} → <cite>denormalize</cite>
transforms chain.
+ * This method shall be invoked only after the {@linkplain
#normalization(boolean) (de)normalization}
+ * matrices have been set to their final values.
*
* @param factory The factory to use for creating math transform
instances.
* @param kernel The (usually non-linear) kernel.
@@ -279,9 +327,20 @@ public class ContextualParameters extend
* transforms.
* @throws FactoryException if an error occurred while creating a math
transform instance.
*/
- public MathTransform createConcatenatedTransform(final
MathTransformFactory factory, final MathTransform kernel)
+ public MathTransform completeTransform(final MathTransformFactory factory,
final MathTransform kernel)
throws FactoryException
{
+ if (isModifiable()) {
+ final GeneralParameterValue[] p = values.toArray(new
GeneralParameterValue[values.size()]);
+ for (int i=0; i<p.length; i++) {
+ p[i] = DefaultParameterValue.unmodifiable((ParameterValue<?>)
p[i]);
+ }
+ values = UnmodifiableArrayList.wrap(p);
+ }
+ /*
+ * Creates the ConcatenatedTransform, letting the factory returns the
cached instance
+ * if the caller already invoked this method previously (which usually
do not happen).
+ */
final MathTransform n = factory.createAffineTransform(normalize);
final MathTransform d = factory.createAffineTransform(denormalize);
Matrix m;
Added:
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/UnmodifiableParameterValueTest.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/UnmodifiableParameterValueTest.java?rev=1669486&view=auto
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/UnmodifiableParameterValueTest.java
(added)
+++
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/UnmodifiableParameterValueTest.java
[UTF-8] Fri Mar 27 00:31:14 2015
@@ -0,0 +1,121 @@
+/*
+ * 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.parameter;
+
+import java.util.Date;
+import org.opengis.parameter.ParameterValue;
+import org.apache.sis.test.DependsOnMethod;
+import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestCase;
+import org.apache.sis.util.ComparisonMode;
+import org.junit.Test;
+
+import static org.apache.sis.test.TestUtilities.date;
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link UnmodifiableParameterValue} class.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.6
+ * @version 0.6
+ * @module
+ */
+@DependsOn(DefaultParameterValueTest.class)
+public final strictfp class UnmodifiableParameterValueTest extends TestCase {
+ /**
+ * Strict tolerance factor for floating point comparisons.
+ */
+ private static final double STRICT = 0.0;
+
+ /**
+ * Creates an {@link UnmodifiableParameterValue} implementation for the
given parameter
+ * and asserts that we got a new instance equivalent to the original one.
+ */
+ private static <T> DefaultParameterValue<T> assertEquivalent(final
ParameterValue<T> modifiable) {
+ final DefaultParameterValue<T> unmodifiable =
DefaultParameterValue.unmodifiable(modifiable);
+ assertNotSame("Expected a new instance.", modifiable, unmodifiable);
+ assertTrue("New instance shall be equal to the original one.",
+ unmodifiable.equals(modifiable, ComparisonMode.BY_CONTRACT));
+ return unmodifiable;
+ }
+
+ /**
+ * Tests the value returned by {@link
DefaultParameterValue#unmodifiable(ParameterValue)}.
+ */
+ @Test
+ public void testCreate() {
+ final ParameterValue<Double> modifiable =
DefaultParameterDescriptorTest
+ .createSimpleOptional("Scale factor",
Double.class).createValue();
+ modifiable.setValue(0.9996); // Scale factor of all UTM projections.
+ /*
+ * Create and validate an unmodifiable parameter,
+ * then verify that we can not modify its value.
+ */
+ final DefaultParameterValue<Double> unmodifiable =
assertEquivalent(modifiable);
+ assertSame("Double instances do not need to be cloned.",
modifiable.getValue(), unmodifiable.getValue());
+ modifiable.setValue(1.0);
+ try {
+ unmodifiable.setValue(1.0);
+ fail("UnmodifiableParameterValue shall not allow modification.");
+ } catch (UnsupportedOperationException e) {
+ final String message = e.getMessage();
+ assertTrue(message, message.contains("DefaultParameterValue"));
+ }
+ assertEquals(1.0, modifiable.doubleValue(), STRICT);
+ assertEquals(0.9996, unmodifiable.doubleValue(), STRICT);
+ /*
+ * Verify that invoking again DefaultParameterValue.unmodifiable(…)
return the same instance.
+ * Opportunistically verify that we detect null value and instances
already unmodifiable.
+ */
+ assertNull ( DefaultParameterValue.unmodifiable(null));
+ assertSame (unmodifiable,
DefaultParameterValue.unmodifiable(unmodifiable));
+ assertNotSame(unmodifiable, assertEquivalent(modifiable));
+ modifiable.setValue(0.9996); // Restore our original value.
+ assertSame (unmodifiable, assertEquivalent(modifiable));
+ }
+
+ /**
+ * Verifies that {@link UnmodifiableParameterValue#getValue()} can clone
the value.
+ */
+ @Test
+ @DependsOnMethod("testCreate")
+ public void testGetValue() {
+ final ParameterValue<Date> modifiable = DefaultParameterDescriptorTest
+ .createSimpleOptional("Time reference",
Date.class).createValue();
+ modifiable.setValue(date("1994-01-01 00:00:00"));
+ /*
+ * Create and validate an unmodifiable parameter,
+ * then verify that the values are not the same.
+ */
+ final DefaultParameterValue<Date> unmodifiable =
assertEquivalent(modifiable);
+ final Date t1 = modifiable.getValue();
+ final Date t2 = unmodifiable.getValue();
+ assertNotSame("Date should be cloned.", t1, t2);
+ assertEquals(t1, t2);
+ /*
+ * Verify that cloning the parameter also clone its value.
+ */
+ final DefaultParameterValue<Date> clone = unmodifiable.clone();
+ assertEquals(DefaultParameterValue.class, clone.getClass());
+ final Date t3 = clone.getValue();
+ assertNotSame(t1, t3);
+ assertNotSame(t2, t3);
+ assertEquals (t1, t3);
+ }
+}
Propchange:
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/UnmodifiableParameterValueTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/parameter/UnmodifiableParameterValueTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified:
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java?rev=1669486&r1=1669485&r2=1669486&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
[UTF-8] Fri Mar 27 00:31:14 2015
@@ -62,6 +62,7 @@ import org.junit.BeforeClass;
org.apache.sis.parameter.DefaultParameterDescriptorGroupTest.class,
org.apache.sis.parameter.DefaultParameterValueTest.class,
org.apache.sis.parameter.DefaultParameterValueGroupTest.class,
+ org.apache.sis.parameter.UnmodifiableParameterValueTest.class,
org.apache.sis.parameter.ParametersTest.class,
org.apache.sis.parameter.ParameterBuilderTest.class,
org.apache.sis.parameter.ParameterFormatTest.class,
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java?rev=1669486&r1=1669485&r2=1669486&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
[UTF-8] Fri Mar 27 00:31:14 2015
@@ -29,7 +29,7 @@ import org.apache.sis.util.resources.Err
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.5
+ * @version 0.6
* @module
*/
@Workaround(library="JDK", version="1.7")
@@ -113,7 +113,7 @@ public class Cloner {
}
} catch (NoSuchMethodException e) {
if (isCloneRequired(object)) {
- throw fail(e);
+ throw fail(e, valueType);
}
method = null;
type = valueType;
@@ -121,36 +121,80 @@ public class Cloner {
if (security != null) {
e.addSuppressed(security);
}
- throw fail(e);
+ throw fail(e, valueType);
} catch (InvocationTargetException e) {
- final Throwable cause = e.getCause();
- if (cause instanceof CloneNotSupportedException) {
- throw (CloneNotSupportedException) cause;
- }
- if (cause instanceof RuntimeException) {
- throw (RuntimeException) cause;
- }
- if (cause instanceof Error) {
- throw (Error) cause;
- }
- throw fail(e);
+ rethrow(e.getCause());
+ throw fail(e, valueType);
} catch (SecurityException e) {
- throw fail(e);
+ throw fail(e, valueType);
}
return object;
}
/**
+ * Throws the given exception if it is an instance of {@code
CloneNotSupportedException}
+ * or an unchecked exception, or do nothing otherwise. If this method
returns normally,
+ * then it is caller's responsibility to throw an other exception.
+ *
+ * @param cause The value of {@link InvocationTargetException#getCause()}.
+ */
+ private static void rethrow(final Throwable cause) throws
CloneNotSupportedException {
+ if (cause instanceof CloneNotSupportedException) {
+ throw (CloneNotSupportedException) cause;
+ }
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ }
+ if (cause instanceof Error) {
+ throw (Error) cause;
+ }
+ // If we reach this point, caller should invoke "throw fail(e, type)".
+ }
+
+ /**
* Returns an exception telling that the object can not be cloned because
of the given error.
- * The {@link #clone(Object)} method must have been attempted before to
invoke this method.
*
* @param cause The cause for the failure to clone an object.
+ * @param type The type of object that we failed to clone.
* @return An exception with an error message and the given cause.
*/
- private CloneNotSupportedException fail(final Throwable cause) {
- CloneNotSupportedException e = new CloneNotSupportedException(
- Errors.format(Errors.Keys.CloneNotSupported_1, type));
- e.initCause(cause);
- return e;
+ private static CloneNotSupportedException fail(final Throwable cause,
final Class<?> type) {
+ return (CloneNotSupportedException) new CloneNotSupportedException(
+ Errors.format(Errors.Keys.CloneNotSupported_1,
type)).initCause(cause);
+ }
+
+ /**
+ * Clones the given object if its {@code clone()} method is public, or
returns the same object otherwise.
+ * This method may be convenient when there is only one object to clone,
otherwise instantiating a new
+ * {@code Cloner} object is more efficient.
+ *
+ * @param object The object to clone, or {@code null}.
+ * @return The given object (which may be {@code null}) or a clone of the
given object.
+ * @throws CloneNotSupportedException if the call to {@link
Object#clone()} failed.
+ *
+ * @since 0.6
+ */
+ public static Object cloneIfPublic(final Object object) throws
CloneNotSupportedException {
+ if (object != null) {
+ final Class<?> type = object.getClass();
+ try {
+ final Method m = type.getMethod("clone", (Class[]) null);
+ if (Modifier.isPublic(m.getModifiers())) {
+ return m.invoke(object, (Object[]) null);
+ }
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ /*
+ * Should never happen because all objects have a clone()
method
+ * and we verified that the method is public.
+ */
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ rethrow(e.getCause());
+ throw fail(e, type);
+ } catch (SecurityException e) {
+ throw fail(e, type);
+ }
+ }
+ return object;
}
}
Modified:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java?rev=1669486&r1=1669485&r2=1669486&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java
[UTF-8] Fri Mar 27 00:31:14 2015
@@ -245,7 +245,7 @@ public class DefaultTreeTable implements
@Override
public DefaultTreeTable clone() throws CloneNotSupportedException {
final DefaultTreeTable clone = (DefaultTreeTable) super.clone();
- clone.root = (TreeTable.Node) new Cloner().clone(clone.root);
+ clone.root = (TreeTable.Node) Cloner.cloneIfPublic(clone.root);
return clone;
}