27 package org.sleuthkit.autopsy.timeline.ui.detailview;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.List;
32 import javafx.beans.property.ObjectProperty;
33 import javafx.beans.property.ObjectPropertyBase;
34 import javafx.beans.property.ReadOnlyDoubleProperty;
35 import javafx.beans.property.ReadOnlyDoubleWrapper;
36 import javafx.scene.chart.Axis;
37 import org.joda.time.DateTime;
38 import org.joda.time.Interval;
52 final class DateAxis
extends Axis<DateTime> {
54 private ObjectProperty<DateTime> lowerBound =
new ObjectPropertyBase<DateTime>(
new DateTime(0)) {
56 protected void invalidated() {
57 if (!isAutoRanging()) {
64 public Object getBean() {
69 public String getName() {
79 private DateTime maxDate;
86 private DateTime minDate;
88 private RangeDivision rangeDivisionInfo;
90 private final ReadOnlyDoubleWrapper tickSpacing =
new ReadOnlyDoubleWrapper();
92 private final ObjectProperty<DateTime> upperBound =
new ObjectPropertyBase<DateTime>(
new DateTime(1)) {
94 protected void invalidated() {
95 if (!isAutoRanging()) {
102 public Object getBean() {
103 return DateAxis.this;
107 public String getName() {
118 setAutoRanging(
false);
119 setTickLabelsVisible(
false);
121 setTickMarkVisible(
false);
125 public double getDisplayPosition(DateTime date) {
126 final double length = -200 + (getSide().isHorizontal() ? getWidth() : getHeight());
129 double diff = getUpperBound().getMillis() - getLowerBound().getMillis();
133 double range = length - getZeroPosition();
137 double d = (date.getMillis() - getLowerBound().getMillis()) / diff;
140 if (getSide().isVertical()) {
141 return getHeight() - d * range + getZeroPosition();
143 return d * range + getZeroPosition();
154 public DateTime getLowerBound() {
155 return lowerBound.get();
165 public void setLowerBound(DateTime date) {
166 lowerBound.set(date);
176 public DateTime getUpperBound() {
177 return upperBound.get();
187 public void setUpperBound(DateTime date) {
188 upperBound.set(date);
192 public DateTime getValueForDisplay(
double displayPosition) {
193 final double length = - 200 + (getSide().isHorizontal() ? getWidth() : getHeight());
196 double diff = getUpperBound().getMillis() - getLowerBound().getMillis();
200 double range = length - getZeroPosition();
202 if (getSide().isVertical()) {
205 return new DateTime((
long) ((displayPosition - getZeroPosition() - getHeight()) / -range * diff + getLowerBound().getMillis()), TimeLineController.getJodaTimeZone());
209 return new DateTime((
long) ((displayPosition - getZeroPosition()) / range * diff + getLowerBound().getMillis()), TimeLineController.getJodaTimeZone());
214 public double getZeroPosition() {
219 public void invalidateRange(List<DateTime> list) {
220 super.invalidateRange(list);
222 Collections.sort(list);
223 if (list.isEmpty()) {
224 minDate = maxDate =
new DateTime();
225 }
else if (list.size() == 1) {
226 minDate = maxDate = list.get(0);
227 }
else if (list.size() > 1) {
228 minDate = list.get(0);
229 maxDate = list.get(list.size() - 1);
234 public boolean isValueOnAxis(DateTime date) {
235 return date.getMillis() > getLowerBound().getMillis() && date.getMillis() < getUpperBound().getMillis();
239 public double toNumericValue(DateTime date) {
240 return date.getMillis();
244 public DateTime toRealValue(
double v) {
245 return new DateTime((
long) v);
249 protected Interval autoRange(
double length) {
250 if (isAutoRanging()) {
251 return new Interval(minDate, maxDate);
253 if (getLowerBound() == null || getUpperBound() == null) {
261 protected List<DateTime> calculateTickValues(
double length, Object range) {
262 List<DateTime> tickDates =
new ArrayList<>();
266 rangeDivisionInfo = RangeDivision.getRangeDivision((Interval) range, TimeLineController.getJodaTimeZone());
267 final DateTime lowerBound1 = getLowerBound();
268 final DateTime upperBound1 = getUpperBound();
270 if (lowerBound1 == null || upperBound1 == null) {
273 DateTime lower = lowerBound1.withZone(TimeLineController.getJodaTimeZone());
274 DateTime upper = upperBound1.withZone(TimeLineController.getJodaTimeZone());
276 DateTime current = lower;
278 while (current.isBefore(upper)) {
279 tickDates.add(current);
280 current = current.plus(rangeDivisionInfo.getPeriodSize().toUnitPeriod());
284 tickDates.add(upper);
290 if (tickDates.size() > 2) {
291 DateTime secondDate = tickDates.get(1);
292 DateTime thirdDate = tickDates.get(2);
293 DateTime lastDate = tickDates.get(tickDates.size() - 2);
294 DateTime previousLastDate = tickDates.get(tickDates.size() - 3);
297 if (secondDate.getMillis() - lower.getMillis() < (thirdDate.getMillis() - secondDate.getMillis()) / 2) {
298 tickDates.remove(lower);
303 if (upper.getMillis() - lastDate.getMillis() < (lastDate.getMillis() - previousLastDate.getMillis()) / 2) {
304 tickDates.remove(lastDate);
308 if (tickDates.size() >= 2) {
309 tickSpacing.set(getDisplayPosition(tickDates.get(1)) - getDisplayPosition(tickDates.get(0)));
310 }
else if (tickDates.size() >= 4) {
311 tickSpacing.set(getDisplayPosition(tickDates.get(2)) - getDisplayPosition(tickDates.get(1)));
317 protected Interval getRange() {
318 return new Interval(getLowerBound(), getUpperBound());
322 protected String getTickMarkLabel(DateTime date) {
323 return rangeDivisionInfo.getTickFormatter().print(date);
332 protected void setRange(Object range,
boolean animating) {
333 rangeDivisionInfo = RangeDivision.getRangeDivision((Interval) range, TimeLineController.getJodaTimeZone());
334 setLowerBound(
new DateTime(rangeDivisionInfo.getLowerBound(), TimeLineController.getJodaTimeZone()));
335 setUpperBound(
new DateTime(rangeDivisionInfo.getUpperBound(), TimeLineController.getJodaTimeZone()));
338 ReadOnlyDoubleProperty getTickSpacing() {
339 return tickSpacing.getReadOnlyProperty();