/* * Copyright (C) 2015 University of Dundee & Open Microscopy Environment. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package ome.model.units; import java.math.BigDecimal; import java.math.MathContext; /** * Base-functor like object which can be used for preparing complex * equations for converting from one unit to another. Primarily these * classes and static methods are used via code-generation. Sympy-generated * strings are placed directly into code. If the proper imports are in place, * then a top-level {@link Conversion} (usually of type {@link Add} or * {@link Mul} is returned from the evaluation. */ public abstract class Conversion { // Helper static methods which prevent the need for "new" // in the generated code. /** * Static helper for creating {@link Add} instances. */ public static Conversion Add(Conversion...conversions) { return new Add(conversions); } /** * Static helper for creating {@link Int} instances. */ public static Conversion Int(long i) { return new Int(i); } /** * Static helper for creating {@link Int} instances. */ public static Conversion Int(String i) { return new Int(i); } /** * Static helper for creating {@link Mul} instances. */ public static Conversion Mul(Conversion...conversions) { return new Mul(conversions); } /** * Static helper for creating {@link Pow} instances. */ public static Conversion Pow(long num, int den) { return new Pow(num, den); } /** * Static helper for creating {@link Rat} instances. */ public static Conversion Rat(long num, long den) { return new Rat(num, den); } /** * Static helper for creating {@link Rat} instances. */ public static Conversion Rat(Conversion... conversions) { return new Rat(conversions); } /** * Static helper for creating {@link Sym} instances. */ public static Conversion Sym(String sym) { return new Sym(sym); } /** * Conversions, if any, which are passed into the constructor * of this instance. If none are passed, then the implementation * has a short-cut form, e.g. taking an {@link Integer} rather than * an {@link Int}. */ protected final Conversion[] conversions; /** * Primary constructor for a {@link Conversion} object. No processing * happens during constructor. Instead, the {@link #convert(double)} * method will handle descending through the recursive structure. * * @param conversions can be empty. */ public Conversion(Conversion...conversions) { this.conversions = conversions; } /** * Primary operator for {@link Conversion} instances. * @param original A unit value which is to be processed through the * tree-like representation of this equation. Only {@link Sym} objects * will actually use the "original" value. * @return a {@link BigDecimal} result from the calculation. If this value * maps to {@link Double#NEGATIVE_INFINITY} or * {@link Double#POSITIVE_INFINITY}, then a {@link BigResult} exception * should be thrown before returning to clients. */ public abstract BigDecimal convert(double original); /** * Sums all {@link Conversion} instances via {@link BigDecimal#add(BigDecimal)}. */ public static class Add extends Conversion { public Add(Conversion[] conversions) { super(conversions); } public BigDecimal convert(double original) { BigDecimal big = BigDecimal.ZERO; for (Conversion c : conversions) { big = big.add(c.convert(original)); } return big; } } /** * Simply is a representation of a possibly large integer. */ public static class Int extends Conversion { private final long i; private final String s; public Int(long i) { this.i = i; this.s = null; } public Int(String s) { this.s = s; this.i = 0; } /** * Returns a {@link BigDecimal} representation of this int. * Original argument is ignored. */ public BigDecimal convert(double original) { if (s == null) { return new BigDecimal(i); } return new BigDecimal(s); } } /** * Multiplies all {@link Conversion} instances via * {@link BigDecimal#multiply(BigDecimal)}. */ public static class Mul extends Conversion { public Mul(Conversion[] conversions) { super(conversions); } public BigDecimal convert(double original) { BigDecimal big = BigDecimal.ONE; for (Conversion c : conversions) { big = big.multiply(c.convert(original)); } return big; } } /** * Exponentiates two {@link Conversion} instances via * {@link BigDecimal#pow(int)}. */ public static class Pow extends Conversion { private final long base; private final int exp; public Pow(long base, int exp) { this.base = base; this.exp = exp; } public BigDecimal convert(double original) { return new BigDecimal(base).pow(exp); } } /** * Divides two {@link Conversion} instances via * {@link BigDecimal#divide(BigDecimal, MathContext)}. */ public static class Rat extends Conversion { private final long num, denom; private final boolean delay; public Rat(long num, long denom) { this.num = num; this.denom = denom; this.delay = false; } public Rat(Conversion...conversions) { super(conversions); this.num = 0; this.denom = 0; if (conversions.length != 2) { throw new IllegalArgumentException( "Too many conversions: " + conversions.length); } this.delay = true; } public BigDecimal convert(double original) { if (!delay) { return new BigDecimal(num).divide(new BigDecimal(denom), MathContext.DECIMAL128); } else { return conversions[0].convert(original).divide( conversions[1].convert(original), MathContext.DECIMAL128); } } } /** * Simply represents the variable of the source unit so that * {@link Sym#convert(double)} just returns the value passed in. */ public static class Sym extends Conversion { public Sym(char sym) { // no-op } public Sym(String sym) { // no-op } public BigDecimal convert(double original) { return new BigDecimal(original); } } }