Revise ChartModel and add ChartRequestHandler, ChartUtils classes
Change-Id: I9c72122368c8270df9a1055845281e999d488243
diff --git a/core/api/src/main/java/org/onosproject/ui/chart/ChartModel.java b/core/api/src/main/java/org/onosproject/ui/chart/ChartModel.java
index 2d24cb4..9493acf 100644
--- a/core/api/src/main/java/org/onosproject/ui/chart/ChartModel.java
+++ b/core/api/src/main/java/org/onosproject/ui/chart/ChartModel.java
@@ -16,16 +16,20 @@
package org.onosproject.ui.chart;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import java.util.Arrays;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
- * A simple model of chart data.
+ * A simple model of time series chart data.
*
* <p>
* Note that this is not a full MVC type model; the expected usage pattern
@@ -35,58 +39,65 @@
*/
public class ChartModel {
- // key is series name, value is series index
- private final Map<String, Integer> seriesMap;
- private final DataPoint[] dataPoints;
+ private final Set<String> seriesSet;
+ private final String[] seriesArray;
+ private final List<Long> labels = Lists.newArrayList();
+ private final List<DataPoint> dataPoints = Lists.newArrayList();
/**
* Constructs a chart model with initialized series set.
*
- * @param size datapoints size
* @param series a set of series
*/
- public ChartModel(int size, String... series) {
+ public ChartModel(String... series) {
checkNotNull(series, "series cannot be null");
checkArgument(series.length > 0, "must be at least one series");
- seriesMap = Maps.newConcurrentMap();
- for (int index = 0; index < series.length; index++) {
- seriesMap.put(series[index], index);
+ seriesSet = Sets.newHashSet(series);
+
+ if (seriesSet.size() != series.length) {
+ throw new IllegalArgumentException("duplicate series detected");
}
- checkArgument(size > 0, "must have at least one data point");
- dataPoints = new DataPoint[size];
+ this.seriesArray = Arrays.copyOf(series, series.length);
}
private void checkDataPoint(DataPoint dataPoint) {
- checkArgument(dataPoint.getSize() == seriesCount(),
+ checkArgument(dataPoint.size() == seriesCount(),
"data size should be equal to number of series");
}
/**
+ * Checks the validity of the given series.
+ *
+ * @param series series name
+ */
+ private void checkSeries(String series) {
+ checkNotNull(series, "must provide a series name");
+ if (!seriesSet.contains(series)) {
+ throw new IllegalArgumentException("unknown series: " + series);
+ }
+ }
+
+ /**
* Returns the number of series in this chart model.
*
* @return number of series
*/
public int seriesCount() {
- return seriesMap.size();
+ return seriesSet.size();
}
/**
- * Shifts all of the data points to the left,
- * and adds a new data point to the tail of the array.
+ * Adds a data point to the chart model.
*
- * @param label label name
- * @param values a set of data values
+ * @return the data point, for chaining
*/
- public void addDataPoint(String label, Double[] values) {
- DataPoint dp = new DataPoint(label, values);
- checkDataPoint(dp);
-
- for (int index = 1; index < dataPoints.length; index++) {
- dataPoints[index - 1] = dataPoints[index];
- }
- dataPoints[dataPoints.length - 1] = dp;
+ public DataPoint addDataPoint(Long label) {
+ DataPoint dp = new DataPoint();
+ labels.add(label);
+ dataPoints.add(dp);
+ return dp;
}
/**
@@ -95,16 +106,34 @@
* @return an array of series
*/
public String[] getSeries() {
- return seriesMap.keySet().toArray(new String[seriesMap.size()]);
+ return seriesArray;
}
/**
- * Returns all of data points.
+ * Returns all of data points in order.
*
* @return an array of data points
*/
public DataPoint[] getDataPoints() {
- return Arrays.copyOf(dataPoints, dataPoints.length);
+ return dataPoints.toArray(new DataPoint[dataPoints.size()]);
+ }
+
+ /**
+ * Returns all of labels in order.
+ *
+ * @return an array of labels
+ */
+ public Object[] getLabels() {
+ return labels.toArray(new Long[labels.size()]);
+ }
+
+ /**
+ * Returns the number of data points in this chart model.
+ *
+ * @return number of data points
+ */
+ public int dataPointCount() {
+ return dataPoints.size();
}
/**
@@ -113,7 +142,7 @@
* @return data point
*/
public DataPoint getLastDataPoint() {
- return dataPoints[dataPoints.length - 1];
+ return dataPoints.get(dataPoints.size() - 1);
}
/**
@@ -121,47 +150,52 @@
*/
public class DataPoint {
// values for all series
- private final Double[] values;
- private final String label;
+ private final Map<String, Double> data = Maps.newHashMap();
/**
- * Constructs a data point.
+ * Sets the data value for the given series of this data point.
*
- * @param label label name
- * @param values a set of data values for all series
+ * @param series series name
+ * @param value value to set
+ * @return self, for chaining
*/
- public DataPoint(String label, Double[] values) {
- this.label = label;
- this.values = values;
+ public DataPoint data(String series, Double value) {
+ checkSeries(series);
+ data.put(series, value);
+ return this;
}
/**
- * Returns the label name of this data point.
+ * Returns the data value with the given series for this data point.
*
- * @return label name
+ * @return data value
*/
- public String getLabel() {
- return label;
+ public Double get(String series) {
+ return data.get(series);
+ }
+
+ /**
+ * Return the data value with the same order of series.
+ *
+ * @return an array of ordered data values
+ */
+ public Double[] getAll() {
+ Double[] value = new Double[getSeries().length];
+ int idx = 0;
+ for (String s : getSeries()) {
+ value[idx] = get(s);
+ idx++;
+ }
+ return value;
}
/**
* Returns the size of data point.
- * This should be identical to the size of series.
*
- * @return size of data point
+ * @return the size of data point
*/
- public int getSize() {
- return values.length;
- }
-
- /**
- * Returns the value of the data point of the given series.
- *
- * @param series series name
- * @return data value of a specific series
- */
- public Double getValue(String series) {
- return values[seriesMap.get(series)];
+ public int size() {
+ return data.size();
}
}
}
diff --git a/core/api/src/main/java/org/onosproject/ui/chart/ChartRequestHandler.java b/core/api/src/main/java/org/onosproject/ui/chart/ChartRequestHandler.java
new file mode 100644
index 0000000..5fba98c
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/chart/ChartRequestHandler.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.ui.chart;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.ui.RequestHandler;
+
+/**
+ * Message handler specifically for the chart views.
+ */
+public abstract class ChartRequestHandler extends RequestHandler {
+
+ private final String respType;
+ private final String nodeName;
+
+ /**
+ * Constructs a chart model handler for a specific graph view. When chart
+ * requests come in, the handler will generate the appropriate chart data
+ * points and send back the response to the client.
+ *
+ * @param reqType type of the request event
+ * @param respType type of the response event
+ * @param nodeName name of JSON node holding data point
+ */
+ public ChartRequestHandler(String reqType, String respType, String nodeName) {
+ super(reqType);
+ this.respType = respType;
+ this.nodeName = nodeName;
+ }
+
+ @Override
+ public void process(long sid, ObjectNode payload) {
+ ChartModel cm = createChartModel();
+ populateChart(cm, payload);
+
+ ObjectNode rootNode = MAPPER.createObjectNode();
+ rootNode.set(nodeName, ChartUtils.generateDataPointArrayNode(cm));
+ sendMessage(respType, 0, rootNode);
+ }
+
+ /**
+ * Creates the chart model using {@link #getSeries()}
+ * to initialize it, ready to be populated.
+ * <p>
+ * This default implementation returns a chart model for all series.
+ * </p>
+ *
+ * @return an empty chart model
+ */
+ protected ChartModel createChartModel() {
+ return new ChartModel(getSeries());
+ }
+
+ /**
+ * Subclasses should return the array of series with which to initialize
+ * their chart model.
+ *
+ * @return the series name
+ */
+ protected abstract String[] getSeries();
+
+ /**
+ * Subclasses should populate the chart model by adding
+ * {@link ChartModel.DataPoint datapoints}.
+ * <pre>
+ * cm.addDataPoint()
+ * .data(SERIES_ONE, ...)
+ * .data(SERIES_TWO, ...)
+ * ... ;
+ * </pre>
+ * The request payload is provided in case there are request filtering
+ * parameters.
+ *
+ * @param cm the chart model
+ * @param payload request payload
+ */
+ protected abstract void populateChart(ChartModel cm, ObjectNode payload);
+}
diff --git a/core/api/src/main/java/org/onosproject/ui/chart/ChartUtils.java b/core/api/src/main/java/org/onosproject/ui/chart/ChartUtils.java
new file mode 100644
index 0000000..959a2b0
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/chart/ChartUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.ui.chart;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Provides static utility methods for dealing with charts.
+ */
+public final class ChartUtils {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ // non-instantiable
+ private ChartUtils() {
+ }
+
+ /**
+ * Generates a JSON array node from the data points of the given chart model.
+ *
+ * @param cm the chart model
+ * @return the array node representation of data points
+ */
+ public static ArrayNode generateDataPointArrayNode(ChartModel cm) {
+ ArrayNode array = MAPPER.createArrayNode();
+ for (ChartModel.DataPoint dp : cm.getDataPoints()) {
+ array.add(toJsonNode(dp, cm));
+ }
+ return array;
+ }
+
+ /**
+ * Generate a JSON node from the data point and given chart model.
+ *
+ * @param dp the data point
+ * @param cm the chart model
+ * @return the node representation of a data point with series
+ */
+ public static JsonNode toJsonNode(ChartModel.DataPoint dp, ChartModel cm) {
+ ObjectNode result = MAPPER.createObjectNode();
+ String[] series = cm.getSeries();
+ Double[] values = dp.getAll();
+ int n = series.length;
+ for (int i = 0; i < n; i++) {
+ result.put(series[i], values[i]);
+ }
+ return result;
+ }
+}
diff --git a/core/api/src/test/java/org/onosproject/ui/chart/ChartModelTest.java b/core/api/src/test/java/org/onosproject/ui/chart/ChartModelTest.java
index da1365d..0a87635 100644
--- a/core/api/src/test/java/org/onosproject/ui/chart/ChartModelTest.java
+++ b/core/api/src/test/java/org/onosproject/ui/chart/ChartModelTest.java
@@ -17,10 +17,7 @@
import org.junit.Test;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
/**
* Unit tests for {@link ChartModel}.
@@ -35,57 +32,97 @@
private static final Double[] VALUES2 = {3D, 4D, 5D};
private static final Double[] VALUES3 = {6D, 7D, 8D};
- private static final String[] SERIES = {FOO, BAR, ZOO};
-
private ChartModel cm;
@Test(expected = NullPointerException.class)
public void guardAgainstNullSeries() {
- cm = new ChartModel(1, null);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void guardAgainstWrongDpNumber() {
- cm = new ChartModel(0, FOO);
+ cm = new ChartModel(null);
}
@Test
public void testSeriesCount() {
- cm = new ChartModel(1, FOO, BAR, ZOO);
+ cm = new ChartModel(FOO, BAR, ZOO);
assertEquals("Wrong series count", 3, cm.seriesCount());
}
@Test
+ public void emptyLabel() {
+ cm = new ChartModel(FOO, BAR, ZOO);
+ cm.addDataPoint(System.currentTimeMillis());
+
+ assertEquals("bad data point count", 1, cm.dataPointCount());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void dataPointBandSeries() {
+ cm = new ChartModel(FOO, BAR);
+
+ cm.addDataPoint(System.currentTimeMillis())
+ .data(ZOO, VALUES3[0]);
+ }
+
+ @Test
public void testAddDataPoint() {
- cm = new ChartModel(2, FOO, BAR, ZOO);
+ cm = new ChartModel(FOO, BAR, ZOO);
- cm.addDataPoint("1", VALUES1);
- cm.addDataPoint("2", VALUES2);
+ long time = System.currentTimeMillis();
- assertEquals("Wrong result", "1", cm.getDataPoints()[0].getLabel());
- assertEquals("Wrong result", "2", cm.getDataPoints()[1].getLabel());
+ cm.addDataPoint(time)
+ .data(FOO, VALUES1[0])
+ .data(BAR, VALUES2[0])
+ .data(ZOO, VALUES3[0]);
- cm.addDataPoint("3", VALUES3);
+ cm.addDataPoint(time + 1)
+ .data(FOO, VALUES1[1])
+ .data(BAR, VALUES2[1])
+ .data(ZOO, VALUES3[1]);
- assertEquals("Wrong result", "2", cm.getDataPoints()[0].getLabel());
- assertEquals("Wrong result", "3", cm.getDataPoints()[1].getLabel());
+ cm.addDataPoint(time + 2)
+ .data(FOO, VALUES1[2])
+ .data(BAR, VALUES2[2])
+ .data(ZOO, VALUES3[2]);
+
+ assertEquals("Wrong result", 3, cm.getDataPoints()[0].size());
+ assertEquals("Wrong result", 3, cm.getDataPoints()[1].size());
+ assertEquals("Wrong result", 3, cm.getDataPoints()[2].size());
+ assertEquals("Wrong result", 3, cm.getDataPoints().length);
}
@Test
- public void testGetData() {
- cm = new ChartModel(2, FOO, BAR, ZOO);
+ public void testGetDataPoint() {
+ cm = new ChartModel(FOO, BAR);
- cm.addDataPoint("1", VALUES1);
- assertThat(cm.getLastDataPoint().getValue(ZOO), is(2D));
+ long time = System.currentTimeMillis();
- cm.addDataPoint("2", VALUES2);
- assertThat(cm.getLastDataPoint().getValue(BAR), is(4D));
+ cm.addDataPoint(time)
+ .data(FOO, VALUES1[0])
+ .data(BAR, VALUES2[0]);
+
+ cm.addDataPoint(time + 1)
+ .data(FOO, VALUES1[1])
+ .data(BAR, VALUES2[1]);
+
+ assertEquals("Wrong result", (Double) 0D, cm.getDataPoints()[0].get(FOO));
+ assertEquals("Wrong result", (Double) 1D, cm.getDataPoints()[1].get(FOO));
+ assertEquals("Wrong result", (Double) 3D, cm.getDataPoints()[0].get(BAR));
+ assertEquals("Wrong result", (Double) 4D, cm.getDataPoints()[1].get(BAR));
}
@Test
- public void testGetSeries() {
- cm = new ChartModel(1, FOO, BAR, ZOO);
+ public void testGetLastDataPoint() {
+ cm = new ChartModel(FOO, BAR);
- assertArrayEquals("series", SERIES, cm.getSeries());
+ long time = System.currentTimeMillis();
+
+ cm.addDataPoint(time)
+ .data(FOO, VALUES1[0])
+ .data(BAR, VALUES2[0]);
+
+ cm.addDataPoint(time + 1)
+ .data(FOO, VALUES1[1])
+ .data(BAR, VALUES2[1]);
+
+ assertEquals("Wrong result", VALUES1[1], cm.getLastDataPoint().get(FOO));
+ assertEquals("Wrong result", VALUES2[1], cm.getLastDataPoint().get(BAR));
}
}
diff --git a/core/api/src/test/java/org/onosproject/ui/chart/ChartUtilsTest.java b/core/api/src/test/java/org/onosproject/ui/chart/ChartUtilsTest.java
new file mode 100644
index 0000000..13059d6
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/ui/chart/ChartUtilsTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed 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.onosproject.ui.chart;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link ChartUtils}.
+ */
+public class ChartUtilsTest {
+
+ private static final String FOO = "foo";
+ private static final String BAR = "bar";
+
+ private static final String ARRAY_AS_STRING =
+ "[{\"foo\":1.0,\"bar\":2.0},{\"foo\":3.0,\"bar\":4.0}]";
+
+ @Test
+ public void basic() {
+ ChartModel cm = new ChartModel(FOO, BAR);
+ cm.addDataPoint(1L).data(FOO, 1D).data(BAR, 2D);
+ cm.addDataPoint(2L).data(FOO, 3D).data(BAR, 4D);
+
+ ArrayNode array = ChartUtils.generateDataPointArrayNode(cm);
+ Assert.assertEquals("wrong results", ARRAY_AS_STRING, array.toString());
+ }
+}