sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1790733 - in /sis/branches/JDK8/core/sis-utility/src: main/java/org/apache/sis/measure/ test/java/org/apache/sis/measure/ test/java/org/apache/sis/test/suite/
Date Sun, 09 Apr 2017 00:08:14 GMT
Author: desruisseaux
Date: Sun Apr  9 00:08:13 2017
New Revision: 1790733

URL: http://svn.apache.org/viewvc?rev=1790733&view=rev
Log:
JSR-363: add Quantity<?> support.

Added:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Scalar.java   (with props)
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/ScalarFactory.java   (with props)
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/ScalarFallback.java   (with props)
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/ScalarTest.java   (with props)
Modified:
    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/UnitServices.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/test/suite/UtilityTestSuite.java

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Scalar.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Scalar.java?rev=1790733&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Scalar.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Scalar.java [UTF-8] Sun Apr  9 00:08:13 2017
@@ -0,0 +1,502 @@
+/*
+ * 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.lang.reflect.Proxy;
+import javax.measure.Unit;
+import javax.measure.Quantity;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.StringBuilders;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * A quantity representable by position on a scale or line, having only magnitude.
+ * {@code Scalar} are represented by a single floating point number and a unit of measurement.
+ * This is the base class for some commonly used quantity types like length, angle and dimensionless quantities.
+ * Instances of this class are unmodifiable.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ *
+ * @param <Q>  the concrete subtype.
+ *
+ * @since 0.8
+ * @module
+ */
+abstract class Scalar<Q extends Quantity<Q>> extends Number implements Quantity<Q>, Comparable<Q> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -381805117700594712L;
+
+    /**
+     * The numerical value of this quantity.
+     */
+    private final double value;
+
+    /**
+     * The unit of measurement associated to the value.
+     */
+    private final Unit<Q> unit;
+
+    /**
+     * Creates a new scalar for the given value.
+     */
+    Scalar(final double value, final Unit<Q> unit) {
+        this.value = value;
+        this.unit  = unit;
+    }
+
+    /**
+     * Creates a quantity of for the given value.
+     */
+    static <Q extends Quantity<Q>> Quantity<Q> factory(final double value, final Unit<Q> unit) {
+        final Unit<Q> system = unit.getSystemUnit();
+        if (system instanceof SystemUnit<?>) {
+            final ScalarFactory<Q> factory = ((SystemUnit<Q>) system).factory;
+            if (factory != null) {
+                return factory.create(value, unit);
+            } else {
+                return ScalarFallback.factory(value, unit, ((SystemUnit<Q>) system).quantity);
+            }
+        } else {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedImplementation_1, unit.getClass()));
+        }
+    }
+
+    /**
+     * Creates a new quantity of same type than this quantity but with a different value and/or unit.
+     * This method performs the same work than {@link #factory(double, Unit)}, but without the need
+     * to check for the Apache SIS specific {@link SystemUnit} implementation.
+     *
+     * @see #factory(double, Unit)
+     */
+    abstract Quantity<Q> create(double newValue, Unit<Q> newUnit);
+
+    /**
+     * Returns a quantity quantity of same type than this quantity but with a different value and/or unit.
+     * If the new value and unit are the same than this quantity, then {@code this} instance is returned.
+     * Positive and negative zeros are considered two different values.
+     */
+    private Quantity<?> of(final double newValue, final Unit<?> newUnit) {
+        if (unit != newUnit || Double.doubleToRawLongBits(value) != Double.doubleToRawLongBits(newValue)) {
+            return factory(newValue, newUnit);
+        }
+        return this;
+    }
+
+    /**
+     * Returns a quantity with the same units than this quantity. If the new value is the same
+     * than current value, then {@code this} instance is returned. Positive and negative zeros
+     * are considered two different values.
+     */
+    private Quantity<Q> of(final double newValue) {
+        if (Double.doubleToRawLongBits(value) != Double.doubleToRawLongBits(newValue)) {
+            return create(newValue, unit);
+        }
+        return this;
+    }
+
+    /**
+     * Returns the unit of measurement specified at construction time.
+     */
+    @Override
+    public final Unit<Q> getUnit() {
+        return unit;
+    }
+
+    /**
+     * Returns the value as a number, which is this instance itself.
+     */
+    @Override
+    public final Number getValue() {
+        return this;
+    }
+
+    /**
+     * Returns the value specified at construction time.
+     */
+    @Override
+    public final double doubleValue() {
+        return value;
+    }
+
+    /**
+     * Returns the value casted to a single-precision floating point number.
+     */
+    @Override
+    public final float floatValue() {
+        return (float) value;
+    }
+
+    /**
+     * Returns the value rounded to nearest integer. {@link Double#NaN} are casted to 0 and values out of
+     * {@code long} range are clamped to minimal or maximal representable numbers of {@code long} type.
+     */
+    @Override
+    public final long longValue() {
+        return Math.round(value);
+    }
+
+    /**
+     * Returns the value rounded to nearest integer. {@link Double#NaN} are casted to 0 and values out of
+     * {@code int} range are clamped to minimal or maximal representable numbers of {@code int} type.
+     */
+    @Override
+    public final int intValue() {
+        return (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, longValue()));
+    }
+
+    /**
+     * Returns the value rounded to nearest integer. {@link Double#NaN} are casted to 0 and values out of
+     * {@code short} range are clamped to minimal or maximal representable numbers of {@code short} type.
+     */
+    @Override
+    public final short shortValue() {
+        return (short) Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, longValue()));
+    }
+
+    /**
+     * Returns the value rounded to nearest integer. {@link Double#NaN} are casted to 0 and values out of
+     * {@code byte} range are clamped to minimal or maximal representable numbers of {@code byte} type.
+     */
+    @Override
+    public final byte byteValue() {
+        return (byte) Math.max(Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, longValue()));
+    }
+
+    /**
+     * Returns the value of the given quantity converted to the same units of measurement than this quantity.
+     */
+    private double doubleValue(final Quantity<Q> other) {
+        double otherValue = other.getValue().doubleValue();
+        final Unit<Q> otherUnit = other.getUnit();
+        if (otherUnit != unit) {
+            otherValue = otherUnit.getConverterTo(unit).convert(otherValue);
+        }
+        return otherValue;
+    }
+
+    /**
+     * Compares the numerical value of this quantity with the value of another quantity of the same type.
+     * The comparison is performed with {@code double} precision in the units of measurement of this quantity.
+     */
+    @Override
+    public final int compareTo(final Q other) {
+        return Double.compare(value, doubleValue(other));
+    }
+
+    /**
+     * Converts this quantity to another unit of measurement.
+     */
+    @Override
+    public final Quantity<Q> to(final Unit<Q> newUnit) {
+        if (newUnit == unit) {
+            return this;
+        }
+        ArgumentChecks.ensureNonNull("unit", newUnit);      // "unit" is the parameter name used in public API.
+        return create(unit.getConverterTo(newUnit).convert(value), newUnit);
+    }
+
+    /**
+     * Returns the sum of this {@code Quantity} with another quantity.
+     * The result is given in units of this quantity.
+     */
+    @Override
+    public final Quantity<Q> add(final Quantity<Q> other) {
+        return of(value + doubleValue(other));
+    }
+
+    /**
+     * Returns the difference between this {@code Quantity} and the given quantity.
+     * The result is given in units of this quantity.
+     */
+    @Override
+    public final Quantity<Q> subtract(final Quantity<Q> other) {
+        return of(value - doubleValue(other));
+    }
+
+    /**
+     * Returns this quantity scaled by the given number.
+     */
+    @Override
+    public final Quantity<Q> multiply(final Number scale) {
+        return of(value * scale.doubleValue());
+    }
+
+    /**
+     * Returns this quantity divided by the given number.
+     */
+    @Override
+    public final Quantity<Q> divide(final Number divisor) {
+        return of(value / divisor.doubleValue());
+    }
+
+    /**
+     * Returns this quantity multiplied by the given quantity.
+     */
+    @Override
+    public final Quantity<?> multiply(final Quantity<?> other) {
+        return of(value * other.getValue().doubleValue(), unit.multiply(other.getUnit()));
+    }
+
+    /**
+     * Returns this quantity divided by the given quantity.
+     */
+    @Override
+    public final Quantity<?> divide(final Quantity<?> other) {
+        return of(value / other.getValue().doubleValue(), unit.divide(other.getUnit()));
+    }
+
+    /**
+     * Returns the reciprocal of this quantity.
+     */
+    @Override
+    public final Quantity<?> inverse() {
+        return of(1 / value, unit.inverse());
+    }
+
+    /**
+     * Ensures that this quantity is of the given type.
+     */
+    @Override
+    public final <T extends Quantity<T>> Quantity<T> asType(final Class<T> type) throws ClassCastException {
+        return type.cast(this);
+    }
+
+    /**
+     * Returns {@code true} if the given object is another {@code Scalar} with the same value and same unit
+     * of measurement.
+     */
+    @Override
+    public final boolean equals(Object other) {
+        if (!(other instanceof Scalar<?>)) {
+            if (!(other instanceof Proxy)) {
+                return false;
+            }
+            try {
+                other = Proxy.getInvocationHandler(other);
+            } catch (IllegalArgumentException | SecurityException e) {
+                return false;
+            }
+            if (!(other instanceof Scalar<?>)) {
+                return false;
+            }
+        }
+        final Scalar<?> that = (Scalar<?>) other;
+        return Double.doubleToLongBits(value) == Double.doubleToLongBits(that.value) && unit.equals(that.unit);
+    }
+
+    /**
+     * Returns a hash code value for this quantity.
+     */
+    @Override
+    public final int hashCode() {
+        return Double.hashCode(value) ^ unit.hashCode();
+    }
+
+    /**
+     * Returns the quantity value followed by its units of measurement.
+     */
+    @Override
+    public final String toString() {
+        final StringBuilder buffer = new StringBuilder().append(value);
+        StringBuilders.trimFractionalPart(buffer);
+        final String symbol = unit.toString();
+        if (symbol != null && !symbol.isEmpty()) {
+            buffer.append(' ').append(symbol);
+        }
+        return buffer.toString();
+    }
+
+
+    /*
+     * Following inner classes are straightforward implementations for some commonly used quantity types.
+     * We do not need to provide an implementation for every types; we will fallback on a proxy mechanism
+     * as a fallback for less frequently used types.
+     */
+
+
+    static final class Dimensionless extends Scalar<javax.measure.quantity.Dimensionless>
+                                     implements     javax.measure.quantity.Dimensionless
+    {
+        private static final long serialVersionUID = -7783945219314403648L;
+        Dimensionless(double value, Unit<javax.measure.quantity.Dimensionless> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Dimensionless> create(double value, Unit<javax.measure.quantity.Dimensionless> unit) {
+            return new Dimensionless(value, unit);
+        }
+    }
+
+    static final class Angle extends Scalar<javax.measure.quantity.Angle>
+                             implements     javax.measure.quantity.Angle
+    {
+        private static final long serialVersionUID = -1706116845342397826L;
+        Angle(double value, Unit<javax.measure.quantity.Angle> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Angle> create(double value, Unit<javax.measure.quantity.Angle> unit) {
+            return new Angle(value, unit);
+        }
+    }
+
+    static final class Length extends Scalar<javax.measure.quantity.Length>
+                              implements     javax.measure.quantity.Length
+    {
+        private static final long serialVersionUID = 6664029554501181657L;
+        Length(double value, Unit<javax.measure.quantity.Length> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Length> create(double value, Unit<javax.measure.quantity.Length> unit) {
+            return new Length(value, unit);
+        }
+    }
+
+    static final class Area extends Scalar<javax.measure.quantity.Area>
+                            implements     javax.measure.quantity.Area
+    {
+        private static final long serialVersionUID = -9127932093170175175L;
+        Area(double value, Unit<javax.measure.quantity.Area> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Area> create(double value, Unit<javax.measure.quantity.Area> unit) {
+            return new Area(value, unit);
+        }
+    }
+
+    static final class Volume extends Scalar<javax.measure.quantity.Volume>
+                              implements     javax.measure.quantity.Volume
+    {
+        private static final long serialVersionUID = -1505528008598251420L;
+        Volume(double value, Unit<javax.measure.quantity.Volume> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Volume> create(double value, Unit<javax.measure.quantity.Volume> unit) {
+            return new Volume(value, unit);
+        }
+    }
+
+    static final class Time extends Scalar<javax.measure.quantity.Time>
+                            implements     javax.measure.quantity.Time
+    {
+        private static final long serialVersionUID = 3992130757485565027L;
+        Time(double value, Unit<javax.measure.quantity.Time> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Time> create(double value, Unit<javax.measure.quantity.Time> unit) {
+            return new Time(value, unit);
+        }
+    }
+
+    static final class Frequency extends Scalar<javax.measure.quantity.Frequency>
+                                 implements     javax.measure.quantity.Frequency
+    {
+        private static final long serialVersionUID = -2038564695278895642L;
+        Frequency(double value, Unit<javax.measure.quantity.Frequency> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Frequency> create(double value, Unit<javax.measure.quantity.Frequency> unit) {
+            return new Frequency(value, unit);
+        }
+    }
+
+    static final class Speed extends Scalar<javax.measure.quantity.Speed>
+                             implements     javax.measure.quantity.Speed
+    {
+        private static final long serialVersionUID = 4086187563299428546L;
+        Speed(double value, Unit<javax.measure.quantity.Speed> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Speed> create(double value, Unit<javax.measure.quantity.Speed> unit) {
+            return new Speed(value, unit);
+        }
+    }
+
+    static final class Mass extends Scalar<javax.measure.quantity.Mass>
+                            implements     javax.measure.quantity.Mass
+    {
+        private static final long serialVersionUID = -3348515590324141647L;
+        Mass(double value, Unit<javax.measure.quantity.Mass> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Mass> create(double value, Unit<javax.measure.quantity.Mass> unit) {
+            return new Mass(value, unit);
+        }
+    }
+
+    static final class Force extends Scalar<javax.measure.quantity.Force>
+                             implements     javax.measure.quantity.Force
+    {
+        private static final long serialVersionUID = -4988289861436247522L;
+        Force(double value, Unit<javax.measure.quantity.Force> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Force> create(double value, Unit<javax.measure.quantity.Force> unit) {
+            return new Force(value, unit);
+        }
+    }
+
+    static final class Energy extends Scalar<javax.measure.quantity.Energy>
+                              implements     javax.measure.quantity.Energy
+    {
+        private static final long serialVersionUID = 857370990868536857L;
+        Energy(double value, Unit<javax.measure.quantity.Energy> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Energy> create(double value, Unit<javax.measure.quantity.Energy> unit) {
+            return new Energy(value, unit);
+        }
+    }
+
+    static final class Power extends Scalar<javax.measure.quantity.Power>
+                             implements     javax.measure.quantity.Power
+    {
+        private static final long serialVersionUID = -5751533351918725110L;
+        Power(double value, Unit<javax.measure.quantity.Power> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Power> create(double value, Unit<javax.measure.quantity.Power> unit) {
+            return new Power(value, unit);
+        }
+    }
+
+    static final class Temperature extends Scalar<javax.measure.quantity.Temperature>
+                                   implements     javax.measure.quantity.Temperature
+    {
+        private static final long serialVersionUID = -6391507887931973739L;
+        Temperature(double value, Unit<javax.measure.quantity.Temperature> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Temperature> create(double value, Unit<javax.measure.quantity.Temperature> unit) {
+            return new Temperature(value, unit);
+        }
+    }
+
+    static final class Pressure extends Scalar<javax.measure.quantity.Pressure>
+                                implements     javax.measure.quantity.Pressure
+    {
+        private static final long serialVersionUID = -8647834252032382587L;
+        Pressure(double value, Unit<javax.measure.quantity.Pressure> unit) {super(value, unit);}
+
+        @Override
+        Quantity<javax.measure.quantity.Pressure> create(double value, Unit<javax.measure.quantity.Pressure> unit) {
+            return new Pressure(value, unit);
+        }
+    }
+}

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

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

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/ScalarFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/ScalarFactory.java?rev=1790733&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/ScalarFactory.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/ScalarFactory.java [UTF-8] Sun Apr  9 00:08:13 2017
@@ -0,0 +1,43 @@
+/*
+ * 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 javax.measure.Unit;
+import javax.measure.Quantity;
+
+
+/**
+ * Creates quantities for the given value and unit of measurement.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ *
+ * @param <Q>  the type of quantities created by this factory.
+ *
+ * @since 0.8
+ * @module
+ */
+interface ScalarFactory<Q extends Quantity<Q>> {
+    /**
+     * Creates a new quantity for the given value and unit of measurement.
+     *
+     * @param  value  the value of the quantity to create.
+     * @param  unit   the unit of measurement associated to the given value.
+     * @return a quantity with the given value and unit of measurement.
+     */
+    Quantity<Q> create(double value, Unit<Q> unit);
+}

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

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

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/ScalarFallback.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/ScalarFallback.java?rev=1790733&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/ScalarFallback.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/ScalarFallback.java [UTF-8] Sun Apr  9 00:08:13 2017
@@ -0,0 +1,87 @@
+/*
+ * 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.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import javax.measure.Quantity;
+import javax.measure.Unit;
+
+
+/**
+ * Fallback used when no {@link Scalar} implementation is available for a given quantity type.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ * @since   0.8
+ * @module
+ */
+@SuppressWarnings("serial")
+final class ScalarFallback<Q extends Quantity<Q>> extends Scalar<Q> implements InvocationHandler {
+    /**
+     * The constructor for new proxy instances. Stored for allowing {@link #create(double, Unit)}
+     * to be implemented more efficiently than invoking {@link Proxy#newProxyInstance}.
+     */
+    private final Constructor<? extends Q> constructor;
+
+    /**
+     * Creates a new scalar for the given value and unit of measurement.
+     */
+    private ScalarFallback(final double value, final Unit<Q> unit, final Constructor<? extends Q> constructor) {
+        super(value, unit);
+        this.constructor = constructor;
+    }
+
+    /**
+     * Creates a new quantity of the same type than this quantity but a different value and/or unit.
+     */
+    @Override
+    Quantity<Q> create(final double newValue, final Unit<Q> newUnit) {
+        try {
+            return constructor.newInstance(new ScalarFallback<>(newValue, newUnit, constructor));
+        } catch (ReflectiveOperationException e) {
+            throw new UnsupportedOperationException(e);                 // Should never happen.
+        }
+    }
+
+    /**
+     * Creates a new {@link ScalarFallback} instance implementing the given quantity type.
+     */
+    @SuppressWarnings("unchecked")
+    static <Q extends Quantity<Q>> Quantity<Q> factory(final double value, final Unit<Q> unit, final Class<Q> type) {
+        final Class<?> pc = Proxy.getProxyClass(Scalar.class.getClassLoader(), new Class<?>[] {type});
+        final Constructor<? extends Q> constructor;
+        try {
+            constructor = (Constructor<? extends Q>) pc.getConstructor(InvocationHandler.class);
+            return constructor.newInstance(new ScalarFallback<>(value, unit, constructor));
+        } catch (ReflectiveOperationException e) {
+            throw new UnsupportedOperationException(e);                 // Should never happen.
+        }
+    }
+
+    /**
+     * Invoked when a method of the {@link Quantity} interface is invoked. Delegates to the same
+     * method of the parent {@link Scalar} class. This works not only for the quantity methods,
+     * but also for {@link #toString()}, {@link #hashCode()} and {@link #equals(Object)}.
+     */
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] args) throws ReflectiveOperationException {
+        return method.invoke(this, args);
+    }
+}

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

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

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=1790733&r1=1790732&r2=1790733&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] Sun Apr  9 00:08:13 2017
@@ -26,11 +26,13 @@ import javax.measure.Dimension;
 import javax.measure.UnitConverter;
 import javax.measure.UnconvertibleException;
 import javax.measure.IncommensurableException;
+import javax.measure.spi.QuantityFactory;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ObjectConverters;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.converter.SurjectiveConverter;
+import org.apache.sis.math.DecimalFunctions;
 import org.apache.sis.math.Fraction;
 
 
@@ -48,7 +50,7 @@ import org.apache.sis.math.Fraction;
  * @since 0.8
  * @module
  */
-final class SystemUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> {
+final class SystemUnit<Q extends Quantity<Q>> extends AbstractUnit<Q> implements QuantityFactory<Q> {
     /**
      * For cross-version compatibility.
      */
@@ -65,6 +67,13 @@ final class SystemUnit<Q extends Quantit
     final UnitDimension dimension;
 
     /**
+     * The factory to use for creating quantities, or {@code null} if none.
+     * This field does not need to be serialized because {@link AbstractUnit#readResolve()}
+     * replaces deserialized instances by corresponding {@link Units} hard-coded instances.
+     */
+    final transient ScalarFactory<Q> factory;
+
+    /**
      * Units for the same quantity but with scale factors that are not the SI one.
      * This is initialized by {@link Units} only and shall not change anymore after.
      * All units in this array shall use an instance of {@link LinearConverter}.
@@ -81,11 +90,15 @@ final class SystemUnit<Q extends Quantit
      * @param  symbol     the unit symbol, or {@code null} if this unit has no specific symbol.
      * @param  scope      {@link UnitRegistry#SI}, {@link UnitRegistry#ACCEPTED}, other constants or 0 if unknown.
      * @param  epsg       the EPSG code, or 0 if this unit has no EPSG code.
+     * @param  factory    the factory to use for creating quantities, or {@code null} if none.
      */
-    SystemUnit(final Class<Q> quantity, final UnitDimension dimension, final String symbol, final byte scope, final short epsg) {
+    SystemUnit(final Class<Q> quantity, final UnitDimension dimension, final String symbol,
+            final byte scope, final short epsg, final ScalarFactory<Q> factory)
+    {
         super(symbol, scope, epsg);
         this.quantity  = quantity;
         this.dimension = dimension;
+        this.factory   = factory;
     }
 
     /**
@@ -98,7 +111,7 @@ final class SystemUnit<Q extends Quantit
         }
         SystemUnit<?> result = Units.get(dim);
         if (result == null) {
-            result = new SystemUnit<>(null, dim, null, (byte) 0, (short) 0);
+            result = new SystemUnit<>(null, dim, null, (byte) 0, (short) 0, null);
         }
         return result;
     }
@@ -240,7 +253,7 @@ final class SystemUnit<Q extends Quantit
          */
         SystemUnit<T> unit = Units.get(type);
         if (unit == null) {
-            unit = new SystemUnit<>(type, dimension, null, (byte) 0, (short) 0);       // Intentionally no symbol.
+            unit = new SystemUnit<>(type, dimension, null, (byte) 0, (short) 0, null);  // Intentionally no symbol.
         }
         if (!dimension.equals(unit.dimension)) {
             throw new ClassCastException(Errors.format(Errors.Keys.IncompatibleUnitDimension_5, new Object[] {
@@ -357,7 +370,7 @@ final class SystemUnit<Q extends Quantit
         if (symbol.equals(getSymbol())) {
             return this;
         }
-        final SystemUnit<Q> alt = new SystemUnit<>(quantity, dimension, symbol, (byte) 0, (short) 0);
+        final SystemUnit<Q> alt = new SystemUnit<>(quantity, dimension, symbol, (byte) 0, (short) 0, factory);
         if (quantity != null) {
             /*
              * Use the cache only if this unit has a non-null quantity type. Do not use the cache even
@@ -508,4 +521,22 @@ final class SystemUnit<Q extends Quantit
     public int hashCode() {
         return super.hashCode() + 37 * dimension.hashCode();
     }
+
+    /**
+     * Creates a quantity for the given value and unit of measurement.
+     */
+    @Override
+    public Quantity<Q> create(final Number value, final Unit<Q> unit) {
+        final double v;
+        if (value instanceof Float) {
+            v = DecimalFunctions.floatToDouble(value.floatValue());
+        } else {
+            v = value.doubleValue();
+        }
+        if (factory != null) {
+            return factory.create(v, unit);
+        } else {
+            return ScalarFallback.factory(v, unit, quantity);
+        }
+    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitServices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitServices.java?rev=1790733&r1=1790732&r2=1790733&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitServices.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/UnitServices.java [UTF-8] Sun Apr  9 00:08:13 2017
@@ -221,7 +221,6 @@ public class UnitServices extends Servic
 
     /**
      * Return a factory for the given {@code Quantity} type, or {@code null} if none.
-     * Current implementation returns {@code null}.
      *
      * @param  <Q>   compile-time value of the {@code type} argument.
      * @param  type  type of the desired the quantity.
@@ -229,6 +228,6 @@ public class UnitServices extends Servic
      */
     @Override
     public <Q extends Quantity<Q>> QuantityFactory<Q> getQuantityFactory(final Class<Q> type) {
-        return null;
+        return Units.get(type);
     }
 }

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=1790733&r1=1790732&r2=1790733&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] Sun Apr  9 00:08:13 2017
@@ -1061,14 +1061,14 @@ 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   = add(Length.class,        length,        "m",   UnitRegistry.SI, Constants.EPSG_METRE);
-        final SystemUnit<Area>          m2  = add(Area.class,          area,          "m²",  UnitRegistry.SI, (short) 0);
-        final SystemUnit<Time>          s   = add(Time.class,          time,          "s",   UnitRegistry.SI, (short) 1040);
-        final SystemUnit<Temperature>   K   = add(Temperature.class,   temperature,   "K",   UnitRegistry.SI, (short) 0);
-        final SystemUnit<Speed>         mps = add(Speed.class,         speed,         "m∕s", UnitRegistry.SI, (short) 1026);
-        final SystemUnit<Pressure>      Pa  = add(Pressure.class,      pressure,      "Pa",  UnitRegistry.SI, (short) 0);
-        final SystemUnit<Angle>         rad = add(Angle.class,         dimensionless, "rad", UnitRegistry.SI, (short) 9101);
-        final SystemUnit<Dimensionless> one = add(Dimensionless.class, dimensionless, "",    UnitRegistry.SI, (short) 9201);
+        final SystemUnit<Length>        m   = add(Length.class,        Scalar.Length::new,        length,        "m",   UnitRegistry.SI, Constants.EPSG_METRE);
+        final SystemUnit<Area>          m2  = add(Area.class,          Scalar.Area::new,          area,          "m²",  UnitRegistry.SI, (short) 0);
+        final SystemUnit<Time>          s   = add(Time.class,          Scalar.Time::new,          time,          "s",   UnitRegistry.SI, (short) 1040);
+        final SystemUnit<Temperature>   K   = add(Temperature.class,   Scalar.Temperature::new,   temperature,   "K",   UnitRegistry.SI, (short) 0);
+        final SystemUnit<Speed>         mps = add(Speed.class,         Scalar.Speed::new,         speed,         "m∕s", UnitRegistry.SI, (short) 1026);
+        final SystemUnit<Pressure>      Pa  = add(Pressure.class,      Scalar.Pressure::new,      pressure,      "Pa",  UnitRegistry.SI, (short) 0);
+        final SystemUnit<Angle>         rad = add(Angle.class,         Scalar.Angle::new,         dimensionless, "rad", UnitRegistry.SI, (short) 9101);
+        final SystemUnit<Dimensionless> one = add(Dimensionless.class, Scalar.Dimensionless::new, dimensionless, "",    UnitRegistry.SI, (short) 9201);
         /*
          * All SI prefix to be used below, with additional converters to be used more than once.
          */
@@ -1144,39 +1144,39 @@ public final class Units extends Static
         /*
          * Electricity and magnetism.
          */
-        AMPERE  = add(ElectricCurrent.class,     current,                      "A",  UnitRegistry.SI, (short) 0);
-        COULOMB = add(ElectricCharge.class,      charge,                       "C",  UnitRegistry.SI, (short) 0);
-        VOLT    = add(ElectricPotential.class,   potential,                    "V",  UnitRegistry.SI, (short) 0);
-        FARAD   = add(ElectricCapacitance.class, charge.divide(potential),     "F",  UnitRegistry.SI, (short) 0);
-        SIEMENS = add(ElectricConductance.class, current.divide(potential),    "S",  UnitRegistry.SI, (short) 0);
-        OHM     = add(ElectricResistance.class,  potential.divide(current),    "Ω",  UnitRegistry.SI, (short) 0);
-        WEBER   = add(MagneticFlux.class,        magneticFlux,                 "Wb", UnitRegistry.SI, (short) 0);
-        TESLA   = add(MagneticFluxDensity.class, magneticFlux.divide(area),    "T",  UnitRegistry.SI, (short) 0);
-        HENRY   = add(ElectricInductance.class,  magneticFlux.divide(current), "H",  UnitRegistry.SI, (short) 0);
+        AMPERE  = add(ElectricCurrent.class,     null, current,                      "A",  UnitRegistry.SI, (short) 0);
+        COULOMB = add(ElectricCharge.class,      null, charge,                       "C",  UnitRegistry.SI, (short) 0);
+        VOLT    = add(ElectricPotential.class,   null, potential,                    "V",  UnitRegistry.SI, (short) 0);
+        FARAD   = add(ElectricCapacitance.class, null, charge.divide(potential),     "F",  UnitRegistry.SI, (short) 0);
+        SIEMENS = add(ElectricConductance.class, null, current.divide(potential),    "S",  UnitRegistry.SI, (short) 0);
+        OHM     = add(ElectricResistance.class,  null, potential.divide(current),    "Ω",  UnitRegistry.SI, (short) 0);
+        WEBER   = add(MagneticFlux.class,        null, magneticFlux,                 "Wb", UnitRegistry.SI, (short) 0);
+        TESLA   = add(MagneticFluxDensity.class, null, magneticFlux.divide(area),    "T",  UnitRegistry.SI, (short) 0);
+        HENRY   = add(ElectricInductance.class,  null, magneticFlux.divide(current), "H",  UnitRegistry.SI, (short) 0);
         /*
          * Other units.
          */
         SQUARE_METRE = m2;
-        HECTARE      = add(m2,  ten4,                                      "ha",  UnitRegistry.ACCEPTED, (short) 0);
-        CUBIC_METRE  = add(Volume.class,            length.pow(3),         "m³",  UnitRegistry.SI,       (short) 0);
-        HERTZ        = add(Frequency.class,         time.pow(-1),          "Hz",  UnitRegistry.SI,       (short) 0);
-        KILOGRAM     = add(Mass.class,              mass,                  "kg",  UnitRegistry.SI,       (short) 0);
-        NEWTON       = add(Force.class,             force,                 "N",   UnitRegistry.SI,       (short) 0);
-        JOULE        = add(Energy.class,            energy,                "J",   UnitRegistry.SI,       (short) 0);
-        WATT         = add(Power.class,             power,                 "W",   UnitRegistry.SI,       (short) 0);
-        LUX          = add(Illuminance.class,       luminous.divide(area), "lx",  UnitRegistry.SI,       (short) 0);
-        LUMEN        = add(LuminousFlux.class,      luminous,              "lm",  UnitRegistry.SI,       (short) 0);
-        CANDELA      = add(LuminousIntensity.class, luminous,              "cd",  UnitRegistry.SI,       (short) 0);    // Must be after Lumen.
-        MOLE         = add(AmountOfSubstance.class, amount,                "mol", UnitRegistry.SI,       (short) 0);
-        STERADIAN    = add(SolidAngle.class,        dimensionless,         "sr",  UnitRegistry.SI,       (short) 0);
+        HECTARE      = add(m2,  ten4,                                                             "ha",  UnitRegistry.ACCEPTED, (short) 0);
+        CUBIC_METRE  = add(Volume.class,            Scalar.Volume::new,    length.pow(3),         "m³",  UnitRegistry.SI,       (short) 0);
+        HERTZ        = add(Frequency.class,         Scalar.Frequency::new, time.pow(-1),          "Hz",  UnitRegistry.SI,       (short) 0);
+        KILOGRAM     = add(Mass.class,              Scalar.Mass::new,      mass,                  "kg",  UnitRegistry.SI,       (short) 0);
+        NEWTON       = add(Force.class,             Scalar.Force::new,     force,                 "N",   UnitRegistry.SI,       (short) 0);
+        JOULE        = add(Energy.class,            Scalar.Energy::new,    energy,                "J",   UnitRegistry.SI,       (short) 0);
+        WATT         = add(Power.class,             Scalar.Power::new,     power,                 "W",   UnitRegistry.SI,       (short) 0);
+        LUX          = add(Illuminance.class,       null,                  luminous.divide(area), "lx",  UnitRegistry.SI,       (short) 0);
+        LUMEN        = add(LuminousFlux.class,      null,                  luminous,              "lm",  UnitRegistry.SI,       (short) 0);
+        CANDELA      = add(LuminousIntensity.class, null,                  luminous,              "cd",  UnitRegistry.SI,       (short) 0);    // Must be after Lumen.
+        MOLE         = add(AmountOfSubstance.class, null,                  amount,                "mol", UnitRegistry.SI,       (short) 0);
+        STERADIAN    = add(SolidAngle.class,        null,                  dimensionless,         "sr",  UnitRegistry.SI,       (short) 0);
         /*
          * All Unit<Dimensionless>.
          */
-        PERCENT = add(one, centi,                         "%",     UnitRegistry.OTHER, (short) 0);
-        PPM     = add(one, micro,                         "ppm",   UnitRegistry.OTHER, (short) 9202);
-        PSU     = add(Dimensionless.class, dimensionless, "psu",   UnitRegistry.OTHER, (short) 0);
-        SIGMA   = add(Dimensionless.class, dimensionless, "sigma", UnitRegistry.OTHER, (short) 0);
-        PIXEL   = add(Dimensionless.class, dimensionless, "px",    UnitRegistry.OTHER, (short) 0);
+        PERCENT = add(one, centi,                                                    "%",     UnitRegistry.OTHER, (short) 0);
+        PPM     = add(one, micro,                                                    "ppm",   UnitRegistry.OTHER, (short) 9202);
+        PSU     = add(Dimensionless.class, Scalar.Dimensionless::new, dimensionless, "psu",   UnitRegistry.OTHER, (short) 0);
+        SIGMA   = add(Dimensionless.class, Scalar.Dimensionless::new, dimensionless, "sigma", UnitRegistry.OTHER, (short) 0);
+        PIXEL   = add(Dimensionless.class, Scalar.Dimensionless::new, dimensionless, "px",    UnitRegistry.OTHER, (short) 0);
         UNITY   = UnitRegistry.init(one);  // Must be last in order to take precedence over all other units associated to UnitDimension.NONE.
 
         UnitRegistry.alias(UNITY,       Short.valueOf((short) 9203));
@@ -1198,8 +1198,10 @@ public final class Units extends Static
      * Invoked by {@code 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.
      */
-    private static <Q extends Quantity<Q>> SystemUnit<Q> add(Class<Q> quantity, UnitDimension dimension, String symbol, byte scope, short epsg) {
-        return UnitRegistry.init(new SystemUnit<>(quantity, dimension, symbol, scope, epsg));
+    private static <Q extends Quantity<Q>> SystemUnit<Q> add(Class<Q> quantity, ScalarFactory<Q> factory,
+            UnitDimension dimension, String symbol, byte scope, short epsg)
+    {
+        return UnitRegistry.init(new SystemUnit<>(quantity, dimension, symbol, scope, epsg, factory));
     }
 
     /**

Added: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/ScalarTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/ScalarTest.java?rev=1790733&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/ScalarTest.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/measure/ScalarTest.java [UTF-8] Sun Apr  9 00:08:13 2017
@@ -0,0 +1,174 @@
+/*
+ * 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.lang.reflect.Proxy;
+import javax.measure.Quantity;
+import javax.measure.quantity.Area;
+import javax.measure.quantity.Frequency;
+import javax.measure.quantity.Length;
+import javax.measure.quantity.Speed;
+import javax.measure.quantity.Time;
+import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.apache.sis.test.Assert.*;
+
+
+/**
+ * Tests {@link Scalar} and its subclasses.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ * @since   0.8
+ * @module
+ */
+@DependsOn(UnitsTest.class)
+public final strictfp class ScalarTest extends TestCase {
+    /**
+     * Tests {@link Scalar#add(Quantity)} and {@link Scalar#subtract(Quantity)}.
+     * Those tests depend on proper working of {@link Scalar#create(double, Unit)}.
+     */
+    @Test
+    public void testAddSubtract() {
+        final Quantity<Length> q0 = new Scalar.Length(0,  Units.MILLIMETRE);
+        final Quantity<Length> q1 = new Scalar.Length(24, Units.METRE);
+        final Quantity<Length> q2 = new Scalar.Length(3,  Units.KILOMETRE);
+        assertSame(q1, q1.add(q0));
+        assertSame(q2, q2.subtract(q0));
+
+        final Quantity<Length> q3 = q1.add(q2);
+        assertSame  ("unit",  Units.METRE, q3.getUnit());
+        assertEquals("value", 3024, q3.getValue().doubleValue(), STRICT);
+
+        final Quantity<Length> q4 = q2.subtract(q1);
+        assertSame  ("unit",  Units.KILOMETRE, q4.getUnit());
+        assertEquals("value", 2.976, q4.getValue().doubleValue(), STRICT);
+    }
+
+    /**
+     * Tests {@link Scalar#multiply(Number)} and {@link Scalar#divide(Number)}.
+     * Those tests depend on proper working of {@link Scalar#create(double, Unit)}.
+     */
+    @Test
+    public void testMultiplyDivideNumber() {
+        final Quantity<Length> q1 = new Scalar.Length(24, Units.KILOMETRE);
+        assertSame(q1, q1.multiply(1));
+        assertSame(q1, q1.divide  (1));
+
+        final Quantity<Length> q2 = q1.multiply(2);
+        assertSame  ("unit",  Units.KILOMETRE, q2.getUnit());
+        assertEquals("value", 48, q2.getValue().doubleValue(), STRICT);
+
+        final Quantity<Length> q3 = q1.divide(3);
+        assertSame  ("unit",  Units.KILOMETRE, q3.getUnit());
+        assertEquals("value", 8, q3.getValue().doubleValue(), STRICT);
+    }
+
+    /**
+     * Tests {@link Scalar#multiply(Quantity)}, {@link Scalar#divide(Quantity)} and {@link Quantity#inverse()}.
+     * Those tests depend on proper working of {@link Scalar#factory(double, Unit)}, which depends in turn on
+     * proper declarations of {@link ScalarFactory} in {@link Units} initialization.
+     */
+    @Test
+    public void testMultiplyDivideQuantity() {
+        final Quantity<Length> q1 = new Scalar.Length(24, Units.METRE);
+        final Quantity<Time>   q2 = new Scalar.Time  ( 4, Units.SECOND);
+        final Quantity<Speed>  q3 = q1.divide(q2).asType(Speed.class);
+        assertSame  ("unit", Units.METRES_PER_SECOND, q3.getUnit());
+        assertEquals("value", 6, q3.getValue().doubleValue(), STRICT);
+        assertInstanceOf("Length/Time", Scalar.Speed.class, q3);
+
+        final Quantity<Area> q4 = q1.multiply(q1).asType(Area.class);
+        assertSame  ("unit", Units.SQUARE_METRE, q4.getUnit());
+        assertEquals("value", 576, q4.getValue().doubleValue(), STRICT);
+        assertInstanceOf("Length⋅Length", Scalar.Area.class, q4);
+
+        final Quantity<Frequency> q5 = q2.inverse().asType(Frequency.class);
+        assertSame  ("unit", Units.HERTZ, q5.getUnit());
+        assertEquals("value", 0.25, q5.getValue().doubleValue(), STRICT);
+        assertInstanceOf("1/Time", Scalar.Frequency.class, q5);
+    }
+
+    /**
+     * Tests {@link Scalar#toString()}.
+     */
+    @Test
+    public void testToString() {
+        assertEquals("toString()", "24 km",   new Scalar.Length       (24.00, Units.KILOMETRE).toString());
+        assertEquals("toString()", "10.25 h", new Scalar.Time         (10.25, Units.HOUR)     .toString());
+        assertEquals("toString()", "0.25",    new Scalar.Dimensionless( 0.25, Units.UNITY)    .toString());
+    }
+
+    /**
+     * Tests {@link Scalar#equals(Object)} and {@link Scalar#hashCode()}.
+     */
+    @Test
+    public void testEqualsAndHashCode() {
+        final Quantity<Length> q1 = new Scalar.Length(24, Units.METRE);
+        Quantity<Length> q2 = new Scalar.Length(24, Units.METRE);
+        assertEquals("hashCode()", q1.hashCode(), q2.hashCode());
+        assertEquals("equals(…)", q1, q2);
+
+        q2 = new Scalar.Length(12, Units.METRE);
+        assertNotEquals("hashCode()", q1.hashCode(), q2.hashCode());
+        assertNotEquals("equals(…)", q1, q2);
+
+        q2 = new Scalar.Length(24, Units.CENTIMETRE);
+        assertNotEquals("hashCode()", q1.hashCode(), q2.hashCode());
+        assertNotEquals("equals(…)", q1, q2);
+    }
+
+    /**
+     * Tests {@link Scalar} serialization.
+     */
+    @Test
+    public void testSerialization() {
+        final Quantity<Length> q1 = new Scalar.Length(24, Units.KILOMETRE);
+        assertNotSame(q1, assertSerializedEquals(q1));
+    }
+
+    /**
+     * Tests {@link ScalarFallback}, used when no specialized implementation is available for a given quantity type.
+     */
+    @Test
+    public void testFallback() {
+        final Quantity<Length> q1 = ScalarFallback.factory(24, Units.KILOMETRE, Length.class);
+        assertInstanceOf("Dynamic proxy", Proxy .class, q1);
+        assertInstanceOf("Dynamic proxy", Length.class, q1);
+        assertSame  ("unit", Units.KILOMETRE, q1.getUnit());
+        assertEquals("value", 24, q1.getValue().doubleValue(), STRICT);
+        assertEquals("toString()", "24 km", q1.toString());
+
+        final Quantity<Length> q2 = ScalarFallback.factory(24, Units.KILOMETRE, Length.class);
+        assertEquals("hashCode()", q1.hashCode(), q2.hashCode());
+        assertEquals("equals(…)", q1, q2);
+
+        final Quantity<Length> q3 = ScalarFallback.factory(1500, Units.METRE, Length.class);
+        final Quantity<Length> q4 = q1.add(q3);
+        assertInstanceOf("Dynamic proxy", Proxy .class, q4);
+        assertInstanceOf("Dynamic proxy", Length.class, q4);
+        assertSame  ("unit", Units.KILOMETRE, q4.getUnit());
+        assertEquals("value", 25.5, q4.getValue().doubleValue(), STRICT);
+        assertEquals("toString()", "25.5 km", q4.toString());
+
+        final Quantity<Length> q5 = q1.multiply(q3).divide(q2).asType(Length.class);
+        assertSame  ("unit", Units.METRE, q5.getUnit());
+        assertEquals("value", 1500, q5.getValue().doubleValue(), STRICT);
+    }
+}

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

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

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=1790733&r1=1790732&r2=1790733&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] Sun Apr  9 00:08:13 2017
@@ -303,8 +303,8 @@ public final strictfp class SystemUnitTe
         /*
          * Test with units outside the pre-defined constants in the Units class.
          */
-        final Unit<Length> anonymous = new SystemUnit<>(Length.class, (UnitDimension) Units.METRE.getDimension(), null,  UnitRegistry.OTHER, (short) 0);
-        final Unit<Length> otherName = new SystemUnit<>(Length.class, (UnitDimension) Units.METRE.getDimension(), "Foo", UnitRegistry.OTHER, (short) 0);
+        final Unit<Length> anonymous = new SystemUnit<>(Length.class, (UnitDimension) Units.METRE.getDimension(), null,  UnitRegistry.OTHER, (short) 0, null);
+        final Unit<Length> otherName = new SystemUnit<>(Length.class, (UnitDimension) Units.METRE.getDimension(), "Foo", UnitRegistry.OTHER, (short) 0, null);
         assertSame(Units.METRE, anonymous.asType(Length.class));
         assertSame(otherName,   otherName.asType(Length.class));
         /*

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1790733&r1=1790732&r2=1790733&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] Sun Apr  9 00:08:13 2017
@@ -110,6 +110,7 @@ import org.junit.BeforeClass;
     org.apache.sis.measure.ConventionalUnitTest.class,
     org.apache.sis.measure.UnitFormatTest.class,
     org.apache.sis.measure.UnitsTest.class,
+    org.apache.sis.measure.ScalarTest.class,
     org.apache.sis.measure.UnitServicesTest.class,
     org.apache.sis.measure.RangeTest.class,
     org.apache.sis.measure.DateRangeTest.class,



Mime
View raw message