sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1507380 - in /sis/branches/JDK7/core: sis-metadata/src/test/java/org/apache/sis/xml/ sis-utility/src/main/java/org/apache/sis/internal/jaxb/ sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/ sis-utility/src/main/java/org/apache/s...
Date Fri, 26 Jul 2013 17:41:33 GMT
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));
+    }
 }



Mime
View raw message