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