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 [8/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/an...

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/DefaultCollectionMap.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/DefaultCollectionMap.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/DefaultCollectionMap.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/DefaultCollectionMap.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,279 @@
+package org.chboston.cnlp.nlp.util.collection;
+
+import java.util.*;
+
+/**
+ * @author SPF , chip-nlp
+ * @version %I%
+ * @since 9/23/2014
+ */
+final public class DefaultCollectionMap<K, V, T extends Collection<V>> implements CollectionMap<K, V, T> {
+
+   private final Map<K, T> _delegate;
+   private final CollectionCreator<V, T> _collectionCreator;
+   private final T EMPTY_COLLECTION;
+
+   public DefaultCollectionMap( final Map<K, T> delegate, final CollectionCreator<V, T> collectionCreator ) {
+      _delegate = delegate;
+      _collectionCreator = collectionCreator;
+      EMPTY_COLLECTION = collectionCreator.createCollection();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Iterator<Entry<K, T>> iterator() {
+      final Iterator<Map.Entry<K, T>> setIterator = _delegate.entrySet().iterator();
+      return new Iterator<Map.Entry<K, T>>() {
+         public boolean hasNext() {
+            return setIterator.hasNext();
+         }
+
+         public Map.Entry<K, T> next() {
+            final Map.Entry<K, T> next = setIterator.next();
+            return new Map.Entry<K, T>() {
+               public K getKey() {
+                  return next.getKey();
+               }
+
+               public T getValue() {
+                  return next.getValue();
+               }
+
+               public T setValue( final T value ) {
+                  return null;
+               }
+            };
+         }
+
+         public void remove() {
+         }
+      };
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<T> getAllCollections() {
+      return new HashSet<>( _delegate.values() );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public T getCollection( final K key ) {
+      final T collection = _delegate.get( key );
+      if ( collection != null ) {
+         return collection;
+      }
+      return EMPTY_COLLECTION;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public T obtainCollection( final K key ) {
+      T collection = _delegate.get( key );
+      if ( collection == null ) {
+         collection = _collectionCreator.createCollection();
+         _delegate.put( key, collection );
+      }
+      return collection;
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsValue( final K key, final V value ) {
+      final T collection = _delegate.get( key );
+      return collection != null && collection.contains( value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean placeValue( final K key, final V value ) {
+      T collection = _delegate.get( key );
+      if ( collection == null ) {
+         collection = _collectionCreator.createCollection();
+         _delegate.put( key, collection );
+      }
+      return collection.add( value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean placeMap( final Map<K, V> map ) {
+      boolean placedAny = false;
+      for ( Map.Entry<K, V> entry : map.entrySet() ) {
+         final boolean placed = placeValue( entry.getKey(), entry.getValue() );
+         placedAny = placedAny || placed;
+      }
+      return placedAny;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void removeValue( final K key, final V value ) {
+      final T collection = _delegate.get( key );
+      if ( collection == null ) {
+         return;
+      }
+      collection.remove( value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public <C extends Collection<V>> int addAllValues( final K key, final C values ) {
+      if ( values == null || values.isEmpty() ) {
+         return 0;
+      }
+      T collection = _delegate.get( key );
+      if ( collection == null ) {
+         collection = _collectionCreator.createCollection();
+         _delegate.put( key, collection );
+      }
+      final int oldSize = collection.size();
+      collection.addAll( values );
+      return collection.size() - oldSize;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void clearCollection( final K key ) {
+      final T collection = _delegate.get( key );
+      if ( collection != null ) {
+         collection.clear();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int size() {
+      return _delegate.size();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isEmpty() {
+      return _delegate.isEmpty();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsKey( final Object key ) {
+      return _delegate.containsKey( key );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsValue( final Object value ) {
+      return _delegate.containsValue( value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public T get( final Object key ) {
+      return _delegate.get( key );
+   }
+
+   // Modification Operations
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public T put( final K key, final T value ) {
+      return _delegate.put( key, value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public T remove( final Object key ) {
+      return _delegate.remove( key );
+   }
+
+
+   // Bulk Operations
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void putAll( final Map<? extends K, ? extends T> map ) {
+      _delegate.putAll( map );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void clear() {
+      _delegate.clear();
+   }
+
+
+   // Views
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<K> keySet() {
+      return _delegate.keySet();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<T> values() {
+      return _delegate.values();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<Map.Entry<K, T>> entrySet() {
+      return _delegate.entrySet();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Map<K, T> toSimpleMap() {
+      return _delegate;
+   }
+
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/EnumSetMap.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/EnumSetMap.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/EnumSetMap.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/EnumSetMap.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,215 @@
+package org.chboston.cnlp.nlp.util.collection;
+
+import java.util.*;
+
+/**
+ * @author SPF , chip-nlp
+ * @version %I%
+ * @since 9/23/2014
+ */
+final public class EnumSetMap<K extends Enum<K>, V> implements CollectionMap<K, V, Set<V>> {
+
+   private final CollectionMap<K, V, Set<V>> _delegate;
+
+
+   public EnumSetMap( final Class<K> enumType ) {
+      final EnumMap<K, Set<V>> enumMap = new EnumMap<>( enumType );
+      final CollectionCreator<V, Set<V>> creator = CollectionCreatorFactory.createSetCreator();
+      _delegate = new DefaultCollectionMap<>( enumMap, creator );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Iterator<Map.Entry<K, Set<V>>> iterator() {
+      return _delegate.iterator();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Set<V>> getAllCollections() {
+      return new HashSet<>( _delegate.values() );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<V> getCollection( final K key ) {
+      return _delegate.getCollection( key );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<V> obtainCollection( final K key ) {
+      return _delegate.obtainCollection( key );
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsValue( final K key, final V value ) {
+      return _delegate.containsValue( key, value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean placeValue( final K key, final V value ) {
+      return _delegate.placeValue( key, value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean placeMap( final Map<K, V> map ) {
+      return _delegate.placeMap( map );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void removeValue( final K key, final V value ) {
+      _delegate.removeValue( key, value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public <C extends Collection<V>> int addAllValues( final K key, final C collection ) {
+      return _delegate.addAllValues( key, collection );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void clearCollection( final K key ) {
+      _delegate.clearCollection( key );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int size() {
+      return _delegate.size();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isEmpty() {
+      return _delegate.isEmpty();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsKey( final Object key ) {
+      return _delegate.containsKey( key );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsValue( final Object value ) {
+      return _delegate.containsValue( value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<V> get( final Object key ) {
+      return _delegate.get( key );
+   }
+
+   // Modification Operations
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<V> put( final K key, final Set<V> value ) {
+      return _delegate.put( key, value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<V> remove( final Object key ) {
+      return _delegate.remove( key );
+   }
+
+
+   // Bulk Operations
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void putAll( final Map<? extends K, ? extends Set<V>> map ) {
+      _delegate.putAll( map );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void clear() {
+      _delegate.clear();
+   }
+
+
+   // Views
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<K> keySet() {
+      return _delegate.keySet();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Set<V>> values() {
+      return _delegate.values();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<Map.Entry<K, Set<V>>> entrySet() {
+      return _delegate.entrySet();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Map<K, Set<V>> toSimpleMap() {
+      return _delegate;
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/HashSetMap.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/HashSetMap.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/HashSetMap.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/HashSetMap.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,225 @@
+package org.chboston.cnlp.nlp.util.collection;
+
+import java.util.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 6/24/14
+ */
+final public class HashSetMap<K, V> implements CollectionMap<K, V, Set<V>> {
+
+   private final CollectionMap<K, V, Set<V>> _delegate;
+
+
+   public HashSetMap() {
+      final Map<K, Set<V>> hashMap = new HashMap<>();
+      final CollectionCreator<V, Set<V>> creator = CollectionCreatorFactory.createSetCreator();
+      _delegate = new DefaultCollectionMap<>( hashMap, creator );
+   }
+
+   /**
+    * @param size initial size of the HashSetMap
+    */
+   public HashSetMap( final int size ) {
+      final Map<K, Set<V>> hashMap = new HashMap<>( size );
+      final CollectionCreator<V, Set<V>> creator = CollectionCreatorFactory.createSetCreator();
+      _delegate = new DefaultCollectionMap<>( hashMap, creator );
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Iterator<Map.Entry<K, Set<V>>> iterator() {
+      return _delegate.iterator();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Set<V>> getAllCollections() {
+      return new HashSet<>( _delegate.values() );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<V> getCollection( final K key ) {
+      return _delegate.getCollection( key );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<V> obtainCollection( final K key ) {
+      return _delegate.obtainCollection( key );
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsValue( final K key, final V value ) {
+      return _delegate.containsValue( key, value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean placeValue( final K key, final V value ) {
+      return _delegate.placeValue( key, value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean placeMap( final Map<K, V> map ) {
+      return _delegate.placeMap( map );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void removeValue( final K key, final V value ) {
+      _delegate.removeValue( key, value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public <C extends Collection<V>> int addAllValues( final K key, final C collection ) {
+      return _delegate.addAllValues( key, collection );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void clearCollection( final K key ) {
+      _delegate.clearCollection( key );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int size() {
+      return _delegate.size();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isEmpty() {
+      return _delegate.isEmpty();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsKey( final Object key ) {
+      return _delegate.containsKey( key );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsValue( final Object value ) {
+      return _delegate.containsValue( value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<V> get( final Object key ) {
+      return _delegate.get( key );
+   }
+
+   // Modification Operations
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<V> put( final K key, final Set<V> value ) {
+      return _delegate.put( key, value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<V> remove( final Object key ) {
+      return _delegate.remove( key );
+   }
+
+
+   // Bulk Operations
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void putAll( final Map<? extends K, ? extends Set<V>> map ) {
+      _delegate.putAll( map );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void clear() {
+      _delegate.clear();
+   }
+
+
+   // Views
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<K> keySet() {
+      return _delegate.keySet();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<Set<V>> values() {
+      return _delegate.values();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<Map.Entry<K, Set<V>>> entrySet() {
+      return _delegate.entrySet();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Map<K, Set<V>> toSimpleMap() {
+      return _delegate;
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/ImmutableCollectionMap.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/ImmutableCollectionMap.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/ImmutableCollectionMap.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/nlp/util/collection/ImmutableCollectionMap.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,217 @@
+package org.chboston.cnlp.nlp.util.collection;
+
+import net.jcip.annotations.Immutable;
+
+import java.util.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/5/2014
+ */
+@Immutable
+final public class ImmutableCollectionMap<K, V, T extends Collection<V>> implements CollectionMap<K, V, T> {
+
+   private final CollectionMap<K, V, T> _protectedMap;
+
+   public ImmutableCollectionMap( final CollectionMap<K, V, T> collectionMap ) {
+      _protectedMap = collectionMap;
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Iterator<Map.Entry<K, T>> iterator() {
+      return _protectedMap.iterator();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<T> getAllCollections() {
+      return Collections.unmodifiableCollection( _protectedMap.values() );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public T getCollection( final K key ) {
+      // unfortunately, we cannot use an unmodifiable from Collections
+      return _protectedMap.getCollection( key );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public T obtainCollection( final K key ) {
+      return getCollection( key );
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsValue( final K key, final V value ) {
+      return _protectedMap.containsValue( key, value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean placeValue( final K key, final V value ) {
+      throw new UnsupportedOperationException();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean placeMap( final Map<K, V> map ) {
+      throw new UnsupportedOperationException();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void removeValue( final K key, final V value ) {
+      throw new UnsupportedOperationException();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public <C extends Collection<V>> int addAllValues( final K key, final C collection ) {
+      throw new UnsupportedOperationException();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void clearCollection( final K key ) {
+      throw new UnsupportedOperationException();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public int size() {
+      return _protectedMap.size();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean isEmpty() {
+      return _protectedMap.isEmpty();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsKey( final Object key ) {
+      return _protectedMap.containsKey( key );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean containsValue( final Object value ) {
+      return _protectedMap.containsValue( value );
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public T get( final Object key ) {
+      return _protectedMap.get( key );
+   }
+
+   // Modification Operations
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public T put( final K key, final T value ) {
+      throw new UnsupportedOperationException();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public T remove( final Object key ) {
+      throw new UnsupportedOperationException();
+   }
+
+
+   // Bulk Operations
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void putAll( final Map<? extends K, ? extends T> map ) {
+      throw new UnsupportedOperationException();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void clear() {
+      throw new UnsupportedOperationException();
+   }
+
+
+   // Views
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<K> keySet() {
+      return _protectedMap.keySet();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Collection<T> values() {
+      return _protectedMap.values();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Set<Map.Entry<K, T>> entrySet() {
+      return _protectedMap.entrySet();
+   }
+
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public Map<K, T> toSimpleMap() {
+      return Collections.unmodifiableMap( _protectedMap );
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/chronic/parser/AtParser.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/chronic/parser/AtParser.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/chronic/parser/AtParser.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/chronic/parser/AtParser.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,148 @@
+package org.chboston.cnlp.timeline.chronic.parser;
+
+import com.mdimension.jchronic.Options;
+import com.mdimension.jchronic.utils.Span;
+import org.chboston.cnlp.nlp.annotation.annotation.Annotation;
+import org.chboston.cnlp.nlp.annotation.attribute.Attribute;
+import org.chboston.cnlp.nlp.annotation.classtype.CustomClassType;
+import org.chboston.cnlp.nlp.annotation.classtype.TemporalClassType;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.timeline.timespan.DefaultTimeSpan;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+
+import java.util.*;
+
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/4/12
+ */
+final public class AtParser {
+
+   private AtParser() {
+   }
+
+   /**
+    * A hack to get the docTime.  Basically parses all date text using the standard java DateFormat.parse
+    * and returns the last parseable date - which assumes no absolutely specified future dates in the text.
+    *
+    * @param times collection of date entities
+    * @return a timex entity representing "now" as the last java parseable date in the list
+    */
+   // TODO getDocTimex called three times - can be down to two or one from createTimeline3
+//   static public Timex getDocTimex( final Collection<Entity> namedEntities, final Collection<Timex> times ) {
+   static public <T extends Entity> Entity getDocTimex( final Collection<T> namedEntities,
+                                                        final Collection<Entity> times ) {
+      for ( Entity entity : namedEntities ) {
+         if ( entity.isClassType( TemporalClassType.DOCTIME ) ) {
+            return entity;
+         }
+         final List<String> attributeNames = entity.getAttributeNames();
+         for ( String name : attributeNames ) {
+            final Attribute attribute = entity.getAttribute( name );
+            if ( attribute.getValue().equalsIgnoreCase( "DOCTIME" ) ) {
+               return entity;
+            }
+         }
+      }
+      for ( Entity timex : times ) {
+         final List<String> attributeNames = timex.getAttributeNames();
+         for ( String name : attributeNames ) {
+            final Attribute attribute = timex.getAttribute( name );
+            if ( attribute.getValue().equalsIgnoreCase( "DOCTIME" ) ) {
+               return timex;
+            }
+         }
+      }
+      Entity docTimex = null;
+      long maxMillis = StringDateParser.IMPROPER_DATE;
+      for ( Entity timex : times ) {
+         final String text = getTimexText( timex );
+         if ( text == null || text.isEmpty() ) {
+            continue;
+         }
+         final long millis = StringDateParser.getJavaDateMillis( text );
+         if ( millis > maxMillis ) {
+            docTimex = timex;
+            maxMillis = millis;
+         }
+      }
+      return docTimex;
+   }
+
+   static public long getTimexMillis( final Entity timex ) {
+      final String text = getTimexText( timex );
+      if ( text == null || text.isEmpty() ) {
+         return Calendar.getInstance().getTimeInMillis();
+      }
+      return StringDateParser.getJavaDateMillis( text );
+   }
+
+   static public Calendar getTimexCalendar( final Entity timex ) {
+      final long timexMillis = getTimexMillis( timex );
+      final Calendar calendar = Calendar.getInstance();
+      calendar.setTimeInMillis( timexMillis );
+      return calendar;
+   }
+
+   static public Collection<Entity> getValidTimes( final Iterable<Entity> times ) {
+      final Collection<Entity> timexSet = new HashSet<>();
+      for ( Entity timex : times ) {
+         final String text = getTimexText( timex );
+         if ( text != null && !text.isEmpty() ) {
+            timexSet.add( timex );
+         }
+      }
+      return timexSet;
+   }
+
+
+   static private String getTimexText( final Annotation timex ) {
+      if ( timex == null ) {
+         return "";
+      }
+      if ( timex.isClassType( TemporalClassType.DOCTIME ) ) {
+         return timex.getSpannedText();
+      }
+      final List<String> attributeNames = timex.getAttributeNames();
+      for ( String name : attributeNames ) {
+         final Attribute attribute = timex.getAttribute( name );
+         final String value = attribute.getValue().toUpperCase();
+         if ( value.equals( "DATE" ) ) {
+            return timex.getSpannedText();
+         } else if ( value.equals( "TIME" ) ) {
+            return timex.getSpannedText();
+         } else if ( value.equals( "DURATION" ) && timex.getSpannedText().contains( "last few" ) ) {
+            // Kludge because annotations often have duration of "last few XXX", which can become a parsed timespan
+            return timex.getSpannedText();
+         } else if ( value.equals( "DURATION" )
+                     && (timex.getSpannedText().startsWith( "the last " )
+                         || timex.getSpannedText().startsWith( "the next " )) ) {
+            // Kludge because annotations often have duration of "last few XXX", which can become a parsed timespan
+            return timex.getSpannedText();
+//         } else if ( attribute.getValue().equalsIgnoreCase( "QUANTIFIER" ) ) {
+//            return timex.getSpannedText();
+         } else if ( value.equals( "PREPOSTEXP" ) ) {
+            return timex.getSpannedText();
+//         } else if ( attribute.getValue().equalsIgnoreCase( "SET" ) ) {
+//            return timex.getSpannedText();
+         }
+      }
+      return "";
+   }
+
+
+   static public TimeSpan createTimeSpan( final String text, final Options chronicOptions ) {
+      if ( text == null || text.isEmpty() ) {
+         return null;
+      }
+      final Span chronicSpan = StringDateParser.getChronicSpan( chronicOptions, text );
+      if ( chronicSpan != null ) {
+         return new DefaultTimeSpan( chronicSpan, StringDateParser.isFuzzy( text ) );
+      }
+      return null;
+   }
+
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/chronic/parser/StringDateParser.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/chronic/parser/StringDateParser.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/chronic/parser/StringDateParser.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/chronic/parser/StringDateParser.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,742 @@
+package org.chboston.cnlp.timeline.chronic.parser;
+
+import com.mdimension.jchronic.Chronic;
+import com.mdimension.jchronic.Options;
+import com.mdimension.jchronic.utils.Span;
+import org.chboston.cnlp.timeline.timespan.CalendarUtil;
+
+import java.util.Calendar;
+import java.util.Date;
+import org.apache.log4j.Logger;
+
+import static java.util.Calendar.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/1/12
+ */
+final public class StringDateParser {
+
+   static private final Logger LOGGER = Logger.getLogger( "StringDateParser" );
+
+   static public final long IMPROPER_DATE = Long.MIN_VALUE;
+
+
+   private StringDateParser() {
+   }
+
+   static public Span getChronicSpan( final Options chronicOptions, final String text ) {
+      final String refinedText = refineTextForChronic( text );
+      try {
+         if ( refinedText.length() == 4 && isInteger( refinedText ) ) {
+            return getYearSpan( refinedText );
+         }
+         Span chronicSpan = Chronic.parse( refinedText, chronicOptions );
+         if ( chronicSpan != null ) {
+            final Span refinedSpan = refineChronicSpan( chronicSpan, chronicOptions, text );
+//            LOGGER.info( "Chronic parsed: " + text + " as: " + refinedText + " to: " + refinedSpan );
+            return refinedSpan;
+         } else {
+            LOGGER.warn( "Chronic couldn't parse: -" + text + "- as: -" + refinedText + "-" );
+            System.err.println( "Chronic couldn't parse: -" + text + "- as: -"  + refinedText + "-" );
+         }
+      } catch ( RuntimeException rtE ) {
+         LOGGER.error( "Chronic parsing " + refinedText + " : " + rtE.getLocalizedMessage() );
+      }
+      return null;
+   }
+
+   static public long getJavaDateMillis( final String text ) {
+      final String refinedText = refineTextForJavaDate( text );
+      if ( refinedText == null || refinedText.isEmpty() ) {
+         return IMPROPER_DATE;
+      }
+      try {
+         // DateFormat doesn't always parse correctly, so we use the deprecated Date.parse method
+         return Date.parse( refinedText );
+      } catch ( IllegalArgumentException iaE ) {
+         //
+      }
+      return IMPROPER_DATE;
+   }
+
+
+   static public boolean isFuzzy( final String dateText ) {
+      final String lowerText = dateText.toLowerCase();
+      return !lowerText.equals( refineFuzzy1( dateText ) )
+             || !lowerText.equals( refineAtThisTime( dateText ) )
+             || !lowerText.equals( refineDecades( dateText) )
+             || !lowerText.equals( refineInterOperative( dateText ) )
+             || !lowerText.equals( refinePreOperative( dateText ) )
+             || !lowerText.equals( refinePostOperative( dateText ) )
+             || !lowerText.equals( refinePeriOperative( dateText ) )
+             || !lowerText.equals( refineIndeterminate( dateText ) )
+             || !lowerText.equals( refineOr( dateText ) )
+             || !lowerText.equals( refineSeason( dateText ) )
+             || !lowerText.equals( refineBeginEnd( dateText ) );
+   }
+
+   static private String refineTextForJavaDate( final String dateText ) {
+      return getYearJanOneText( dateText );
+   }
+
+   static private String getYearJanOneText( final String text ) {
+      if ( text != null && text.length() == 4 && isInteger( text ) ) {
+         return "01/01/" + text;
+      }
+      return text;
+   }
+
+   static final private String[] MONTH_TRIS = { "jan", "feb", "mar", "apr", "may", "jun",
+                                                "jul", "aug", "sep", "oct", "nov", "dec" };
+   static final private String[] MONTH_FULLS = { "january", "february", "march", "april", "may", "june",
+                                                 "july", "august", "september", "october", "november", "december" };
+   static final private String[] DAY_NAMES = { "monday", "tuesday", "wednesday", "thursday",
+                                               "friday", "saturday", "sunday" };
+
+   static private String refineWhiteSpace( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      lowerText = lowerText.replace( '\r', ' ' );
+      lowerText = lowerText.replace( '\n', ' ' );
+      lowerText = lowerText.replaceAll( "\\s+", " " );
+      return lowerText.trim();
+   }
+
+
+   static private String refineFuzzy1( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      lowerText = lowerText.replaceAll( "\\bwithin\\b", "" );
+      lowerText = lowerText.replaceAll( "\\b(approximately|about|around|at least|some time in)\\b", "" );
+//      lowerText = lowerText.replaceAll( "\\bin\\b", "" );
+      lowerText = lowerText.replaceAll( "\\bor so\\b", "" );
+      lowerText = lowerText.replaceAll( "-some\\b", "" );
+      lowerText = lowerText.replaceAll( "^(<|before|prior to)\\b", "" );
+      lowerText = lowerText.replaceAll( "\\bfrom\\b", "" );
+      lowerText = lowerText.replaceAll( "\\b(|the) first week in\\b", "" );
+      lowerText = lowerText.replaceAll( "\\ba little (over|under)\\b", "" );
+      return lowerText;
+   }
+
+   // remove seconds from times like 14:33:51
+   static private String removeSeconds( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      if ( lowerText.contains( ":" ) && lowerText.length() > 9
+           && lowerText.charAt( lowerText.length() - 3 ) == ':' && lowerText.charAt( lowerText.length() - 5 ) == ':' ) {
+         return lowerText.substring( 0, lowerText.length() - 9 );
+      }
+      return lowerText;
+   }
+
+   // remove times from expressions like May 25, 1972 at 1:32 pm.   07:13 AM, 08 Mar 2010
+   static private String removeDateHours( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      if ( !lowerText.replaceAll( "\\b[0-9]?[0-9], \\d{4} at ", "" ).equals( lowerText ) ) {
+         final int atIndex = lowerText.indexOf( " at " );
+         return lowerText.substring( 0, atIndex );
+      }
+      if ( !lowerText.replaceAll( "\\b[0-9]?[0-9]:\\d{2}(| am| pm), [0-9]?[0-9] [a-z]* \\d{4}\\b", "" ).equals( lowerText ) ) {
+         final int commaIndex = lowerText.indexOf( "," );
+         return lowerText.substring( commaIndex+1 );
+      }
+      return lowerText;
+   }
+
+   // fix undecorated times like 0100 hours
+   static private String refineUndecoratedHours( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      if ( !lowerText.replaceAll( "\\b\\d{4} hours", "" ).equals( lowerText ) ) {
+         final int hourIndex = lowerText.indexOf( " hours" );
+         if ( hourIndex > 3 ) {
+            return lowerText.substring( 0, hourIndex-2 ) + ":" + lowerText.substring( hourIndex-2, hourIndex );
+         }
+      }
+      return lowerText;
+   }
+
+   // fix undecorated dates like 20110210 0909
+   static private String refineUndecoratedDate( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      if ( !lowerText.replaceAll( "\\b\\d{8} \\d{4}\\b", "" ).equals( lowerText ) ) {
+         return lowerText.substring( 4, 6 ) + "/" + lowerText.substring( 6, 8 ) + "/" + lowerText.substring( 8, 10 )
+                + " " + lowerText.substring( 11, 13 ) + ":" + lowerText.substring( 13 );
+      }
+      if ( !lowerText.replaceAll( "\\b\\d{8}\\b", "" ).equals( lowerText ) ) {
+         return lowerText.substring( 4, 6 ) + "/" + lowerText.substring( 6, 8 ) + "/" + lowerText.substring( 8, 10 )
+                + lowerText.substring( 10 );
+      }
+      return lowerText;
+   }
+
+   // kludge for "DAY, date"
+   static private String reOrderDayDate( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      for ( String day : DAY_NAMES ) {
+         if ( lowerText.startsWith( day ) ) {
+            final int spaceIndex = lowerText.indexOf( ' ' );
+            if ( spaceIndex < 0 ) {
+               return lowerText;
+            }
+            final String possibleMonther = lowerText.substring( spaceIndex + 1 );
+            for ( String monthTri : MONTH_TRIS ) {
+               if ( possibleMonther.startsWith( monthTri ) ) {
+                  // Friday, Mar 10 1976
+                  return possibleMonther;
+               }
+            }
+            for ( String monthFull : MONTH_FULLS ) {
+               if ( possibleMonther.startsWith( monthFull ) ) {
+                  // Friday, March 10 1976
+                  return possibleMonther;
+               }
+            }
+            break;
+         }
+      }
+      return lowerText;
+   }
+
+   // fix for dates like "9 May, 2010"
+   static private String reOrderDayMonth( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      String month = "";
+      for ( String monthTri : MONTH_TRIS ) {
+         if ( !lowerText.equals( lowerText.replaceAll( "\\b[0-9]?[0-9] " + monthTri + ", \\d{4}\\b", "" ) ) ) {
+            month = monthTri;
+            break;
+         }
+      }
+      for ( String monthFull : MONTH_FULLS ) {
+         if ( !lowerText.equals( lowerText.replaceAll( "\\b[0-9]?[0-9] " + monthFull + ", \\d{4}\\b", "" ) ) ) {
+            month = monthFull;
+            break;
+         }
+      }
+      if ( month.isEmpty() ) {
+         return lowerText;
+      }
+      final int monthIndex = lowerText.indexOf( month );
+      final int dayIndex = Math.max( 0, monthIndex-3 );
+      return month + " " + lowerText.substring( dayIndex, monthIndex ) + " "
+             + lowerText.substring( monthIndex + month.length() + 1 );
+   }
+
+   // fix for dates like 02-May-2014, May-02-2014
+   static private String refineDashMonthTri( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      int monthNum = 1;
+      for ( String monthTri : MONTH_TRIS ) {
+         if ( lowerText.startsWith( monthTri + "-" ) ) {
+            int nextDash = lowerText.indexOf( '-', 4 );
+            if ( nextDash < 0 ) {
+               nextDash = lowerText.length();
+            }
+            final String day = lowerText.substring( 4, nextDash );
+            if ( nextDash == lowerText.length() ) {
+               return monthNum + " " + day;
+            }
+            final String year = lowerText.substring( nextDash + 1 );
+            return monthNum + "/" + day + "/" + year;
+         }
+         final int dashIndex = lowerText.indexOf( "-" + monthTri + "-" );
+         if ( dashIndex > 0 ) {
+            final String day = lowerText.substring( 0, dashIndex );
+            final String year = lowerText.substring( dashIndex + 5 );
+            return monthNum + "/" + day + "/" + year;
+         }
+         monthNum++;
+      }
+      return lowerText;
+   }
+
+   // 15th of January      15 january 2010
+   static private String refineOfMonth( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      for ( String monthFull : MONTH_FULLS ) {
+         lowerText = lowerText.replaceAll( monthFull + ",", monthFull );
+         final String ofMonth = lowerText.replaceAll( " of " + monthFull, "OF_MONTH" );
+         final int ofIndex = ofMonth.indexOf( "OF_MONTH" );
+         if ( ofIndex > 0 ) {
+            if ( ofMonth.indexOf( ' ' ) < 0 ) {
+               return monthFull + " " + ofMonth.substring( 0, ofIndex );
+            }
+
+            return monthFull + " " + ofMonth.substring( 0, ofIndex ) + " " + ofMonth.substring( ofIndex+8 );
+         }
+      }
+      return lowerText;
+   }
+
+   static private String refineDashMonthFull( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      int monthNum = 1;
+      for ( String monthFull : MONTH_FULLS ) {
+         if ( lowerText.startsWith( monthFull + "-" ) ) {
+            int nextDash = lowerText.indexOf( '-', monthFull.length() + 1 );
+            if ( nextDash < 0 ) {
+               nextDash = lowerText.length();
+            }
+            final String day = lowerText.substring( monthFull.length(), nextDash );
+            if ( nextDash == lowerText.length() ) {
+               return monthNum + " " + day;
+            }
+            final String year = lowerText.substring( nextDash + 1 );
+            return monthNum + "/" + day + "/" + year;
+         }
+         monthNum++;
+      }
+      return lowerText;
+   }
+
+   static private String refineNumberEnds( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      lowerText = lowerText.replaceAll( "1st,?\\b", "1" );
+      lowerText = lowerText.replaceAll( "2nd,?\\b", "2" );
+      lowerText = lowerText.replaceAll( "3rd,?\\b", "3" );
+      lowerText = lowerText.replaceAll( "4th,?\\b", "4" );
+      lowerText = lowerText.replaceAll( "5th,?\\b", "5" );
+      lowerText = lowerText.replaceAll( "6th,?\\b", "6" );
+      lowerText = lowerText.replaceAll( "7th,?\\b", "7" );
+      lowerText = lowerText.replaceAll( "8th,?\\b", "8" );
+      lowerText = lowerText.replaceAll( "9th,?\\b", "9" );
+      lowerText = lowerText.replaceAll( "\\b10th,?\\b", "10" );
+      lowerText = lowerText.replaceAll( "\\b11th,?\\b", "11" );
+      lowerText = lowerText.replaceAll( "\\b12th,?\\b", "12" );
+      lowerText = lowerText.replaceAll( "\\b13th,?\\b", "13" );
+      return lowerText;
+   }
+
+   static private String refineDecades( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      String decadeless =  lowerText.replaceAll( "\\b\\d{4}'?s\\b", "DECADE" );
+      int decadeIndex = decadeless.indexOf( "DECADE" );
+      if ( decadeIndex >= 0 ) {
+         return lowerText.substring( decadeIndex, decadeIndex+4 );
+      }
+      decadeless =  lowerText.replaceAll( "\\b\\d{2}'?s\\b", "DECADE" );
+      decadeIndex = decadeless.indexOf( "DECADE" );
+      if ( decadeIndex >= 0 ) {
+         return "19" + lowerText.substring( decadeIndex, decadeIndex+2 );
+      }
+      lowerText = lowerText.replaceAll( "\\b(a|one) decade\\b", "10 years" );
+      lowerText = lowerText.replaceAll( "\\btwo decades\\b", "20 years" );
+      lowerText = lowerText.replaceAll( "\\three decades\\b", "30 years" );
+      return lowerText;
+   }
+
+
+   static private String refineAtThisTime( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      if ( lowerText.equals( "now" ) ) {
+         return "this day";
+      }
+      lowerText = lowerText.replaceAll( "\\b(|at )this (time|point|juncture)(| in time)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\b(|at )the current time\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\bthe time( |-)being\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\b(|at )(pres|curr|rec)ent(|ly)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\b(|most )(recent|late)ly\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\b(immediately|as soon as possible|at the current time)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\btoday'?s\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\b(|this )mid-?(day| ?afternoon)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\byesterday'?s\\b", "yesterday" );
+      lowerText = lowerText.replaceAll( "\\b(|this )mid-? ?morning\\b", "this morning" );
+      lowerText = lowerText.replaceAll( "\\bthis a.m.?\\b", "this morning" );
+      lowerText = lowerText.replaceAll( "\\b(overnight|nighttime|evening)\\b", "night" );
+      lowerText = lowerText.replaceAll( "\\b(tonite|tonight)\\b", "this night" );
+      return lowerText;
+   }
+
+   static private String refineInterOperative( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      if ( lowerText.equals( "operative" ) ) {
+         return "this day";
+      }
+      lowerText = lowerText.replaceAll( "\\b(|at )(|the )(|point of |time of )(|hospital )(admission|consultation)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\bint(er|ra)-? ?op(|erative|eratively)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\b(during|time of) (|the )(operation|surgery)\\b", "this day" );
+      // kludge - discharge is not a great thing to put here
+      lowerText = lowerText.replaceAll( "\\b(|at )(|the )(|point of |time of )(|hospital )(discharge)\\b", "this night" );
+      return lowerText;
+   }
+
+   static private String refinePreOperative( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      if ( lowerText.equals( "pre" ) ) {
+         return "this day";
+      }
+      lowerText = lowerText.replaceAll( "\\bpre-? ?op(|eration|erative|eratively)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\bpre-? ?surg(ical|ery)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\bpre-? ?(treatment|procedure)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\b(before|prior to) (|the )(operation|surgery)\\b", "this day" );
+      return lowerText;
+   }
+
+   static private String refinePostOperative( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      if ( lowerText.equals( "post" ) ) {
+         return "this day";
+      }
+      lowerText = lowerText.replaceAll( "\\bpost-? ?op(|eration|erative|eratively)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\bpost-? ?(treatment|procedure|hospitalization)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\bpost-? ?surg(ical|ery)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\b(after|following) (|the )(operation|surgery)\\b", "this day" );
+      // kludge for expressions like "3-4 weeks postop"
+      if ( lowerText.endsWith( "this day" ) ) {
+         return "this day";
+      }
+      return lowerText;
+   }
+
+   static private String refinePeriOperative( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      if ( lowerText.equals( "peri" ) ) {
+         return "this day";
+      }
+      lowerText = lowerText.replaceAll( "\\bperi?-? ?op(|eration|erative|eratively)\\b", "this day" );
+      lowerText = lowerText.replaceAll( "\\bperi?-? ?procedural\\b", "this day" );
+      return lowerText;
+   }
+
+   static private String refineIndeterminate( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      lowerText = lowerText.replaceAll( "\\b(|a )couple(| of)\\b", "two" );
+      lowerText = lowerText.replaceAll( "\\b(|a )few\\b", "three" );
+      lowerText = lowerText.replaceAll( "\\b(several|some)\\b", "three" );
+      lowerText = lowerText.replaceAll( "\\bmany|(, many)\\b", "five" );
+      return lowerText;
+   }
+
+   static private String refineOr( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      lowerText = lowerText.replaceAll( "\\b(1|one)-? ?(to|or)-? ?(2|two)\\b", "two" );
+      lowerText = lowerText.replaceAll( "\\b(1|one)-? ?(to|or)-? ?(3|three)\\b", "three" );
+      lowerText = lowerText.replaceAll( "\\b(2|two)-? ?(to|or)-? ?(3|three)\\b", "three" );
+      lowerText = lowerText.replaceAll( "\\b(3|three)-? ?(to|or)-? ?(4|four)\\b", "four" );
+      lowerText = lowerText.replaceAll( "\\b(4|four)-? ?(to|or)-? ?(5|five)\\b", "five" );
+      lowerText = lowerText.replaceAll( "\\b(4|four)-? ?(to|or)-? ?(6|six)\\b", "five" );
+      lowerText = lowerText.replaceAll( "\\b(4|four)-? ?(to|or)-? ?(8|eight)\\b", "six" );
+      lowerText = lowerText.replaceAll( "\\b(5|five)-? ?(to|or)-? ?(6|six)\\b", "six" );
+      lowerText = lowerText.replaceAll( "\\b(6|six)-? ?(to|or)-? ?(7|seven)\\b", "seven" );
+      lowerText = lowerText.replaceAll( "\\b(6|six)-? ?(to|or)-? ?(8|eight)\\b", "seven" );
+      lowerText = lowerText.replaceAll( "\\b(7|seven)-? ?(to|or)-? ?(8|eight)\\b", "eight" );
+      lowerText = lowerText.replaceAll( "\\b(9|nine)-? ?(to|or)-? ?(10|ten)\\b", "ten" );
+      lowerText = lowerText.replaceAll( "\\b(10|ten)-? ?(to|or)-? ?(12|twelve)\\b", "twelve" );
+      lowerText = lowerText.replaceAll( "\\b(1|one)-? ?(to|or)-? ?(12|twelve)\\b", "six" );
+      lowerText = lowerText.replaceAll( "\\b(17|seventeen)-? ?(to|or)-? ?(18|eighteen)\\b", "18" );
+      lowerText = lowerText.replaceAll( "\\b(20|twenty)-? ?some\\b", "20" );
+      final String orYears = lowerText.replaceAll( "\\b\\d{4} or \\d{4}\\b", "YEAR_OR" );
+      final int orIndex = orYears.indexOf( "YEAR_OR" );
+      if ( orIndex >= 0 ) {
+         return lowerText.substring( 0,orIndex+5 );
+      }
+//      lowerText = lowerText.replaceAll( "\\b1-\\b", "one" );
+      return lowerText;
+   }
+
+   static private String refineSeason( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      lowerText = lowerText.replaceAll( "\\b(|the|this|last) ?(|early-? ?|mid-? ?|late-? ?)winter ?(|of)(| ?this year)\\b", "december 21 " );
+      lowerText = lowerText.replaceAll( "\\b(|the|this|last) ?(|early-? ?|mid-? ?|late-? ?)spring ?(|of)(| ?this year)\\b", "march 20 " );
+      lowerText = lowerText.replaceAll( "\\b(|the|this|last) ?(|early-? ?|mid-? ?|late-? ?)summer ?(|of)(| ?this year)\\b", "june 21 " );
+      lowerText = lowerText.replaceAll( "\\b(|the|this|last) ?(|early-? ?|mid-? ?|late-? ?)fall ?(|of)(| ?this year)\\b", "september 22 " );
+      lowerText = lowerText.replaceAll( "\\b(|the|this|last) ?(|early-? ?|mid-? ?|late-? ?)autumn ?(|of)(| ?this year)\\b", "september 22 " );
+      return lowerText;
+   }
+
+   static private String refineBeginEnd( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      lowerText = lowerText.replaceAll( "\\b(|at |in )(|the )beginning of (the|this) year\\b", "january 1" );
+      lowerText = lowerText.replaceAll( "\\b(|at |in )(|the )(end|latter part) of (the|this) year\\b", "september 22" );
+      lowerText = lowerText.replaceAll( "\\b(|at |in )(|the )(beginning|middle|end) of the day\\b", "this day" );
+//      lowerText = lowerText.replaceAll( "^(the |in |0 )", "" );
+      lowerText = lowerText.replaceAll( "^(the |0 )", "" );
+      lowerText = lowerText.replaceAll( "^(early |late |later |latter part of )(|the |this |in )", "" );
+      lowerText = lowerText.replaceAll( "\\b(|at |in )(|the )(beginning|middle|end) (|of )", "" );
+      return lowerText;
+   }
+
+   static private String refineTextForChronic( final String dateText ) {
+      String lowerText = dateText.toLowerCase();
+      lowerText = refineWhiteSpace( lowerText );
+      lowerText = refineInterOperative( lowerText );
+      lowerText = refinePreOperative( lowerText );
+      lowerText = refinePostOperative( lowerText );
+      lowerText = refinePeriOperative( lowerText );
+      lowerText = refineFuzzy1( lowerText );
+      if ( lowerText.endsWith( "," ) || lowerText.endsWith( "." ) ) {
+         lowerText = lowerText.substring( 0, lowerText.length() - 1 );
+      }
+      lowerText = removeDateHours( lowerText );
+      lowerText = removeSeconds( lowerText );
+      lowerText = refineUndecoratedHours( lowerText );
+      lowerText = refineUndecoratedDate( lowerText );
+      lowerText = lowerText.trim();
+      lowerText = reOrderDayDate( lowerText );
+      lowerText = reOrderDayMonth( lowerText );
+      lowerText = refineDashMonthTri( lowerText );
+      // 15th of January
+      lowerText = refineOfMonth( lowerText );
+      lowerText = refineDashMonthFull( lowerText );
+      lowerText = refineNumberEnds( lowerText );
+      lowerText = lowerText.replaceAll( "\\b24-? ?hours\\b", "day" );
+      lowerText = lowerText.replaceAll( "s' time\\b", "s" );
+      lowerText = refineDecades( lowerText );
+      lowerText = refineAtThisTime( lowerText );
+      lowerText = lowerText.replaceAll( "^day\\b", "a day" );
+      lowerText = refineIndeterminate( lowerText );
+      lowerText = refineOr( lowerText );
+      lowerText = refineSeason( lowerText );
+      // first/last XX in YY
+      lowerText = lowerText.replaceAll( "^(|the )(first|last) [a-z]* (in|of) ", "" );
+      // of this year
+      lowerText = lowerText.replaceAll( "\\b of this year\\b", "" );
+      // last / next / prior to
+      lowerText = lowerText.replaceAll( "^(over|in|during) the\\b", "this" );
+      lowerText = lowerText.replaceAll( "^(|the )(p|l)ast\\b", "this past" );
+      // kludge for 1-2 days in demo
+      lowerText = lowerText.replaceAll( "\\b(|in )(|the next )(1|one)-? ?(|to )-? ?(2|two) weeks\\b", "in 2 weeks" );
+      lowerText = refineBeginEnd( lowerText );
+      lowerText = lowerText.replaceAll( "^in\\b", "" );
+      lowerText = lowerText.replaceAll( "^at\\b", "" );
+      lowerText = lowerText.replaceAll( "\\byrs\\b", "years" );
+      lowerText = lowerText.replaceAll( "-days\\b", " days" );
+      lowerText = lowerText.replaceAll( "-month\\b", " month" );
+      lowerText = lowerText.replaceAll( "-year\\b", " year" );
+      lowerText = lowerText.replace( '"', ' ' );
+      lowerText = lowerText.replace( '=', ' ' );
+      lowerText = lowerText.replaceAll( "\\s+", " " );
+      return lowerText.trim();
+   }
+
+   static private int getNumber( final String dateText ) {
+      final String[] tokens = dateText.split( " " );
+      for ( String token : tokens ) {
+         if ( !token.isEmpty() ) {
+            try {
+               return Integer.parseInt( token );
+            } catch ( NumberFormatException nfE ) {
+               // continue
+            }
+         }
+      }
+      return Integer.MIN_VALUE;
+   }
+
+   static private boolean isPreSurgery( final String lowerText ) {
+      return !lowerText.equals( refinePreOperative( lowerText ) );
+   }
+
+   static private boolean isPostSurgery( final String lowerText ) {
+      return !lowerText.equals( refinePostOperative( lowerText ) );
+   }
+
+   static private boolean isIntraOperative( final String lowerText ) {
+      return !lowerText.equals( refineInterOperative( lowerText ) );
+   }
+
+   static private boolean isPeriOperative( final String lowerText ) {
+      return !lowerText.equals( refinePeriOperative( lowerText ) );
+   }
+
+
+   static private final String[] EARLY_LATE_KEYS = { "early", "beginning of", "late", "end of", "latter part of",
+                                                     "mid" };
+
+   static private Span createEarlyLateSpan( final Calendar beginCalendar, final Calendar endCalendar,
+                                            final int unit, final int offset, final String lowerText ) {
+      if ( lowerText.startsWith( "early" ) || lowerText.contains( "beginning of" ) ) {
+         endCalendar.add( unit, -1 * offset );
+      } else if ( lowerText.startsWith( "late" ) || lowerText.startsWith( "the late" )
+                  || lowerText.contains( "end of" ) || lowerText.contains( "latter part of" ) ) {
+         beginCalendar.add( unit, offset );
+      } else if ( (lowerText.startsWith( "mid" ) || lowerText.startsWith( "the mid" )) && offset > 1 ) {
+         beginCalendar.add( unit, offset / 2 );
+         endCalendar.add( unit, -1 * offset / 2 );
+      }
+      return new Span( beginCalendar, endCalendar );
+   }
+
+   static private Span refineChronicSpan( final Span chronicSpan,
+                                          final Options chronicOptions,
+                                          final String dateText ) {
+      final String lowerText = dateText.toLowerCase();
+      final Calendar beginCalendar = chronicSpan.getBeginCalendar();
+      final Calendar endCalendar = chronicSpan.getEndCalendar();
+      // preop / postop (be careful)
+      if ( isPreSurgery( lowerText ) ) {
+         beginCalendar.add( DAY_OF_YEAR, -7 );
+         return createEarlyLateSpan( beginCalendar, endCalendar, DAY_OF_YEAR, 3, lowerText );
+      } else if ( isPostSurgery( lowerText ) ) {
+         endCalendar.add( DAY_OF_YEAR, 30 );
+         return createEarlyLateSpan( beginCalendar, endCalendar, DAY_OF_YEAR, 15, lowerText );
+      } else if ( isPeriOperative( lowerText ) ) {
+         beginCalendar.add( DAY_OF_YEAR, -7 );
+         endCalendar.add( DAY_OF_YEAR, 30 );
+         return createEarlyLateSpan( beginCalendar, endCalendar, DAY_OF_YEAR, 18, lowerText );
+      }
+      // adjust for season spans
+      else if ( lowerText.contains( "winter" ) ) {
+         endCalendar.set( MONTH, MARCH );
+         endCalendar.set( DAY_OF_MONTH, 20 );
+         endCalendar.add( YEAR, 1 );
+         return createEarlyLateSpan( beginCalendar, endCalendar, DAY_OF_YEAR, 30, lowerText );
+      } else if ( lowerText.contains( "spring" ) ) {
+         endCalendar.set( MONTH, JUNE );
+         endCalendar.set( DAY_OF_MONTH, 21 );
+         return createEarlyLateSpan( beginCalendar, endCalendar, DAY_OF_YEAR, 30, lowerText );
+      } else if ( lowerText.contains( "summer" ) ) {
+         endCalendar.set( MONTH, SEPTEMBER );
+         endCalendar.set( DAY_OF_MONTH, 22 );
+         return createEarlyLateSpan( beginCalendar, endCalendar, DAY_OF_YEAR, 30, lowerText );
+      } else if ( lowerText.contains( "fall" ) || lowerText.contains( "autumn" ) ) {
+         endCalendar.set( MONTH, DECEMBER );
+         endCalendar.set( DAY_OF_MONTH, 21 );
+         return createEarlyLateSpan( beginCalendar, endCalendar, DAY_OF_YEAR, 30, lowerText );
+         // kludge
+      } else if ( lowerText.contains( "end of the year" ) ) {
+         endCalendar.set( MONTH, DECEMBER );
+         endCalendar.set( DAY_OF_MONTH, 21 );
+         return createEarlyLateSpan( beginCalendar, endCalendar, DAY_OF_YEAR, 30, lowerText );
+      }
+      // adjust for "early march", "late april"
+      for ( String month : MONTH_FULLS ) {
+         if ( lowerText.endsWith( month ) ) {
+            for ( String earlyLate : EARLY_LATE_KEYS ) {
+               if ( lowerText.startsWith( earlyLate ) ) {
+                  return createEarlyLateSpan( beginCalendar, endCalendar, DAY_OF_YEAR, 14, lowerText );
+               }
+            }
+         }
+      }
+      // adjust for "last few" spans
+      if ( !lowerText.replaceAll( "\\b(|in|during|over )th(e |is )(l|p)ast", "" ).equals( lowerText )
+           || !lowerText.replaceAll( "\\b(last|past) (couple|few|several)", "" ).equals( lowerText ) ) {
+         final Span nowChronicSpan = Chronic.parse( "today", chronicOptions );
+         if ( nowChronicSpan != null ) {
+            return new Span( beginCalendar, nowChronicSpan.getEndCalendar() );
+         }
+      }
+      // adjust for "the last" spans
+      if ( lowerText.startsWith( "the next" ) ) {
+         final Span nowChronicSpan = Chronic.parse( "today", chronicOptions );
+         if ( nowChronicSpan != null ) {
+            return new Span( nowChronicSpan.getBeginCalendar(), endCalendar );
+         }
+      }
+      // adjust for "ago" spans
+      if ( lowerText.contains( "ago" ) || lowerText.contains( "before" ) || lowerText.contains( "prior" )
+           || lowerText.contains( "after" ) || lowerText.contains( "in " ) ) {
+         if ( lowerText.contains( "year" ) ) {
+            beginCalendar.add( Calendar.MONTH, -6 );
+            endCalendar.add( Calendar.MONTH, 6 );
+         } else if ( lowerText.contains( "month" ) ) {
+            beginCalendar.add( Calendar.DAY_OF_YEAR, -15 );
+            endCalendar.add( Calendar.DAY_OF_YEAR, 15 );
+         } else if ( lowerText.contains( "week" ) ) {
+            beginCalendar.add( Calendar.DAY_OF_YEAR, -3 );
+            endCalendar.add( Calendar.DAY_OF_YEAR, 3 );
+         } else if ( lowerText.contains( "day" ) ) {
+            // Day and hour are fine gradation that they shouldn't be adjusted by half
+            endCalendar.add( Calendar.HOUR_OF_DAY, 24 );
+         } else if ( lowerText.contains( "hours" ) ) {
+            // a large number of hours can represent days
+            final int totalHours = getNumber( lowerText );
+            if ( totalHours == Integer.MIN_VALUE || totalHours < 24 ) {
+               endCalendar.add( Calendar.MINUTE, 60 );
+            } else {
+               beginCalendar.add( Calendar.HOUR_OF_DAY, -totalHours % 24 );
+               endCalendar.add( Calendar.HOUR_OF_DAY, 24 );
+            }
+         } else if ( lowerText.contains( "hour" ) ) {
+            endCalendar.add( Calendar.MINUTE, 60 );
+         }
+         return new Span( beginCalendar, endCalendar );
+      }
+      // adjust for "first of" spans
+      if ( lowerText.startsWith( "first" ) && (lowerText.contains( " in " ) || lowerText.contains( " of " )) ) {
+         endCalendar.setTimeInMillis( beginCalendar.getTimeInMillis() );
+         if ( lowerText.contains( "year" ) ) {
+            endCalendar.add( Calendar.YEAR, 1 );
+         } else if ( lowerText.contains( "month" ) ) {
+            endCalendar.add( Calendar.MONTH, 1 );
+         } else if ( lowerText.contains( "week" ) ) {
+            endCalendar.add( Calendar.DAY_OF_YEAR, 7 );
+         } else if ( lowerText.contains( "day" ) ) {
+            // Day and hour are fine gradation that they shouldn't be adjusted by half
+            endCalendar.add( Calendar.DAY_OF_YEAR, 1 );
+         } else if ( lowerText.contains( "hours" ) ) {
+            // a large number of hours can represent days
+            final int totalHours = getNumber( lowerText );
+            endCalendar.add( Calendar.HOUR_OF_DAY, totalHours );
+         } else if ( lowerText.contains( "hour" ) ) {
+            endCalendar.add( Calendar.HOUR_OF_DAY, 1 );
+         }
+         return new Span( beginCalendar, endCalendar );
+      }
+      // adjust for decades
+      if ( !lowerText.equals( refineDecades( lowerText ) ) ) {
+         endCalendar.add( Calendar.YEAR, 10 );
+         return new Span( beginCalendar, endCalendar );
+      }
+      // adjust for "last of" spans
+      if ( lowerText.startsWith( "last" ) && (lowerText.contains( " in " ) || lowerText.contains( " of " )) ) {
+         beginCalendar.setTimeInMillis( endCalendar.getTimeInMillis() );
+         if ( lowerText.contains( "year" ) ) {
+            beginCalendar.add( Calendar.YEAR, -1 );
+         } else if ( lowerText.contains( "month" ) ) {
+            beginCalendar.add( Calendar.MONTH, -1 );
+         } else if ( lowerText.contains( "week" ) ) {
+            beginCalendar.add( Calendar.DAY_OF_YEAR, -7 );
+         } else if ( lowerText.contains( "day" ) ) {
+            // Day and hour are fine gradation that they shouldn't be adjusted by half
+            beginCalendar.add( Calendar.DAY_OF_YEAR, -1 );
+         } else if ( lowerText.contains( "hours" ) ) {
+            // a large number of hours can represent days
+            final int totalHours = getNumber( lowerText );
+            beginCalendar.add( Calendar.HOUR_OF_DAY, -totalHours );
+         } else if ( lowerText.contains( "hour" ) ) {
+            beginCalendar.add( Calendar.HOUR_OF_DAY, -1 );
+         }
+         return new Span( beginCalendar, endCalendar );
+      }
+
+      // Adjust for times set to midnight for a relative date textspan
+      if ( beginCalendar.get( DAY_OF_YEAR ) == endCalendar.get( DAY_OF_YEAR )
+           && beginCalendar.get( YEAR ) == endCalendar.get( YEAR ) ) {
+         if ( CalendarUtil.isMidnight( beginCalendar ) && endCalendar.get( HOUR_OF_DAY ) == 0
+              && endCalendar.get( MINUTE ) == 0 && endCalendar.get( SECOND ) == 1 ) {
+            beginCalendar.set( HOUR_OF_DAY, 12 );
+            // Remember that Chronic spans take seconds, not milliseconds
+            return new Span( beginCalendar.getTimeInMillis() / 1000, beginCalendar.getTimeInMillis() / 1000 );
+         }
+      }
+      return chronicSpan;
+   }
+
+
+   static private Span getYearSpan( final String text ) {
+      try {
+         int year = Integer.decode( text );
+         final Calendar begin = Calendar.getInstance();
+         begin.set( year, JANUARY, 1, 0, 0, 0 );
+         final Calendar end = Calendar.getInstance();
+         end.set( year + 1, JANUARY, 1, 0, 0, 0 );
+         return new Span( begin, end );
+      } catch ( NumberFormatException nfE ) {
+         // do nothing
+      }
+      return null;
+   }
+
+   // Java standard libraries should really have a method for this
+   static private boolean isInteger( final String text ) {
+      try {
+         Integer.decode( text );
+         return true;
+      } catch ( NumberFormatException nfE ) {
+         // do nothing
+      }
+      return false;
+   }
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/debug/InfoPanel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/debug/InfoPanel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/debug/InfoPanel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/debug/InfoPanel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,252 @@
+package org.chboston.cnlp.timeline.debug;
+
+import org.chboston.cnlp.gui.PositioningSplitPane;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.nlp.annotation.gui.EventDetailsTableModel;
+import org.chboston.cnlp.timeline.eventSelection.EventSelectionEvent;
+import org.chboston.cnlp.timeline.eventSelection.EventSelectionListener;
+import org.chboston.cnlp.timeline.gui.timespan.TimeSpanSelectionEvent;
+import org.chboston.cnlp.timeline.gui.timespan.TimeSpanSelectionListener;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+
+import javax.swing.*;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.TreePath;
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/2/13
+ */
+public class InfoPanel extends JPanel {
+
+
+   private final TimeSpanSelectionListener _timeSpanSelectionListener;
+   private final EventSelectionListener _eventSelectionListener;
+
+
+   public InfoPanel() {
+      super( new BorderLayout() );
+
+      _timeSpanSelectionListener = new InfoPanelTimeSpanListener();
+      _eventSelectionListener = new InfoPanelEventListener();
+   }
+
+   public TimeSpanSelectionListener getSelectionListener() {
+      return _timeSpanSelectionListener;
+   }
+
+   public EventSelectionListener getEventSelectionListener() {
+      return _eventSelectionListener;
+   }
+
+
+   public void setTimeline( final Timeline timeline ) {
+      //      final EntityCollectionTableModel eventsTableModel = new EntityCollectionTableModel();
+      final EventDetailsTableModel detailsTableModel = new EventDetailsTableModel();
+      //      final JTable entityTable = new JTable( eventsTableModel );
+      final JTable eventDetailsTable = new JTable( detailsTableModel );
+      //      final ListSelectionListener listSelectionListener = new EntitySelectionHandler( eventsTableModel,
+      //                                                                                      detailsTableModel );
+      //      entityTable.getSelectionModel().addListSelectionListener( listSelectionListener );
+      //      final PositioningSplitPane tablePanel = new PositioningSplitPane( JSplitPane.VERTICAL_SPLIT,
+      //                                                                        new JScrollPane( entityTable ),
+      //                                                                        new JScrollPane( relationTable ) );
+      //      tablePanel.setOneTouchExpandable( true );
+      //      tablePanel.setDividerLocation( 0.5d );
+      final TimeSpanTreeModel treeModel = new TimeSpanTreeModel( timeline );
+      final JTree tree = new JTree( treeModel );
+      tree.setRootVisible( false );
+      //      final TreeSelectionListener treeSelectionListener = new TimeSpanSelectionHandler( eventsTableModel,
+      //                                                                                        detailsTableModel );
+      final TreeSelectionListener treeSelectionListener = new TimeSpanSelectionHandler( detailsTableModel );
+      tree.addTreeSelectionListener( treeSelectionListener );
+      final PositioningSplitPane splitPane = new PositioningSplitPane( JSplitPane.HORIZONTAL_SPLIT,
+            new JScrollPane( tree ),
+            new JScrollPane( eventDetailsTable ) );
+      //                                                                       tablePanel );
+      splitPane.setOneTouchExpandable( true );
+      splitPane.setDividerLocation( 0.3d );
+      add( splitPane, BorderLayout.CENTER );
+   }
+
+
+   private class TimeSpanSelectionHandler implements TreeSelectionListener {
+      final private EventDetailsTableModel __detailsTableModel;
+
+      private TimeSpanSelectionHandler( final EventDetailsTableModel detailsTableModel ) {
+         __detailsTableModel = detailsTableModel;
+      }
+
+      public void valueChanged( final TreeSelectionEvent event ) {
+         final TreePath selectedPath = event.getNewLeadSelectionPath();
+         if ( selectedPath == null ) {
+            return;
+         }
+         final Object selection = selectedPath.getLastPathComponent();
+         if ( selection instanceof TimeSpanTreeModel.FunkyTimeSpanNode ) {
+            __detailsTableModel.setEntity( null );
+         } else if ( selection instanceof TimeSpanTreeModel.EntityNode ) {
+            final Entity entity = ((TimeSpanTreeModel.EntityNode)selection).getEntity();
+            __detailsTableModel.setEntity( entity );
+         } else {
+            __detailsTableModel.setEntity( null );
+         }
+      }
+   }
+
+
+   //   private class TimeSpanSelectionHandler implements TreeSelectionListener {
+   //      final private EntityCollectionTableModel __eventsTableModel;
+   //      final private EventDetailsTableModel __detailsTableModel;
+   //      private TimeSpanSelectionHandler( final EntityCollectionTableModel eventsTableModel,
+   //                                        final EventDetailsTableModel detailsTableModel ) {
+   //         __eventsTableModel = eventsTableModel;
+   //         __detailsTableModel = detailsTableModel;
+   //      }
+   //
+   //      public void valueChanged( final TreeSelectionEvent event ) {
+   //         final TreePath selectedPath = event.getNewLeadSelectionPath();
+   //         if ( selectedPath == null ) {
+   //            return;
+   //         }
+   //         final Object selection = selectedPath.getLastPathComponent();
+   //         if ( selection instanceof TimeSpanTreeModel.FunkyTimeSpanNode ) {
+   //            final java.util.List<Entity> eventList = ((TimeSpanTreeModel.FunkyTimeSpanNode)selection).getEvents();
+   //            __eventsTableModel.setEntityList( eventList );
+   //            __detailsTableModel.setEntity( null );
+   //         } else if ( selection instanceof TimeSpanTreeModel.EntityNode ) {
+   //            final Entity entity = ((TimeSpanTreeModel.EntityNode)selection).getEntity();
+   //            final java.util.List<Entity> eventList = Arrays.asList( entity );
+   //            __eventsTableModel.setEntityList( eventList );
+   //            __detailsTableModel.setEntity( entity );
+   //         } else {
+   //            __eventsTableModel.setEntityList( null );
+   //            __detailsTableModel.setEntity( null );
+   //         }
+   //      }
+   //   }
+
+   //   private class EntitySelectionHandler implements ListSelectionListener {
+   //      final private EntityCollectionTableModel __eventsTableModel;
+   //      final private EventDetailsTableModel __detailsTableModel;
+   //      private EntitySelectionHandler( final EntityCollectionTableModel eventsTableModel,
+   //                                      final EventDetailsTableModel detailsTableModel ) {
+   //         __eventsTableModel = eventsTableModel;
+   //         __detailsTableModel = detailsTableModel;
+   //      }
+   //      public void valueChanged( final ListSelectionEvent event ) {
+   //         if ( event.getValueIsAdjusting() ) {
+   //            return;
+   //         }
+   //         final Object source = event.getSource();
+   //         if ( !(source instanceof ListSelectionModel) ) {
+   //            return;
+   //         }
+   //         final int leadIndex = ((ListSelectionModel) source).getLeadSelectionIndex();
+   //         if ( leadIndex >= 0 ) {
+   //            final Entity entity = __eventsTableModel.getEntity( leadIndex );
+   //            __detailsTableModel.setEntity( entity );
+   //         } else {
+   //            __detailsTableModel.setEntity( null );
+   //         }
+   //      }
+   //   }
+
+
+   private class InfoPanelTimeSpanListener implements TimeSpanSelectionListener {
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public void valueChanged( final TimeSpanSelectionEvent event ) {
+         //         if ( event == null || event.getValueIsAdjusting() ) {
+         //            return;
+         //         }
+         //         final Object source = event.getSource();
+         //         if ( source == null || source.equals( InfoPanel.this ) ) {
+         //            return;
+         //         }
+         //         final Collection<TimeSpan> timeSpans = event.getSelectedTimeSpans();
+         //         if ( timeSpans.isEmpty() ) {
+         //            setText( "" );
+         //         } else {
+         //            final Collection<String> texts = event.getSelectionTexts();
+         //            final StringBuilder sb = new StringBuilder();
+         //            sb.append( HEADER );
+         //            for ( TimeSpan timeSpan : timeSpans ) {
+         //               if ( timeSpan instanceof TimeSpanPlus ) {
+         //                  appendFunkyTimeSpan( sb, source, (TimeSpanPlus)timeSpan, texts );
+         //                  System.out.println("Texts");
+         //                  for ( String text : texts ) {
+         //                     System.out.println( "   " + text );
+         //                  }
+         //                  final JTimelineComponent comp = (JTimelineComponent)source;
+         //                  final DefaultTimeline timeline = comp.getModel();
+         //                  final Set<Relation> tlinks = timeline.getTlinks( (TimeSpanPlus)timeSpan );
+         //                  final List<Relation> tlinkList = new ArrayList<Relation>( tlinks );
+         //                  Collections.sort( tlinkList, TlinkRefiner.INSTANCE );
+         //                  final Set<Entity> usedEntities = new HashSet<Entity>();
+         //                  System.out.println("Tlinks");
+         //                  for ( Relation tlink : tlinkList ) {
+         //                     if ( usedEntities.contains( tlink.getFirstEntity() ) ) {
+         //                        continue;
+         //                     }
+         //                     final Attribute tlinkAttribute = tlink.getAttribute( "Relation Type" );
+         //                     if ( tlinkAttribute == null ) {
+         //                        System.out.print("   Equal ");
+         //                     } else {
+         //                        System.out.print( "   " + tlinkAttribute.getValue() );
+         //                     }
+         //                     System.out.println( " " + tlink.toString() );
+         //
+         //                     final Entity entity = tlink.getFirstEntity();
+         //                     final Set<Relation> eventEvents = timeline.getEntityEntityLinks( entity );
+         //                     if ( eventEvents != null && !eventEvents.isEmpty() ) {
+         //                        System.out.println( "   EventEvents");
+         //                        final List<Relation> eventEventList = new ArrayList<Relation>( eventEvents );
+         //                        Collections.sort( eventEventList, TlinkRefiner.INSTANCE );
+         //                        final Set<Map.Entry<Entity,Entity>> usedEntityEntities = new HashSet<Map.Entry<Entity, Entity>>();
+         //                        for ( final Relation eventEvent : eventEventList ) {
+         //                           final Map.Entry<Entity,Entity> entry = new AbstractMap.SimpleEntry<Entity,Entity>( eventEvent.getFirstEntity(),
+         //                                                                                                              eventEvent.getSecondEntity() );
+         //                           if ( usedEntityEntities.contains( entry ) ) {
+         //                              continue;
+         //                           }
+         //                           final Attribute eeAttribute = eventEvent.getAttribute( "Relation Type" );
+         //                           if ( eeAttribute == null ) {
+         //                              System.out.print("      Equal ");
+         //                           } else {
+         //                              System.out.print("      " + eeAttribute.getValue() );
+         //                           }
+         //                           System.out.println( " " + eventEvent.toString() );
+         //                           usedEntityEntities.add( entry );
+         //                        }
+         //                     }
+         //                     usedEntities.add( entity );
+         //                  }
+         //               } else {
+         //                  appendTimeSpan( sb, timeSpan, texts );
+         //               }
+         //            }
+         //            sb.append( FOOTER );
+         //            setText( sb.toString() );
+         //         }
+      }
+   }
+
+
+   private class InfoPanelEventListener implements EventSelectionListener {
+      /**
+       * {@inheritDoc}
+       */
+      @Override
+      public void valueChanged( final EventSelectionEvent event ) {
+
+      }
+   }
+
+
+}

Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/debug/TimeSpanTreeModel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/debug/TimeSpanTreeModel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/debug/TimeSpanTreeModel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/debug/TimeSpanTreeModel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,294 @@
+package org.chboston.cnlp.timeline.debug;
+
+import org.chboston.cnlp.nlp.annotation.annotation.AnnotationTextComparator;
+import org.chboston.cnlp.nlp.annotation.coreference.CoreferenceChain;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 8/2/13
+ */
+public class TimeSpanTreeModel extends DefaultTreeModel {
+
+
+   public TimeSpanTreeModel( final Timeline timeline ) {
+      super( new TimelineRootNode( timeline ) );
+   }
+
+
+   static private class TimelineRootNode implements TreeNode {
+      final private String __name;
+      final private List<FunkyTimeSpanNode> __childNodes;
+
+      private TimelineRootNode( final Timeline timeline ) {
+         __name = timeline.getTitle();
+         __childNodes = new ArrayList<>();
+         for ( PointedTimeSpan timeSpanPlus : timeline ) {
+            __childNodes.add( new FunkyTimeSpanNode( timeline, this, timeSpanPlus ) );
+         }
+      }
+
+      public TreeNode getParent() {
+         return null;
+      }
+
+      public FunkyTimeSpanNode getChildAt( final int childIndex ) {
+         return __childNodes.get( childIndex );
+      }
+
+      public int getChildCount() {
+         return __childNodes.size();
+      }
+
+      public int getIndex( final TreeNode node ) {
+         if ( node instanceof FunkyTimeSpanNode ) {
+            return __childNodes.indexOf( node );
+         }
+         return -1;
+      }
+
+      public boolean getAllowsChildren() {
+         return false;
+      }
+
+      public boolean isLeaf() {
+         return false;
+      }
+
+      public Enumeration<FunkyTimeSpanNode> children() {
+         return new Enumeration<FunkyTimeSpanNode>() {
+            private int ___index = -1;
+
+            @Override
+            public boolean hasMoreElements() {
+               return ___index < getChildCount() - 2;
+            }
+
+            @Override
+            public FunkyTimeSpanNode nextElement() {
+               ___index++;
+               return getChildAt( ___index );
+            }
+         };
+      }
+
+      public String toString() {
+         return __name;
+      }
+
+      public Iterator<FunkyTimeSpanNode> iterator() {
+         return __childNodes.iterator();
+      }
+   }
+
+
+   //   static public class TimeSpanNode implements TreeNode {
+   //      private String __name = "Date?";
+   //      final private TimeSpan __timeSpan;
+   //      final private TimelineRootNode __parent;
+   //      final private List<FunkyTimeSpanNode> __childNodes;
+   //      private TimeSpanNode( final DefaultTimeline timeline, final TimelineRootNode timelineRootNode,
+   //                            final TimeSpan timeSpan, final List<TimeSpanPlus> timeSpans ) {
+   //         __parent = timelineRootNode;
+   //         __timeSpan = timeSpan;
+   //         __name = timeSpan.toString();
+   //         __childNodes = new ArrayList<FunkyTimeSpanNode>( timeSpans.size() );
+   //         Collections.sort( timeSpans, TimeSpanPlusComparator.getInstance() );
+   //         for ( TimeSpanPlus funkyTimeSpan : timeSpans ) {
+   //            __childNodes.add( new FunkyTimeSpanNode( timeline, this, funkyTimeSpan ) );
+   //         }
+   //      }
+   //      public TimeSpan getTimeSpan() {
+   //         return __timeSpan;
+   //      }
+   //      public TimelineRootNode getParent() {
+   //         return __parent;
+   //      }
+   //      public FunkyTimeSpanNode getChildAt( final int childIndex ) {
+   //         return __childNodes.get( childIndex );
+   //      }
+   //      public int getChildCount() {
+   //         return __childNodes.size();
+   //      }
+   //      public int getIndex( final TreeNode node ) {
+   //         if ( node instanceof FunkyTimeSpanNode ) {
+   //            return __childNodes.indexOf( node );
+   //         }
+   //         return -1;
+   //      }
+   //      public boolean getAllowsChildren() {
+   //         return false;
+   //      }
+   //      public boolean isLeaf() {
+   //         return false;
+   //      }
+   //      public Enumeration<FunkyTimeSpanNode> children() {
+   //         return new Enumeration<FunkyTimeSpanNode>() {
+   //            private int ___index = -1;
+   //            @Override
+   //            public boolean hasMoreElements() {
+   //               return ___index < getChildCount()-2;
+   //            }
+   //
+   //            @Override
+   //            public FunkyTimeSpanNode nextElement() {
+   //               ___index++;
+   //               return getChildAt( ___index );
+   //            }
+   //         };
+   //      }
+   //      public String toString() {
+   //         return __name;
+   //      }
+   //      public Iterator<FunkyTimeSpanNode> iterator() {
+   //         return __childNodes.iterator();
+   //      }
+   //   }
+
+
+   static public class FunkyTimeSpanNode implements TreeNode {
+      private String __name = "Overlaps";
+      final private TimelineRootNode __parent;
+      final private List<EntityNode> __childNodes;
+
+      private FunkyTimeSpanNode( final Timeline timeline, final TimelineRootNode timelineRootNode,
+                                 final PointedTimeSpan timeSpan ) {
+         // It makes sense to the reader to read "Timex relates to Event" in this tree format, so get reciprocal text
+         __parent = timelineRootNode;
+         __name = timeSpan.toString();
+         __childNodes = new ArrayList<>();
+         final Collection<Entity> entities = timeline.getEntities( timeSpan );
+         final List<Entity> entityList = new ArrayList<>( entities );
+         Collections.sort( entityList, AnnotationTextComparator.getInstance() );
+         for ( Entity entity : entityList ) {
+            __childNodes.add( new EntityNode( this, entity ) );
+         }
+      }
+
+      public List<Entity> getEvents() {
+         final List<Entity> eventList = new ArrayList<>();
+         for ( EntityNode node : __childNodes ) {
+            eventList.add( node.getEntity() );
+         }
+         return eventList;
+      }
+
+      public TimelineRootNode getParent() {
+         return __parent;
+      }
+
+      public EntityNode getChildAt( final int childIndex ) {
+         return __childNodes.get( childIndex );
+      }
+
+      public int getChildCount() {
+         return __childNodes.size();
+      }
+
+      public int getIndex( final TreeNode node ) {
+         if ( node instanceof EntityNode ) {
+            return __childNodes.indexOf( node );
+         }
+         return -1;
+      }
+
+      public boolean getAllowsChildren() {
+         return false;
+      }
+
+      public boolean isLeaf() {
+         return false;
+      }
+
+      public Enumeration<EntityNode> children() {
+         return new Enumeration<EntityNode>() {
+            private int ___index = -1;
+
+            @Override
+            public boolean hasMoreElements() {
+               return ___index < getChildCount() - 2;
+            }
+
+            @Override
+            public EntityNode nextElement() {
+               ___index++;
+               return getChildAt( ___index );
+            }
+         };
+      }
+
+      public String toString() {
+         return __name;
+      }
+
+      public Iterator<EntityNode> iterator() {
+         return __childNodes.iterator();
+      }
+   }
+
+
+   static public class EntityNode implements TreeNode {
+      static private final Pattern SPACE_PATTERN = Pattern.compile( "\\s\\s\\s+" );
+      private String __name;
+      private FunkyTimeSpanNode __parentNode;
+      final private Entity __entity;
+
+      private EntityNode( final FunkyTimeSpanNode rockinTimeSpan, final Entity entity ) {
+         __name = SPACE_PATTERN.matcher( entity.getSpannedText() ).replaceAll( " ... " ).trim().toLowerCase();
+         __parentNode = rockinTimeSpan;
+         __entity = entity;
+         if ( entity instanceof CoreferenceChain ) {
+            for ( Entity reference : (CoreferenceChain)entity ) {
+               __name = SPACE_PATTERN.matcher( reference.getSpannedText() ).replaceAll( " ... " ).trim().toLowerCase();
+               break;
+            }
+         }
+      }
+
+      public Entity getEntity() {
+         return __entity;
+      }
+
+      public TreeNode getChildAt( final int childIndex ) {
+         return null;
+      }
+
+      public int getChildCount() {
+         return 0;
+      }
+
+      public TreeNode getParent() {
+         return __parentNode;
+      }
+
+      public int getIndex( final TreeNode node ) {
+         return -1;
+      }
+
+      public boolean getAllowsChildren() {
+         return false;
+      }
+
+      public boolean isLeaf() {
+         return true;
+      }
+
+      public Enumeration children() {
+         return null;
+      }
+
+      public String toString() {
+         return __name;
+      }
+   }
+
+
+}