content: 
  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;
      }
  }
  