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.accessor.ArrayAccessor;
\r
16 import org.simantics.databoard.accessor.CloseableAccessor;
\r
17 import org.simantics.databoard.accessor.StreamAccessor;
\r
18 import org.simantics.databoard.accessor.error.AccessorException;
\r
19 import org.simantics.databoard.adapter.AdaptException;
\r
20 import org.simantics.databoard.binding.Binding;
\r
21 import org.simantics.databoard.binding.NumberBinding;
\r
22 import org.simantics.databoard.binding.RecordBinding;
\r
23 import org.simantics.databoard.binding.error.BindingException;
\r
24 import org.simantics.databoard.type.Datatype;
\r
25 import org.simantics.databoard.type.NumberType;
\r
26 import org.simantics.databoard.type.RecordType;
\r
27 import org.simantics.history.HistoryException;
\r
30 * This utility adds random access (time) to array accessor.
\r
32 * @author toni.kalajainen
\r
34 public class Stream {
\r
36 public ArrayAccessor accessor;
\r
37 public RecordType sampleType;
\r
38 public RecordBinding sampleBinding;
\r
40 public int timeIndex;
\r
41 public Datatype timeType;
\r
42 public Binding timeBinding;
\r
44 public int endTimeIndex;
\r
45 public Datatype endTimeType;
\r
46 public Binding endTimeBinding;
\r
48 public int valueIndex;
\r
49 public Datatype valueType;
\r
50 public Binding valueBinding;
\r
52 public int qualityIndex=-1;
\r
53 public NumberType qualityType;
\r
54 public NumberBinding qualityBinding;
\r
61 public Stream(ArrayAccessor accessor)
\r
63 this.accessor = accessor;
\r
64 this.sampleType = (RecordType) accessor.type().componentType();
\r
65 this.sampleBinding = (RecordBinding) Bindings.getBeanBinding( sampleType );
\r
67 this.valueIndex = sampleType.getComponentIndex2("value");
\r
68 if (valueIndex<0) throw new IllegalArgumentException("Array is not a sample array, value field is missing");
\r
69 this.valueType = sampleType.getComponentType(valueIndex);
\r
70 this.valueBinding = this.sampleBinding.getComponentBinding(valueIndex);
\r
72 this.timeIndex = sampleType.getComponentIndex2("time");
\r
73 if (timeIndex<0) throw new IllegalArgumentException("Array is not a sample array, time field is missing");
\r
74 this.timeType = sampleType.getComponentType(timeIndex);
\r
75 this.timeBinding = this.sampleBinding.getComponentBinding(timeIndex);
\r
77 this.endTimeIndex = sampleType.getComponentIndex2("endTime");
\r
79 this.qualityIndex = sampleType.getComponentIndex2("quality");
\r
80 this.qualityType = qualityIndex>=0?(NumberType)sampleType.getComponentType(qualityIndex):null;
\r
81 this.qualityBinding = qualityType!=null?(NumberBinding)this.sampleBinding.getComponentBinding(qualityIndex):null;
\r
89 public Stream(ArrayAccessor accessor, RecordBinding recordBinding)
\r
91 this.accessor = accessor;
\r
92 this.sampleType = (RecordType) accessor.type().componentType();
\r
93 if (!this.sampleType.equals(recordBinding.type())) throw new IllegalArgumentException("Wrong binding. Got " + recordBinding.type() + ", expected " + this.sampleType);
\r
94 this.sampleBinding = recordBinding;
\r
96 this.valueIndex = sampleType.getComponentIndex2("value");
\r
97 this.timeIndex = sampleType.getComponentIndex2("time");
\r
98 this.endTimeIndex = sampleType.getComponentIndex2("endTime");
\r
100 if (valueIndex<0) throw new IllegalArgumentException("Array is not a sample array, value field is missing");
\r
101 if (timeIndex<0) throw new IllegalArgumentException("Array is not a sample array, time field is missing");
\r
102 //if (endTimeIndex<0) throw new IllegalArgumentException("Array is not a sample array, time field is missing");
\r
104 this.valueType = sampleType.getComponentType(valueIndex);
\r
105 this.timeType = sampleType.getComponentType(timeIndex);
\r
106 this.endTimeType = endTimeIndex>=0 ? sampleType.getComponentType(endTimeIndex) : null;
\r
108 this.valueBinding = this.sampleBinding.getComponentBinding("value");
\r
109 this.timeBinding = this.sampleBinding.getComponentBinding("time");
\r
110 this.endTimeBinding = endTimeIndex>=0 ? this.sampleBinding.getComponentBinding("endTime") : null;
\r
113 public void close() {
\r
114 if (accessor instanceof CloseableAccessor) {
\r
115 CloseableAccessor ca = (CloseableAccessor) accessor;
\r
118 } catch (AccessorException e) {
\r
123 public void reset() {
\r
124 if (accessor instanceof StreamAccessor) {
\r
125 StreamAccessor sa = (StreamAccessor) accessor;
\r
128 } catch (AccessorException e) {
\r
134 * Make a binary search to stream data
\r
137 * @param timeBinding
\r
139 * @return index of the search key, if it is contained in the array
\r
140 * within the specified range;
\r
141 * otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>. The
\r
142 * <i>insertion point</i> is defined as the point at which the
\r
143 * key would be inserted into the array: the index of the first
\r
144 * element in the range greater than the key,
\r
145 * or <tt>toIndex</tt> if all
\r
146 * elements in the range are less than the specified key. Note
\r
147 * that this guarantees that the return value will be >= 0 if
\r
148 * and only if the key is found.
\r
149 * @throws HistoryException
\r
151 public int binarySearch(Binding timeBinding, Object time) throws HistoryException
\r
154 Object time_ = Bindings.adapt(time, timeBinding, this.timeBinding);
\r
157 int toIndex = accessor.size();
\r
159 int ix = binarySearch0(fromIndex, toIndex, time_);
\r
161 } catch (AccessorException e) {
\r
162 throw new HistoryException(e);
\r
163 } catch (BindingException e) {
\r
164 throw new HistoryException(e);
\r
165 } catch (AdaptException e) {
\r
166 throw new HistoryException(e);
\r
170 // Like public version, but without range checks.
\r
171 private int binarySearch0(int fromIndex, int toIndex, Object key) throws AccessorException, BindingException {
\r
172 int low = fromIndex;
\r
173 int high = toIndex - 1;
\r
174 Binding timeBinding = sampleBinding.getComponentBinding(timeIndex);
\r
176 while (low <= high) {
\r
177 int mid = (low + high) >>> 1;
\r
178 Object midSam = accessor.get(mid, sampleBinding);
\r
179 Object midVal = sampleBinding.getComponent(midSam, timeIndex);
\r
180 int cmp = timeBinding.compare(midVal, key);
\r
186 return mid; // key found
\r
188 return -(low + 1); // key not found.
\r
191 public Object getLowerSample(Binding timeBinding, Object time) throws HistoryException
\r
194 int index = binarySearch(timeBinding, time);
\r
196 if (index==0) return null;
\r
198 return accessor.get(index-1, sampleBinding);
\r
201 if (index<0) return null;
\r
202 if (index>=accessor.size()) return null;
\r
203 return accessor.get(index, sampleBinding);
\r
204 } catch (AccessorException e) {
\r
205 throw new HistoryException( e );
\r
210 public Object getFloorSample(Binding timeBinding, Object time) throws HistoryException
\r
213 int index = binarySearch(timeBinding, time);
\r
216 return accessor.get(index, sampleBinding);
\r
218 // The position where the sample would be inserted
\r
220 if (index<0) return null;
\r
221 if (index>=accessor.size()) return null;
\r
222 return accessor.get(index, sampleBinding);
\r
223 } catch (AccessorException e) {
\r
224 throw new HistoryException( e );
\r
228 public Object getSample(Binding timeBinding, Object time) throws HistoryException
\r
231 int pos = binarySearch(timeBinding, time);
\r
233 return accessor.get(pos, sampleBinding);
\r
236 } catch (AccessorException e) {
\r
237 throw new HistoryException(e);
\r
241 public boolean getSample(Binding timeBinding, Object time, Object sample) throws HistoryException
\r
244 int pos = binarySearch(timeBinding, time);
\r
246 accessor.get(pos, sampleBinding, sample);
\r
250 } catch (AccessorException e) {
\r
251 throw new HistoryException(e);
\r
255 public Object getCeilingSample(Binding timeBinding, Object time) throws HistoryException
\r
258 int index = binarySearch(timeBinding, time);
\r
261 return accessor.get(index, sampleBinding);
\r
263 // The position where the sample would be inserted
\r
265 if (index<0) return null;
\r
266 if (index>=accessor.size()) return null;
\r
267 return accessor.get(index, sampleBinding);
\r
268 } catch (AccessorException e) {
\r
269 throw new HistoryException( e );
\r
273 public Object getHigherSample(Binding timeBinding, Object time) throws HistoryException
\r
276 int index = binarySearch(timeBinding, time);
\r
278 int count = accessor.size();
\r
280 index++; // exact match, -> next
\r
282 // The position where the sample would be inserted
\r
285 if (index<0 || index>=count) return null;
\r
286 return accessor.get(index, sampleBinding);
\r
287 } catch (AccessorException e) {
\r
288 throw new HistoryException( e );
\r
292 private Object _getTime(Object sample, Binding timeBinding) throws HistoryException
\r
295 Object time__ = sampleBinding.getComponent( sample, timeIndex );
\r
296 return Bindings.adapt(time__, this.timeBinding, timeBinding);
\r
297 } catch (AdaptException e) {
\r
298 throw new HistoryException(e);
\r
299 } catch (BindingException e) {
\r
300 throw new HistoryException(e);
\r
304 public Object getLowerTime(Binding timeBinding, Object time) throws HistoryException
\r
307 int index = binarySearch(timeBinding, time);
\r
309 if (index==0) return null;
\r
314 if (index<0) return null;
\r
315 if (index>=accessor.size()) return null;
\r
316 return _getTime(accessor.get(index, sampleBinding), timeBinding);
\r
317 } catch (AccessorException e) {
\r
318 throw new HistoryException( e );
\r
323 public Object getFloorTime(Binding timeBinding, Object time) throws HistoryException
\r
326 int index = binarySearch(timeBinding, time);
\r
331 // The position where the sample would be inserted
\r
333 if (index<0) return null;
\r
334 if (index>=accessor.size()) return null;
\r
335 return _getTime( accessor.get(index, sampleBinding), timeBinding );
\r
336 } catch (AccessorException e) {
\r
337 throw new HistoryException( e );
\r
341 public Object getCeilingTime(Binding timeBinding, Object time) throws HistoryException
\r
344 int index = binarySearch(timeBinding, time);
\r
349 // The position where the sample would be inserted
\r
351 if (index<0) return null;
\r
352 if (index>=accessor.size()) return null;
\r
353 return _getTime( accessor.get(index, sampleBinding), timeBinding );
\r
354 } catch (AccessorException e) {
\r
355 throw new HistoryException( e );
\r
359 public Object getHigherTime(Binding timeBinding, Object time) throws HistoryException
\r
362 int index = binarySearch(timeBinding, time);
\r
365 int count = accessor.size();
\r
367 index++; // exact match, -> next
\r
369 // The position where the sample would be inserted
\r
372 if (index<0 || index>=count) return null;
\r
373 return _getTime( accessor.get(index, sampleBinding), timeBinding );
\r
374 } catch (AccessorException e) {
\r
375 throw new HistoryException( e );
\r
380 public Object getValue(Binding timeBinding, Object time, Binding valueBinding) throws HistoryException
\r
383 int pos = binarySearch(timeBinding, time);
\r
385 Object sample = accessor.get(pos, sampleBinding);
\r
386 Object value = sampleBinding.getComponent(sample, valueIndex);
\r
388 if (valueBinding != this.valueBinding) {
\r
389 value = Bindings.adapt(value, this.valueBinding, valueBinding);
\r
394 } catch (AccessorException e) {
\r
395 throw new HistoryException(e);
\r
396 } catch (BindingException e) {
\r
397 throw new HistoryException(e);
\r
398 } catch (AdaptException e) {
\r
399 throw new HistoryException(e);
\r
403 public Object getValue(Binding timeBinding, Object time) throws HistoryException
\r
406 int pos = binarySearch(timeBinding, time);
\r
408 Object sample = accessor.get(pos, sampleBinding);
\r
409 Object value = sampleBinding.getComponent(sample, valueIndex);
\r
413 } catch (AccessorException e) {
\r
414 throw new HistoryException(e);
\r
415 } catch (BindingException e) {
\r
416 throw new HistoryException(e);
\r
420 public Object getQuality(Binding timeBinding, Object time) throws HistoryException
\r
423 int pos = binarySearch(timeBinding, time);
\r
425 Object sample = accessor.get(pos, sampleBinding);
\r
426 Object value = sampleBinding.getComponent(sample, qualityIndex);
\r
430 } catch (AccessorException e) {
\r
431 throw new HistoryException(e);
\r
432 } catch (BindingException e) {
\r
433 throw new HistoryException(e);
\r
438 * Get value if exists, otherwise null
\r
439 * @param timeBinding
\r
441 * @return value or null
\r
442 * @throws HistoryException
\r
444 public Object getPossibleValue(Binding timeBinding, Object time) throws HistoryException
\r
447 int pos = binarySearch(timeBinding, time);
\r
449 Object sample = accessor.get(pos, sampleBinding);
\r
450 if (qualityBinding != null) {
\r
451 Object quality = sampleBinding.getComponent(sample, qualityIndex);
\r
452 if ( !qualityBinding.getValue(quality).equals( ValueBand.QUALITY_GOOD ) ) return null;
\r
454 Object value = sampleBinding.getComponent(sample, valueIndex);
\r
458 } catch (AccessorException e) {
\r
459 throw new HistoryException(e);
\r
460 } catch (BindingException e) {
\r
461 throw new HistoryException(e);
\r
465 public int count() throws HistoryException {
\r
467 return accessor.size();
\r
468 } catch (AccessorException e) {
\r
469 throw new HistoryException(e);
\r
473 public Object getFirstTime(Binding binding) throws HistoryException {
\r
475 if (accessor.size()==0) {
\r
478 Object sample = accessor.get(0, sampleBinding);
\r
479 Object time = sampleBinding.getComponent(sample, timeIndex);
\r
480 if (timeBinding!=binding) time = Bindings.adapt(time, timeBinding, binding);
\r
482 } catch (BindingException e) {
\r
483 throw new HistoryException(e);
\r
484 } catch (AccessorException e) {
\r
485 throw new HistoryException(e);
\r
486 } catch (AdaptException e) {
\r
487 throw new HistoryException(e);
\r
491 public Object getEndTime(Binding binding) throws HistoryException {
\r
493 if (accessor.size()==0) {
\r
496 Object sample = accessor.get(0, sampleBinding);
\r
498 if (endTimeIndex>=0) {
\r
499 Object endtime = sampleBinding.getComponent(sample, endTimeIndex);
\r
500 if (endTimeBinding!=binding) endtime = Bindings.adapt(endtime, endTimeBinding, binding);
\r
503 Object time = sampleBinding.getComponent(sample, timeIndex);
\r
504 if (timeBinding!=binding) time = Bindings.adapt(time, timeBinding, binding);
\r
507 } catch (BindingException e) {
\r
508 throw new HistoryException(e);
\r
509 } catch (AccessorException e) {
\r
510 throw new HistoryException(e);
\r
511 } catch (AdaptException e) {
\r
512 throw new HistoryException(e);
\r
516 public boolean isEmpty() throws HistoryException {
\r
518 return accessor.size() == 0;
\r
519 } catch (AccessorException e) {
\r
520 throw new HistoryException(e);
\r