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