You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by vf...@apache.org on 2016/01/13 18:14:40 UTC
[15/15] incubator-geode git commit: GEODE-628: Rename container
folder to "geode-jvsd". - The jvsd code now sits in the geode-jvsd directory.
- Files "pom.xml", "README.txt" and "runjvsd.sh" updated.
GEODE-628: Rename container folder to "geode-jvsd".
- The jvsd code now sits in the geode-jvsd directory.
- Files "pom.xml", "README.txt" and "runjvsd.sh" updated.
Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/8a86c01a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/8a86c01a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/8a86c01a
Branch: refs/heads/feature/GEODE-78
Commit: 8a86c01a367ddbef5a0b11a9091b9d00d6ce405c
Parents: 8c8a9cb
Author: jujoramos <ju...@gmail.com>
Authored: Tue Dec 8 16:09:29 2015 +0000
Committer: jujoramos <ju...@gmail.com>
Committed: Tue Dec 8 16:09:29 2015 +0000
----------------------------------------------------------------------
geode-jvsd/.gitignore | 7 +
geode-jvsd/README.txt | 66 +
geode-jvsd/pom.xml | 155 +
geode-jvsd/runjvsd.sh | 8 +
.../org.jdesktop.application.Application | 1 +
.../geode/jvsd/chart/AdvancedLineChart.java | 703 ++++
.../chart/util/LargestTriangleThreeBuckets.java | 102 +
.../geode/jvsd/controller/RootController.java | 239 ++
.../org/apache/geode/jvsd/fx/ChartManager.java | 100 +
.../geode/jvsd/fx/HoveredThresholdNode.java | 49 +
.../jvsd/fx/LongDoubleMemoryMappedSeries.java | 115 +
.../java/org/apache/geode/jvsd/fx/Main.java | 62 +
.../geode/jvsd/fx/ObservableByteBuffer.java | 21 +
.../geode/jvsd/fx/ObservableDataBuffer.java | 44 +
.../apache/geode/jvsd/fx/StatFileManager.java | 57 +
.../apache/geode/jvsd/fx/VSDChartWindow.java | 288 ++
.../org/apache/geode/jvsd/fx/VsdSeries.java | 51 +
.../jvsd/javafx/scene/chart/AbstractSeries.java | 289 ++
.../jvsd/javafx/scene/chart/BasicDataSet.java | 152 +
.../scene/chart/ByteBufferNumberSeries.java | 450 +++
.../javafx/scene/chart/DataChangedListener.java | 21 +
.../geode/jvsd/javafx/scene/chart/DataSet.java | 29 +
.../geode/jvsd/javafx/scene/chart/DateAxis.java | 393 ++
.../geode/jvsd/model/ResourceWrapper.java | 73 +
.../geode/jvsd/model/stats/StatArchiveFile.java | 3458 ++++++++++++++++++
.../jvsd/model/stats/StatArchiveFormat.java | 200 +
.../org/apache/geode/jvsd/stats/Utility.java | 236 ++
.../VisualDisplayToolAboutBox.properties | 14 +
.../resources/VisualDisplayToolApp.properties | 11 +
.../resources/VisualDisplayToolView.properties | 53 +
.../java/visualdisplaytool/resources/about.png | Bin 0 -> 8187 bytes
.../resources/busyicons/busy-icon0.png | Bin 0 -> 3588 bytes
.../resources/busyicons/busy-icon1.png | Bin 0 -> 3585 bytes
.../resources/busyicons/busy-icon10.png | Bin 0 -> 3568 bytes
.../resources/busyicons/busy-icon11.png | Bin 0 -> 3581 bytes
.../resources/busyicons/busy-icon12.png | Bin 0 -> 3589 bytes
.../resources/busyicons/busy-icon13.png | Bin 0 -> 3586 bytes
.../resources/busyicons/busy-icon14.png | Bin 0 -> 3586 bytes
.../resources/busyicons/busy-icon2.png | Bin 0 -> 3585 bytes
.../resources/busyicons/busy-icon3.png | Bin 0 -> 3572 bytes
.../resources/busyicons/busy-icon4.png | Bin 0 -> 3576 bytes
.../resources/busyicons/busy-icon5.png | Bin 0 -> 3580 bytes
.../resources/busyicons/busy-icon6.png | Bin 0 -> 3581 bytes
.../resources/busyicons/busy-icon7.png | Bin 0 -> 3598 bytes
.../resources/busyicons/busy-icon8.png | Bin 0 -> 3594 bytes
.../resources/busyicons/busy-icon9.png | Bin 0 -> 3581 bytes
.../resources/busyicons/idle-icon.png | Bin 0 -> 3360 bytes
.../java/visualdisplaytool/resources/splash.png | Bin 0 -> 21747 bytes
.../src/main/resources/META-INF/css/style.css | 49 +
geode-jvsd/src/main/resources/chart.fxml | 31 +
geode-jvsd/src/main/resources/jvsd.css | 3 +
geode-jvsd/src/main/resources/jvsd.fxml | 97 +
.../java/io/pivotal/jvsd/CopyOfCopyOfTest.java | 40 +
.../test/java/io/pivotal/jvsd/CopyOfTest.java | 45 +
.../java/io/pivotal/jvsd/Copy_2_of_Test.java | 59 +
.../src/test/java/io/pivotal/jvsd/Test.java | 110 +
jvsdfx-mm/.gitignore | 6 -
jvsdfx-mm/README.txt | 68 -
jvsdfx-mm/lib/nblibraries.properties | 4 -
jvsdfx-mm/pom.xml | 156 -
jvsdfx-mm/runjvsd.sh | 9 -
.../org.jdesktop.application.Application | 1 -
.../geode/jvsd/chart/AdvancedLineChart.java | 703 ----
.../chart/util/LargestTriangleThreeBuckets.java | 102 -
.../geode/jvsd/controller/RootController.java | 239 --
.../org/apache/geode/jvsd/fx/ChartManager.java | 100 -
.../geode/jvsd/fx/HoveredThresholdNode.java | 49 -
.../jvsd/fx/LongDoubleMemoryMappedSeries.java | 115 -
.../java/org/apache/geode/jvsd/fx/Main.java | 62 -
.../geode/jvsd/fx/ObservableByteBuffer.java | 21 -
.../geode/jvsd/fx/ObservableDataBuffer.java | 44 -
.../apache/geode/jvsd/fx/StatFileManager.java | 57 -
.../apache/geode/jvsd/fx/VSDChartWindow.java | 288 --
.../org/apache/geode/jvsd/fx/VsdSeries.java | 51 -
.../jvsd/javafx/scene/chart/AbstractSeries.java | 289 --
.../jvsd/javafx/scene/chart/BasicDataSet.java | 152 -
.../scene/chart/ByteBufferNumberSeries.java | 450 ---
.../javafx/scene/chart/DataChangedListener.java | 21 -
.../geode/jvsd/javafx/scene/chart/DataSet.java | 29 -
.../geode/jvsd/javafx/scene/chart/DateAxis.java | 393 --
.../geode/jvsd/model/ResourceWrapper.java | 73 -
.../geode/jvsd/model/stats/StatArchiveFile.java | 3458 ------------------
.../jvsd/model/stats/StatArchiveFormat.java | 200 -
.../org/apache/geode/jvsd/stats/Utility.java | 236 --
.../VisualDisplayToolAboutBox.properties | 14 -
.../resources/VisualDisplayToolApp.properties | 11 -
.../resources/VisualDisplayToolView.properties | 53 -
.../java/visualdisplaytool/resources/about.png | Bin 8187 -> 0 bytes
.../resources/busyicons/busy-icon0.png | Bin 3588 -> 0 bytes
.../resources/busyicons/busy-icon1.png | Bin 3585 -> 0 bytes
.../resources/busyicons/busy-icon10.png | Bin 3568 -> 0 bytes
.../resources/busyicons/busy-icon11.png | Bin 3581 -> 0 bytes
.../resources/busyicons/busy-icon12.png | Bin 3589 -> 0 bytes
.../resources/busyicons/busy-icon13.png | Bin 3586 -> 0 bytes
.../resources/busyicons/busy-icon14.png | Bin 3586 -> 0 bytes
.../resources/busyicons/busy-icon2.png | Bin 3585 -> 0 bytes
.../resources/busyicons/busy-icon3.png | Bin 3572 -> 0 bytes
.../resources/busyicons/busy-icon4.png | Bin 3576 -> 0 bytes
.../resources/busyicons/busy-icon5.png | Bin 3580 -> 0 bytes
.../resources/busyicons/busy-icon6.png | Bin 3581 -> 0 bytes
.../resources/busyicons/busy-icon7.png | Bin 3598 -> 0 bytes
.../resources/busyicons/busy-icon8.png | Bin 3594 -> 0 bytes
.../resources/busyicons/busy-icon9.png | Bin 3581 -> 0 bytes
.../resources/busyicons/idle-icon.png | Bin 3360 -> 0 bytes
.../java/visualdisplaytool/resources/splash.png | Bin 21747 -> 0 bytes
.../src/main/resources/META-INF/css/style.css | 49 -
jvsdfx-mm/src/main/resources/chart.fxml | 31 -
jvsdfx-mm/src/main/resources/jvsd.css | 3 -
jvsdfx-mm/src/main/resources/jvsd.fxml | 97 -
.../java/io/pivotal/jvsd/CopyOfCopyOfTest.java | 40 -
.../test/java/io/pivotal/jvsd/CopyOfTest.java | 45 -
.../java/io/pivotal/jvsd/Copy_2_of_Test.java | 59 -
.../src/test/java/io/pivotal/jvsd/Test.java | 110 -
113 files changed, 7881 insertions(+), 7888 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/.gitignore
----------------------------------------------------------------------
diff --git a/geode-jvsd/.gitignore b/geode-jvsd/.gitignore
new file mode 100644
index 0000000..f733b4b
--- /dev/null
+++ b/geode-jvsd/.gitignore
@@ -0,0 +1,7 @@
+target
+nbproject/private
+/.settings/
+/.classpath
+/.project
+/output.txt
+/lib/
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/README.txt
----------------------------------------------------------------------
diff --git a/geode-jvsd/README.txt b/geode-jvsd/README.txt
new file mode 100644
index 0000000..7ff9d63
--- /dev/null
+++ b/geode-jvsd/README.txt
@@ -0,0 +1,66 @@
+Check Java JDK version, it should version 1.8.0_60 or later.
+
+$java --version
+java version "1.8.0_60"
+Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
+Java HotSpot(TM) Server VM (build 25.60-b23, mixed mode)
+
+$jps
+13760 Jps
+
+Clone Geode from the Apache repository, checkout JVSD branch and then build Geode.
+
+$git clone https://git-wip-us.apache.org/repos/asf/incubator-geode.git
+$cd incubator-geode/
+$git branch jvsd origin/feature/GEODE-78
+$git checkout jvsd
+$./gradlew clean build installDist -Dskip.tests=true
+
+Clone and build the third party charting library MultiAxisChartFX.
+$cd ..
+$git clone https://github.com/gemfire/MultiAxisChartFX
+$cd MultiAxisChartFX/
+$mvn install
+
+Install the MultiAxisChart jar into local maven repository.
+
+$mvn install:install-file \
+-Dfile=./MultiAxisChart-1.0-SNAPSHOT.jar \
+-DgroupId=com.pivotal.javafx \
+-DartifactId=MultiAxisChart \
+-Dversion=1.0-SNAPSHOT \
+-Dpackaging=jar \
+-DgeneratePom=true
+
+For testing, additional jars may need to be added from the Apache Geode build to
+the local Maven repository.
+
+mvn install:install-file -Dfile=gemfire-core-1.0.0-incubating-SNAPSHOT.jar \
+-DgroupId=org.apache.geode \
+-DartifactId=gemfire-core \
+-Dversion=1.0.0-incubating-SNAPSHOT \
+-Dpackaging=jar \
+-DgeneratePom=true
+
+mvn install:install-file -Dfile=gemfire-jgroups-1.0.0-incubating-SNAPSHOT.jar \
+-DgroupId=org.apache.geode \
+-DartifactId=gemfire-jgroups \
+-Dversion=1.0.0-incubating-SNAPSHOT \
+-Dpackaging=jar \
+-DgeneratePom=true
+
+mvn install:install-file -Dfile=fastutil-7.0.2.jar \
+-DgroupId=org.apache.geode \
+-DartifactId=fastutil \
+-Dversion=7.0.2 \
+-Dpackaging=jar \
+-DgeneratePom=true
+
+Change back into the Geode directory and then into the JVSD directory. Build JVSD.
+$cd ../incubator-geode
+$cd geode-jvsd/
+$mvn install
+
+Run the JVSD application.
+$runjvsd.sh
+
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/pom.xml
----------------------------------------------------------------------
diff --git a/geode-jvsd/pom.xml b/geode-jvsd/pom.xml
new file mode 100644
index 0000000..8e1fdd5
--- /dev/null
+++ b/geode-jvsd/pom.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.geode</groupId>
+ <artifactId>geode-jvsd</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <properties>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>commons-cli</groupId>
+ <artifactId>commons-cli</artifactId>
+ <version>1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>3.2.1</version>
+ </dependency>
+ <!-- Enable only for Java 1.7 support
+ <dependency>
+ <groupId>com.oracle</groupId>
+ <artifactId>javafx</artifactId>
+ <version>2.2.60</version>
+ <scope>system</scope>
+ <systemPath>${java.home}/lib/jfxrt.jar</systemPath>
+ </dependency>
+ -->
+ <dependency>
+ <groupId>com.pivotal.javafx</groupId>
+ <artifactId>MultiAxisChart</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <!-- use scope and system path only if not installing jar into local maven repo -->
+ <!--<scope>system</scope>
+ <systemPath>${HOME}/Projects/MultiAxisChartFX/target/MultiAxisChart-1.0-SNAPSHOT.jar</systemPath> -->
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geode</groupId>
+ <artifactId>gemfire-core</artifactId>
+ <version>1.0.0-incubating-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geode</groupId>
+ <artifactId>gemfire-jgroups</artifactId>
+ <version>1.0.0-incubating-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.10</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>2.1</version>
+ <!--<scope>test</scope> -->
+ <type>pom</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <version>2.1</version>
+ <!--<scope>test</scope> -->
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <version>2.1</version>
+ <!--<scope>test</scope> -->
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geode</groupId>
+ <artifactId>fastutil</artifactId>
+ <version>7.0.2</version>
+ <type>jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.transaction</groupId>
+ <artifactId>jta</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <finalName>geode-jvsd</finalName>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>1.3.1</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <transformers>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
+ </transformers>
+ <!-- <artifactSet> <includes> <include>org.mortbay.jetty:jetty*</include>
+ </includes> </artifactSet> -->
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>org.apache.geode.jvsd.fx.Main</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/runjvsd.sh
----------------------------------------------------------------------
diff --git a/geode-jvsd/runjvsd.sh b/geode-jvsd/runjvsd.sh
new file mode 100755
index 0000000..41f869c
--- /dev/null
+++ b/geode-jvsd/runjvsd.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+echo `which java`
+
+if [ -f target/geode-jvsd-jar-with-dependencies.jar ]; then
+ java -verbose:class -jar target/geode-jvsd-jar-with-dependencies.jar $@ >output.txt
+else
+ echo "missing critical jar file, rebuild"
+fi
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/META-INF/services/org.jdesktop.application.Application
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/META-INF/services/org.jdesktop.application.Application b/geode-jvsd/src/META-INF/services/org.jdesktop.application.Application
new file mode 100644
index 0000000..c2e1341
--- /dev/null
+++ b/geode-jvsd/src/META-INF/services/org.jdesktop.application.Application
@@ -0,0 +1 @@
+visualdisplaytool.VisualDisplayToolApp
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/chart/AdvancedLineChart.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/chart/AdvancedLineChart.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/chart/AdvancedLineChart.java
new file mode 100644
index 0000000..6d2da1f
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/chart/AdvancedLineChart.java
@@ -0,0 +1,703 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.chart;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.animation.Timeline;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ObjectPropertyBase;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.Event;
+import javafx.event.EventHandler;
+import javafx.event.EventType;
+import javafx.geometry.Rectangle2D;
+import javafx.scene.Cursor;
+import javafx.scene.Node;
+import javafx.scene.chart.Axis;
+import javafx.scene.chart.ValueAxis;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.input.ScrollEvent;
+import javafx.scene.layout.Region;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Line;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.shape.StrokeType;
+import javafx.util.Duration;
+
+import com.pivotal.javafx.scene.chart.LegendItem;
+import com.pivotal.javafx.scene.chart.MultiAxisLineChart;
+import com.pivotal.javafx.scene.chart.Series;
+
+/**
+ * Line chart with crosshairs, zooming and panning.
+ *
+ * @author jbarrett
+ *
+ * @param <X>
+ * @param <Y>
+ */
+public class AdvancedLineChart<X extends Number, Y extends Number> extends MultiAxisLineChart<X, Y> {
+
+ /** Selected series */
+ private final ObjectProperty<Series<X, Y>> selectedSeries = new ObjectPropertyBase<Series<X, Y>>() {
+ {
+ addListener(new ChangeListener<Series<X, Y>>() {
+ @Override
+ public void changed(ObservableValue<? extends Series<X, Y>> observable, Series<X, Y> oldValue, Series<X, Y> newValue) {
+ if (null != oldValue) {
+ oldValue.getNode().getStyleClass().remove("advanced-chart-selected-series");
+ final LegendItem legendItem = oldValue.getLegendItem();
+ legendItem.getLabel().getStyleClass().remove("advanced-chart-selected-series");
+ }
+ if (null != newValue) {
+ newValue.getNode().toFront();
+ newValue.getNode().getStyleClass().add("advanced-chart-selected-series");
+ final LegendItem legendItem = newValue.getLegendItem();
+ legendItem.getLabel().getStyleClass().add("advanced-chart-selected-series");
+ }
+ requestChartLayout();
+ }
+ });
+ }
+
+ @Override
+ public Object getBean() {
+ return AdvancedLineChart.this;
+ }
+
+ @Override
+ public String getName() {
+ return "selectedSeries";
+ }
+ };
+
+ public final Series<X, Y> getSelectedSeries() {
+ return selectedSeries.get();
+ }
+
+ public final void setSelectedSeries(Series<X, Y> value) {
+ selectedSeries.set(value);
+ }
+
+ public final ObjectProperty<Series<X, Y>> selectedSeriesProperty() {
+ return selectedSeries;
+ }
+
+ private final Node chart;
+
+ // BEGIN Crosshair properties
+ private final DoubleProperty crosshairX = new SimpleDoubleProperty();
+ private final DoubleProperty crosshairY = new SimpleDoubleProperty();
+ final Line crosshairLineX;
+ final Line crosshairLineY;
+ // END Crosshair properties
+
+ // BEGIN Panning properties
+ private double panningLastX;
+ private double panningLastY;
+ private final SimpleBooleanProperty panning = new SimpleBooleanProperty(false);
+ // END Panning properties
+
+ // BEGIN Zooming properties
+ private final SimpleDoubleProperty rectX = new SimpleDoubleProperty();
+ private final SimpleDoubleProperty rectY = new SimpleDoubleProperty();
+ private final SimpleBooleanProperty zooming = new SimpleBooleanProperty(false);
+
+ private final DoubleProperty zoomDurationMillis = new SimpleDoubleProperty(700.0);
+ private final BooleanProperty zoomAnimated = new SimpleBooleanProperty(true);
+ private final BooleanProperty mouseWheelZoomAllowed = new SimpleBooleanProperty(true);
+
+ private static enum ZoomMode {
+ Horizontal, Vertical, Both
+ }
+
+ private ZoomMode zoomMode;
+
+ private final Rectangle selectRect;
+ private final Timeline zoomAnimation = new Timeline();
+ // END Zooming properties
+
+ public AdvancedLineChart(Axis<X> xAxis, Axis<Y> yAxis) {
+ super(xAxis, yAxis);
+
+ // Get the chart region
+ chart = getChartChildren().get(0).getParent();
+
+ // TODO style
+
+ crosshairLineX = new Line(0, 0, this.getWidth(), 0);
+ crosshairLineX.endXProperty().bind(this.widthProperty());
+ crosshairLineX.setStroke(Color.ORANGE);
+ crosshairLineX.setCursor(Cursor.CROSSHAIR);
+ crosshairLineX.setMouseTransparent(true);
+ crosshairLineX.translateYProperty().bind(crosshairY);
+ getPlotChildren().add(crosshairLineX);
+
+ crosshairLineY = new Line(0, 0, 0, this.getHeight());
+ crosshairLineY.endYProperty().bind(this.heightProperty());
+ crosshairLineY.setStroke(Color.ORANGE);
+ crosshairLineY.setCursor(Cursor.CROSSHAIR);
+ crosshairLineY.setMouseTransparent(true);
+ crosshairLineY.translateXProperty().bind(crosshairX);
+ getPlotChildren().add(crosshairLineY);
+
+ selectRect = new Rectangle(0, 0, 0, 0);
+ selectRect.setFill(Color.DODGERBLUE);
+ selectRect.setMouseTransparent(true);
+ selectRect.setOpacity(0.3);
+ selectRect.setStroke(Color.rgb(0, 0x29, 0x66));
+ selectRect.setStrokeType(StrokeType.INSIDE);
+ selectRect.setStrokeWidth(3.0);
+ selectRect.widthProperty().bind(rectX.subtract(selectRect.translateXProperty()));
+ selectRect.heightProperty().bind(rectY.subtract(selectRect.translateYProperty()));
+ selectRect.visibleProperty().bind(zooming);
+ getPlotChildren().add(selectRect);
+
+ chart.addEventHandler(MouseEvent.MOUSE_MOVED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ // TODO change events on transforms?
+ // TODO bind to transform property because axis change
+ final double x = event.getX() - getXShift(getXAxis());
+ final double y = event.getY() - getYShift(getPrimaryYAxis());
+
+ // TODO do as properties? Crosshair.visible property?
+ if (y > 0 && y < getPrimaryYAxis().getHeight() && x > 0 && x < getXAxis().getWidth()) {
+ crosshairX.set(x);
+ crosshairY.set(y);
+ setCursor(Cursor.CROSSHAIR);
+ crosshairLineX.setVisible(true);
+ crosshairLineY.setVisible(true);
+ } else {
+ setCursor(Cursor.DEFAULT);
+ crosshairLineX.setVisible(false);
+ crosshairLineY.setVisible(false);
+ }
+ }
+ });
+
+ chart.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent mouseEvent) {
+ if (isZoomEvent(mouseEvent))
+ setupZooming(mouseEvent.getX(), mouseEvent.getY());
+ mouseEvent.consume();
+ }
+ });
+
+ chart.addEventHandler(MouseEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent mouseEvent) {
+ if (isPanEvent(mouseEvent)) {
+ startPanning(mouseEvent.getX(), mouseEvent.getY());
+ mouseEvent.consume();
+ } else if (isZoomEvent(mouseEvent)) {
+ startZooming();
+ mouseEvent.consume();
+ }
+ }
+ });
+
+ chart.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent mouseEvent) {
+ if (panning.get()) {
+ doPanning(mouseEvent.getX(), mouseEvent.getY());
+ mouseEvent.consume();
+ } else if (zooming.get()) {
+ doZomming(mouseEvent.getX(), mouseEvent.getY());
+ mouseEvent.consume();
+ }
+ }
+ });
+
+ chart.addEventHandler(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent mouseEvent) {
+ if (panning.get()) {
+ stopPanning();
+ mouseEvent.consume();
+ } else if (zooming.get()) {
+ stopZooming();
+ mouseEvent.consume();
+ }
+ }
+ });
+
+ chart.addEventHandler(ScrollEvent.ANY, new MouseWheelZoomHandler());
+
+ }
+
+ public final double getCrosshairX() {
+ return crosshairX.getValue();
+ }
+
+ public final DoubleProperty crosshairXProperty() {
+ return crosshairX;
+ }
+
+ public final double getCrosshairY() {
+ return crosshairY.getValue();
+ }
+
+ public final DoubleProperty crosshairYProperty() {
+ return crosshairY;
+ }
+
+ @Override
+ protected void seriesAdded(final Series<X, Y> series, int seriesIndex) {
+ super.seriesAdded(series, seriesIndex);
+
+ series.getNode().addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ setSelectedSeries(getSelectedSeries() == series ? null : series);
+ }
+ });
+ }
+
+ @Override
+ protected void updateLegend() {
+ super.updateLegend();
+
+ for (final Series<X, Y> series : getData()) {
+ series.getLegendItem().getLabel().addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ setSelectedSeries(getSelectedSeries() == series ? null : series);
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void yAxisAdded(Axis<Y> yAxis) {
+ super.yAxisAdded(yAxis);
+
+ yAxis.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
+ @Override
+ public void handle(MouseEvent event) {
+ @SuppressWarnings("unchecked")
+ final Axis<Y> yAxis = (Axis<Y>) event.getSource();
+ setPrimaryYAxis(yAxis);
+ }
+ });
+ }
+
+ @Override
+ protected void layoutPlotChildren() {
+ super.layoutPlotChildren();
+
+ selectRect.toFront();
+ crosshairLineX.toFront();
+ crosshairLineY.toFront();
+ }
+
+ protected boolean isPanEvent(MouseEvent mouseEvent) {
+ return mouseEvent.getButton() == MouseButton.SECONDARY || (mouseEvent.getButton() == MouseButton.PRIMARY && mouseEvent.isShortcutDown());
+ }
+
+ protected boolean isZoomEvent(MouseEvent mouseEvent) {
+ return mouseEvent.getButton() == MouseButton.PRIMARY;
+ }
+
+ protected void startPanning(final double fromX, final double fromY) {
+ panningLastX = fromX;
+ panningLastY = fromY;
+
+ final Axis<X> xAxis = this.getXAxis();
+
+ // TODO check or bind to chart animation?
+ // wasXAnimated = xAxis.getAnimated();
+ // wasYAnimated = chart.getPrimaryYAxis().getAnimated();
+
+ xAxis.setAnimated(false);
+ xAxis.setAutoRanging(false);
+
+ for (Axis<Y> yAxis : getYAxes()) {
+ yAxis.setAnimated(false);
+ yAxis.setAutoRanging(false);
+ }
+
+ // TODO property?
+ panning.set(true);
+ }
+
+ protected void doPanning(final double toX, final double toY) {
+ // TODO animated?
+ // TODO do we really need to check this? Filters?
+ if (!panning.get()) {
+ return;
+ }
+
+ final ValueAxis<?> xAxis = (ValueAxis<?>) getXAxis();
+
+ final double dX = (toX - panningLastX) / -xAxis.getScale();
+ xAxis.setAutoRanging(false);
+ xAxis.setLowerBound(xAxis.getLowerBound() + dX);
+ xAxis.setUpperBound(xAxis.getUpperBound() + dX);
+
+ @SuppressWarnings("unchecked")
+ final List<ValueAxis<?>> yAxes = ((List<ValueAxis<?>>) (List<?>) getYAxes());
+ for (ValueAxis<?> yAxis : yAxes) {
+ final double dY = (toY - panningLastY) / -yAxis.getScale();
+ yAxis.setAutoRanging(false);
+ yAxis.setLowerBound(yAxis.getLowerBound() + dY);
+ yAxis.setUpperBound(yAxis.getUpperBound() + dY);
+ }
+
+ panningLastX = toX;
+ panningLastY = toY;
+ }
+
+ protected void stopPanning() {
+ if (!panning.get())
+ return;
+
+ panning.set(false);
+
+ // TODO should we allow certain axis to not be animated?
+ getXAxis().setAnimated(getAnimated());
+
+ for (Axis<Y> yAxis : getYAxes()) {
+ yAxis.setAnimated(getAnimated());
+ }
+ }
+
+ protected void setupZooming(final double x, final double y) {
+ final Axis<X> xAxis = getXAxis();
+ final Axis<Y> yAxis = getPrimaryYAxis();
+
+ final double xShifted = x - getXShift(xAxis);
+ final double yShifted = y - getYShift(yAxis);
+
+ final double width = xAxis.getWidth();
+ final double height = yAxis.getHeight();
+
+ if (xShifted >= 0 && xShifted <= width &&
+ yShifted >= 0 && yShifted <= height) {
+ selectRect.setTranslateX(xShifted);
+ selectRect.setTranslateY(yShifted);
+ rectX.set(xShifted);
+ rectY.set(yShifted);
+ zoomMode = ZoomMode.Both;
+
+ } else if (getComponentArea(xAxis).contains(x, y)) {
+ selectRect.setTranslateX(xShifted);
+ selectRect.setTranslateY(0);
+ rectX.set(xShifted);
+ rectY.set(height);
+ zoomMode = ZoomMode.Horizontal;
+
+ } else if (getComponentArea(yAxis).contains(x, y)) {
+ selectRect.setTranslateX(0);
+ selectRect.setTranslateY(yShifted);
+ rectX.set(width);
+ rectY.set(yShifted);
+ zoomMode = ZoomMode.Vertical;
+ }
+ }
+
+ protected void startZooming() {
+ // Don't actually start the selecting process until it's officially a drag
+ // But, we saved the original coordinates from where we started.
+ // TODO this??
+ zooming.set(true);
+ }
+
+ protected void doZomming(double x, double y) {
+ if (!zooming.get())
+ return;
+
+ final Axis<X> xAxis = getXAxis();
+ final Axis<Y> yAxis = getPrimaryYAxis();
+
+ x -= getXShift(xAxis);
+ y -= getYShift(yAxis);
+
+ if (zoomMode == ZoomMode.Both || zoomMode == ZoomMode.Horizontal) {
+ // Clamp to the selection start
+ x = Math.max(x, selectRect.getTranslateX());
+ // Clamp to plot area
+ x = Math.min(x, xAxis.getWidth());
+ rectX.set(x);
+ }
+
+ if (zoomMode == ZoomMode.Both || zoomMode == ZoomMode.Vertical) {
+ // Clamp to the selection start
+ y = Math.max(y, selectRect.getTranslateY());
+ // Clamp to plot area
+ y = Math.min(y, yAxis.getHeight());
+ rectY.set(y);
+ }
+ }
+
+ protected void stopZooming() {
+ if (!zooming.get())
+ return;
+
+ // Prevent a silly zoom... I'm still undecided about && vs ||
+ if (selectRect.getWidth() == 0.0 || selectRect.getHeight() == 0.0) {
+ zooming.set(false);
+ return;
+ }
+
+ // Rectangle2D zoomWindow = chartInfo.getDataCoordinates(
+ // selectRect.getTranslateX(), selectRect.getTranslateY(),
+ // rectX.get(), rectY.get()
+ // );
+
+ double minX = selectRect.getTranslateX();
+ double minY = selectRect.getTranslateY();
+ double maxX = rectX.get();
+ double maxY = rectY.get();
+
+ // TODO allow zoom in any x/y direction
+
+ if (minX > maxX || minY > maxY) {
+ throw new IllegalArgumentException("min > max for X and/or Y");
+ }
+
+ final ValueAxis<X> xAxis = (ValueAxis<X>) getXAxis();
+
+ // Axis xAxis = chart.getXAxis();
+ // Axis yAxis = chart.getPrimaryYAxis();
+
+ double minDataX = translatePositionToData(xAxis, minX);
+ double maxDataX = translatePositionToData(xAxis, maxX);
+
+ // The "low" Y data value is actually at the maxY graphical location as Y
+ // graphical axis gets
+ // larger as you go down on the screen.
+ // double minDataY = yAxis.toNumericValue( yAxis.getValueForDisplay( maxY -
+ // yStart ) );
+ // double maxDataY = yAxis.toNumericValue( yAxis.getValueForDisplay( minY -
+ // yStart ) );
+ //
+ // return new Rectangle2D( minDataX,
+ // minDataY,
+ // maxDataX - minDataX,
+ // maxDataY - minDataY );
+
+ if (zoomMode == ZoomMode.Both || zoomMode == ZoomMode.Horizontal) {
+ xAxis.setAutoRanging(false);
+ } else {
+ // TODO this doesn't work, try to auto range x when only zooming y??
+ ArrayList<X> d = new ArrayList<>();
+ getData().forEach(s -> {s.getData().forEach(dy -> {d.add(dy.getXValue());});});
+ xAxis.invalidateRange(d);
+ }
+
+ @SuppressWarnings("unchecked")
+ final List<ValueAxis<Y>> yAxes = ((List<ValueAxis<Y>>) (List<?>) getYAxes());
+ if (zoomMode == ZoomMode.Both || zoomMode == ZoomMode.Vertical) {
+ for (ValueAxis<Y> yAxis : yAxes) {
+ yAxis.setAutoRanging(false);
+ }
+ } else {
+ // TODO this doesn't work, try to auto range y when only zooming x
+ for (ValueAxis<Y> yAxis : yAxes) {
+ ArrayList<Y> d = new ArrayList<>();
+ getData().forEach(s -> {s.getData().forEach(dy -> {d.add(dy.getYValue());});});
+ yAxis.invalidateRange(d);
+ }
+ }
+
+ if (zoomAnimated.get()) {
+ zoomAnimation.stop();
+
+ final KeyValue[] startFrames = new KeyValue[2 + (yAxes.size() * 2)];
+ final KeyValue[] stopFrames = new KeyValue[2 + (yAxes.size() * 2)];
+ int startFramesIndex = 0;
+ int stopFramesIndex = 0;
+ if (zoomMode == ZoomMode.Both || zoomMode == ZoomMode.Horizontal) {
+ startFrames[startFramesIndex++] = new KeyValue(xAxis.lowerBoundProperty(), xAxis.getLowerBound());
+ startFrames[startFramesIndex++] = new KeyValue(xAxis.upperBoundProperty(), xAxis.getUpperBound());
+ stopFrames[stopFramesIndex++] = new KeyValue(xAxis.lowerBoundProperty(), minDataX);
+ stopFrames[stopFramesIndex++] = new KeyValue(xAxis.upperBoundProperty(), maxDataX);
+ }
+
+ if (zoomMode == ZoomMode.Both || zoomMode == ZoomMode.Vertical) {
+ for (ValueAxis<?> yAxis : yAxes) {
+ final double minDataY = translatePositionToData(yAxis, maxY);
+ final double maxDataY = translatePositionToData(yAxis, minY);
+ startFrames[startFramesIndex++] = new KeyValue(yAxis.lowerBoundProperty(), yAxis.getLowerBound());
+ startFrames[startFramesIndex++] = new KeyValue(yAxis.upperBoundProperty(), yAxis.getUpperBound());
+ stopFrames[stopFramesIndex++] = new KeyValue(yAxis.lowerBoundProperty(), minDataY);
+ stopFrames[stopFramesIndex++] = new KeyValue(yAxis.upperBoundProperty(), maxDataY);
+ }
+ }
+
+ zoomAnimation.getKeyFrames().setAll(new KeyFrame(Duration.ZERO, startFrames), new KeyFrame(Duration.millis(zoomDurationMillis.get()), stopFrames));
+ zoomAnimation.play();
+ } else {
+ zoomAnimation.stop();
+ if (zoomMode == ZoomMode.Both || zoomMode == ZoomMode.Horizontal) {
+ xAxis.setLowerBound(minDataX);
+ xAxis.setUpperBound(maxDataX);
+ }
+ if (zoomMode == ZoomMode.Both || zoomMode == ZoomMode.Vertical) {
+ for (ValueAxis<?> yAxis : yAxes) {
+ final double minDataY = translatePositionToData(yAxis, maxY);
+ final double maxDataY = translatePositionToData(yAxis, minY);
+ yAxis.setLowerBound(minDataY);
+ yAxis.setUpperBound(maxDataY);
+ }
+ }
+ }
+
+ zooming.set(false);
+ }
+
+ protected Rectangle2D getComponentArea(Region childRegion) {
+ double xStart = getXShift(childRegion);
+ double yStart = getYShift(childRegion);
+
+ return new Rectangle2D(xStart, yStart, childRegion.getWidth(), childRegion.getHeight());
+ }
+
+ protected static final double getXShift(Node node) {
+ return node.getLocalToParentTransform().getTx();
+ }
+
+ protected static final double getYShift(Node node) {
+ return node.getLocalToParentTransform().getTy();
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected static final double translatePositionToData(ValueAxis axis, double position) {
+ return axis.toNumericValue(axis.getValueForDisplay(position));
+ }
+
+ protected class MouseWheelZoomHandler implements EventHandler<ScrollEvent> {
+ // TODO never getting start event
+ private boolean ignoring = false;
+
+ @Override
+ public void handle(ScrollEvent event) {
+ EventType<? extends Event> eventType = event.getEventType();
+ if (eventType == ScrollEvent.SCROLL_STARTED) {
+ // mouse wheel events never send SCROLL_STARTED, bullshit
+ ignoring = false;
+ } else if (eventType == ScrollEvent.SCROLL_FINISHED) {
+ // end non-mouse wheel event, never comes either
+ ignoring = false;
+
+ } else if (eventType == ScrollEvent.SCROLL &&
+ // If we are allowing mouse wheel zooming
+ mouseWheelZoomAllowed.get() &&
+ // If we aren't between SCROLL_STARTED and SCROLL_FINISHED
+ !ignoring &&
+ // inertia from non-wheel gestures might have touch count of 0
+ // !event.isInertia() &&
+ // Only care about vertical wheel events
+ event.getDeltaY() != 0 &&
+ // mouse wheel always has touch count of 0
+ event.getTouchCount() == 0) {
+
+ // If we are are doing a zoom animation, stop it. Also of note is that
+ // we don't zoom the
+ // mouse wheel zooming. Because the mouse wheel can "fly" and generate a
+ // lot of events,
+ // animation doesn't work well. Plus, as the mouse wheel changes the
+ // view a small amount in
+ // a predictable way, it "looks like" an animation when you roll it.
+ // We might experiment with mouse wheel zoom animation in the future,
+ // though.
+ zoomAnimation.stop();
+
+ final ValueAxis<?> xAxis = (ValueAxis<?>) getXAxis();
+
+ // If we wheel zoom on either axis, we restrict zooming to that axis
+ // only, else if anywhere
+ // else, including the plot area, zoom both axes.
+ ZoomMode zoomMode;
+ final double eventX = event.getX();
+ final double eventY = event.getY();
+ if (getComponentArea(xAxis).contains(eventX, eventY)) {
+ zoomMode = ZoomMode.Horizontal;
+ } else if (getComponentArea(getPrimaryYAxis()).contains(eventX, eventY)) {
+ // TODO other y axes
+ zoomMode = ZoomMode.Vertical;
+ } else {
+ zoomMode = ZoomMode.Both;
+ }
+
+ // At this point we are a mouse wheel event, based on everything I've
+ // read
+ // Point2D dataCoords = chartInfo.getDataCoordinates( eventX, eventY );
+
+ // TODO shift properties
+ final double xStart = getXShift(xAxis);
+ final double yStart = getYShift(getPrimaryYAxis());
+
+ double distance = event.getDeltaY();
+
+ // TODO accelerator property
+ double zoomAmount = 0.0005 * distance;
+
+ if (zoomMode == ZoomMode.Both || zoomMode == ZoomMode.Horizontal) {
+ // Determine the proportion of change to the lower and upper bounds
+ // based on how far the
+ // cursor is along the axis.
+ final double dataX = translatePositionToData(xAxis, eventX - xStart);
+ final double xZoomBalance = getBalance(dataX, xAxis.getLowerBound(), xAxis.getUpperBound());
+ final double xZoomDelta = (xAxis.getUpperBound() - xAxis.getLowerBound()) * zoomAmount;
+ xAxis.setAutoRanging(false);
+ xAxis.setLowerBound(xAxis.getLowerBound() - xZoomDelta * xZoomBalance);
+ xAxis.setUpperBound(xAxis.getUpperBound() + xZoomDelta * (1 - xZoomBalance));
+ }
+
+ if (zoomMode == ZoomMode.Both || zoomMode == ZoomMode.Vertical) {
+ @SuppressWarnings("unchecked")
+ final List<ValueAxis<?>> yAxes = ((List<ValueAxis<?>>) (List<?>) getYAxes());
+ for (final ValueAxis<?> yAxis : yAxes) {
+ // Determine the proportion of change to the lower and upper bounds
+ // based on how far the
+ // cursor is along the axis.
+ final double dataY = translatePositionToData(yAxis, eventY - yStart);
+ final double yZoomBalance = getBalance(dataY, yAxis.getLowerBound(), yAxis.getUpperBound());
+ final double yZoomDelta = (yAxis.getUpperBound() - yAxis.getLowerBound()) * zoomAmount;
+ yAxis.setAutoRanging(false);
+ yAxis.setLowerBound(yAxis.getLowerBound() - yZoomDelta * yZoomBalance);
+ yAxis.setUpperBound(yAxis.getUpperBound() + yZoomDelta * (1 - yZoomBalance));
+ }
+ }
+ }
+ }
+ }
+
+ protected static final double getBalance(double val, double min, double max) {
+ if (val <= min)
+ return 0.0;
+ else if (val >= max)
+ return 1.0;
+
+ return (val - min) / (max - min);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/chart/util/LargestTriangleThreeBuckets.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/chart/util/LargestTriangleThreeBuckets.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/chart/util/LargestTriangleThreeBuckets.java
new file mode 100644
index 0000000..2d1233e
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/chart/util/LargestTriangleThreeBuckets.java
@@ -0,0 +1,102 @@
+/*
+ * Copied and ported from https://raw.githubusercontent.com/sveinn-steinarsson/flot-downsample/master/jquery.flot.downsample.js
+ * Portions of this file are under the following license:
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2013 by Sveinn Steinarsson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.apache.geode.jvsd.chart.util;
+
+/**
+ * Java implementation of Largest Triangle Three Bucket downsampling algorithm
+ * based on http://skemman.is/stream/get/1946/15343/37285/3/SS_MSthesis.pdf.
+ *
+ * @author jbarrett
+ *
+ */
+public class LargestTriangleThreeBuckets {
+
+ public static final Number[][] downsample(final Number[][] data, final int threshold) {
+
+ final int dataLength = data.length;
+ if (threshold >= dataLength || 0 == threshold) {
+ return data; // Nothing to do
+ }
+
+ final Number[][] sampled = new Number[threshold][];
+ int sampledIndex = 0;
+
+ // Bucket size. Leave room for start and end data points
+ final double bucketSize = (double) (dataLength - 2) / (threshold - 2);
+
+ int a = 0; // Initially a is the first point in the triangle
+ int nextA = 0;
+
+ sampled[sampledIndex++] = data[a]; // Always add the first point
+
+ for (int i = 0; i < threshold - 2; i++) {
+
+ // Calculate point average for next bucket (containing c)
+ double pointCX = 0;
+ double pointCY = 0;
+ int pointCStart = (int) Math.floor((i + 1) * bucketSize) + 1;
+ int pointCEnd = (int) Math.floor((i + 2) * bucketSize) + 1;
+ pointCEnd = pointCEnd < dataLength ? pointCEnd : dataLength;
+ final int pointCSize = pointCEnd - pointCStart;
+ for (; pointCStart < pointCEnd; pointCStart++) {
+ pointCX += data[pointCStart][0].doubleValue(); // TODO DATE??
+ pointCY += data[pointCStart][1].doubleValue();
+ }
+ pointCX /= pointCSize;
+ pointCY /= pointCSize;
+
+ // Point a
+ double pointAX = data[a][0].doubleValue(); // TODO Date
+ double pointAY = data[a][1].doubleValue();
+
+ // Get the range for bucket b
+ int pointBStart = (int) Math.floor((i + 0) * bucketSize) + 1;
+ final int pointBEnd = (int) Math.floor((i + 1) * bucketSize) + 1;
+ double maxArea = -1;
+ Number[] maxAreaPoint = null;
+ for (; pointBStart < pointBEnd; pointBStart++) {
+ // Calculate triangle area over three buckets
+ final double area = Math.abs((pointAX - pointCX) * (data[pointBStart][1].doubleValue() - pointAY) - (pointAX - data[pointBStart][0].doubleValue())
+ * (pointCY - pointAY)) * 0.5;
+ if (area > maxArea) {
+ maxArea = area;
+ maxAreaPoint = data[pointBStart];
+ nextA = pointBStart; // Next a is this b
+ }
+ }
+
+ sampled[sampledIndex++] = maxAreaPoint; // Pick this point from the bucket
+ a = nextA; // This a is the next a (chosen b)
+ }
+
+ sampled[sampledIndex++] = data[dataLength - 1]; // Always add last
+
+ return sampled;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/controller/RootController.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/controller/RootController.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/controller/RootController.java
new file mode 100644
index 0000000..b0c6fb4
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/controller/RootController.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.controller;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.Button;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.ListView;
+import javafx.scene.control.SelectionMode;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.stage.FileChooser;
+
+import org.apache.geode.jvsd.fx.ChartManager;
+import org.apache.geode.jvsd.fx.StatFileManager;
+import org.apache.geode.jvsd.model.ResourceWrapper;
+import org.apache.geode.jvsd.model.stats.StatArchiveFile;
+import org.apache.geode.jvsd.model.stats.StatArchiveFile.StatValue;
+
+/**
+ * @author Jens Deppe
+ */
+public class RootController implements Initializable {
+
+ @FXML
+ private TableView<ResourceWrapper> tableView;
+
+ @FXML
+ private ListView listView;
+
+ @FXML
+ private Button newChartButton;
+
+ @FXML
+ private Button addToChartButton;
+
+ @FXML
+ private ComboBox chartCombo;
+
+ private ObservableList<Integer> chartList;
+
+ private ObservableList<ResourceWrapper> resources;
+
+ private ObservableList statNames;
+
+ private StatFileManager fileManager = StatFileManager.getInstance();
+
+ private ChartManager chartManager = ChartManager.getInstance();
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) {
+ double[] columnPercent = {
+ 0.05, // Row
+ 0.25, // Start Time
+ 0.05, // File
+ 0.075, // Samples
+ 0.075, // PID
+ 0.2, // Type
+ 0.3 // Name
+ };
+
+ ObservableList<TableColumn<ResourceWrapper, ?>> cols = tableView.getColumns();
+ // Set column widths
+ for (int i = 0; i < 7; i++) {
+ cols.get(i).prefWidthProperty().
+ bind(tableView.widthProperty().multiply(columnPercent[i]));
+ }
+
+ cols.get(0).setCellValueFactory(new PropertyValueFactory<>("row"));
+ cols.get(1).setCellValueFactory(new PropertyValueFactory<>("startTime"));
+ cols.get(2).setCellValueFactory(new PropertyValueFactory<>("file"));
+ cols.get(3).setCellValueFactory(new PropertyValueFactory<>("samples"));
+ cols.get(4).setCellValueFactory(new PropertyValueFactory<>("pid"));
+ cols.get(5).setCellValueFactory(new PropertyValueFactory<>("type"));
+ cols.get(6).setCellValueFactory(new PropertyValueFactory<>("name"));
+
+ resources = FXCollections.observableArrayList();
+
+ updateArchives();
+
+ tableView.setItems(resources);
+ tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
+ tableView.getSelectionModel().selectedItemProperty().addListener(
+ (observableValue, oldValue, newValue) -> {
+ updateStatList();
+ });
+
+ statNames = FXCollections.observableArrayList();
+ listView.setItems(statNames);
+ listView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
+ listView.getSelectionModel().selectedItemProperty().addListener(
+ (observableValue, oldValue, newValue) -> {
+ System.out.println(newValue);
+ });
+
+ chartList = FXCollections.observableArrayList();
+ chartCombo.setItems(chartList);
+ }
+
+ private void updateArchives() {
+ int row = 0;
+ for (StatArchiveFile s : fileManager.getArchives()) {
+ for (StatArchiveFile.ResourceInst r : s.getResourceInstList()) {
+ resources.add(new ResourceWrapper(r, row++));
+ }
+ }
+ }
+
+ private List<StatValue> getSelectedStatValues() {
+ List<StatValue> result = new ArrayList<>();
+
+ List<String> statNames = listView.getSelectionModel().getSelectedItems();
+ ObservableList<ResourceWrapper> rows =
+ tableView.getSelectionModel().getSelectedItems();
+ if (rows != null && rows.size() > 0) {
+ for (ResourceWrapper r : rows) {
+ for (String stat : statNames) {
+ StatValue sv = r.getStatValue(stat);
+ result.add(sv);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @FXML
+ private void newChartButtonClicked() {
+ List<StatValue> svs = getSelectedStatValues();
+ if (svs.size() > 0) {
+ try {
+ int id = chartManager.newChart(svs.get(0));
+ svs.remove(0);
+ for (StatValue s : svs) {
+ chartManager.addToChart(id, s);
+ }
+ chartCombo.getSelectionModel().select(id);
+ } catch (IOException iox) {
+ iox.printStackTrace();
+ }
+ }
+ }
+
+ @FXML
+ private void addToChartButtonClicked() {
+ Integer i = (Integer) chartCombo.getSelectionModel().getSelectedItem();
+ List<StatValue> svs = getSelectedStatValues();
+ try {
+ for (StatValue s : svs) {
+ chartManager.addToChart(i, s);
+ }
+ } catch (IOException iox) {
+ iox.printStackTrace();
+ }
+ }
+
+ @FXML
+ private void handleOpenMenuAction(ActionEvent e){
+ try {
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.setTitle("Open Statistics File");
+ File file=fileChooser.showOpenDialog(null);
+ StatFileManager.getInstance().add(file.getCanonicalPath());
+ updateArchives();
+ } catch (IOException ex) {
+ //TODO needs warning dialog of failed file open.
+ }
+ }
+
+ @FXML
+ private void handleCloseMenuAction(ActionEvent e){
+ //TODO do a real clean up and shutdown
+ //just kill it now
+ System.exit(0);
+ }
+
+ public void setChartList(Collection<Integer> chartList) {
+ if (chartList.size() > 0) {
+ chartCombo.setDisable(false);
+ this.chartList.clear();
+ this.chartList.addAll(chartList);
+ this.chartCombo.getSelectionModel().select(0);
+ addToChartButton.setDisable(false);
+ } else {
+ this.chartList.clear();
+ chartCombo.setDisable(true);
+ addToChartButton.setDisable(true);
+ }
+
+ }
+
+ private void updateStatList() {
+ ObservableList<ResourceWrapper> rows =
+ tableView.getSelectionModel().getSelectedItems();
+ List<String> tmpStatNames = rows.get(0).getStatNames();
+
+ for (int i = 1; i < rows.size(); i++) {
+ List<String> tmpNames = rows.get(i).getStatNames();
+ Iterator<String> it = tmpStatNames.iterator();
+ while (it.hasNext()) {
+ String s = it.next();
+ if (!tmpNames.contains(s)) {
+ it.remove();
+ }
+ }
+ }
+
+ statNames.clear();
+ statNames.addAll(tmpStatNames);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ChartManager.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ChartManager.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ChartManager.java
new file mode 100644
index 0000000..e5d10d0
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ChartManager.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.fx;
+
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.Pane;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.geode.jvsd.controller.RootController;
+import org.apache.geode.jvsd.model.stats.StatArchiveFile;
+
+/**
+ * Basic container for all charts which are currently being displayed
+ *
+ * @author Jens Deppe
+ */
+public class ChartManager {
+
+ static final boolean MMTEST = true;
+
+ private Map<Integer, VSDChartWindow> charts = new HashMap<>();
+
+ private static ChartManager instance = new ChartManager();
+
+ private RootController root;
+
+ private int chartId = 0;
+
+ private ChartManager() {
+ // We are a singleton
+ }
+
+ public static ChartManager getInstance() {
+ return instance;
+ }
+
+ public VSDChartWindow add(VSDChartWindow chart) {
+ return chart;
+ }
+
+ public VSDChartWindow get(String chartName) {
+ return charts.get(chartName);
+ }
+
+ public void setRootController(RootController root) {
+ this.root = root;
+ }
+
+ public void removeChart(int id) {
+ charts.remove(id);
+ root.setChartList(charts.keySet());
+ }
+
+ public int newChart(StatArchiveFile.StatValue sv) throws IOException {
+ int id = chartId++;
+
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("/chart.fxml"));
+ Pane chartPane = loader.load();
+ Stage stage = new Stage();
+ Scene scene = new Scene(chartPane);
+ scene.getStylesheets().add("META-INF/css/style.css");
+ stage.setScene(scene);
+ stage.show();
+
+ VSDChartWindow controller = loader.getController();
+ controller.setId(id);
+ charts.put(id, controller);
+ root.setChartList(charts.keySet());
+
+ controller.setStage(stage);
+// if (!MMTEST) {
+ controller.addToChart(sv);
+// }
+
+ return id;
+ }
+
+ public void addToChart(int id, StatArchiveFile.StatValue sv) throws IOException {
+ charts.get(id).addToChart(sv);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/HoveredThresholdNode.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/HoveredThresholdNode.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/HoveredThresholdNode.java
new file mode 100644
index 0000000..dab61c3
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/HoveredThresholdNode.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.fx;
+
+import javafx.scene.Cursor;
+import javafx.scene.control.Label;
+import javafx.scene.layout.StackPane;
+
+public class HoveredThresholdNode extends StackPane {
+ public HoveredThresholdNode(String labelString) {
+ setPrefSize(2, 2);
+
+ final Label label = createDataThresholdLabel(labelString);
+
+ setOnMouseEntered(mouseEvent -> {
+ getChildren().setAll(label);
+ setCursor(Cursor.NONE);
+ toFront();
+ });
+ setOnMouseExited(mouseEvent -> {
+ getChildren().clear();
+ setCursor(Cursor.CROSSHAIR);
+ });
+ }
+
+ private Label createDataThresholdLabel(String labelString) {
+ final Label label = new Label(labelString);
+ // TODO style, colors, etc.
+ label.getStyleClass().addAll("chart-line-symbol", "chart-series-line");
+ label.setStyle("-fx-font-size: 8pt;");
+
+ label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
+ return label;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/LongDoubleMemoryMappedSeries.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/LongDoubleMemoryMappedSeries.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/LongDoubleMemoryMappedSeries.java
new file mode 100644
index 0000000..ee7be7b
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/LongDoubleMemoryMappedSeries.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.fx;
+
+import java.nio.ByteBuffer;
+
+import org.apache.geode.jvsd.javafx.scene.chart.ByteBufferNumberSeries;
+
+import com.pivotal.javafx.scene.chart.Data;
+
+import javafx.collections.ObservableList;
+
+/**
+ * A named series of data items
+ */
+public class LongDoubleMemoryMappedSeries extends ByteBufferNumberSeries<Number, Number> {
+
+ // -------------- CONSTRUCTORS ----------------------------------------------
+
+ /**
+ * Construct a empty series
+ */
+ public LongDoubleMemoryMappedSeries() {
+ super();
+ }
+
+ /**
+ * Constructs a Series and populates it with the given {@link ObservableList}
+ * data.
+ *
+ * @param data
+ * ObservableList of MultiAxisChart.Data
+ */
+ public LongDoubleMemoryMappedSeries(ByteBuffer buffer) {
+ super(buffer);
+ }
+
+ /**
+ * Constructs a named Series and populates it with the given
+ * {@link ObservableList} data.
+ *
+ * @param name
+ * a name for the series
+ * @param data
+ * ObservableList of MultiAxisChart.Data
+ */
+ public LongDoubleMemoryMappedSeries(String name, ByteBuffer buffer) {
+ super(name, buffer);
+ }
+
+ // BEGIN FORMAT SPECIFIC
+ private static final int DATA_X_OFFSET = 0;
+ private static final int DATA_X_LENGTH = 8;
+ private static final int DATA_Y_OFFSET = DATA_X_OFFSET + DATA_X_LENGTH;
+ private static final int DATA_Y_LENGTH = 8;
+ private static final int DATA_WIDTH = DATA_Y_OFFSET + DATA_Y_LENGTH;
+
+ // TODO Make these Abstract
+ @Override
+ public int getDataSize() {
+ return getBuffer().limit() / DATA_WIDTH;
+ }
+
+ // TODO Make these Abstract
+ private long getXRaw(final int index) {
+ return buffer.getLong(index * DATA_WIDTH + DATA_X_OFFSET);
+ }
+
+ // TODO Make these Abstract
+ private double getYRaw(final int index) {
+ return buffer.getDouble(index * DATA_WIDTH + DATA_Y_OFFSET);
+ }
+
+ // END FORMAT SPECIFIC
+
+ @Override
+ final protected Data<Number,Number> createData(int index) {
+ return new Data<>(getXRaw(index), getYRaw(index));
+ };
+
+ @Override
+ final protected Long convertX(final double x) {
+ return (long) x;
+ }
+
+ @Override
+ final protected Double convertY(final double y) {
+ return y;
+ }
+
+ @Override
+ final protected double getX(final int index) {
+ return (double) getXRaw(index);
+ };
+
+ @Override
+ final protected double getY(final int index) {
+ return (double) getYRaw(index);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/Main.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/Main.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/Main.java
new file mode 100644
index 0000000..28f9af6
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/Main.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.fx;
+
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.Pane;
+import javafx.stage.Stage;
+
+import java.io.IOException;
+
+import org.apache.geode.jvsd.controller.RootController;
+
+/**
+ * @author Jens Deppe
+ */
+public class Main extends Application {
+
+ private Stage primaryStage;
+
+ private RootController controller;
+
+ @Override
+ public void start(Stage stage) throws IOException {
+ primaryStage = stage;
+ primaryStage.setTitle("jVSD");
+
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("/jvsd.fxml"));
+ Pane rootLayout = loader.load();
+
+ Scene scene = new Scene(rootLayout);
+ scene.getStylesheets().add("/jvsd.css");
+
+ controller = loader.getController();
+
+ ChartManager.getInstance().setRootController(controller);
+
+ primaryStage.setScene(scene);
+ primaryStage.show();
+ }
+
+ public static void main(String[] args) throws IOException {
+ StatFileManager.getInstance().add(args);
+ launch(args);
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ObservableByteBuffer.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ObservableByteBuffer.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ObservableByteBuffer.java
new file mode 100644
index 0000000..a68db32
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ObservableByteBuffer.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.fx;
+
+public class ObservableByteBuffer {
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ObservableDataBuffer.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ObservableDataBuffer.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ObservableDataBuffer.java
new file mode 100644
index 0000000..9f24715
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/ObservableDataBuffer.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.fx;
+
+import javafx.collections.ObservableListBase;
+
+import com.pivotal.javafx.scene.chart.Data;
+
+public class ObservableDataBuffer<X, Y> extends ObservableListBase<Data<X, Y>> {
+
+ @Override
+ public Data<X, Y> get(int index) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean add(Data<X, Y> e) {
+
+
+ return true;
+ }
+
+ @Override
+ public int size() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/StatFileManager.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/StatFileManager.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/StatFileManager.java
new file mode 100644
index 0000000..cae2be9
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/StatFileManager.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.fx;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.geode.jvsd.model.stats.StatArchiveFile;
+
+/**
+ * Basic container for all stat files we are managing.
+ *
+ * @author Jens Deppe
+ */
+public class StatFileManager {
+
+ private List<StatArchiveFile> statFiles = new ArrayList<>();
+
+ private static StatFileManager instance = new StatFileManager();
+
+ private StatFileManager() {
+ // We are a singleton
+ }
+
+ public static StatFileManager getInstance() {
+ return instance;
+ }
+
+ public void add(String[] fileNames) throws IOException {
+ for (String name : fileNames) {
+ statFiles.add(new StatArchiveFile(name));
+ }
+ }
+
+ public void add(String fileName) throws IOException {
+ statFiles.add(new StatArchiveFile(fileName));
+ }
+
+ public List<StatArchiveFile> getArchives() {
+ return statFiles;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/VSDChartWindow.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/VSDChartWindow.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/VSDChartWindow.java
new file mode 100644
index 0000000..40d0474
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/VSDChartWindow.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.fx;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.URL;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import javafx.beans.binding.Bindings;
+import javafx.collections.FXCollections;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.geometry.Side;
+import javafx.scene.chart.Axis;
+import javafx.scene.chart.NumberAxis;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.Pane;
+import javafx.stage.Stage;
+
+import org.apache.geode.jvsd.chart.AdvancedLineChart;
+import org.apache.geode.jvsd.javafx.scene.chart.DateAxis;
+import org.apache.geode.jvsd.model.stats.StatArchiveFile.StatValue;
+import org.apache.geode.jvsd.stats.Utility;
+
+import com.pivotal.javafx.scene.chart.BasicSeries;
+import com.pivotal.javafx.scene.chart.Data;
+
+/**
+ * An advanced line chart with a variety of actions and settable properties.
+ *
+ * @see javafx.scene.chart.LineChart
+ * @see javafx.scene.chart.Chart
+ * @see javafx.scene.chart.NumberAxis
+ * @see javafx.scene.chart.XYChart
+ */
+public class VSDChartWindow implements Initializable {
+
+ private int chartId;
+
+ private Stage stage;
+
+ private AdvancedLineChart<Number, Number> chart;
+
+ private ChartManager manager = ChartManager.getInstance();
+
+ @FXML
+ private Pane root;
+
+ @FXML
+ private Button resetButton;
+
+ @FXML
+ private Label coordLabel;
+
+ public void setStage(Stage stage) {
+ this.stage = stage;
+ this.stage.setOnCloseRequest(eh -> {
+ manager.removeChart(chartId);
+ });
+ }
+
+ public void setId(int chartId) {
+ this.chartId = chartId;
+ }
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) {
+ chart = createChart(root);
+ root.getChildren().add(chart);
+
+ // TODO better way to do this?
+ coordLabel.textProperty().bind(Bindings.format("%s x %s",
+ Bindings.createStringBinding(() -> DateFormat.getInstance().format(
+ chart.getXAxis().getValueForDisplay(
+ chart.crosshairXProperty().doubleValue())),
+ chart.crosshairXProperty()),
+ Bindings.createStringBinding(() -> {
+ final NumberFormat formatter = NumberFormat.getInstance();
+ formatter.setGroupingUsed(true);
+ return formatter.format(
+ chart.getPrimaryYAxis().getValueForDisplay(
+ chart.crosshairYProperty().doubleValue()));
+ }, chart.crosshairYProperty(), chart.primaryYAxisProperty())));
+ }
+
+ @FXML
+ private void resetRangeClicked() {
+ chart.getXAxis().setAutoRanging(true);
+ for (Axis<Number> yAxis : chart.getYAxes()) {
+ yAxis.setAutoRanging(true);
+ }
+ }
+
+ protected AdvancedLineChart<Number, Number> createChart(Pane root) {
+ final DateAxis xAxis = new DateAxis();
+ xAxis.setAutoRanging(true);
+ final NumberAxis yAxis = new NumberAxis();
+ final AdvancedLineChart<Number, Number> lc =
+ new AdvancedLineChart<>(xAxis, yAxis);
+ lc.setCreateSymbols(false);
+ lc.prefWidthProperty().bind(root.widthProperty());
+ lc.prefHeightProperty().bind(root.heightProperty());
+
+ if (ChartManager.MMTEST) {
+//
+// try (final RandomAccessFile memoryMappedFile = new RandomAccessFile("/tmp/test.vsd.stat1.vss", "rw")) {
+//
+// final MappedByteBuffer in = memoryMappedFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, memoryMappedFile.length());
+//// in.limit(1000000);
+// final LongDoubleMemoryMappedSeries s = new LongDoubleMemoryMappedSeries(in);
+// lc.getData().add(s);
+//
+//// Timeline animation = new Timeline();
+//// animation.getKeyFrames().add(new KeyFrame(Duration.seconds(1), actionEvent -> {
+//// in.limit(in.limit()+16);
+//// System.out.println(in.limit());
+////// lc.toFront();
+////// Data<Number, Number> last = s.getData().get(s.getData().size() - 1);
+////// s.getData().add(new Data<>(last.getXValue().longValue() + 1000, last.getYValue().doubleValue() + (new Random().nextDouble() * 1000) - 500));
+////
+//// }));
+//// animation.setCycleCount(Animation.INDEFINITE);
+//// animation.play();
+//
+//
+// } catch (FileNotFoundException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+
+ }
+
+// lc.getData().add(new LongDoubleFileSeries(new File("/tmp/test.vsd.stat1.vss")));
+
+ return lc;
+ }
+
+ public void addToChart(StatValue sv) {
+ if (!stage.titleProperty().isBound()) {
+ stage.setTitle(
+ "Chart" + chartId + "_" + sv.getDescriptor().getName());
+ }
+
+ Axis<Number> yAxis;
+ final String units = sv.getDescriptor().getUnits();
+ yAxis = findOrCreateYAxis(units);
+
+ final long[] timestamps = sv.getRawAbsoluteTimeStamps();
+ final double[] values = sv.getRawSnapshots();
+
+ long timeSampleAverage = Utility.computeTimeSampleAverage(timestamps);
+ long samplePeriod = timeSampleAverage / 1000;
+
+ final List<Data<Number, Number>> data;
+ switch (sv.getFilter()) {
+ case StatValue.FILTER_NONE:
+ data = doFilterNone(samplePeriod, timestamps, values);
+ break;
+ case StatValue.FILTER_PERSEC:
+ data = doFilterPerSec(samplePeriod, timestamps, values);
+ break;
+ default:
+ data = null;
+ }
+
+ // add series data
+ if (ChartManager.MMTEST) {
+ addToChartMM(sv, yAxis, data);
+ } else {
+ BasicSeries<Number, Number> series = new BasicSeries<>(FXCollections.observableList(data));
+ series.setYAxis(yAxis);
+ series.setName(sv.getDescriptor().getName());
+ chart.getData().add(series);
+ }
+ }
+
+ private void addToChartMM(StatValue sv, Axis<Number> yAxis, List<Data<Number, Number>> data) {
+ try {
+ final File tmp = File.createTempFile(sv.getDescriptor().getName(), ".vss");
+ tmp.deleteOnExit();
+
+ try (final RandomAccessFile memoryMappedFile = new RandomAccessFile(tmp, "rw")) {
+ final MappedByteBuffer out = memoryMappedFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, data.size() * 16);
+
+ for (Data<Number, Number> d : data) {
+ out.putLong(d.getXValue().longValue());
+ out.putDouble(d.getYValue().doubleValue());
+ }
+ out.force();
+
+ final LongDoubleMemoryMappedSeries s = new LongDoubleMemoryMappedSeries(out);
+ s.setYAxis(yAxis);
+ s.setName(sv.getDescriptor().getName());
+ chart.getData().add(s);
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ protected List<Data<Number, Number>> doFilterNone(long samplePeriod, final long[] timestamps, final double[] values) {
+ final ArrayList<Data<Number, Number>> list = new ArrayList<>(timestamps.length - 1);
+
+ // skip first value to calc value/s.
+ for (int i = 1; i < timestamps.length; i++) {
+ final Data<Number, Number> data = new Data<Number, Number>(timestamps[i], values[i]);
+
+//TODO speed up data.setNode(new HoveredThresholdNode(numberFormat.format(values[i])));
+ list.add(data);
+ }
+
+ return list;
+ }
+
+ protected List<Data<Number, Number>> doFilterPerSec(long samplePeriod, final long[] timestamps, final double[] values) {
+ final ArrayList<Data<Number, Number>> list = new ArrayList<>(timestamps.length - 1);
+
+ // skip first value to calc value/s.
+ for (int i = 1; i < timestamps.length; i++) {
+ final long timestamp = timestamps[i];
+ final double psValue = Utility.computePerSecondValue(timestamp, timestamps[i - 1], values[i], values[i - 1], samplePeriod);
+ final Data<Number, Number> data = new Data<Number, Number>(timestamp, psValue);
+
+//TODO speed up data.setNode(new HoveredThresholdNode(numberFormat.format(psValue)));
+ list.add(data);
+ }
+
+ return list;
+ }
+
+ private Axis<Number> findOrCreateYAxis(final String units) {
+ Axis<Number> yAxis = findYAxis(units);
+
+ if (null == yAxis) {
+ yAxis = new NumberAxis();
+ yAxis.setSide(Side.RIGHT);
+ chart.getYAxes().add(yAxis);
+ }
+
+ if (null == yAxis.getLabel()) {
+ yAxis.setLabel(units);
+ }
+
+ return yAxis;
+ }
+
+ private Axis<Number> findYAxis(final String units) {
+ for (Axis<Number> y : chart.getYAxes()) {
+ if (null == y.getLabel() || y.getLabel().equals(units)) {
+ return y;
+ }
+ }
+
+ return null;
+ }
+
+
+ public String getTitle() {
+ return stage.getTitle();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8a86c01a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/VsdSeries.java
----------------------------------------------------------------------
diff --git a/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/VsdSeries.java b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/VsdSeries.java
new file mode 100644
index 0000000..25c8ef1
--- /dev/null
+++ b/geode-jvsd/src/main/java/org/apache/geode/jvsd/fx/VsdSeries.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.jvsd.fx;
+
+import java.nio.MappedByteBuffer;
+
+public class VsdSeries {
+ // TODO concurrency
+
+ private MappedByteBuffer buffer = null;
+
+ private int length = 0;
+
+
+
+
+ public void addData(long time, double value) {
+ if (buffer.remaining() < 8) {
+ expandBuffer();
+ }
+
+ buffer.putLong(time);
+ buffer.putDouble(value);
+ length++;
+
+ // TODO notify change
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ private void expandBuffer() {
+ // TODO Auto-generated method stub
+
+ }
+}