Jian Li
Committed by Gerrit Code Review

Revise ChartModel and add ChartRequestHandler, ChartUtils classes

Change-Id: I9c72122368c8270df9a1055845281e999d488243
...@@ -16,16 +16,20 @@ ...@@ -16,16 +16,20 @@
16 16
17 package org.onosproject.ui.chart; 17 package org.onosproject.ui.chart;
18 18
19 +import com.google.common.collect.Lists;
19 import com.google.common.collect.Maps; 20 import com.google.common.collect.Maps;
21 +import com.google.common.collect.Sets;
20 22
21 import java.util.Arrays; 23 import java.util.Arrays;
24 +import java.util.List;
22 import java.util.Map; 25 import java.util.Map;
26 +import java.util.Set;
23 27
24 import static com.google.common.base.Preconditions.checkArgument; 28 import static com.google.common.base.Preconditions.checkArgument;
25 import static com.google.common.base.Preconditions.checkNotNull; 29 import static com.google.common.base.Preconditions.checkNotNull;
26 30
27 /** 31 /**
28 - * A simple model of chart data. 32 + * A simple model of time series chart data.
29 * 33 *
30 * <p> 34 * <p>
31 * Note that this is not a full MVC type model; the expected usage pattern 35 * Note that this is not a full MVC type model; the expected usage pattern
...@@ -35,58 +39,65 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -35,58 +39,65 @@ import static com.google.common.base.Preconditions.checkNotNull;
35 */ 39 */
36 public class ChartModel { 40 public class ChartModel {
37 41
38 - // key is series name, value is series index 42 + private final Set<String> seriesSet;
39 - private final Map<String, Integer> seriesMap; 43 + private final String[] seriesArray;
40 - private final DataPoint[] dataPoints; 44 + private final List<Long> labels = Lists.newArrayList();
45 + private final List<DataPoint> dataPoints = Lists.newArrayList();
41 46
42 /** 47 /**
43 * Constructs a chart model with initialized series set. 48 * Constructs a chart model with initialized series set.
44 * 49 *
45 - * @param size datapoints size
46 * @param series a set of series 50 * @param series a set of series
47 */ 51 */
48 - public ChartModel(int size, String... series) { 52 + public ChartModel(String... series) {
49 checkNotNull(series, "series cannot be null"); 53 checkNotNull(series, "series cannot be null");
50 checkArgument(series.length > 0, "must be at least one series"); 54 checkArgument(series.length > 0, "must be at least one series");
51 - seriesMap = Maps.newConcurrentMap();
52 55
53 - for (int index = 0; index < series.length; index++) { 56 + seriesSet = Sets.newHashSet(series);
54 - seriesMap.put(series[index], index); 57 +
58 + if (seriesSet.size() != series.length) {
59 + throw new IllegalArgumentException("duplicate series detected");
55 } 60 }
56 61
57 - checkArgument(size > 0, "must have at least one data point"); 62 + this.seriesArray = Arrays.copyOf(series, series.length);
58 - dataPoints = new DataPoint[size];
59 } 63 }
60 64
61 private void checkDataPoint(DataPoint dataPoint) { 65 private void checkDataPoint(DataPoint dataPoint) {
62 - checkArgument(dataPoint.getSize() == seriesCount(), 66 + checkArgument(dataPoint.size() == seriesCount(),
63 "data size should be equal to number of series"); 67 "data size should be equal to number of series");
64 } 68 }
65 69
66 /** 70 /**
71 + * Checks the validity of the given series.
72 + *
73 + * @param series series name
74 + */
75 + private void checkSeries(String series) {
76 + checkNotNull(series, "must provide a series name");
77 + if (!seriesSet.contains(series)) {
78 + throw new IllegalArgumentException("unknown series: " + series);
79 + }
80 + }
81 +
82 + /**
67 * Returns the number of series in this chart model. 83 * Returns the number of series in this chart model.
68 * 84 *
69 * @return number of series 85 * @return number of series
70 */ 86 */
71 public int seriesCount() { 87 public int seriesCount() {
72 - return seriesMap.size(); 88 + return seriesSet.size();
73 } 89 }
74 90
75 /** 91 /**
76 - * Shifts all of the data points to the left, 92 + * Adds a data point to the chart model.
77 - * and adds a new data point to the tail of the array.
78 * 93 *
79 - * @param label label name 94 + * @return the data point, for chaining
80 - * @param values a set of data values
81 */ 95 */
82 - public void addDataPoint(String label, Double[] values) { 96 + public DataPoint addDataPoint(Long label) {
83 - DataPoint dp = new DataPoint(label, values); 97 + DataPoint dp = new DataPoint();
84 - checkDataPoint(dp); 98 + labels.add(label);
85 - 99 + dataPoints.add(dp);
86 - for (int index = 1; index < dataPoints.length; index++) { 100 + return dp;
87 - dataPoints[index - 1] = dataPoints[index];
88 - }
89 - dataPoints[dataPoints.length - 1] = dp;
90 } 101 }
91 102
92 /** 103 /**
...@@ -95,16 +106,34 @@ public class ChartModel { ...@@ -95,16 +106,34 @@ public class ChartModel {
95 * @return an array of series 106 * @return an array of series
96 */ 107 */
97 public String[] getSeries() { 108 public String[] getSeries() {
98 - return seriesMap.keySet().toArray(new String[seriesMap.size()]); 109 + return seriesArray;
99 } 110 }
100 111
101 /** 112 /**
102 - * Returns all of data points. 113 + * Returns all of data points in order.
103 * 114 *
104 * @return an array of data points 115 * @return an array of data points
105 */ 116 */
106 public DataPoint[] getDataPoints() { 117 public DataPoint[] getDataPoints() {
107 - return Arrays.copyOf(dataPoints, dataPoints.length); 118 + return dataPoints.toArray(new DataPoint[dataPoints.size()]);
119 + }
120 +
121 + /**
122 + * Returns all of labels in order.
123 + *
124 + * @return an array of labels
125 + */
126 + public Object[] getLabels() {
127 + return labels.toArray(new Long[labels.size()]);
128 + }
129 +
130 + /**
131 + * Returns the number of data points in this chart model.
132 + *
133 + * @return number of data points
134 + */
135 + public int dataPointCount() {
136 + return dataPoints.size();
108 } 137 }
109 138
110 /** 139 /**
...@@ -113,7 +142,7 @@ public class ChartModel { ...@@ -113,7 +142,7 @@ public class ChartModel {
113 * @return data point 142 * @return data point
114 */ 143 */
115 public DataPoint getLastDataPoint() { 144 public DataPoint getLastDataPoint() {
116 - return dataPoints[dataPoints.length - 1]; 145 + return dataPoints.get(dataPoints.size() - 1);
117 } 146 }
118 147
119 /** 148 /**
...@@ -121,47 +150,52 @@ public class ChartModel { ...@@ -121,47 +150,52 @@ public class ChartModel {
121 */ 150 */
122 public class DataPoint { 151 public class DataPoint {
123 // values for all series 152 // values for all series
124 - private final Double[] values; 153 + private final Map<String, Double> data = Maps.newHashMap();
125 - private final String label;
126 154
127 /** 155 /**
128 - * Constructs a data point. 156 + * Sets the data value for the given series of this data point.
129 * 157 *
130 - * @param label label name 158 + * @param series series name
131 - * @param values a set of data values for all series 159 + * @param value value to set
160 + * @return self, for chaining
132 */ 161 */
133 - public DataPoint(String label, Double[] values) { 162 + public DataPoint data(String series, Double value) {
134 - this.label = label; 163 + checkSeries(series);
135 - this.values = values; 164 + data.put(series, value);
165 + return this;
136 } 166 }
137 167
138 /** 168 /**
139 - * Returns the label name of this data point. 169 + * Returns the data value with the given series for this data point.
140 * 170 *
141 - * @return label name 171 + * @return data value
142 */ 172 */
143 - public String getLabel() { 173 + public Double get(String series) {
144 - return label; 174 + return data.get(series);
145 } 175 }
146 176
147 /** 177 /**
148 - * Returns the size of data point. 178 + * Return the data value with the same order of series.
149 - * This should be identical to the size of series.
150 * 179 *
151 - * @return size of data point 180 + * @return an array of ordered data values
152 */ 181 */
153 - public int getSize() { 182 + public Double[] getAll() {
154 - return values.length; 183 + Double[] value = new Double[getSeries().length];
184 + int idx = 0;
185 + for (String s : getSeries()) {
186 + value[idx] = get(s);
187 + idx++;
188 + }
189 + return value;
155 } 190 }
156 191
157 /** 192 /**
158 - * Returns the value of the data point of the given series. 193 + * Returns the size of data point.
159 * 194 *
160 - * @param series series name 195 + * @return the size of data point
161 - * @return data value of a specific series
162 */ 196 */
163 - public Double getValue(String series) { 197 + public int size() {
164 - return values[seriesMap.get(series)]; 198 + return data.size();
165 } 199 }
166 } 200 }
167 } 201 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.ui.chart;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onosproject.ui.RequestHandler;
20 +
21 +/**
22 + * Message handler specifically for the chart views.
23 + */
24 +public abstract class ChartRequestHandler extends RequestHandler {
25 +
26 + private final String respType;
27 + private final String nodeName;
28 +
29 + /**
30 + * Constructs a chart model handler for a specific graph view. When chart
31 + * requests come in, the handler will generate the appropriate chart data
32 + * points and send back the response to the client.
33 + *
34 + * @param reqType type of the request event
35 + * @param respType type of the response event
36 + * @param nodeName name of JSON node holding data point
37 + */
38 + public ChartRequestHandler(String reqType, String respType, String nodeName) {
39 + super(reqType);
40 + this.respType = respType;
41 + this.nodeName = nodeName;
42 + }
43 +
44 + @Override
45 + public void process(long sid, ObjectNode payload) {
46 + ChartModel cm = createChartModel();
47 + populateChart(cm, payload);
48 +
49 + ObjectNode rootNode = MAPPER.createObjectNode();
50 + rootNode.set(nodeName, ChartUtils.generateDataPointArrayNode(cm));
51 + sendMessage(respType, 0, rootNode);
52 + }
53 +
54 + /**
55 + * Creates the chart model using {@link #getSeries()}
56 + * to initialize it, ready to be populated.
57 + * <p>
58 + * This default implementation returns a chart model for all series.
59 + * </p>
60 + *
61 + * @return an empty chart model
62 + */
63 + protected ChartModel createChartModel() {
64 + return new ChartModel(getSeries());
65 + }
66 +
67 + /**
68 + * Subclasses should return the array of series with which to initialize
69 + * their chart model.
70 + *
71 + * @return the series name
72 + */
73 + protected abstract String[] getSeries();
74 +
75 + /**
76 + * Subclasses should populate the chart model by adding
77 + * {@link ChartModel.DataPoint datapoints}.
78 + * <pre>
79 + * cm.addDataPoint()
80 + * .data(SERIES_ONE, ...)
81 + * .data(SERIES_TWO, ...)
82 + * ... ;
83 + * </pre>
84 + * The request payload is provided in case there are request filtering
85 + * parameters.
86 + *
87 + * @param cm the chart model
88 + * @param payload request payload
89 + */
90 + protected abstract void populateChart(ChartModel cm, ObjectNode payload);
91 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.ui.chart;
17 +
18 +import com.fasterxml.jackson.databind.JsonNode;
19 +import com.fasterxml.jackson.databind.ObjectMapper;
20 +import com.fasterxml.jackson.databind.node.ArrayNode;
21 +import com.fasterxml.jackson.databind.node.ObjectNode;
22 +
23 +/**
24 + * Provides static utility methods for dealing with charts.
25 + */
26 +public final class ChartUtils {
27 +
28 + private static final ObjectMapper MAPPER = new ObjectMapper();
29 +
30 + // non-instantiable
31 + private ChartUtils() {
32 + }
33 +
34 + /**
35 + * Generates a JSON array node from the data points of the given chart model.
36 + *
37 + * @param cm the chart model
38 + * @return the array node representation of data points
39 + */
40 + public static ArrayNode generateDataPointArrayNode(ChartModel cm) {
41 + ArrayNode array = MAPPER.createArrayNode();
42 + for (ChartModel.DataPoint dp : cm.getDataPoints()) {
43 + array.add(toJsonNode(dp, cm));
44 + }
45 + return array;
46 + }
47 +
48 + /**
49 + * Generate a JSON node from the data point and given chart model.
50 + *
51 + * @param dp the data point
52 + * @param cm the chart model
53 + * @return the node representation of a data point with series
54 + */
55 + public static JsonNode toJsonNode(ChartModel.DataPoint dp, ChartModel cm) {
56 + ObjectNode result = MAPPER.createObjectNode();
57 + String[] series = cm.getSeries();
58 + Double[] values = dp.getAll();
59 + int n = series.length;
60 + for (int i = 0; i < n; i++) {
61 + result.put(series[i], values[i]);
62 + }
63 + return result;
64 + }
65 +}
...@@ -17,10 +17,7 @@ package org.onosproject.ui.chart; ...@@ -17,10 +17,7 @@ package org.onosproject.ui.chart;
17 17
18 import org.junit.Test; 18 import org.junit.Test;
19 19
20 -import static org.hamcrest.Matchers.is;
21 -import static org.junit.Assert.assertArrayEquals;
22 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertEquals;
23 -import static org.junit.Assert.assertThat;
24 21
25 /** 22 /**
26 * Unit tests for {@link ChartModel}. 23 * Unit tests for {@link ChartModel}.
...@@ -35,57 +32,97 @@ public class ChartModelTest { ...@@ -35,57 +32,97 @@ public class ChartModelTest {
35 private static final Double[] VALUES2 = {3D, 4D, 5D}; 32 private static final Double[] VALUES2 = {3D, 4D, 5D};
36 private static final Double[] VALUES3 = {6D, 7D, 8D}; 33 private static final Double[] VALUES3 = {6D, 7D, 8D};
37 34
38 - private static final String[] SERIES = {FOO, BAR, ZOO};
39 -
40 private ChartModel cm; 35 private ChartModel cm;
41 36
42 @Test(expected = NullPointerException.class) 37 @Test(expected = NullPointerException.class)
43 public void guardAgainstNullSeries() { 38 public void guardAgainstNullSeries() {
44 - cm = new ChartModel(1, null); 39 + cm = new ChartModel(null);
45 - }
46 -
47 - @Test(expected = IllegalArgumentException.class)
48 - public void guardAgainstWrongDpNumber() {
49 - cm = new ChartModel(0, FOO);
50 } 40 }
51 41
52 @Test 42 @Test
53 public void testSeriesCount() { 43 public void testSeriesCount() {
54 - cm = new ChartModel(1, FOO, BAR, ZOO); 44 + cm = new ChartModel(FOO, BAR, ZOO);
55 assertEquals("Wrong series count", 3, cm.seriesCount()); 45 assertEquals("Wrong series count", 3, cm.seriesCount());
56 } 46 }
57 47
58 @Test 48 @Test
49 + public void emptyLabel() {
50 + cm = new ChartModel(FOO, BAR, ZOO);
51 + cm.addDataPoint(System.currentTimeMillis());
52 +
53 + assertEquals("bad data point count", 1, cm.dataPointCount());
54 + }
55 +
56 + @Test(expected = IllegalArgumentException.class)
57 + public void dataPointBandSeries() {
58 + cm = new ChartModel(FOO, BAR);
59 +
60 + cm.addDataPoint(System.currentTimeMillis())
61 + .data(ZOO, VALUES3[0]);
62 + }
63 +
64 + @Test
59 public void testAddDataPoint() { 65 public void testAddDataPoint() {
60 - cm = new ChartModel(2, FOO, BAR, ZOO); 66 + cm = new ChartModel(FOO, BAR, ZOO);
61 67
62 - cm.addDataPoint("1", VALUES1); 68 + long time = System.currentTimeMillis();
63 - cm.addDataPoint("2", VALUES2);
64 69
65 - assertEquals("Wrong result", "1", cm.getDataPoints()[0].getLabel()); 70 + cm.addDataPoint(time)
66 - assertEquals("Wrong result", "2", cm.getDataPoints()[1].getLabel()); 71 + .data(FOO, VALUES1[0])
72 + .data(BAR, VALUES2[0])
73 + .data(ZOO, VALUES3[0]);
67 74
68 - cm.addDataPoint("3", VALUES3); 75 + cm.addDataPoint(time + 1)
76 + .data(FOO, VALUES1[1])
77 + .data(BAR, VALUES2[1])
78 + .data(ZOO, VALUES3[1]);
69 79
70 - assertEquals("Wrong result", "2", cm.getDataPoints()[0].getLabel()); 80 + cm.addDataPoint(time + 2)
71 - assertEquals("Wrong result", "3", cm.getDataPoints()[1].getLabel()); 81 + .data(FOO, VALUES1[2])
82 + .data(BAR, VALUES2[2])
83 + .data(ZOO, VALUES3[2]);
84 +
85 + assertEquals("Wrong result", 3, cm.getDataPoints()[0].size());
86 + assertEquals("Wrong result", 3, cm.getDataPoints()[1].size());
87 + assertEquals("Wrong result", 3, cm.getDataPoints()[2].size());
88 + assertEquals("Wrong result", 3, cm.getDataPoints().length);
72 } 89 }
73 90
74 @Test 91 @Test
75 - public void testGetData() { 92 + public void testGetDataPoint() {
76 - cm = new ChartModel(2, FOO, BAR, ZOO); 93 + cm = new ChartModel(FOO, BAR);
94 +
95 + long time = System.currentTimeMillis();
77 96
78 - cm.addDataPoint("1", VALUES1); 97 + cm.addDataPoint(time)
79 - assertThat(cm.getLastDataPoint().getValue(ZOO), is(2D)); 98 + .data(FOO, VALUES1[0])
99 + .data(BAR, VALUES2[0]);
80 100
81 - cm.addDataPoint("2", VALUES2); 101 + cm.addDataPoint(time + 1)
82 - assertThat(cm.getLastDataPoint().getValue(BAR), is(4D)); 102 + .data(FOO, VALUES1[1])
103 + .data(BAR, VALUES2[1]);
104 +
105 + assertEquals("Wrong result", (Double) 0D, cm.getDataPoints()[0].get(FOO));
106 + assertEquals("Wrong result", (Double) 1D, cm.getDataPoints()[1].get(FOO));
107 + assertEquals("Wrong result", (Double) 3D, cm.getDataPoints()[0].get(BAR));
108 + assertEquals("Wrong result", (Double) 4D, cm.getDataPoints()[1].get(BAR));
83 } 109 }
84 110
85 @Test 111 @Test
86 - public void testGetSeries() { 112 + public void testGetLastDataPoint() {
87 - cm = new ChartModel(1, FOO, BAR, ZOO); 113 + cm = new ChartModel(FOO, BAR);
114 +
115 + long time = System.currentTimeMillis();
116 +
117 + cm.addDataPoint(time)
118 + .data(FOO, VALUES1[0])
119 + .data(BAR, VALUES2[0]);
120 +
121 + cm.addDataPoint(time + 1)
122 + .data(FOO, VALUES1[1])
123 + .data(BAR, VALUES2[1]);
88 124
89 - assertArrayEquals("series", SERIES, cm.getSeries()); 125 + assertEquals("Wrong result", VALUES1[1], cm.getLastDataPoint().get(FOO));
126 + assertEquals("Wrong result", VALUES2[1], cm.getLastDataPoint().get(BAR));
90 } 127 }
91 } 128 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.ui.chart;
17 +
18 +import com.fasterxml.jackson.databind.node.ArrayNode;
19 +import org.junit.Assert;
20 +import org.junit.Test;
21 +
22 +/**
23 + * Unit tests for {@link ChartUtils}.
24 + */
25 +public class ChartUtilsTest {
26 +
27 + private static final String FOO = "foo";
28 + private static final String BAR = "bar";
29 +
30 + private static final String ARRAY_AS_STRING =
31 + "[{\"foo\":1.0,\"bar\":2.0},{\"foo\":3.0,\"bar\":4.0}]";
32 +
33 + @Test
34 + public void basic() {
35 + ChartModel cm = new ChartModel(FOO, BAR);
36 + cm.addDataPoint(1L).data(FOO, 1D).data(BAR, 2D);
37 + cm.addDataPoint(2L).data(FOO, 3D).data(BAR, 4D);
38 +
39 + ArrayNode array = ChartUtils.generateDataPointArrayNode(cm);
40 + Assert.assertEquals("wrong results", ARRAY_AS_STRING, array.toString());
41 + }
42 +}