1 /* 2 * Copyright 2012 AndroidPlot.com 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 17 package com.androidplot.xy; 18 19 import android.graphics.Canvas; 20 import android.util.Log; 21 import android.util.Pair; 22 import com.androidplot.Plot; 23 import com.androidplot.PlotListener; 24 25 import java.util.LinkedList; 26 import java.util.List; 27 import java.util.NoSuchElementException; 28 import java.util.concurrent.locks.ReentrantReadWriteLock; 29 30 31 /** 32 * A convenience class used to create instances of XYPlot generated from Lists of Numbers. 33 */ 34 public class SimpleXYSeries implements XYSeries, PlotListener { 35 36 private static final String TAG = SimpleXYSeries.class.getName(); 37 38 @Override onBeforeDraw(Plot source, Canvas canvas)39 public void onBeforeDraw(Plot source, Canvas canvas) { 40 lock.readLock().lock(); 41 } 42 43 @Override onAfterDraw(Plot source, Canvas canvas)44 public void onAfterDraw(Plot source, Canvas canvas) { 45 lock.readLock().unlock(); 46 } 47 48 public enum ArrayFormat { 49 Y_VALS_ONLY, 50 XY_VALS_INTERLEAVED 51 } 52 53 private volatile LinkedList<Number> xVals = new LinkedList<Number>(); 54 private volatile LinkedList<Number> yVals = new LinkedList<Number>(); 55 private volatile String title = null; 56 private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); 57 58 SimpleXYSeries(String title)59 public SimpleXYSeries(String title) { 60 this.title = title; 61 } 62 /** 63 * Generates an XYSeries instance from the List of numbers passed in. This is a convenience class 64 * and should only be used for static data models; it is not suitable for representing dynamically 65 * changing data. 66 * 67 * @param model A List of Number elements comprising the data model. 68 * @param format Format of the model. A format of Y_VALS_ONLY means that the array only contains y-values. 69 * For this format x values are autogenerated using values of 0 through n-1 where n is the size of the model. 70 * @param title Title of the series 71 */ SimpleXYSeries(List<? extends Number> model, ArrayFormat format, String title)72 public SimpleXYSeries(List<? extends Number> model, ArrayFormat format, String title) { 73 this(title); 74 setModel(model, format); 75 } 76 SimpleXYSeries(List<? extends Number> xVals, List<? extends Number> yVals, String title)77 public SimpleXYSeries(List<? extends Number> xVals, List<? extends Number> yVals, String title) { 78 this(title); 79 if(xVals == null || yVals == null) { 80 throw new IllegalArgumentException("Neither the xVals nor the yVals parameters may be null."); 81 } 82 83 if(xVals.size() != yVals.size()) { 84 throw new IllegalArgumentException("xVals and yVals List parameters must be of the same size."); 85 } 86 87 this.xVals.addAll(xVals); 88 this.yVals.addAll(yVals); 89 } 90 91 /** 92 * Use index value as xVal, instead of explicit, user provided xVals. 93 */ useImplicitXVals()94 public void useImplicitXVals() { 95 lock.writeLock().lock(); 96 try { 97 xVals = null; 98 } finally { 99 lock.writeLock().unlock(); 100 } 101 } 102 103 /** 104 * Use the provided list of Numbers as yVals and their corresponding indexes as xVals. 105 * @param model A List of Number elements comprising the data model. 106 * @param format Format of the model. A format of Y_VALS_ONLY means that the array only contains y-values. 107 * For this format x values are autogenerated using values of 0 through n-1 where n is the size of the model. 108 */ setModel(List<? extends Number> model, ArrayFormat format)109 public void setModel(List<? extends Number> model, ArrayFormat format) { 110 111 lock.writeLock().lock(); 112 try { 113 // empty the current values: 114 //xVals.clear(); 115 xVals = null; 116 yVals.clear(); 117 118 // make sure the new model has data: 119 if (model == null || model.size() == 0) { 120 return; 121 } 122 123 switch (format) { 124 125 // array containing only y-vals. assume x = index: 126 case Y_VALS_ONLY: 127 for(Number n : model) { 128 yVals.add(n); 129 } 130 /*for (int i = 0; i < model.size(); i++) { 131 //xVals.add(i); 132 yVals.add(model.get(i)); 133 }*/ 134 break; 135 136 // xy interleaved array: 137 case XY_VALS_INTERLEAVED: 138 if (xVals == null) { 139 xVals = new LinkedList<Number>(); 140 } 141 if (model.size() % 2 != 0) { 142 throw new IndexOutOfBoundsException("Cannot auto-generate series from odd-sized xy List."); 143 } 144 // always need an x and y array so init them now: 145 int sz = model.size() / 2; 146 for (int i = 0, j = 0; i < sz; i++, j += 2) { 147 xVals.add(model.get(j)); 148 yVals.add(model.get(j + 1)); 149 } 150 break; 151 default: 152 throw new IllegalArgumentException("Unexpected enum value: " + format); 153 } 154 } finally { 155 lock.writeLock().unlock(); 156 } 157 } 158 159 /** 160 * Sets individual x value based on index 161 * @param value 162 * @param index 163 */ setX(Number value, int index)164 public void setX(Number value, int index) { 165 lock.writeLock().lock(); 166 try { 167 xVals.set(index, value); 168 } finally { 169 lock.writeLock().unlock(); 170 } 171 } 172 173 /** 174 * Sets individual y value based on index 175 * @param value 176 * @param index 177 */ setY(Number value, int index)178 public void setY(Number value, int index) { 179 lock.writeLock().lock(); 180 try { 181 yVals.set(index, value); 182 } finally { 183 lock.writeLock().unlock(); 184 } 185 } 186 187 /** 188 * Sets xy values based on index 189 * @param xVal 190 * @param yVal 191 * @param index 192 */ setXY(Number xVal, Number yVal, int index)193 public void setXY(Number xVal, Number yVal, int index) { 194 lock.writeLock().lock(); 195 try { 196 yVals.set(index, yVal); 197 xVals.set(index, xVal); 198 } finally {lock.writeLock().unlock();} 199 } 200 addFirst(Number x, Number y)201 public void addFirst(Number x, Number y) { 202 lock.writeLock().lock(); 203 try { 204 if (xVals != null) { 205 xVals.addFirst(x); 206 } 207 yVals.addFirst(y); 208 } finally { 209 lock.writeLock().unlock(); 210 } 211 } 212 213 /** 214 * 215 * @return Pair<Number, Number> with first equal to x-val and second equal to y-val. 216 */ removeFirst()217 public Pair<Number, Number> removeFirst() { 218 lock.writeLock().lock(); 219 try { 220 if (size() <= 0) { 221 throw new NoSuchElementException(); 222 } 223 return new Pair<Number, Number>(xVals != null ? xVals.removeFirst() : 0, yVals.removeFirst()); 224 } finally { 225 lock.writeLock().unlock(); 226 } 227 } 228 addLast(Number x, Number y)229 public void addLast(Number x, Number y) { 230 lock.writeLock().lock(); 231 try { 232 if (xVals != null) { 233 xVals.addLast(x); 234 } 235 yVals.addLast(y); 236 } finally { 237 lock.writeLock().unlock(); 238 } 239 } 240 241 /** 242 * 243 * @return Pair<Number, Number> with first equal to x-val and second equal to y-val. 244 */ removeLast()245 public Pair<Number, Number> removeLast() { 246 lock.writeLock().lock(); 247 try { 248 if (size() <= 0) { 249 throw new NoSuchElementException(); 250 } 251 return new Pair<Number, Number>(xVals != null ? xVals.removeLast() : yVals.size() - 1, yVals.removeLast()); 252 } finally { 253 lock.writeLock().unlock(); 254 } 255 } 256 257 @Override getTitle()258 public String getTitle() { 259 return title; 260 } 261 setTitle(String title)262 public void setTitle(String title) { 263 lock.writeLock().lock(); 264 try { 265 this.title = title; 266 } finally {lock.writeLock().unlock();} 267 } 268 269 @Override size()270 public int size() { 271 return yVals != null ? yVals.size() : 0; 272 } 273 274 @Override getX(int index)275 public Number getX(int index) { 276 return xVals != null ? xVals.get(index) : index; 277 } 278 279 @Override getY(int index)280 public Number getY(int index) { 281 return yVals.get(index); 282 } 283 } 284