/***************************************************************************** * Limpet - the Lightweight InforMation ProcEssing Toolkit * http://limpet.info * * (C) 2015-2016, Deep Blue C Technologies Ltd * * This library is free software; you can redistribute it and/or * modify it under the terms of the Eclipse Public License v1.0 * (http://www.eclipse.org/legal/epl-v10.html) * * This library 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. *****************************************************************************/ package info.limpet.data.operations; import info.limpet.ICollection; import info.limpet.ICommand; import info.limpet.IContext; import info.limpet.IOperation; import info.limpet.IQuantityCollection; import info.limpet.IStore; import info.limpet.IStoreItem; import info.limpet.data.commands.AbstractCommand; import info.limpet.data.impl.QuantityCollection; import info.limpet.data.impl.TemporalQuantityCollection; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import javax.measure.Measurable; import javax.measure.converter.UnitConverter; import javax.measure.quantity.Quantity; import javax.measure.unit.Unit; public class UnitConversionOperation implements IOperation<ICollection> { public static final String CONVERTED_TO = " converted to "; private final CollectionComplianceTests aTests = new CollectionComplianceTests(); private final Unit<?> targetUnit; public UnitConversionOperation(Unit<?> targetUnit) { this.targetUnit = targetUnit; } public Collection<ICommand<ICollection>> actionsFor( List<ICollection> selection, IStore destination, IContext context) { Collection<ICommand<ICollection>> res = new ArrayList<ICommand<ICollection>>(); if (appliesTo(selection)) { String unitsName = targetUnit.toString(); String name = "Convert to " + unitsName; ICommand<ICollection> newC = new ConvertQuanityValues(name, selection, destination, context); res.add(newC); } return res; } private boolean appliesTo(List<ICollection> selection) { boolean singleSeries = selection.size() == 1; boolean allQuantity = aTests.allQuantity(selection); boolean sameDimension = false; boolean sameUnits = true; if (selection.size() > 0 && allQuantity) { Unit<?> units = ((IQuantityCollection<?>) selection.get(0)).getUnits(); sameDimension = units.getDimension().equals(targetUnit.getDimension()); // check they're different units. It's not worth offering the // operation // if // they're already in the same units sameUnits = units.equals(targetUnit); } return singleSeries && allQuantity && sameDimension && !sameUnits; } public class ConvertQuanityValues extends AbstractCommand<ICollection> { public ConvertQuanityValues(String operationName, List<ICollection> selection, IStore store, IContext context) { super(operationName, "Convert units of the provided series", store, false, false, selection, context); } @Override protected String getOutputName() { return getContext().getInput("Convert units", NEW_DATASET_MESSAGE, super.getSubjectList() + " converted to " + targetUnit); } @Override public void execute() { List<ICollection> outputs = new ArrayList<ICollection>(); ICollection theInput = getInputs().iterator().next(); // ok, generate the new series final IQuantityCollection<?> target; // hmm, is it a temporal operation if (theInput.isTemporal()) { target = new TemporalQuantityCollection<>(getOutputName(), this, targetUnit); } else { target = new QuantityCollection<>(getOutputName(), this, targetUnit); } outputs.add(target); // store the output super.addOutput(target); // start adding values. performCalc(outputs); // tell each series that we're a dependent Iterator<ICollection> iter = getInputs().iterator(); while (iter.hasNext()) { ICollection iCollection = iter.next(); iCollection.addDependent(this); } // ok, done List<IStoreItem> res = new ArrayList<IStoreItem>(); res.add(target); getStore().addAll(res); } @Override protected void recalculate(IStoreItem subject) { // update the results performCalc(getOutputs()); } /** * wrap the actual operation. We're doing this since we need to separate it from the core * "execute" operation in order to support dynamic updates * * @param unit * @param outputs */ @SuppressWarnings("unchecked") private void performCalc(List<ICollection> outputs) { IQuantityCollection<Quantity> target = (IQuantityCollection<Quantity>) outputs.iterator().next(); // clear out the lists, first Iterator<ICollection> iter = outputs.iterator(); while (iter.hasNext()) { IQuantityCollection<Quantity> qC = (IQuantityCollection<Quantity>) iter.next(); qC.clearQuiet(); } IQuantityCollection<Quantity> singleInputSeries = (IQuantityCollection<Quantity>) getInputs().get(0); UnitConverter converter = singleInputSeries.getUnits().getConverterTo(target.getUnits()); for (int j = 0; j < singleInputSeries.getValues().size(); j++) { Measurable<Quantity> thisValue = singleInputSeries.getValues().get(j); double converted = converter.convert(thisValue.doubleValue(singleInputSeries .getUnits())); if (singleInputSeries.isTemporal()) { TemporalQuantityCollection<Quantity> tq = (TemporalQuantityCollection<Quantity>) singleInputSeries; Long time = (Long) tq.getTimes().get(j); TemporalQuantityCollection<Quantity> tgtQ = (TemporalQuantityCollection<Quantity>) target; tgtQ.add(time, converted); } else { target.add(converted); } } // and fire the update target.fireDataChanged(); } } }