⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dynamictimeseriescollection.java

📁 Web图形化的Java库
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/* ======================================
 * JFreeChart : a free Java chart library
 * ======================================
 *
 * Project Info:  http://www.jfree.org/jfreechart/index.html
 * Project Lead:  David Gilbert (david.gilbert@object-refinery.com);
 *
 * (C) Copyright 2000-2003, by Object Refinery Limited and Contributors.
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation;
 * either version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * --------------------------------
 * DynamicTimeSeriesCollection.java
 * --------------------------------
 * (C) Copyright 2002, 2003, by I. H. Thomae and Contributors.
 *
 * Original Author:  I. H. Thomae (ithomae@ists.dartmouth.edu);
 * Contributor(s):   David Gilbert (for Object Refinery Limited);
 *
 * $Id: DynamicTimeSeriesCollection.java,v 1.5 2003/06/13 15:46:46 mungady Exp $
 *
 * Changes
 * -------
 * 22-Nov-2002 : Initial version completed
 *    Jan 2003 : Optimized advanceTime(), added implemnt'n of RangeInfo intfc
 *               (using cached values for min, max, and range); also added
 *               getOldestIndex() and getNewestIndex() ftns so client classes
 *               can use this class as the master "index authority".
 * 22-Jan-2003 : Made this class stand on its own, rather than extending
 *               class FastTimeSeriesCollection
 * 31-Jan-2003 : Changed TimePeriod --> RegularTimePeriod (DG);
 * 13-Mar-2003 : Moved to com.jrefinery.data.time package (DG);
 * 29-Apr-2003 : Added small change to appendData method, from Irv Thomae (DG);
 *
 */

package org.jfree.data.time;

import java.util.Calendar;
import java.util.TimeZone;

import org.jfree.data.AbstractSeriesDataset;
import org.jfree.data.DomainInfo;
import org.jfree.data.IntervalXYDataset;
import org.jfree.data.Range;
import org.jfree.data.RangeInfo;
import org.jfree.data.SeriesChangeEvent;

/**
 * A dynamic dataset.
 * <p>
 * Like FastTimeSeriesCollection, this class is a functional replacement
 * for JFreeChart's TimeSeriesCollection _and_ TimeSeries classes.
 * FastTimeSeriesCollection is appropriate for a fixed time range; for
 * real-time applications this subclass adds the ability to append new
 * data and discard the oldest.
 * In this class, the arrays used in FastTimeSeriesCollection become FIFO's.
 * NOTE:As presented here, all data is assumed >= 0, an assumption which is
 * embodied only in methods associated with interface RangeInfo.
 *
 * @author Irv Thomae.
 */
public class DynamicTimeSeriesCollection extends AbstractSeriesDataset
                                         implements IntervalXYDataset,
                                                    DomainInfo,
                                                    RangeInfo {

    /** Useful constant for controlling the x-value returned for a time period. */
    public static final int START = 0;

    /** Useful constant for controlling the x-value returned for a time period. */
    public static final int MIDDLE = 1;

    /** Useful constant for controlling the x-value returned for a time period. */
    public static final int END = 2;

    /** The maximum number of items for each series (can be overridden). */
    private int maximumItemCount = 2000;  // an arbitrary safe default value

    /** The history count. */
    protected int historyCount;

    /** Storage for the series names. */
    private String[] seriesNames;

    /** The time period class - barely used, and could be removed (DG). */
    private Class timePeriodClass = Minute.class;   // default value;

    /** Storage for the x-values. */
    protected RegularTimePeriod[] pointsInTime;

    /** The number of series. */
    private int seriesCount;

    /**
     * A wrapper for a fixed array of float values.
     */
    protected class ValueSequence {

        /** Storage for the float values. */
        float dataPoints[];

        /**
         * Default constructor:
         */
        public ValueSequence() {
            this(DynamicTimeSeriesCollection.this.maximumItemCount);
        }

        /**
         * Creates a sequence with the specified length.
         *
         * @param length  the length.
         */
        public ValueSequence(int length) {
            dataPoints = new float[length];
            for (int i = 0; i < length; i++) {
                this.dataPoints[i] = 0.0f;
            }
        }

        /**
         * Enters data into the storage array.
         *
         * @param index  the index.
         * @param value  the value.
         */
        public void enterData(int index, float value) {
            this.dataPoints[index] = value;
        }

        /**
         * Returns a value from the storage array.
         *
         * @param index  the index.
         *
         * @return The value.
         */
        public float getData(int index) {
            return dataPoints[index];
        }
    }

    /** An array for storing the objects that represent each series. */
    protected ValueSequence[] valueHistory;

    /** A working calendar (to recycle) */
    protected Calendar workingCalendar;

    /** The position within a time period to return as the x-value (START, MIDDLE or END). */
    private int position;

    /**
     * A flag that indicates that the domain is 'points in time'.  If this flag is true, only
     * the x-value is used to determine the range of values in the domain, the start and end
     * x-values are ignored.
     */
    private boolean domainIsPointsInTime;

    /** index for mapping: points to the oldest valid time & data. */
    private int oldestAt;  // as a class variable, initializes == 0

    /** Index of the newest data item. */
    private int newestAt;

    // cached values used for interface DomainInfo:

    /** the # of msec by which time advances. */
    private long deltaTime;

    /** Cached domain start (for use by DomainInfo). */
    private Long domainStart;

    /** Cached domain end (for use by DomainInfo). */
    private Long domainEnd;

    /** Cached domain range (for use by DomainInfo). */
    private Range domainRange;

    // Cached values used for interface RangeInfo: (note minValue pinned at 0)
    //   A single set of extrema covers the entire SeriesCollection

    /** The minimum value. */
    private Float minValue = new Float(0.0f);

    /** The maximum value. */
    private Float maxValue = null;

    /** The value range. */
    private Range valueRange;  // autoinit's to null.

    /**
     * Constructs a dataset with capacity for N series, tied to default timezone.
     *
     * @param nSeries the number of series to be accommodated.
     * @param nMoments the number of TimePeriods to be spanned.
     */
    public DynamicTimeSeriesCollection(int nSeries, int nMoments) {

        this(nSeries, nMoments, new Millisecond(), TimeZone.getDefault());
        this.newestAt = nMoments - 1;

    }

    /**
     * Constructs an empty dataset, tied to a specific timezone.
     *
     * @param nSeries the number of series to be accommodated
     * @param nMoments the number of TimePeriods to be spanned
     * @param zone the timezone.
     */
    public DynamicTimeSeriesCollection(int nSeries, int nMoments, TimeZone zone) {
        this(nSeries, nMoments, new Millisecond(), zone);
        this.newestAt = nMoments - 1;
    }

    /**
     * Creates a new dataset.
     *
     * @param nSeries  the number of series.
     * @param nMoments  the number of items per series.
     * @param timeSample  a time period sample.
     */
    public DynamicTimeSeriesCollection(int nSeries,
                                       int nMoments,
                                       RegularTimePeriod timeSample) {
        this(nSeries, nMoments, timeSample, TimeZone.getDefault());
    }

    /**
     * Creates a new dataset.
     *
     * @param nSeries  the number of series.
     * @param nMoments  the number of items per series.
     * @param timeSample  a time period sample.
     * @param zone  the time zone.
     */
    public DynamicTimeSeriesCollection(int nSeries,
                                       int nMoments,
                                       RegularTimePeriod timeSample,
                                       TimeZone zone) {

        // the first initialization must precede creation of the ValueSet array:
        this.maximumItemCount = nMoments;  // establishes length of each array
        this.historyCount = nMoments;
        this.seriesNames = new String[nSeries];
        // initialize the members of "seriesNames" array so they won't be null:
        for (int i = 0; i < nSeries; i++) {
            seriesNames[i] = "";
        }
        this.newestAt = nMoments - 1;
        this.valueHistory = new ValueSequence[nSeries];
        this.timePeriodClass = timeSample.getClass();

        /// Expand the following for all defined TimePeriods:
        if (timePeriodClass == Second.class) {
            this.pointsInTime = new Second[nMoments];
        }
        else if (timePeriodClass == Minute.class) {
            this.pointsInTime = new Minute[nMoments];
        }
        else if (timePeriodClass == Hour.class) {
            this.pointsInTime = new Hour[nMoments];
        }
        ///  .. etc....
        this.workingCalendar = Calendar.getInstance(zone);
        this.position = START;
        this.domainIsPointsInTime = true;
    }

    /**
     * Fill the pointsInTime with times using TimePeriod.next():
     * Will silently return if the time array was already populated.
     *
     * Also computes the data cached for later use by
     * methods implementing the DomainInfo interface:
     *
     * @param start  the start.
     *
     * @return ??.
     */
    public synchronized long setTimeBase(RegularTimePeriod start) {

        if (pointsInTime[0] == null) {
            pointsInTime[0] = start;
            for (int i = 1; i < historyCount; i++) {
                pointsInTime[i] = pointsInTime[i - 1].next();
            }
        }
        long oldestL = pointsInTime[0].getFirstMillisecond(workingCalendar);
        long nextL = pointsInTime[1].getFirstMillisecond(workingCalendar);
        deltaTime = nextL - oldestL;
        oldestAt = 0;
        newestAt = historyCount - 1;
        findDomainLimits();
        return deltaTime;

    }

    /**
     * Finds the domain limits.
     * <p>
     * Note: this doesn't need to be synchronized because it's called from within another method
     * that already is.
     */
    protected void findDomainLimits() {

        long startL = getOldestTime().getFirstMillisecond(workingCalendar);
        long endL;
        if (domainIsPointsInTime) {
            endL = getNewestTime().getFirstMillisecond(workingCalendar);
        }
        else {
            endL = getNewestTime().getLastMillisecond(workingCalendar);
        }
        this.domainStart = new Long(startL);
        this.domainEnd = new Long(endL);
        this.domainRange = new Range((double) startL, (double) endL);

    }

    /**
     * Returns the x position type (START, MIDDLE or END).
     *
     * @return The x position type.
     */
    public int getPosition() {
        return position;
    }

    /**
     * Sets the x position type (START, MIDDLE or END).
     *
     * @param position The x position type.
     */
    public void setPosition(int position) {
        this.position = position;
    }

    /**
     * Adds a series to the dataset.  Only the y-values are supplied, the x-values are specified
     * elsewhere.
     *
     * @param values  the y-values.
     * @param seriesNumber  the series index (zero-based).
     * @param seriesName  the seriesName.
     *
     * Use this as-is during setup only, or add the synchronized keyword around the copy loop.
     */
    public void addSeries(float[] values,
                          int seriesNumber, String seriesName) {

        invalidateRangeInfo();
        int i;
        if (values == null) {
            throw new IllegalArgumentException("TimeSeriesDataset.addSeries(...): "
                + "cannot add null array of values.");
        }
        if (seriesNumber >= valueHistory.length) {
            throw new IllegalArgumentException("TimeSeriesDataset.addSeries(...): "
                + "cannot add more series than specified in c'tor");
        }
        if (valueHistory[seriesNumber] == null) {
            valueHistory[seriesNumber] = new ValueSequence(historyCount);
            seriesCount++;
        }   // But if that series array already exists, just overwrite its contents

        // Avoid IndexOutOfBoundsException:
        int srcLength = values.length;
        int copyLength = historyCount;
        boolean fillNeeded = false;
        if (srcLength < historyCount) {
            fillNeeded = true;
            copyLength = srcLength;
        }
        //{
        for (i = 0; i < copyLength; i++) { // deep copy from values[], caller can safely discard
                                           // that array
            valueHistory[seriesNumber].enterData(i, values[i]);
        }
        if (fillNeeded) {
            for (i = copyLength; i < historyCount; i++) {
                valueHistory[seriesNumber].enterData(i, 0.0f);
            }
        }
      //}
        if (seriesName != null) {
            seriesNames[seriesNumber] = seriesName;
        }
        fireSeriesChanged();

    }

    /**
     * Sets the name of a series.
     * <p>
     * If planning to add values individually.
     *
     * @param seriesNumber  the series.
     * @param newName  the new name.
     */
    public void setSeriesName(int seriesNumber, String newName) {
        seriesNames[seriesNumber] = newName;
    }

    /**
     * Adds a value to a series.
     *
     * @param seriesNumber  the series index.
     * @param index  ??.
     * @param value  the value.
     */
    public void addValue(int seriesNumber, int index, float value) {

        invalidateRangeInfo();
        if (seriesNumber >= valueHistory.length) {
            throw new IllegalArgumentException("TimeSeriesDataset.addValue(...): series #"
                + seriesNumber + "unspecified in c'tor");
        }
        if (valueHistory[seriesNumber] == null) {
            valueHistory[seriesNumber] = new ValueSequence(historyCount);
            seriesCount++;
        }  // But if that series array already exists, just overwrite its contents
        //synchronized(this)
        {
            valueHistory[seriesNumber].enterData(index, value);
        }
        fireSeriesChanged();
    }

    /**
     * Returns the number of series in the collection.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -