001/* 002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences. 003 * Copyright (C) 2006 - JScience (http://jscience.org/) 004 * All rights reserved. 005 * 006 * Permission to use, copy, modify, and distribute this software is 007 * freely granted, provided that this notice is preserved. 008 */ 009package javax.measure.converter; 010 011import java.io.Serializable; 012 013/** 014 * <p> This class represents a converter of numeric values.</p> 015 * 016 * <p> It is not required for sub-classes to be immutable 017 * (e.g. currency converter).</p> 018 * 019 * <p> Sub-classes must ensure unicity of the {@link #IDENTITY identity} 020 * converter. In other words, if the result of an operation is equivalent 021 * to the identity converter, then the unique {@link #IDENTITY} instance 022 * should be returned.</p> 023 * 024 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 025 * @version 3.1, April 22, 2006 026 */ 027public abstract class UnitConverter implements Serializable { 028 029 /** 030 * Holds the identity converter (unique). This converter does nothing 031 * (<code>ONE.convert(x) == x</code>). 032 */ 033 public static final UnitConverter IDENTITY = new Identity(); 034 035 /** 036 * Default constructor. 037 */ 038 protected UnitConverter() { 039 } 040 041 /** 042 * Returns the inverse of this converter. If <code>x</code> is a valid 043 * value, then <code>x == inverse().convert(convert(x))</code> to within 044 * the accuracy of computer arithmetic. 045 * 046 * @return the inverse of this converter. 047 */ 048 public abstract UnitConverter inverse(); 049 050 /** 051 * Converts a double value. 052 * 053 * @param x the numeric value to convert. 054 * @return the converted numeric value. 055 * @throws ConversionException if an error occurs during conversion. 056 */ 057 public abstract double convert(double x) throws ConversionException; 058 059 /** 060 * Indicates if this converter is linear. A converter is linear if 061 * <code>convert(u + v) == convert(u) + convert(v)</code> and 062 * <code>convert(r * u) == r * convert(u)</code>. 063 * For linear converters the following property always hold:[code] 064 * y1 = c1.convert(x1); 065 * y2 = c2.convert(x2); 066 * then y1*y2 = c1.concatenate(c2).convert(x1*x2)[/code] 067 * 068 * @return <code>true</code> if this converter is linear; 069 * <code>false</code> otherwise. 070 */ 071 public abstract boolean isLinear(); 072 073 /** 074 * Indicates whether this converter is considered the same as the 075 * converter specified. To be considered equal this converter 076 * concatenated with the one specified must returns the {@link #IDENTITY}. 077 * 078 * @param cvtr the converter with which to compare. 079 * @return <code>true</code> if the specified object is a converter 080 * considered equals to this converter;<code>false</code> otherwise. 081 */ 082 public boolean equals(Object cvtr) { 083 if (!(cvtr instanceof UnitConverter)) return false; 084 return this.concatenate(((UnitConverter)cvtr).inverse()) == IDENTITY; 085 } 086 087 /** 088 * Returns a hash code value for this converter. Equals object have equal 089 * hash codes. 090 * 091 * @return this converter hash code value. 092 * @see #equals 093 */ 094 public int hashCode() { 095 return Float.floatToIntBits((float)convert(1.0)); 096 } 097 098 /** 099 * Concatenates this converter with another converter. The resulting 100 * converter is equivalent to first converting by the specified converter, 101 * and then converting by this converter. 102 * 103 * <p>Note: Implementations must ensure that the {@link #IDENTITY} instance 104 * is returned if the resulting converter is an identity 105 * converter.</p> 106 * 107 * @param converter the other converter. 108 * @return the concatenation of this converter with the other converter. 109 */ 110 public UnitConverter concatenate(UnitConverter converter) { 111 return (converter == IDENTITY) ? this : new Compound(converter, this); 112 } 113 114 /** 115 * This inner class represents the identity converter (singleton). 116 */ 117 private static final class Identity extends UnitConverter { 118 119 @Override 120 public UnitConverter inverse() { 121 return this; 122 } 123 124 @Override 125 public double convert(double x) { 126 return x; 127 } 128 129 @Override 130 public boolean isLinear() { 131 return true; 132 } 133 134 @Override 135 public UnitConverter concatenate(UnitConverter converter) { 136 return converter; 137 } 138 139 private static final long serialVersionUID = 1L; 140 141 } 142 143 /** 144 * This inner class represents a compound converter. 145 */ 146 private static final class Compound extends UnitConverter { 147 148 /** 149 * Holds the first converter. 150 */ 151 private final UnitConverter _first; 152 153 /** 154 * Holds the second converter. 155 */ 156 private final UnitConverter _second; 157 158 /** 159 * Creates a compound converter resulting from the combined 160 * transformation of the specified converters. 161 * 162 * @param first the first converter. 163 * @param second the second converter. 164 */ 165 private Compound(UnitConverter first, UnitConverter second) { 166 _first = first; 167 _second = second; 168 } 169 170 @Override 171 public UnitConverter inverse() { 172 return new Compound(_second.inverse(), _first.inverse()); 173 } 174 175 @Override 176 public double convert(double x) { 177 return _second.convert(_first.convert(x)); 178 } 179 180 @Override 181 public boolean isLinear() { 182 return _first.isLinear() && _second.isLinear(); 183 } 184 185 private static final long serialVersionUID = 1L; 186 187 } 188}