/* @VaadinAddonLicenseForJavaFiles@ */ package com.spaceapplications.vaadin.addon.eventtimeline.gwt.client; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; /** * Client-side cache for received events, based on original version from * vaadin-timeline. * * @author Thomas Neidhart / Space Applications Services NV/SA */ public class VClientCache { /** * A range of events sorted by time */ public static class DataRange implements Comparable<DataRange> { private final Long from; private final Long to; private final List<VEvent> events; /** * Constructor * * @param from * Date that range starts * @param to * Date the range ends * @param events * The events in the range */ public DataRange(Date from, Date to, List<VEvent> events) { this.from = from.getTime(); this.to = to.getTime(); // Add events this.events = events; } /** * Checks if a date is in the range * * @param date * The date to check * @return Returns true if date is in range, else false */ public boolean inRange(Date date) { Long time = date.getTime(); return time >= from && time <= to; } /** * Gets the events in the range * * @return Returns the events as a list of TimelineEvents */ public List<VEvent> getEvents() { return events; } /** * Gets the starting time of the range * * @return The starting time as Long */ public Long getStartTime() { return from; } /** * Gets the ending time of the range * * @return The ending time */ public Long getEndTime() { return to; } /* * (non-Javadoc) * * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(DataRange o) { Date start = new Date(o.getStartTime()); Date end = new Date(o.getEndTime()); if (inRange(start) || inRange(end)) { return 0; } if (o.getStartTime() > to) { return -1; } return 1; } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof DataRange) { DataRange r = (DataRange) o; Date start = new Date(r.getStartTime()); Date end = new Date(r.getEndTime()); if (inRange(start) || inRange(end)) { return true; } } return false; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return from.hashCode() + to.hashCode(); } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { DateTimeFormat formatter = DateTimeFormat.getFormat(PredefinedFormat.DATE_TIME_SHORT); Date f = new Date(from); Date t = new Date(to); return "Datarange: " + formatter.format(f) + " - " + formatter.format(t) + ", events=" + events.size(); } } // Band - Data range map private final Map<Integer, List<DataRange>> dataCache; // The main widget private final VEventTimelineWidget widget; public VClientCache(VEventTimelineWidget widget) { dataCache = new HashMap<Integer, List<DataRange>>(); this.widget = widget; } /** * Get a date range by start and end dates. Note: A range with different * start and end dates might be returned! * * @param band * The band of the range * @param from * The start date of the range * @param to * The end date of the range * @return A date range */ private DataRange getRange(Integer band, Date from, Date to) { List<DataRange> ranges = dataCache.get(band); if (ranges == null) { return null; } for (DataRange r : ranges) { if (r.inRange(from) || r.inRange(to)) { return r; } } return null; } /** * Merge two overlapping date ranges into one * * @param band * The band of the ranges * @param r1 * The first date range * @param r2 * The second date range */ private void merge(Integer band, DataRange r1, DataRange r2) { List<DataRange> ranges = dataCache.get(band); if (ranges == null) { return; } // Remove both ranges from the cache if (ranges.contains(r1)) { ranges.remove(r1); } if (ranges.contains(r2)) { ranges.remove(r2); } // Calculate new from and to times Date from = r1.getStartTime() > r2.getStartTime() ? new Date(r2.getStartTime()) : new Date(r1.getStartTime()); Date to = r1.getEndTime() > r2.getEndTime() ? new Date(r1.getEndTime()) : new Date(r2.getEndTime()); // Add events Set<VEvent> events = new HashSet<VEvent>(); if (r1.getEvents() != null) { events.addAll(r1.getEvents()); } if (r2.getEvents() != null) { events.addAll(r2.getEvents()); } // Create a new Data range List<VEvent> mergedEvents = new ArrayList<VEvent>(events); Collections.sort(mergedEvents); DataRange range = new DataRange(from, to, mergedEvents); ranges.add(range); } /** * Add data points to cache * * @param band * The band id * @param from * THe from date * @param to * The to date * @param events * THe events */ public void addToCache(Integer band, Date from, Date to, List<VEvent> events) { List<DataRange> ranges = dataCache.get(band); if (ranges == null) { ranges = new LinkedList<DataRange>(); dataCache.put(band, ranges); } // Check if we have an mergeable data range DataRange range = getRange(band, from, to); // No existing range was found, so creating a new one if (range == null) { range = new DataRange(from, to, events); ranges.add(range); } // Else merge the ranges else { merge(band, range, new DataRange(from, to, events)); } } /** * Removes all events for the given band id from the cache. * * @param band * The band id */ public void removeFromCache(Integer band) { dataCache.remove(band); } /** * Get events from cache * * @param band * The band index number * @param from * The start date * @param to * The end date * @return Returns a list of events. If the range was not found in cache * null is returned. */ public List<VEvent> getFromCache(Integer band, Date from, Date to) { if (band == null || from == null || to == null) { return null; } List<DataRange> ranges = dataCache.get(band); if (ranges == null) { return null; } for (DataRange dr : ranges) { if ((dr.inRange(from) && dr.inRange(to)) || (dr.inRange(from) && to.getTime() > widget.getEndDate().getTime() && dr .getEndTime() == widget.getEndDate().getTime()) || (dr.inRange(to) && from.getTime() < widget.getStartDate().getTime() && dr .getStartTime() == widget.getStartDate().getTime())) { List<VEvent> events = new ArrayList<VEvent>(); // Get the events if (dr.getEvents() != null) { for (VEvent event : dr.getEvents()) { Long time = event.getStartTime(); if (time >= dr.getStartTime() && time <= dr.getEndTime()) { events.add(event); } } } return events; } } return null; } }