You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2016/10/20 16:00:31 UTC
svn commit: r1765842 - in /sis/branches/JDK8/core/sis-utility/src:
main/java/org/apache/sis/measure/ test/java/org/apache/sis/measure/
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(\u2026),
* 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. (\u2026snip\u2026) 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, "\u212a", (short) 0);
- final SystemUnit<Speed> mps = new SystemUnit<>(Speed.class, speed, "m\u2215s", (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, "\u212a", (short) 0);
+ final SystemUnit<Speed> mps = add(Speed.class, speed, "m\u2215s", (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), "\u2032", (short) 9103);
- ARC_SECOND = new ConventionalUnit<>(rad, LinearConverter.create(Math.PI / (180*60*60), 0), "\u2033", (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), "\u2032", (short) 9103);
+ ARC_SECOND = add(rad, LinearConverter.scale(Math.PI, 180*60*60), "\u2033", (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\u2215h", (short) 0);
- CELSIUS = new ConventionalUnit<>(K, LinearConverter.create(1, 273.15), "\u2103", (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\u2215h", (short) 0);
+ CELSIUS = add(K, LinearConverter.create(1, 273.15), "\u2103", (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 \u201crad\u201d 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 \u201c�\u201d 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);
}