Author: desruisseaux
Date: Fri Jul 26 17:41:33 2013
New Revision: 1507380
URL: http://svn.apache.org/r1507380
Log:
Support 'nilReason' on Boolean values.
Added:
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java
(with props)
Modified:
sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/xml/NilReasonMarshallingTest.java
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Boolean.java
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakValueHashMap.java
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/xml/NilObject.java
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/xml/NilReason.java
sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/collection/WeakValueHashMapTest.java
Modified: sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/xml/NilReasonMarshallingTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/xml/NilReasonMarshallingTest.java?rev=1507380&r1=1507379&r2=1507380&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/xml/NilReasonMarshallingTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/xml/NilReasonMarshallingTest.java
[UTF-8] Fri Jul 26 17:41:33 2013
@@ -19,6 +19,8 @@ package org.apache.sis.xml;
import javax.xml.bind.JAXBException;
import org.opengis.metadata.citation.Series;
import org.opengis.metadata.citation.Citation;
+import org.opengis.metadata.quality.ConformanceResult;
+import org.apache.sis.test.DependsOnMethod;
import org.apache.sis.test.XMLTestCase;
import org.junit.Test;
@@ -30,7 +32,7 @@ import static org.apache.sis.test.Assert
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3 (derived from geotk-3.18)
- * @version 0.3
+ * @version 0.4
* @module
*
* @see <a href="http://jira.geotoolkit.org/browse/GEOTK-149">GEOTK-149</a>
@@ -73,11 +75,48 @@ public final strictfp class NilReasonMar
}
/**
+ * Tests a missing boolean value. The {@link Boolean}, {@link Integer}, {@link Double}
and {@link String}
+ * values are implemented as special cases in {@link NilReason}, because they are final
classes on which
+ * we have no control.
+ *
+ * @throws JAXBException Should never happen.
+ */
+ @Test
+ @DependsOnMethod("testMissing")
+ public void testMissingBoolean() throws JAXBException {
+ final String expected =
+ "<gmd:DQ_ConformanceResult xmlns:gmd=\"" + Namespaces.GMD + '"' +
+ " xmlns:gco=\"" + Namespaces.GCO + '"' +
+ " xmlns:xlink=\"" + Namespaces.XLINK + "\">\n"
+
+ " <gmd:explanation>\n" +
+ " <gco:CharacterString>An explanation</gco:CharacterString>\n"
+
+ " </gmd:explanation>\n" +
+ " <gmd:pass gco:nilReason=\"missing\"/>\n" +
+ "</gmd:DQ_ConformanceResult>";
+
+ final ConformanceResult result = (ConformanceResult) XML.unmarshal(expected);
+ assertEquals("explanation", "An explanation", result.getExplanation().toString());
+
+ final Boolean pass = result.pass();
+ assertNotNull("Expected a sentinal value.", pass);
+ assertEquals ("Nil value shall be false.", Boolean.FALSE, pass);
+ assertNotSame("Expected a sentinal value.", Boolean.FALSE, pass);
+
+ final NilReason reason = NilReason.getNilReason(pass);
+ assertSame("nilReason", NilReason.MISSING, reason);
+
+ final String actual = XML.marshal(result);
+ assertXmlEquals(expected, actual, "xmlns:*");
+ assertEquals(result, XML.unmarshal(actual));
+ }
+
+ /**
* Tests a case where the nil reason is specified by an other reason.
*
* @throws JAXBException Should never happen.
*/
@Test
+ @DependsOnMethod("testMissing")
public void testOther() throws JAXBException {
final String expected =
"<gmd:CI_Citation xmlns:gmd=\"" + Namespaces.GMD + '"' +
@@ -113,6 +152,7 @@ public final strictfp class NilReasonMar
* @throws JAXBException Should never happen.
*/
@Test
+ @DependsOnMethod("testMissing")
public void testURI() throws JAXBException {
final String expected =
"<gmd:CI_Citation xmlns:gmd=\"" + Namespaces.GMD + '"' +
Added: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java?rev=1507380&view=auto
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java
(added)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java
[UTF-8] Fri Jul 26 17:41:33 2013
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.jaxb;
+
+import java.lang.reflect.Modifier;
+import org.apache.sis.xml.NilReason;
+import org.apache.sis.util.collection.WeakValueHashMap;
+
+
+/**
+ * A workaround for attaching properties ({@code nilreason}, {@code href}, <i>etc.</i>)
to primitive type wrappers.
+ * The normal approach in SIS is to implement the {@link org.apache.sis.xml.NilObject} interface.
However we can not
+ * do so when the object is a final Java class like {@link Boolean}, {@link Integer}, {@link
Double} or {@link String}.
+ * This class provides a workaround using specific instances of some primitive types.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.4
+ * @version 0.4
+ * @module
+ *
+ * @see NilReason#createNilObject(Class)
+ */
+public final class PrimitiveTypeProperties {
+ /**
+ * The map where to store specific instances. We really need an identity hash map;
+ * using the {@code Object.equals(Object)} method is not allowed here.
+ *
+ * <p>Keys are the primitive type instances. Values are the {@code NilReason} why
this value is missing,
+ * or any other property we may want to attach.</p>
+ */
+ private static final WeakValueHashMap<Object,Object> SENTINAL_VALUES = new WeakValueHashMap<>(Object.class,
true);
+
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private PrimitiveTypeProperties() {
+ }
+
+ /**
+ * Returns {@code true} if the given type is a valid key. This {@code PrimitiveTypeProperties}
+ * class is a workaround to be used only for final classes on which we have no control.
+ * Non-final classes shall implement {@link org.apache.sis.xml.NilObject} instead.
+ */
+ private static boolean isValidKey(final Object primitive) {
+ return Modifier.isFinal(primitive.getClass().getModifiers());
+ }
+
+ /**
+ * Associates the given property to the given primitive.
+ * The {@code primitive} argument shall be a specific instance created by the {@code
new} keyword, not
+ * a shared instance link {@link Boolean#FALSE} or the values returned by {@link Integer#valueOf(int)}.
+ *
+ * @param primitive The {@link Boolean}, {@link Integer}, {@link Double} or {@link String}
specific instance.
+ * @param property The {@link NilReason} or other property to associate to the given
instance.
+ */
+ public static void associate(final Object primitive, final Object property) {
+ assert isValidKey(primitive) : primitive;
+ final Object old = SENTINAL_VALUES.put(primitive, property);
+ if (old != null) { // Should never happen - this is rather debugging check.
+ SENTINAL_VALUES.put(primitive, old);
+ throw new AssertionError(primitive);
+ }
+ }
+
+ /**
+ * Returns the property of the given primitive type, or {@code null} if none.
+ *
+ * @param primitive The {@link Boolean}, {@link Integer}, {@link Double} or {@link String}
specific instance.
+ * @return The property associated to the given instance, or {@code null} if none.
+ */
+ public static Object property(final Object primitive) {
+ assert isValidKey(primitive) : primitive;
+ return SENTINAL_VALUES.get(primitive);
+ }
+}
Propchange: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/PrimitiveTypeProperties.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Boolean.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Boolean.java?rev=1507380&r1=1507379&r2=1507380&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Boolean.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/GO_Boolean.java
[UTF-8] Fri Jul 26 17:41:33 2013
@@ -17,7 +17,6 @@
package org.apache.sis.internal.jaxb.gco;
import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.adapters.XmlAdapter;
/**
@@ -31,26 +30,10 @@ import javax.xml.bind.annotation.adapter
* @author Cédric Briançon (Geomatys)
* @author Martin Desruisseaux (Geomatys)
* @since 0.3 (derived from geotk-2.5)
- * @version 0.3
+ * @version 0.4
* @module
*/
-public final class GO_Boolean extends XmlAdapter<GO_Boolean, Boolean> {
- /**
- * Wraps the {@link Boolean#TRUE} value.
- */
- private static final GO_Boolean TRUE = new GO_Boolean(Boolean.TRUE);
-
- /**
- * Wraps the {@link Boolean#FALSE} value.
- */
- private static final GO_Boolean FALSE = new GO_Boolean(Boolean.FALSE);
-
- /**
- * The boolean value to handle.
- */
- @XmlElement(name = "Boolean")
- public Boolean value;
-
+public final class GO_Boolean extends PropertyType<GO_Boolean, Boolean> {
/**
* Empty constructor used only by JAXB.
*/
@@ -63,18 +46,15 @@ public final class GO_Boolean extends Xm
* @param value The value.
*/
private GO_Boolean(final Boolean value) {
- this.value = value;
+ super(value, !value);
}
/**
- * Allows JAXB to generate a Boolean object using the value found in the adapter.
- *
- * @param value The value wrapped in an adapter.
- * @return The boolean value extracted from the adapter.
+ * Returns the Java type which is bound by this adapter.
*/
@Override
- public Boolean unmarshal(final GO_Boolean value) {
- return (value != null) ? value.value : null;
+ protected Class<Boolean> getBoundType() {
+ return Boolean.class;
}
/**
@@ -86,12 +66,26 @@ public final class GO_Boolean extends Xm
* by {@code <gco:Boolean>} element.
*/
@Override
- public GO_Boolean marshal(final Boolean value) {
- if (value == null) {
- return null;
- }
- final GO_Boolean c = value ? TRUE : FALSE;
- assert value.equals(c.value) : value;
- return c;
+ protected GO_Boolean wrap(final Boolean value) {
+ return new GO_Boolean(value);
+ }
+
+ /**
+ * Invoked by JAXB at marshalling time for getting the actual value to write.
+ *
+ * @return The value to be marshalled.
+ */
+ @XmlElement(name = "Boolean")
+ public Boolean getElement() {
+ return skip() ? null : metadata;
+ }
+
+ /**
+ * Invoked by JAXB at unmarshalling time for storing the result temporarily.
+ *
+ * @param metadata The unmarshalled value.
+ */
+ public void setElement(final Boolean metadata) {
+ this.metadata = metadata;
}
}
Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java?rev=1507380&r1=1507379&r2=1507380&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
[UTF-8] Fri Jul 26 17:41:33 2013
@@ -30,6 +30,7 @@ import org.apache.sis.xml.IdentifierSpac
import org.apache.sis.xml.IdentifiedObject;
import org.apache.sis.xml.ReferenceResolver;
import org.apache.sis.internal.jaxb.Context;
+import org.apache.sis.internal.jaxb.PrimitiveTypeProperties;
import org.apache.sis.util.iso.SimpleInternationalString;
@@ -139,6 +140,23 @@ public abstract class PropertyType<Value
}
/**
+ * Builds an adapter for the given primitive wrapper. This constructor checks for nil
reasons
+ * only if {@code check} is {@code true}.
+ *
+ * @param value The primitive type wrapper.
+ * @param check {@code true} if we should check for nil reasons.
+ */
+ PropertyType(final BoundType value, final boolean check) {
+ metadata = value;
+ if (check) {
+ final Object property = PrimitiveTypeProperties.property(value);
+ if (property instanceof NilReason) {
+ reference = property.toString();
+ }
+ }
+ }
+
+ /**
* Builds an adapter for the given GeoAPI interface. This constructor checks if the given
metadata
* implements the {@link NilObject} or {@link IdentifiedObject} interface. If the object
implements
* both of them (should not happen, but we never know), then the identifiers will have
precedence.
@@ -147,6 +165,10 @@ public abstract class PropertyType<Value
*/
protected PropertyType(final BoundType metadata) {
this.metadata = metadata;
+ /*
+ * Do not invoke NilReason.getNilReason(metadata) in order to avoid unnecessary synchronization.
+ * Subclasses will use PropertyType(BoundType, boolean) when a check for primitive
type is required.
+ */
if (metadata instanceof NilObject) {
final NilReason reason = ((NilObject) metadata).getNilReason();
if (reason != null) {
@@ -272,7 +294,7 @@ public abstract class PropertyType<Value
* @return {@code true} if the wrapped metadata should not be marshalled.
*/
protected final boolean skip() {
- return (metadata instanceof NilObject) || (reference instanceof ObjectReference);
+ return reference != null;
}
/**
Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakValueHashMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakValueHashMap.java?rev=1507380&r1=1507379&r2=1507380&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakValueHashMap.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/collection/WeakValueHashMap.java
[UTF-8] Fri Jul 26 17:41:33 2013
@@ -42,8 +42,12 @@ import java.util.Objects;
* A hashtable-based map implementation that uses {@linkplain WeakReference weak references},
* leaving memory when an entry is not used anymore. An entry in a {@code WeakValueHashMap}
* will automatically be removed when its value is no longer in ordinary use. This class
is
- * similar to the standard {@link java.util.WeakHashMap} class provided in J2SE, except that
- * weak references are hold on values instead of keys.
+ * similar to the standard {@link java.util.WeakHashMap} class, except that weak references
+ * apply to values rather than keys.
+ *
+ * <p>Note that this class is <strong>not</strong> a cache, because the
entries are discarded
+ * as soon as the garbage collector determines that they are no longer in use. If caching
+ * service are wanted, or if concurrency are wanted, consider using {@link Cache} instead.</p>
*
* <p>This class is convenient for avoiding the creation of duplicated elements, as
in the
* example below:</p>
@@ -60,20 +64,19 @@ import java.util.Objects;
* }
* }
*
- * The calculation of a new value should be fast, because it is performed inside a synchronized
- * statement blocking all other access to the map. This is okay if that particular map instance
+ * In the above example, the calculation of a new value needs to be fast because it is performed
inside a synchronized
+ * statement blocking all other access to the map. This is okay if that particular {@code
WeakValueHashMap} instance
* is not expected to be used in a highly concurrent environment.
*
- * <p>Note that this class is <strong>not</strong> a cache, because the
entries are discarded
- * as soon as the garbage collector determines that they are no longer in use. If caching
- * service are wanted, or if concurrency are wanted, consider using {@link Cache} instead.</p>
+ * <p>{@code WeakValueHashMap} works with array keys as one would expect. For example
arrays of {@code int[]} are
+ * compared using the {@link java.util.Arrays#equals(int[], int[])} method.</p>
*
* @param <K> The class of key elements.
* @param <V> The class of value elements.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @since 0.3 (derived from geotk-2.0)
- * @version 0.3
+ * @version 0.4
* @module
*
* @see java.util.WeakHashMap
@@ -83,6 +86,14 @@ import java.util.Objects;
@ThreadSafe
public class WeakValueHashMap<K,V> extends AbstractMap<K,V> {
/**
+ * The comparison mode for key objects.
+ *
+ * @see #keyEquals(Object, Object)
+ * @see #keyHashCode(Object)
+ */
+ private static final int IDENTITY = 0, EQUALS = 1, DEEP_EQUALS = 2;
+
+ /**
* An entry in the {@link WeakValueHashMap}. This is a weak reference
* to a value together with a strong reference to a key.
*/
@@ -184,10 +195,10 @@ public class WeakValueHashMap<K,V> exten
private final Class<K> keyType;
/**
- * {@code true} if the keys in this map may be arrays. If the keys can not be
- * arrays, then we can avoid the calls to the costly {@link Utilities} methods.
+ * {@link #DEEP_EQUALS} if the keys in this map may be arrays. If the keys can not be
arrays,
+ * then we can avoid the calls to the costly {@link Utilities} methods.
*/
- private final boolean mayContainArrays;
+ private final int comparisonMode;
/**
* The set of entries, created only when first needed.
@@ -208,8 +219,24 @@ public class WeakValueHashMap<K,V> exten
* @param keyType The type of keys in the map.
*/
public WeakValueHashMap(final Class<K> keyType) {
- this.keyType = keyType;
- mayContainArrays = keyType.isArray() || keyType.equals(Object.class);
+ this(keyType, false);
+ }
+
+ /**
+ * Creates a new {@code WeakValueHashMap}, optionally using reference-equality in place
of object-equality.
+ * If {@code identity} is {@code true}, then two keys {@code k1} and {@code k2} are considered
equal if and
+ * only if {@code (k1 == k2)} instead than if {@code k1.equals(k2)}.
+ *
+ * @param keyType The type of keys in the map.
+ * @param byIdentity {@code true} if the map shall use reference-equality in place of
object-equality
+ * when comparing keys, or {@code false} for the standard behavior.
+ *
+ * @since 0.4
+ */
+ public WeakValueHashMap(final Class<K> keyType, final boolean byIdentity) {
+ this.keyType = keyType;
+ comparisonMode = byIdentity ? IDENTITY :
+ (keyType.isArray() || keyType.equals(Object.class)) ? DEEP_EQUALS : EQUALS;
lastTimeNormalCapacity = System.nanoTime();
/*
* Workaround for the "generic array creation" compiler error.
@@ -277,16 +304,31 @@ public class WeakValueHashMap<K,V> exten
/**
* Returns the hash code value for the given key.
+ *
+ * @param key The key (can not be null).
*/
final int keyHashCode(final Object key) {
- return mayContainArrays ? Utilities.deepHashCode(key) : key.hashCode();
+ switch (comparisonMode) {
+ case IDENTITY: return System.identityHashCode(key);
+ case EQUALS: return key.hashCode();
+ case DEEP_EQUALS: return Utilities.deepHashCode(key);
+ default: throw new AssertionError(comparisonMode);
+ }
}
/**
* Returns {@code true} if the two given keys are equal.
+ *
+ * @param k1 The first key (can not be null).
+ * @paral k2 The second key.
*/
final boolean keyEquals(final Object k1, final Object k2) {
- return mayContainArrays ? Objects.deepEquals(k1, k2) : k1.equals(k2);
+ switch (comparisonMode) {
+ case IDENTITY: return k1 == k2;
+ case EQUALS: return k1.equals(k2);
+ case DEEP_EQUALS: return Objects.deepEquals(k1, k2);
+ default: throw new AssertionError(comparisonMode);
+ }
}
/**
Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/xml/NilObject.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/xml/NilObject.java?rev=1507380&r1=1507379&r2=1507380&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/xml/NilObject.java [UTF-8]
(original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/xml/NilObject.java [UTF-8]
Fri Jul 26 17:41:33 2013
@@ -81,6 +81,8 @@ public interface NilObject {
* Returns the reason why this object contains no information.
*
* @return The reason why this object contains no information.
+ *
+ * @see NilReason#getNilReason(Object)
*/
NilReason getNilReason();
}
Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/xml/NilReason.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/xml/NilReason.java?rev=1507380&r1=1507379&r2=1507380&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/xml/NilReason.java [UTF-8]
(original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/xml/NilReason.java [UTF-8]
Fri Jul 26 17:41:33 2013
@@ -27,6 +27,7 @@ import org.apache.sis.util.ArgumentCheck
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.LenientComparable;
import org.apache.sis.util.collection.WeakHashSet;
+import org.apache.sis.internal.jaxb.PrimitiveTypeProperties;
/**
@@ -48,7 +49,7 @@ import org.apache.sis.util.collection.We
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3 (derived from geotk-3.18)
- * @version 0.3
+ * @version 0.4
* @module
*
* @see NilObject
@@ -143,6 +144,26 @@ public final class NilReason implements
private final Object reason;
/**
+ * Special boolean value for attaching this {@code NilReason} to a {@code Boolean}.
+ */
+ private transient Boolean nilBoolean;
+
+ /**
+ * Special integer value for attaching this {@code NilReason} to an {@code Integer}.
+ */
+ private transient Integer nilInteger;
+
+ /**
+ * Special double value for attaching this {@code NilReason} to a {@code Double}.
+ */
+ private transient Double nilDouble;
+
+ /**
+ * Special string value for attaching this {@code NilReason} to a {@code String}.
+ */
+ private transient String nilString;
+
+ /**
* The invocation handler for {@link NilObject} instances, created when first needed.
* The same handler can be shared for all objects having the same {@code NilReason},
* no matter the interface they implement.
@@ -334,21 +355,31 @@ public final class NilReason implements
/**
* Returns an object of the given type which is nil for the reason represented by this
instance.
- * This method returns an object which implement the given interface together with the
- * {@link NilObject} interface. The {@link NilObject#getNilReason()} method will return
- * this {@code NilReason} instance, and all other methods (except the ones inherited
from
- * the {@code Object} class) will return an empty collection, empty array, {@code null},
- * {@link Double#NaN NaN}, {@code 0} or {@code false}, in this preference order,
- * depending on the method return type.
+ * The {@code type} argument can be one of the following cases:
+ *
+ * <ul>
+ * <li><p>An <strong>interface</strong>: in such case, this
method returns an object which implement the given
+ * interface together with the {@link NilObject} interface. The {@link NilObject#getNilReason()}
method
+ * will return this {@code NilReason} instance, and all other methods (except the
ones inherited from
+ * the {@code Object} class) will return an empty collection, empty array, {@code
null},
+ * {@link Double#NaN NaN}, {@code 0} or {@code false}, in this preference order,
+ * depending on the method return type.</p></li>
+ * <li><p>One of {@link Boolean}, {@link Integer}, {@link Double} or {@link
String} types: in such case,
+ * this method returns a specific instance which will be recognized as "nil" by
the XML marshaller.</p></li>
+ * </ul>
*
* @param <T> The compile-time type of the {@code type} argument.
- * @param type The object type as an <strong>interface</strong>.
- * This is usually a <a href="http://www.geoapi.org">GeoAPI</a> interface.
+ * @param type The object type as an <strong>interface</strong>
+ * (usually a <a href="http://www.geoapi.org">GeoAPI</a> one) or
one of the special types.
+ * @throws IllegalArgumentException If the given type is not a supported type.
* @return An {@link NilObject} of the given type.
*/
@SuppressWarnings("unchecked")
public <T> T createNilObject(final Class<T> type) {
ArgumentChecks.ensureNonNull("type", type);
+ if (!type.isInterface()) {
+ return createNilPrimitive(type);
+ }
if (NilObjectHandler.isIgnoredInterface(type)) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2,
"type", type));
}
@@ -361,4 +392,98 @@ public final class NilReason implements
return (T) Proxy.newProxyInstance(NilReason.class.getClassLoader(),
new Class<?>[] {type, NilObject.class, LenientComparable.class}, h);
}
+
+ /**
+ * Returns an {@code Boolean}, {@code Integer}, {@code Double} or {@code String} which
is nil for the reason
+ * represented by this instance.
+ *
+ * @throws IllegalArgumentException If the given type is not a supported type.
+ */
+ @SuppressWarnings("unchecked")
+ private <T> T createNilPrimitive(final Class<T> type) {
+ if (type == Boolean.class) {
+ Boolean value;
+ synchronized (this) {
+ if ((value = nilBoolean) == null) {
+ nilBoolean = value = new Boolean(false); // REALLY need a new instance,
not Boolean.FALSE.
+ PrimitiveTypeProperties.associate(value, this);
+ }
+ }
+ return (T) value;
+ }
+ if (type == Integer.class) {
+ Integer value;
+ synchronized (this) {
+ if ((value = nilInteger) == null) {
+ nilInteger = value = new Integer(0); // REALLY need a new instance, not
Integer.valueOf(…).
+ PrimitiveTypeProperties.associate(value, this);
+ }
+ }
+ return (T) value;
+ }
+ if (type == Double.class) {
+ Double value;
+ synchronized (this) {
+ if ((value = nilDouble) == null) {
+ nilDouble = value = new Double(Double.NaN); // REALLY need a new instance,
not Double.valueOf(…).
+ PrimitiveTypeProperties.associate(value, this);
+ }
+ }
+ return (T) value;
+ }
+ if (type == String.class) {
+ String value;
+ synchronized (this) {
+ if ((value = nilString) == null) {
+ nilString = value = new String(""); // REALLY need a new instance.
+ PrimitiveTypeProperties.associate(value, this);
+ }
+ }
+ return (T) value;
+ }
+ /*
+ * REMINDER: If more special cases are added, do not forget to update the getNilReason(Object)
method
+ * below and to update javadoc.
+ */
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2,
"type", type));
+ }
+
+ /**
+ * If the given object is nil, returns the reason why it does not contain information.
+ * This method performs the following choices:
+ *
+ * <ul>
+ * <li>If the given object implements the {@link NilObject} interface, then this
method delegates
+ * to the {@link NilObject#getNilReason()} method.</li>
+ * <li>Otherwise if the given object is one of the {@link Boolean}, {@link Integer},
{@link Double}
+ * or {@link String} instances returned by {@link #createNilObject(Class)}, then
this method
+ * returns the associated reason.</li>
+ * <li>Otherwise this method returns {@code null}.</li>
+ * </ul>
+ *
+ * @param object The object for which to get the {@code NilReason}, or {@code null}.
+ * @return The reason why the given object contains no information,
+ * or {@code null} if the given object is not nil.
+ *
+ * @see NilObject#getNilReason()
+ *
+ * @since 0.4
+ */
+ public static NilReason getNilReason(final Object object) {
+ if (object != null) {
+ if (object instanceof NilObject) {
+ return ((NilObject) object).getNilReason();
+ }
+ /*
+ * Invoke 'PrimitiveTypeProperties' method only if the given type is one of the
hard-coded
+ * types from the above 'createNilPrimitive(Object)' method, in order to avoid
unnecessary
+ * synchronization (implicitly done by PrimitiveTypeProperties).
+ */
+ final Class<?> type = object.getClass();
+ if (type == Boolean.class || type == Integer.class || type == Double.class ||
type == String.class) {
+ return (NilReason) PrimitiveTypeProperties.property(object);
+ }
+ }
+ return null;
+ }
}
Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/collection/WeakValueHashMapTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/collection/WeakValueHashMapTest.java?rev=1507380&r1=1507379&r2=1507380&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/collection/WeakValueHashMapTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/collection/WeakValueHashMapTest.java
[UTF-8] Fri Jul 26 17:41:33 2013
@@ -36,7 +36,7 @@ import static org.apache.sis.test.TestUt
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @since 0.3 (derived from geotk-2.0)
- * @version 0.3
+ * @version 0.4
* @module
*/
@DependsOn(org.apache.sis.util.ArraysExtTest.class)
@@ -197,4 +197,29 @@ public final strictfp class WeakValueHas
assertSame (v1, weakMap.get(k1));
assertSame (v2, weakMap.get(k2));
}
+
+ /**
+ * Tests using identity comparisons. This test uses two {@link Integer} keys having the
same value
+ * but being different instances.
+ *
+ * @since 0.4
+ */
+ @Test
+ @DependsOnMethod("testStrongReferences")
+ public void testIdentityComparisons() {
+ final WeakValueHashMap<Integer,Integer> weakMap = new WeakValueHashMap<>(Integer.class,
true);
+ final Integer k1 = 10;
+ final Integer k2 = 20;
+ final Integer k3 = new Integer(10); // Really want a new instance.
+ final Integer v1 = 1;
+ final Integer v2 = 2;
+ final Integer v3 = 3;
+ assertEquals(k1, k3); // Necessary condition for the test to be valid.
+ assertNull(weakMap.put(k1, v1)); assertSame(v1, weakMap.put(k1, v1));
+ assertNull(weakMap.put(k2, v2)); assertSame(v2, weakMap.put(k2, v2));
+ assertNull(weakMap.put(k3, v3)); assertSame(v3, weakMap.put(k3, v3));
+ assertSame(v1, weakMap.get(k1));
+ assertSame(v2, weakMap.get(k2));
+ assertSame(v3, weakMap.get(k3));
+ }
}
|