blob: 3305d2aec4a902e23876a61bafef509a9d844df0 [file] [log] [blame]
/*******************************************************************************
* Crown Copyright (c) 2006, 2013, Copyright (c) 2006, 2008 Kestral Computing P/L.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Kestral Computing P/L - initial implementation
* Werner Keil - fixes and improvements
*******************************************************************************/
package org.eclipse.uomo.ucum.impl;
import static org.eclipse.uomo.core.impl.OutputHelper.println;
import java.io.File;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.uomo.core.UOMoRuntimeException;
import org.eclipse.uomo.core.impl.CodeValuePair;
import org.eclipse.uomo.ucum.UcumService;
import org.eclipse.uomo.ucum.expression.Symbol;
import org.eclipse.uomo.ucum.expression.Term;
import org.eclipse.uomo.ucum.model.BaseUnit;
import org.eclipse.uomo.ucum.model.Concept;
import org.eclipse.uomo.ucum.model.ConceptKind;
import org.eclipse.uomo.ucum.model.DefinedUnit;
import org.eclipse.uomo.ucum.model.UcumModel;
import org.eclipse.uomo.ucum.parsers.DefinitionParser;
import org.eclipse.uomo.ucum.parsers.ExpressionComposer;
import org.eclipse.uomo.ucum.parsers.ExpressionParser;
import org.eclipse.uomo.ucum.parsers.FormalStructureComposer;
import org.eclipse.uomo.ucum.special.SpecialUnitHandler;
import org.eclipse.uomo.ucum.special.SpecialUnitRegistry;
import org.eclipse.uomo.util.Registry;
/**
* implements UCUM services. Applications must provide a copy of
* ucum-essence.xml as either a stream or a file name to create the services.
*
* the provided ucum-essence.xml must be released on 25 Apr 2008 or more recent.
* Note that if the ucum-essence.xml file does not contain a release date on an
* attribute of the root element, it is not more recent than this date (Gunther
* added it on this date for this usage, thanks)
*
* @author Grahame Grieve
* @author Werner Keil
*/
public class UcumEssenceService implements UcumService {
/**
* UCUM ID
*/
public static final String UCUM_OID = "2.16.840.1.113883.6.8";
private final UcumModel model;
@SuppressWarnings("rawtypes")
private final Registry<SpecialUnitHandler> handlers = new SpecialUnitRegistry();
/**
* Create an instance of Ucum services. Stream must point to a valid
* ucum-essence file (see class documentation)
*/
public UcumEssenceService(InputStream stream) throws UOMoRuntimeException {
super();
assert stream != null : paramError("factory", "stream",
"must not be null");
try {
model = new DefinitionParser().parse(stream);
} catch (Exception e) {
throw new UOMoRuntimeException(e);
}
}
/**
* Create an instance of Ucum services. filename must point to a valid
* ucum-essence file (see class documentation)
*/
public UcumEssenceService(String filename) throws UOMoRuntimeException {
super();
assert new File(filename).exists() : paramError("factory", "file",
"must exist");
try {
model = new DefinitionParser().parse(filename);
} catch (Exception e) {
throw new UOMoRuntimeException(e);
}
}
private String paramError(String method, String param, String msg) {
return getClass().getName() + "." + method + "." + param
+ " is not acceptable: " + msg;
}
public UcumVersionDetails ucumIdentification() {
return new UcumVersionDetails(model.getRevisionDate(),
model.getVersion());
}
/*
* (non-Javadoc)
*
* @see org.eclipse.uomo.ucum.UcumServiceEx#getModel()
*/
public UcumModel getModel() {
return model;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.uomo.ucum.UcumServiceEx#search(org.eclipse.uomo.ucum.model.
* ConceptKind, java.lang.String, boolean)
*/
public List<Concept> search(ConceptKind kind, String text, boolean isRegex) {
assert checkStringParam(text) : paramError("search", "text",
"must not be null or empty");
return new SearchImpl().doSearch(model, kind, text, isRegex);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.uomo.ucum.UcumService#validateUCUM()
*/
public List<String> validateUCUM() {
return new UcumValidator(model, handlers).validate();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.uomo.ucum.UcumService#getProperties()
*/
public Set<String> getProperties() {
Set<String> result = new HashSet<String>();
for (DefinedUnit unit : model.getDefinedUnits()) {
result.add(unit.getProperty());
}
for (BaseUnit unit : model.getBaseUnits()) {
result.add(unit.getProperty());
}
return result;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ohf.ucum.UcumServiceEx#validate(java.lang.String)
*/
public String validate(String unit) {
assert unit != null : paramError("validate", "unit", "must not be null");
try {
new ExpressionParser(model).parse(unit);
return null;
} catch (UOMoRuntimeException e) {
return e.getMessage();
}
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ohf.ucum.UcumServiceEx#validateInProperty(java.lang.String,
* java.lang.String)
*/
public String validateInProperty(String unit, String property) {
assert checkStringParam(unit) : paramError("validate", "unit",
"must not be null or empty");
assert checkStringParam(property) : paramError("validateInProperty",
"property", "must not be null or empty");
try {
Term term = new ExpressionParser(model).parse(unit);
Canonical can = new UcumConverter(model, handlers).convert(term);
term = can.getUnit();
String cu = new ExpressionComposer().compose(term);
if (term.hasComp() && !term.hasOp()
&& term.getComp() instanceof Symbol) {
Symbol sym = (Symbol) term.getComp();
if (sym.getExponent() == 1 && sym.getUnit() instanceof BaseUnit) {
BaseUnit b = (BaseUnit) sym.getUnit();
if (property.equals(b.getProperty()))
return null;
else
return "unit " + unit + " is of the property type "
+ b.getProperty() + " (" + cu + "), not "
+ property + " as required.";
}
}
// defined special case
if ("concentration".equals(property)
&& ("g/L".equals(cu) || "mol/L".equals(cu)))
return null;
return "unit " + unit + " has the base units " + cu
+ ", and are not from the property " + property
+ " as required.";
} catch (UOMoRuntimeException e) {
return e.getMessage();
}
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ohf.ucum.UcumServiceEx#validateCanonicalUnits(java.lang.String
* , java.lang.String)
*/
public String validateCanonicalUnits(String unit, String canonical) {
assert checkStringParam(unit) : paramError("validate", "unit",
"must not be null or empty");
assert checkStringParam(canonical) : paramError(
"validateCanonicalUnits", "canonical",
"must not be null or empty");
try {
Term term = new ExpressionParser(model).parse(unit);
Canonical can = new UcumConverter(model, handlers).convert(term);
term = can.getUnit();
String cu = new ExpressionComposer().compose(term);
if (!canonical.equals(cu))
return "unit " + unit + " has the base units " + cu + ", not "
+ canonical + " as required.";
return null;
} catch (UOMoRuntimeException e) {
return e.getMessage();
}
}
/**
* given a unit, return a formal description of what the units stand for
* using full names
*
* @param units
* the unit code
* @return formal description
* @throws UOMoRuntimeException
*/
public String analyse(String unit) throws UOMoRuntimeException {
assert checkStringParam(unit) : paramError("analyse", "unit",
"must not be null or empty");
Term term = new ExpressionParser(model).parse(unit);
return new FormalStructureComposer().compose(term);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ohf.ucum.UcumServiceEx#getCanonicalUnits(java.lang.String)
*/
public String getCanonicalUnits(String unit) throws UOMoRuntimeException {
assert checkStringParam(unit) : paramError("getCanonicalUnits", "unit",
"must not be null or empty");
try {
Term term = new ExpressionParser(model).parse(unit);
term = new UcumConverter(model, handlers).convert(term).getUnit();
return new ExpressionComposer().compose(term);
} catch (RuntimeException e) {
throw new UOMoRuntimeException("Error processing " + unit + ": "
+ e.getMessage(), e);
}
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.uomo.ucum.UcumServiceEx#getDefinedForms(java.lang.String)
*/
public List<DefinedUnit> getDefinedForms(String code)
throws UOMoRuntimeException {
assert checkStringParam(code) : paramError("getDefinedForms", "code",
"must not be null or empty");
List<DefinedUnit> result = new ArrayList<DefinedUnit>();
BaseUnit base = model.getBaseUnit(code);
if (base != null) {
for (DefinedUnit unit : model.getDefinedUnits()) {
if (!unit.isSpecial()
&& code.equals(getCanonicalUnits(unit.getCode())))
result.add(unit);
}
}
return result;
}
private boolean checkStringParam(String s) {
return s != null && !s.equals("");
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ohf.ucum.UcumServiceEx#getCanonicalForm(org.eclipse.ohf.ucum
* .UcumEssenceService.Pair)
*/
public CodeValuePair<Number, String> getCanonicalForm(CodeValuePair<Number, String> value)
throws UOMoRuntimeException {
assert value != null : paramError("getCanonicalForm", "value",
"must not be null");
assert checkStringParam(value.getCode()) : paramError(
"getCanonicalForm", "value.code", "must not be null or empty");
Term term = new ExpressionParser(model).parse(value.getCode());
Canonical c = new UcumConverter(model, handlers).convert(term);
if (value.getValue() == null)
return new CodeValuePair<Number, String>(null,
new ExpressionComposer().compose(c.getUnit()));
else
return new CodeValuePair<Number, String>(
((BigDecimal) value.getValue()).multiply(c.getValue()),
new ExpressionComposer().compose(c.getUnit()));
}
/*
* (non-Javadoc)
*
* @see org.eclipse.uomo.ucum.UcumServiceEx#convert(java.math.BigDecimal,
* java.lang.String, java.lang.String)
*/
public Number convert(final Number value, String sourceUnit, String destUnit)
throws UOMoRuntimeException {
assert value != null : paramError("convert", "value",
"must not be null");
assert checkStringParam(sourceUnit) : paramError("convert",
"sourceUnit", "must not be null or empty");
assert checkStringParam(destUnit) : paramError("convert", "destUnit",
"must not be null or empty");
if (sourceUnit.equals(destUnit))
return value;
Canonical src = new UcumConverter(model, handlers)
.convert(new ExpressionParser(model).parse(sourceUnit));
Canonical dst = new UcumConverter(model, handlers)
.convert(new ExpressionParser(model).parse(destUnit));
String s = new ExpressionComposer().compose(src.getUnit());
String d = new ExpressionComposer().compose(dst.getUnit());
if (!s.equals(d))
throw new UOMoRuntimeException("Unable to convert between units "
+ sourceUnit + " and " + destUnit
+ " as they do not have matching canonical forms (" + s
+ " and " + d + " respectively)");
final BigDecimal decValue = (BigDecimal) value;
final BigDecimal canValue = decValue.multiply(src.getValue());
final BigDecimal result = canValue.divide(dst.getValue(), new MathContext(25));
println(decValue.toPlainString() + sourceUnit + " =("
+ src.getValue().toPlainString() + ")= "
+ canValue.toPlainString() + s + " =("
+ dst.getValue().toPlainString() + ")= "
+ result);
return result;
}
public CodeValuePair<Number, String> multiply(CodeValuePair<Number, String> o1,
CodeValuePair<Number, String> o2) {
// Term term = new ExpressionParser(model).parse(o1.getCode());
// Canonical c = new Converter(model, handlers).convert(term);
return new CodeValuePair<Number, String>(
((BigDecimal) o1.getValue()).multiply((BigDecimal) o2
.getValue()),
// new ExpressionComposer().compose(c.getUnit()));
o1.getCode() + "." + o2.getCode());
}
}