/******************************************************************************* * Copyright (c) 2007, 2011 Association for Decentralized Information Management in * Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.history.util; import org.simantics.databoard.Bindings; import org.simantics.databoard.adapter.AdaptException; import org.simantics.databoard.adapter.Adapter; import org.simantics.databoard.adapter.AdapterConstructionException; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.BooleanBinding; import org.simantics.databoard.binding.ByteBinding; import org.simantics.databoard.binding.DoubleBinding; import org.simantics.databoard.binding.FloatBinding; import org.simantics.databoard.binding.NumberBinding; import org.simantics.databoard.binding.RecordBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.error.RuntimeBindingException; import org.simantics.databoard.type.RecordType; import org.simantics.history.HistoryException; /** * Value band is an utility class intended for reading and writing to samples. * There are many different formats of samples, their fields, order and datatypes vary. * * @author toni.kalajainen */ public class ValueBand { public static final Byte QUALITY_GOOD = Byte.valueOf( (byte) 0 ); public static final Byte QUALITY_NOVALUE = Byte.valueOf( (byte) -1 ); protected RecordBinding binding; protected RecordType type; protected Object sample; /** Field value cloners */ FieldAdapter timeField, endTimeField, valueField, lastValueField, avgField, medianField, minField, maxField, countField, qualityField; /** Default value*/ Object defaultValue; public ValueBand(Binding sampleBinding) { if ( sampleBinding instanceof RecordBinding == false ) { throw new IllegalArgumentException(); } this.binding = (RecordBinding) sampleBinding; type = this.binding.type(); timeField = new FieldAdapter("time"); endTimeField = new FieldAdapter("endTime"); valueField = new FieldAdapter("value"); lastValueField = new FieldAdapter("lastValue"); avgField = new FieldAdapter("avg"); medianField = new FieldAdapter("median"); minField = new FieldAdapter("min"); maxField = new FieldAdapter("max"); countField = new FieldAdapter("count"); qualityField = new FieldAdapter("quality"); try { defaultValue = binding.createDefault(); } catch (BindingException e) { throw new RuntimeBindingException( e ); } } public ValueBand(Binding sampleBinding, Object sample) { this( sampleBinding ); this.sample = sample; } public void reset() { try { binding.readFrom(binding, defaultValue, sample); } catch (BindingException e) { throw new RuntimeBindingException( e ); } } public Binding getBinding() { return binding; } /** * Get the internal sample instance * * @return the internal sample instance */ public Object getSample() { return sample; } /** * Change the internal sample instance * * @param sample */ public void setSample(Object sample) { this.sample = sample; } /** * Write to the internal sample instance * * @param sample */ public void writeSample(Object sample) { try { binding.readFrom(binding, sample, this.sample); } catch (BindingException e) { throw new RuntimeBindingException(e); } } /** * Write to the internal sample instance * * @param binding * @param sample */ public void writeSample(Binding binding, Object sample) { try { binding.readFrom(binding, sample, this.sample); } catch (BindingException e) { throw new RuntimeBindingException(e); } } /// Time /// public boolean hasTime() { return timeField.isEnabled(); } public Binding getTimeBinding() { return timeField.binding; } public Object getTime() throws HistoryException { return timeField.getValue(); } public Object getTime(Binding b) throws HistoryException { return timeField.getValue(b); } public double getTimeDouble() throws HistoryException { return timeField.getDoubleValue(); } public void setTime(Object v) throws HistoryException { timeField.setValue(v); } public void setTime(Binding binding, Object v) throws HistoryException { timeField.setValue(binding, v); } /// EndTime /// public boolean hasEndTime() { return endTimeField.isEnabled(); } public Binding getEndTimeBinding() { return endTimeField.binding; } public Object getEndTime() throws HistoryException { return endTimeField.getValue(); } public Object getEndTime(Binding b) throws HistoryException { return endTimeField.getValue(b); } public double getEndTimeDouble() throws HistoryException { return endTimeField.getDoubleValue(); } public void setEndTime(Object v) throws HistoryException { endTimeField.setValue(v); } public void setEndTime(Binding binding, Object v) throws HistoryException { endTimeField.setValue(binding, v); } /// Value /// public boolean hasValue() { return valueField.isEnabled(); } public Binding getValueBinding() { return valueField.binding; } public Object getValue() throws HistoryException { return valueField.getValue(); } public Object getValue(Binding b) throws HistoryException { return valueField.getValue(b); } public double getValueDouble() throws HistoryException { return valueField.getDoubleValue(); } public Object getPossibleValue() throws HistoryException { if ( isNullValue() ) return null; return valueField.getValue(); } public Object getPossibleValue(Binding b) throws HistoryException { if ( isNullValue() ) return null; return valueField.getValue(b); } public Double getPossibleValueDouble() throws HistoryException { if ( isNullValue() ) return null; return valueField.getDoubleValue(); } public boolean getValueBoolean() throws HistoryException { return valueField.getBooleanValue(); } public void setValue(Object v) throws HistoryException { valueField.setValue(v); } public void setValue(Binding binding, Object v) throws HistoryException { valueField.setValue(binding, v); } /// LastValue /// public boolean hasLastValue() { return lastValueField.isEnabled(); } public Binding getLastValueBinding() { return lastValueField.binding; } public Object getLastValue() throws HistoryException { return lastValueField.getValue(); } public Object getLastValue(Binding b) throws HistoryException { return lastValueField.getValue(b); } public void setLastValue(Object v) throws HistoryException { lastValueField.setValue(v); } public void setLastValue(Binding binding, Object v) throws HistoryException { lastValueField.setValue(binding, v); } /// Avg /// public boolean hasAvg() { return avgField.isEnabled(); } public Binding getAvgBinding() { return avgField.binding; } public Object getAvg() throws HistoryException { return avgField.getValue(); } public Object getAvg(Binding b) throws HistoryException { return avgField.getValue(b); } public double getAvgDouble() throws HistoryException { return avgField.getDoubleValue(); } public void setAvg(Object v) throws HistoryException { avgField.setValue(v); } public void setAvg(Binding binding, Object v) throws HistoryException { avgField.setValue(binding, v); } /// Median /// public boolean hasMedian() { return medianField.isEnabled(); } public Binding getMedianBinding() { return medianField.binding; } public Object getMedian() throws HistoryException { return medianField.getValue(); } public Object getMedian(Binding b) throws HistoryException { return medianField.getValue(b); } public double getMedianDouble() throws HistoryException { return medianField.getDoubleValue(); } public void setMedian(Object v) throws HistoryException { medianField.setValue(v); } public void setMedian(Binding binding, Object v) throws HistoryException { medianField.setValue(binding, v); } /// Min /// public boolean hasMin() { return minField.isEnabled(); } public Binding getMinBinding() { return minField.binding; } public Object getMin() throws HistoryException { return minField.getValue(); } public Object getMin(Binding b) throws HistoryException { return minField.getValue(b); } public double getMinDouble() throws HistoryException { return minField.getDoubleValue(); } public void setMin(Object v) throws HistoryException { minField.setValue(v); } public void setMin(Binding binding, Object v) throws HistoryException { minField.setValue(binding, v); } /// Max /// public boolean hasMax() { return maxField.isEnabled(); } public Binding getMaxBinding() { return maxField.binding; } public Object getMax() throws HistoryException { return maxField.getValue(); } public Object getMax(Binding b) throws HistoryException { return maxField.getValue(b); } public double getMaxDouble() throws HistoryException { return maxField.getDoubleValue(); } public void setMax(Object v) throws HistoryException { maxField.setValue(v); } public void setMax(Binding binding, Object v) throws HistoryException { maxField.setValue(binding, v); } /// Count /// public boolean hasCount() { return countField.isEnabled(); } public NumberBinding getCountBinding() { return (NumberBinding) countField.binding; } public int getCount() throws HistoryException { Integer i = (Integer) countField.getValue( Bindings.INTEGER ); return i == null ? 0 : i; } public Object getCount(Binding b) throws HistoryException { return countField.getValue(b); } public void setCount(int v) throws HistoryException { countField.setValue(Bindings.INTEGER, v); } /// Quality /// public boolean hasQuality() { return qualityField.isEnabled(); } public Binding getQualityBinding() { return qualityField.binding; } public Object getQuality() throws HistoryException { return qualityField.getValue(); } public Object getQuality(Binding b) throws HistoryException { return qualityField.getValue(b); } public void setQuality(Object v) throws HistoryException { qualityField.setValue(v); } public void setQuality(Binding binding, Object v) throws HistoryException { qualityField.setValue(binding, v); } /** * Value band is a region of one or more samples. * This method returns true, if value band is expressed with a single sample. * * If value band is expressed with two samples, there is start and end * sample. The format is typicaly simple (time, value). * * @return true if it can represent more than one sample */ public boolean isRanged() { return endTimeField.isEnabled(); } public boolean isValidValue() throws HistoryException { return !isNanSample() && !isNullValue(); } /** * Return true, if this sample * * @return true if the value is Not-a-number */ public boolean isNanSample() { try { if (valueField.binding instanceof DoubleBinding) { DoubleBinding db = (DoubleBinding) valueField.binding; double d = db.getValue_( binding.getComponent(sample, valueField.index) ); return Double.isNaN( d ); } if (valueField.binding instanceof FloatBinding) { FloatBinding db = (FloatBinding) valueField.binding; float d = db.getValue_( binding.getComponent(sample, valueField.index) ); return Float.isNaN( d ); } return false; } catch (BindingException e) { return false; } } /** * Returns true, if this sample format supports a way to express * discontinuation regions. * * @return true if can support discontinuation */ public boolean supportsNullValue() { return qualityField.isEnabled(); } /** * Marks this sample as discontinuation sample * @throws HistoryException */ public void setValueNull() throws HistoryException { qualityField.setValue(Bindings.BYTE, QUALITY_NOVALUE); } /** * Returns true, if the sample is discontinuation sample * * @return true, if the sample is discontinuation sample * @throws HistoryException */ public boolean isNullValue() throws HistoryException { Byte b = (Byte) qualityField.getValue(Bindings.BYTE); return b == null ? false : b.equals( QUALITY_NOVALUE ); } public boolean isNumericValue() { return valueField.isNumeric(); } @Override public String toString() { try { return binding.toString(sample); } catch (BindingException e) { return e.toString(); } } class FieldAdapter { /** Binding of the field, with possible optionalbinding stripped */ Binding binding; /// Adapter1 is cached adapter for setValue Binding adapter1binding; Adapter adapter1; /// Adapter2 is cached adapter for getValue Binding adapter2binding; Adapter adapter2; /** Field index in sample record */ int index; /** Field name */ String fieldName; public FieldAdapter(String fieldName) { this.fieldName = fieldName; index = ValueBand.this.binding.type().getComponentIndex2( fieldName ); if (index<0) return; this.binding = ValueBand.this.binding.getComponentBinding(index); } public boolean getBooleanValue() throws HistoryException { if (sample == null || index==-1) return false; try { Object value = ValueBand.this.binding.getComponent(sample, index); if (binding instanceof BooleanBinding) { return ValueBand.this.binding.getBoolean(sample, index); // BooleanBinding bb = (BooleanBinding) binding; // return bb.getValue_( value ); } if (binding instanceof ByteBinding) { ByteBinding nb = (ByteBinding) binding; return nb.getValue_( value ) != 0; } if (binding instanceof DoubleBinding) { DoubleBinding nb = (DoubleBinding) binding; return nb.getValue_( value ) != 0.; } if (binding instanceof NumberBinding) { NumberBinding nb = (NumberBinding) binding; return nb.getValue( value ).doubleValue() != 0.; } return false; } catch (BindingException e) { throw new HistoryException( e ); } } public double getDoubleValue() throws HistoryException { if (sample == null || index==-1) return Double.NaN; try { // Read field from record if (binding != Bindings.DOUBLE) { Object result = ValueBand.this.binding.getComponent(sample, index); result = Bindings.adapt(result, binding, Bindings.DOUBLE); return (Double) result; } else { return ValueBand.this.binding.getDouble(sample, index); } } catch (BindingException e) { throw new HistoryException( e ); } catch (AdaptException e) { throw new HistoryException( e ); } } public boolean isEnabled() { return index>=0; } /** * Get the value * * @return value or null, if value is not available * @throws HistoryException */ public Object getValue() throws HistoryException { if (sample == null || index==-1) return null; try { // Read field from record Object result = ValueBand.this.binding.getComponent(sample, index); return result; } catch (BindingException e) { throw new HistoryException( e ); } } /** * Get the value * @param binding * @return value in given binding or null, if value is not available * @throws HistoryException */ public Object getValue(Binding binding) throws HistoryException { if (sample == null || index==-1) return null; try { // Read field from record Object result = ValueBand.this.binding.getComponent(sample, index); // Adapt value if ( binding != this.binding ) { if ( binding != adapter2binding ) { adapter2 = Bindings.adapterFactory.getAdapter(this.binding, binding, true, false); adapter2binding = binding; } result = adapter2.adapt( result ); } return result; } catch (BindingException e) { throw new HistoryException( e ); } catch (AdaptException e) { throw new HistoryException( e ); } catch (AdapterConstructionException e) { throw new HistoryException( e ); } } public void setValue(Object object) throws HistoryException { if (sample == null || index==-1) return; setValue(binding, object); } public void setValue(Binding binding, Object object) throws HistoryException { if (sample == null || index==-1) return; try { // Adapt value if (this.binding != binding) { // Create new adapter if (binding != adapter1binding) { adapter1 = Bindings.adapterFactory.getAdapter(binding, this.binding, true, !binding.isImmutable()); adapter1binding = binding; } // Adapt object = adapter1.adapt(object); } // Write value ValueBand.this.binding.setComponent(sample, index, object); } catch (AdaptException e) { throw new HistoryException( e ); } catch (BindingException e) { throw new HistoryException( e ); } catch (AdapterConstructionException e) { throw new HistoryException( e ); } } public boolean isNumeric() { return binding instanceof NumberBinding; } } }