/* * Copyright 2012 AndroidPlot.com * * 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 com.androidplot.xy; import android.graphics.Canvas; import android.util.Log; import android.util.Pair; import com.androidplot.Plot; import com.androidplot.PlotListener; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * A convenience class used to create instances of XYPlot generated from Lists of Numbers. */ public class SimpleXYSeries implements XYSeries, PlotListener { private static final String TAG = SimpleXYSeries.class.getName(); @Override public void onBeforeDraw(Plot source, Canvas canvas) { lock.readLock().lock(); } @Override public void onAfterDraw(Plot source, Canvas canvas) { lock.readLock().unlock(); } public enum ArrayFormat { Y_VALS_ONLY, XY_VALS_INTERLEAVED } private volatile LinkedList<Number> xVals = new LinkedList<Number>(); private volatile LinkedList<Number> yVals = new LinkedList<Number>(); private volatile String title = null; private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); public SimpleXYSeries(String title) { this.title = title; } /** * Generates an XYSeries instance from the List of numbers passed in. This is a convenience class * and should only be used for static data models; it is not suitable for representing dynamically * changing data. * * @param model A List of Number elements comprising the data model. * @param format Format of the model. A format of Y_VALS_ONLY means that the array only contains y-values. * For this format x values are autogenerated using values of 0 through n-1 where n is the size of the model. * @param title Title of the series */ public SimpleXYSeries(List<? extends Number> model, ArrayFormat format, String title) { this(title); setModel(model, format); } public SimpleXYSeries(List<? extends Number> xVals, List<? extends Number> yVals, String title) { this(title); if(xVals == null || yVals == null) { throw new IllegalArgumentException("Neither the xVals nor the yVals parameters may be null."); } if(xVals.size() != yVals.size()) { throw new IllegalArgumentException("xVals and yVals List parameters must be of the same size."); } this.xVals.addAll(xVals); this.yVals.addAll(yVals); } /** * Use index value as xVal, instead of explicit, user provided xVals. */ public void useImplicitXVals() { lock.writeLock().lock(); try { xVals = null; } finally { lock.writeLock().unlock(); } } /** * Use the provided list of Numbers as yVals and their corresponding indexes as xVals. * @param model A List of Number elements comprising the data model. * @param format Format of the model. A format of Y_VALS_ONLY means that the array only contains y-values. * For this format x values are autogenerated using values of 0 through n-1 where n is the size of the model. */ public void setModel(List<? extends Number> model, ArrayFormat format) { lock.writeLock().lock(); try { // empty the current values: //xVals.clear(); xVals = null; yVals.clear(); // make sure the new model has data: if (model == null || model.size() == 0) { return; } switch (format) { // array containing only y-vals. assume x = index: case Y_VALS_ONLY: for(Number n : model) { yVals.add(n); } /*for (int i = 0; i < model.size(); i++) { //xVals.add(i); yVals.add(model.get(i)); }*/ break; // xy interleaved array: case XY_VALS_INTERLEAVED: if (xVals == null) { xVals = new LinkedList<Number>(); } if (model.size() % 2 != 0) { throw new IndexOutOfBoundsException("Cannot auto-generate series from odd-sized xy List."); } // always need an x and y array so init them now: int sz = model.size() / 2; for (int i = 0, j = 0; i < sz; i++, j += 2) { xVals.add(model.get(j)); yVals.add(model.get(j + 1)); } break; default: throw new IllegalArgumentException("Unexpected enum value: " + format); } } finally { lock.writeLock().unlock(); } } /** * Sets individual x value based on index * @param value * @param index */ public void setX(Number value, int index) { lock.writeLock().lock(); try { xVals.set(index, value); } finally { lock.writeLock().unlock(); } } /** * Sets individual y value based on index * @param value * @param index */ public void setY(Number value, int index) { lock.writeLock().lock(); try { yVals.set(index, value); } finally { lock.writeLock().unlock(); } } /** * Sets xy values based on index * @param xVal * @param yVal * @param index */ public void setXY(Number xVal, Number yVal, int index) { lock.writeLock().lock(); try { yVals.set(index, yVal); xVals.set(index, xVal); } finally {lock.writeLock().unlock();} } public void addFirst(Number x, Number y) { lock.writeLock().lock(); try { if (xVals != null) { xVals.addFirst(x); } yVals.addFirst(y); } finally { lock.writeLock().unlock(); } } /** * * @return Pair<Number, Number> with first equal to x-val and second equal to y-val. */ public Pair<Number, Number> removeFirst() { lock.writeLock().lock(); try { if (size() <= 0) { throw new NoSuchElementException(); } return new Pair<Number, Number>(xVals != null ? xVals.removeFirst() : 0, yVals.removeFirst()); } finally { lock.writeLock().unlock(); } } public void addLast(Number x, Number y) { lock.writeLock().lock(); try { if (xVals != null) { xVals.addLast(x); } yVals.addLast(y); } finally { lock.writeLock().unlock(); } } /** * * @return Pair<Number, Number> with first equal to x-val and second equal to y-val. */ public Pair<Number, Number> removeLast() { lock.writeLock().lock(); try { if (size() <= 0) { throw new NoSuchElementException(); } return new Pair<Number, Number>(xVals != null ? xVals.removeLast() : yVals.size() - 1, yVals.removeLast()); } finally { lock.writeLock().unlock(); } } @Override public String getTitle() { return title; } public void setTitle(String title) { lock.writeLock().lock(); try { this.title = title; } finally {lock.writeLock().unlock();} } @Override public int size() { return yVals != null ? yVals.size() : 0; } @Override public Number getX(int index) { return xVals != null ? xVals.get(index) : index; } @Override public Number getY(int index) { return yVals.get(index); } }