sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1765842 - in /sis/branches/JDK8/core/sis-utility/src: main/java/org/apache/sis/measure/ test/java/org/apache/sis/measure/
Date Thu, 20 Oct 2016 16:00:31 GMT
Author: desruisseaux
Date: Thu Oct 20 16:00:31 2016
New Revision: 1765842

URL: http://svn.apache.org/viewvc?rev=1765842&view=rev
Log:
Keep trace of hard-coded units and allow to retrieve them by their symbol.

Added:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitDimension.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/SystemUnitTest.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitDimensionTest.java

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java?rev=1765842&r1=1765841&r2=1765842&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java
[UTF-8] Thu Oct 20 16:00:31 2016
@@ -20,6 +20,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.ResourceBundle;
 import java.util.MissingResourceException;
+import java.io.ObjectStreamException;
 import java.io.Serializable;
 import javax.measure.Unit;
 import javax.measure.Quantity;
@@ -250,4 +251,17 @@ abstract class AbstractUnit<Q extends Qu
             return UnitFormat.INSTANCE.format(this);
         }
     }
+
+    /**
+     * Invoked on deserialization for returning a unique instance of {@code AbstractUnit}
if possible.
+     */
+    final Object readResolve() throws ObjectStreamException {
+        if (Units.initialized) {                // Force Units class initialization.
+            final Unit<?> exising = (Unit<?>) UnitRegistry.putIfAbsent(symbol,
this);
+            if (equals(exising)) {
+                return exising;
+            }
+        }
+        return this;
+    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java?rev=1765842&r1=1765841&r2=1765842&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/SystemUnit.java
[UTF-8] Thu Oct 20 16:00:31 2016
@@ -315,13 +315,23 @@ final class SystemUnit<Q extends Quantit
      * @throws IllegalArgumentException if the specified symbol is already associated to
a different unit.
      */
     @Override
+    @SuppressWarnings("unchecked")
     public Unit<Q> alternate(final String symbol) {
         ArgumentChecks.ensureNonNull("symbol", symbol);
         if (symbol.equals(getSymbol())) {
             return this;
         }
-        // TODO: check for existing units.
-        return new SystemUnit<>(quantity, dimension, symbol, (short) 0);
+        final SystemUnit<Q> alt = new SystemUnit<>(quantity, dimension, symbol,
(short) 0);
+        final Unit<?> existing = (Unit<?>) UnitRegistry.putIfAbsent(symbol, alt);
+        if (existing != null) {
+            if (existing instanceof SystemUnit<?> && ((SystemUnit<?>)
existing).quantity == quantity
+                    && dimension.equals(existing.getDimension()))
+            {
+                return (Unit<Q>) existing;
+            }
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.ElementAlreadyPresent_1,
symbol));
+        }
+        return alt;
     }
 
     /**
@@ -426,12 +436,4 @@ final class SystemUnit<Q extends Quantit
     public int hashCode() {
         return super.hashCode() + 37 * dimension.hashCode();
     }
-
-    /**
-     * Invoked on deserialization for returning a unique instance of {@code SystemUnit}.
-     */
-    Object readResolve() throws ObjectStreamException {
-        final SystemUnit<Q> u = Units.get(quantity);
-        return (u != null) ? u : this;
-    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitDimension.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitDimension.java?rev=1765842&r1=1765841&r2=1765842&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitDimension.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitDimension.java
[UTF-8] Thu Oct 20 16:00:31 2016
@@ -19,8 +19,6 @@ package org.apache.sis.measure;
 import java.util.Collections;
 import java.util.Map;
 import java.util.LinkedHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ConcurrentHashMap;
 import java.io.Serializable;
 import java.io.IOException;
 import java.io.ObjectStreamException;
@@ -67,19 +65,10 @@ final class UnitDimension implements Dim
     private static final long serialVersionUID = 2568769237612674235L;
 
     /**
-     * Pool of all {@code UnitDimension} instances created up to date.
-     * Keys are the same map than {@link UnitDimension#components}, which shall be immutable.
-     * We hold the dimensions by strong reference on the assumption that we will not create
many of them.
-     */
-    private static final ConcurrentMap<Map<UnitDimension,Fraction>, UnitDimension>
POOL = new ConcurrentHashMap<>();
-
-    // The POOL map must be created before the following constants.
-
-    /**
      * Pseudo-dimension for dimensionless units.
      */
     static final UnitDimension NONE = new UnitDimension(Collections.emptyMap());
-    // No need to store in the POOL cache since UnitDimension performs special checks for
dimensionless instances.
+    // No need to store in UnitRegistry since UnitDimension performs special checks for dimensionless
instances.
 
     /**
      * The product of base dimensions that make this dimension. All keys in this map shall
be base dimensions
@@ -106,9 +95,7 @@ final class UnitDimension implements Dim
     UnitDimension(final char symbol) {
         this.symbol = symbol;
         components  = Collections.singletonMap(this, new Fraction(1,1).unique());
-        if (POOL.putIfAbsent(components, this) != null) {
-            throw new AssertionError(this);
-        }
+        UnitRegistry.init(components, this);
     }
 
     /**
@@ -144,14 +131,18 @@ final class UnitDimension implements Dim
          * Implementation note: following code duplicates the functionality of Map.computeIfAbsent(…),
          * but we had to do it because we compute not only the value, but also the 'components'
key.
          */
-        UnitDimension dim = POOL.get(components);
+        UnitDimension dim = (UnitDimension) UnitRegistry.get(components);
         if (dim == null) {
             components.replaceAll((c, power) -> power.unique());
             components = CollectionsExt.unmodifiableOrCopy(components);
             dim = new UnitDimension(components);
-            final UnitDimension c = POOL.putIfAbsent(components, dim);
-            if (c != null) {
-                return c;       // UnitDimension created concurrently in another thread.
+            if (!Units.initialized) {
+                UnitRegistry.init(components, dim);
+            } else {
+                final UnitDimension c = (UnitDimension) UnitRegistry.putIfAbsent(components,
dim);
+                if (c != null) {
+                    return c;       // UnitDimension created concurrently in another thread.
+                }
             }
         }
         return dim;
@@ -161,9 +152,16 @@ final class UnitDimension implements Dim
      * Invoked on deserialization for returning a unique instance of {@code UnitDimension}.
      */
     Object readResolve() throws ObjectStreamException {
-        if (isDimensionless()) return NONE;
-        final UnitDimension dim = POOL.putIfAbsent(components, this);
-        return (dim != null) ? dim : this;
+        if (isDimensionless()) {
+            return NONE;
+        }
+        if (Units.initialized) {        // Force Units class initialization.
+            final UnitDimension dim = (UnitDimension) UnitRegistry.putIfAbsent(components,
this);
+            if (dim != null) {
+                return dim;
+            }
+        }
+        return this;
     }
 
     /**

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java?rev=1765842&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
(added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
[UTF-8] Thu Oct 20 16:00:31 2016
@@ -0,0 +1,202 @@
+/*
+ * 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.measure;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashMap;
+import java.util.HashSet;
+import javax.measure.Unit;
+import javax.measure.Quantity;
+import javax.measure.Dimension;
+import javax.measure.spi.SystemOfUnits;
+import org.apache.sis.math.Fraction;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.collection.WeakValueHashMap;
+
+
+/**
+ * Lookup mechanism for finding a units from its quantity, dimension or symbol.
+ * This class opportunistically implements {@link SystemOfUnits}, but Apache SIS
+ * rather uses the static methods directly since we define all units in terms of SI.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.8
+ * @version 0.8
+ * @module
+ */
+final class UnitRegistry implements SystemOfUnits {
+    /**
+     * All {@link UnitDimension}, {@link SystemUnit} or {@link ConventionalUnit} that are
hard-coded in Apache SIS.
+     * This map is populated by {@link Units} static initializer and shall not be modified
after initialization,
+     * in order to avoid the need for synchronization. Key and value types are restricted
to the following pairs:
+     *
+     * <table class="sis">
+     *   <caption>Key and value types</caption>
+     *   <tr><th>Key type</th>                            <th>Value
type</th>            <th>Description</th></tr>
+     *   <tr><td>{@code Map<UnitDimension,Fraction>}</td> <td>{@link
UnitDimension}</td> <td>Key is the base dimensions with their powers</td></tr>
+     *   <tr><td>{@link UnitDimension}</td>               <td>{@link
SystemUnit}</td>    <td>Key is the dimension of base or derived units.</td></tr>
+     *   <tr><td>{@code Class<Quantity>}</td>             <td>{@link
SystemUnit}</td>    <td>Key is the quantity type of base of derived units.</td></tr>
+     *   <tr><td>{@link String}</td>                      <td>{@link
AbstractUnit}</td>  <td>Key is the unit symbol.</td></tr>
+     *   <tr><td>{@link Short}</td>                       <td>{@link
AbstractUnit}</td>  <td>Key is the EPSG code.</td></tr>
+     * </table>
+     */
+    private static final Map<Object,Object> HARD_CODED = new HashMap<>(128);
+
+    /**
+     * Units defined by the user. Accesses to this map implies synchronization.
+     * Values are stored by weak references and garbage collected when no longer used.
+     * Key and value types are the same than the one described in {@link #HARD_CODED}.
+     *
+     * <div class="note"><b>Implementation note:</b>
+     * we separate hard-coded values from user-defined values because the amount of hard-coded
values is relatively
+     * large, using weak references for them is useless, and most applications will not define
any custom values.
+     * This map will typically stay empty.</div>
+     */
+    private static final WeakValueHashMap<Object,Object> USER_DEFINED = new WeakValueHashMap<>(Object.class);
+
+    /**
+     * Adds the given {@code components}, {@code dim} pair in the map of hard-coded values.
+     * This method shall be invoked in a single thread by the {@code Units} class initializer
only (indirectly).
+     */
+    static void init(final Map<UnitDimension,Fraction> components, final UnitDimension
dim) {
+        assert !Units.initialized : dim.symbol;         // This assertion happens during
Units initialization, but it is okay.
+        if (HARD_CODED.put(components, dim) != null) {
+            throw new AssertionError(dim.symbol);       // Shall not map the same dimension
twice.
+        }
+    }
+
+    /**
+     * Invoked by {@link Units} static class initializer for registering SI base and derived
units.
+     * This method shall be invoked in a single thread by the {@code Units} class initializer
only.
+     */
+    static <Q extends Quantity<Q>> SystemUnit<Q> init(final SystemUnit<Q>
unit) {
+        assert !Units.initialized : unit;        // This assertion happens during Units initialization,
but it is okay.
+        boolean existed;
+        existed  = HARD_CODED.put(unit.dimension,   unit) != null;
+        existed |= HARD_CODED.put(unit.quantity,    unit) != null;
+        existed |= HARD_CODED.put(unit.getSymbol(), unit) != null;
+        assert !existed || unit.dimension.components.isEmpty() : unit;   // Key collision
tolerated for dimensionless unit only.
+        return unit;
+    }
+
+    /**
+     * Invoked by {@link Units} static class initializer for registering SI conventional
units.
+     * This method shall be invoked in a single thread by the {@code Units} class initializer
only.
+     */
+    static <Q extends Quantity<Q>> ConventionalUnit<Q> init(final ConventionalUnit<Q>
unit) {
+        assert !Units.initialized : unit;        // This assertion happens during Units initialization,
but it is okay.
+        if (HARD_CODED.put(unit.getSymbol(), unit) != null) {
+            throw new AssertionError(unit);      // Shall not map the same unit twice.
+        }
+        return unit;
+    }
+
+    /**
+     * Adds the given {@code key}, {@code value} pair in the map of user-defined values,
provided that no value
+     * is currently associated to the given key. This method shall be invoked only after
the {@link Units} class
+     * has been fully initialized.
+     */
+    static Object putIfAbsent(final Object key, final Object value) {
+        assert Units.initialized : value;
+        Object previous = HARD_CODED.get(key);
+        if (previous == null) {
+            previous = USER_DEFINED.putIfAbsent(key, value);
+        }
+        return previous;
+    }
+
+    /**
+     * Returns the value associated to the given key, or {@code null} if none.
+     * This method can be invoked at anytime (at {@link Units} class initialization time
or not).
+     */
+    static Object get(final Object key) {
+        Object value = HARD_CODED.get(key);     // Treated as immutable, no synchronization
needed.
+        if (value == null) {
+            value = USER_DEFINED.get(key);      // Implies a synchronization lock.
+        }
+        return value;
+    }
+
+    /**
+     * The value returned by {@link #getUnits()}, created when first needed.
+     */
+    private static Set<Unit<?>> units;
+
+    /**
+     * Creates a new unit system.
+     */
+    UnitRegistry() {
+    }
+
+    /**
+     * Returns the well-known acronym that stands for "Système International"
+     * together with the name of other systems used.
+     */
+    @Override
+    public String getName() {
+        return "SI and others";
+    }
+
+    /**
+     * Returns the default unit for the specified quantity, or {@code null} if none.
+     */
+    @Override
+    public <Q extends Quantity<Q>> Unit<Q> getUnit(final Class<Q>
type) {
+        return Units.get(type);
+    }
+
+    /**
+     * Returns a read only view over the units explicitly defined by this system.
+     * This include the base and derived units which are assigned a special name and symbol.
+     * This set does not include new units created by arithmetic or other operations.
+     */
+    @Override
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    public Set<Unit<?>> getUnits() {
+        if (Units.initialized) {                    // Force Units class initialization.
+            synchronized (UnitRegistry.class) {
+                if (units == null) {
+                    units = new HashSet<>();
+                    for (final Object value : HARD_CODED.values()) {
+                        if (value instanceof Unit<?>) {
+                            units.add((Unit<?>) value);
+                        }
+                    }
+                    units = Collections.unmodifiableSet(units);
+                }
+            }
+        }
+        return units;
+    }
+
+    /**
+     * Returns the units defined in this system having the specified dimension, or an empty
set if none.
+     */
+    @Override
+    public Set<Unit<?>> getUnits(final Dimension dimension) {
+        ArgumentChecks.ensureNonNull("dimension", dimension);
+        final Set<Unit<?>> filtered = new HashSet<>();
+        for (final Unit<?> unit : getUnits()) {
+            if (dimension.equals(unit.getDimension())) {
+                filtered.add(unit);
+            }
+        }
+        return filtered;
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitRegistry.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java?rev=1765842&r1=1765841&r2=1765842&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8]
(original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8]
Thu Oct 20 16:00:31 2016
@@ -16,8 +16,6 @@
  */
 package org.apache.sis.measure;
 
-import java.util.Map;
-import java.util.HashMap;
 import javax.measure.Dimension;
 import javax.measure.Unit;
 import javax.measure.UnitConverter;
@@ -83,30 +81,6 @@ import static org.apache.sis.measure.Sex
  */
 public final class Units extends Static {
     /**
-     * The units for given {@link UnitDimension} or {@code Class<Quantity>} instances.
-     * This map contains mostly SI units (no imperial units) with the addition of some alternative
units.
-     * This map must be unmodified after it has been populated.
-     */
-    private static final Map<Object, SystemUnit<?>> SYSTEM = new HashMap<>();
-
-    /**
-     * Returns the system unit for the given quantity, or {@code null} if none.
-     */
-    @SuppressWarnings("unchecked")
-    static <Q extends Quantity<Q>> SystemUnit<Q> get(final Class<Q>
type) {
-        return (SystemUnit<Q>) SYSTEM.get(type);
-    }
-
-    /**
-     * Returns the system unit for the given dimension, or {@code null} if none.
-     * Note that this method can not distinguish the different kinds of dimensionless units.
-     * If the quantity type is known, use {@link #get(Class)} instead.
-     */
-    static SystemUnit<?> get(final Dimension dim) {
-        return SYSTEM.get(dim);
-    }
-
-    /**
      * Unit of measurement defined as 10<sup>-9</sup> metres (1 nm). This unit
is often used in
      * {@linkplain org.apache.sis.metadata.iso.content.DefaultBand#getBoundUnits() wavelength
measurements}.
      * The {@linkplain ConventionalUnit#getSystemUnit() system unit} is {@link #METRE}
@@ -650,6 +624,25 @@ public final class Units extends Static
      */
     public static final Unit<Dimensionless> PIXEL;
 
+    /**
+     * Sets to {@code true} by the static initializer after the initialization has been completed.
+     * This is a safety against unexpected changes in the {@link UnitRegistry#HARD_CODED}
map.
+     *
+     * <p>We use here a "lazy final initialization" pattern. We rely on the fact that
this field is
+     * initialized to {@code true} only at the end of the following static initializer. All
methods
+     * invoked in the static initializer will see the default value, which is {@code false},
until
+     * the initializer fully completed. While apparently dangerous, this behavior is actually
documented
+     * in <a href="http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.1">section
12.4.1
+     * of Java language specification</a>:</p>
+     *
+     * <blockquote>The fact that initialization code is unrestricted allows examples
to be constructed where
+     * the value of a class variable can be observed when it still has its initial default
value, before its
+     * initializing expression is evaluated, but such examples are rare in practice. (…snip…)
The full power
+     * of the Java programming language is available in these initializers; programmers must
exercise some care.
+     * This power places an extra burden on code generators, but this burden would arise
in any case because
+     * the Java programming language is concurrent.</blockquote>
+     */
+    static final boolean initialized;
     static {
         final UnitDimension length        = new UnitDimension('L');
         final UnitDimension mass          = new UnitDimension('M');
@@ -664,13 +657,13 @@ public final class Units extends Static
         /*
          * Base, derived or alternate units that we need to reuse more than once in this
static initializer.
          */
-        final SystemUnit<Length>        m   = new SystemUnit<>(Length.class,
       length,        "m",   Constants.EPSG_METRE);
-        final SystemUnit<Time>          s   = new SystemUnit<>(Time.class,  
       time,          "s",   (short) 1040);
-        final SystemUnit<Temperature>   K   = new SystemUnit<>(Temperature.class,
  temperature,   "K",   (short) 0);
-        final SystemUnit<Speed>         mps = new SystemUnit<>(Speed.class, 
       speed,         "m∕s", (short) 1026);
-        final SystemUnit<Pressure>      Pa  = new SystemUnit<>(Pressure.class,
     pressure,      "Pa",  (short) 0);
-        final SystemUnit<Angle>         rad = new SystemUnit<>(Angle.class, 
       dimensionless, "rad", (short) 9101);
-        final SystemUnit<Dimensionless> one = new SystemUnit<>(Dimensionless.class,
dimensionless, "",    (short) 9201);
+        final SystemUnit<Length>        m   = add(Length.class,        length,    
   "m",   Constants.EPSG_METRE);
+        final SystemUnit<Time>          s   = add(Time.class,          time,      
   "s",   (short) 1040);
+        final SystemUnit<Temperature>   K   = add(Temperature.class,   temperature,
  "K",   (short) 0);
+        final SystemUnit<Speed>         mps = add(Speed.class,         speed,     
   "m∕s", (short) 1026);
+        final SystemUnit<Pressure>      Pa  = add(Pressure.class,      pressure,  
   "Pa",  (short) 0);
+        final SystemUnit<Angle>         rad = add(Angle.class,         dimensionless,
"rad", (short) 9101);
+        final SystemUnit<Dimensionless> one = add(Dimensionless.class, dimensionless,
"",    (short) 9201);
         /*
          * All SI prefix to be used below.
          */
@@ -683,74 +676,113 @@ public final class Units extends Static
         /*
          * All Unit<Angle>
          */
-        RADIAN      = add(rad);
-        GRAD        = new ConventionalUnit<>(rad, LinearConverter.create(Math.PI /
 200, 0),     "grad", (short) 9105);
-        DEGREE      = new ConventionalUnit<>(rad, LinearConverter.create(Math.PI /
 180, 0),        "°", (short) 9102);
-        ARC_MINUTE  = new ConventionalUnit<>(rad, LinearConverter.create(Math.PI /
(180*60),    0), "′", (short) 9103);
-        ARC_SECOND  = new ConventionalUnit<>(rad, LinearConverter.create(Math.PI /
(180*60*60), 0), "″", (short) 9104);
-        MICRORADIAN = new ConventionalUnit<>(rad, micro, "µrad", (short) 9109);
+        RADIAN      = rad;
+        GRAD        = add(rad, LinearConverter.scale(Math.PI, 200),    "grad", (short) 9105);
+        DEGREE      = add(rad, LinearConverter.scale(Math.PI, 180),       "°", (short) 9102);
+        ARC_MINUTE  = add(rad, LinearConverter.scale(Math.PI, 180*60),    "′", (short)
9103);
+        ARC_SECOND  = add(rad, LinearConverter.scale(Math.PI, 180*60*60), "″", (short)
9104);
+        MICRORADIAN = add(rad, micro, "µrad", (short) 9109);
         /*
          * All Unit<Length>
          */
-        METRE          = add(m);
-        NANOMETRE      = new ConventionalUnit<>(m, nano,  "nm", (short) 0);
-        MILLIMETRE     = new ConventionalUnit<>(m, milli, "mm", (short) 1025);
-        CENTIMETRE     = new ConventionalUnit<>(m, centi, "cm", (short) 1033);
-        KILOMETRE      = new ConventionalUnit<>(m, kilo,  "km", (short) 9036);
-        NAUTICAL_MILE  = new ConventionalUnit<>(m, LinearConverter.scale(   1852, 
      1), "M",     (short) 9030);
-        STATUTE_MILE   = new ConventionalUnit<>(m, LinearConverter.scale(1609344, 
    100), "mi",    (short) 9093);
-        US_SURVEY_FOOT = new ConventionalUnit<>(m, LinearConverter.scale(   1200, 
   3937), "ft_US", (short) 9003);
-        FOOT           = new ConventionalUnit<>(m, LinearConverter.scale(   3048, 
  10000), "ft",    (short) 9002);
-        INCH           = new ConventionalUnit<>(m, LinearConverter.scale(    254, 
  10000), "in",    (short) 0);
-        POINT          = new ConventionalUnit<>(m, LinearConverter.scale( 996264, 72000000),
"pt",    (short) 0);
+        METRE          = m;
+        NANOMETRE      = add(m, nano,  "nm", (short) 0);
+        MILLIMETRE     = add(m, milli, "mm", (short) 1025);
+        CENTIMETRE     = add(m, centi, "cm", (short) 1033);
+        KILOMETRE      = add(m, kilo,  "km", (short) 9036);
+        NAUTICAL_MILE  = add(m, LinearConverter.scale(   1852,        1), "M",     (short)
9030);
+        STATUTE_MILE   = add(m, LinearConverter.scale(1609344,      100), "mi",    (short)
9093);
+        US_SURVEY_FOOT = add(m, LinearConverter.scale(   1200,     3937), "ft_US", (short)
9003);
+        FOOT           = add(m, LinearConverter.scale(   3048,    10000), "ft",    (short)
9002);
+        INCH           = add(m, LinearConverter.scale(    254,    10000), "in",    (short)
0);
+        POINT          = add(m, LinearConverter.scale( 996264, 72000000), "pt",    (short)
0);
         /*
          * All Unit<Time>
          */
-        SECOND         = add(s);
-        MILLISECOND    = new ConventionalUnit<>(s, milli, "ms", (short) 0);
-        MINUTE         = new ConventionalUnit<>(s, LinearConverter.scale(         60,
     1), "min", (short) 0);
-        HOUR           = new ConventionalUnit<>(s, LinearConverter.scale(      60*60,
     1), "h",   (short) 0);
-        DAY            = new ConventionalUnit<>(s, LinearConverter.scale(   24*60*60,
     1), "d",   (short) 0);
-        WEEK           = new ConventionalUnit<>(s, LinearConverter.scale( 7*24*60*60,
     1), "wk",  (short) 0);
-        TROPICAL_YEAR  = new ConventionalUnit<>(s, LinearConverter.scale(31556925445.0,
1000), "a",   (short) 1029);
+        SECOND         = s;
+        MILLISECOND    = add(s, milli, "ms", (short) 0);
+        MINUTE         = add(s, LinearConverter.scale(         60,      1), "min", (short)
0);
+        HOUR           = add(s, LinearConverter.scale(      60*60,      1), "h",   (short)
0);
+        DAY            = add(s, LinearConverter.scale(   24*60*60,      1), "d",   (short)
0);
+        WEEK           = add(s, LinearConverter.scale( 7*24*60*60,      1), "wk",  (short)
0);
+        TROPICAL_YEAR  = add(s, LinearConverter.scale(31556925445.0, 1000), "a",   (short)
1029);
         /*
          * Other units.
          */
-        KELVIN              = add(K);
-        PASCAL              = add(Pa);
-        METRES_PER_SECOND   = add(mps);
-        KILOGRAM            = add(new SystemUnit<>(Mass.class,      mass,         
          "kg",   (short) 0));
-        SQUARE_METRE        = add(new SystemUnit<>(Area.class,      area,         
          "m²",   (short) 0));
-        CUBIC_METRE         = add(new SystemUnit<>(Volume.class,    length.pow(3),
          "m³",   (short) 0));
-        NEWTON              = add(new SystemUnit<>(Force.class,     force,        
          "N",    (short) 0));
-        JOULE               = add(new SystemUnit<>(Energy.class,    energy,       
          "J",    (short) 0));
-        WATT                = add(new SystemUnit<>(Power.class,     energy.divide(time),
    "W",    (short) 0));
-        HERTZ               = add(new SystemUnit<>(Frequency.class, time.pow(-1), 
          "Hz",   (short) 0));
-        HECTOPASCAL         = new ConventionalUnit<>(Pa, hecto,                   
          "hPa",  (short) 0);
-        KILOMETRES_PER_HOUR = new ConventionalUnit<>(mps, LinearConverter.scale(6,
100),     "km∕h", (short) 0);
-        CELSIUS             = new ConventionalUnit<>(K, LinearConverter.create(1, 273.15),
  "℃",    (short) 0);
+        KELVIN              = K;
+        PASCAL              = Pa;
+        METRES_PER_SECOND   = mps;
+        KILOGRAM            = add(Mass.class,      mass,                "kg",   (short) 0);
+        SQUARE_METRE        = add(Area.class,      area,                "m²",   (short)
0);
+        CUBIC_METRE         = add(Volume.class,    length.pow(3),       "m³",   (short)
0);
+        NEWTON              = add(Force.class,     force,               "N",    (short) 0);
+        JOULE               = add(Energy.class,    energy,              "J",    (short) 0);
+        WATT                = add(Power.class,     energy.divide(time), "W",    (short) 0);
+        HERTZ               = add(Frequency.class, time.pow(-1),        "Hz",   (short) 0);
+        HECTOPASCAL         = add(Pa, hecto,                            "hPa",  (short) 0);
+        KILOMETRES_PER_HOUR = add(mps, LinearConverter.scale(6, 100),   "km∕h", (short)
0);
+        CELSIUS             = add(K, LinearConverter.create(1, 273.15), "℃",    (short)
0);
         /*
          * All Unit<Dimensionless>
          */
-        PERCENT = new ConventionalUnit<>(one, centi, "%",   (short) 0);
-        PPM     = new ConventionalUnit<>(one, micro, "ppm", (short) 9202);
-        PSU     = new SystemUnit<>(Dimensionless.class, dimensionless, "psu",   (short)
0);
-        SIGMA   = new SystemUnit<>(Dimensionless.class, dimensionless, "sigma", (short)
0);
-        PIXEL   = new SystemUnit<>(Dimensionless.class, dimensionless, "px",    (short)
0);
-        UNITY   = add(one);  // Must be last in order to take precedence over all other units
associated to UnitDimension.NONE.
+        PERCENT = add(one, centi, "%",   (short) 0);
+        PPM     = add(one, micro, "ppm", (short) 9202);
+        PSU     = add(Dimensionless.class, dimensionless, "psu",   (short) 0);
+        SIGMA   = add(Dimensionless.class, dimensionless, "sigma", (short) 0);
+        PIXEL   = add(Dimensionless.class, dimensionless, "px",    (short) 0);
+        UNITY   = UnitRegistry.init(one);  // Must be last in order to take precedence over
all other units associated to UnitDimension.NONE.
+
+        initialized = true;
     }
 
     /**
      * Invoked by {@code Units} static class initializer for registering SI base and derived
units.
-     * We do not synchronize that method on the assumption that {@link #SYSTEM} map will
be fully
-     * populated in a single thread by the {@code Units} class initializer, then never modified.
+     * This method shall be invoked in a single thread by the {@code Units} class initializer
only.
      */
-    private static <Q extends Quantity<Q>> SystemUnit<Q> add(final SystemUnit<Q>
unit) {
-        SYSTEM.put(unit.dimension, unit);
-        if (SYSTEM.put(unit.quantity, unit) != null) {
-            throw new AssertionError();                 // Shall not map the same dimension
twice.
-        }
-        return unit;
+    private static <Q extends Quantity<Q>> SystemUnit<Q> add(Class<Q>
quantity, UnitDimension dimension, String symbol, short epsg) {
+        return UnitRegistry.init(new SystemUnit<>(quantity, dimension, symbol, epsg));
+    }
+
+    /**
+     * Invoked by {@code Units} static class initializer for registering SI conventional
units.
+     * This method shall be invoked in a single thread by the {@code Units} class initializer
only.
+     */
+    private static <Q extends Quantity<Q>> ConventionalUnit<Q> add(SystemUnit<Q>
target, UnitConverter toTarget, String symbol, short epsg) {
+        return UnitRegistry.init(new ConventionalUnit<>(target, toTarget, symbol, epsg));
+    }
+
+    /**
+     * Returns the system unit for the given dimension, or {@code null} if none.
+     * Note that this method can not distinguish the different kinds of dimensionless units.
+     * If the symbol or the quantity type is known, use {@link #get(String)} or {@link #get(Class)}
instead.
+     *
+     * <p><b>Implementation note:</b> this method must be defined in this
{@code Units} class
+     * in order to force a class initialization before use.</p>
+     */
+    static SystemUnit<?> get(final Dimension dim) {
+        return (SystemUnit<?>) UnitRegistry.get(dim);
+    }
+
+    /**
+     * Returns the system unit for the given quantity, or {@code null} if none.
+     *
+     * <p><b>Implementation note:</b> this method must be defined in this
{@code Units} class
+     * in order to force a class initialization before use.</p>
+     */
+    @SuppressWarnings("unchecked")
+    static <Q extends Quantity<Q>> SystemUnit<Q> get(final Class<Q>
type) {
+        return (SystemUnit<Q>) UnitRegistry.get(type);
+    }
+
+    /**
+     * Returns the system unit for the given symbol, or {@code null} if none.
+     *
+     * <p><b>Implementation note:</b> this method must be defined in this
{@code Units} class
+     * in order to force a class initialization before use.</p>
+     */
+    @SuppressWarnings("unchecked")
+    static Unit<?> get(final String symbol) {
+        return (Unit<?>) UnitRegistry.get(symbol);
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/SystemUnitTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/SystemUnitTest.java?rev=1765842&r1=1765841&r2=1765842&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/SystemUnitTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/SystemUnitTest.java
[UTF-8] Thu Oct 20 16:00:31 2016
@@ -99,7 +99,7 @@ public final strictfp class SystemUnitTe
      */
     @Test
     public void testGetBaseDimensions() {
-        assertNull("METRE",  Units.METRE .getBaseUnits());
+        assertNull("METRE",  Units.METRE .getBaseUnits());      // Null value as per JSR-363
specification.
         assertNull("SECOND", Units.SECOND.getBaseUnits());
         assertTrue("UNITY",  Units.UNITY .getBaseUnits().isEmpty());
 
@@ -168,7 +168,7 @@ public final strictfp class SystemUnitTe
         assertTrue (Units.RADIAN.isCompatible(Units.RADIAN));
         assertFalse(Units.RADIAN.isCompatible(Units.METRE ));
         assertFalse(Units.METRE .isCompatible(Units.RADIAN));
-        assertTrue (Units.UNITY .isCompatible(Units.RADIAN));   // Really 'true', not 'false'.
+        assertTrue (Units.UNITY .isCompatible(Units.RADIAN));   // Really true (not false)
as per JSR-363 specification.
         assertTrue (Units.RADIAN.isCompatible(Units.UNITY ));
     }
 
@@ -243,5 +243,21 @@ public final strictfp class SystemUnitTe
     @Test
     public void testAlternate() {
         assertSame(Units.RADIAN, Units.RADIAN.alternate("rad"));
+        assertSame(Units.PIXEL,  Units.PIXEL .alternate("px"));
+        assertSame(Units.PIXEL , Units.UNITY .alternate("px"));
+        try {
+            Units.UNITY.alternate("rad");
+            fail("Should not accept since “rad” is already used for a unit of another
quantity type (Angle).");
+        } catch (IllegalArgumentException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("rad"));
+        }
+        try {
+            Units.RADIAN.alternate("°");
+            fail("Should not accept since “°” is already used for a unit of another
type (ConventionalUnit).");
+        } catch (IllegalArgumentException e) {
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("°"));
+        }
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitDimensionTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitDimensionTest.java?rev=1765842&r1=1765841&r2=1765842&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitDimensionTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/UnitDimensionTest.java
[UTF-8] Thu Oct 20 16:00:31 2016
@@ -141,7 +141,7 @@ public final strictfp class UnitDimensio
      */
     @Test
     public void testGetBaseDimensions() {
-        assertNull("LENGTH",        LENGTH       .getBaseDimensions());
+        assertNull("LENGTH",        LENGTH       .getBaseDimensions());     // Null value
as per JSR-363 specification.
         assertNull("TIME",          TIME         .getBaseDimensions());
         assertTrue("DIMENSIONLESS", DIMENSIONLESS.getBaseDimensions().isEmpty());
         assertMapEquals(Collections.singletonMap(LENGTH, 3), VOLUME.getBaseDimensions());
@@ -166,7 +166,7 @@ public final strictfp class UnitDimensio
         verifyEqualsAndHashCode("Derived dimensions", false, Units.NEWTON, Units.JOULE);
         verifyEqualsAndHashCode("Dimensionsless",     true,  Units.UNITY,  Units.UNITY);
         verifyEqualsAndHashCode("Dimensionsless",     true,  Units.DEGREE, Units.DEGREE);
-        verifyEqualsAndHashCode("Dimensionsless",     true,  Units.UNITY,  Units.DEGREE);
   // Really true, not false.
+        verifyEqualsAndHashCode("Dimensionsless",     true,  Units.UNITY,  Units.DEGREE);
   // Really true (not false) as per JSR-363 specification.
         verifyEqualsAndHashCode("Mixed types",        false, Units.METRE,  Units.UNITY);
         verifyEqualsAndHashCode("Mixed types",        false, Units.METRE,  Units.NEWTON);
     }




Mime
View raw message