View Javadoc

1   /* Open Log Viewer
2    *
3    * Copyright 2011
4    *
5    * This file is part of the OpenLogViewer project.
6    *
7    * OpenLogViewer software is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as published by
9    * the Free Software Foundation, either version 3 of the License, or
10   * (at your option) any later version.
11   *
12   * OpenLogViewer software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with any OpenLogViewer software.  If not, see http://www.gnu.org/licenses/
19   *
20   * I ask that if you make any changes to this file you fork the code on github.com!
21   *
22   */
23  package org.diyefi.openlogviewer.genericlog;
24  
25  import java.awt.Color;
26  import java.awt.datatransfer.DataFlavor;
27  import java.awt.datatransfer.Transferable;
28  import java.awt.datatransfer.UnsupportedFlavorException;
29  import java.beans.PropertyChangeListener;
30  import java.beans.PropertyChangeSupport;
31  import java.io.IOException;
32  import java.io.Serializable;
33  import java.util.Arrays;
34  
35  import org.diyefi.openlogviewer.coloring.InitialLineColoring;
36  
37  /**
38   * GenericDataElement is Comparable Serializable and Transferable and supports property change events
39   * it was built this way in order to be copy/pasteable later in the future
40   * when constructed this is the meat and potatoes of the program, the graphs and data
41   * displayed are pulled from these objects.
42   * @author Bryan Harris
43   */
44  public final class GenericDataElement implements Comparable<GenericDataElement>, Serializable, Transferable {
45  	private static final long serialVersionUID = 1L;
46  
47  	private static int currentRecord;
48  
49  	/**
50  	 * The meat of this object! Previously in a slow fat ArrayList.
51  	 */
52  	private double[] values;
53  
54  	// These two fields belong here:
55  	private double minValue;
56  	private double maxValue;
57  	private boolean realMinAndMaxFound = false;
58  
59  	// These three do not - move them into some graphics object and keep the data separated from the look...
60  	private double displayMinValue;
61  	private double displayMaxValue;
62  	private Color displayColor;
63  	private boolean displayMinAndMaxSet = false;
64  
65  	/**
66  	 * GDE Header name
67  	 */
68  	private String name;
69  
70  	/**
71  	 * Division on the Graphing layer
72  	 */
73  	private int splitNumber;
74  	private PropertyChangeSupport PCS;
75  	private DataFlavor[] dataFlavor;
76  
77  	/**
78  	 * Constructor brings the GDE up to speed, defaulting with an available 50,000 datapoints
79  	 * in order to reduce the number of times the Array list has to copy its contents
80  	 * in order to increase size.
81  	 */
82  	protected GenericDataElement(final int initialLength) {
83  		values = new double[initialLength];
84  
85  		PCS = new PropertyChangeSupport(this);
86  
87  		maxValue = -Double.MAX_VALUE;
88  		minValue = Double.MAX_VALUE;
89  
90  		// this should just be white and should become something at drop time. Drop has an event, let's use it.
91  		displayColor = InitialLineColoring.INSTANCE.getBestAvailableColor();
92  		splitNumber = 1;
93  		addFlavors();
94  	}
95  
96  	protected void increaseCapacity(final int ourLoadFactor) {
97  		values = Arrays.copyOf(values, (values.length * ourLoadFactor));
98  	}
99  
100 	protected static void incrementPosition() {
101 		currentRecord++;
102 	}
103 	protected static void resetPosition() {
104 		currentRecord = -1;
105 	}
106 
107 	/**
108 	 * Data type support for Transferable
109 	 */
110 	private void addFlavors() {
111 		dataFlavor = new DataFlavor[3];
112 		final String supportedFlavour = DataFlavor.javaSerializedObjectMimeType + ";class=\"" + GenericDataElement.class.getName() + "\"";
113 		dataFlavor[0] = new DataFlavor(supportedFlavour, "OLV GenericDataElement");
114 		dataFlavor[1] = DataFlavor.stringFlavor;
115 		dataFlavor[2] = DataFlavor.getTextPlainUnicodeFlavor();
116 	}
117 
118 	/**
119 	 * override add(<T> t) of ArrayList to find min and max values before adding to the List
120 	 * @param d Double - Double.parseDouble(String) value to add to the array
121 	 * @return true on success, false if unable
122 	 */
123 	public void add(final double value) { //  TODO simplify the shit out of this, and/or remove it.
124 		values[currentRecord] = value;
125 	}
126 
127 	// TODO maybe make these a direct reference to the array for efficiency sake, and above ^
128 	public double get(final int index) {
129 		return values[index];
130 	}
131 	public int size() { // TODO ^
132 		return currentRecord; // was values.length; array length is longer than data in it.
133 	}
134 
135 	/**
136 	 * sets the splitNumber or division of the graph in the graphing screen
137 	 * if its the same a property change event is fired called "Split"
138 	 *
139 	 * @param splitNumber
140 	 */
141 	public void setSplitNumber(final int splitNumber) {
142 		int newSplitNumber = 1;
143 		if (splitNumber > 1) {
144 			newSplitNumber = splitNumber;
145 		}
146 
147 		final int old = this.splitNumber;
148 		this.splitNumber = newSplitNumber;
149 		PCS.firePropertyChange("Split", old, this.splitNumber);
150 	}
151 
152 	/**
153 	 * TODO move this into GUI space and out of this class!!
154 	 */
155 	public void reset() {
156 		displayMinValue = getMinValue();
157 		displayMaxValue = getMaxValue();
158 	}
159 
160 	public void addPropertyChangeListener(final String property, final PropertyChangeListener PCL) {
161 		PCS.addPropertyChangeListener(property, PCL);
162 	}
163 
164 	public void removePropertyChangeListener(final String property, final PropertyChangeListener PCL) {
165 		PCS.removePropertyChangeListener(property, PCL);
166 	}
167 
168 	@Override
169 	public String toString() {
170 		return this.name;
171 	}
172 
173 	@Override
174 	public int compareTo(final GenericDataElement otherGDE) {
175 		return this.getName().compareToIgnoreCase(otherGDE.getName());
176 	}
177 
178 	@Override
179 	public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException, IOException {
180 		if (flavor.equals(dataFlavor[0])) {
181 			return this;
182 		} else if (flavor.equals(dataFlavor[1])) {
183 			return "Unsupported";
184 		} else if (flavor.equals(dataFlavor[2])) {
185 			return "Unsupported";
186 		} else {
187 			throw new UnsupportedFlavorException(flavor);
188 		}
189 	}
190 
191 	@Override
192 	public DataFlavor[] getTransferDataFlavors() {
193 		return dataFlavor;
194 	}
195 
196 	@Override
197 	public boolean isDataFlavorSupported(final DataFlavor flavor) {
198 		for (int i = 0; i < dataFlavor.length; i++) {
199 			if (flavor.equals(dataFlavor[i])) {
200 				return true;
201 			}
202 		}
203 		return false;
204 	}
205 
206 	/**
207 	 * set header name, called during GenericLog construction
208 	 * @param name
209 	 */
210 	public void setName(final String name) {
211 		this.name = name;
212 	}
213 	public String getName() {
214 		return name;
215 	}
216 
217 	public double getMaxValue() {
218 		findMinAndMaxValues();
219 		return maxValue;
220 	}
221 	public double getMinValue() {
222 		findMinAndMaxValues();
223 		return minValue;
224 	}
225 
226 	private void findMinAndMaxValues() {
227 		if (!realMinAndMaxFound) {
228 			for (int i = 0; i < currentRecord; i++) {
229 				final double value = values[i];
230 				if (maxValue < value) {
231 					maxValue = value;
232 				}
233 				if (minValue > value) {
234 					minValue = value;
235 				}
236 			}
237 			// System.out.println("MinMaxFor: " + this.name); // Used to check for option pane running this code inappropriately...
238 
239 			realMinAndMaxFound = true;
240 		}
241 	}
242 
243 	public double getDisplayMinValue() {
244 		setDisplayMinAndMaxDefaultsIfRequired();
245 		return displayMinValue;
246 	}
247 	public double getDisplayMaxValue() {
248 		setDisplayMinAndMaxDefaultsIfRequired();
249 		return displayMaxValue;
250 	}
251 	private void setDisplayMinAndMaxDefaultsIfRequired() {
252 		if (!displayMinAndMaxSet) {
253 			displayMinValue = getMinValue();
254 			displayMaxValue = getMaxValue();
255 			displayMinAndMaxSet = true;
256 		}
257 	}
258 
259 	public void setDisplayMaxValue(final double highValue) {
260 		if (!displayMinAndMaxSet) {
261 			displayMinValue = getMinValue();
262 			displayMinAndMaxSet = true;
263 		}
264 		this.displayMaxValue = highValue;
265 	}
266 	public void setDisplayMinValue(final double lowValue) {
267 		if (!displayMinAndMaxSet) {
268 			displayMaxValue = getMaxValue();
269 			displayMinAndMaxSet = true;
270 		}
271 		this.displayMinValue = lowValue;
272 	}
273 
274 	public Color getDisplayColor() {
275 		return displayColor;
276 	}
277 	public void setDisplayColor(final Color c) {
278 		displayColor = c;
279 	}
280 	public int getSplitNumber() {
281 		return splitNumber;
282 	}
283 
284 	public void clearOut() {
285 		values = null;
286 	}
287 }