]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.trend/src/org/simantics/trend/impl/ItemNode.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.trend / src / org / simantics / trend / impl / ItemNode.java
diff --git a/bundles/org.simantics.trend/src/org/simantics/trend/impl/ItemNode.java b/bundles/org.simantics.trend/src/org/simantics/trend/impl/ItemNode.java
new file mode 100644 (file)
index 0000000..f57997e
--- /dev/null
@@ -0,0 +1,688 @@
+/*******************************************************************************\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.trend.impl;\r
+\r
+import java.awt.AlphaComposite;\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.Composite;\r
+import java.awt.Graphics2D;\r
+import java.awt.RenderingHints;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.Arrays;\r
+import java.util.List;\r
+import java.util.TreeSet;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.accessor.StreamAccessor;\r
+import org.simantics.databoard.accessor.error.AccessorException;\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.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.databoard.type.BooleanType;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.NumberType;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.util.Bean;\r
+import org.simantics.history.HistoryException;\r
+import org.simantics.history.HistoryItem;\r
+import org.simantics.history.HistoryManager;\r
+import org.simantics.history.ItemManager;\r
+import org.simantics.history.util.Stream;\r
+import org.simantics.history.util.ValueBand;\r
+import org.simantics.history.util.subscription.SamplingFormat;\r
+import org.simantics.scenegraph.g2d.G2DNode;\r
+import org.simantics.trend.configuration.LineQuality;\r
+import org.simantics.trend.configuration.Scale;\r
+import org.simantics.trend.configuration.TrendItem;\r
+import org.simantics.trend.configuration.TrendItem.DrawMode;\r
+import org.simantics.trend.configuration.TrendItem.Renderer;\r
+import org.simantics.trend.util.KvikDeviationBuilder;\r
+\r
+/**\r
+ * Data node for a TrendItem\r
+ * \r
+ * @author toni.kalajainen\r
+ */\r
+public class ItemNode extends G2DNode implements TrendLayout {\r
+\r
+       private static final long serialVersionUID = -4741446944761752871L;\r
+\r
+       public static final AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .61f);\r
+\r
+       public TrendItem item;\r
+       VertRuler ruler;\r
+       TrendNode trendNode;\r
+\r
+       /** History Items */\r
+       Bean historyItems[];\r
+       \r
+       /** A list of items that is ordered in by interval value, starting from the lowest interval */\r
+       Bean[] renderableItems = new Bean[0];\r
+       \r
+       /** Format for the minmax stream, or null */  \r
+       Bean minmaxItem;\r
+       \r
+       /** Known item min->max value range, and from->end time range */\r
+       double from=Double.NaN, end=Double.NaN, min=0, max=1;\r
+\r
+       BasicStroke stroke;\r
+       Color color;\r
+       \r
+       Stream openStream;\r
+       StreamAccessor openStreamAccessor;\r
+       Bean openStreamItem;\r
+       \r
+       Logger log = Logger.getLogger( this.getClass().getName() );     \r
+       \r
+       boolean disabled = false;\r
+       Point2D.Double pt = new Point2D.Double();\r
+       Point2D.Double pt2 = new Point2D.Double();\r
+       \r
+       // Cached shapes\r
+       KvikDeviationBuilder dev = new KvikDeviationBuilder();\r
+       Path2D.Double line = new Path2D.Double();\r
+       \r
+       /**\r
+        * Set trend item and initialize history\r
+        * @param ti\r
+        * @param items all items in history\r
+        */\r
+       public void setTrendItem(TrendItem ti, ItemManager items) {\r
+               if (openStream!=null) {\r
+                       openStream.close();\r
+                       openStream = null;\r
+                       openStreamAccessor = null;\r
+                       openStreamItem = null;\r
+               }\r
+               this.item = ti;\r
+               this.minmaxItem = null;\r
+               this.renderableItems = new HistoryItem[0];\r
+               disabled = true;\r
+               if (ti==null) return;\r
+               \r
+               try {\r
+                       List<Bean> trendItems = items.search("groupId", ti.groupId, "groupItemId", ti.groupItemId, "variableId", ti.variableId);\r
+                       this.historyItems = trendItems.toArray( new Bean[trendItems.size()] );\r
+                       Arrays.sort( this.historyItems, SamplingFormat.INTERVAL_COMPARATOR );\r
+                       \r
+                       // Read renderable formats, and minmax format\r
+                       TreeSet<Bean> streamFormats = new TreeSet<Bean>( SamplingFormat.INTERVAL_COMPARATOR );\r
+                       for (Bean item : trendItems) {\r
+                               SamplingFormat format = new SamplingFormat();\r
+                               format.readAvailableFields(item);\r
+                               RecordType rt = (RecordType) format.format;\r
+                               Boolean enabled = (Boolean) item.getField("enabled");\r
+                               if (!enabled) continue;\r
+                                       \r
+                               boolean isMinMaxFormat = format.interval==Double.MAX_VALUE &&\r
+                                               format.deadband==Double.MAX_VALUE &&\r
+                                               rt.getComponentIndex2("min")>=0 &&\r
+                                               rt.getComponentIndex2("max")>=0;\r
+       \r
+                               if (isMinMaxFormat) {\r
+                                       this.minmaxItem = item;\r
+                               } else {\r
+                                       streamFormats.add(item);\r
+                               }                                       \r
+                       }\r
+                       if (streamFormats.isEmpty()) return;\r
+                       \r
+                       renderableItems = streamFormats.toArray( new Bean[streamFormats.size()] );\r
+                       disabled = false;\r
+               } catch (BindingException e) {\r
+                       throw new RuntimeException( e );\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Draw to graphics context as time,value pairs are.\r
+        * \r
+        * Phases, 0-Init data, 1-Draw deviation, 2-Draw line, 3-Cleanup \r
+        * \r
+        * @param g\r
+        * @param phase \r
+        */\r
+       public void draw(Graphics2D g, int phase, boolean bold) {\r
+               boolean devAndLine = \r
+                               item.drawMode==DrawMode.DeviationAndAverage || \r
+                               item.drawMode==DrawMode.DeviationAndLine || \r
+                               item.drawMode==DrawMode.DeviationAndSample;\r
+               \r
+               // Draw deviation\r
+               Object newQuality = getTrendNode().printing||getTrendNode().quality.lineQuality==LineQuality.Antialias?RenderingHints.VALUE_ANTIALIAS_ON:RenderingHints.VALUE_ANTIALIAS_OFF;\r
+               if (phase == 1) {                       \r
+                       g.setColor(color);\r
+                       if (!dev.isEmpty()) {\r
+                               Object old = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);\r
+                               g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, newQuality);\r
+\r
+                               if (item.renderer == Renderer.Binary) {\r
+                                       dev.drawRectangles(g);\r
+                               } \r
+                               \r
+                               if (item.renderer == Renderer.Analog) {\r
+                                       if (devAndLine) {\r
+                                               // Draw opaque\r
+                                               Composite c = g.getComposite();\r
+                                               g.setComposite( composite );\r
+                                               dev.drawRectangles(g);\r
+                                               g.setComposite( c );\r
+                                       } else if (item.drawMode == DrawMode.Deviation) {\r
+                                               // Draw solid\r
+                                               dev.drawRectangles(g);\r
+                                       }\r
+                               }\r
+                   g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, old);\r
+                       }                       \r
+               }\r
+                       \r
+               // Draw line\r
+               if (phase == 2) {\r
+                       g.setColor(color);\r
+                       Object old = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);\r
+                       g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, newQuality);\r
+                       g.draw(line);                   \r
+            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, old);\r
+               }\r
+               \r
+       }\r
+       \r
+       private boolean validSample(ValueBand band) throws HistoryException {\r
+               if(band.getTimeDouble() > 1e50) return false;\r
+               if(band.getTimeDouble() > band.getEndTimeDouble()) return false;\r
+//             if(band.getTimeDouble() < 0) return false;\r
+//             if(band.getEndTimeDouble() < 0) return false;\r
+               //if(band.getEndTimeDouble()-band.getTimeDouble() < 1e-9) return false;\r
+               return true;\r
+       }\r
+       \r
+       // Draw state needed for clipping\r
+       double currentX;\r
+       double currentY;\r
+       double minX;\r
+       double maxX;\r
+       \r
+       void moveTo(double x, double y) {\r
+               line.moveTo(x, y);\r
+               currentX = x;\r
+               currentY = y;\r
+       }\r
+\r
+       void lineToOrdered(double x, double y) {\r
+               \r
+               if(x < minX) return;\r
+               if(currentX > maxX) return;\r
+               \r
+               // We have something to draw\r
+               \r
+               // Clip left\r
+               if(currentX < minX) {\r
+                       double ny = currentY + (y-currentY) * (minX-currentX) / (x-currentX);\r
+                       line.moveTo(minX, ny);\r
+                       currentX = minX;\r
+                       currentY = ny;\r
+               }\r
+\r
+               // Right clip\r
+               if(x > maxX) {\r
+                       double ny = currentY + (y-currentY) * (maxX-currentX) / (x-currentX);\r
+                       line.lineTo(maxX, ny);\r
+                       line.moveTo(x, y);              \r
+               } else {\r
+                       line.lineTo(x, y);              \r
+               }\r
+               \r
+       }\r
+       \r
+       void lineTo(double x, double y) {\r
+\r
+               // First assert ordering - if samples are in wrong order draw nothing\r
+               if(currentX <= x) {\r
+                       lineToOrdered(x, y);\r
+               } else {\r
+                       line.moveTo(x, y);\r
+               }\r
+               \r
+               currentX = x;\r
+               currentY = y;\r
+               \r
+       }\r
+       \r
+\r
+       /**\r
+        * This method prepares line and deviation shapes.\r
+        * @param from\r
+        * @param end\r
+        * @param secondsPerPixel\r
+        * @param at \r
+        * @throws HistoryException \r
+        * @throws AccessorException \r
+        */\r
+       public void prepareLine(double from, double end, double pixelsPerSecond, AffineTransform at) throws HistoryException, AccessorException\r
+       {\r
+//             boolean devAndLine = \r
+//                             item.drawMode==DrawMode.DeviationAndAverage || \r
+//                             item.drawMode==DrawMode.DeviationAndLine || \r
+//                             item.drawMode==DrawMode.DeviationAndSample;\r
+//             boolean deviationEnabled = devAndLine || \r
+//                             item.drawMode == DrawMode.Deviation;\r
+\r
+               boolean devAndLine = false;\r
+               boolean deviationEnabled = false;\r
+               \r
+               // Collect data\r
+               dev.reset();\r
+               line.reset();\r
+               Stream s = openStream(pixelsPerSecond);\r
+               if (s==null) return;\r
+               ValueBand vb = new ValueBand(s.sampleBinding, s.sampleBinding.createDefaultUnchecked());\r
+               boolean hasDeviation = vb.hasMin() && vb.hasMax();\r
+               boolean drawDeviation = hasDeviation & deviationEnabled;\r
+               \r
+               Datatype valueType = vb.getValueBinding().type();\r
+               s.reset();\r
+               if ( valueType instanceof BooleanType == false && valueType instanceof NumberType == false ) return;\r
+                               \r
+               int start = s.binarySearch(Bindings.DOUBLE, from);\r
+               int count = s.count();\r
+               if (start<0) start = -2-start;\r
+               if (start<0) start = 0;\r
+                               \r
+               // moveTo, on next draw, if true\r
+               boolean lineNotAttached = true; // =next draw should be move to\r
+               boolean devNotAttached = true;\r
+               \r
+               currentX = Double.NaN;\r
+               currentY = Double.NaN;\r
+\r
+               pt.setLocation(from, 0);\r
+               at.transform(pt, pt);\r
+               minX = pt.x;\r
+               \r
+               pt.setLocation(end, 0);\r
+               at.transform(pt, pt);\r
+               maxX = pt.x;\r
+                               \r
+               s.reset();\r
+               \r
+               boolean wentOver = false;\r
+               \r
+               for (int i=start; i<count; i++) {\r
+                       // Read sample\r
+                       s.accessor.get(i, s.sampleBinding, vb.getSample());\r
+                       \r
+                       if(!validSample(vb)) {\r
+                               System.err.println("-invalid value band: " + i + "/" + count + ":" + vb);\r
+                               continue;\r
+                       }\r
+                       // Non-continuation point\r
+                       if (vb.hasQuality()) {\r
+                               Byte b = (Byte) vb.getQuality(Bindings.BYTE);\r
+                               boolean noncontinuation = b.equals( ValueBand.QUALITY_NOVALUE ); \r
+                               if ( noncontinuation ) {\r
+                                       lineNotAttached = true;\r
+                                       devNotAttached = true;\r
+                                       continue;\r
+                               }\r
+                       }\r
+                                       \r
+                       // Line\r
+                       double t1 = vb.getTimeDouble();\r
+                       double t2 = vb.hasEndTime() ? vb.getEndTimeDouble() : t1;\r
+                                       \r
+                       // Analog signal\r
+                       if (item.renderer == Renderer.Analog) {\r
+                                               \r
+                               // Add points to line\r
+                               if (item.drawMode == DrawMode.Deviation && hasDeviation) {\r
+                                       // ...\r
+                               } else {\r
+                                       \r
+                                       double yy = Double.NaN;\r
+//                                     boolean showBand = true;\r
+                                       boolean flat = false;\r
+\r
+                                       if( trendNode != null && trendNode.drawSamples ) {\r
+                                               yy = vb.getValueDouble();\r
+                                               flat = true;\r
+                                       } else {\r
+                                               yy = vb.getValueDouble();\r
+                                               // Only render the last band\r
+//                                             if(i < count-1) showBand = false;                                       \r
+                                       }\r
+                                       \r
+//                                     if (item.drawMode == DrawMode.DeviationAndAverage) {\r
+//                                             yy = vb.hasMedian() ? vb.getMedianDouble() : ( vb.hasAvg() ? vb.getAvgDouble() : vb.getValueDouble() );\r
+//                                     } else if (item.drawMode == DrawMode.Average || item.drawMode == DrawMode.DeviationAndAverage) {\r
+//                                             yy = vb.hasAvg() ? vb.getAvgDouble() : vb.getValueDouble();\r
+//                                     } else if (item.drawMode == DrawMode.Line || item.drawMode == DrawMode.DeviationAndLine) {\r
+//                                             yy = vb.getValueDouble();\r
+//                                             // Only render the last band\r
+//                                             if(i < count-1) showBand = false;                                       \r
+//                                     } else /* if (item.drawMode == DrawMode.Sample || item.drawMode == DrawMode.DeviationAndSample) */ {\r
+//                                             yy = vb.getValueDouble();\r
+//                                             flat = true;\r
+//                                     } \r
+                       \r
+                                       if ( Double.isNaN(yy) ) {\r
+                                               lineNotAttached = true;\r
+                                       } else {\r
+//                                             pt.setLocation(t1, yy);\r
+//                                             at.transform(pt, pt);\r
+//                                             if ( t1==t2 ) {\r
+//                                                     if (lineNotAttached) {\r
+//                                                             moveTo(pt.getX(), pt.getY());\r
+//                                                     } else {\r
+//                                                             if(flat) {\r
+//                                                                     \r
+//                                                             } else {\r
+//                                                                     lineTo(pt.getX(), pt.getY());\r
+//                                                             }\r
+//                                                     }\r
+//                                             } else {\r
+                                                       // Static variables may have months data that consists of single value\r
+                                                       // When zoomed in, the single line may be drawn from -1e10, ... 1e10\r
+                                                       // This is too much for Graphics2D, it refuses to draw.\r
+//                                                     if (t1<from) {\r
+//                                                             t1 = from;\r
+//                                                     }\r
+//                                                     if (t2>end) {\r
+//                                                             t2 = end;\r
+//                                                     }\r
+                                                                       \r
+                                                       pt.setLocation(t1, yy);\r
+                                                       at.transform(pt, pt);\r
+                                                       pt2.setLocation(t2, yy);\r
+                                                       at.transform(pt2, pt2);\r
+\r
+                                                       double x1 = pt.x, y1 = pt.y, x2 = pt2.x, y2 = pt2.y;\r
+\r
+                                                       if (lineNotAttached) {\r
+                                                               moveTo(x1, y1);\r
+                                                       } else {\r
+                                                               if(flat) {\r
+                                                                       lineTo(x1, currentY);\r
+                                                                       lineTo(x1, y1);\r
+                                                               } else {\r
+                                                                       lineTo(x1, y1);\r
+                                                               }\r
+                                                       }\r
+\r
+                                                       lineTo(x2, y2);\r
+                                                       \r
+//                                                     if(showBand) {\r
+//                                                             lineTo(x2, y2);\r
+//                                                     }\r
+                                                       \r
+//                                             }\r
+                                               lineNotAttached = false;                                        \r
+                                       }\r
+                               }\r
+                                               \r
+                               if (drawDeviation) {\r
+                                       double min = vb.getMinDouble();\r
+                                       double max = vb.getMaxDouble();\r
+\r
+                                       pt.setLocation(t1, min);\r
+                                       at.transform(pt, pt);\r
+                                       pt2.setLocation(t2, max);\r
+                                       at.transform(pt2, pt2);\r
+                                       double x1 = pt.x;\r
+                                       double x2 = pt2.x;\r
+                                       double y1 = pt.y;\r
+                                       double y2 = pt2.y;\r
+                                                       \r
+                                       double width = x2-x1;\r
+                                       boolean tooWide = devAndLine && (width>2.0);\r
+                                       if (Double.isNaN(min) || Double.isNaN(max) || tooWide || width<=0.0 || y1==y2) {\r
+                                               devNotAttached = true;\r
+                                       } else {\r
+                                               if (devNotAttached) {\r
+                                                       dev.addRectangle(x1, x2, y1, y2);\r
+                                               } else {\r
+                                                       dev.extendRectangle(x1);\r
+                                                       dev.addRectangle(x1, x2, y1, y2);\r
+                                               }\r
+                                               devNotAttached = false;\r
+                                       }\r
+                               }\r
+                       }\r
+                                       \r
+                       // Binary signal\r
+                       if (item.renderer == Renderer.Binary) {\r
+                               byte value = 0;\r
+                               if (vb.getValueBinding() instanceof BooleanBinding) {\r
+                                       value = ((Boolean) vb.getValue(Bindings.BOOLEAN)) ? (byte)0 : (byte)1;\r
+                               } else if (vb.getValueBinding() instanceof ByteBinding) {\r
+                                       value = ((Byte) vb.getValue(Bindings.BYTE));\r
+                               } else if (vb.hasMax()) {\r
+                                       value = (Byte) vb.getMax(Bindings.BYTE);\r
+                               } else {\r
+                                       value = (Byte) vb.getValue(Bindings.BYTE);\r
+                               }\r
+                               pt.setLocation(t1, value==1 ? BINARY[1] : BINARY[0]);\r
+                               at.transform(pt, pt);\r
+                               pt2.setLocation(t2, BINARY[2]);\r
+                               at.transform(pt2, pt2);\r
+                               double x1 = pt.x;\r
+                               double x2 = pt2.x;\r
+                               double y1 = pt.y;\r
+                               double y2 = pt2.y;\r
+                               dev.extendRectangle(x1);\r
+                               dev.addRectangle(x1, x2, y1, y2);\r
+                               devNotAttached = false;                                 \r
+                       }\r
+                                       \r
+                       // Already over => break\r
+                       if(wentOver) break;\r
+                       \r
+                       // Out of range\r
+                       if (t2>=end) {\r
+                               wentOver = true;\r
+                       }\r
+               }       \r
+               \r
+       }\r
+       \r
+       public boolean readMinMaxFromEnd() {\r
+               if (disabled) return false;\r
+               HistoryManager history = getTrendNode().historian;\r
+\r
+               boolean hasVariable = !item.variableId.isEmpty() && !item.groupItemId.isEmpty() && !item.groupId.isEmpty();\r
+               boolean canReadMinMax = minmaxItem != null;\r
+               boolean manualScale = item.scale instanceof Scale.Manual;\r
+               \r
+               if ( !hasVariable ) {\r
+                       min = 0;\r
+                       max = 1;\r
+                       from = Double.NaN;\r
+                       end = Double.NaN;\r
+                       return false;\r
+               }\r
+               \r
+               try {\r
+                       if (canReadMinMax && !manualScale) {            \r
+                               String id = (String) minmaxItem.getFieldUnchecked("id");\r
+                               StreamAccessor sa = history.openStream(id, "r");\r
+                               if ( sa==null ) {\r
+                                       min = 0;\r
+                                       max = 1;\r
+                                       from = Double.NaN;\r
+                                       end = Double.NaN;\r
+                                       return false;\r
+                               } else \r
+                               try {\r
+                                       if (sa.size()==0) return false;\r
+                                       min = Double.MAX_VALUE;\r
+                                       max = -Double.MAX_VALUE;\r
+                                       from = Double.MAX_VALUE;\r
+                                       end = -Double.MAX_VALUE;\r
+                                       for (int i=0; i<sa.size(); i++) {\r
+                                               Binding binding = Bindings.getBinding( sa.type().componentType() );\r
+                                               Object sample = sa.get(i, binding);\r
+                                               ValueBand vb = new ValueBand(binding, sample);\r
+                                               if (!vb.isNullValue() && !vb.isNanSample()) {\r
+                                                       min = Math.min(min, vb.getMinDouble());\r
+                                                       max = Math.max(max, vb.getMaxDouble());\r
+                                               } \r
+                                               if (!vb.isNullValue()) {\r
+                                                       from = Math.min(from, vb.getTimeDouble());\r
+                                                       end = Math.max(end, vb.getEndTimeDouble());\r
+                                               }\r
+                                       }\r
+                                       if ( min==Double.MAX_VALUE || max==-Double.MAX_VALUE) {\r
+                                               min = 0; max = 1.0;\r
+                                       }\r
+                                       if ( from==Double.MAX_VALUE || end==-Double.MAX_VALUE) {\r
+                                               from = 0; end = 100.0;\r
+                                       }\r
+                                       return true;\r
+                               } finally {\r
+                                       try { sa.close(); } catch (AccessorException e) {}\r
+                               } \r
+                       } else {\r
+                               \r
+                               if (manualScale) {\r
+                                       Scale.Manual ms = (Scale.Manual) item.scale;\r
+                                       min = ms.min;\r
+                                       max = ms.max;\r
+                               } \r
+                               \r
+                               // Read from, end from any stream\r
+                               if (openStreamAccessor==null) {\r
+                                       openStream(1);\r
+                               } \r
+                               if (openStreamAccessor!=null){\r
+                                       // Open some stream\r
+                                       StreamAccessor sa = openStreamAccessor;\r
+                                       sa.reset();\r
+                                       int count = sa.size();\r
+                                       if (count>0) {\r
+                                               Binding binding = Bindings.getBinding( sa.type().componentType() );\r
+                                               Object sample = sa.get(0, binding);\r
+                                               ValueBand vb = new ValueBand(binding, sample);\r
+                                               from = vb.getTimeDouble();\r
+                                               sa.get(count-1, binding, sample);\r
+                                               end = vb.hasEndTime() ? vb.getEndTimeDouble() : vb.getTimeDouble();\r
+                                       }\r
+                                       return true;\r
+                               } else {\r
+                                       return false;\r
+                               }\r
+                       }\r
+               } catch (AccessorException e) {\r
+                       log.log(Level.FINE, e.toString(), e);\r
+                       return false;\r
+               } catch (HistoryException e) {\r
+                       log.log(Level.FINE, e.toString(), e);\r
+                       return false;\r
+               }\r
+       }\r
+       \r
+       @Override\r
+       public void cleanup() {\r
+               trendNode = null;\r
+               if (openStreamAccessor != null) {\r
+                       try {\r
+                               openStreamAccessor.close();\r
+                       } catch (AccessorException e) {\r
+                       }\r
+                       openStreamAccessor = null;\r
+                       openStreamItem = null;\r
+               }\r
+               super.cleanup();\r
+       }\r
+       \r
+       Bean getFormat(double pixelsPerSecond) {\r
+               Bean result = null;\r
+               if (renderableItems == null)\r
+                       return null;\r
+               \r
+               for (Bean format : renderableItems)\r
+               {\r
+                       double interval = 0.0;\r
+                       try {\r
+                               interval = format.hasField("interval") ? (Double) format.getFieldUnchecked("interval") : 0.0;\r
+                       } catch (RuntimeBindingException e) {\r
+                       } catch (BindingException e) {\r
+                       }\r
+                       if (Double.isNaN( interval ) || interval<=pixelsPerSecond) {\r
+                               result = format;\r
+                       } else {\r
+                               break;\r
+                       }\r
+               }               \r
+               if (result==null) {\r
+                       if ( renderableItems.length == 0 ) {\r
+                               result = null;\r
+                       } else {\r
+                               result = renderableItems[0];\r
+                       }\r
+               }\r
+               \r
+               return result;\r
+       }\r
+       \r
+       public Stream openStream(double pixelsPerSecond) {\r
+               Bean f = getFormat(pixelsPerSecond);\r
+               if (f==openStreamItem) return openStream;\r
+               \r
+               if (openStream != null) {\r
+                       openStream.close();\r
+                       openStreamAccessor = null;\r
+                       openStreamItem = null;\r
+                       openStream = null;\r
+               }\r
+               \r
+               if (disabled) return null;\r
+               \r
+               if (f!=null) {\r
+                       HistoryManager historian = getTrendNode().historian;\r
+                       try {\r
+                               String id = (String) f.getFieldUnchecked("id");\r
+                               openStreamAccessor = historian.openStream(id, "r");\r
+                               if ( openStreamAccessor!=null ) {\r
+                                       openStream = new Stream(openStreamAccessor);\r
+                                       openStreamItem = f;                     \r
+                               } else {\r
+                                       openStream = null;\r
+                               }\r
+                       } catch (HistoryException e) {\r
+                               log.log(Level.FINE, e.toString(), e);\r
+                       }\r
+               }\r
+               \r
+               return openStream;\r
+       }\r
+       \r
+       @Override\r
+       public void render(Graphics2D g2d) {\r
+       }\r
+\r
+       @Override\r
+       public Rectangle2D getBoundsInLocal() {\r
+               return null;\r
+       }\r
+       \r
+       TrendNode getTrendNode() {\r
+               return (TrendNode) getParent();\r
+       }\r
+       \r
+}\r