package com.github.mikephil.charting.data;
import android.util.Log;
import com.github.mikephil.charting.utils.Highlight;
import java.util.ArrayList;
/**
* Class that holds all relevant data that represents the chart. That involves
* at least one (or more) DataSets, and an array of x-values.
*
* @author Philipp Jahoda
*/
public abstract class ChartData<T extends DataSet<? extends Entry>> {
/** maximum y-value in the y-value array */
protected float mYMax = 0.0f;
/** the minimum y-value in the y-value array */
protected float mYMin = 0.0f;
/** the total sum of all y-values */
private float mYValueSum = 0f;
/** total number of y-values across all DataSet objects */
private int mYValCount = 0;
/**
* contains the average length (in characters) an entry in the x-vals array
* has
*/
private float mXValAverageLength = 0;
/** holds all x-values the chart represents */
protected ArrayList<String> mXVals;
/** array that holds all DataSets the ChartData object represents */
protected ArrayList<T> mDataSets;
/**
* Constructor for only x-values. This constructor can be used for setting
* up an empty chart without data.
*
* @param xVals
*/
public ChartData(ArrayList<String> xVals) {
this.mXVals = xVals;
init();
}
/**
* Constructor for only x-values. This constructor can be used for setting
* up an empty chart without data.
*
* @param xVals
*/
public ChartData(String[] xVals) {
this.mXVals = arrayToArrayList(xVals);
init();
}
/**
* constructor for chart data
*
* @param xVals The values describing the x-axis. Must be at least as long
* as the highest xIndex in the Entry objects across all
* DataSets.
* @param sets the dataset array
*/
public ChartData(ArrayList<String> xVals, ArrayList<T> sets) {
this.mXVals = xVals;
this.mDataSets = sets;
init();
}
/**
* constructor that takes string array instead of arraylist string
*
* @param xVals The values describing the x-axis. Must be at least as long
* as the highest xIndex in the Entry objects across all
* DataSets.
* @param sets the dataset array
*/
public ChartData(String[] xVals, ArrayList<T> sets) {
this.mXVals = arrayToArrayList(xVals);
this.mDataSets = sets;
init();
}
/**
* Turns an array of strings into an arraylist of strings.
*
* @param array
* @return
*/
private ArrayList<String> arrayToArrayList(String[] array) {
ArrayList<String> arraylist = new ArrayList<String>();
for (int i = 0; i < array.length; i++) {
arraylist.add(array[i]);
}
return arraylist;
}
/**
* performs all kinds of initialization calculations, such as min-max and
* value count and sum
*/
private void init() {
isLegal(mDataSets);
calcMinMax(mDataSets);
calcYValueSum(mDataSets);
calcYValueCount(mDataSets);
calcXValAverageLength();
}
/**
* calculates the average length (in characters) across all x-value strings
*/
private void calcXValAverageLength() {
if (mXVals.size() == 0) {
mXValAverageLength = 1;
return;
}
float sum = 1f;
for (int i = 0; i < mXVals.size(); i++) {
sum += mXVals.get(i).length();
}
mXValAverageLength = sum / (float) mXVals.size();
}
/**
* Checks if the combination of x-values array and DataSet array is legal or
* not.
*
* @param dataSets
*/
private void isLegal(ArrayList<T> dataSets) {
if (dataSets == null)
return;
for (int i = 0; i < dataSets.size(); i++) {
if (dataSets.get(i)
.getYVals()
.size() > mXVals.size()) {
throw new IllegalArgumentException(
"One or more of the DataSet Entry arrays are longer than the x-values array of this ChartData object.");
}
}
}
/**
* Call this method to let the CartData know that the underlying data has
* changed.
*/
public void notifyDataChanged() {
init();
}
/**
* calc minimum and maximum y value over all datasets
*/
protected void calcMinMax(ArrayList<T> dataSets) {
if (dataSets == null || dataSets.size() < 1) {
mYMax = 0f;
mYMin = 0f;
} else {
mYMin = dataSets.get(0).getYMin();
mYMax = dataSets.get(0).getYMax();
for (int i = 0; i < dataSets.size(); i++) {
if (dataSets.get(i).getYMin() < mYMin)
mYMin = dataSets.get(i).getYMin();
if (dataSets.get(i).getYMax() > mYMax)
mYMax = dataSets.get(i).getYMax();
}
}
}
/**
* calculates the sum of all y-values in all datasets
*/
protected void calcYValueSum(ArrayList<T> dataSets) {
mYValueSum = 0;
if (dataSets == null)
return;
for (int i = 0; i < dataSets.size(); i++) {
mYValueSum += Math.abs(dataSets.get(i).getYValueSum());
}
}
/**
* Calculates the total number of y-values across all DataSets the ChartData
* represents.
*
* @return
*/
protected void calcYValueCount(ArrayList<T> dataSets) {
mYValCount = 0;
if (dataSets == null)
return;
int count = 0;
for (int i = 0; i < dataSets.size(); i++) {
count += dataSets.get(i).getEntryCount();
}
mYValCount = count;
}
/** ONLY GETTERS AND SETTERS BELOW THIS */
/**
* returns the number of LineDataSets this object contains
*
* @return
*/
public int getDataSetCount() {
if (mDataSets == null)
return 0;
return mDataSets.size();
}
/**
* Returns the smallest y-value the data object contains.
*
* @return
*/
public float getYMin() {
return mYMin;
}
/**
* Returns the greatest y-value the data object contains.
*
* @return
*/
public float getYMax() {
return mYMax;
}
/**
* returns the average length (in characters) across all values in the
* x-vals array
*
* @return
*/
public float getXValAverageLength() {
return mXValAverageLength;
}
/**
* Returns the total y-value sum across all DataSet objects the this object
* represents.
*
* @return
*/
public float getYValueSum() {
return mYValueSum;
}
/**
* Returns the total number of y-values across all DataSet objects the this
* object represents.
*
* @return
*/
public int getYValCount() {
return mYValCount;
}
// /**
// * Checks if the ChartData object contains valid data
// *
// * @return
// */
// public boolean isValid() {
// if (mXVals == null || mXVals.size() < 1)
// return false;
//
// if (mDataSets == null || mDataSets.size() < 1)
// return false;
//
// return true;
// }
/**
* returns the x-values the chart represents
*
* @return
*/
public ArrayList<String> getXVals() {
return mXVals;
}
/**
* Returns an the array of DataSets this object holds.
*
* @return
*/
public ArrayList<T> getDataSets() {
return mDataSets;
}
// /**
// * returns the Entries array from the DataSet at the given index. If a
// * filter is set, the filtered Entries are returned
// *
// * @param index
// * @return
// */
// public ArrayList<Entry> getYVals(int index) {
// return mDataSets.get(index).getYVals();
// }
/**
* Retrieve the index of a DataSet with a specific label from the ChartData.
* Search can be case sensitive or not. IMPORTANT: This method does
* calculations at runtime, do not over-use in performance critical
* situations.
*
* @param dataSets the DataSet array to search
* @param type
* @param ignorecase if true, the search is not case-sensitive
* @return
*/
protected int getDataSetIndexByLabel(ArrayList<T> dataSets, String label,
boolean ignorecase) {
if (ignorecase) {
for (int i = 0; i < dataSets.size(); i++)
if (label.equalsIgnoreCase(dataSets.get(i).getLabel()))
return i;
} else {
for (int i = 0; i < dataSets.size(); i++)
if (label.equals(dataSets.get(i).getLabel()))
return i;
}
return -1;
}
/**
* returns the total number of x-values this ChartData object represents
* (the size of the x-values array)
*
* @return
*/
public int getXValCount() {
return mXVals.size();
}
/**
* Returns the labels of all DataSets as a string array.
*
* @return
*/
protected String[] getDataSetLabels() {
String[] types = new String[mDataSets.size()];
for (int i = 0; i < mDataSets.size(); i++) {
types[i] = mDataSets.get(i).getLabel();
}
return types;
}
/**
* Get the Entry for a corresponding highlight object
*
* @param highlight
* @return the entry that is highlighted
*/
public Entry getEntryForHighlight(Highlight highlight) {
return mDataSets.get(highlight.getDataSetIndex()).getEntryForXIndex(
highlight.getXIndex());
}
/**
* Returns the DataSet object with the given label. Search can be case
* sensitive or not. IMPORTANT: This method does calculations at runtime.
* Use with care in performance critical situations.
*
* @param label
* @param ignorecase
* @return
*/
public T getDataSetByLabel(String label, boolean ignorecase) {
int index = getDataSetIndexByLabel(mDataSets, label, ignorecase);
if (index < 0 || index >= mDataSets.size())
return null;
else
return mDataSets.get(index);
}
/**
* Returns the DataSet object at the given index.
*
* @param index
* @return
*/
public T getDataSetByIndex(int index) {
if (mDataSets == null || index < 0 || index >= mDataSets.size())
return null;
return mDataSets.get(index);
}
/**
* Adds a DataSet dynamically.
*
* @param d
*/
public void addDataSet(T d) {
if (mDataSets == null)
mDataSets = new ArrayList<T>();
mDataSets.add(d);
mYValCount += d.getEntryCount();
mYValueSum += d.getYValueSum();
if (mYMax < d.getYMax())
mYMax = d.getYMax();
if (mYMin > d.getYMin())
mYMin = d.getYMin();
}
/**
* Removes the given DataSet from this data object. Also recalculates all
* minimum and maximum values. Returns true if a DataSet was removed, false
* if no DataSet could be removed.
*
* @param d
*/
public boolean removeDataSet(T d) {
if (mDataSets == null || d == null)
return false;
boolean removed = mDataSets.remove(d);
// if a DataSet was removed
if (removed) {
mYValCount -= d.getEntryCount();
mYValueSum -= d.getYValueSum();
calcMinMax(mDataSets);
}
return removed;
}
/**
* Removes the DataSet at the given index in the DataSet array from the data
* object. Also recalculates all minimum and maximum values. Returns true if
* a DataSet was removed, false if no DataSet could be removed.
*
* @param index
*/
public boolean removeDataSet(int index) {
if (mDataSets == null || index >= mDataSets.size() || index < 0)
return false;
T set = mDataSets.get(index);
return removeDataSet(set);
}
/**
* Adds an Entry to the DataSet at the specified index. Entries are added to
* the end of the list.
*
* @param e
* @param dataSetIndex
*/
public void addEntry(Entry e, int dataSetIndex) {
float val = e.getVal();
mYValCount += 1;
mYValueSum += val;
if (mYMax < val)
mYMax = val;
if (mYMin > val)
mYMin = val;
if (mDataSets == null)
mDataSets = new ArrayList<T>();
if (mDataSets.size() > dataSetIndex) {
T set = mDataSets.get(dataSetIndex);
if (set != null) {
// add the entry to the dataset
set.addEntry(e);
}
} else {
Log.e("addEntry", "Cannot add Entry because dataSetIndex too high.");
}
}
/**
* Removes the given Entry object from the DataSet at the specified index.
*
* @param e
* @param dataSetIndex
*/
public boolean removeEntry(Entry e, int dataSetIndex) {
// entry null, outofbounds
if (e == null || dataSetIndex >= mDataSets.size())
return false;
// remove the entry from the dataset
boolean removed = mDataSets.get(dataSetIndex).removeEntry(e.getXIndex());
if (removed) {
float val = e.getVal();
mYValCount -= 1;
mYValueSum -= val;
calcMinMax(mDataSets);
}
return removed;
}
/**
* Removes the Entry object at the given xIndex from the DataSet at the
* specified index. Returns true if an Entry was removed, false if no Entry
* was found that meets the specified requirements.
*
* @param xIndex
* @param dataSetIndex
* @return
*/
public boolean removeEntry(int xIndex, int dataSetIndex) {
if (dataSetIndex >= mDataSets.size())
return false;
T dataSet = mDataSets.get(dataSetIndex);
Entry e = dataSet.getEntryForXIndex(xIndex);
return removeEntry(e, dataSetIndex);
}
/**
* Returns the DataSet that contains the provided Entry, or null, if no
* DataSet contains this Entry.
*
* @param e
* @return
*/
public T getDataSetForEntry(Entry e) {
if (e == null)
return null;
for (int i = 0; i < mDataSets.size(); i++) {
T set = mDataSets.get(i);
for (int j = 0; j < set.getEntryCount(); j++) {
if (e.equalTo(set.getEntryForXIndex(e.getXIndex())))
return set;
}
}
return null;
}
/**
* Returns all colors used across all DataSet objects this object
* represents.
*
* @return
*/
public int[] getColors() {
if (mDataSets == null)
return null;
int clrcnt = 0;
for (int i = 0; i < mDataSets.size(); i++) {
clrcnt += mDataSets.get(i).getColors().size();
}
int[] colors = new int[clrcnt];
int cnt = 0;
for (int i = 0; i < mDataSets.size(); i++) {
ArrayList<Integer> clrs = mDataSets.get(i).getColors();
for (Integer clr : clrs) {
colors[cnt] = clr;
cnt++;
}
}
return colors;
}
/**
* Generates an x-values array filled with numbers in range specified by the
* parameters. Can be used for convenience.
*
* @return
*/
public static ArrayList<String> generateXVals(int from, int to) {
ArrayList<String> xvals = new ArrayList<String>();
for (int i = from; i < to; i++) {
xvals.add("" + i);
}
return xvals;
}
}