1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.history.util;
\r
14 import org.simantics.databoard.Bindings;
\r
15 import org.simantics.databoard.adapter.AdaptException;
\r
16 import org.simantics.databoard.adapter.Adapter;
\r
17 import org.simantics.databoard.adapter.AdapterConstructionException;
\r
18 import org.simantics.databoard.binding.Binding;
\r
19 import org.simantics.databoard.binding.BooleanBinding;
\r
20 import org.simantics.databoard.binding.ByteBinding;
\r
21 import org.simantics.databoard.binding.DoubleBinding;
\r
22 import org.simantics.databoard.binding.FloatBinding;
\r
23 import org.simantics.databoard.binding.NumberBinding;
\r
24 import org.simantics.databoard.binding.RecordBinding;
\r
25 import org.simantics.databoard.binding.error.BindingException;
\r
26 import org.simantics.databoard.binding.error.RuntimeBindingException;
\r
27 import org.simantics.databoard.type.RecordType;
\r
28 import org.simantics.history.HistoryException;
\r
31 * Value band is an utility class intended for reading and writing to samples.
\r
32 * There are many different formats of samples, their fields, order and datatypes vary.
\r
34 * @author toni.kalajainen
\r
36 public class ValueBand {
\r
38 public static final Byte QUALITY_GOOD = Byte.valueOf( (byte) 0 );
\r
39 public static final Byte QUALITY_NOVALUE = Byte.valueOf( (byte) -1 );
\r
41 protected RecordBinding binding;
\r
42 protected RecordType type;
\r
43 protected Object sample;
\r
45 /** Field value cloners */
\r
46 FieldAdapter timeField, endTimeField, valueField, lastValueField, avgField, medianField, minField, maxField, countField, qualityField;
\r
48 Object defaultValue;
\r
50 public ValueBand(Binding sampleBinding)
\r
52 if ( sampleBinding instanceof RecordBinding == false ) {
\r
53 throw new IllegalArgumentException();
\r
56 this.binding = (RecordBinding) sampleBinding;
\r
57 type = this.binding.type();
\r
59 timeField = new FieldAdapter("time");
\r
60 endTimeField = new FieldAdapter("endTime");
\r
61 valueField = new FieldAdapter("value");
\r
62 lastValueField = new FieldAdapter("lastValue");
\r
63 avgField = new FieldAdapter("avg");
\r
64 medianField = new FieldAdapter("median");
\r
65 minField = new FieldAdapter("min");
\r
66 maxField = new FieldAdapter("max");
\r
67 countField = new FieldAdapter("count");
\r
68 qualityField = new FieldAdapter("quality");
\r
71 defaultValue = binding.createDefault();
\r
72 } catch (BindingException e) {
\r
73 throw new RuntimeBindingException( e );
\r
77 public ValueBand(Binding sampleBinding, Object sample)
\r
79 this( sampleBinding );
\r
80 this.sample = sample;
\r
87 binding.readFrom(binding, defaultValue, sample);
\r
88 } catch (BindingException e) {
\r
89 throw new RuntimeBindingException( e );
\r
93 public Binding getBinding()
\r
99 * Get the internal sample instance
\r
101 * @return the internal sample instance
\r
103 public Object getSample() {
\r
108 * Change the internal sample instance
\r
112 public void setSample(Object sample) {
\r
113 this.sample = sample;
\r
117 * Write to the internal sample instance
\r
121 public void writeSample(Object sample) {
\r
123 binding.readFrom(binding, sample, this.sample);
\r
124 } catch (BindingException e) {
\r
125 throw new RuntimeBindingException(e);
\r
130 * Write to the internal sample instance
\r
135 public void writeSample(Binding binding, Object sample) {
\r
137 binding.readFrom(binding, sample, this.sample);
\r
138 } catch (BindingException e) {
\r
139 throw new RuntimeBindingException(e);
\r
145 public boolean hasTime() {
\r
146 return timeField.isEnabled();
\r
149 public Binding getTimeBinding() {
\r
150 return timeField.binding;
\r
153 public Object getTime() throws HistoryException {
\r
154 return timeField.getValue();
\r
157 public Object getTime(Binding b) throws HistoryException {
\r
158 return timeField.getValue(b);
\r
161 public double getTimeDouble() throws HistoryException {
\r
162 return timeField.getDoubleValue();
\r
165 public void setTime(Object v) throws HistoryException {
\r
166 timeField.setValue(v);
\r
169 public void setTime(Binding binding, Object v) throws HistoryException
\r
171 timeField.setValue(binding, v);
\r
175 public boolean hasEndTime() {
\r
176 return endTimeField.isEnabled();
\r
179 public Binding getEndTimeBinding() {
\r
180 return endTimeField.binding;
\r
183 public Object getEndTime() throws HistoryException {
\r
184 return endTimeField.getValue();
\r
187 public Object getEndTime(Binding b) throws HistoryException {
\r
188 return endTimeField.getValue(b);
\r
191 public double getEndTimeDouble() throws HistoryException {
\r
192 return endTimeField.getDoubleValue();
\r
196 public void setEndTime(Object v) throws HistoryException {
\r
197 endTimeField.setValue(v);
\r
200 public void setEndTime(Binding binding, Object v) throws HistoryException
\r
202 endTimeField.setValue(binding, v);
\r
206 public boolean hasValue() {
\r
207 return valueField.isEnabled();
\r
210 public Binding getValueBinding() {
\r
211 return valueField.binding;
\r
214 public Object getValue() throws HistoryException {
\r
215 return valueField.getValue();
\r
218 public Object getValue(Binding b) throws HistoryException {
\r
219 return valueField.getValue(b);
\r
222 public double getValueDouble() throws HistoryException {
\r
223 return valueField.getDoubleValue();
\r
226 public Object getPossibleValue() throws HistoryException {
\r
227 if ( isNullValue() ) return null;
\r
228 return valueField.getValue();
\r
231 public Object getPossibleValue(Binding b) throws HistoryException {
\r
232 if ( isNullValue() ) return null;
\r
233 return valueField.getValue(b);
\r
236 public Double getPossibleValueDouble() throws HistoryException {
\r
237 if ( isNullValue() ) return null;
\r
238 return valueField.getDoubleValue();
\r
242 public boolean getValueBoolean() throws HistoryException {
\r
243 return valueField.getBooleanValue();
\r
246 public void setValue(Object v) throws HistoryException {
\r
247 valueField.setValue(v);
\r
250 public void setValue(Binding binding, Object v) throws HistoryException
\r
252 valueField.setValue(binding, v);
\r
256 public boolean hasLastValue() {
\r
257 return lastValueField.isEnabled();
\r
260 public Binding getLastValueBinding() {
\r
261 return lastValueField.binding;
\r
264 public Object getLastValue() throws HistoryException {
\r
265 return lastValueField.getValue();
\r
268 public Object getLastValue(Binding b) throws HistoryException {
\r
269 return lastValueField.getValue(b);
\r
272 public void setLastValue(Object v) throws HistoryException {
\r
273 lastValueField.setValue(v);
\r
276 public void setLastValue(Binding binding, Object v) throws HistoryException {
\r
277 lastValueField.setValue(binding, v);
\r
281 public boolean hasAvg() {
\r
282 return avgField.isEnabled();
\r
285 public Binding getAvgBinding() {
\r
286 return avgField.binding;
\r
289 public Object getAvg() throws HistoryException {
\r
290 return avgField.getValue();
\r
293 public Object getAvg(Binding b) throws HistoryException {
\r
294 return avgField.getValue(b);
\r
297 public double getAvgDouble() throws HistoryException {
\r
298 return avgField.getDoubleValue();
\r
301 public void setAvg(Object v) throws HistoryException {
\r
302 avgField.setValue(v);
\r
305 public void setAvg(Binding binding, Object v) throws HistoryException
\r
307 avgField.setValue(binding, v);
\r
311 public boolean hasMedian() {
\r
312 return medianField.isEnabled();
\r
315 public Binding getMedianBinding() {
\r
316 return medianField.binding;
\r
319 public Object getMedian() throws HistoryException {
\r
320 return medianField.getValue();
\r
323 public Object getMedian(Binding b) throws HistoryException {
\r
324 return medianField.getValue(b);
\r
327 public double getMedianDouble() throws HistoryException {
\r
328 return medianField.getDoubleValue();
\r
331 public void setMedian(Object v) throws HistoryException {
\r
332 medianField.setValue(v);
\r
335 public void setMedian(Binding binding, Object v) throws HistoryException
\r
337 medianField.setValue(binding, v);
\r
341 public boolean hasMin() {
\r
342 return minField.isEnabled();
\r
345 public Binding getMinBinding() {
\r
346 return minField.binding;
\r
349 public Object getMin() throws HistoryException {
\r
350 return minField.getValue();
\r
353 public Object getMin(Binding b) throws HistoryException {
\r
354 return minField.getValue(b);
\r
357 public double getMinDouble() throws HistoryException {
\r
358 return minField.getDoubleValue();
\r
361 public void setMin(Object v) throws HistoryException {
\r
362 minField.setValue(v);
\r
365 public void setMin(Binding binding, Object v) throws HistoryException
\r
367 minField.setValue(binding, v);
\r
371 public boolean hasMax() {
\r
372 return maxField.isEnabled();
\r
375 public Binding getMaxBinding() {
\r
376 return maxField.binding;
\r
379 public Object getMax() throws HistoryException {
\r
380 return maxField.getValue();
\r
383 public Object getMax(Binding b) throws HistoryException {
\r
384 return maxField.getValue(b);
\r
387 public double getMaxDouble() throws HistoryException {
\r
388 return maxField.getDoubleValue();
\r
391 public void setMax(Object v) throws HistoryException {
\r
392 maxField.setValue(v);
\r
395 public void setMax(Binding binding, Object v) throws HistoryException
\r
397 maxField.setValue(binding, v);
\r
402 public boolean hasCount() {
\r
403 return countField.isEnabled();
\r
406 public NumberBinding getCountBinding() {
\r
407 return (NumberBinding) countField.binding;
\r
410 public int getCount() throws HistoryException {
\r
411 Integer i = (Integer) countField.getValue( Bindings.INTEGER );
\r
412 return i == null ? 0 : i;
\r
415 public Object getCount(Binding b) throws HistoryException {
\r
416 return countField.getValue(b);
\r
419 public void setCount(int v) throws HistoryException {
\r
420 countField.setValue(Bindings.INTEGER, v);
\r
424 public boolean hasQuality() {
\r
425 return qualityField.isEnabled();
\r
428 public Binding getQualityBinding() {
\r
429 return qualityField.binding;
\r
432 public Object getQuality() throws HistoryException {
\r
433 return qualityField.getValue();
\r
436 public Object getQuality(Binding b) throws HistoryException {
\r
437 return qualityField.getValue(b);
\r
440 public void setQuality(Object v) throws HistoryException {
\r
441 qualityField.setValue(v);
\r
444 public void setQuality(Binding binding, Object v) throws HistoryException
\r
446 qualityField.setValue(binding, v);
\r
451 * Value band is a region of one or more samples.
\r
452 * This method returns true, if value band is expressed with a single sample.
\r
454 * If value band is expressed with two samples, there is start and end
\r
455 * sample. The format is typicaly simple (time, value).
\r
457 * @return true if it can represent more than one sample
\r
459 public boolean isRanged()
\r
461 return endTimeField.isEnabled();
\r
464 public boolean isValidValue() throws HistoryException {
\r
465 return !isNanSample() && !isNullValue();
\r
469 * Return true, if this sample
\r
471 * @return true if the value is Not-a-number
\r
473 public boolean isNanSample() {
\r
475 if (valueField.binding instanceof DoubleBinding) {
\r
476 DoubleBinding db = (DoubleBinding) valueField.binding;
\r
477 double d = db.getValue_( binding.getComponent(sample, valueField.index) );
\r
478 return Double.isNaN( d );
\r
480 if (valueField.binding instanceof FloatBinding) {
\r
481 FloatBinding db = (FloatBinding) valueField.binding;
\r
482 float d = db.getValue_( binding.getComponent(sample, valueField.index) );
\r
483 return Float.isNaN( d );
\r
486 } catch (BindingException e) {
\r
492 * Returns true, if this sample format supports a way to express
\r
493 * discontinuation regions.
\r
495 * @return true if can support discontinuation
\r
497 public boolean supportsNullValue() {
\r
498 return qualityField.isEnabled();
\r
502 * Marks this sample as discontinuation sample
\r
503 * @throws HistoryException
\r
505 public void setValueNull() throws HistoryException {
\r
506 qualityField.setValue(Bindings.BYTE, QUALITY_NOVALUE);
\r
510 * Returns true, if the sample is discontinuation sample
\r
512 * @return true, if the sample is discontinuation sample
\r
513 * @throws HistoryException
\r
515 public boolean isNullValue() throws HistoryException {
\r
516 Byte b = (Byte) qualityField.getValue(Bindings.BYTE);
\r
517 return b == null ? false : b.equals( QUALITY_NOVALUE );
\r
520 public boolean isNumericValue() {
\r
521 return valueField.isNumeric();
\r
525 public String toString() {
\r
527 return binding.toString(sample);
\r
528 } catch (BindingException e) {
\r
529 return e.toString();
\r
533 class FieldAdapter {
\r
534 /** Binding of the field, with possible optionalbinding stripped */
\r
537 /// Adapter1 is cached adapter for setValue
\r
538 Binding adapter1binding;
\r
541 /// Adapter2 is cached adapter for getValue
\r
542 Binding adapter2binding;
\r
545 /** Field index in sample record */
\r
551 public FieldAdapter(String fieldName) {
\r
552 this.fieldName = fieldName;
\r
553 index = ValueBand.this.binding.type().getComponentIndex2( fieldName );
\r
554 if (index<0) return;
\r
556 this.binding = ValueBand.this.binding.getComponentBinding(index);
\r
559 public boolean getBooleanValue() throws HistoryException {
\r
560 if (sample == null || index==-1) return false;
\r
562 Object value = ValueBand.this.binding.getComponent(sample, index);
\r
563 if (binding instanceof BooleanBinding) {
\r
564 return ValueBand.this.binding.getBoolean(sample, index);
\r
565 // BooleanBinding bb = (BooleanBinding) binding;
\r
566 // return bb.getValue_( value );
\r
568 if (binding instanceof ByteBinding) {
\r
569 ByteBinding nb = (ByteBinding) binding;
\r
570 return nb.getValue_( value ) != 0;
\r
572 if (binding instanceof DoubleBinding) {
\r
573 DoubleBinding nb = (DoubleBinding) binding;
\r
574 return nb.getValue_( value ) != 0.;
\r
576 if (binding instanceof NumberBinding) {
\r
577 NumberBinding nb = (NumberBinding) binding;
\r
578 return nb.getValue( value ).doubleValue() != 0.;
\r
581 } catch (BindingException e) {
\r
582 throw new HistoryException( e );
\r
586 public double getDoubleValue() throws HistoryException {
\r
587 if (sample == null || index==-1) return Double.NaN;
\r
589 // Read field from record
\r
590 if (binding != Bindings.DOUBLE) {
\r
591 Object result = ValueBand.this.binding.getComponent(sample, index);
\r
592 result = Bindings.adapt(result, binding, Bindings.DOUBLE);
\r
593 return (Double) result;
\r
595 return ValueBand.this.binding.getDouble(sample, index);
\r
597 } catch (BindingException e) {
\r
598 throw new HistoryException( e );
\r
599 } catch (AdaptException e) {
\r
600 throw new HistoryException( e );
\r
604 public boolean isEnabled() {
\r
611 * @return value or null, if value is not available
\r
612 * @throws HistoryException
\r
614 public Object getValue() throws HistoryException {
\r
615 if (sample == null || index==-1) return null;
\r
617 // Read field from record
\r
618 Object result = ValueBand.this.binding.getComponent(sample, index);
\r
621 } catch (BindingException e) {
\r
622 throw new HistoryException( e );
\r
629 * @return value in given binding or null, if value is not available
\r
630 * @throws HistoryException
\r
632 public Object getValue(Binding binding) throws HistoryException {
\r
633 if (sample == null || index==-1) return null;
\r
635 // Read field from record
\r
636 Object result = ValueBand.this.binding.getComponent(sample, index);
\r
639 if ( binding != this.binding ) {
\r
640 if ( binding != adapter2binding ) {
\r
641 adapter2 = Bindings.adapterFactory.getAdapter(this.binding, binding, true, false);
\r
642 adapter2binding = binding;
\r
644 result = adapter2.adapt( result );
\r
648 } catch (BindingException e) {
\r
649 throw new HistoryException( e );
\r
650 } catch (AdaptException e) {
\r
651 throw new HistoryException( e );
\r
652 } catch (AdapterConstructionException e) {
\r
653 throw new HistoryException( e );
\r
657 public void setValue(Object object) throws HistoryException {
\r
658 if (sample == null || index==-1) return;
\r
659 setValue(binding, object);
\r
662 public void setValue(Binding binding, Object object) throws HistoryException {
\r
663 if (sample == null || index==-1) return;
\r
667 if (this.binding != binding) {
\r
668 // Create new adapter
\r
669 if (binding != adapter1binding) {
\r
670 adapter1 = Bindings.adapterFactory.getAdapter(binding, this.binding, true, !binding.isImmutable());
\r
671 adapter1binding = binding;
\r
675 object = adapter1.adapt(object);
\r
679 ValueBand.this.binding.setComponent(sample, index, object);
\r
681 } catch (AdaptException e) {
\r
682 throw new HistoryException( e );
\r
683 } catch (BindingException e) {
\r
684 throw new HistoryException( e );
\r
685 } catch (AdapterConstructionException e) {
\r
686 throw new HistoryException( e );
\r
690 public boolean isNumeric() {
\r
691 return binding instanceof NumberBinding;
\r