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:28 UTC
[03/15] incubator-geode git commit: GEODE-341: Refactor Java packages
to reflect Apache organization. - Root package changed to
"org.apache.geode.jvsd". - pom.xml changed to use "org.apache.geode" as the
groupId.
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8c8a9cb6/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/AbstractSeries.java
----------------------------------------------------------------------
diff --git a/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/AbstractSeries.java b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/AbstractSeries.java
new file mode 100644
index 0000000..27bbeb0
--- /dev/null
+++ b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/AbstractSeries.java
@@ -0,0 +1,289 @@
+/*
+ * 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.javafx.scene.chart;
+
+import java.util.stream.Collectors;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyObjectProperty;
+import javafx.beans.property.ReadOnlyObjectWrapper;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.StringProperty;
+import javafx.beans.property.StringPropertyBase;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.ObservableList;
+import javafx.scene.Node;
+import javafx.scene.chart.Axis;
+
+import com.pivotal.javafx.scene.chart.Data;
+import com.pivotal.javafx.scene.chart.LegendItem;
+import com.pivotal.javafx.scene.chart.MultiAxisChart;
+import com.pivotal.javafx.scene.chart.Series;
+
+//import com.pivotal.com.sun.javafx.charts.LegendItem;
+
+/**
+ * A named series of data items
+ */
+public abstract class AbstractSeries<X extends Number,Y extends Number> implements Series<X, Y> {
+
+ // -------------- PRIVATE PROPERTIES ----------------------------------------
+
+ /** the style class for default color for this series */
+ protected String defaultColorStyleClass;
+
+ // TODO make property
+ @Override
+ public String getDefaultColorStyleClass() {
+ return defaultColorStyleClass;
+ }
+
+ @Override
+ public void setDefaultColorStyleClass(String defaultColorStyleClass) {
+ this.defaultColorStyleClass = defaultColorStyleClass;
+ }
+
+// // TODO remove
+// protected Data<X,Y> begin = null; // start pointer of a data linked list.
+//
+// // TODO remove
+// @Override
+// public Data<X, Y> getBegin() {
+// return begin;
+// }
+//
+// // TODO remove
+// @Override
+// public void setBegin(Data<X, Y> begin) {
+// this.begin = begin;
+// }
+
+ /*
+ * Next pointer for the next series. We maintain a linkedlist of the
+ * serieses so even after the series is deleted from the list,
+ * we have a reference to it - needed by BarChart e.g.
+ */
+ // TODO remove
+ protected Series<X,Y> next = null;
+
+ // TODO remove
+ @Override
+ public Series<X, Y> getNext() {
+ return next;
+ }
+
+ // TODO remove
+ @Override
+ public void setNext(Series<X, Y> next) {
+ this.next = next;
+ }
+
+
+ // -------------- PUBLIC PROPERTIES ----------------------------------------
+
+ /** Reference to the chart this series belongs to */
+ private final ReadOnlyObjectWrapper<MultiAxisChart<X, Y>> chart = new ReadOnlyObjectWrapper<MultiAxisChart<X, Y>>(this, "chart");
+
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#getChart()
+ */
+ @Override
+ public final MultiAxisChart<X,Y> getChart() { return chart.get(); }
+ @Override
+ public void setChart(MultiAxisChart<X,Y> value) { chart.set(value); }
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#chartProperty()
+ */
+ @Override
+ public final ReadOnlyObjectProperty<MultiAxisChart<X,Y>> chartProperty() { return chart.getReadOnlyProperty(); }
+
+ /** Reference to the Y Axis this chart belongs to */
+ private final ReadOnlyObjectWrapper<Axis<Y>> yAxis = new ReadOnlyObjectWrapper<Axis<Y>>(null, "yAxis") {
+ protected void invalidated() {
+ final Axis<Y> axis = get();
+ axis.autoRangingProperty().addListener(new ChangeListener<Boolean>() {
+ @Override
+ public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+ if (newValue) {
+ // TODO keep lists in series changed listener?
+ axis.invalidateRange(getData().stream().map((Data<X,Y> d) -> d.getYValue()).collect(Collectors.toList()));
+ }
+ }
+ });
+ };
+ };
+
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#getYAxis()
+ */
+ @Override
+ public final Axis<Y> getYAxis() { return yAxis.get(); }
+ // TODO change axis event
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#setYAxis(javafx.scene.chart.Axis)
+ */
+ @Override
+ public void setYAxis(Axis<Y> value) { yAxis.set(value); }
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#yAxisProperty()
+ */
+ @Override
+ public final ReadOnlyObjectProperty<Axis<Y>> yAxisProperty() { return yAxis.getReadOnlyProperty(); }
+
+ /** Reference to the legend for this series */
+ private final ReadOnlyObjectWrapper<LegendItem> legendItem = new ReadOnlyObjectWrapper<>(null, "legendItem");
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#getLegendItem()
+ */
+ @Override
+ public final LegendItem getLegendItem() { return legendItem.get(); }
+
+ @Override
+ public void setLegendItem(LegendItem value) { legendItem.set(value); }
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#legendItemProperty()
+ */
+ @Override
+ public final ReadOnlyObjectProperty<LegendItem> legendItemProperty() { return legendItem.getReadOnlyProperty(); }
+
+
+ /** The user displayable name for this series */
+ private final StringProperty name = new StringPropertyBase() {
+ @Override protected void invalidated() {
+ get(); // make non-lazy
+ // TODO: We don't have visibility to execute the method, check if there's another way to call updateLegend() and requestChartLayout() from outside of MultiAxisChart.
+// if(getChart() != null) getChart().seriesNameChanged();
+ if(getChart() != null) getChart().requestLayout();
+ }
+
+ @Override
+ public Object getBean() {
+ return AbstractSeries.this;
+ }
+
+ @Override
+ public String getName() {
+ return "name";
+ }
+ };
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#getName()
+ */
+ @Override
+ public final String getName() { return name.get(); }
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#setName(java.lang.String)
+ */
+ @Override
+ public final void setName(String value) { name.set(value); }
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#nameProperty()
+ */
+ @Override
+ public final StringProperty nameProperty() { return name; }
+
+ /**
+ * The node to display for this series. This is created by the chart if it uses nodes to represent the whole
+ * series. For example line chart uses this for the line but scatter chart does not use it. This node will be
+ * set as soon as the series is added to the chart. You can then get it to add mouse listeners etc.
+ */
+ private ObjectProperty<Node> node = new SimpleObjectProperty<Node>(this, "node");
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#getNode()
+ */
+ @Override
+ public final Node getNode() { return node.get(); }
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#setNode(javafx.scene.Node)
+ */
+ @Override
+ public final void setNode(Node value) { node.set(value); }
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#nodeProperty()
+ */
+ @Override
+ public final ObjectProperty<Node> nodeProperty() { return node; }
+
+
+ // -------------- CONSTRUCTORS ----------------------------------------------
+
+ /**
+ * Construct a empty series
+ */
+ public AbstractSeries() {
+ }
+
+ /**
+ * 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 AbstractSeries(String name, ObservableList<Data<X,Y>> data) {
+ setName(name);
+ }
+
+ // -------------- PUBLIC METHODS ----------------------------------------------
+
+ /* (non-Javadoc)
+ * @see com.pivotal.javafx.scene.chart.Series#toString()
+ */
+ @Override public String toString() {
+ return "Series["+getName()+"]";
+ }
+
+ // -------------- PRIVATE/PROTECTED METHODS -----------------------------------
+
+ /*
+ * The following methods are for manipulating the pointers in the linked list
+ * when data is deleted.
+ */
+// @Override
+// public void removeDataItemRef(Data<X,Y> item) {
+// if (begin == item) {
+// begin = item.next;
+// } else {
+// Data<X,Y> ptr = begin;
+// while(ptr != null && ptr.next != item) {
+// ptr = ptr.next;
+// }
+// if(ptr != null) ptr.next = item.next;
+// }
+// }
+
+ @Override
+ public int getItemIndex(Data<X,Y> item) {
+// int itemIndex = 0;
+// for (Data<X,Y> d = begin; d != null; d = d.next) {
+// if (d == item) break;
+// itemIndex++;
+// }
+// return itemIndex;
+ return getData().indexOf(item);
+ }
+
+ @Override
+ public int getDataSize() {
+// int count = 0;
+// for (Data<X,Y> d = begin; d != null; d = d.next) {
+// count++;
+// }
+// return count;
+ return getData().size();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8c8a9cb6/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/BasicDataSet.java
----------------------------------------------------------------------
diff --git a/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/BasicDataSet.java b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/BasicDataSet.java
new file mode 100644
index 0000000..3328328
--- /dev/null
+++ b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/BasicDataSet.java
@@ -0,0 +1,152 @@
+/*
+ * 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.javafx.scene.chart;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import com.pivotal.javafx.scene.chart.Data;
+
+import javafx.beans.InvalidationListener;
+
+public class BasicDataSet<X extends Number & Comparable<X>, Y extends Number & Comparable<Y>> implements DataSet<X, Y> {
+
+ protected final List<Data<X,Y>> list = new ArrayList<>();
+
+ public void addData(final Data<X,Y> data) {
+ list.add(data);
+
+ // TODO fire change;
+ }
+
+ @Override
+ public List<Data<X, Y>> getData(final X from, final X to, final int limit) {
+
+ List<Data<X, Y>> visible = list.stream().filter(new Predicate<Data<X,Y>>() {
+ @Override
+ public boolean test(Data<X, Y> item) {
+ final X x = item.getXValue();
+ return between(from, to, x);
+ }
+
+ }).collect(Collectors.toList());
+
+ //System.out.println(getData().size() + " - " + visible.size());
+
+ if (visible.size() > limit) {
+ visible = getVisibleDataSubsampled(visible, limit);
+ }
+
+ return visible;
+ }
+
+ private List<Data<X, Y>> getVisibleDataSubsampled(List<Data<X, Y>> data, int threshold) {
+ // Bucket size. Leave room for start and end data points
+ final int dataLength = data.size();
+ final double bucketSize = (double) (dataLength - 2) / (threshold - 2);
+ final ArrayList<Data<X,Y>> sampled = new ArrayList<>(threshold);
+
+ int a = 0; // Initially a is the first point in the triangle
+ int nextA = 0;
+
+ sampled.add(data.get(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++) {
+ final Data<X, Y> item = data.get(pointCStart);
+ // TODO use the axis position? Don't have to assume number type then
+ pointCX += item.getXValue().doubleValue();
+ pointCY +=item.getYValue().doubleValue();
+ }
+ pointCX /= pointCSize;
+ pointCY /= pointCSize;
+
+ // Point a
+ final double pointAX;
+ final double pointAY;
+ {
+ final Data<X, Y> item = data.get(a);
+ pointAX = item.getXValue().doubleValue();
+ pointAY = item.getYValue().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;
+ for (; pointBStart < pointBEnd; pointBStart++) {
+ // Calculate triangle area over three buckets
+ final Data<X, Y> item = data.get(pointBStart);
+ final double pointBX = item.getXValue().doubleValue();
+ final double pointBY = item.getYValue().doubleValue();
+ final double area = Math.abs((pointAX - pointCX) * (pointBY - pointAY) - (pointAX - pointBX)
+ * (pointCY - pointAY)) * 0.5;
+ if (area > maxArea) {
+ maxArea = area;
+ nextA = pointBStart; // Next a is this b
+ }
+ }
+
+ // Pick this point from the bucket
+ sampled.add(data.get(nextA));
+ a = nextA; // This a is the next a (chosen b)
+ }
+
+ // Always add last
+ sampled.add(data.get(dataLength - 1));
+
+ return sampled;
+ }
+
+ @Override
+ public void addListener(InvalidationListener listener) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void removeListener(InvalidationListener listener) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void addListener(DataChangedListener listener) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void removeListener(DataChangedListener listener) {
+ // TODO Auto-generated method stub
+
+ }
+
+ protected static <T extends Comparable<T>> boolean between(final T from, final T to, final T value) {
+ return from.compareTo(value) >= 0 && to.compareTo(value) <= 0;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8c8a9cb6/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/ByteBufferNumberSeries.java
----------------------------------------------------------------------
diff --git a/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/ByteBufferNumberSeries.java b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/ByteBufferNumberSeries.java
new file mode 100644
index 0000000..475334b
--- /dev/null
+++ b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/ByteBufferNumberSeries.java
@@ -0,0 +1,450 @@
+/*
+ * 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.javafx.scene.chart;
+
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.text.NumberFormat;
+import java.util.AbstractCollection;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.scene.Node;
+import javafx.scene.chart.Axis;
+
+import org.apache.geode.jvsd.fx.HoveredThresholdNode;
+
+import com.pivotal.javafx.scene.chart.BasicSeries;
+import com.pivotal.javafx.scene.chart.Data;
+
+/**
+ * A named series of data items
+ */
+public abstract class ByteBufferNumberSeries<X extends Number, Y extends Number> extends BasicSeries<X, Y> {
+
+ // -------------- PUBLIC PROPERTIES ----------------------------------------
+
+ // TODO remove
+ /** ObservableList of data items that make up this series */
+ private final ObjectProperty<ByteBuffer> bufferProperty = new SimpleObjectProperty<ByteBuffer>() {
+ protected void invalidated() {
+ buffer = get();
+ }
+ };
+
+ // here for speed over getBuffer();
+ protected ByteBuffer buffer;
+
+ public final ByteBuffer getBuffer() {
+ return bufferProperty.get();
+ }
+
+ public final void setBuffer(ByteBuffer value) {
+ bufferProperty.setValue(value);
+ }
+
+ public final ObjectProperty<ByteBuffer> bufferProperty() {
+ return bufferProperty;
+ }
+
+// // TODO remove
+// /** ObservableList of data items that make up this series */
+// private final ObjectProperty<ObservableList<Data<X, Y>>> data = new ObjectPropertyBase<ObservableList<Data<X, Y>>>() {
+// @Override
+// protected void invalidated() {
+// // TODO update buffer, minX, maxX, etc.
+// }
+//
+// @Override
+// public Object getBean() {
+// return ByteBufferNumberSeries.this;
+// }
+//
+// @Override
+// public String getName() {
+// return "data";
+// }
+// };
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.pivotal.javafx.scene.chart.Series#getData()
+// */
+// // TODO remove
+// @Override
+// public final ObservableList<Data<X, Y>> getData() {
+// return data.getValue();
+// // System.out.println("getData:");
+// // return ;
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see
+// * com.pivotal.javafx.scene.chart.Series#setData(javafx.collections.ObservableList
+// * )
+// */
+// // TODO remove
+// @Override
+// public final void setData(ObservableList<Data<X, Y>> value) {
+// data.setValue(value);
+// }
+//
+// /*
+// * (non-Javadoc)
+// *
+// * @see com.pivotal.javafx.scene.chart.Series#dataProperty()
+// */
+// // TODO remove
+// @Override
+// public final ObjectProperty<ObservableList<Data<X, Y>>> dataProperty() {
+// return data;
+// }
+
+ // -------------- CONSTRUCTORS ----------------------------------------------
+
+ /**
+ * Construct a empty series
+ */
+ public ByteBufferNumberSeries() {
+ super();
+ buffer = null;
+ }
+
+ /**
+ * Constructs a Series and populates it with the given {@link ObservableList}
+ * data.
+ *
+ * @param data
+ * ObservableList of MultiAxisChart.Data
+ */
+ public ByteBufferNumberSeries(ByteBuffer buffer) {
+ super();
+ setBuffer(buffer);
+ //setData(FXCollections.observableArrayList(new Data(0, 1000000), new Data(10000000000000l, -100000)));
+ setData(FXCollections.observableArrayList(new ThresholdDataCollection(0, getDataSize(), 1000)));
+
+ }
+
+ /**
+ * 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 ByteBufferNumberSeries(String name, ByteBuffer buffer) {
+ this(buffer);
+ setName(name);
+ }
+
+ @Override
+ public Collection<Data<X, Y>> getVisibleData() {
+
+ final Axis<X> xAxis = getChart().getXAxis();
+ final double width = xAxis.getWidth();
+
+ final int end = getDataSize() - 1;
+
+ final double min = getX(0);
+ final double max = getX(end);
+
+ final double left = xAxis.getValueForDisplay(0).doubleValue();
+ final double right = xAxis.getValueForDisplay(width).doubleValue();
+
+ // find file position for first visible point
+ int first;
+ if (left <= min) {
+ first = 0;
+ } else if (left >= max) {
+ first = end;
+ } else {
+ // time series should be linear on x, so guess file position and scan
+ first = (int) (((left - min) / (max - min)) * end);
+ if (getX(first) < left) {
+ first = findFirst(first + 1, left) - 1;
+ } else {
+ first = findLast(first - 1, left);
+ }
+ }
+
+ // find file position for last visible point
+ int last;
+ if (right <= min) {
+ last = 0;
+ } else if (right >= max) {
+ last = end;
+ } else {
+ // time series should be linear on x, so guess file position and scan
+ last = (int) (((right - min) / (max - min)) * end);
+ if (getX(last) < right) {
+ last = findFirst(last + 1, right);
+ } else {
+ last = findLast(last - 1, right) + 1;
+ }
+ }
+
+ // fit threshold to series width
+ int threshold = (int) width;
+ if (left < min || right > max) {
+ // there is space on either/both ends so adjust the threshold
+ threshold = Math.max((int) (xAxis.getDisplayPosition(convertX(Math.min(right, max))) - xAxis.getDisplayPosition(convertX(Math.max(left, min)))), 3);
+ }
+
+ // using 2 times the width makes it look a little smoother. Consider dynamic bucket size algorithm.
+ // you can still seem some points blink in and out on zoom and pan
+ // maybe used fixed points for bucket boundaries, first/last buckets will shrink by first/last data position
+ final Collection<Data<X, Y>> v = new ThresholdDataCollection(first, last - first + 1, threshold);
+
+ return v;
+ }
+
+
+
+ protected Data<X, Y> getData(final int index) {
+ Data<X, Y> data = createData(index);
+ data.setNode(createSymbol(data));
+ return data;
+ }
+
+
+ private Node createSymbol(Data<X, Y> data) {
+ return new HoveredThresholdNode(NumberFormat.getNumberInstance().format(data.getYValue()));
+ }
+
+
+ // TODO Make these Abstract
+ @Override
+ public abstract int getDataSize();
+
+ protected abstract Data<X,Y> createData(final int index);
+
+ protected abstract double getX(final int index);
+
+ protected abstract X convertX(double x);
+
+ protected abstract double getY(final int index);
+
+ protected abstract Y convertY(double y);
+
+ protected int findFirst(final int start, final double left) {
+ // TODO improve over scan? since likely linear chances are we hit on first try.
+ for (int i = start; i < getDataSize(); i++) {
+ if (getX(i) > left) {
+// System.out.println("f: " + (i - start));
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ protected int findLast(final int start, final double right) {
+ // TODO improve over scan? since likely linear chances are we hit on first try.
+ for (int i = start; i >= 0; i--) {
+ if (getX(i) < right) {
+// System.out.println("l: " + (start - i));
+ return i;
+ }
+ }
+
+ return getDataSize() - 1;
+ }
+
+ protected class ThresholdDataCollection extends AbstractCollection<Data<X, Y>> {
+ final int offset;
+ final int length;
+ final int threshold;
+ final int size;
+
+ public ThresholdDataCollection(final int offset, final int length, final int threshold) {
+ this.offset = offset;
+ this.length = length;
+ this.threshold = threshold;
+ this.size = Math.min(length, (int) threshold);
+
+ System.out.println(MessageFormat.format("ThresholdDataCollection: offset={0}, length={1}, threshold={2}, end={3}", offset, length, threshold, offset + length));
+ }
+
+ @Override
+ public Iterator<Data<X, Y>> iterator() {
+ if (length > threshold) {
+ return createDownsampleVisibleIterator();
+ } else {
+ return createVisibleIterator();
+ }
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ protected Iterator<Data<X, Y>> createVisibleIterator() {
+ return new VisibleIterator();
+ }
+
+ protected Iterator<Data<X, Y>> createDownsampleVisibleIterator() {
+ return new DownsampleVisibleIterator();
+ }
+
+ protected abstract class AbstractDataIterator implements Iterator<Data<X, Y>> {
+ int index = offset;
+ Data<X, Y> next = null;
+
+ protected abstract void advance();
+
+ protected Data<X, Y> consume() {
+ final Data<X, Y> data = next;
+ next = null;
+ return data;
+ }
+
+ @Override
+ public boolean hasNext() {
+ advance();
+ return (null != next);
+ }
+
+ @Override
+ public Data<X, Y> next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ return consume();
+ }
+
+ }
+
+ protected class VisibleIterator extends AbstractDataIterator {
+
+
+ @Override
+ protected void advance() {
+ if (null != next) {
+ return;
+ }
+
+ if (index >= (offset + length)) {
+ return;
+ }
+
+ next = getData(index);
+ index++;
+ }
+ }
+
+ protected class DownsampleVisibleIterator extends AbstractDataIterator {
+ // Bucket size. Leave room for start and end data points
+ final double bucketSize = (double) (length - 2) / (threshold - 2);
+
+ int bucket = 0;
+ int a = index; // Initially a is the first point in the triangle
+
+ public DownsampleVisibleIterator() {
+ super();
+
+ System.out.println(MessageFormat.format("DownsampleVisibleIterator: bucketSize={0}", bucketSize));
+ }
+
+ @Override
+ protected void advance() {
+ // TODO when zooming out bound to visible width not chart width
+ if (null != next) {
+ return;
+ }
+
+ if (index >= (offset + length)) {
+ return;
+ }
+
+// final long start = System.nanoTime();
+// try {
+
+ if (offset == index) {
+ // Always add the first point
+ next = getData(index);
+ index++;
+ return;
+ }
+
+ if (bucket < threshold - 2) {
+ // final long start2 = System.nanoTime();
+ // Calculate point average for next bucket (containing c)
+ double pointCX = 0;
+ double pointCY = 0;
+ int pointCStart = (int) Math.floor((bucket + 1) * bucketSize) + offset + 1;
+ int pointCEnd = (int) Math.floor((bucket + 2) * bucketSize) + offset + 1;
+ pointCEnd = Math.min(pointCEnd, (offset + length));
+ final int pointCSize = pointCEnd - pointCStart;
+ for (; pointCStart < pointCEnd; pointCStart++) {
+ pointCX += getX(pointCStart);
+ pointCY += getY(pointCStart);
+ }
+ pointCX /= pointCSize;
+ pointCY /= pointCSize;
+ // System.out.println("time2: " + (System.nanoTime() - start2));
+
+ // Point a
+ // TODO cache?
+ final double pointAX = getX(a);
+ final double pointAY = getY(a);
+
+ // Get the range for bucket b
+ int pointBStart = (int) Math.floor((bucket + 0) * bucketSize) + offset + 1;
+ final int pointBEnd = (int) Math.floor((bucket + 1) * bucketSize) + offset + 1;
+ double maxArea = -1;
+ for (; pointBStart < pointBEnd; pointBStart++) {
+ index++;
+ // Calculate triangle area over three buckets
+ final double area = Math.abs((pointAX - pointCX) * (getY(pointBStart) - pointAY) - (pointAX - getX(pointBStart))
+ * (pointCY - pointAY)) * 0.5;
+ if (area > maxArea) {
+ maxArea = area;
+ a = pointBStart; // Next a is this b
+ }
+ }
+
+ // Pick this point from the bucket
+ next = getData(a);
+ bucket++;
+ return;
+ }
+
+ // Always add last
+ assert ((offset + length - 1) == index);
+ next = getData(index);
+ index++;
+// } finally {
+// System.out.println(MessageFormat.format("DownsampleVisibleIterator: bucket={0}, time={1}ms",
+// bucket, ((double) System.nanoTime() - start) / 1000000));
+// }
+ }
+ }
+
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8c8a9cb6/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DataChangedListener.java
----------------------------------------------------------------------
diff --git a/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DataChangedListener.java b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DataChangedListener.java
new file mode 100644
index 0000000..c88696e
--- /dev/null
+++ b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DataChangedListener.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.javafx.scene.chart;
+
+public interface DataChangedListener {
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8c8a9cb6/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DataSet.java
----------------------------------------------------------------------
diff --git a/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DataSet.java b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DataSet.java
new file mode 100644
index 0000000..c234761
--- /dev/null
+++ b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DataSet.java
@@ -0,0 +1,29 @@
+/*
+ * 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.javafx.scene.chart;
+
+import java.util.List;
+
+import com.pivotal.javafx.scene.chart.Data;
+
+public interface DataSet<X, Y> extends javafx.beans.Observable {
+ public List<Data<X, Y>> getData(X from, X to, int limit);
+
+ public void addListener(DataChangedListener listener);
+
+ public void removeListener(DataChangedListener listener);
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8c8a9cb6/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DateAxis.java
----------------------------------------------------------------------
diff --git a/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DateAxis.java b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DateAxis.java
new file mode 100644
index 0000000..f423489
--- /dev/null
+++ b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/javafx/scene/chart/DateAxis.java
@@ -0,0 +1,393 @@
+/*
+ * Portions of this source copied with love from https://bitbucket.org/sco0ter/extfx
+ * under the following license:
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, Christian Schudt
+ *
+ * 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.javafx.scene.chart;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.beans.property.ReadOnlyDoubleWrapper;
+import javafx.scene.chart.ValueAxis;
+import javafx.util.Duration;
+import javafx.util.StringConverter;
+
+import com.sun.javafx.charts.ChartLayoutAnimator;
+
+/**
+ * Implemenation of {@link ValueAxis} similar to {@link NumberAxis} but with
+ * formatting for date and time.
+ *
+ * @author jbarrett
+ *
+ */
+public final class DateAxis extends ValueAxis<Number> {
+
+ private ChartLayoutAnimator animator = new ChartLayoutAnimator(this);
+
+ private Object currentAnimationID;
+
+ private Interval actualInterval = Interval.DECADE;
+
+ /**
+ * Default constructor. By default the lower and upper bound are calculated by
+ * the data.
+ */
+ public DateAxis() {
+ }
+
+ /**
+ * Constructs a date axis with fix lower and upper bounds.
+ *
+ * @param lowerBound
+ * The lower bound.
+ * @param upperBound
+ * The upper bound.
+ */
+ public DateAxis(Long lowerBound, Long upperBound) {
+ this();
+ setAutoRanging(false);
+ setLowerBound(lowerBound);
+ setUpperBound(upperBound);
+ }
+
+ /**
+ * Constructs a date axis with a label and fix lower and upper bounds.
+ *
+ * @param axisLabel
+ * The label for the axis.
+ * @param lowerBound
+ * The lower bound.
+ * @param upperBound
+ * The upper bound.
+ */
+ public DateAxis(String axisLabel, Long lowerBound, Long upperBound) {
+ this(lowerBound, upperBound);
+ setLabel(axisLabel);
+ }
+
+ @Override
+ protected Object autoRange(double minValue, double maxValue, double length, double labelSize) {
+ return new Object[] { (long) minValue, (long) maxValue, calculateNewScale(length, (long) minValue, (long) maxValue) };
+ }
+
+ @Override
+ protected void setRange(Object range, boolean animating) {
+ Object[] r = (Object[]) range;
+ long oldLowerBound = (long) getLowerBound();
+ long lower = (long) r[0];
+ long upper = (long) r[1];
+ double scale = (double) r[2];
+ setLowerBound(lower);
+ setUpperBound(upper);
+
+ if (animating) {
+ animator.stop(currentAnimationID);
+ currentAnimationID = animator.animate(
+ new KeyFrame(Duration.ZERO,
+ new KeyValue(currentLowerBound, oldLowerBound),
+ new KeyValue(scalePropertyImplProtected(), getScale())),
+ new KeyFrame(Duration.millis(700),
+ new KeyValue(currentLowerBound, lower),
+ new KeyValue(scalePropertyImplProtected(), scale)));
+
+ } else {
+ currentLowerBound.set(getLowerBound());
+ setScale(scale);
+ }
+ }
+
+ @Override
+ protected Object getRange() {
+ return new Object[] { (long) getLowerBound(), (long) getUpperBound() };
+ }
+
+ @Override
+ protected List<Number> calculateTickValues(double v, Object range) {
+ Object[] r = (Object[]) range;
+ long lower = (long) r[0];
+ long upper = (long) r[1];
+
+ List<Date> dateList = new ArrayList<Date>();
+ Calendar calendar = Calendar.getInstance();
+
+ // The preferred gap which should be between two tick marks.
+ double averageTickGap = 100;
+ double averageTicks = v / averageTickGap;
+
+ List<Date> previousDateList = new ArrayList<Date>();
+
+ Interval previousInterval = Interval.values()[0];
+
+ // Starting with the greatest interval, add one of each calendar unit.
+ for (Interval interval : Interval.values()) {
+ // Reset the calendar.
+ calendar.setTime(new Date(lower));
+ // Clear the list.
+ dateList.clear();
+ previousDateList.clear();
+ actualInterval = interval;
+
+ // Loop as long we exceeded the upper bound.
+ while (calendar.getTime().getTime() <= upper) {
+ dateList.add(calendar.getTime());
+ calendar.add(interval.interval, interval.amount);
+ }
+ // Then check the size of the list. If it is greater than the amount of
+ // ticks, take that list.
+ if (dateList.size() > averageTicks) {
+ calendar.setTime(new Date(lower));
+ // Recheck if the previous interval is better suited.
+ while (calendar.getTime().getTime() <= upper) {
+ previousDateList.add(calendar.getTime());
+ calendar.add(previousInterval.interval, previousInterval.amount);
+ }
+ break;
+ }
+
+ previousInterval = interval;
+ }
+ if (previousDateList.size() - averageTicks > averageTicks - dateList.size()) {
+ dateList = previousDateList;
+ actualInterval = previousInterval;
+ }
+
+ // At last add the upper bound.
+ dateList.add(new Date(upper));
+
+ List<Number> evenDateList = makeDatesEven(dateList, calendar);
+ // If there are at least three dates, check if the gap between the lower
+ // date and the second date is at least half the gap of the second and third
+ // date.
+ // Do the same for the upper bound.
+ // If gaps between dates are to small, remove one of them.
+ // This can occur, e.g. if the lower bound is 25.12.2013 and years are
+ // shown. Then the next year shown would be 2014 (01.01.2014) which would be
+ // too narrow to 25.12.2013.
+ if (evenDateList.size() > 2) {
+
+ long secondDate = evenDateList.get(1).longValue();
+ long thirdDate = evenDateList.get(2).longValue();
+ long lastDate = evenDateList.get(dateList.size() - 2).longValue();
+ long previousLastDate = evenDateList.get(dateList.size() - 3).longValue();
+
+ // If the second date is too near by the lower bound, remove it.
+ if (secondDate - lower < (thirdDate - secondDate) / 2) {
+ evenDateList.remove(secondDate);
+ }
+
+ // If difference from the upper bound to the last date is less than the
+ // half of the difference of the previous two dates,
+ // we better remove the last date, as it comes to close to the upper
+ // bound.
+ if (upper - lastDate < (lastDate - previousLastDate) / 2) {
+ evenDateList.remove(lastDate);
+ }
+ }
+
+ return evenDateList;
+ }
+
+ @Override
+ protected String getTickMarkLabel(final Number ts) {
+ final Date date = new Date(ts.longValue());
+
+ StringConverter<Number> converter = getTickLabelFormatter();
+ if (converter != null) {
+ return converter.toString(date.getTime());
+ }
+
+ DateFormat dateFormat;
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+
+ if (actualInterval.interval == Calendar.YEAR && calendar.get(Calendar.MONTH) == 0 && calendar.get(Calendar.DATE) == 1) {
+ dateFormat = new SimpleDateFormat("yyyy");
+ } else if (actualInterval.interval == Calendar.MONTH && calendar.get(Calendar.DATE) == 1) {
+ dateFormat = new SimpleDateFormat("MMM yy");
+ } else {
+ switch (actualInterval.interval) {
+ case Calendar.DATE:
+ case Calendar.WEEK_OF_YEAR:
+ default:
+ dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
+ break;
+ case Calendar.HOUR:
+ case Calendar.MINUTE:
+ dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT);
+ break;
+ case Calendar.SECOND:
+ dateFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM);
+ break;
+ case Calendar.MILLISECOND:
+ dateFormat = DateFormat.getTimeInstance(DateFormat.FULL);
+ break;
+ }
+ }
+ return dateFormat.format(date);
+ }
+
+ /**
+ * Makes dates even, in the sense of that years always begin in January,
+ * months always begin on the 1st and days always at midnight.
+ *
+ * @param dates
+ * The list of dates.
+ * @return The new list of dates.
+ */
+ private List<Number> makeDatesEven(List<Date> dates, Calendar calendar) {
+ // If the dates contain more dates than just the lower and upper bounds,
+ // make the dates in between even.
+ // TODO if (dates.size() > 2) {
+ List<Number> evenDates = new ArrayList<Number>();
+
+ // For each interval, modify the date slightly by a few millis, to make sure
+ // they are different days.
+ // This is because Axis stores each value and won't update the tick labels,
+ // if the value is already known.
+ // This happens if you display days and then add a date many years in the
+ // future the tick label will still be displayed as day.
+ for (int i = 0; i < dates.size(); i++) {
+ calendar.setTime(dates.get(i));
+ switch (actualInterval.interval) {
+ case Calendar.YEAR:
+ // If its not the first or last date (lower and upper bound), make the
+ // year begin with first month and let the months begin with first day.
+ if (i != 0 && i != dates.size() - 1) {
+ calendar.set(Calendar.MONTH, 0);
+ calendar.set(Calendar.DATE, 1);
+ }
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 6);
+ break;
+ case Calendar.MONTH:
+ // If its not the first or last date (lower and upper bound), make the
+ // months begin with first day.
+ if (i != 0 && i != dates.size() - 1) {
+ calendar.set(Calendar.DATE, 1);
+ }
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 5);
+ break;
+ case Calendar.WEEK_OF_YEAR:
+ // Make weeks begin with first day of week?
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 4);
+ break;
+ case Calendar.DATE:
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 3);
+ break;
+ case Calendar.HOUR:
+ if (i != 0 && i != dates.size() - 1) {
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ }
+ calendar.set(Calendar.MILLISECOND, 2);
+ break;
+ case Calendar.MINUTE:
+ if (i != 0 && i != dates.size() - 1) {
+ calendar.set(Calendar.SECOND, 0);
+ }
+ calendar.set(Calendar.MILLISECOND, 1);
+ break;
+ case Calendar.SECOND:
+ calendar.set(Calendar.MILLISECOND, 0);
+ break;
+
+ }
+ evenDates.add(calendar.getTime().getTime());
+ }
+
+ return evenDates;
+ // } else {
+ // return dates;
+ // }
+ }
+
+ /**
+ * The intervals, which are used for the tick labels. Beginning with the
+ * largest interval, the axis tries to calculate the tick values for this
+ * interval. If a smaller interval is better suited for, that one is taken.
+ */
+ private enum Interval {
+ DECADE(Calendar.YEAR, 10), YEAR(Calendar.YEAR, 1), MONTH_6(Calendar.MONTH, 6), MONTH_3(Calendar.MONTH, 3), MONTH_1(Calendar.MONTH, 1), WEEK(
+ Calendar.WEEK_OF_YEAR, 1), DAY(Calendar.DATE, 1), HOUR_12(Calendar.HOUR, 12), HOUR_6(Calendar.HOUR, 6), HOUR_3(Calendar.HOUR, 3), HOUR_1(Calendar.HOUR,
+ 1), MINUTE_15(Calendar.MINUTE, 15), MINUTE_5(Calendar.MINUTE, 5), MINUTE_1(Calendar.MINUTE, 1), SECOND_15(Calendar.SECOND, 15), SECOND_5(
+ Calendar.SECOND, 5), SECOND_1(Calendar.SECOND, 1), MILLISECOND(Calendar.MILLISECOND, 1);
+
+ private final int amount;
+
+ private final int interval;
+
+ private Interval(int interval, int amount) {
+ this.interval = interval;
+ this.amount = amount;
+ }
+ }
+
+ @Override
+ protected List<Number> calculateMinorTickMarks() {
+ return Collections.<Number>emptyList();
+ }
+
+ /* Access to package protected method */
+ private static final Method scalePropertyImplMethod;
+
+ static {
+ try {
+ scalePropertyImplMethod = ValueAxis.class.getDeclaredMethod("scalePropertyImpl");
+ scalePropertyImplMethod.setAccessible(true);
+ } catch (SecurityException | NoSuchMethodException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ protected ReadOnlyDoubleWrapper scalePropertyImplProtected() {
+ try {
+ return (ReadOnlyDoubleWrapper) scalePropertyImplMethod.invoke(this);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8c8a9cb6/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/model/ResourceWrapper.java
----------------------------------------------------------------------
diff --git a/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/model/ResourceWrapper.java b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/model/ResourceWrapper.java
new file mode 100644
index 0000000..fdd2f4a
--- /dev/null
+++ b/jvsdfx-mm/src/main/java/org/apache/geode/jvsd/model/ResourceWrapper.java
@@ -0,0 +1,73 @@
+/*
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.geode.jvsd.model.stats.StatArchiveFile;
+
+/**
+* @author Jens Deppe
+*/
+public class ResourceWrapper {
+ private int row;
+
+ private StatArchiveFile.ResourceInst inst;
+
+ public ResourceWrapper(StatArchiveFile.ResourceInst inst, int idx) {
+ this.inst = inst;
+ this.row = idx;
+ }
+
+ public int getRow() {
+ return row;
+ }
+
+ public Date getStartTime() {
+ return new Date(inst.getFirstTimeMillis());
+ }
+
+ public int getSamples() {
+ return inst.getSampleCount();
+ }
+
+ public String getType() {
+ return inst.getType().getName();
+ }
+
+ public String getName() {
+ return inst.getName();
+ }
+
+ public List<String> getStatNames() {
+ List<String> statNames = new ArrayList<>();
+
+ for (StatArchiveFile.StatValue sv : inst.getStatValues()) {
+ if (!(sv.getSnapshotsAverage() == 0 && sv.getSnapshotsMaximum() == 0 && sv.
+ getSnapshotsMinimum() == 0)) {
+ statNames.add(sv.getDescriptor().getName());
+ }
+ }
+ return statNames;
+ }
+
+ public StatArchiveFile.StatValue getStatValue(String name) {
+ return inst.getStatValue(name);
+ }
+}