| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.internal.converter; |
| |
| import java.util.Set; |
| import java.util.EnumSet; |
| import org.apache.sis.util.Numbers; |
| import org.apache.sis.math.FunctionProperty; |
| import org.apache.sis.util.ObjectConverter; |
| import org.apache.sis.util.UnconvertibleObjectException; |
| import org.apache.sis.util.resources.Errors; |
| |
| |
| /** |
| * Handles conversions from {@link java.lang.Number} to other kind of numbers. |
| * This class supports only the type supported by {@link Numbers}. |
| * |
| * <div class="section">Performance note</div> |
| * We provide a single class for all supported kinds of {@code Number} and delegate the actual |
| * work to the {@code Numbers} static methods. This is not a very efficient way to do the work. |
| * For example it may be more efficient to provide specialized subclasses for each target class, |
| * so we don't have to execute the {@code switch} inside the {@code Numbers} class every time a |
| * value is converted. However performance is not the primary concern here, since those converters |
| * will typically be used by code doing more costly work (e.g. the {@code sis-metadata} module |
| * providing {@code Map} views using Java reflection). So we rather try to be more compact. |
| * If nevertheless performance appears to be a problem, consider reverting to revision |
| * {@code d73a10558dda4b41723d4f5652a792ae9c24f69e} (subversion: 1455255) of this class, |
| * which was using one subclass per target type as described above. |
| * |
| * <div class="section">Immutability and thread safety</div> |
| * This class and all inner classes are immutable, and thus inherently thread-safe. |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| * @version 0.3 |
| * |
| * @param <S> the source number type. |
| * @param <T> the target number type. |
| * |
| * @since 0.3 |
| * @module |
| */ |
| final class NumberConverter<S extends Number, T extends Number> extends SystemConverter<S,T> { |
| /** |
| * For cross-version compatibility. |
| */ |
| private static final long serialVersionUID = 3339549290992876106L; |
| |
| /** |
| * The inverse converter, created when first needed. |
| */ |
| private transient volatile ObjectConverter<T,S> inverse; |
| |
| /** |
| * Creates a new converter for the given source and target classes. |
| * This constructor does not verify the validity of parameter values. |
| * It is caller's responsibility to ensure that the given class are |
| * supported by the {@link Numbers} methods. |
| */ |
| NumberConverter(final Class<S> sourceClass, final Class<T> targetClass) { |
| super(sourceClass, targetClass); |
| } |
| |
| /** |
| * Returns the inverse converter, creating it when first needed. |
| * This method delegates to {@link SystemRegistry#INSTANCE} and caches the result. |
| * We do not provide pre-defined constant for the various converter because there |
| * is too many possibly combinations. |
| */ |
| @Override |
| public ObjectConverter<T,S> inverse() throws UnsupportedOperationException { |
| /* |
| * No need to synchronize. This is not a big deal if the same object is fetched twice. |
| * The ConverterRegistry clas provides the required synchronization. |
| */ |
| ObjectConverter<T,S> candidate = inverse; |
| if (candidate == null) try { |
| inverse = candidate = SystemRegistry.INSTANCE.findExact(targetClass, sourceClass); |
| } catch (UnconvertibleObjectException e) { |
| throw new UnsupportedOperationException(Errors.format(Errors.Keys.NonInvertibleConversion), e); |
| } |
| return candidate; |
| } |
| |
| /** |
| * Declares this converter as a injective or surjective function, |
| * depending on whether conversions loose information or not. |
| */ |
| @Override |
| public Set<FunctionProperty> properties() { |
| return EnumSet.of(Numbers.widestClass(sourceClass, targetClass) == targetClass |
| ? FunctionProperty.INJECTIVE : FunctionProperty.SURJECTIVE, |
| FunctionProperty.ORDER_PRESERVING, FunctionProperty.INVERTIBLE); |
| } |
| |
| /** |
| * Converts the given number to the target type if that type is different. |
| * This implementation is inefficient, but avoid us the need to create one |
| * subclass for each number type. See class javadoc for more details. |
| */ |
| @Override |
| public T apply(final S source) { |
| final double sourceValue = source.doubleValue(); |
| T target; |
| try { |
| target = Numbers.cast(source, targetClass); |
| } catch (IllegalArgumentException e) { |
| throw new UnconvertibleObjectException(formatErrorMessage(source), e); |
| } |
| final double targetValue = target.doubleValue(); |
| if (Double.doubleToLongBits(targetValue) != Double.doubleToLongBits(sourceValue)) { |
| /* |
| * Casted value is not equal to the source value. Maybe we just lost the fraction digits |
| * in a (double → long) cast, in which case the difference should be smaller than 1. |
| */ |
| final double delta = Math.abs(targetValue - sourceValue); |
| if (!(delta < 0.5)) { // Use '!' for catching NaN. |
| if (delta < 1) { |
| target = Numbers.cast(Math.round(sourceValue), targetClass); |
| } else { |
| /* |
| * The delta may be greater than 1 in a (BigInteger/BigDecimal → long) cast if the |
| * BigInteger/BigDecimal has more significant digits than what the double type can |
| * hold. |
| */ |
| throw new UnconvertibleObjectException(formatErrorMessage(source)); |
| } |
| } |
| } |
| return target; |
| } |
| |
| /** |
| * Converter from numbers to comparables. This special case exists because {@link Number} |
| * does not implement {@link java.lang.Comparable} directly, but all known subclasses do. |
| */ |
| static final class Comparable<S extends Number> extends SystemConverter<S, java.lang.Comparable<?>> { |
| /** |
| * For cross-version compatibility. |
| */ |
| private static final long serialVersionUID = -6366381413315460619L; |
| |
| /** |
| * Creates a new converter from the given type of numbers to {@code Comparable} instances. |
| */ |
| @SuppressWarnings({"rawtypes","unchecked"}) |
| Comparable(final Class<S> sourceClass) { |
| super(sourceClass, (Class) java.lang.Comparable.class); |
| } |
| |
| /** |
| * If the source class implements {@code Comparable}, then this converter is bijective. |
| * Otherwise there is no known property for this converter. |
| */ |
| @Override |
| public Set<FunctionProperty> properties() { |
| if (targetClass.isAssignableFrom(sourceClass)) { |
| return bijective(); |
| } |
| return EnumSet.noneOf(FunctionProperty.class); |
| } |
| |
| /** |
| * Converts the given number to a {@code Comparable} if its type is different. |
| */ |
| @Override |
| public java.lang.Comparable<?> apply(final Number source) { |
| if (source == null || source instanceof java.lang.Comparable<?>) { |
| return (java.lang.Comparable<?>) source; |
| } |
| return (java.lang.Comparable<?>) Numbers.narrowestNumber(source); |
| } |
| } |
| } |