1:
120:
121: package ;
122:
123: import ;
124: import ;
125: import ;
126: import ;
127: import ;
128: import ;
129: import ;
130: import ;
131: import ;
132: import ;
133: import ;
134: import ;
135: import ;
136:
137: import ;
138: import ;
139: import ;
140: import ;
141: import ;
142: import ;
143: import ;
144: import ;
145: import ;
146: import ;
147: import ;
148: import ;
149: import ;
150:
151:
164: public class DateAxis extends ValueAxis implements Cloneable, Serializable {
165:
166:
167: private static final long serialVersionUID = -1013460999649007604L;
168:
169:
170: public static final DateRange DEFAULT_DATE_RANGE = new DateRange();
171:
172:
173: public static final double
174: DEFAULT_AUTO_RANGE_MINIMUM_SIZE_IN_MILLISECONDS = 2.0;
175:
176:
177: public static final DateTickUnit DEFAULT_DATE_TICK_UNIT
178: = new DateTickUnit(DateTickUnit.DAY, 1, new SimpleDateFormat());
179:
180:
181: public static final Date DEFAULT_ANCHOR_DATE = new Date();
182:
183:
184: private DateTickUnit tickUnit;
185:
186:
187: private DateFormat dateFormatOverride;
188:
189:
193: private DateTickMarkPosition tickMarkPosition = DateTickMarkPosition.START;
194:
195:
199: private static class DefaultTimeline implements Timeline, Serializable {
200:
201:
208: public long toTimelineValue(long millisecond) {
209: return millisecond;
210: }
211:
212:
219: public long toTimelineValue(Date date) {
220: return date.getTime();
221: }
222:
223:
231: public long toMillisecond(long value) {
232: return value;
233: }
234:
235:
243: public boolean containsDomainValue(long millisecond) {
244: return true;
245: }
246:
247:
255: public boolean containsDomainValue(Date date) {
256: return true;
257: }
258:
259:
268: public boolean containsDomainRange(long from, long to) {
269: return true;
270: }
271:
272:
281: public boolean containsDomainRange(Date from, Date to) {
282: return true;
283: }
284:
285:
292: public boolean equals(Object object) {
293: if (object == null) {
294: return false;
295: }
296: if (object == this) {
297: return true;
298: }
299: if (object instanceof DefaultTimeline) {
300: return true;
301: }
302: return false;
303: }
304: }
305:
306:
307: private static final Timeline DEFAULT_TIMELINE = new DefaultTimeline();
308:
309:
310: private TimeZone timeZone;
311:
312:
313: private Timeline timeline;
314:
315:
318: public DateAxis() {
319: this(null);
320: }
321:
322:
327: public DateAxis(String label) {
328: this(label, TimeZone.getDefault());
329: }
330:
331:
341: public DateAxis(String label, TimeZone zone) {
342: super(label, DateAxis.createStandardDateTickUnits(zone));
343: setTickUnit(DateAxis.DEFAULT_DATE_TICK_UNIT, false, false);
344: setAutoRangeMinimumSize(
345: DEFAULT_AUTO_RANGE_MINIMUM_SIZE_IN_MILLISECONDS);
346: setRange(DEFAULT_DATE_RANGE, false, false);
347: this.dateFormatOverride = null;
348: this.timeZone = zone;
349: this.timeline = DEFAULT_TIMELINE;
350: }
351:
352:
360: public TimeZone getTimeZone() {
361: return this.timeZone;
362: }
363:
364:
373: public void setTimeZone(TimeZone zone) {
374: if (!this.timeZone.equals(zone)) {
375: this.timeZone = zone;
376: setStandardTickUnits(createStandardDateTickUnits(zone));
377: notifyListeners(new AxisChangeEvent(this));
378: }
379: }
380:
381:
386: public Timeline getTimeline() {
387: return this.timeline;
388: }
389:
390:
398: public void setTimeline(Timeline timeline) {
399: if (this.timeline != timeline) {
400: this.timeline = timeline;
401: notifyListeners(new AxisChangeEvent(this));
402: }
403: }
404:
405:
418: public DateTickUnit getTickUnit() {
419: return this.tickUnit;
420: }
421:
422:
432: public void setTickUnit(DateTickUnit unit) {
433: setTickUnit(unit, true, true);
434: }
435:
436:
445: public void setTickUnit(DateTickUnit unit, boolean notify,
446: boolean turnOffAutoSelection) {
447:
448: this.tickUnit = unit;
449: if (turnOffAutoSelection) {
450: setAutoTickUnitSelection(false, false);
451: }
452: if (notify) {
453: notifyListeners(new AxisChangeEvent(this));
454: }
455:
456: }
457:
458:
464: public DateFormat getDateFormatOverride() {
465: return this.dateFormatOverride;
466: }
467:
468:
474: public void setDateFormatOverride(DateFormat formatter) {
475: this.dateFormatOverride = formatter;
476: notifyListeners(new AxisChangeEvent(this));
477: }
478:
479:
486: public void setRange(Range range) {
487: setRange(range, true, true);
488: }
489:
490:
501: public void setRange(Range range, boolean turnOffAutoRange,
502: boolean notify) {
503: if (range == null) {
504: throw new IllegalArgumentException("Null 'range' argument.");
505: }
506:
507:
508: if (!(range instanceof DateRange)) {
509: range = new DateRange(range);
510: }
511: super.setRange(range, turnOffAutoRange, notify);
512: }
513:
514:
521: public void setRange(Date lower, Date upper) {
522: if (lower.getTime() >= upper.getTime()) {
523: throw new IllegalArgumentException("Requires 'lower' < 'upper'.");
524: }
525: setRange(new DateRange(lower, upper));
526: }
527:
528:
535: public void setRange(double lower, double upper) {
536: if (lower >= upper) {
537: throw new IllegalArgumentException("Requires 'lower' < 'upper'.");
538: }
539: setRange(new DateRange(lower, upper));
540: }
541:
542:
550: public Date getMinimumDate() {
551: Date result = null;
552: Range range = getRange();
553: if (range instanceof DateRange) {
554: DateRange r = (DateRange) range;
555: result = r.getLowerDate();
556: }
557: else {
558: result = new Date((long) range.getLowerBound());
559: }
560: return result;
561: }
562:
563:
575: public void setMinimumDate(Date date) {
576: if (date == null) {
577: throw new IllegalArgumentException("Null 'date' argument.");
578: }
579:
580: Date maxDate = getMaximumDate();
581: long maxMillis = maxDate.getTime();
582: long newMinMillis = date.getTime();
583: if (maxMillis <= newMinMillis) {
584: Date oldMin = getMinimumDate();
585: long length = maxMillis - oldMin.getTime();
586: maxDate = new Date(newMinMillis + length);
587: }
588: setRange(new DateRange(date, maxDate), true, false);
589: notifyListeners(new AxisChangeEvent(this));
590: }
591:
592:
600: public Date getMaximumDate() {
601: Date result = null;
602: Range range = getRange();
603: if (range instanceof DateRange) {
604: DateRange r = (DateRange) range;
605: result = r.getUpperDate();
606: }
607: else {
608: result = new Date((long) range.getUpperBound());
609: }
610: return result;
611: }
612:
613:
625: public void setMaximumDate(Date maximumDate) {
626: if (maximumDate == null) {
627: throw new IllegalArgumentException("Null 'maximumDate' argument.");
628: }
629:
630: Date minDate = getMinimumDate();
631: long minMillis = minDate.getTime();
632: long newMaxMillis = maximumDate.getTime();
633: if (minMillis >= newMaxMillis) {
634: Date oldMax = getMaximumDate();
635: long length = oldMax.getTime() - minMillis;
636: minDate = new Date(newMaxMillis - length);
637: }
638: setRange(new DateRange(minDate, maximumDate), true, false);
639: notifyListeners(new AxisChangeEvent(this));
640: }
641:
642:
647: public DateTickMarkPosition getTickMarkPosition() {
648: return this.tickMarkPosition;
649: }
650:
651:
657: public void setTickMarkPosition(DateTickMarkPosition position) {
658: if (position == null) {
659: throw new IllegalArgumentException("Null 'position' argument.");
660: }
661: this.tickMarkPosition = position;
662: notifyListeners(new AxisChangeEvent(this));
663: }
664:
665:
669: public void configure() {
670: if (isAutoRange()) {
671: autoAdjustRange();
672: }
673: }
674:
675:
683: public boolean isHiddenValue(long millis) {
684: return (!this.timeline.containsDomainValue(new Date(millis)));
685: }
686:
687:
698: public double valueToJava2D(double value, Rectangle2D area,
699: RectangleEdge edge) {
700:
701: value = this.timeline.toTimelineValue((long) value);
702:
703: DateRange range = (DateRange) getRange();
704: double axisMin = this.timeline.toTimelineValue(range.getLowerDate());
705: double axisMax = this.timeline.toTimelineValue(range.getUpperDate());
706: double result = 0.0;
707: if (RectangleEdge.isTopOrBottom(edge)) {
708: double minX = area.getX();
709: double maxX = area.getMaxX();
710: if (isInverted()) {
711: result = maxX + ((value - axisMin) / (axisMax - axisMin))
712: * (minX - maxX);
713: }
714: else {
715: result = minX + ((value - axisMin) / (axisMax - axisMin))
716: * (maxX - minX);
717: }
718: }
719: else if (RectangleEdge.isLeftOrRight(edge)) {
720: double minY = area.getMinY();
721: double maxY = area.getMaxY();
722: if (isInverted()) {
723: result = minY + (((value - axisMin) / (axisMax - axisMin))
724: * (maxY - minY));
725: }
726: else {
727: result = maxY - (((value - axisMin) / (axisMax - axisMin))
728: * (maxY - minY));
729: }
730: }
731: return result;
732:
733: }
734:
735:
746: public double dateToJava2D(Date date, Rectangle2D area,
747: RectangleEdge edge) {
748: double value = date.getTime();
749: return valueToJava2D(value, area, edge);
750: }
751:
752:
764: public double java2DToValue(double java2DValue, Rectangle2D area,
765: RectangleEdge edge) {
766:
767: DateRange range = (DateRange) getRange();
768: double axisMin = this.timeline.toTimelineValue(range.getLowerDate());
769: double axisMax = this.timeline.toTimelineValue(range.getUpperDate());
770:
771: double min = 0.0;
772: double max = 0.0;
773: if (RectangleEdge.isTopOrBottom(edge)) {
774: min = area.getX();
775: max = area.getMaxX();
776: }
777: else if (RectangleEdge.isLeftOrRight(edge)) {
778: min = area.getMaxY();
779: max = area.getY();
780: }
781:
782: double result;
783: if (isInverted()) {
784: result = axisMax - ((java2DValue - min) / (max - min)
785: * (axisMax - axisMin));
786: }
787: else {
788: result = axisMin + ((java2DValue - min) / (max - min)
789: * (axisMax - axisMin));
790: }
791:
792: return this.timeline.toMillisecond((long) result);
793: }
794:
795:
802: public Date calculateLowestVisibleTickValue(DateTickUnit unit) {
803: return nextStandardDate(getMinimumDate(), unit);
804: }
805:
806:
813: public Date calculateHighestVisibleTickValue(DateTickUnit unit) {
814: return previousStandardDate(getMaximumDate(), unit);
815: }
816:
817:
825: protected Date previousStandardDate(Date date, DateTickUnit unit) {
826:
827: int milliseconds;
828: int seconds;
829: int minutes;
830: int hours;
831: int days;
832: int months;
833: int years;
834:
835: Calendar calendar = Calendar.getInstance(this.timeZone);
836: calendar.setTime(date);
837: int count = unit.getCount();
838: int current = calendar.get(unit.getCalendarField());
839: int value = count * (current / count);
840:
841: switch (unit.getUnit()) {
842:
843: case (DateTickUnit.MILLISECOND) :
844: years = calendar.get(Calendar.YEAR);
845: months = calendar.get(Calendar.MONTH);
846: days = calendar.get(Calendar.DATE);
847: hours = calendar.get(Calendar.HOUR_OF_DAY);
848: minutes = calendar.get(Calendar.MINUTE);
849: seconds = calendar.get(Calendar.SECOND);
850: calendar.set(years, months, days, hours, minutes, seconds);
851: calendar.set(Calendar.MILLISECOND, value);
852: Date mm = calendar.getTime();
853: if (mm.getTime() >= date.getTime()) {
854: calendar.set(Calendar.MILLISECOND, value - 1);
855: mm = calendar.getTime();
856: }
857: return mm;
858:
859: case (DateTickUnit.SECOND) :
860: years = calendar.get(Calendar.YEAR);
861: months = calendar.get(Calendar.MONTH);
862: days = calendar.get(Calendar.DATE);
863: hours = calendar.get(Calendar.HOUR_OF_DAY);
864: minutes = calendar.get(Calendar.MINUTE);
865: if (this.tickMarkPosition == DateTickMarkPosition.START) {
866: milliseconds = 0;
867: }
868: else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) {
869: milliseconds = 500;
870: }
871: else {
872: milliseconds = 999;
873: }
874: calendar.set(Calendar.MILLISECOND, milliseconds);
875: calendar.set(years, months, days, hours, minutes, value);
876: Date dd = calendar.getTime();
877: if (dd.getTime() >= date.getTime()) {
878: calendar.set(Calendar.SECOND, value - 1);
879: dd = calendar.getTime();
880: }
881: return dd;
882:
883: case (DateTickUnit.MINUTE) :
884: years = calendar.get(Calendar.YEAR);
885: months = calendar.get(Calendar.MONTH);
886: days = calendar.get(Calendar.DATE);
887: hours = calendar.get(Calendar.HOUR_OF_DAY);
888: if (this.tickMarkPosition == DateTickMarkPosition.START) {
889: seconds = 0;
890: }
891: else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) {
892: seconds = 30;
893: }
894: else {
895: seconds = 59;
896: }
897: calendar.clear(Calendar.MILLISECOND);
898: calendar.set(years, months, days, hours, value, seconds);
899: Date d0 = calendar.getTime();
900: if (d0.getTime() >= date.getTime()) {
901: calendar.set(Calendar.MINUTE, value - 1);
902: d0 = calendar.getTime();
903: }
904: return d0;
905:
906: case (DateTickUnit.HOUR) :
907: years = calendar.get(Calendar.YEAR);
908: months = calendar.get(Calendar.MONTH);
909: days = calendar.get(Calendar.DATE);
910: if (this.tickMarkPosition == DateTickMarkPosition.START) {
911: minutes = 0;
912: seconds = 0;
913: }
914: else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) {
915: minutes = 30;
916: seconds = 0;
917: }
918: else {
919: minutes = 59;
920: seconds = 59;
921: }
922: calendar.clear(Calendar.MILLISECOND);
923: calendar.set(years, months, days, value, minutes, seconds);
924: Date d1 = calendar.getTime();
925: if (d1.getTime() >= date.getTime()) {
926: calendar.set(Calendar.HOUR_OF_DAY, value - 1);
927: d1 = calendar.getTime();
928: }
929: return d1;
930:
931: case (DateTickUnit.DAY) :
932: years = calendar.get(Calendar.YEAR);
933: months = calendar.get(Calendar.MONTH);
934: if (this.tickMarkPosition == DateTickMarkPosition.START) {
935: hours = 0;
936: minutes = 0;
937: seconds = 0;
938: }
939: else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) {
940: hours = 12;
941: minutes = 0;
942: seconds = 0;
943: }
944: else {
945: hours = 23;
946: minutes = 59;
947: seconds = 59;
948: }
949: calendar.clear(Calendar.MILLISECOND);
950: calendar.set(years, months, value, hours, 0, 0);
951:
952:
953: Date d2 = calendar.getTime();
954: if (d2.getTime() >= date.getTime()) {
955: calendar.set(Calendar.DATE, value - 1);
956: d2 = calendar.getTime();
957: }
958: return d2;
959:
960: case (DateTickUnit.MONTH) :
961: years = calendar.get(Calendar.YEAR);
962: calendar.clear(Calendar.MILLISECOND);
963: calendar.set(years, value, 1, 0, 0, 0);
964: Month month = new Month(calendar.getTime(), this.timeZone);
965: Date standardDate = calculateDateForPosition(
966: month, this.tickMarkPosition);
967: long millis = standardDate.getTime();
968: if (millis >= date.getTime()) {
969: month = (Month) month.previous();
970: standardDate = calculateDateForPosition(
971: month, this.tickMarkPosition);
972: }
973: return standardDate;
974:
975: case(DateTickUnit.YEAR) :
976: if (this.tickMarkPosition == DateTickMarkPosition.START) {
977: months = 0;
978: days = 1;
979: }
980: else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) {
981: months = 6;
982: days = 1;
983: }
984: else {
985: months = 11;
986: days = 31;
987: }
988: calendar.clear(Calendar.MILLISECOND);
989: calendar.set(value, months, days, 0, 0, 0);
990: Date d3 = calendar.getTime();
991: if (d3.getTime() >= date.getTime()) {
992: calendar.set(Calendar.YEAR, value - 1);
993: d3 = calendar.getTime();
994: }
995: return d3;
996:
997: default: return null;
998:
999: }
1000:
1001: }
1002:
1003:
1012: private Date calculateDateForPosition(RegularTimePeriod period,
1013: DateTickMarkPosition position) {
1014:
1015: if (position == null) {
1016: throw new IllegalArgumentException("Null 'position' argument.");
1017: }
1018: Date result = null;
1019: if (position == DateTickMarkPosition.START) {
1020: result = new Date(period.getFirstMillisecond());
1021: }
1022: else if (position == DateTickMarkPosition.MIDDLE) {
1023: result = new Date(period.getMiddleMillisecond());
1024: }
1025: else if (position == DateTickMarkPosition.END) {
1026: result = new Date(period.getLastMillisecond());
1027: }
1028: return result;
1029:
1030: }
1031:
1032:
1041: protected Date nextStandardDate(Date date, DateTickUnit unit) {
1042: Date previous = previousStandardDate(date, unit);
1043: Calendar calendar = Calendar.getInstance(this.timeZone);
1044: calendar.setTime(previous);
1045: calendar.add(unit.getCalendarField(), unit.getCount());
1046: return calendar.getTime();
1047: }
1048:
1049:
1058: public static TickUnitSource createStandardDateTickUnits() {
1059: return createStandardDateTickUnits(TimeZone.getDefault());
1060: }
1061:
1062:
1073: public static TickUnitSource createStandardDateTickUnits(TimeZone zone) {
1074:
1075: if (zone == null) {
1076: throw new IllegalArgumentException("Null 'zone' argument.");
1077: }
1078: TickUnits units = new TickUnits();
1079:
1080:
1081: DateFormat f1 = new SimpleDateFormat("HH:mm:ss.SSS");
1082: DateFormat f2 = new SimpleDateFormat("HH:mm:ss");
1083: DateFormat f3 = new SimpleDateFormat("HH:mm");
1084: DateFormat f4 = new SimpleDateFormat("d-MMM, HH:mm");
1085: DateFormat f5 = new SimpleDateFormat("d-MMM");
1086: DateFormat f6 = new SimpleDateFormat("MMM-yyyy");
1087: DateFormat f7 = new SimpleDateFormat("yyyy");
1088:
1089: f1.setTimeZone(zone);
1090: f2.setTimeZone(zone);
1091: f3.setTimeZone(zone);
1092: f4.setTimeZone(zone);
1093: f5.setTimeZone(zone);
1094: f6.setTimeZone(zone);
1095: f7.setTimeZone(zone);
1096:
1097:
1098: units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 1, f1));
1099: units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 5,
1100: DateTickUnit.MILLISECOND, 1, f1));
1101: units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 10,
1102: DateTickUnit.MILLISECOND, 1, f1));
1103: units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 25,
1104: DateTickUnit.MILLISECOND, 5, f1));
1105: units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 50,
1106: DateTickUnit.MILLISECOND, 10, f1));
1107: units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 100,
1108: DateTickUnit.MILLISECOND, 10, f1));
1109: units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 250,
1110: DateTickUnit.MILLISECOND, 10, f1));
1111: units.add(new DateTickUnit(DateTickUnit.MILLISECOND, 500,
1112: DateTickUnit.MILLISECOND, 50, f1));
1113:
1114:
1115: units.add(new DateTickUnit(DateTickUnit.SECOND, 1,
1116: DateTickUnit.MILLISECOND, 50, f2));
1117: units.add(new DateTickUnit(DateTickUnit.SECOND, 5,
1118: DateTickUnit.SECOND, 1, f2));
1119: units.add(new DateTickUnit(DateTickUnit.SECOND, 10,
1120: DateTickUnit.SECOND, 1, f2));
1121: units.add(new DateTickUnit(DateTickUnit.SECOND, 30,
1122: DateTickUnit.SECOND, 5, f2));
1123:
1124:
1125: units.add(new DateTickUnit(DateTickUnit.MINUTE, 1,
1126: DateTickUnit.SECOND, 5, f3));
1127: units.add(new DateTickUnit(DateTickUnit.MINUTE, 2,
1128: DateTickUnit.SECOND, 10, f3));
1129: units.add(new DateTickUnit(DateTickUnit.MINUTE, 5,
1130: DateTickUnit.MINUTE, 1, f3));
1131: units.add(new DateTickUnit(DateTickUnit.MINUTE, 10,
1132: DateTickUnit.MINUTE, 1, f3));
1133: units.add(new DateTickUnit(DateTickUnit.MINUTE, 15,
1134: DateTickUnit.MINUTE, 5, f3));
1135: units.add(new DateTickUnit(DateTickUnit.MINUTE, 20,
1136: DateTickUnit.MINUTE, 5, f3));
1137: units.add(new DateTickUnit(DateTickUnit.MINUTE, 30,
1138: DateTickUnit.MINUTE, 5, f3));
1139:
1140:
1141: units.add(new DateTickUnit(DateTickUnit.HOUR, 1,
1142: DateTickUnit.MINUTE, 5, f3));
1143: units.add(new DateTickUnit(DateTickUnit.HOUR, 2,
1144: DateTickUnit.MINUTE, 10, f3));
1145: units.add(new DateTickUnit(DateTickUnit.HOUR, 4,
1146: DateTickUnit.MINUTE, 30, f3));
1147: units.add(new DateTickUnit(DateTickUnit.HOUR, 6,
1148: DateTickUnit.HOUR, 1, f3));
1149: units.add(new DateTickUnit(DateTickUnit.HOUR, 12,
1150: DateTickUnit.HOUR, 1, f4));
1151:
1152:
1153: units.add(new DateTickUnit(DateTickUnit.DAY, 1,
1154: DateTickUnit.HOUR, 1, f5));
1155: units.add(new DateTickUnit(DateTickUnit.DAY, 2,
1156: DateTickUnit.HOUR, 1, f5));
1157: units.add(new DateTickUnit(DateTickUnit.DAY, 7,
1158: DateTickUnit.DAY, 1, f5));
1159: units.add(new DateTickUnit(DateTickUnit.DAY, 15,
1160: DateTickUnit.DAY, 1, f5));
1161:
1162:
1163: units.add(new DateTickUnit(DateTickUnit.MONTH, 1,
1164: DateTickUnit.DAY, 1, f6));
1165: units.add(new DateTickUnit(DateTickUnit.MONTH, 2,
1166: DateTickUnit.DAY, 1, f6));
1167: units.add(new DateTickUnit(DateTickUnit.MONTH, 3,
1168: DateTickUnit.MONTH, 1, f6));
1169: units.add(new DateTickUnit(DateTickUnit.MONTH, 4,
1170: DateTickUnit.MONTH, 1, f6));
1171: units.add(new DateTickUnit(DateTickUnit.MONTH, 6,
1172: DateTickUnit.MONTH, 1, f6));
1173:
1174:
1175: units.add(new DateTickUnit(DateTickUnit.YEAR, 1,
1176: DateTickUnit.MONTH, 1, f7));
1177: units.add(new DateTickUnit(DateTickUnit.YEAR, 2,
1178: DateTickUnit.MONTH, 3, f7));
1179: units.add(new DateTickUnit(DateTickUnit.YEAR, 5,
1180: DateTickUnit.YEAR, 1, f7));
1181: units.add(new DateTickUnit(DateTickUnit.YEAR, 10,
1182: DateTickUnit.YEAR, 1, f7));
1183: units.add(new DateTickUnit(DateTickUnit.YEAR, 25,
1184: DateTickUnit.YEAR, 5, f7));
1185: units.add(new DateTickUnit(DateTickUnit.YEAR, 50,
1186: DateTickUnit.YEAR, 10, f7));
1187: units.add(new DateTickUnit(DateTickUnit.YEAR, 100,
1188: DateTickUnit.YEAR, 20, f7));
1189:
1190: return units;
1191:
1192: }
1193:
1194:
1197: protected void autoAdjustRange() {
1198:
1199: Plot plot = getPlot();
1200:
1201: if (plot == null) {
1202: return;
1203: }
1204:
1205: if (plot instanceof ValueAxisPlot) {
1206: ValueAxisPlot vap = (ValueAxisPlot) plot;
1207:
1208: Range r = vap.getDataRange(this);
1209: if (r == null) {
1210: if (this.timeline instanceof SegmentedTimeline) {
1211:
1212: r = new DateRange((
1213: (SegmentedTimeline) this.timeline).getStartTime(),
1214: ((SegmentedTimeline) this.timeline).getStartTime()
1215: + 1);
1216: }
1217: else {
1218: r = new DateRange();
1219: }
1220: }
1221:
1222: long upper = this.timeline.toTimelineValue(
1223: (long) r.getUpperBound());
1224: long lower;
1225: long fixedAutoRange = (long) getFixedAutoRange();
1226: if (fixedAutoRange > 0.0) {
1227: lower = upper - fixedAutoRange;
1228: }
1229: else {
1230: lower = this.timeline.toTimelineValue((long) r.getLowerBound());
1231: double range = upper - lower;
1232: long minRange = (long) getAutoRangeMinimumSize();
1233: if (range < minRange) {
1234: long expand = (long) (minRange - range) / 2;
1235: upper = upper + expand;
1236: lower = lower - expand;
1237: }
1238: upper = upper + (long) (range * getUpperMargin());
1239: lower = lower - (long) (range * getLowerMargin());
1240: }
1241:
1242: upper = this.timeline.toMillisecond(upper);
1243: lower = this.timeline.toMillisecond(lower);
1244: DateRange dr = new DateRange(new Date(lower), new Date(upper));
1245: setRange(dr, false, false);
1246: }
1247:
1248: }
1249:
1250:
1259: protected void selectAutoTickUnit(Graphics2D g2,
1260: Rectangle2D dataArea,
1261: RectangleEdge edge) {
1262:
1263: if (RectangleEdge.isTopOrBottom(edge)) {
1264: selectHorizontalAutoTickUnit(g2, dataArea, edge);
1265: }
1266: else if (RectangleEdge.isLeftOrRight(edge)) {
1267: selectVerticalAutoTickUnit(g2, dataArea, edge);
1268: }
1269:
1270: }
1271:
1272:
1281: protected void selectHorizontalAutoTickUnit(Graphics2D g2,
1282: Rectangle2D dataArea,
1283: RectangleEdge edge) {
1284:
1285: long shift = 0;
1286: if (this.timeline instanceof SegmentedTimeline) {
1287: shift = ((SegmentedTimeline) this.timeline).getStartTime();
1288: }
1289: double zero = valueToJava2D(shift + 0.0, dataArea, edge);
1290: double tickLabelWidth
1291: = estimateMaximumTickLabelWidth(g2, getTickUnit());
1292:
1293:
1294: TickUnitSource tickUnits = getStandardTickUnits();
1295: TickUnit unit1 = tickUnits.getCeilingTickUnit(getTickUnit());
1296: double x1 = valueToJava2D(shift + unit1.getSize(), dataArea, edge);
1297: double unit1Width = Math.abs(x1 - zero);
1298:
1299:
1300: double guess = (tickLabelWidth / unit1Width) * unit1.getSize();
1301: DateTickUnit unit2 = (DateTickUnit) tickUnits.getCeilingTickUnit(guess);
1302: double x2 = valueToJava2D(shift + unit2.getSize(), dataArea, edge);
1303: double unit2Width = Math.abs(x2 - zero);
1304: tickLabelWidth = estimateMaximumTickLabelWidth(g2, unit2);
1305: if (tickLabelWidth > unit2Width) {
1306: unit2 = (DateTickUnit) tickUnits.getLargerTickUnit(unit2);
1307: }
1308: setTickUnit(unit2, false, false);
1309: }
1310:
1311:
1320: protected void selectVerticalAutoTickUnit(Graphics2D g2,
1321: Rectangle2D dataArea,
1322: RectangleEdge edge) {
1323:
1324:
1325: TickUnitSource tickUnits = getStandardTickUnits();
1326: double zero = valueToJava2D(0.0, dataArea, edge);
1327:
1328:
1329: double estimate1 = getRange().getLength() / 10.0;
1330: DateTickUnit candidate1
1331: = (DateTickUnit) tickUnits.getCeilingTickUnit(estimate1);
1332: double labelHeight1 = estimateMaximumTickLabelHeight(g2, candidate1);
1333: double y1 = valueToJava2D(candidate1.getSize(), dataArea, edge);
1334: double candidate1UnitHeight = Math.abs(y1 - zero);
1335:
1336:
1337: double estimate2
1338: = (labelHeight1 / candidate1UnitHeight) * candidate1.getSize();
1339: DateTickUnit candidate2
1340: = (DateTickUnit) tickUnits.getCeilingTickUnit(estimate2);
1341: double labelHeight2 = estimateMaximumTickLabelHeight(g2, candidate2);
1342: double y2 = valueToJava2D(candidate2.getSize(), dataArea, edge);
1343: double unit2Height = Math.abs(y2 - zero);
1344:
1345:
1346: DateTickUnit finalUnit;
1347: if (labelHeight2 < unit2Height) {
1348: finalUnit = candidate2;
1349: }
1350: else {
1351: finalUnit = (DateTickUnit) tickUnits.getLargerTickUnit(candidate2);
1352: }
1353: setTickUnit(finalUnit, false, false);
1354:
1355: }
1356:
1357:
1370: private double estimateMaximumTickLabelWidth(Graphics2D g2,
1371: DateTickUnit unit) {
1372:
1373: RectangleInsets tickLabelInsets = getTickLabelInsets();
1374: double result = tickLabelInsets.getLeft() + tickLabelInsets.getRight();
1375:
1376: Font tickLabelFont = getTickLabelFont();
1377: FontRenderContext frc = g2.getFontRenderContext();
1378: LineMetrics lm = tickLabelFont.getLineMetrics("ABCxyz", frc);
1379: if (isVerticalTickLabels()) {
1380:
1381:
1382: result += lm.getHeight();
1383: }
1384: else {
1385:
1386: DateRange range = (DateRange) getRange();
1387: Date lower = range.getLowerDate();
1388: Date upper = range.getUpperDate();
1389: String lowerStr = null;
1390: String upperStr = null;
1391: DateFormat formatter = getDateFormatOverride();
1392: if (formatter != null) {
1393: lowerStr = formatter.format(lower);
1394: upperStr = formatter.format(upper);
1395: }
1396: else {
1397: lowerStr = unit.dateToString(lower);
1398: upperStr = unit.dateToString(upper);
1399: }
1400: FontMetrics fm = g2.getFontMetrics(tickLabelFont);
1401: double w1 = fm.stringWidth(lowerStr);
1402: double w2 = fm.stringWidth(upperStr);
1403: result += Math.max(w1, w2);
1404: }
1405:
1406: return result;
1407:
1408: }
1409:
1410:
1423: private double estimateMaximumTickLabelHeight(Graphics2D g2,
1424: DateTickUnit unit) {
1425:
1426: RectangleInsets tickLabelInsets = getTickLabelInsets();
1427: double result = tickLabelInsets.getTop() + tickLabelInsets.getBottom();
1428:
1429: Font tickLabelFont = getTickLabelFont();
1430: FontRenderContext frc = g2.getFontRenderContext();
1431: LineMetrics lm = tickLabelFont.getLineMetrics("ABCxyz", frc);
1432: if (!isVerticalTickLabels()) {
1433:
1434:
1435: result += lm.getHeight();
1436: }
1437: else {
1438:
1439: DateRange range = (DateRange) getRange();
1440: Date lower = range.getLowerDate();
1441: Date upper = range.getUpperDate();
1442: String lowerStr = null;
1443: String upperStr = null;
1444: DateFormat formatter = getDateFormatOverride();
1445: if (formatter != null) {
1446: lowerStr = formatter.format(lower);
1447: upperStr = formatter.format(upper);
1448: }
1449: else {
1450: lowerStr = unit.dateToString(lower);
1451: upperStr = unit.dateToString(upper);
1452: }
1453: FontMetrics fm = g2.getFontMetrics(tickLabelFont);
1454: double w1 = fm.stringWidth(lowerStr);
1455: double w2 = fm.stringWidth(upperStr);
1456: result += Math.max(w1, w2);
1457: }
1458:
1459: return result;
1460:
1461: }
1462:
1463:
1474: public List refreshTicks(Graphics2D g2,
1475: AxisState state,
1476: Rectangle2D dataArea,
1477: RectangleEdge edge) {
1478:
1479: List result = null;
1480: if (RectangleEdge.isTopOrBottom(edge)) {
1481: result = refreshTicksHorizontal(g2, dataArea, edge);
1482: }
1483: else if (RectangleEdge.isLeftOrRight(edge)) {
1484: result = refreshTicksVertical(g2, dataArea, edge);
1485: }
1486: return result;
1487:
1488: }
1489:
1490:
1499: protected List refreshTicksHorizontal(Graphics2D g2,
1500: Rectangle2D dataArea,
1501: RectangleEdge edge) {
1502:
1503: List result = new java.util.ArrayList();
1504:
1505: Font tickLabelFont = getTickLabelFont();
1506: g2.setFont(tickLabelFont);
1507:
1508: if (isAutoTickUnitSelection()) {
1509: selectAutoTickUnit(g2, dataArea, edge);
1510: }
1511:
1512: DateTickUnit unit = getTickUnit();
1513: Date tickDate = calculateLowestVisibleTickValue(unit);
1514: Date upperDate = getMaximumDate();
1515:
1516: while (tickDate.before(upperDate)) {
1517:
1518: if (!isHiddenValue(tickDate.getTime())) {
1519:
1520: String tickLabel;
1521: DateFormat formatter = getDateFormatOverride();
1522: if (formatter != null) {
1523: tickLabel = formatter.format(tickDate);
1524: }
1525: else {
1526: tickLabel = this.tickUnit.dateToString(tickDate);
1527: }
1528: TextAnchor anchor = null;
1529: TextAnchor rotationAnchor = null;
1530: double angle = 0.0;
1531: if (isVerticalTickLabels()) {
1532: anchor = TextAnchor.CENTER_RIGHT;
1533: rotationAnchor = TextAnchor.CENTER_RIGHT;
1534: if (edge == RectangleEdge.TOP) {
1535: angle = Math.PI / 2.0;
1536: }
1537: else {
1538: angle = -Math.PI / 2.0;
1539: }
1540: }
1541: else {
1542: if (edge == RectangleEdge.TOP) {
1543: anchor = TextAnchor.BOTTOM_CENTER;
1544: rotationAnchor = TextAnchor.BOTTOM_CENTER;
1545: }
1546: else {
1547: anchor = TextAnchor.TOP_CENTER;
1548: rotationAnchor = TextAnchor.TOP_CENTER;
1549: }
1550: }
1551:
1552: Tick tick = new DateTick(tickDate, tickLabel, anchor,
1553: rotationAnchor, angle);
1554: result.add(tick);
1555: tickDate = unit.addToDate(tickDate, this.timeZone);
1556: }
1557: else {
1558: tickDate = unit.rollDate(tickDate, this.timeZone);
1559: continue;
1560: }
1561:
1562:
1563: switch (unit.getUnit()) {
1564:
1565: case (DateTickUnit.MILLISECOND) :
1566: case (DateTickUnit.SECOND) :
1567: case (DateTickUnit.MINUTE) :
1568: case (DateTickUnit.HOUR) :
1569: case (DateTickUnit.DAY) :
1570: break;
1571: case (DateTickUnit.MONTH) :
1572: tickDate = calculateDateForPosition(new Month(tickDate,
1573: this.timeZone), this.tickMarkPosition);
1574: break;
1575: case(DateTickUnit.YEAR) :
1576: tickDate = calculateDateForPosition(new Year(tickDate,
1577: this.timeZone), this.tickMarkPosition);
1578: break;
1579:
1580: default: break;
1581:
1582: }
1583:
1584: }
1585: return result;
1586:
1587: }
1588:
1589:
1598: protected List refreshTicksVertical(Graphics2D g2,
1599: Rectangle2D dataArea,
1600: RectangleEdge edge) {
1601:
1602: List result = new java.util.ArrayList();
1603:
1604: Font tickLabelFont = getTickLabelFont();
1605: g2.setFont(tickLabelFont);
1606:
1607: if (isAutoTickUnitSelection()) {
1608: selectAutoTickUnit(g2, dataArea, edge);
1609: }
1610: DateTickUnit unit = getTickUnit();
1611: Date tickDate = calculateLowestVisibleTickValue(unit);
1612:
1613: Date upperDate = getMaximumDate();
1614: while (tickDate.before(upperDate)) {
1615:
1616: if (!isHiddenValue(tickDate.getTime())) {
1617:
1618: String tickLabel;
1619: DateFormat formatter = getDateFormatOverride();
1620: if (formatter != null) {
1621: tickLabel = formatter.format(tickDate);
1622: }
1623: else {
1624: tickLabel = this.tickUnit.dateToString(tickDate);
1625: }
1626: TextAnchor anchor = null;
1627: TextAnchor rotationAnchor = null;
1628: double angle = 0.0;
1629: if (isVerticalTickLabels()) {
1630: anchor = TextAnchor.BOTTOM_CENTER;
1631: rotationAnchor = TextAnchor.BOTTOM_CENTER;
1632: if (edge == RectangleEdge.LEFT) {
1633: angle = -Math.PI / 2.0;
1634: }
1635: else {
1636: angle = Math.PI / 2.0;
1637: }
1638: }
1639: else {
1640: if (edge == RectangleEdge.LEFT) {
1641: anchor = TextAnchor.CENTER_RIGHT;
1642: rotationAnchor = TextAnchor.CENTER_RIGHT;
1643: }
1644: else {
1645: anchor = TextAnchor.CENTER_LEFT;
1646: rotationAnchor = TextAnchor.CENTER_LEFT;
1647: }
1648: }
1649:
1650: Tick tick = new DateTick(tickDate, tickLabel, anchor,
1651: rotationAnchor, angle);
1652: result.add(tick);
1653: tickDate = unit.addToDate(tickDate, this.timeZone);
1654: }
1655: else {
1656: tickDate = unit.rollDate(tickDate, this.timeZone);
1657: }
1658: }
1659: return result;
1660: }
1661:
1662:
1678: public AxisState draw(Graphics2D g2,
1679: double cursor,
1680: Rectangle2D plotArea,
1681: Rectangle2D dataArea,
1682: RectangleEdge edge,
1683: PlotRenderingInfo plotState) {
1684:
1685:
1686: if (!isVisible()) {
1687: AxisState state = new AxisState(cursor);
1688:
1689:
1690: List ticks = refreshTicks(g2, state, dataArea, edge);
1691: state.setTicks(ticks);
1692: return state;
1693: }
1694:
1695:
1696: AxisState state = drawTickMarksAndLabels(g2, cursor, plotArea,
1697: dataArea, edge);
1698:
1699:
1700:
1701: state = drawLabel(getLabel(), g2, plotArea, dataArea, edge, state);
1702:
1703: return state;
1704:
1705: }
1706:
1707:
1713: public void zoomRange(double lowerPercent, double upperPercent) {
1714: double start = this.timeline.toTimelineValue(
1715: (long) getRange().getLowerBound()
1716: );
1717: double length = (this.timeline.toTimelineValue(
1718: (long) getRange().getUpperBound())
1719: - this.timeline.toTimelineValue(
1720: (long) getRange().getLowerBound()));
1721: Range adjusted = null;
1722: if (isInverted()) {
1723: adjusted = new DateRange(this.timeline.toMillisecond((long) (start
1724: + (length * (1 - upperPercent)))),
1725: this.timeline.toMillisecond((long) (start + (length
1726: * (1 - lowerPercent)))));
1727: }
1728: else {
1729: adjusted = new DateRange(this.timeline.toMillisecond(
1730: (long) (start + length * lowerPercent)),
1731: this.timeline.toMillisecond((long) (start + length
1732: * upperPercent)));
1733: }
1734: setRange(adjusted);
1735: }
1736:
1737:
1744: public boolean equals(Object obj) {
1745: if (obj == this) {
1746: return true;
1747: }
1748: if (!(obj instanceof DateAxis)) {
1749: return false;
1750: }
1751: DateAxis that = (DateAxis) obj;
1752: if (!ObjectUtilities.equal(this.tickUnit, that.tickUnit)) {
1753: return false;
1754: }
1755: if (!ObjectUtilities.equal(this.dateFormatOverride,
1756: that.dateFormatOverride)) {
1757: return false;
1758: }
1759: if (!ObjectUtilities.equal(this.tickMarkPosition,
1760: that.tickMarkPosition)) {
1761: return false;
1762: }
1763: if (!ObjectUtilities.equal(this.timeline, that.timeline)) {
1764: return false;
1765: }
1766: if (!super.equals(obj)) {
1767: return false;
1768: }
1769: return true;
1770: }
1771:
1772:
1777: public int hashCode() {
1778: if (getLabel() != null) {
1779: return getLabel().hashCode();
1780: }
1781: else {
1782: return 0;
1783: }
1784: }
1785:
1786:
1794: public Object clone() throws CloneNotSupportedException {
1795:
1796: DateAxis clone = (DateAxis) super.clone();
1797:
1798:
1799: if (this.dateFormatOverride != null) {
1800: clone.dateFormatOverride
1801: = (DateFormat) this.dateFormatOverride.clone();
1802: }
1803:
1804:
1805: return clone;
1806:
1807: }
1808:
1809: }