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: */
009: package javax.measure.converter;
010:
011: import 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: */
027: public 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))
084: return false;
085: return this .concatenate(((UnitConverter) cvtr).inverse()) == IDENTITY;
086: }
087:
088: /**
089: * Returns a hash code value for this converter. Equals object have equal
090: * hash codes.
091: *
092: * @return this converter hash code value.
093: * @see #equals
094: */
095: public int hashCode() {
096: return Float.floatToIntBits((float) convert(1.0));
097: }
098:
099: /**
100: * Concatenates this converter with another converter. The resulting
101: * converter is equivalent to first converting by the specified converter,
102: * and then converting by this converter.
103: *
104: * <p>Note: Implementations must ensure that the {@link #IDENTITY} instance
105: * is returned if the resulting converter is an identity
106: * converter.</p>
107: *
108: * @param converter the other converter.
109: * @return the concatenation of this converter with the other converter.
110: */
111: public UnitConverter concatenate(UnitConverter converter) {
112: return (converter == IDENTITY) ? this : new Compound(converter,
113: this );
114: }
115:
116: /**
117: * This inner class represents the identity converter (singleton).
118: */
119: private static final class Identity extends UnitConverter {
120:
121: @Override
122: public UnitConverter inverse() {
123: return this ;
124: }
125:
126: @Override
127: public double convert(double x) {
128: return x;
129: }
130:
131: @Override
132: public boolean isLinear() {
133: return true;
134: }
135:
136: @Override
137: public UnitConverter concatenate(UnitConverter converter) {
138: return converter;
139: }
140:
141: private static final long serialVersionUID = 1L;
142:
143: }
144:
145: /**
146: * This inner class represents a compound converter.
147: */
148: private static final class Compound extends UnitConverter {
149:
150: /**
151: * Holds the first converter.
152: */
153: private final UnitConverter _first;
154:
155: /**
156: * Holds the second converter.
157: */
158: private final UnitConverter _second;
159:
160: /**
161: * Creates a compound converter resulting from the combined
162: * transformation of the specified converters.
163: *
164: * @param first the first converter.
165: * @param second the second converter.
166: */
167: private Compound(UnitConverter first, UnitConverter second) {
168: _first = first;
169: _second = second;
170: }
171:
172: @Override
173: public UnitConverter inverse() {
174: return new Compound(_second.inverse(), _first.inverse());
175: }
176:
177: @Override
178: public double convert(double x) {
179: return _second.convert(_first.convert(x));
180: }
181:
182: @Override
183: public boolean isLinear() {
184: return _first.isLinear() && _second.isLinear();
185: }
186:
187: private static final long serialVersionUID = 1L;
188:
189: }
190: }
|