All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.loadcoder.load.chart.logic.ChartLogic Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (C) 2018 Stefan Vahlgren at Loadcoder
 * 
 * This file is part of Loadcoder.
 * 
 * Loadcoder is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Loadcoder 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 ******************************************************************************/
package com.loadcoder.load.chart.logic;

import static com.loadcoder.load.chart.common.YCalculator.avg;
import static com.loadcoder.load.chart.common.YCalculator.max;
import static com.loadcoder.statics.Time.HOUR;

import java.awt.Color;
import java.awt.Paint;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jfree.chart.LegendItem;

import com.loadcoder.load.chart.common.CommonSample;
import com.loadcoder.load.chart.common.CommonSampleGroup;
import com.loadcoder.load.chart.common.CommonSeries;
import com.loadcoder.load.chart.common.CommonSeriesCalculator;
import com.loadcoder.load.chart.common.CommonYCalculator;
import com.loadcoder.load.chart.common.YCalculator;
import com.loadcoder.load.chart.data.DataSet;
import com.loadcoder.load.chart.data.FilteredData;
import com.loadcoder.load.chart.data.Point;
import com.loadcoder.load.chart.data.Range;
import com.loadcoder.load.chart.jfreechart.XYDottedSeriesExtension;
import com.loadcoder.load.chart.jfreechart.XYPlotExtension;
import com.loadcoder.load.chart.jfreechart.XYSeriesCollectionExtention;
import com.loadcoder.load.chart.jfreechart.XYSeriesExtension;
import com.loadcoder.load.chart.menu.DataSetUserType;
import com.loadcoder.load.chart.sampling.Sample;
import com.loadcoder.load.chart.sampling.SampleGroup;
import com.loadcoder.load.chart.utilities.ChartUtils;
import com.loadcoder.load.chart.utilities.ColorUtils;
import com.loadcoder.load.chart.utilities.SampleStatics;
import com.loadcoder.load.jfreechartfixes.XYLineAndShapeRendererExtention;
import com.loadcoder.result.TransactionExecutionResult;

public abstract class ChartLogic {

	private Map seriesCommonMap = new HashMap();

	private CommonSeries[] commonsToBeUsed;

	protected Long earliestX;

	protected long highestX = 0;

	private long sampleLengthToUse;

	final boolean locked;

	private FilteredData filteredData;

	private List removalFiltersInUse = new ArrayList();

	protected final XYSeriesCollectionExtention seriesCollection;

	protected final XYPlotExtension plot;

	protected final XYLineAndShapeRendererExtention renderer;

	protected final Map seriesVisible;

	protected final List yCalculators = new ArrayList();

	public YCalculator yCalculatorToUse = avg;

	private final Map existingColors;

	public final static int TARGET_AMOUNT_OF_POINTS_DEFAULT = 20_000;

	protected static final long SAMPLELENGTH_DEFAULT = 1000;
	/**
	 * customizedColors is not yet supported.
	 */
	@Deprecated
	Map customizedColors;

	private final List seriesKeys = new ArrayList();

	protected final Map legends = new HashMap();

	protected final List commonSeriesCalculators = new ArrayList();

	protected final List ranges = new ArrayList();

	public final List blacklistColors = new ArrayList();

	protected List getSeriesKeys() {
		return seriesKeys;
	}

	protected void addSeriesKey(String key) {
		if (!seriesKeys.contains(key)) {
			seriesKeys.add(key);
		}
	}

	protected Map getCommonSeriesMap() {
		return seriesCommonMap;
	}

	protected abstract void update(Map> listOfListOfList,
			HashSet hashesGettingUpdated);

	protected abstract void doUpdate();

	public YCalculator getYCalculatorToUse() {
		return yCalculatorToUse;
	}

	protected XYSeriesCollectionExtention getSeriesCollection() {
		return seriesCollection;
	}

	public Map getExistingColors() {
		return existingColors;
	}

	public XYPlotExtension getPlot() {
		return plot;
	}

//	protected List getCommonSeries() {
//		return commonSeries;
//	}

	protected long getXDiff() {
		if (earliestX == null)
			return 0;
		long xDiff = highestX - earliestX;
		return xDiff;
	}

	// only used for test
	protected Range lookupCorrectRange(long ts) {
		for (Range range : ranges) {
			if (range.isTimestampInThisRange(ts))
				return range;
		}
		throw new RuntimeException("No range was found for timestamp " + ts);
	}

	public void doSafeUpdate() {
		synchronized (plot) {
			doUpdate();
			long xDiff = getXDiff();
			if (xDiff > 23 * HOUR) {
				plot.changeToMonthAndDayDateAxisFormat();
			}
		}
	}

	public List getRemovalFiltersInUse() {
		return removalFiltersInUse;
	}

	protected void setFilteredData(FilteredData filteredData) {
		this.filteredData = filteredData;
	}

	public FilteredData getFilteredData() {
		return filteredData;
	}

	public long getSampleLengthToUse() {
		return sampleLengthToUse;
	}

	public void setSampleLengthToUse(long sampleLengthToUse) {
		this.sampleLengthToUse = sampleLengthToUse;
	}

	public List getyCalculators() {
		return yCalculators;
	}

	void addToSeriesKeys(FilteredData filteredData, List seriesKeys) {
		for (DataSet dataSet : filteredData.getDataSets()) {
			String s = dataSet.getName();
			if (!seriesKeys.contains(s))
				seriesKeys.add(s);
		}
	}

	public ChartLogic(XYSeriesCollectionExtention seriesCollection, XYPlotExtension plot,
			XYLineAndShapeRendererExtention renderer, Map seriesVisible, CommonSeries[] commonSeries,
			boolean locked, Map existingColors) {
		this.locked = locked;
		this.seriesCollection = seriesCollection;
		this.plot = plot;
		this.renderer = renderer;
		this.seriesVisible = seriesVisible;
		this.commonsToBeUsed = commonSeries == null ? CommonSeries.values() : commonSeries;
		this.existingColors = existingColors;
		for (CommonSeries s : commonsToBeUsed) {
			existingColors.put(s.getName(), s.getColor());
		}

		yCalculators.add(avg);
		yCalculators.add(max);

		ColorUtils.defaultBlacklistColors.stream().forEach((blackListed) -> {
			blacklistColors.add(blackListed);
		});
//		populateColorArray();
	}

	public static void addSurroundingTimestampsAsUpdates(Set hashesGettingUpdated, long sampleStart,
			long earliest, long latest, List ranges, long currentSampleLength, Set sampleTimestamps,
			Map aboutToBeUpdated) {

		// check backwards
		long iterator = sampleStart;
		while (earliest < iterator) {
			long lastTsInPrevious = iterator - 1;
			long sampleLength = Range.findSampleLength(lastTsInPrevious, ranges);
			long firstTsInPrevious = SampleGroup.calculateFirstTs(lastTsInPrevious, sampleLength);

			boolean exists = false;
			if (sampleTimestamps.contains(firstTsInPrevious) || aboutToBeUpdated.containsKey(firstTsInPrevious)) {
				exists = true;
			}

			if (exists) {
				break;
			} else {
				if (!hashesGettingUpdated.contains(firstTsInPrevious))
					hashesGettingUpdated.add(firstTsInPrevious);
				iterator = firstTsInPrevious;
			}
		}

		// check forward
		iterator = sampleStart;
		while (latest > iterator) {
			long sampleLength = Range.findSampleLength(iterator, ranges);
			long firstTsInNext = iterator + sampleLength;
			boolean exists = false;
			if (sampleTimestamps.contains(firstTsInNext) || aboutToBeUpdated.containsKey(firstTsInNext)) {
				exists = true;
			}

			if (exists) {
				break;
			} else {
				if (!hashesGettingUpdated.contains(firstTsInNext))
					hashesGettingUpdated.add(firstTsInNext);
				iterator = firstTsInNext;
			}
		}
	}

	public void createCommons() {

//		commonSeries = new ArrayList();
		commonSeriesCalculators.clear();
//		commonSeries.clear();
		seriesCommonMap.clear();

		Arrays.stream(commonsToBeUsed).forEach((common) -> {
			Color c = existingColors.get(common.getName());
			if (c == null) {
				c = common.getColor();
			}
			XYSeriesExtension xySeries = new XYSeriesExtension(common.getName(), true, false, c);
			seriesCommonMap.put(common.getName(), xySeries);
			commonSeriesCalculators.add(new CommonSeriesCalculator(xySeries, common.getCommonYCalculator()));
//			commonSeries.add(xySeries);
		});

	}

	public void addSeries(XYSeriesExtension serie) {
		seriesCollection.addSeries(serie);
		int indexOfSeries = seriesCollection.indexOf(serie);
		LegendItem legend = legends.get(serie.getKey());
		if (legend == null) {
			legend = plot.getRenderer().getLegendItem(0, indexOfSeries);
			Color c = existingColors.get(serie.getKey());
//			legend.setFillPaint(serie.getColorInTheChart());
			legend.setFillPaint(c);

			legend.setShapeVisible(true);
			legend.setLineVisible(false);

			legends.put(serie.getKey(), legend);
			plot.getLegendItems().add(legend);
		} else {
		}
		serie.setLegend(legend);
	}

	public void removeSeries(XYSeriesExtension serie) {
		int indexOfSeries = seriesCollection.indexOf(serie);
		seriesCollection.removeSeries(indexOfSeries);
	}

	/*
	 * iterate through all timestamp that are the first one in one of the updated
	 * samples. The series that are affected by the transaction series are going to
	 * be upated below
	 */
	void updateCommonsWithSamples(HashSet hashesGettingUpdated, Map sampleGroups,
			Map samplesCommonMap, List sampleGroupCommonList) {

		/*
		 * iterate through all timestamp that are the first one in one of the updated
		 * samples. The series that are affected by the transaction series are going to
		 * be upated below
		 */
		for (Long l : hashesGettingUpdated) {
			for (CommonSeriesCalculator calc : commonSeriesCalculators) {
				XYSeriesExtension series = calc.getSeries();

				CommonYCalculator calculator = calc.getCalculator();
				Range r = getSampleLength(l);
				double amount = calculator.calculateCommonY(seriesKeys, l, sampleGroups, r.getSampleLength());

				// get or create the samplegroup for the common series
				String commonKey = series.getKey();
				CommonSampleGroup commonSampleGroup = samplesCommonMap.get(commonKey);
				if (commonSampleGroup == null) {
					commonSampleGroup = new CommonSampleGroup(series);
					samplesCommonMap.put(commonKey, commonSampleGroup);
					sampleGroupCommonList.add(commonSampleGroup);
				}

				CommonSample cs = commonSampleGroup.getAndCreateSample(l, commonKey, r.getSampleLength());

				long longAmount = Sample.amountToYValue(amount);
				cs.setY(longAmount);
				if (cs.getFirst() == null) {
					cs.initDataItems();
					series.add(cs.getFirst(), false);
					if (SampleStatics.USE_TWO_SAMPLE_POINTS) {
						series.add(cs.getLast(), false);
					}
				} else {
					cs.updateDataItems();
				}
			}
		}
	}

	Paint getSeriesColor(String dataSetName) {
		LegendItem legend = legends.get(dataSetName);

		Paint seriesColor = null;
		if (legend == null) {
			seriesColor = getNewColor(dataSetName);
		} else {
			seriesColor = legend.getFillPaint();
		}
		return seriesColor;
	}

	void getSerieses(List dataSets, boolean dottedMode, Map seriesMap) {
		for (DataSet dataSet : dataSets) {
			String dataSetName = dataSet.getName();

			Paint seriesColor = getSeriesColor(dataSetName);

			XYSeriesExtension seriesName = seriesMap.get(dataSetName);
			if (seriesName == null) {
				XYSeriesExtension serie = new XYSeriesExtension(dataSetName, true, false, seriesColor);
				seriesMap.put(dataSetName, serie);
			}
		}
	}

	void adjustVisibilityOfSeries(XYSeriesExtension serie) {
		int indexOfSeries = seriesCollection.indexOf(serie);
		renderer.setSeriesShape(indexOfSeries, XYDottedSeriesExtension.DOTTEDSHAPE);

		Boolean visible = seriesVisible.get(serie.getKey());

		if (visible == null) {
			visible = true;
			seriesVisible.put(serie.getKey(), visible);
		}

		serie.setVisible(visible);
		serie.getLegend().setShapeVisible(visible);
		renderer.setSeriesLinesVisible(indexOfSeries, visible);
		// is sample mode, the shapes shall always be invisible.
		renderer.setSeriesShapesVisible(indexOfSeries, false);
	}

	void addPoints(List dataSets, Map sampleGroups, Set sampleTimestamps) {
		long start = System.currentTimeMillis();
		for (DataSet dataSet : dataSets) {
			String dataSetName = dataSet.getName();
			SampleGroup sampleGroup = sampleGroups.get(dataSetName);
			createSamplesAndAddPoints(dataSetName, sampleGroup.getSeries(), dataSet, sampleGroup, sampleTimestamps);
		}
	}

	void createSamplesGroups(Map seriesMap, Map sampleGroups) {
		for (String key : seriesKeys) {
			XYSeriesExtension serie = seriesMap.get(key);
			SampleGroup sampleGroup = sampleGroups.get(key);
			if (sampleGroup == null) {
				sampleGroup = new SampleGroup(sampleLengthToUse, serie, locked);
				sampleGroups.put(key, sampleGroup);
			}
		}
	}

	void createSamplesAndAddPoints(String dataSetName, XYSeriesExtension serie, DataSet dataSet,
			SampleGroup sampleGroup, Set sampleTimestamps) {
		for (Point point : dataSet.getPoints()) {
			long x = point.getX();

			if (x > highestX)
				highestX = x;
			if (earliestX == null || x < earliestX) {
				earliestX = x;

				if (ranges.isEmpty()) {
					Sample s = sampleGroup.getAndCreateSample(point.getX(), dataSetName, sampleLengthToUse);
					ranges.add(new Range(s.getFirstTs(), Long.MAX_VALUE, sampleLengthToUse));

				} else {

					/*
					 * if there are more than one range, and a new earliestTimestamp is added we
					 * must pick up the last added range here.
					 */
					Range r = ranges.get(ranges.size() - 1);
					long sampleLengthOfTheLastAddedRange = r.getSampleLength();

					/*
					 * the last added range will get a new start equal to the firstTs for the Sample
					 * of this Point that has the earliest timestamp
					 */
					Sample s = sampleGroup.getAndCreateSample(x, dataSetName, sampleLengthOfTheLastAddedRange);
					r.setStart(s.getFirstTs());
				}
			}

			// fetch the correct Range for the point
			Range rangeToUse = getSampleLength(x);

			// here the Sample for the point is either fetched of created
			Sample s = sampleGroup.getAndCreateSample(point.getX(), dataSetName, rangeToUse.getSampleLength());

			if (!sampleTimestamps.contains(s.getFirstTs())) {
				sampleTimestamps.add(s.getFirstTs());
			}

			s.addPoint(point);
			if (!point.isStatus()) {
				s.increaseFails();
			}

			// This sample needs to be recalculated and redrawn!
			if (!sampleGroup.getSamplesUnupdated().containsKey(s.getFirstTs())) {
				sampleGroup.getSamplesUnupdated().put(s.getFirstTs(), s);
			}
		}
	}

	public void addAllCommonSeriesToTheChart() {
		for (XYSeriesExtension series : getCommonSeriesMap().values()) {
			addSeries(series);
			int indexOfSeries = seriesCollection.indexOf(series);

			Boolean visible = seriesVisible.get(series.getKey());
			if (visible == null || visible) {
				renderer.setSeriesLinesVisible(indexOfSeries, true);
			} else {
				renderer.setSeriesLinesVisible(indexOfSeries, false);
			}
			renderer.setSeriesShapesVisible(indexOfSeries, false);
		}
	}

	public Range getSampleLength(long timestamp) {
		for (Range range : ranges) {
			if (timestamp >= range.getStart() && timestamp <= range.getEnd()) {
				return range;
			}
		}
		return null;
	}

//	void populateColorArray() {
//		if (customizedColors != null) {
//			Iterator> i = customizedColors.entrySet().iterator();
//			while (i.hasNext()) {
//				Entry e = i.next();
//				existingColors.add(e.getValue());
//			}
//		}
//		for (CommonSeries commonSerie : commonsToBeUsed) {
//			Color c = commonSerie.getColor();
//			existingColors.add(c);
//		}
//	}

	public synchronized Color getNewColor(String seriesKey) {

//		if (customizedColors != null) {
//			Color color = customizedColors.get(seriesKey);
//			return color;
//		}
		Set set = new HashSet(existingColors.values());
		Color newColor = ColorUtils.getNewContrastfulColor(set, blacklistColors);
		existingColors.put(seriesKey, newColor);
//		existingColors.add(newColor);
		return newColor;
	}

	public void forceRerender() {
		seriesCollection.fireChange();
	}

	void updateSeriesWithSamples(Set hashesGettingUpdated, List dataSets,
			Map sampleGroups, Set sampleTimestamps, boolean dottedMode) {

		/*
		 * The new data has now been arranged into correct Samples. Time to calculate
		 * each of the affected Samples and update the points.
		 */
		for (String key : seriesKeys) {

			SampleGroup group = sampleGroups.get(key);
			XYSeriesExtension series = group.getSeries();

			Set aboutToBeUpdatedKeys = group.getSamplesUnupdated().keySet();
			for (Long unupdatedSampleKey : aboutToBeUpdatedKeys) {
				Sample sample = group.getSamplesUnupdated().get(unupdatedSampleKey);
				sample.calculateY(yCalculatorToUse);
				double y = sample.getY();

				if (dottedMode == false) {
					// y will be -1 if there are no data in the sample. No items to add to the
					// series
					if (y != -1) {
						// itemSeriesAdder.add(series, sample);
						ChartUtils.populateSeriesWithSamples(sample, series);
					}

				}

				long l = sample.getFirstTs();

				if (!hashesGettingUpdated.contains(l)) {
					hashesGettingUpdated.add(l);
				}

				long[] minmaxPoints = getFilteredData().getMinmaxPoints();

				long earliest = minmaxPoints[0];
				long latest = minmaxPoints[1];

				addSurroundingTimestampsAsUpdates(hashesGettingUpdated, l, earliest, latest, ranges, sample.getLength(),
						sampleTimestamps, group.getSamplesUnupdated());
			}
			group.getSamplesUnupdated().clear();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy