Java tutorial
/* @VaadinAddonLicenseForJavaFiles@ */ package com.spaceapplications.vaadin.addon.eventtimeline; import java.io.Serializable; import java.lang.reflect.Method; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import com.spaceapplications.vaadin.addon.eventtimeline.event.TimelineEvent; import com.spaceapplications.vaadin.addon.eventtimeline.event.TimelineEventProvider; import com.spaceapplications.vaadin.addon.eventtimeline.event.TimelineEventProvider.EventSetChange; import com.spaceapplications.vaadin.addon.eventtimeline.event.TimelineEventProvider.EventSetChangeListener; import com.spaceapplications.vaadin.addon.eventtimeline.gwt.client.VEventTimelineWidget; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.ClientWidget; import com.vaadin.ui.ClientWidget.LoadStyle; import com.vaadin.ui.Component; /** * EventTimeline implementation, based on original version from vaadin-timeline. * * @author Thomas Neidhart / Space Applications Services NV/SA * @author Florian Pirchner (add / remove bands) * @author John Ahlroos / IT Mill Oy Ltd 2010 */ @ClientWidget(value = VEventTimelineWidget.class, loadStyle = LoadStyle.EAGER) public class EventTimeline extends AbstractComponent implements EventSetChangeListener { private static final long serialVersionUID = 6595058445231789530L; // The style name private static final String STYLENAME = "v-eventtimeline"; // Event band information private final List<BandInfo> bandInfos = new ArrayList<BandInfo>(); private final List<BandInfo> bandsToBeRemoved = new ArrayList<EventTimeline.BandInfo>(); private final List<BandInfo> bandsToBeAdded = new ArrayList<EventTimeline.BandInfo>(); private int lastBandId = -1; // The number of event bands visible in the event page area private int eventBandPageSize = -1; // Initialization is done private boolean initDone = false; // Should the time limits be sent private boolean sendTimeLimits = false; // Should the captions for the zoom etc. be sent private boolean sendUICaptions = false; // Should the bands information be sent private boolean sendBands = false; // Should a refresh command be sent private boolean sendRefreshRequest = false; // Should the date range be sent. (Maximum and Minimum dates be sent) private boolean sendDateRange = false; // Activate change events private boolean sendChangeEventAvailable = false; // Should the component visibilities be sent private boolean sendComponentVisibilities = false; // Should the zoom levels be sent private boolean sendZoomLevels = false; // Should the graph grid color be sent private boolean sendGridColor = false; // Should the date format info be sent private boolean sendDateFormatInfo = false; // Is the browser bar locked to the selection protected boolean selectionBarLocked = true; // Should a the page size be sent private boolean sendEventBandPageSize = false; // Should the band height be sent private boolean sendBandHeight = false; // The events to send in the next refresh private Date eventsStartTime = null; private Date eventsEndTime = null; private final Map<Integer, List<TimelineEvent>> eventsToSend = new HashMap<Integer, List<TimelineEvent>>(); /** * The zoom levels to send in the next refresh.<br/> * key = Caption of the zoom level<br/> * value = The time in milliseconds of the zoom level<br/> */ private final Map<String, Long> zoomLevels = new LinkedHashMap<String, Long>(); /* * We need to keep count of the date range listeners so we can turn off the feature if not needed */ private int dateRangeListenerCounter = 0; // The minimum date of all the graphs private Date minDate = null; // The maximum data of all the graphs private Date maxDate = null; // Selection minimum date private Date selectedStartDate = null; // Selection maximum date private Date selectedEndDate = null; // Is the browser visible protected boolean browserIsVisible = true; // Is the zoom levels visible protected boolean zoomIsVisible = true; // The caption of the zoom levels protected String zoomLevelCaption = "Zoom"; // The captions of the band paging navigation private PageNavigationCaptions pagingCaption = new PageNavigationCaptions("Pages", "next", "previous"); // Is the date select visible protected boolean dateSelectVisible = true; // Is the date select enabled protected boolean dateSelectEnabled = true; // Is the legend visible protected boolean legendVisible = false; // Is the page navigation visible protected boolean pageNavigationVisible; // Is the band selection enabled protected boolean bandSelectionEnabled; // The graph grid color (as CSS3 style rgba string) protected String gridColor = "rgba(192,192,192,1)"; // The height of a band, -1 indicates automatic sizing protected int bandHeight = -1; // Date formats protected final DateFormatInfo dateFormatInfo = new DateFormatInfo(); /** Date format that will be used in the UIDL for dates. */ protected DateFormat df_date = new SimpleDateFormat("yyyy-MM-dd"); /** Time format that will be used in the UIDL for time. */ protected DateFormat df_time = new SimpleDateFormat("HH:mm:ss"); /** Date format that will be used in the UIDL for both date and time. */ protected DateFormat df_date_time = new SimpleDateFormat("yyyy-MM-dd-HH-mm"); /** * Date range changed event.<br/> * The date range changed event represents a change in the time span. */ public class DateRangeChangedEvent extends Component.Event { private static final long serialVersionUID = -5424380516338748718L; private Date startDate; private Date endDate; /** * Default constructor.<br/> * See {@link Component.Event} for more details. * * @param source * The source of the event */ public DateRangeChangedEvent(Component source) { super(source); } /** * See {@link Component.Event} for more details. * * @param source * The source of the event * @param start * The start date of the range * @param end * The end date of the range */ public DateRangeChangedEvent(Component source, Date start, Date end) { super(source); startDate = start; endDate = end; } /** * Returns the start date of the range * * @return The start date */ public Date getStartDate() { return startDate; } /** * Returns the end date of the range * * @return The end date */ public Date getEndDate() { return endDate; } } /** * Event button click event. This event is sent when a user clicks an event button in the graph. * */ public class EventButtonClickEvent extends Component.Event { private static final long serialVersionUID = 1215106616175652769L; private Object id; /** * See {@link Component.Event} for details. * * @param source * The source of the event */ public EventButtonClickEvent(Component source) { super(source); } /** * See {@link Component.Event} for more details. * * @param source * The source of the event * @param itemIds * The item id:s in the event data source which are related to the event */ public EventButtonClickEvent(Component source, Object itemId) { super(source); id = itemId; } /** * Gets the item id:s in the event data source which are related to the event * * @return The item id:s related to the event */ public Object getItemId() { return id; } } /** * Band selection event. This event is sent when a user clicks a band in the graph. */ public class BandSelectionEvent extends Component.Event { private static final long serialVersionUID = -621449416684203285L; private int id; /** * See {@link Component.Event} for details. * * @param source * The source of the event */ public BandSelectionEvent(Component source) { super(source); } /** * See {@link Component.Event} for more details. * * @param source * The source of the event * @param id * The band id. */ public BandSelectionEvent(Component source, int id) { super(source); this.id = id; } /** * Returns the id of the selected band. * * @return The band id. */ public int getBandId() { return id; } } /** * Page navigation event. This event is sent when a user selected the next / previous page button. */ public class PageNavigationEvent extends Component.Event { private static final long serialVersionUID = -5391363403702750953L; private final int page; /** * * @param source * The source of the event * @param page * If true, then the next band is requested. False otherwise. */ public PageNavigationEvent(Component source, int page) { super(source); this.page = page; } /** * Returns the page number of the bands area. * * @return the page */ public int getPage() { return page; } } /** * Describes the date formats used by the EventTimeline. */ public class DateFormatInfo implements Serializable { private static final long serialVersionUID = -3103432458378549206L; private String dateSelectDisplaySimpleDateFormat = "MMM d, y"; private String dateSelectEditSimpleDateFormat = "dd/MM/yyyy"; private String shortYearFormat = "''yy"; private String longYearFormat = "yyyy"; private String shortMonthFormat = "MMM yyyy"; private String longMonthFormat = "MMMM yyyy"; private String shortDayFormat = "MMM d, yyyy"; private String longDayFormat = "MMMM d, yyyy"; private String shortTimeFormat = "HH:mm"; private String longTimeFormat = "HH:mm:ss"; private static final char DELIM = '|'; /** * Get the date format which is used to display the selected date range in the top right corner. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public String getDateSelectDisplaySimpleDateFormat() { return dateSelectDisplaySimpleDateFormat; } /** * Set the date format used to display the selected date range in the top right corner. * * See {@link SimpleDateFormat} for format details. * * @param format * A format describing how the date should be formatted */ public void setDateSelectDisplaySimpleDateFormat(String format) { if (dateSelectDisplaySimpleDateFormat != null) { this.dateSelectDisplaySimpleDateFormat = format; sendDateFormatInfo = true; if (initDone) { requestRepaint(); } } } /** * Get the date format which is used to edit the selected date range in the top right corner * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public String getDateSelectEditSimpleDateFormat() { return dateSelectEditSimpleDateFormat; } /** * Set the date format used to display the selected date range in the top right corner. * * See {@link SimpleDateFormat} for format details. * * @param format * A format describing how the date should be formatted */ public void setDateSelectEditSimpleDateFormat(String format) { if (dateSelectEditSimpleDateFormat != null) { this.dateSelectEditSimpleDateFormat = format; sendDateFormatInfo = true; if (initDone) { requestRepaint(); } } } /** * Get the date format which is displayed in the horizontal scale when the scale has a * year-resolution and only a little amount of space is available. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public String getShortYearFormat() { return shortYearFormat; } /** * Set the date format which is displayed in the horizontal scale when the scale has a * year-resolution and only a little amount of space is available. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public void setShortYearFormat(String shortYearFormat) { if (shortYearFormat != null) { this.shortYearFormat = shortYearFormat; sendDateFormatInfo = true; if (initDone) { requestRepaint(); } } } /** * Get the date format which is displayed in the horizontal scale when the scale has a * year-resolution. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public String getLongYearFormat() { return longYearFormat; } /** * Get the date format which is displayed in the horizontal scale when the scale has a * year-resolution. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public void setLongYearFormat(String longYearFormat) { if (longYearFormat != null) { this.longYearFormat = longYearFormat; sendDateFormatInfo = true; if (initDone) { requestRepaint(); } } } /** * Get the date format which is displayed in the horizontal scale when the scale has a * month-resolution and only a little amount of space is available. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public String getShortMonthFormat() { return shortMonthFormat; } /** * Set the date format which is displayed in the horizontal scale when the scale has a * month-resolution and only a little amount of space is available. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public void setShortMonthFormat(String shortMonthFormat) { if (shortMonthFormat != null) { this.shortMonthFormat = shortMonthFormat; sendDateFormatInfo = true; if (initDone) { requestRepaint(); } } } /** * Get the date format which is displayed in the horizontal scale when the scale has a * month-resolution. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public String getLongMonthFormat() { return longMonthFormat; } /** * Set the date format which is displayed in the horizontal scale when the scale has a * month-resolution. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public void setLongMonthFormat(String longMonthFormat) { if (longMonthFormat != null) { this.longMonthFormat = longMonthFormat; sendDateFormatInfo = true; if (initDone) { requestRepaint(); } } } /** * Get the date format which is displayed in the horizontal scale when the scale has a * day-resolution and only a little amount of space is available. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public String getShortDayFormat() { return shortDayFormat; } /** * Set the date format which is displayed in the horizontal scale when the scale has a * day-resolution and only a little amount of space is available. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public void setShortDayFormat(String shortDayFormat) { if (shortDayFormat != null) { this.shortDayFormat = shortDayFormat; sendDateFormatInfo = true; if (initDone) { requestRepaint(); } } } /** * Get the date format which is displayed in the horizontal scale when the scale has a * day-resolution. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public String getLongDayFormat() { return longDayFormat; } /** * Set the date format which is displayed in the horizontal scale when the scale has a * day-resolution. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public void setLongDayFormat(String longDayFormat) { if (longDayFormat != null) { this.longDayFormat = longDayFormat; sendDateFormatInfo = true; if (initDone) { requestRepaint(); } } } /** * Get the date format which is displayed in the horizontal scale when the scale is displaying * time and only a little amount of space is available. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public String getShortTimeFormat() { return shortTimeFormat; } /** * Set the date format which is displayed in the horizontal scale when the scale is displaying * time and only a little amount of space is available. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public void setShortTimeFormat(String shortTimeFormat) { if (shortTimeFormat != null) { this.shortTimeFormat = shortTimeFormat; sendDateFormatInfo = true; if (initDone) { requestRepaint(); } } } /** * Get the date format which is displayed in the horizontal scale when the scale is displaying * time. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public String getLongTimeFormat() { return longTimeFormat; } /** * Set the date format which is displayed in the horizontal scale when the scale is displaying * time. * * See {@link SimpleDateFormat} for format details. * * @return A format describing how the date should be formatted */ public void setLongTimeFormat(String longTimeFormat) { if (longTimeFormat != null) { this.longTimeFormat = longTimeFormat; sendDateFormatInfo = true; if (initDone) { requestRepaint(); } } } /** * Serializes the date formats into a string which can be sent to the client. * * @return */ public String serialize() { return dateSelectDisplaySimpleDateFormat + DELIM + dateSelectEditSimpleDateFormat + DELIM + shortYearFormat + DELIM + longYearFormat + DELIM + shortMonthFormat + DELIM + longMonthFormat + DELIM + shortDayFormat + DELIM + longDayFormat + DELIM + shortTimeFormat + DELIM + longTimeFormat; } @Override public String toString() { return serialize(); } } /** * The date range listener interface */ public interface DateRangeListener { public void dateRangeChanged(DateRangeChangedEvent event); } /** * The event click listener interface */ public interface EventClickListener { public void eventClick(EventButtonClickEvent event); } /** * The band selected listener interface */ public interface BandSelectionListener { public void bandSelected(BandSelectionEvent event); } /** * The band paging listener interface */ public interface PageNavigationListener { public void requestNavigation(PageNavigationEvent event); } /* * Event methods */ private static final Method DATERANGE_CHANGED_METHOD; private static final Method EVENT_CLICK_METHOD; private static final Method PAGE_NAVIGATION_METHOD; private static final Method BAND_SELECTION_METHOD; static { try { DATERANGE_CHANGED_METHOD = DateRangeListener.class.getDeclaredMethod("dateRangeChanged", new Class[] { DateRangeChangedEvent.class }); EVENT_CLICK_METHOD = EventClickListener.class.getDeclaredMethod("eventClick", new Class[] { EventButtonClickEvent.class }); PAGE_NAVIGATION_METHOD = PageNavigationListener.class.getDeclaredMethod("requestNavigation", new Class[] { PageNavigationEvent.class }); BAND_SELECTION_METHOD = BandSelectionListener.class.getDeclaredMethod("bandSelected", new Class[] { BandSelectionEvent.class }); } catch (final java.lang.NoSuchMethodException e) { // This should never happen throw new java.lang.RuntimeException("Internal error finding methods in EventTimeline"); } } /** * Default constructor */ public EventTimeline() { setStyleName(STYLENAME); // Default size setWidth("400px"); setHeight("300px"); } /** * Constructor * * @param caption * The caption of the graph */ public EventTimeline(String caption) { this(); setCaption(caption); } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object, java.util.Map) */ @Override public void changeVariables(Object source, @SuppressWarnings("rawtypes") Map variables) { // Initialization data requested from the client side (refresh occurred) if (variables.containsKey("init")) { // also re-send all bands if (!sendBands) { sendBands = true; for (BandInfo info : bandInfos) { if (!bandsToBeAdded.contains(info)) { bandsToBeAdded.add(info); } } } initDataFlags(); } // The client need some events to display if (variables.containsKey(VEventTimelineWidget.ATTR_EVENTS)) { Object[] indexes = (Object[]) variables.get(VEventTimelineWidget.ATTR_EVENTS); Date start = new Date(Long.parseLong(indexes[0].toString())); Date end = new Date(Long.parseLong(indexes[1].toString())); if (eventsStartTime == null) { eventsStartTime = start; eventsEndTime = end; } else { if (start.before(eventsStartTime)) { eventsStartTime = start; } if (end.after(eventsEndTime)) { eventsEndTime = end; } eventsToSend.clear(); } eventsToSend.putAll(getAllEventsMap(eventsStartTime, eventsEndTime)); } // Send the data to the client if (variables.containsKey("send") || variables.containsKey("init")) { requestRepaint(); } // Date range changed event if (variables.containsKey("drce")) { Object[] values = (Object[]) variables.get("drce"); selectedStartDate = new Date(Long.parseLong(values[0].toString())); selectedEndDate = new Date(Long.parseLong(values[1].toString())); fireDateRangeChangedEvent(selectedStartDate, selectedEndDate); } // Event button click event received if (variables.containsKey("ebce")) { String itemId = (String) variables.get("ebce"); fireEventButtonClickEvent(itemId); } // The user requests the page of the band area if (variables.containsKey("bandPage")) { int page = (Integer) variables.get("bandPage"); fireBandPagingEvent(page); } // The user selected a band if (variables.containsKey("bandSel")) { int bandId = (Integer) variables.get("bandSel"); fireBandSelectionEvent(bandId); } } /* * (non-Javadoc) * * @see com.vaadin.ui.AbstractComponent#paintContent(com.vaadin.terminal.PaintTarget ) */ @Override public void paintContent(PaintTarget target) throws PaintException { // Superclass writes any common attributes in the paint target. super.paintContent(target); // Send the selection lock target.addAttribute("lock", selectionBarLocked); // Always send change event available flag target.addAttribute("e1", sendChangeEventAvailable); // Always sending locale final Locale l = getLocale(); if (l != null) { target.addAttribute("locale", l.toString()); } // Add the events if (eventsToSend.size() > 0) { target.startTag(VEventTimelineWidget.ATTR_EVENTS); target.addAttribute(VEventTimelineWidget.ATTR_START, eventsStartTime.getTime()); target.addAttribute(VEventTimelineWidget.ATTR_END, eventsEndTime.getTime()); for (Map.Entry<Integer, List<TimelineEvent>> entry : eventsToSend.entrySet()) { target.startTag(VEventTimelineWidget.ATTR_BAND); target.addAttribute(VEventTimelineWidget.ATTR_BANDID, entry.getKey()); for (TimelineEvent event : entry.getValue()) { target.startTag(VEventTimelineWidget.ATTR_EVENT); paintEvent(event, target); target.endTag(VEventTimelineWidget.ATTR_EVENT); } target.endTag(VEventTimelineWidget.ATTR_BAND); } target.endTag(VEventTimelineWidget.ATTR_EVENTS); eventsToSend.clear(); eventsStartTime = null; eventsEndTime = null; } // Send time limits if (sendTimeLimits && minDate != null && maxDate != null) { target.addAttribute("startDate", minDate.getTime()); target.addAttribute("endDate", maxDate.getTime()); sendTimeLimits = false; } // Send refresh request if (sendRefreshRequest) { target.addAttribute("refresh", true); sendRefreshRequest = false; } // Send new selected date range if (sendDateRange && selectedStartDate != null && selectedEndDate != null) { target.addVariable(this, "selectStart", selectedStartDate.getTime()); target.addVariable(this, "selectEnd", selectedEndDate.getTime()); sendDateRange = false; } // Send the component visibilities if (sendComponentVisibilities) { target.addAttribute("browserVisibility", browserIsVisible); target.addAttribute("zoomVisibility", zoomIsVisible); target.addAttribute("dateSelectVisibility", dateSelectVisible); target.addAttribute("dateSelectEnabled", dateSelectEnabled); target.addAttribute("legendVisibility", legendVisible); target.addAttribute("bandPagingVisible", pageNavigationVisible); target.addAttribute("bandSelectionEnabled", bandSelectionEnabled); sendComponentVisibilities = false; } // Send zoom levels if (sendZoomLevels) { // put them into an array of objects to keep the order String[] levels = new String[zoomLevels.size()]; int idx = 0; for (Map.Entry<String, Long> entry : zoomLevels.entrySet()) { levels[idx++] = entry.getKey() + "," + entry.getValue(); } target.addAttribute("zoomLevels", levels); sendZoomLevels = false; } // Send the graph grid color if it has changed if (sendGridColor) { // target target.addAttribute("gridColor", gridColor == null ? "" : gridColor); sendGridColor = false; } // Send the UI captions if they have changed if (sendUICaptions) { target.addAttribute("zlvlcaption", zoomLevelCaption); target.addAttribute("bpgingCaption", pagingCaption.getCaption()); target.addAttribute("bpgingCptPrevious", pagingCaption.getCaption_previous()); target.addAttribute("bpgingCptNext", pagingCaption.getCaption_next()); sendUICaptions = false; } // Send the number of event bands visible if (sendEventBandPageSize) { target.addAttribute(VEventTimelineWidget.ATTR_BAND_PAGE_SIZE, eventBandPageSize); sendEventBandPageSize = false; } if (sendBandHeight) { target.addAttribute("bandheight", bandHeight); sendBandHeight = false; } if (sendBands) { if (bandsToBeRemoved.size() > 0 || bandsToBeAdded.size() > 0) { BandsPainter.start(target); for (BandInfo band : bandsToBeRemoved) { BandsPainter.paintRemove(target, band.getBandId()); } bandsToBeRemoved.clear(); for (BandInfo band : bandsToBeAdded) { BandsPainter.paintAdd(target, band.getBandId(), band.getCaption()); } bandsToBeAdded.clear(); BandsPainter.end(target); } sendBands = false; } // Send date formats if set if (sendDateFormatInfo) { target.addAttribute("dateformats", dateFormatInfo.serialize()); sendDateFormatInfo = false; } } /** * Paints single timeline event to UIDL. Override this method to add custom attributes for events. * * @param i * Index of target Timeline.Event * @param target * PaintTarget */ protected void paintEvent(TimelineEvent e, PaintTarget target) throws PaintException { target.addAttribute(VEventTimelineWidget.ATTR_ID, e.getEventId()); target.addAttribute(VEventTimelineWidget.ATTR_CAPTION, (e.getCaption() == null ? "" : e.getCaption())); target.addAttribute(VEventTimelineWidget.ATTR_DATEFROM, df_date.format(e.getStart())); target.addAttribute(VEventTimelineWidget.ATTR_DATETO, df_date.format(e.getEnd())); target.addAttribute(VEventTimelineWidget.ATTR_TIMEFROM, e.getStart().getTime()); target.addAttribute(VEventTimelineWidget.ATTR_TIMETO, e.getEnd().getTime()); target.addAttribute(VEventTimelineWidget.ATTR_DESCRIPTION, e.getDescription() == null ? "" : e.getDescription()); target.addAttribute(VEventTimelineWidget.ATTR_STYLE, e.getStyleName() == null ? "" : e.getStyleName()); } protected String[] getEventsAsStringArray(final List<TimelineEvent> events) throws PaintException { String[] result = new String[events.size()]; int idx = 0; for (TimelineEvent e : events) { StringBuilder sb = new StringBuilder(); sb.append(e.getEventId()); sb.append(";"); sb.append(e.getCaption()); sb.append(";"); sb.append(df_date.format(e.getStart())); sb.append(";"); sb.append(df_date.format(e.getEnd())); sb.append(";"); sb.append(e.getStart().getTime()); sb.append(";"); sb.append(e.getEnd().getTime()); sb.append(";"); sb.append(e.getDescription()); sb.append(";"); sb.append(e.getStyleName()); result[idx++] = sb.toString(); } return result; } /** * Add another event band. * * @param caption * The caption for this band * @param provider * The provider for events displayed in this band * @return the created band */ public BandInfo addEventBand(final String caption, final TimelineEventProvider provider) { lastBandId++; provider.addListener(this); BandInfo result = new BandInfo(lastBandId, provider, caption); bandInfos.add(result); bandsToBeAdded.add(result); if (initDone) { sendBands = true; List<TimelineEvent> events = provider.getEvents(minDate, maxDate); eventsToSend.put(result.getBandId(), events); eventsStartTime = minDate; eventsEndTime = maxDate; requestRepaint(); } return result; } /** * Removes the event band. * * @param provider * The provider for events displayed in this band */ public void removeEventBand(final TimelineEventProvider provider) { provider.removeListener(this); BandInfo result = null; for (BandInfo info : bandInfos) { if (info.getProvider() == provider) { result = info; break; } } if (result != null) { result.getProvider().removeListener(this); bandInfos.remove(result); // if the band was added previously before sending the band // to the client, than remove the band again if (bandsToBeAdded.contains(result)) { bandsToBeAdded.remove(result); eventsToSend.remove(result.getBandId()); } else { bandsToBeRemoved.add(result); } } if (initDone) { sendBands = true; requestRepaint(); } } /** * Removes all event bands from the timeline. */ public void removeAllEventBands() { for (BandInfo info : getBandInfos()) { removeEventBand(info.getProvider()); } } @Override public void eventSetChange(EventSetChange changeEvent) { TimelineEventProvider provider = changeEvent.getProvider(); List<TimelineEvent> events = provider.getEvents(minDate, maxDate); int bandId = -1; for (BandInfo info : bandInfos) { if (info.getProvider().equals(provider)) { bandId = info.getBandId(); break; } } if (bandId >= 0) { eventsToSend.put(bandId, events); eventsStartTime = minDate; eventsEndTime = maxDate; } requestRepaint(); } /** * Sets the displayed time range.<br/> * The displayed time is the time range selected in the browser and displayed in the main area of * the component. The displayed time range cannot be set until some data source has been added to * the component. * * @param start * The start date * @param end * The end data */ public void setVisibleDateRange(Date start, Date end) { // Do consistency check if (start.equals(end) || end.after(start)) { // new time limits minDate = start; maxDate = end; sendTimeLimits = true; selectedStartDate = start; selectedEndDate = end; sendDateRange = true; if (initDone) { requestRepaint(); } } else { throw new IllegalArgumentException("End date must come after the start date."); } } /** * Makes the whole graph visible and selected in the browser. */ public void selectFullRange() { setVisibleDateRange(minDate, maxDate); } /** * Adds a date range listener.<br/> * This is triggered when the date range is changed. * * @param listener * The listener to be added */ public void addListener(DateRangeListener listener) { addListener(DateRangeChangedEvent.class, listener, DATERANGE_CHANGED_METHOD); sendChangeEventAvailable = true; dateRangeListenerCounter++; } /** * Add a button click listener * * @param listener * The listener to be added */ public void addListener(EventClickListener listener) { addListener(EventButtonClickEvent.class, listener, EVENT_CLICK_METHOD); } /** * Add a band paging listener * * @param listener * The listener to be added */ public void addListener(PageNavigationListener listener) { addListener(PageNavigationEvent.class, listener, PAGE_NAVIGATION_METHOD); } /** * Add a band selection listener * * @param listener * The listener to be added */ public void addListener(BandSelectionListener listener) { addListener(BandSelectionEvent.class, listener, BAND_SELECTION_METHOD); } /** * Remove a date range listener * * @param listener * The listener to be removed */ public void removeListener(DateRangeListener listener) { removeListener(DateRangeChangedEvent.class, listener); dateRangeListenerCounter--; if (dateRangeListenerCounter == 0) { sendChangeEventAvailable = false; } } /** * Remove a event button click listener * * @param listener * The listener to be removed */ public void removeListener(EventClickListener listener) { removeListener(EventButtonClickEvent.class, listener); } /** * Remove a page navigation listener * * @param listener * The listener to be removed */ public void removeListener(PageNavigationListener listener) { removeListener(PageNavigationEvent.class, listener); } /** * Remove a band selection listener * * @param listener * The listener to be removed */ public void removeListener(BandSelectionListener listener) { removeListener(BandSelectionEvent.class, listener); } /** * Fires a date range changed event * * @param start * The start date of the range * @param end * The end date of the range */ protected void fireDateRangeChangedEvent(Date start, Date end) { fireEvent(new EventTimeline.DateRangeChangedEvent(this, start, end)); } /** * Fires a event button click event which occurs when the user presses an event button in the * graph * * @param itemId * The item id in the Event container which represents the event */ protected void fireEventButtonClickEvent(String itemId) { fireEvent(new EventTimeline.EventButtonClickEvent(this, itemId)); } /** * Fires a band paging event if the user clicks on next / previous band. * * @param page * The visible page of the band area. */ protected void fireBandPagingEvent(int page) { fireEvent(new EventTimeline.PageNavigationEvent(this, page)); } /** * Fires a band selection event if the user selected a band. * * @param page * The id of the selected band. */ protected void fireBandSelectionEvent(int bandId) { fireEvent(new EventTimeline.BandSelectionEvent(this, bandId)); } /** * Shows or hides the browser.<br/> * The browser is the timeline browser in the bottom of the component. With the browser you can * move or zoom in time. * * @param visible * If true then the browser is visible */ public void setBrowserVisible(boolean visible) { browserIsVisible = visible; sendComponentVisibilities = true; if (initDone) { requestRepaint(); } } /** * Is the browser visible */ public boolean isBrowserVisible() { return browserIsVisible; } /** * Show the zoom levels.<br/> * Zoom levels are predefined time ranges which are displayed in the top left corner of the * Timeline component. * * @param visible * If true then the zoom levels are visible */ public void setZoomLevelsVisible(boolean visible) { zoomIsVisible = visible; sendComponentVisibilities = true; if (initDone) { requestRepaint(); } } /** * Are the zoom levels visible.<br/> * Zoom levels are predefined time ranges which are displayed in the top left corner of the * Timeline component. */ public boolean isZoomLevelsVisible() { return zoomIsVisible; } /** * Sets the caption of the zoom levels * * @param caption * */ public void setZoomLevelsCaption(String caption) { if (caption == null) { caption = ""; } this.zoomLevelCaption = caption; sendUICaptions = true; if (initDone) { requestRepaint(); } } /** * Get the caption of the zoom levels * * @return */ public String getZoomLevelsCaption() { return this.zoomLevelCaption; } /** * Sets the band height. A value of -1 indicates that the bands shall use automatic resizing to * available space. * * @param height * the band heigth to be used * */ public void setBandHeight(int height) { this.bandHeight = height; sendBandHeight = true; if (initDone) { requestRepaint(); } } /** * Get the configured band height * * @return the band height */ public int getBandHeight() { return this.bandHeight; } /** * Returns the captions of the band paging element. * * @return the pagingCaption */ public PageNavigationCaptions getPageNavigationCaptions() { return pagingCaption; } /** * Sets the pagingCaptions of the band paging element. * * @param pagingCaption * the pagingCaption to set */ public void setPageNavigationCaptions(PageNavigationCaptions pagingCaption) { if (pagingCaption == null) { pagingCaption = new PageNavigationCaptions("", "", ""); } this.pagingCaption = pagingCaption; sendUICaptions = true; if (initDone) { requestRepaint(); } } /** * Returns a collection with informations about each added band. * * @return the bandInfos */ public List<BandInfo> getBandInfos() { return Collections.unmodifiableList(new ArrayList<BandInfo>(bandInfos)); } /** * Returns the band info for the given bandId or <code>null</code> if no band could be found. * * @param bandId * @return */ public BandInfo getBand(int bandId) { for (BandInfo info : bandInfos) { if (info.getBandId() == bandId) { return info; } } return null; } /** * Show the date select.<br/> * The date select is the text fields in the top right corner of the component which shows the * currently selected date range. * * @param visible * Should the data select be visible */ public void setDateSelectVisible(boolean visible) { dateSelectVisible = visible; sendComponentVisibilities = true; if (initDone) { requestRepaint(); } } /** * Is the date select visible.<br/> * The date select is the text fields in the top right corner of the component which shows the * currently selected date range. */ public boolean isDateSelectVisible() { return dateSelectVisible; } /** * Enable manual editing of selected date range.<br/> * The date select is the text fields in the top right corner of the component which shows the * currently selected date range.<br/> * The date range can be used to either just display the currently selected date range or one can * allow the used to manually edit the selected date range by clicking on the dates by setting * enabled to true. * * @param enabled * Is the date selected manually editable */ public void setDateSelectEnabled(boolean enabled) { dateSelectEnabled = enabled; sendComponentVisibilities = true; if (initDone) { requestRepaint(); } } /** * Shows the page navigation.<br/> * Using the page navigation allows to observe the requested band page. * * @param visible */ public void setPageNavigationVisible(boolean visible) { pageNavigationVisible = visible; sendComponentVisibilities = true; if (initDone) { requestRepaint(); } } /** * Returns true, if the page navigation is visible. * * @return the pageNavigationVisible */ public boolean isPageNavigationVisible() { return pageNavigationVisible; } /** * Returns true, if the band selection is enabled. See {@link #setBandSelectionEnabled(boolean)} * for more details. * * @return */ public boolean isBandSelectionEnabled() { return bandSelectionEnabled; } /** * If the band selection is enabled the selected band will be marked at the UI. Additionally band * selection events are sent, if the selected band changes. * * @param bandSelectionEnabled */ public void setBandSelectionEnabled(boolean bandSelectionEnabled) { this.bandSelectionEnabled = bandSelectionEnabled; sendComponentVisibilities = true; if (initDone) { requestRepaint(); } } /** * Returns the numbers of event bands shown in the event bands area. A value lower equal 0 means * that an unlimited number of event bands can be shown simultaneously at the band area. * * @return the eventBandPageSize */ public int getEventBandPageSize() { return eventBandPageSize; } /** * Specifies the numbers of event bands shown in the event bands area.<br/> * Setting a value lower equal 0 means that an unlimited number of event bands can be shown * simultaneously at the band area. * * @param eventBandPageSize * the eventBandPageSize to set */ public void setEventBandPageSize(int eventBandPageSize) { this.eventBandPageSize = eventBandPageSize; sendEventBandPageSize = true; if (initDone) { requestRepaint(); } } /** * Is the date select enabled.<br/> * The date select is the text fields in the top right corner of the component which shows the * currently selected date range.<br/> * The date range can be used to either just display the currently selected date range or one can * allow the used to manually edit the selected date range by clicking on the dates by setting * enabled to true. */ public boolean isDateSelectEnabled() { return dateSelectEnabled; } /** * Set legend visibility.<br/> * The legend displays labels for each graph * * @param visible * The legend visibility */ public void setGraphLegendVisible(boolean visible) { legendVisible = visible; sendComponentVisibilities = true; if (initDone) { requestRepaint(); } } /** * Is the legend visible */ public boolean isGraphLegendVisible() { return legendVisible; } /** * Add a zoom level.<br/> * Adds a custom zoom level. Zoom levels are defined as milliseconds and are added to the top left * of the Timeline component. * * @param caption * The title of the zoom level * @param time * The timespan of the zoom level */ public void addZoomLevel(String caption, Long time) { zoomLevels.put(caption, time); sendZoomLevels = true; if (initDone) { requestRepaint(); } } /** * Remove a zoom level.<br/> * Zoom levels are defined as milliseconds and are added to the top left of the Timeline * component. * * @param caption * The title of the zoom level */ public void removeZoomLevel(String caption) { zoomLevels.remove(caption); sendZoomLevels = true; if (initDone) { requestRepaint(); } } /** * Sets the color of the grid.<br/> * Setting the color to NULL will remove the grid. * * @param color * The color (as CSS3-style rgba string) of the grid or Null to remove the grid */ public void setGridColor(String color) { gridColor = color; sendGridColor = true; if (initDone) { requestRepaint(); } } /** * Gets the grid color used to draw the vertical and horizontal scale grids.<br/> * Returns null if no grid is drawn in the graph * * @return The color of the graph */ public String getGridColor() { return gridColor; } /** * When using dynamically updating graphs the updates may cause the selection bar to move when new * items are added to the data source and the graph changes. To lock the browser bar so it stays * still and instead the selection changes when new items are added set this to false. By default * the selection bar is locked to the selection. <br/> * Please note that when the selection lock is unlocked then the selection range will change with * each update. * * @param locked * Should the selection range be locked to the selected range or should the selection * change when new items are added to the data source */ public void setBrowserSelectionLock(boolean locked) { selectionBarLocked = locked; if (initDone) { requestRepaint(); } } /** * Returns a map with all events grouped by the bandId. * * @param start * @param end * @return */ private Map<Integer, List<TimelineEvent>> getAllEventsMap(Date start, Date end) { Map<Integer, List<TimelineEvent>> events = new HashMap<Integer, List<TimelineEvent>>(); for (BandInfo info : bandInfos) { TimelineEventProvider provider = info.getProvider(); events.put(info.getBandId(), provider.getEvents(start, end)); } return events; } private void initDataFlags() { initDone = true; sendTimeLimits = true; sendDateRange = true; sendComponentVisibilities = true; sendGridColor = true; sendZoomLevels = true; sendUICaptions = true; sendBands = true; sendBandHeight = true; sendEventBandPageSize = true; } /** * Gets an object describing the date formats. * * To use your own date formats retrieve the formats using this method and use the setters to * customize the date formats. * * @return */ public DateFormatInfo getDateFormats() { return this.dateFormatInfo; } /** * A helper class to paint band commands. */ private static class BandsPainter { /** * Start tag {@link VEventTimelineWidget#ATTR_BANDS} * * @param target * @throws PaintException */ public static void start(PaintTarget target) throws PaintException { target.startTag(VEventTimelineWidget.ATTR_BANDS); } /** * End tag {@link VEventTimelineWidget#ATTR_BANDS} * * @param target * @throws PaintException */ public static void end(PaintTarget target) throws PaintException { target.endTag(VEventTimelineWidget.ATTR_BANDS); } /** * Paints the add band command. * * @param target * @param bandId * @param caption * @throws PaintException */ public static void paintAdd(PaintTarget target, int bandId, String caption) throws PaintException { target.startTag(VEventTimelineWidget.ATTR_BAND); target.addAttribute(VEventTimelineWidget.ATTR_BANDID, bandId); target.addAttribute(VEventTimelineWidget.ATTR_OPERATION, VEventTimelineWidget.OPERATION_ADD); target.addAttribute(VEventTimelineWidget.ATTR_BAND_CAPTION, caption); target.endTag(VEventTimelineWidget.ATTR_BAND); } /** * Paints the remove band command. * * @param target * @param bandId * @throws PaintException */ public static void paintRemove(PaintTarget target, int bandId) throws PaintException { target.startTag(VEventTimelineWidget.ATTR_BAND); target.addAttribute(VEventTimelineWidget.ATTR_BANDID, bandId); target.addAttribute(VEventTimelineWidget.ATTR_OPERATION, VEventTimelineWidget.OPERATION_REMOVE); target.endTag(VEventTimelineWidget.ATTR_BAND); } } /** * Can be used to specify the captions of the band paging navigation element. */ public static class PageNavigationCaptions implements Serializable { private static final long serialVersionUID = 7731112570587996160L; private final String caption; private final String caption_next; private final String caption_previous; /** * * @param caption * main caption * @param caption_next * caption of the next button * @param caption_previous * caption of the previous button */ public PageNavigationCaptions(String caption, String caption_next, String caption_previous) { this.caption = caption; this.caption_next = caption_next; this.caption_previous = caption_previous; } /** * Main caption. * * @return the caption */ public String getCaption() { return caption; } /** * Caption of the next button. * * @return the caption_next */ public String getCaption_next() { return caption_next; } /** * Caption of the previous button. * * @return the caption_previous */ public String getCaption_previous() { return caption_previous; } } /** * Class providing band information. */ public static class BandInfo implements Serializable { private static final long serialVersionUID = 6388755571992132307L; private final int bandId; private final TimelineEventProvider provider; private String caption; public BandInfo(int bandId, TimelineEventProvider provider, String caption) { this.bandId = bandId; this.provider = provider; this.caption = caption; } /** * @return the caption */ public String getCaption() { return caption; } /** * @param caption the caption to set */ protected void setCaption(String caption) { this.caption = caption; } /** * @return the bandId */ public int getBandId() { return bandId; } /** * @return the provider */ public TimelineEventProvider getProvider() { return provider; } } }