001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jfreechart/index.html
008: *
009: * This library is free software; you can redistribute it and/or modify it
010: * under the terms of the GNU Lesser General Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017: * License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022: * USA.
023: *
024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025: * in the United States and other countries.]
026: *
027: * -----------------------
028: * RelativeDateFormat.java
029: * -----------------------
030: * (C) Copyright 2006, 2007, by Object Refinery Limited and Contributors.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: RelativeDateFormat.java,v 1.1.2.3 2007/03/05 13:47:40 mungady Exp $
036: *
037: * Changes:
038: * --------
039: * 01-Nov-2006 : Version 1 (DG);
040: * 23-Nov-2006 : Added argument checks, updated equals(), added clone() and
041: * hashCode() (DG);
042: *
043: */
044: package org.jfree.chart.util;
045:
046: import java.text.DateFormat;
047: import java.text.DecimalFormat;
048: import java.text.FieldPosition;
049: import java.text.NumberFormat;
050: import java.text.ParsePosition;
051: import java.util.Calendar;
052: import java.util.Date;
053: import java.util.GregorianCalendar;
054:
055: /**
056: * A formatter that formats dates to show the elapsed time relative to some
057: * base date.
058: *
059: * @since 1.0.3
060: */
061: public class RelativeDateFormat extends DateFormat {
062:
063: /** The base milliseconds for the elapsed time calculation. */
064: private long baseMillis;
065:
066: /**
067: * A flag that controls whether or not a zero day count is displayed.
068: */
069: private boolean showZeroDays;
070:
071: /**
072: * A formatter for the day count (most likely not critical until the
073: * day count exceeds 999).
074: */
075: private NumberFormat dayFormatter;
076:
077: /**
078: * A string appended after the day count.
079: */
080: private String daySuffix;
081:
082: /**
083: * A string appended after the hours.
084: */
085: private String hourSuffix;
086:
087: /**
088: * A string appended after the minutes.
089: */
090: private String minuteSuffix;
091:
092: /**
093: * A formatter for the seconds (and milliseconds).
094: */
095: private NumberFormat secondFormatter;
096:
097: /**
098: * A string appended after the seconds.
099: */
100: private String secondSuffix;
101:
102: /**
103: * A constant for the number of milliseconds in one hour.
104: */
105: private static long MILLISECONDS_IN_ONE_HOUR = 60 * 60 * 1000L;
106:
107: /**
108: * A constant for the number of milliseconds in one day.
109: */
110: private static long MILLISECONDS_IN_ONE_DAY = 24 * MILLISECONDS_IN_ONE_HOUR;
111:
112: /**
113: * Creates a new instance.
114: */
115: public RelativeDateFormat() {
116: this (0L);
117: }
118:
119: /**
120: * Creates a new instance.
121: *
122: * @param time the date/time (<code>null</code> not permitted).
123: */
124: public RelativeDateFormat(Date time) {
125: this (time.getTime());
126: }
127:
128: /**
129: * Creates a new instance.
130: *
131: * @param baseMillis the time zone (<code>null</code> not permitted).
132: */
133: public RelativeDateFormat(long baseMillis) {
134: super ();
135: this .baseMillis = baseMillis;
136: this .showZeroDays = false;
137: this .dayFormatter = NumberFormat.getInstance();
138: this .daySuffix = "d";
139: this .hourSuffix = "h";
140: this .minuteSuffix = "m";
141: this .secondFormatter = NumberFormat.getNumberInstance();
142: this .secondFormatter.setMaximumFractionDigits(3);
143: this .secondFormatter.setMinimumFractionDigits(3);
144: this .secondSuffix = "s";
145:
146: // we don't use the calendar or numberFormat fields, but equals(Object)
147: // is failing without them being non-null
148: this .calendar = new GregorianCalendar();
149: this .numberFormat = new DecimalFormat("0");
150: }
151:
152: /**
153: * Returns the base date/time used to calculate the elapsed time for
154: * display.
155: *
156: * @return The base date/time in milliseconds since 1-Jan-1970.
157: *
158: * @see #setBaseMillis(long)
159: */
160: public long getBaseMillis() {
161: return this .baseMillis;
162: }
163:
164: /**
165: * Sets the base date/time used to calculate the elapsed time for display.
166: * This should be specified in milliseconds using the same encoding as
167: * <code>java.util.Date</code>.
168: *
169: * @param baseMillis the base date/time in milliseconds.
170: *
171: * @see #getBaseMillis()
172: */
173: public void setBaseMillis(long baseMillis) {
174: this .baseMillis = baseMillis;
175: }
176:
177: /**
178: * Returns the flag that controls whether or not zero day counts are
179: * shown in the formatted output.
180: *
181: * @return The flag.
182: *
183: * @see #setShowZeroDays(boolean)
184: */
185: public boolean getShowZeroDays() {
186: return this .showZeroDays;
187: }
188:
189: /**
190: * Sets the flag that controls whether or not zero day counts are shown
191: * in the formatted output.
192: *
193: * @param show the flag.
194: *
195: * @see #getShowZeroDays()
196: */
197: public void setShowZeroDays(boolean show) {
198: this .showZeroDays = show;
199: }
200:
201: /**
202: * Returns the string that is appended to the day count.
203: *
204: * @return The string.
205: *
206: * @see #setDaySuffix(String)
207: */
208: public String getDaySuffix() {
209: return this .daySuffix;
210: }
211:
212: /**
213: * Sets the string that is appended to the day count.
214: *
215: * @param suffix the suffix (<code>null</code> not permitted).
216: *
217: * @see #getDaySuffix()
218: */
219: public void setDaySuffix(String suffix) {
220: if (suffix == null) {
221: throw new IllegalArgumentException(
222: "Null 'suffix' argument.");
223: }
224: this .daySuffix = suffix;
225: }
226:
227: /**
228: * Returns the string that is appended to the hour count.
229: *
230: * @return The string.
231: *
232: * @see #setHourSuffix(String)
233: */
234: public String getHourSuffix() {
235: return this .hourSuffix;
236: }
237:
238: /**
239: * Sets the string that is appended to the hour count.
240: *
241: * @param suffix the suffix (<code>null</code> not permitted).
242: *
243: * @see #getHourSuffix()
244: */
245: public void setHourSuffix(String suffix) {
246: if (suffix == null) {
247: throw new IllegalArgumentException(
248: "Null 'suffix' argument.");
249: }
250: this .hourSuffix = suffix;
251: }
252:
253: /**
254: * Returns the string that is appended to the minute count.
255: *
256: * @return The string.
257: *
258: * @see #setMinuteSuffix(String)
259: */
260: public String getMinuteSuffix() {
261: return this .minuteSuffix;
262: }
263:
264: /**
265: * Sets the string that is appended to the minute count.
266: *
267: * @param suffix the suffix (<code>null</code> not permitted).
268: *
269: * @see #getMinuteSuffix()
270: */
271: public void setMinuteSuffix(String suffix) {
272: if (suffix == null) {
273: throw new IllegalArgumentException(
274: "Null 'suffix' argument.");
275: }
276: this .minuteSuffix = suffix;
277: }
278:
279: /**
280: * Returns the string that is appended to the second count.
281: *
282: * @return The string.
283: *
284: * @see #setSecondSuffix(String)
285: */
286: public String getSecondSuffix() {
287: return this .secondSuffix;
288: }
289:
290: /**
291: * Sets the string that is appended to the second count.
292: *
293: * @param suffix the suffix (<code>null</code> not permitted).
294: *
295: * @see #getSecondSuffix()
296: */
297: public void setSecondSuffix(String suffix) {
298: if (suffix == null) {
299: throw new IllegalArgumentException(
300: "Null 'suffix' argument.");
301: }
302: this .secondSuffix = suffix;
303: }
304:
305: /**
306: * Sets the formatter for the seconds and milliseconds.
307: *
308: * @param formatter the formatter (<code>null</code> not permitted).
309: */
310: public void setSecondFormatter(NumberFormat formatter) {
311: if (formatter == null) {
312: throw new IllegalArgumentException(
313: "Null 'formatter' argument.");
314: }
315: this .secondFormatter = formatter;
316: }
317:
318: /**
319: * Formats the given date as the amount of elapsed time (relative to the
320: * base date specified in the constructor).
321: *
322: * @param date the date.
323: * @param toAppendTo the string buffer.
324: * @param fieldPosition the field position.
325: *
326: * @return The formatted date.
327: */
328: public StringBuffer format(Date date, StringBuffer toAppendTo,
329: FieldPosition fieldPosition) {
330: long currentMillis = date.getTime();
331: long elapsed = currentMillis - this .baseMillis;
332:
333: long days = elapsed / MILLISECONDS_IN_ONE_DAY;
334: elapsed = elapsed - (days * MILLISECONDS_IN_ONE_DAY);
335: long hours = elapsed / MILLISECONDS_IN_ONE_HOUR;
336: elapsed = elapsed - (hours * MILLISECONDS_IN_ONE_HOUR);
337: long minutes = elapsed / 60000L;
338: elapsed = elapsed - (minutes * 60000L);
339: double seconds = elapsed / 1000.0;
340: if (days != 0 || this .showZeroDays) {
341: toAppendTo.append(this .dayFormatter.format(days)
342: + getDaySuffix());
343: }
344: toAppendTo.append(String.valueOf(hours) + getHourSuffix());
345: toAppendTo.append(String.valueOf(minutes) + getMinuteSuffix());
346: toAppendTo.append(this .secondFormatter.format(seconds)
347: + getSecondSuffix());
348: return toAppendTo;
349: }
350:
351: /**
352: * Parses the given string (not implemented).
353: *
354: * @param source the date string.
355: * @param pos the parse position.
356: *
357: * @return <code>null</code>, as this method has not been implemented.
358: */
359: public Date parse(String source, ParsePosition pos) {
360: return null;
361: }
362:
363: /**
364: * Tests this formatter for equality with an arbitrary object.
365: *
366: * @param obj the object (<code>null</code> permitted).
367: *
368: * @return A boolean.
369: */
370: public boolean equals(Object obj) {
371: if (obj == this ) {
372: return true;
373: }
374: if (!(obj instanceof RelativeDateFormat)) {
375: return false;
376: }
377: if (!super .equals(obj)) {
378: return false;
379: }
380: RelativeDateFormat that = (RelativeDateFormat) obj;
381: if (this .baseMillis != that.baseMillis) {
382: return false;
383: }
384: if (this .showZeroDays != that.showZeroDays) {
385: return false;
386: }
387: if (!this .daySuffix.equals(that.daySuffix)) {
388: return false;
389: }
390: if (!this .hourSuffix.equals(that.hourSuffix)) {
391: return false;
392: }
393: if (!this .minuteSuffix.equals(that.minuteSuffix)) {
394: return false;
395: }
396: if (!this .secondSuffix.equals(that.secondSuffix)) {
397: return false;
398: }
399: if (!this .secondFormatter.equals(that.secondFormatter)) {
400: return false;
401: }
402: return true;
403: }
404:
405: /**
406: * Returns a hash code for this instance.
407: *
408: * @return A hash code.
409: */
410: public int hashCode() {
411: int result = 193;
412: result = 37 * result
413: + (int) (this .baseMillis ^ (this .baseMillis >>> 32));
414: result = 37 * result + this .daySuffix.hashCode();
415: result = 37 * result + this .hourSuffix.hashCode();
416: result = 37 * result + this .minuteSuffix.hashCode();
417: result = 37 * result + this .secondSuffix.hashCode();
418: result = 37 * result + this .secondFormatter.hashCode();
419: return result;
420: }
421:
422: /**
423: * Returns a clone of this instance.
424: *
425: * @return A clone.
426: */
427: public Object clone() {
428: RelativeDateFormat clone = (RelativeDateFormat) super .clone();
429: clone.dayFormatter = (NumberFormat) this .dayFormatter.clone();
430: clone.secondFormatter = (NumberFormat) this .secondFormatter
431: .clone();
432: return clone;
433: }
434:
435: /**
436: * Some test code.
437: *
438: * @param args ignored.
439: */
440: public static void main(String[] args) {
441: GregorianCalendar c0 = new GregorianCalendar(2006, 10, 1, 0, 0,
442: 0);
443: GregorianCalendar c1 = new GregorianCalendar(2006, 10, 1, 11,
444: 37, 43);
445: c1.set(Calendar.MILLISECOND, 123);
446:
447: System.out.println("Default: ");
448: RelativeDateFormat rdf = new RelativeDateFormat(c0
449: .getTimeInMillis());
450: System.out.println(rdf.format(c1.getTime()));
451: System.out.println();
452:
453: System.out.println("Hide milliseconds: ");
454: rdf.setSecondFormatter(new DecimalFormat("0"));
455: System.out.println(rdf.format(c1.getTime()));
456: System.out.println();
457:
458: System.out.println("Show zero day output: ");
459: rdf.setShowZeroDays(true);
460: System.out.println(rdf.format(c1.getTime()));
461: System.out.println();
462:
463: System.out.println("Alternative suffixes: ");
464: rdf.setShowZeroDays(false);
465: rdf.setDaySuffix(":");
466: rdf.setHourSuffix(":");
467: rdf.setMinuteSuffix(":");
468: rdf.setSecondSuffix("");
469: System.out.println(rdf.format(c1.getTime()));
470: System.out.println();
471: }
472: }
|