You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ctakes.apache.org by se...@apache.org on 2015/02/19 19:06:17 UTC

svn commit: r1660963 [17/19] - in /ctakes/sandbox/timelanes: META-INF/ edu/ edu/mayo/ edu/mayo/bmi/ edu/mayo/bmi/annotation/ edu/mayo/bmi/annotation/knowtator/ org/ org/chboston/ org/chboston/cnlp/ org/chboston/cnlp/anafora/ org/chboston/cnlp/anafora/a...

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralledScrollPaneLayout.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralledScrollPaneLayout.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralledScrollPaneLayout.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/SpiralledScrollPaneLayout.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,516 @@
+package org.chboston.cnlp.timeline.oldUI;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/13/12
+ */
+public class SpiralledScrollPaneLayout extends ScrollPaneLayout {
+
+   static public final String SPIRAL_COMPONENT = "SpiralComponent";
+   static public final String ROW_FOOTER_COMPONENT = "RowFooterComponent";
+
+   // TODO constructor that takes SpiralUIDelegate and sues it for sizing
+
+
+   public void addLayoutComponent( String s, Component c ) {
+      if ( s.equals( SPIRAL_COMPONENT ) ) {
+         super.addLayoutComponent( LOWER_LEFT_CORNER, c );
+         return;
+      }
+      if ( s.equals( ROW_FOOTER_COMPONENT ) ) {
+         super.addLayoutComponent( LOWER_RIGHT_CORNER, c );
+         return;
+      }
+
+      super.addLayoutComponent( s, c );
+   }
+
+
+   /**
+    * The preferred size of a <code>ScrollPane</code> is the size of the insets,
+    * plus the preferred size of the viewport, plus the preferred size of
+    * the visible headers, plus the preferred size of the scrollbars
+    * that will appear given the current view and the current
+    * scrollbar displayPolicies.
+    * <p>Note that the rowHeader is calculated as part of the preferred width
+    * and the colHeader is calculated as part of the preferred size.
+    *
+    * @param parent the <code>Container</code> that will be laid out
+    * @return a <code>Dimension</code> object specifying the preferred size of the
+    * viewport and any scrollbars
+    * @see ViewportLayout
+    * @see LayoutManager
+    */
+   public Dimension preferredLayoutSize( Container parent ) {
+      final Component spiralComponent = lowerLeft;
+      final Dimension preferredSize = super.preferredLayoutSize( parent );
+      if ( spiralComponent == null ) {
+         return preferredSize;
+      }
+      final int spiralDiameter = spiralComponent.getHeight();
+      final int leftIndent = getLeftIndentation();
+      final int rightIndent = spiralDiameter / 2;
+      return new Dimension( preferredSize.width + leftIndent + rightIndent, preferredSize.height + spiralDiameter );
+      //      return new Dimension( preferredSize.width + 2 * spiralDiameter, preferredSize.height + spiralDiameter );
+   }
+
+
+   /**
+    * The minimum size of a <code>ScrollPane</code> is the size of the insets
+    * plus minimum size of the viewport, plus the scrollpane's
+    * viewportBorder insets, plus the minimum size
+    * of the visible headers, plus the minimum size of the
+    * scrollbars whose displayPolicy isn't NEVER.
+    *
+    * @param parent the <code>Container</code> that will be laid out
+    * @return a <code>Dimension</code> object specifying the minimum size
+    */
+   public Dimension minimumLayoutSize( Container parent ) {
+      final Component spiralComponent = lowerLeft;
+      final Dimension minimumSize = super.minimumLayoutSize( parent );
+      if ( spiralComponent == null ) {
+         return minimumSize;
+      }
+      final int spiralDiameter = spiralComponent.getHeight();
+      final int leftIndent = getLeftIndentation();
+      final int rightIndent = spiralDiameter / 2;
+      return new Dimension( minimumSize.width + leftIndent + rightIndent, minimumSize.height + spiralDiameter );
+      //      return new Dimension( minimumSize.width + 2 * spiralDiameter, minimumSize.height + spiralDiameter );
+   }
+
+   private int getLeftIndentation() {
+      final Component spiralComponent = lowerLeft;
+      final int spiralRadius = (spiralComponent == null) ? 0 : spiralComponent.getHeight() / 2;
+      if ( (rowHead != null) && (rowHead.isVisible()) ) {
+         int rowHeadWidth = rowHead.getPreferredSize().width;
+         return Math.max( spiralRadius, rowHeadWidth );
+      }
+      return spiralRadius;
+   }
+
+
+   /**
+    * Lays out the scrollpane. The positioning of components depends on
+    * the following constraints:
+    * <ul>
+    * <li> The row header, if present and visible, gets its preferred
+    * width and the viewport's height.
+    * <p/>
+    * <li> The column header, if present and visible, gets its preferred
+    * height and the viewport's width.
+    * <p/>
+    * <li> If a vertical scrollbar is needed, i.e. if the viewport's extent
+    * height is smaller than its view height or if the <code>displayPolicy</code>
+    * is ALWAYS, it's treated like the row header with respect to its
+    * dimensions and is made visible.
+    * <p/>
+    * <li> If a horizontal scrollbar is needed, it is treated like the
+    * column header (see the paragraph above regarding the vertical scrollbar).
+    * <p/>
+    * <li> If the scrollpane has a non-<code>null</code>
+    * <code>viewportBorder</code>, then space is allocated for that.
+    * <p/>
+    * <li> The viewport gets the space available after accounting for
+    * the previous constraints.
+    * <p/>
+    * <li> The corner components, if provided, are aligned with the
+    * ends of the scrollbars and headers. If there is a vertical
+    * scrollbar, the right corners appear; if there is a horizontal
+    * scrollbar, the lower corners appear; a row header gets left
+    * corners, and a column header gets upper corners.
+    * </ul>
+    *
+    * @param parent the <code>Container</code> to lay out
+    */
+   public void layoutContainer( Container parent ) {
+      final Component spiralComponent = lowerLeft;
+      if ( spiralComponent == null ) {
+         super.layoutContainer( parent );
+         return;
+      }
+      final int spiralDiameter = spiralComponent.getHeight();
+      final int spiralRadius = spiralDiameter / 2;
+      final int leftIndent = getLeftIndentation();
+      final int rightIndent = spiralRadius;
+
+      /* Sync the (now obsolete) policy fields with the
+      * JScrollPane.
+      */
+      final JScrollPane scrollPane = (JScrollPane)parent;
+      final Rectangle scrollPaneBounds = scrollPane.getBounds();
+      vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
+      hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
+
+      Rectangle availR = scrollPane.getBounds();
+      availR.x = availR.y = 0;
+
+      final Insets insets = parent.getInsets();
+      availR.x = insets.left;
+      availR.y = insets.top;
+      availR.width -= insets.left + insets.right;
+      availR.height -= insets.top + insets.bottom;
+
+      /* Get the scrollPane's orientation.
+      */
+      boolean leftToRight = true;//SwingUtilities.isLeftToRight(scrollPane);
+
+      /* If there's a visible column header remove the space it
+      * needs from the top of availR.  The column header is treated
+      * as if it were fixed height, arbitrary width.
+      */
+
+      Rectangle colHeadR = new Rectangle( 0, availR.y, 0, 0 );
+
+      if ( (colHead != null) && (colHead.isVisible()) ) {
+         int colHeadHeight = Math.min( availR.height,
+               colHead.getPreferredSize().height );
+         colHeadR.height = colHeadHeight;
+         availR.y += colHeadHeight;
+         availR.height -= colHeadHeight;
+      }
+
+      /* If there's a visible row header remove the space it needs
+      * from the left or right of availR.  The row header is treated
+      * as if it were fixed width, arbitrary height.
+      */
+
+      Rectangle rowHeadR = new Rectangle( 0, 0, 0, 0 );
+
+      if ( (rowHead != null) && (rowHead.isVisible()) ) {
+         //         int rowHeadWidth = Math.min(availR.width,
+         //                                     rowHead.getPreferredSize().width);
+         //         int rowHeadWidth = Math.min(leftIndent,
+         //                                     rowHead.getPreferredSize().width);
+         //         rowHeadR.width = rowHeadWidth;
+         rowHeadR.x = insets.left;
+         rowHeadR.width = leftIndent - insets.left;
+         //         availR.width -= rowHeadWidth;
+         //         if ( leftToRight ) {
+         //            rowHeadR.x = availR.x;
+         //            availR.x += rowHeadWidth;
+         //         } else {
+         //            rowHeadR.x = availR.x + availR.width;
+         //         }
+      }
+
+      // Account for the left indentation caused by the larger of 1/2 spiral or rowheader
+      availR.width -= leftIndent;
+      availR.x += leftIndent;
+      // Account for the right indentation caused by 1/2 spiral
+      availR.width -= rightIndent;
+      availR.height -= spiralDiameter;
+
+
+      /* If there's a JScrollPane.viewportBorder, remove the
+      * space it occupies for availR.
+      */
+      // We don't want to allow borders
+      //      Border viewportBorder = scrollPane.getViewportBorder();
+      Insets vpbInsets;
+      //      if (viewportBorder != null) {
+      //         vpbInsets = viewportBorder.getBorderInsets(parent);
+      //         availR.x += vpbInsets.left;
+      //         availR.y += vpbInsets.top;
+      //         availR.width -= vpbInsets.left + vpbInsets.right;
+      //         availR.height -= vpbInsets.top + vpbInsets.bottom;
+      //      }
+      //      else {
+      vpbInsets = new Insets( 0, 0, 0, 0 );
+      //      }
+
+
+      /* At this point availR is the space available for the viewport
+      * and scrollbars. rowHeadR is correct except for its height and y
+           * and colHeadR is correct except for its width and x.  Once we're
+      * through computing the dimensions  of these three parts we can
+      * go back and set the dimensions of rowHeadR.height, rowHeadR.y,
+           * colHeadR.width, colHeadR.x and the bounds for the corners.
+      *
+           * We'll decide about putting up scrollbars by comparing the
+           * viewport views preferred size with the viewports extent
+      * size (generally just its size).  Using the preferredSize is
+      * reasonable because layout proceeds top down - so we expect
+      * the viewport to be laid out next.  And we assume that the
+      * viewports layout manager will give the view it's preferred
+      * size.  One exception to this is when the view implements
+      * Scrollable and Scrollable.getViewTracksViewport{Width,Height}
+      * methods return true.  If the view is tracking the viewports
+      * width we don't bother with a horizontal scrollbar, similarly
+      * if view.getViewTracksViewport(Height) is true we don't bother
+      * with a vertical scrollbar.
+      */
+
+      final Component view = (viewport != null) ? viewport.getView() : null;
+      Dimension viewPrefSize =
+            (view != null) ? view.getPreferredSize()
+                           : new Dimension( 0, 0 );
+
+      Dimension extentSize =
+            (viewport != null) ? viewport.toViewCoordinates( availR.getSize() )
+                               : new Dimension( 0, 0 );
+
+      boolean viewTracksViewportWidth = false;
+      boolean viewTracksViewportHeight = false;
+      boolean isEmpty = (availR.width < 0 || availR.height < 0);
+      Scrollable sv;
+      // Don't bother checking the Scrollable methods if there is no room
+      // for the viewport, we aren't going to show any scrollbars in this
+      // case anyway.
+      if ( !isEmpty && view instanceof Scrollable ) {
+         sv = (Scrollable)view;
+         viewTracksViewportWidth = sv.getScrollableTracksViewportWidth();
+         viewTracksViewportHeight = sv.getScrollableTracksViewportHeight();
+      } else {
+         sv = null;
+      }
+
+      /* If there's a vertical scrollbar and we need one, allocate
+      * space for it (we'll make it visible later). A vertical
+      * scrollbar is considered to be fixed width, arbitrary height.
+      */
+
+      Rectangle vsbR = new Rectangle( 0, availR.y - vpbInsets.top, 0, 0 );
+
+      boolean vsbNeeded;
+      if ( isEmpty ) {
+         vsbNeeded = false;
+      } else if ( vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS ) {
+         vsbNeeded = true;
+      } else if ( vsbPolicy == VERTICAL_SCROLLBAR_NEVER ) {
+         vsbNeeded = false;
+      } else {  // vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED
+         vsbNeeded = !viewTracksViewportHeight && (viewPrefSize.height > extentSize.height);
+      }
+
+      if ( (vsb != null) && vsbNeeded ) {
+         adjustForVSB( true, availR, vsbR, vpbInsets, leftToRight );
+         extentSize = viewport.toViewCoordinates( availR.getSize() );
+      }
+
+      /* If there's a horizontal scrollbar and we need one, allocate
+      * space for it (we'll make it visible later). A horizontal
+      * scrollbar is considered to be fixed height, arbitrary width.
+      */
+
+      //      Rectangle hsbR = new Rectangle(availR.x - vpbInsets.left, 0, 0, 0);
+      final int Scrollbar_Spacer = 3;
+      Rectangle hsbR = new Rectangle( availR.x - vpbInsets.left + spiralRadius + Scrollbar_Spacer, 0, 0, 0 );
+      boolean hsbNeeded;
+      if ( isEmpty ) {
+         hsbNeeded = false;
+      } else if ( hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS ) {
+         hsbNeeded = true;
+      } else if ( hsbPolicy == HORIZONTAL_SCROLLBAR_NEVER ) {
+         hsbNeeded = false;
+      } else {  // hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED
+         hsbNeeded = !viewTracksViewportWidth && (viewPrefSize.width > extentSize.width);
+      }
+
+      if ( (hsb != null) && hsbNeeded ) {
+         adjustForHSB( true, availR, hsbR, vpbInsets );
+         /* If we added the horizontal scrollbar then we've implicitly
+           * reduced  the vertical space available to the viewport.
+           * As a consequence we may have to add the vertical scrollbar,
+           * if that hasn't been done so already.  Of course we
+           * don't bother with any of this if the vsbPolicy is NEVER.
+           */
+         if ( (vsb != null) && !vsbNeeded && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER) ) {
+            extentSize = viewport.toViewCoordinates( availR.getSize() );
+            vsbNeeded = viewPrefSize.height > extentSize.height;
+            if ( vsbNeeded ) {
+               adjustForVSB( true, availR, vsbR, vpbInsets, leftToRight );
+            }
+         }
+      }
+
+      /* Set the size of the viewport first, and then recheck the Scrollable
+      * methods. Some components base their return values for the Scrollable
+      * methods on the size of the Viewport, so that if we don't
+      * ask after resetting the bounds we may have gotten the wrong
+      * answer.
+      */
+
+      if ( viewport != null ) {
+         viewport.setBounds( availR );
+         if ( sv != null ) {
+            extentSize = viewport.toViewCoordinates( availR.getSize() );
+            boolean oldHSBNeeded = hsbNeeded;
+            boolean oldVSBNeeded = vsbNeeded;
+            viewTracksViewportWidth = sv.getScrollableTracksViewportWidth();
+            viewTracksViewportHeight = sv.getScrollableTracksViewportHeight();
+            if ( vsb != null && vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED ) {
+               boolean newVSBNeeded = !viewTracksViewportHeight && (viewPrefSize.height > extentSize.height);
+               if ( newVSBNeeded != vsbNeeded ) {
+                  vsbNeeded = newVSBNeeded;
+                  adjustForVSB( vsbNeeded, availR, vsbR, vpbInsets, leftToRight );
+                  extentSize = viewport.toViewCoordinates( availR.getSize() );
+               }
+            }
+            if ( hsb != null && hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED ) {
+               boolean newHSBbNeeded = !viewTracksViewportWidth && (viewPrefSize.width > extentSize.width);
+               if ( newHSBbNeeded != hsbNeeded ) {
+                  hsbNeeded = newHSBbNeeded;
+                  adjustForHSB( hsbNeeded, availR, hsbR, vpbInsets );
+                  if ( (vsb != null) && !vsbNeeded && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER) ) {
+                     extentSize = viewport.toViewCoordinates( availR.getSize() );
+                     vsbNeeded = viewPrefSize.height > extentSize.height;
+                     if ( vsbNeeded ) {
+                        adjustForVSB( true, availR, vsbR, vpbInsets, leftToRight );
+                     }
+                  }
+               }
+            }
+            if ( oldHSBNeeded != hsbNeeded || oldVSBNeeded != vsbNeeded ) {
+               viewport.setBounds( availR );
+               // You could argue that we should recheck the
+               // Scrollable methods again until they stop changing,
+               // but they might never stop changing, so we stop here
+               // and don't do any additional checks.
+            }
+         }
+      }
+
+      /* We now have the final size of the viewport: availR.
+      * Now fixup the header and scrollbar widths/heights.
+      */
+      vsbR.height = availR.height + vpbInsets.top + vpbInsets.bottom;
+      //      hsbR.width = availR.width + vpbInsets.left + vpbInsets.right;
+      hsbR.width = availR.width + vpbInsets.left + vpbInsets.right - spiralDiameter - 2 * Scrollbar_Spacer;
+      rowHeadR.height = availR.height + vpbInsets.top + vpbInsets.bottom;
+      rowHeadR.y = availR.y - vpbInsets.top;
+      colHeadR.width = availR.width + vpbInsets.left + vpbInsets.right;
+      colHeadR.x = availR.x - vpbInsets.left;
+
+      /* Set the bounds of the remaining components.  The scrollbars
+      * are made invisible if they're not needed.
+      */
+
+      if ( rowHead != null ) {
+         rowHead.setBounds( rowHeadR );
+      }
+
+      if ( colHead != null ) {
+         colHead.setBounds( colHeadR );
+      }
+
+      if ( vsb != null ) {
+         if ( vsbNeeded ) {
+            vsb.setVisible( true );
+            vsbR.x = Math.max( vsbR.x, scrollPaneBounds.width - insets.right - vsbR.width );
+            vsb.setBounds( vsbR );
+         } else {
+            vsb.setVisible( false );
+         }
+      }
+
+      if ( hsb != null ) {
+         if ( hsbNeeded ) {
+            hsb.setVisible( true );
+            hsb.setBounds( hsbR );
+         } else {
+            hsb.setVisible( false );
+         }
+      }
+
+      if ( upperLeft != null ) {
+         upperLeft.setBounds( leftToRight ? rowHeadR.x : vsbR.x,
+               colHeadR.y,
+               leftToRight ? rowHeadR.width : vsbR.width,
+               colHeadR.height );
+      }
+
+      if ( upperRight != null ) {
+         upperRight.setBounds( leftToRight ? vsbR.x : rowHeadR.x,
+               colHeadR.y,
+               leftToRight ? vsbR.width : rowHeadR.width,
+               colHeadR.height );
+      }
+      // lower right is used for row footer - spiral obscures the lower right
+      if ( lowerRight != null ) {
+         final int rowFooterWidth = scrollPane.getBounds().width - (availR.x + availR.width) - insets.right - 2;
+         int lrWidth = Math.min( lowerRight.getPreferredSize().width, rowFooterWidth );
+         final Rectangle viewBounds = viewport.getBounds();
+         int lrX = viewBounds.x + viewBounds.width + 2;
+         if ( vsbNeeded ) {
+            lrWidth = Math.min( lowerRight.getPreferredSize().width, rowFooterWidth - vsbR.width );
+         }
+         lowerRight.setBounds( leftToRight ? lrX : rowHeadR.x,
+               viewBounds.y,
+               leftToRight ? lrWidth : insets.left,
+               vsbR.height );
+      }
+
+      // lower left is used for spiral
+      final int spiralX = insets.left + leftIndent - spiralRadius;
+      final int spiralWidth = scrollPane.getBounds().width - leftIndent + spiralRadius - insets.left - insets.right;
+      //      final int width = scrollPane.getBounds().width - insets.left - insets.right;
+      //      spiralComponent.setBounds( insets.left, availR.y + availR.height, width, spiralDiameter );
+      spiralComponent.setBounds( spiralX, availR.y + availR.height, spiralWidth, spiralDiameter );
+
+   }
+
+   /**
+    * Adjusts the <code>Rectangle</code> <code>available</code> based on if
+    * the vertical scrollbar is needed (<code>wantsVSB</code>).
+    * The location of the vsb is updated in <code>vsbR</code>, and
+    * the viewport border insets (<code>vpbInsets</code>) are used to offset
+    * the vsb. This is only called when <code>wantsVSB</code> has
+    * changed, eg you shouldn't invoke adjustForVSB(true) twice.
+    */
+   private void adjustForVSB( boolean wantsVSB, Rectangle available,
+                              Rectangle vsbR, Insets vpbInsets,
+                              boolean leftToRight ) {
+      //      int oldWidth = vsbR.width;
+      if ( wantsVSB ) {
+         //         int vsbWidth = Math.max(0, Math.min(vsb.getPreferredSize().width,
+         //                                             available.width));
+         //
+         //         available.width -= vsbWidth;
+         //         vsbR.width = vsbWidth;
+         vsbR.width = vsb.getPreferredSize().width;
+         //
+         //         if( leftToRight ) {
+         vsbR.x = available.x + available.width + vpbInsets.right;
+         //         } else {
+         //            vsbR.x = available.x - vpbInsets.left;
+         //            available.x += vsbWidth;
+         //         }
+         //      }
+         //      else {
+         //         available.width += oldWidth;
+      }
+   }
+
+   /**
+    * Adjusts the <code>Rectangle</code> <code>available</code> based on if
+    * the horizontal scrollbar is needed (<code>wantsHSB</code>).
+    * The location of the hsb is updated in <code>hsbR</code>, and
+    * the viewport border insets (<code>vpbInsets</code>) are used to offset
+    * the hsb.  This is only called when <code>wantsHSB</code> has
+    * changed, eg you shouldn't invoked adjustForHSB(true) twice.
+    */
+   private void adjustForHSB( boolean wantsHSB, Rectangle available,
+                              Rectangle hsbR, Insets vpbInsets ) {
+      //      int oldHeight = hsbR.height;
+      if ( wantsHSB ) {
+         //         int hsbHeight = Math.max(0, Math.min(available.height,
+         //                                              hsb.getPreferredSize().height));
+         //
+         //         available.height -= hsbHeight;
+         //         hsbR.y = available.y + available.height + vpbInsets.bottom;
+         //         hsbR.height = hsbHeight;
+         hsbR.height = hsb.getPreferredSize().height;
+         final Component spiralComponent = lowerLeft;
+         hsbR.y = available.y + available.height + vpbInsets.bottom + spiralComponent.getHeight() - hsbR.height;
+         //      }
+         //      else {
+         //         available.height += oldHeight;
+      }
+   }
+
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/TimelineDrawUtil.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/TimelineDrawUtil.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/TimelineDrawUtil.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/oldUI/TimelineDrawUtil.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,250 @@
+package org.chboston.cnlp.timeline.oldUI;
+
+import org.chboston.cnlp.nlp.annotation.annotation.Annotation;
+
+import java.awt.*;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.regex.Pattern;
+
+import static org.chboston.cnlp.timeline.timespan.CalendarUtil.TIME_MILLIS.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/22/12
+ */
+final public class TimelineDrawUtil {
+
+   static final Color DATE_SPAN_COLOR = Color.BLACK;
+   static final String DATE_SPAN_COLOR_NAME = "Black";
+   static final Color DATE_TEXT_COLOR = Color.BLUE;
+   static final String DATE_TEXT_COLOR_NAME = "Blue";
+   static final Color EVENT_TEXT_COLOR = Color.GREEN.darker().darker();
+   static final String EVENT_TEXT_COLOR_NAME = "Green";
+   static final Color EVENT_NEG_TEXT_COLOR = Color.RED;
+   static final String EVENT_NEG_TEXT_COLOR_NAME = "Red";
+   static private final Pattern SPACE_PATTERN = Pattern.compile( "\\s\\s+" );
+
+   static final class DateSelectionEvent extends EventObject {
+      final private String __dateSpanText;
+      final private Collection<Annotation> __dates;
+      final private Collection<Annotation> __events;
+
+      DateSelectionEvent( final Object source, final String dateSpanText,
+                          final Collection<Annotation> dates,
+                          final Collection<Annotation> events ) {
+         super( source );
+         __dateSpanText = dateSpanText;
+         __dates = dates;
+         __events = events;
+      }
+
+      public String getDateSpanText() {
+         return __dateSpanText;
+      }
+
+      public Collection<Annotation> getDates() {
+         return __dates;
+      }
+
+      public Collection<Annotation> getEvents() {
+         return __events;
+      }
+   }
+
+   static public interface DateSelectionListener extends EventListener {
+      public void dateSelected( final DateSelectionEvent selectionEvent );
+   }
+
+   private TimelineDrawUtil() {
+   }
+
+//
+//   /**
+//    * Partial implementation of Annotation that wraps a real Annotation.
+//    * Generates a Hashcode based only upon text, class, and attributes.
+//    * This prevents storage and plotting of annotations that have the same Class, text, and attributes.
+//    */
+//   static public class SpecialHashAnnotation implements Annotation {
+//      final private Annotation __annotation;
+//      final private String __simpleText;
+//      private int __hashcode = -1;
+//
+//      public SpecialHashAnnotation( final Annotation annotation ) {
+//         __annotation = annotation;
+//         __simpleText = SPACE_PATTERN.matcher( annotation.getSpannedText() ).replaceAll( " " );
+//
+//      }
+//
+//      public TextSpan getTextSpan() {
+//         return TextSpan.NULL_TEXT_SPAN.INSTANCE;
+//      }
+//
+//      /**
+//       * @return the text for the entire textspan of this entity
+//       */
+//      public String getSpannedText() {
+//         return __simpleText;
+//      }
+//
+//      /**
+//       * @return a representation of the text for the entire textspan of this entity.  Most useful for relations
+//       */
+//      public String getSpannedTextRepresentation() {
+//         return __annotation.getSpannedTextRepresentation();
+//      }
+//
+//      /**
+//       * @return the classtype type of this entity, e.g. "sign or symptom"
+//       */
+//      public ClassType getClassType() {
+//         return __annotation.getClassType();
+//      }
+//
+//      /**
+//       * @return all the attributes registered for this annotation
+//       */
+//      public java.util.List<String> getAttributeNames() {
+//         return __annotation.getAttributeNames();
+//      }
+//
+//      /**
+//       * @param key the name of a trait that may belong to this annotation
+//       * @return the attribute if this annotation has it, otherwise null
+//       */
+//      public Attribute getAttribute( final String key ) {
+//         return __annotation.getAttribute( key );
+//      }
+//
+//      public String getAnnotatorName() {
+//         return "SpecialHashAnnotation";
+//      }
+//
+//
+//      public boolean areValuesEqual( final Annotation annotation ) {
+//         if ( !annotation.getSpannedTextRepresentation().equals( getSpannedTextRepresentation() )
+//               || !annotation.getClassType().equals( getClassType() ) ) {
+//            return false;
+//         }
+//         final Collection<String> attributeNames = getAttributeNames();
+//         final Collection<String> annotationAttributeNames = annotation.getAttributeNames();
+//         if ( annotationAttributeNames.size() != attributeNames.size() ) {
+//            return false;
+//         }
+//         for ( String key : attributeNames ) {
+//            if ( !getAttribute( key ).equals( annotation.getAttribute( key ) ) ) {
+//               return false;
+//            }
+//         }
+//         return true;
+//      }
+//
+//      /**
+//       * {@inheritDoc}
+//       */
+//      @Override
+//      public boolean equals( final Object object ) {
+//         return object instanceof Annotation && areValuesEqual( (Annotation) object );
+//      }
+//
+//      /**
+//       * {@inheritDoc}
+//       */
+//      @Override
+//      public int hashCode() {
+//         if ( __hashcode != -1 ) {
+//            // hashcode already set
+//            return __hashcode;
+//         }
+//         long bits = 17 + getSpannedTextRepresentation().hashCode();
+//         bits = 2 * bits + getClassType().hashCode();
+//         final Collection<String> attributeNames = getAttributeNames();
+//         for ( String attributeName : attributeNames ) {
+//            bits = 2 * bits + getAttribute( attributeName ).hashCode();
+//         }
+//         __hashcode = (((int) bits) ^ ((int) (bits >> 32)));
+//         return __hashcode;
+//      }
+//   }
+
+
+   static public Calendar getBufferedBeginTime( final Calendar beginTime, final Calendar endTime ) {
+      final long timeSpanMillis = endTime.getTimeInMillis() - beginTime.getTimeInMillis();
+      final int calendarField = getOffsetCalendarField( timeSpanMillis );
+      final Calendar drawBeginTime = Calendar.getInstance();
+      drawBeginTime.set( beginTime.get( Calendar.YEAR ),
+            beginTime.get( Calendar.MONTH ),
+            beginTime.get( Calendar.DATE ),
+            12,
+            0 );
+      if ( drawBeginTime.getTimeInMillis() > beginTime.getTimeInMillis() ) {
+         drawBeginTime.add( Calendar.HOUR_OF_DAY, -12 );
+      }
+      final int amount = getOffsetCalendarAmount( timeSpanMillis );
+      drawBeginTime.add( calendarField, -1 * amount );
+      return drawBeginTime;
+   }
+
+   static public Calendar getBufferedEndTime( final Calendar beginTime, final Calendar endTime ) {
+      final long timeSpanMillis = endTime.getTimeInMillis() - beginTime.getTimeInMillis();
+      final int calendarField = getOffsetCalendarField( timeSpanMillis );
+      final Calendar drawEndTime = Calendar.getInstance();
+      drawEndTime.set( endTime.get( Calendar.YEAR ), endTime.get( Calendar.MONTH ), endTime.get( Calendar.DATE ),
+            12, 0 );
+      if ( drawEndTime.getTimeInMillis() < endTime.getTimeInMillis() ) {
+         drawEndTime.add( Calendar.HOUR_OF_DAY, 12 );
+      }
+      final int amount = getOffsetCalendarAmount( timeSpanMillis );
+      drawEndTime.add( calendarField, amount );
+      return drawEndTime;
+   }
+
+   static private int getOffsetCalendarField( final long timeSpanMillis ) {
+      int calendarField = Calendar.MONTH;
+      if ( timeSpanMillis <= HOUR.__millis ) {
+         calendarField = Calendar.MINUTE;
+      } else if ( timeSpanMillis <= DAY.__millis ) {
+         calendarField = Calendar.MINUTE;
+      } else if ( timeSpanMillis <= WEEK.__millis ) {
+         calendarField = Calendar.HOUR_OF_DAY;
+      } else if ( timeSpanMillis <= MONTH.__millis ) {
+         calendarField = Calendar.DAY_OF_YEAR;
+//      } else if ( timeSpanMillis <= SIX_MONTH_MILLIS ) {
+//         calendarField = Calendar.DAY_OF_YEAR;
+      } else if ( timeSpanMillis <= YEAR.__millis ) {
+         calendarField = Calendar.WEEK_OF_YEAR;
+      } else if ( timeSpanMillis <= DECADE.__millis ) {
+         calendarField = Calendar.MONTH;
+      }
+      return calendarField;
+   }
+
+   static private int getOffsetCalendarAmount( final long timeSpanMillis ) {
+      if ( timeSpanMillis <= HOUR.__millis ) {
+         if ( timeSpanMillis >= HOUR.__millis / 2 ) {
+            return 10; // 10 minutes
+         }
+         return 5; // 5 minutes
+      } else if ( timeSpanMillis <= DAY.__millis ) {
+         final long factor = timeSpanMillis / HOUR.__millis;
+         final long minutes = 10 * factor; // +- 10 minutes per hour
+         return (int)Math.max( 30, (minutes - minutes % 30) ); // +- 10 minutes to n*30 minutes
+      } else if ( timeSpanMillis <= WEEK.__millis ) {
+         final long factor = 2 * timeSpanMillis / DAY.__millis;
+         return (int)Math.max( 2, factor ); // +- 2 hours per day
+      } else if ( timeSpanMillis <= MONTH.__millis ) {
+         final long factor = 2 * timeSpanMillis / WEEK.__millis;
+         return (int)Math.max( 2, factor ); // +- 2 days per week
+      } else if ( timeSpanMillis <= YEAR.__millis ) {
+         final long factor = timeSpanMillis / MONTH.__millis;
+         return (int)Math.max( 1, factor ); // +- 1 week per month
+      } else if ( timeSpanMillis <= DECADE.__millis ) {
+         return 6; // +- 6 months
+      }
+      return 5;
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/CompoundTimeline.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/CompoundTimeline.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/CompoundTimeline.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/CompoundTimeline.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,156 @@
+package org.chboston.cnlp.timeline.timeline;
+
+import net.jcip.annotations.Immutable;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.nlp.annotation.relation.Relation;
+import org.chboston.cnlp.timeline.timespan.AbstractTimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import java.util.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 3/17/14
+ */
+@Immutable
+final public class CompoundTimeline extends AbstractTimeSpan implements Timeline {
+
+   // TODO use MutableTimeline and make this class mutable
+
+   final private Timeline _timelineDelegate;
+   final private Collection<Timeline> _timelines;
+
+   public CompoundTimeline( final String title, final long referenceMillis,
+                            final Collection<Timeline> timelines ) {
+      _timelines = Collections.unmodifiableCollection( timelines );
+      // List of all Time Spans.
+      final Collection<PointedTimeSpan> timeSpans = new HashSet<>();
+      final Collection<Entity> times = new HashSet<>();
+      for ( Timeline timeline : timelines ) {
+         timeSpans.addAll( timeline.getTimeSpans() );
+         times.addAll( timeline.getTimes() );
+      }
+      // Map of all Entities in TimeSpans
+      final Map<PointedTimeSpan, Collection<Entity>> timeSpanEntityMap
+            = new HashMap<>( timeSpans.size() );
+      final Map<Entity, Collection<Relation>> entityTlinkMap = new HashMap<>();
+      for ( PointedTimeSpan timeSpan : timeSpans ) {
+         final Collection<Entity> entities = new HashSet<>();
+         for ( Timeline timeline : timelines ) {
+            final Collection<Entity> timelineEntities = timeline.getEntities( timeSpan );
+            if ( timelineEntities != null && !timelineEntities.isEmpty() ) {
+               entities.addAll( timelineEntities );
+               for ( Entity entity : timelineEntities ) {
+                  final Collection<Relation> relations = timeline.getEntityEntityRelations( entity );
+                  if ( relations != null && !relations.isEmpty() ) {
+                     entityTlinkMap.put( entity, relations );
+                  }
+               }
+            }
+         }
+         timeSpanEntityMap.put( timeSpan, entities );
+      }
+      _timelineDelegate = new DefaultTimeline( title, referenceMillis, timeSpanEntityMap, entityTlinkMap, times );
+   }
+
+   /**
+    * @return the individual timelines that make up this compound timeline
+    */
+   public Collection<Timeline> getTimelines() {
+      return _timelines;
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getTitle() {
+      return _timelineDelegate.getTitle();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getReferenceMillis() {
+      return _timelineDelegate.getReferenceMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public List<PointedTimeSpan> getTimeSpans() {
+      return _timelineDelegate.getTimeSpans();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Relation> getEntityEntityRelations( final Entity entity ) {
+      return _timelineDelegate.getEntityEntityRelations( entity );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities( final PointedTimeSpan timeSpan ) {
+      return _timelineDelegate.getEntities( timeSpan );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities() {
+      return _timelineDelegate.getEntities();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getTimes() {
+      return _timelineDelegate.getTimes();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStartMillis() {
+      return _timelineDelegate.getStartMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStopMillis() {
+      return _timelineDelegate.getStopMillis();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isFuzzyDate() {
+      return _timelineDelegate.isFuzzyDate();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Iterator<PointedTimeSpan> iterator() {
+      return _timelineDelegate.iterator();
+   }
+
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/DefaultTimeline.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/DefaultTimeline.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/DefaultTimeline.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/DefaultTimeline.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,197 @@
+package org.chboston.cnlp.timeline.timeline;
+
+import net.jcip.annotations.Immutable;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.nlp.annotation.relation.Relation;
+import org.chboston.cnlp.nlp.util.CollectionMap;
+import org.chboston.cnlp.timeline.timespan.AbstractTimeSpan;
+import org.chboston.cnlp.timeline.timespan.DefaultTimeSpan;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.TimeSpanPlus;
+import org.chboston.cnlp.timeline.timespan.plus.TimeSpanPlusComparator;
+
+import java.util.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/2/13
+ */
+@Immutable
+final public class DefaultTimeline extends AbstractTimeSpan implements Timeline {
+
+   // TODO make this a wrapper around a MutableTimeline created with Collections.unmodifiableCollection( parm )
+   // throw UnsupportedOperationException on add, set methods
+
+   final private String _title;
+   // reference time for the timeline.  Used for searching, etc.  Basically the "docTime" or Current Date.
+   final private long _referenceMillis;
+
+   // List of all Time Spans.
+   final private List<PointedTimeSpan> _timeSpanList;
+   // Every timeline has/is a time span with start and end times
+   final private TimeSpan _timeSpanDelegate;
+
+   final private Map<PointedTimeSpan, Collection<Entity>> _timeSpanEntityMap;
+   final private Map<Entity, Collection<Relation>> _entityTlinkMap;
+
+   final private Collection<Entity> _times;
+
+
+   DefaultTimeline( final String title, final long referenceMillis,
+                    final Map<PointedTimeSpan, Collection<Entity>> timeSpanEntityMap,
+                    final Map<Entity, Collection<Relation>> entityTlinkMap,
+                    final Collection<Entity> times ) {
+      _title = title;
+      final Calendar calendar = Calendar.getInstance();
+      calendar.setTimeInMillis( referenceMillis );
+      calendar.set( Calendar.HOUR_OF_DAY, 0 );
+      calendar.set( Calendar.MINUTE, 0 );
+      _referenceMillis = calendar.getTimeInMillis();
+      _timeSpanEntityMap = Collections.unmodifiableMap( timeSpanEntityMap );
+      _entityTlinkMap = Collections.unmodifiableMap( entityTlinkMap );
+      _times = Collections.unmodifiableCollection( times );
+      final List<PointedTimeSpan> timeSpanList = new ArrayList<>( timeSpanEntityMap.keySet() );
+      timeSpanList.remove( TimeSpanPlus.UNKNOWN_TIMESPAN_PLUS );
+      Collections.sort( timeSpanList, TimeSpanPlusComparator.getInstance() );
+      _timeSpanList = Collections.unmodifiableList( timeSpanList );
+      long start = Long.MAX_VALUE;
+      long end = Long.MIN_VALUE;
+      for ( TimeSpan timeSpan : timeSpanList ) {
+         start = Math.min( start, timeSpan.getStartMillis() );
+         end = Math.max( end, timeSpan.getStopMillis() );
+      }
+      _timeSpanDelegate = new DefaultTimeSpan( start, end );
+   }
+
+   DefaultTimeline( final String title, final long referenceMillis,
+                    final CollectionMap<PointedTimeSpan, Entity> timeSpanEntityMap,
+                    final CollectionMap<Entity, Relation> entityTlinkMap,
+                    final Collection<Entity> times ) {
+      _title = title;
+      final Calendar calendar = Calendar.getInstance();
+      calendar.setTimeInMillis( referenceMillis );
+      calendar.set( Calendar.HOUR_OF_DAY, 0 );
+      calendar.set( Calendar.MINUTE, 0 );
+      _referenceMillis = calendar.getTimeInMillis();
+      _timeSpanEntityMap = Collections.unmodifiableMap( (Map<PointedTimeSpan, Collection<Entity>>)timeSpanEntityMap );
+      _entityTlinkMap = Collections.unmodifiableMap( (Map<Entity, Collection<Relation>>)entityTlinkMap );
+      _times = Collections.unmodifiableCollection( times );
+      final List<PointedTimeSpan> timeSpanList = new ArrayList<>( _timeSpanEntityMap.keySet() );
+      timeSpanList.remove( TimeSpanPlus.UNKNOWN_TIMESPAN_PLUS );
+      Collections.sort( timeSpanList, TimeSpanPlusComparator.getInstance() );
+      _timeSpanList = Collections.unmodifiableList( timeSpanList );
+      long start = Long.MAX_VALUE;
+      long end = Long.MIN_VALUE;
+      for ( TimeSpan timeSpan : timeSpanList ) {
+         start = Math.min( start, timeSpan.getStartMillis() );
+         end = Math.max( end, timeSpan.getStopMillis() );
+      }
+      _timeSpanDelegate = new DefaultTimeSpan( start, end );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getTitle() {
+      return _title;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getReferenceMillis() {
+      return _referenceMillis;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public List<PointedTimeSpan> getTimeSpans() {
+      return _timeSpanList;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Relation> getEntityEntityRelations( final Entity entity ) {
+      final Collection<Relation> tlinks = _entityTlinkMap.get( entity );
+      if ( tlinks != null ) {
+         return tlinks;
+      }
+      return Collections.emptySet();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities( final PointedTimeSpan timeSpan ) {
+      return _timeSpanEntityMap.get( timeSpan );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities() {
+      final Collection<Entity> allEntities = new HashSet<>( _entityTlinkMap.keySet() );
+      for ( PointedTimeSpan timeSpan : _timeSpanList ) {
+         allEntities.addAll( getEntities( timeSpan ) );
+      }
+      final Collection<Entity> unknownTimeEntities = getEntities( TimeSpanPlus.UNKNOWN_TIMESPAN_PLUS );
+      if ( unknownTimeEntities != null ) {
+         allEntities.addAll( unknownTimeEntities );
+      }
+      return allEntities;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getTimes() {
+      return _times;
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStartMillis() {
+      return _timeSpanDelegate.getStartMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStopMillis() {
+      return _timeSpanDelegate.getStopMillis();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isFuzzyDate() {
+      return _timeSpanDelegate.isFuzzyDate();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Iterator<PointedTimeSpan> iterator() {
+      return _timeSpanList.iterator();
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/EventTextTimeline.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/EventTextTimeline.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/EventTextTimeline.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/EventTextTimeline.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,176 @@
+package org.chboston.cnlp.timeline.timeline;
+
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.nlp.annotation.relation.Relation;
+import org.chboston.cnlp.timeline.timespan.AbstractTimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import java.util.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 3/21/14
+ */
+public class EventTextTimeline extends AbstractTimeSpan implements Timeline {
+
+   final private String _title;
+   final private Timeline _fullTimeline;
+   final private Collection<String> _eventTexts;
+   final private List<PointedTimeSpan> _eventTextTimeSpans = new ArrayList<>();
+
+
+   public EventTextTimeline( final String title,
+                             final Timeline fullTimeline,
+                             final Collection<String> eventTexts ) {
+      _title = title;
+      _fullTimeline = fullTimeline;
+      _eventTexts = new ArrayList<>( eventTexts.size() );
+      for ( String text : eventTexts ) {
+         _eventTexts.add( text.toLowerCase() );
+      }
+      segregateByEventText( fullTimeline, _eventTexts );
+   }
+
+   public Collection<String> getEventTexts() {
+      return _eventTexts;
+   }
+
+   private void segregateByEventText( final Timeline fullTimeline, final Collection<String> eventTexts ) {
+      // The number of timespans in a timeline can be large, so sorting by event text should be done
+      // at instantiation rather than dynamically on-call
+      for ( PointedTimeSpan timeSpan : fullTimeline ) {
+         boolean added = false;
+         final Collection<Entity> entities = getEntities( timeSpan );
+         for ( Entity entity : entities ) {
+            final String entityText = entity.getSpannedText().toLowerCase();
+            for ( String text : eventTexts ) {
+               if ( entityText.contains( text ) ) {
+                  _eventTextTimeSpans.add( timeSpan );
+                  added = true;
+                  break;
+               }
+            }
+            if ( added ) {
+               break;
+            }
+         }
+      }
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getTitle() {
+      return _title;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getReferenceMillis() {
+      return _fullTimeline.getReferenceMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public List<PointedTimeSpan> getTimeSpans() {
+      return _eventTextTimeSpans;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Relation> getEntityEntityRelations( final Entity entity ) {
+      return _fullTimeline.getEntityEntityRelations( entity );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities( final PointedTimeSpan timeSpan ) {
+      // There should be few enough entities in a single timespan to do this dynamically
+      final Collection<Entity> allEntities = _fullTimeline.getEntities( timeSpan );
+      final Collection<Entity> textEntities = new HashSet<>();
+      for ( Entity entity : allEntities ) {
+         final String entityText = entity.getSpannedText().toLowerCase();
+         for ( String text : _eventTexts ) {
+            if ( entityText.contains( text ) ) {
+               textEntities.add( entity );
+               break;
+            }
+         }
+      }
+      return textEntities;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities() {
+      final Collection<Entity> allEntities = _fullTimeline.getEntities();
+      final Collection<Entity> textEntities = new HashSet<>();
+      for ( Entity entity : allEntities ) {
+         final String entityText = entity.getSpannedText().toLowerCase();
+         for ( String text : _eventTexts ) {
+            if ( entityText.contains( text ) ) {
+               textEntities.add( entity );
+               break;
+            }
+         }
+      }
+      return textEntities;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getTimes() {
+      return _fullTimeline.getTimes();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStartMillis() {
+      return _fullTimeline.getStartMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStopMillis() {
+      return _fullTimeline.getStopMillis();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isFuzzyDate() {
+      return _fullTimeline.isFuzzyDate();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Iterator<PointedTimeSpan> iterator() {
+      return _eventTextTimeSpans.iterator();
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/MutableTimeline.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/MutableTimeline.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/MutableTimeline.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/MutableTimeline.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,269 @@
+package org.chboston.cnlp.timeline.timeline;
+
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.nlp.annotation.relation.Relation;
+import org.chboston.cnlp.timeline.timespan.AbstractTimeSpan;
+import org.chboston.cnlp.timeline.timespan.DefaultTimeSpan;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.TimeSpanPlus;
+import org.chboston.cnlp.timeline.timespan.plus.TimeSpanPlusComparator;
+
+import java.util.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 3/21/14
+ */
+final public class MutableTimeline extends AbstractTimeSpan implements Timeline {
+
+   // TODO add mutable state methods (add, set) to interface Timeline, make MutableTimeline DefaultTimeline,
+   // Make DefaultTimeline UnmodifiableTimeline (or remove altogether)
+
+   final private String _title;
+   // reference time for the timeline.  Used for searching, etc.  Basically the "docTime" or Current Date.
+   private long _referenceMillis;
+
+   // List of all Time Spans.
+   final private List<PointedTimeSpan> _timeSpanList;
+   // Every timeline has/is a time span with start and end times
+   private TimeSpan _timeSpanDelegate;
+
+   final private Map<PointedTimeSpan, Collection<Entity>> _timeSpanEntityMap;
+   final private Map<Entity, Collection<Relation>> _entityTlinkMap;
+   final private Collection<Entity> _times;
+
+   MutableTimeline( final String title ) {
+      this( title, System.currentTimeMillis(),
+            new HashMap<PointedTimeSpan, Collection<Entity>>(),
+            new HashMap<Entity, Collection<Relation>>(), new HashSet<Entity>() );
+   }
+
+   MutableTimeline( final String title, final long referenceMillis,
+                    final Map<PointedTimeSpan, Collection<Entity>> timeSpanEntityMap,
+                    final Map<Entity, Collection<Relation>> entityTlinkMap, final Collection<Entity> times ) {
+      _title = title;
+      final Calendar calendar = Calendar.getInstance();
+      calendar.setTimeInMillis( referenceMillis );
+      calendar.set( Calendar.HOUR_OF_DAY, 0 );
+      calendar.set( Calendar.MINUTE, 0 );
+      _referenceMillis = calendar.getTimeInMillis();
+      _timeSpanEntityMap = timeSpanEntityMap;
+      _entityTlinkMap = entityTlinkMap;
+      _times = times;
+      final List<PointedTimeSpan> timeSpanList = new ArrayList<>( timeSpanEntityMap.keySet() );
+      Collections.sort( timeSpanList, TimeSpanPlusComparator.getInstance() );
+      _timeSpanList = timeSpanList;
+
+      long start = Long.MAX_VALUE;
+      long end = Long.MIN_VALUE;
+      for ( TimeSpan timeSpan : timeSpanList ) {
+         start = Math.min( start, timeSpan.getStartMillis() );
+         end = Math.max( end, timeSpan.getStopMillis() );
+      }
+      _timeSpanDelegate = new DefaultTimeSpan( start, end );
+   }
+
+   MutableTimeline( final String title, final long referenceMillis,
+                    final Map<PointedTimeSpan, Collection<Entity>> timeSpanEntityMap,
+                    final Map<Entity, Collection<Relation>> entityTlinkMap,
+                    final long startMillis, final long stopMillis, final Collection<Entity> times ) {
+      _title = title;
+      _referenceMillis = referenceMillis;
+      _timeSpanEntityMap = timeSpanEntityMap;
+      _entityTlinkMap = entityTlinkMap;
+      _times = times;
+      final List<PointedTimeSpan> timeSpanList = new ArrayList<>( timeSpanEntityMap.keySet() );
+      Collections.sort( timeSpanList, TimeSpanPlusComparator.getInstance() );
+      _timeSpanList = timeSpanList;
+      _timeSpanDelegate = new DefaultTimeSpan( startMillis, stopMillis );
+   }
+
+   public void setReferenceMillis( final long referenceMillis ) {
+      _referenceMillis = referenceMillis;
+      fireStateChanged();
+   }
+
+   public void setTimeSpan( final long startMillis, final long stopMillis ) {
+      _timeSpanDelegate = new DefaultTimeSpan( startMillis, stopMillis );
+      fireStateChanged();
+   }
+
+   public void addTimeSpans( final Collection<TimeSpanPlus> timeSpans ) {
+      _timeSpanList.addAll( timeSpans );
+      Collections.sort( _timeSpanList, TimeSpanPlusComparator.getInstance() );
+      fireStateChanged();
+   }
+
+   public void addEntities( final Map<TimeSpanPlus, Collection<Entity>> timeSpanEntityMap ) {
+      for ( Map.Entry<TimeSpanPlus, Collection<Entity>> entry : timeSpanEntityMap.entrySet() ) {
+         final Collection<Entity> entities = _timeSpanEntityMap.get( entry.getKey() );
+         if ( entities != null ) {
+            entities.addAll( entry.getValue() );
+         } else {
+            _timeSpanEntityMap.put( entry.getKey(), entry.getValue() );
+         }
+      }
+      fireStateChanged();
+   }
+
+   public void addEntityEntityRelations( final Map<Entity, Collection<Relation>> entityTlinkMap ) {
+      for ( Map.Entry<Entity, Collection<Relation>> entry : entityTlinkMap.entrySet() ) {
+         final Collection<Relation> relations = _entityTlinkMap.get( entry.getKey() );
+         if ( relations != null ) {
+            relations.addAll( entry.getValue() );
+         } else {
+            _entityTlinkMap.put( entry.getKey(), entry.getValue() );
+         }
+      }
+      fireStateChanged();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getTitle() {
+      return _title;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getReferenceMillis() {
+      return _referenceMillis;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public List<PointedTimeSpan> getTimeSpans() {
+      return _timeSpanList;
+   }
+
+   //   /**
+   //    * Collapse and connect time spans in such a way that only large discontiguous non-overlapping spans are returned
+   //    * @return spans made by overlapping all smaller time spans
+   //    */
+   //   public Collection<TimeSpan> getTimeSpanCoverage() {
+   //      final Collection<TimeSpan> addSpans = new HashSet<TimeSpan>();
+   //      final Collection<TimeSpan> removeSpans = new HashSet<TimeSpan>();
+   //      final int size = _timeSpanList.size();
+   //      for ( int i=0; i<size; i++ ) {
+   //         TimeSpan bigTimeSpan = new DefaultTimeSpan( _timeSpanList.get(i).getStartMillis(),
+   //                                                     _timeSpanList.get(i).getStopMillis() );
+   //         for ( int j=size-1; j>=0; j-- ) {
+   //            final TimeSpan checkTimeSpan = _timeSpanList.get(j);
+   //            if ( bigTimeSpan.getStartMillis() == checkTimeSpan.getStartMillis()
+   //                  && bigTimeSpan.getStopMillis() == checkTimeSpan.getStopMillis() ) {
+   //               continue;
+   //            }
+   //            if ( bigTimeSpan.contains( checkTimeSpan ) ) {
+   //               continue;
+   //            }
+   //            if ( checkTimeSpan.contains( bigTimeSpan ) ) {
+   //               bigTimeSpan = new DefaultTimeSpan( checkTimeSpan.getStartMillis(), checkTimeSpan.getStopMillis() );
+   //               continue;
+   //            }
+   //            if ( bigTimeSpan.overlaps( checkTimeSpan ) ) {
+   //               bigTimeSpan = new DefaultTimeSpan( Math.min( bigTimeSpan.getStartMillis(), checkTimeSpan.getStartMillis()),
+   //                                                  Math.max( bigTimeSpan.getStopMillis(), checkTimeSpan.getStopMillis()) );
+   //            }
+   //         }
+   //         for ( TimeSpan addSpan : addSpans ) {
+   //            if ( addSpan.contains( bigTimeSpan ) ) {
+   //               bigTimeSpan = addSpan;
+   //               continue;
+   //            }
+   //            if ( bigTimeSpan.contains( addSpan ) ) {
+   //               removeSpans.add( addSpan );
+   //            }
+   //            if ( addSpan.overlaps( bigTimeSpan ) ) {
+   //               bigTimeSpan = new DefaultTimeSpan( Math.min( bigTimeSpan.getStartMillis(), addSpan.getStartMillis()),
+   //                                                  Math.max( bigTimeSpan.getStopMillis(), addSpan.getStopMillis()) );
+   //               removeSpans.add( addSpan );
+   //            }
+   //         }
+   //         addSpans.removeAll( removeSpans );
+   //         removeSpans.clear();
+   //         addSpans.add( bigTimeSpan );
+   //      }
+   //      return addSpans;
+   //   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Relation> getEntityEntityRelations( final Entity entity ) {
+      final Collection<Relation> tlinks = _entityTlinkMap.get( entity );
+      if ( tlinks != null ) {
+         return tlinks;
+      }
+      return Collections.emptySet();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities( final PointedTimeSpan timeSpan ) {
+      return _timeSpanEntityMap.get( timeSpan );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities() {
+      return _entityTlinkMap.keySet();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getTimes() {
+      return _times;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStartMillis() {
+      return _timeSpanDelegate.getStartMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStopMillis() {
+      return _timeSpanDelegate.getStopMillis();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isFuzzyDate() {
+      return _timeSpanDelegate.isFuzzyDate();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Iterator<PointedTimeSpan> iterator() {
+      return _timeSpanList.iterator();
+   }
+
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/SectionedTimeline.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/SectionedTimeline.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/SectionedTimeline.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/SectionedTimeline.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,141 @@
+package org.chboston.cnlp.timeline.timeline;
+
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.nlp.annotation.relation.Relation;
+import org.chboston.cnlp.timeline.timespan.AbstractTimeSpan;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 3/21/14
+ */
+public class SectionedTimeline extends AbstractTimeSpan implements Timeline {
+
+   final private String _title;
+   final private Timeline _fullTimeline;
+   final private Collection<TimeSpan> _sections;
+   final private List<PointedTimeSpan> _sectionTimeSpans = new ArrayList<>();
+
+
+   public SectionedTimeline( final String title,
+                             final Timeline fullTimeline,
+                             final Collection<TimeSpan> sections ) {
+      _title = title;
+      _fullTimeline = fullTimeline;
+      _sections = sections;
+      segregateBySection( fullTimeline, sections );
+   }
+
+   private void segregateBySection( final Timeline fullTimeline, final Collection<TimeSpan> sections ) {
+      // The number of timespans in a timeline can be large, so sorting by event text should be done
+      // at instantiation rather than dynamically on-call
+      for ( PointedTimeSpan timeSpan : fullTimeline ) {
+         for ( TimeSpan section : sections ) {
+            if ( section.contains( timeSpan ) ) {
+               _sectionTimeSpans.add( timeSpan );
+               break;
+            }
+         }
+      }
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getTitle() {
+      return _title;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getReferenceMillis() {
+      return _fullTimeline.getReferenceMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public List<PointedTimeSpan> getTimeSpans() {
+      return _sectionTimeSpans;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Relation> getEntityEntityRelations( final Entity entity ) {
+      return _fullTimeline.getEntityEntityRelations( entity );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities( final PointedTimeSpan timeSpan ) {
+      return _fullTimeline.getEntities( timeSpan );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities() {
+      return _fullTimeline.getEntities();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStartMillis() {
+      return _fullTimeline.getStartMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStopMillis() {
+      return _fullTimeline.getStopMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getTimes() {
+      return _fullTimeline.getTimes();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isFuzzyDate() {
+      return _fullTimeline.isFuzzyDate();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Iterator<PointedTimeSpan> iterator() {
+      return _sectionTimeSpans.iterator();
+   }
+
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/SemanticTypeTimeline.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/SemanticTypeTimeline.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/SemanticTypeTimeline.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/SemanticTypeTimeline.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,157 @@
+package org.chboston.cnlp.timeline.timeline;
+
+import org.chboston.cnlp.nlp.annotation.classtype.SemanticClassType;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.nlp.annotation.relation.Relation;
+import org.chboston.cnlp.timeline.timespan.AbstractTimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import java.util.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 3/21/14
+ */
+public class SemanticTypeTimeline extends AbstractTimeSpan implements Timeline {
+
+   final private String _title;
+   final private Timeline _fullTimeline;
+   final private SemanticClassType _semanticClassType;
+   final private List<PointedTimeSpan> _semanticTimeSpans = new ArrayList<>();
+
+
+   public SemanticTypeTimeline( final String title,
+                                final Timeline fullTimeline,
+                                final SemanticClassType semanticClassType ) {
+      _title = title;
+      _fullTimeline = fullTimeline;
+      _semanticClassType = semanticClassType;
+      segregateBySemantics( fullTimeline, semanticClassType );
+   }
+
+   public SemanticClassType getSemanticClassType() {
+      return _semanticClassType;
+   }
+
+   private void segregateBySemantics( final Timeline fullTimeline, final SemanticClassType semanticClassType ) {
+      // The number of timespans in a timeline can be large, so sorting by semantic type should be done
+      // at instantiation rather than dynamically on-call
+      for ( PointedTimeSpan timeSpan : fullTimeline ) {
+         final Collection<Entity> entities = fullTimeline.getEntities( timeSpan );
+         for ( Entity entity : entities ) {
+            if ( entity.isClassType( semanticClassType ) ) {
+               _semanticTimeSpans.add( timeSpan );
+               break;
+            }
+         }
+      }
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public String getTitle() {
+      return _title;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getReferenceMillis() {
+      return _fullTimeline.getReferenceMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public List<PointedTimeSpan> getTimeSpans() {
+      return _semanticTimeSpans;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Relation> getEntityEntityRelations( final Entity entity ) {
+      return _fullTimeline.getEntityEntityRelations( entity );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities( final PointedTimeSpan timeSpan ) {
+      // There should be few enough entities in a single timespan to do this dynamically
+      final Collection<Entity> allEntities = _fullTimeline.getEntities( timeSpan );
+      final Collection<Entity> semanticEntities = new HashSet<>();
+      for ( Entity entity : allEntities ) {
+         if ( entity.isClassType( _semanticClassType ) ) {
+            semanticEntities.add( entity );
+         }
+      }
+      return semanticEntities;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getEntities() {
+      final Collection<Entity> allEntities = _fullTimeline.getEntities();
+      final Collection<Entity> semanticEntities = new HashSet<>();
+      for ( Entity entity : allEntities ) {
+         if ( entity.isClassType( _semanticClassType ) ) {
+            semanticEntities.add( entity );
+         }
+      }
+      return semanticEntities;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Entity> getTimes() {
+      return _fullTimeline.getTimes();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStartMillis() {
+      return _fullTimeline.getStartMillis();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public long getStopMillis() {
+      return _fullTimeline.getStopMillis();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isFuzzyDate() {
+      return _fullTimeline.isFuzzyDate();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Iterator<PointedTimeSpan> iterator() {
+      return _semanticTimeSpans.iterator();
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/Timeline.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/Timeline.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/Timeline.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/timeline/Timeline.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,56 @@
+package org.chboston.cnlp.timeline.timeline;
+
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.nlp.annotation.relation.Relation;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 3/17/14
+ */
+public interface Timeline extends TimeSpan, Iterable<PointedTimeSpan> {
+
+   /**
+    * @return title
+    */
+   String getTitle();
+
+   /**
+    * @return reference time for the timeline.  Used for searching, etc.  Basically the "docTime" or Current Date.
+    */
+   long getReferenceMillis();
+
+   /**
+    * @return List of all Time Spans.
+    */
+   List<PointedTimeSpan> getTimeSpans();
+
+   /**
+    * @param entity entity that may or may not be in temporal relations
+    * @return all temporal relations for the given entity
+    */
+   Collection<Relation> getEntityEntityRelations( Entity entity );
+
+   /**
+    * @param timeSpan some time span
+    * @return all entities associated with time span
+    */
+   Collection<Entity> getEntities( PointedTimeSpan timeSpan );
+
+   /**
+    * @return all entities associated with timeline
+    */
+   Collection<Entity> getEntities();
+
+   /**
+    * @return all timex associated with timeline
+    */
+   Collection<Entity> getTimes();
+
+
+}