View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of CosyBeans-Common.
5    *
6    * CosyBeans-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * CosyBeans-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with CosyBeans-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.gui.components.wheelswitch;
21  
22  import java.beans.Beans;
23  
24  import com.cosylab.util.PrintfFormat;
25  
26  
27  /**
28   * Formats <code>java.lang.String</code>s to be displayed as digits in  the
29   * <code>Wheelswitch</code>. The format is specified in a format
30   * <code>java.lang.String</code>. <code>WheelswitchFormatter</code> also
31   * stores the value, its bounds (minimum and maximum) and also the unit
32   * displayed by the <code>Wheelswitch</code>.
33   *
34   * @author <a href="mailto:jernej.kamenik@cosylab.com">Jernej Kamenik</a>
35   * @version $id$
36   */
37  public abstract class AbstractWheelswitchFormatter
38  {
39  	protected PrintfFormat defFormatter;
40  	protected PrintfFormat formatter;
41  	protected String formatString = null;
42  	protected String generatedFormatString = null;
43  	protected String unit = null;
44  	protected String valueString = "0.00";
45  	protected double maximum;
46  	protected double minimum;
47  	protected double value = 0.;
48  	protected int maximumDigits = 42;
49  	protected boolean debug = false;
50  
51  	/**
52  	 * Constructs the PlainWheelswitchFormatter and sets the format  string.
53  	 *
54  	 * @param newFormatString
55  	 *
56  	 * @see #setFormat(String)
57  	 */
58  	public AbstractWheelswitchFormatter(String newFormatString)
59  	{
60  		super();
61  		defFormatter = new PrintfFormat("%3.2e");
62  		setMaximum(Double.POSITIVE_INFINITY);
63  		setMinimum(Double.NEGATIVE_INFINITY);
64  		setFormat(newFormatString);
65  	}
66  
67  	/**
68  	 * Constructs the PlainWheelswitchFormatter with no format string.
69  	 *
70  	 * @see #setFormat(String)
71  	 */
72  	public AbstractWheelswitchFormatter()
73  	{
74  		this(null);
75  	}
76  
77  	/**
78  	 * Sets the format string specifiing the format of <code>Wheelswitch</code>
79  	 * display. The format is first checked for validity by the
80  	 * <code>checkFormat(String)</code> method.
81  	 *
82  	 * @param newFormatString
83  	 *
84  	 * @throws IllegalArgumentException DOCUMENT ME!
85  	 *
86  	 * @see Double#parseDouble(String)
87  	 * @see NumberFormatException
88  	 */
89  	public void setFormat(String newFormatString)
90  		throws IllegalArgumentException
91  	{
92  		if (debug) System.out.println("WheelswitchFormatter::setFormat = "+newFormatString);
93  		
94  		if (newFormatString == formatString || Beans.isDesignTime()) {
95  			return;
96  		} else if (newFormatString == null || newFormatString == "") {
97  			formatString = null;
98  			generatedFormatString = null;
99  			formatter = new PrintfFormat(transformFormat(generateFormat()));
100 		} else if (checkFormat(newFormatString)) {
101 			formatString = newFormatString;
102 			formatter = new PrintfFormat(transformFormat(newFormatString));
103 		} else {
104 			throw (new IllegalArgumentException(
105 			    "Invalid format string entered."));
106 		}
107 
108 		setString(String.valueOf(value));
109 	}
110 
111 	/**
112 	 * Returns the currently stored format string.
113 	 *
114 	 * @return the format
115 	 */
116 	public String getFormat()
117 	{
118 		return formatString;
119 	}
120 
121 	/**
122 	 * Sets a new maximum allowed value.
123 	 *
124 	 * @param newMaximum new maximum value
125 	 */
126 	public void setMaximum(double newMaximum)
127 	{
128 		if (newMaximum < minimum) {
129 			newMaximum = minimum;
130 		}
131 
132 		maximum = newMaximum;
133 
134 		if (formatString == null) {
135 			generatedFormatString = null;
136 			formatter = new PrintfFormat(transformFormat(generateFormat()));
137 		}
138 	}
139 
140 	/**
141 	 * Returns the current maximum allowed value.
142 	 *
143 	 * @return double
144 	 */
145 	public double getMaximum()
146 	{
147 		return maximum;
148 	}
149 
150 	/**
151 	 * Sets a new minimum allowed value.
152 	 *
153 	 * @param newMinimum new minimum value
154 	 */
155 	public void setMinimum(double newMinimum)
156 	{
157 		if (newMinimum > maximum) {
158 			newMinimum = maximum;
159 		}
160 
161 		minimum = newMinimum;
162 
163 		if (formatString == null) {
164 			generatedFormatString = null;
165 			formatter = new PrintfFormat(transformFormat(generateFormat()));
166 		}
167 	}
168 
169 	/**
170 	 * Returns the current minimum allowed value.
171 	 *
172 	 * @return the minimum
173 	 */
174 	public double getMinimum()
175 	{
176 		return minimum;
177 	}
178 
179 	/**
180 	 * Sets the value as string.
181 	 *
182 	 * @param newValueString String representing the number to be formatted.  
183 	 * 		It only accepts values within bounds (maximum and minimum).
184 	 */
185 	public void setString(String newValueString)
186 	{
187 		//Debug.out("WheelswitchFormatter#setString(): newValueString=" + newValueString);
188 		//Debug.out("WheelswitchFormatter#setString(): formatString=" + formatString);
189 
190 		double newValue = Double.parseDouble(newValueString);
191 
192 		if (newValue > maximum || newValue < minimum) {
193 			setValue(trimValue(newValue));
194 		} else {
195 			internalSetString(newValueString);
196 		}
197 	}
198 	
199 	/**
200 	 * Returns the formatted string representing the currently stored value.
201 	 *
202 	 * @return value as string
203 	 */
204 	public String getString()
205 	{
206 		String retVal = valueString;
207 
208 		if (maximumDigits > 0 && maximumDigits < valueString.length()) {
209 			retVal = defFormatter.sprintf(value);
210 		}
211 
212 		return retVal;
213 	}
214 
215 	/**
216 	 * Sets a new value and stores its formatted string. It accepts any value.
217 	 *
218 	 * @param newValue new value
219 	 *
220 	 */
221 	public void setValue(double newValue)
222 	{
223 		if (debug) System.out.println("WheelswitchFormatter:SET VALUE: " + newValue);
224 		
225 		internalSetString(String.valueOf(newValue));
226 	}
227 	
228 	/*
229 	 * Sets value and valueString when newValueString is set.
230 	 */
231 	protected abstract void internalSetString(String newValueString);
232 
233 	private double trimValue(double newValue) {
234 		if (newValue > maximum) {
235 			return maximum;
236 		} else if (newValue < minimum) {
237 			return minimum;
238 		}
239 		
240 		return newValue;
241 	}
242 	
243 	/**
244 	 * Returns the currently stored value.
245 	 *
246 	 * @return the value
247 	 */
248 	public double getValue()
249 	{
250 		return value;
251 	}
252 
253 	/**
254 	 * Checks the number format string. The format string should only  consist
255 	 * of characters '#' reperesenting a single number digit, '+' representing
256 	 * a sign digit, '.' representing the decimal symbol  digit and 'E'
257 	 * representing the exponent denominator. These  characters should be
258 	 * arranged in such way that substitution of  the characters '#' with any
259 	 * numerical digits would result in a  correct double expression
260 	 * acceptible by <code>Double.parseDouble()</code>.
261 	 *
262 	 * @param format
263 	 *
264 	 * @return true if a correct format string was entered, false otherwise.
265 	 */
266 	public static boolean checkFormat(String format)
267 	{
268 		boolean dotted = false;
269 		boolean edotted = false;
270 
271 		for (int i = 0; i < format.length(); i++) {
272 			if (format.charAt(i) != '#' && format.charAt(i) != '.'
273 			    && format.charAt(i) != 'E' && format.charAt(i) != '+') {
274 				return false;
275 			}
276 
277 			if (format.charAt(i) == '.') {
278 				if (dotted) {
279 					return false;
280 				} else {
281 					dotted = true;
282 				}
283 			}
284 
285 			if (format.charAt(i) == 'E') {
286 				if (edotted) {
287 					return false;
288 				} else {
289 					dotted = edotted = true;
290 				}
291 
292 				if (i == format.length() - 1) {
293 					return false;
294 				}
295 			}
296 
297 			if (format.charAt(i) == '+') {
298 				if (i != 0 && format.charAt(i - 1) != 'E') {
299 					return false;
300 				}
301 
302 				if (i == format.length() - 1) {
303 					return false;
304 				}
305 
306 				if (format.charAt(i + 1) == 'E') {
307 					return false;
308 				}
309 			}
310 		}
311 
312 		return true;
313 	}
314 
315 	/**
316 	 * A conviniance method for transforming between the  Printf type format
317 	 * strings and wheelswitch type format strings. The transform can be used
318 	 * in either direction. The method does not check for validity of the
319 	 * supplied parameter string and may return bogus results if invalid
320 	 * format strings are passed to it.
321 	 *
322 	 * @param format 
323 	 *
324 	 * @return transformed format
325 	 *
326 	 * @throws NullPointerException if format is null
327 	 * @throws IllegalArgumentException if format is not supported (does not include
328 	 * 		f,d,i,e or E tag)
329 	 */
330 	public static String transformFormat(String format)
331 	{
332 		String retVal = new String();
333 
334 		if (format == null) {
335 			throw new NullPointerException("format");
336 		}
337 
338 		if (checkFormat(format)) {
339 			int expIndex = format.indexOf('E');
340 
341 			if (expIndex == -1) {
342 				expIndex = format.indexOf('e');
343 			}
344 
345 			if (expIndex == -1) {
346 				expIndex = format.length();
347 			}
348 
349 			int dotIndex = format.indexOf('.');
350 
351 			if (dotIndex == -1) {
352 				dotIndex = expIndex;
353 			}
354 
355 			if (expIndex > dotIndex) {
356 				expIndex--;
357 			}
358 
359 			int signTag = ((format.charAt(0) == '+') ? (1) : (0));
360 			retVal = "%";
361 
362 			if (signTag == 1) {
363 				retVal += "+";
364 			}
365 
366 			retVal += String.valueOf(expIndex - signTag) + "."
367 			+ String.valueOf(expIndex - dotIndex) + "f";
368 		} else {
369 			int startIndex = format.indexOf('%');
370 			boolean signTag = format.charAt(startIndex + 1) == '+';
371 			boolean exp = false;
372 
373 			if (signTag) {
374 				startIndex++;
375 			}
376 
377 			int finishIndex = format.indexOf('f', startIndex);
378 
379 			if (finishIndex < 0) {
380 				finishIndex = format.indexOf('d', startIndex);
381 			}
382 
383 			if (finishIndex < 0) {
384 				finishIndex = format.indexOf('i', startIndex);
385 			}
386 
387 			if (finishIndex < 0) {
388 				finishIndex = format.indexOf('e', startIndex);
389 				exp = true;
390 			}
391 
392 			if (finishIndex < 0) {
393 				finishIndex = format.indexOf('E', startIndex);
394 				exp = true;
395 			}
396 
397 			if (finishIndex < 0) {
398 				throw new IllegalArgumentException(
399 				    "Unsuported or invalid format string '" + format + "'!");
400 			}
401 
402 			int dotIndex = format.indexOf('.', startIndex);
403 
404 			if (dotIndex == -1 || dotIndex > finishIndex) {
405 				dotIndex = finishIndex;
406 			}
407 
408 			int lengthDecimal = dotIndex + 1 >= finishIndex ? 0
409 				: Integer.parseInt(format.substring(dotIndex + 1, finishIndex));
410 			int lengthAll = startIndex + 1 == dotIndex ? lengthDecimal + 1
411 				: Integer.parseInt(format.substring(startIndex + 1, dotIndex));
412 			int lengthInteger = Math.max(1, lengthAll - lengthDecimal);
413 
414 			if (signTag) {
415 				retVal += "+";
416 			}
417 
418 			for (int i = 0; i < lengthInteger; i++) {
419 				retVal += "#";
420 			}
421 
422 			if (dotIndex < finishIndex) {
423 				retVal += ".";
424 
425 				for (int i = 0; i < lengthDecimal; i++) {
426 					retVal += "#";
427 				}
428 			}
429 
430 			if (exp) {
431 				retVal += "E";
432 
433 				if (finishIndex + 1 == format.length()) {
434 					retVal += "#";
435 				} else {
436 					if (format.charAt(finishIndex + 1) == '+') {
437 						retVal += "+";
438 						finishIndex++;
439 					}
440 
441 					finishIndex++;
442 
443 					int num = 0;
444 
445 					while (finishIndex + num < format.length()
446 					    && Character.isDigit(format.charAt(finishIndex + num))) {
447 						num++;
448 					}
449 
450 					int val = Integer.parseInt(format.substring(finishIndex,
451 						        finishIndex + num));
452 
453 					for (int i = 0; i < val; i++) {
454 						retVal += "#";
455 					}
456 				}
457 			}
458 		}
459 
460 		return retVal;
461 	}
462 
463 	/**
464 	 * Sets the maximum allowed number of digits to represent the value.
465 	 *
466 	 * @param i new maximum number of digits
467 	 */
468 	public void setMaximumDigits(int i)
469 	{
470 		maximumDigits = i;
471 	}
472 
473 	/**
474 	 * Returns the maximum allowed number of digits to represent the value.
475 	 *
476 	 * @return number of allowed digits.
477 	 */
478 	public int getMaximumDigits()
479 	{
480 		return maximumDigits;
481 	}
482 
483 	/**
484 	 * Sets the unit.
485 	 *
486 	 * @param unit new units
487 	 */
488 	public void setUnit(String unit)
489 	{
490 		this.unit = unit;
491 	}
492 
493 	/**
494 	 * Returns the unit.
495 	 *
496 	 * @return String
497 	 */
498 	public String getUnit()
499 	{
500 		return unit;
501 	}
502 
503 	/*
504 	 * A format string generator used when no format string is set
505 	 * to the wheelswitch. The format string is determined from value bounds
506 	 * so that all possible values in range can be displayed with the same number
507 	 * and position of digits.
508 	 */
509 	protected String generateFormat()
510 	{
511 		if (generatedFormatString == null) {
512 			if (maximum == Double.POSITIVE_INFINITY
513 			    || minimum == Double.NEGATIVE_INFINITY) {
514 				generatedFormatString = "+#.##E+###";
515 			} else {
516 				double abs = Math.max(Math.abs(minimum), Math.abs(maximum));
517 				int integer = 0;
518 
519 				while (Math.floor(abs) > 0) {
520 					abs /= 10;
521 					integer++;
522 				}
523 
524 				integer = Math.max(integer, 1);
525 
526 				abs = Math.min(Math.abs(minimum), Math.abs(maximum));
527 
528 				int decimal = 0;
529 
530 				while (Math.floor(abs) == 0 && abs != 0) {
531 					decimal++;
532 					abs *= 10;
533 				}
534 
535 				generatedFormatString = new String();
536 
537 				//fix for default format string with '+'
538 				if (true || minimum < 0) {
539 					generatedFormatString += "+";
540 				}
541 
542 				for (int i = 0; i < integer; i++) {
543 					generatedFormatString += "#";
544 				}
545 
546 				if (decimal > 0) {
547 					generatedFormatString += ".";
548 				}
549 
550 				for (int i = 0; i < decimal; i++) {
551 					generatedFormatString += "#";
552 				}
553 			}
554 		}
555 
556 		return generatedFormatString;
557 	}
558 }
559 
560 /* __oOo__ */