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 [12/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/gui/qaclipper/QaToAnafora.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaToAnafora.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaToAnafora.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QaToAnafora.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,184 @@
+package org.chboston.cnlp.timeline.gui.qaclipper;
+
+import org.chboston.cnlp.nlp.annotation.textspan.DefaultTextSpan;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 5/2/14
+ */
+public class QaToAnafora {
+
+
+ private String _comment = "";
+
+
+ public static void main( final String[] args ) {
+ if ( args.length != 1 ) {
+ System.err.println( "What the heck? Give me the path to a text file!" );
+ System.exit( 0 );
+ }
+ new QaToAnafora( args[ 0 ] );
+ }
+
+
+ private QaToAnafora( final String textFilePath ) {
+ final List<QaClip> qaClips = loadQaClips( textFilePath );
+ final QaClipAnaforaWriter writer = new QaClipAnaforaWriter();
+ writer.saveQaClips( textFilePath, qaClips );
+ }
+
+
+ public List<QaClip> loadQaClips( final String textFilePath ) {
+ _comment = "";
+ final List<QaClip> qaClips = new ArrayList<>();
+ final File file = new File( textFilePath + QaClipSaver.FILE_EXTENSION );
+ if ( !file.exists() ) {
+ return qaClips;
+ }
+ try {
+ final BufferedReader reader = new BufferedReader( new FileReader( file ) );
+ _comment = loadHeader( reader );
+ QaClip qaClip = loadQaClip( reader );
+ while ( qaClip != null ) {
+ qaClips.add( qaClip );
+ qaClip = loadQaClip( reader );
+ }
+ reader.close();
+ } catch ( IOException ioE ) {
+ System.err.println( "Couldn't load QaClips: " + ioE.getMessage() );
+ }
+ return qaClips;
+ }
+
+ private String loadHeader( final BufferedReader reader ) throws IOException {
+ final StringBuilder stringBuilder = new StringBuilder();
+ boolean readingComment = false;
+ String line = reader.readLine();
+ while ( line != null && !line.equals( QaClipSaver.HEADER_END ) ) {
+ line = reader.readLine();
+ if ( readingComment && !line.equals( QaClipSaver.HEADER_END ) ) {
+ stringBuilder.append( line ).append( "\n" );
+ } else if ( line.startsWith( "Comment: " ) ) {
+ stringBuilder.append( line.substring( 10 ) ).append( "\n" );
+ readingComment = true;
+ }
+ }
+ return stringBuilder.toString();
+ }
+
+ private QaClip loadQaClip( final BufferedReader reader ) throws IOException {
+ final QaClip qaClip = new QaClip();
+ String questionLine = reader.readLine();
+ if ( questionLine != null && questionLine.isEmpty() ) {
+ questionLine = reader.readLine();
+ }
+ if ( questionLine == null ) {
+ return null;
+ }
+ if ( !questionLine.startsWith( QaClipSaver.QUESTION_LABEL ) ) {
+ System.err.println( "Malformed Question: " + questionLine );
+ return null;
+ }
+ qaClip.setQuestion( questionLine.substring( QaClipSaver.QUESTION_LABEL.length() ) );
+ final String answerLine = reader.readLine();
+ if ( !answerLine.startsWith( QaClipSaver.ANSWER_LABEL ) ) {
+ System.err.println( "Malformed Answer: " + answerLine );
+ return null;
+ }
+ final String answer = answerLine.substring( QaClipSaver.ANSWER_LABEL.length() );
+ qaClip.setAnswer( answerLine.substring( QaClipSaver.ANSWER_LABEL.length() ) );
+ if ( answer.equals( QaClipSaver.NO_ANSWER ) ) {
+ // skip the next question separator
+ reader.readLine();
+ return qaClip;
+ }
+ final String confidenceLine = reader.readLine();
+ if ( !confidenceLine.startsWith( QaClipSaver.CONFIDENCE_LABEL ) ) {
+ System.err.println( "Malformed Confidence: " + confidenceLine );
+ return null;
+ }
+ qaClip.setConfidence( confidenceLine.substring( QaClipSaver.CONFIDENCE_LABEL.length() ) );
+ final String difficultyLine = reader.readLine();
+ if ( !difficultyLine.startsWith( QaClipSaver.DIFFICULTY_LABEL ) ) {
+ System.err.println( "Malformed Difficulty: " + difficultyLine );
+ return null;
+ }
+ qaClip.setDifficulty( difficultyLine.substring( QaClipSaver.DIFFICULTY_LABEL.length() ) );
+ final String docTimeRelLine = reader.readLine();
+ if ( !docTimeRelLine.startsWith( QaClipSaver.DOCTIMEREL_LABEL ) ) {
+ System.err.println( "Malformed DocTimeRel: " + docTimeRelLine );
+ return null;
+ }
+ qaClip.setQaDocTimeRel( docTimeRelLine.substring( QaClipSaver.DOCTIMEREL_LABEL.length() ) );
+ String textClipLine = reader.readLine();
+ while ( textClipLine != null && !textClipLine.equals( QaClipSaver.QA_CLIP_END ) ) {
+ if ( textClipLine.startsWith( QaClipSaver.TEXT_CLIP_LABEL ) ) {
+ final QaClip.TextClip textClip = loadTextClip( textClipLine
+ .substring( QaClipSaver.TEXT_CLIP_LABEL.length() ) );
+ qaClip.addTextClip( textClip );
+ }
+ textClipLine = reader.readLine();
+ }
+ return qaClip;
+ }
+
+ private QaClip.TextClip loadTextClip( final String textClipLine ) {
+ final String[] splits = textClipLine.split( "\\s+" );
+ if ( splits.length < 4 ) {
+ System.err.println( "Malformed Text Span: " + textClipLine );
+ return null;
+ }
+ final String[] startStop = splits[ 1 ].split( "," );
+ if ( startStop.length != 2 ) {
+ System.err.println( "Malformed Text Span: " + textClipLine );
+ return null;
+ }
+ String orderString = splits[ 0 ];
+ String subOrder = "";
+ final int orderDotIndex = splits[ 0 ].indexOf( '.' );
+ if ( orderDotIndex > 0 ) {
+ orderString = splits[ 0 ].substring( 0, orderDotIndex );
+ if ( splits[ 0 ].length() > orderDotIndex + 1 ) {
+ subOrder = splits[ 0 ].substring( orderDotIndex + 1 );
+ }
+ }
+ Integer order;
+ Integer start;
+ Integer stop;
+ try {
+ order = Integer.parseInt( orderString );
+ start = Integer.parseInt( startStop[ 0 ] );
+ stop = Integer.parseInt( startStop[ 1 ] );
+ } catch ( NumberFormatException nfE ) {
+ System.err.println( "Malformed Text Span: " + textClipLine );
+ return null;
+ }
+ final QaClip.TextClip textClip = new QaClip.TextClip( new DefaultTextSpan( start, stop ), order, subOrder );
+ textClip.setIsExactAnswer( splits[ 2 ].equals( QaClipSaver.EXACT_ANSWER_CODE ) );
+ textClip.setUseDocTimeRel( splits[ 3 ].equals( QaClipSaver.USE_DOCTIMEREL_CODE ) );
+ if ( splits.length > 4 ) {
+ int lastBarIndex = -1;
+ int nextBarIndex = -1;
+ for ( int i = 0; i < 5; i++ ) {
+ nextBarIndex = splits[ 4 ].indexOf( '|', lastBarIndex + 1 );
+ if ( nextBarIndex < 0 ) {
+ break;
+ }
+ if ( nextBarIndex != lastBarIndex + 1 ) {
+ textClip.setInfo( i, splits[ 4 ].substring( lastBarIndex + 1, nextBarIndex ) );
+ }
+ lastBarIndex = nextBarIndex;
+ }
+ }
+ return textClip;
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QuestionClipper.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QuestionClipper.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QuestionClipper.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/QuestionClipper.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,217 @@
+package org.chboston.cnlp.timeline.gui.qaclipper;
+
+import org.chboston.cnlp.gui.FileSelectionPanel;
+import org.chboston.cnlp.gui.FontResizeSlider;
+import org.chboston.cnlp.gui.GlobalHotkeyManager;
+import org.chboston.cnlp.gui.error.ErrorLabel;
+import org.chboston.cnlp.timeline.gui.qaclipper.gui.ClipperMainPanel;
+import org.chboston.cnlp.timeline.gui.qaclipper.gui.QaClipListList;
+import org.chboston.cnlp.timeline.gui.qaclipper.gui.QaClipListModel;
+
+import javax.swing.*;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/11/13
+ */
+public class QuestionClipper {
+
+ static private final String TEXT_DOC_PATH
+ = "C:\\Spiffy\\Data\\THYME2\\Gold_Batch_08\\ID008_clinic_024.txt";
+
+ final private QaClipLoader _qaClipLoader = new QaClipLoader();
+ final private QaClipSaver _qaClipSaver = new QaClipSaver();
+ final private QaClipListModel _qaClipListModel = new QaClipListModel();
+ final private ListSelectionModel _qaClipListSelectionModel;
+
+ final JTextArea _commentArea = new JTextArea();
+
+ public QuestionClipper() {
+ final JFrame frame = new JFrame( "Question Answer Clipper (QuACer)" );
+ frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
+ // Use 1024 x 768 as the minimum required resolution (XGA)
+ // iPhone 3 : 480 x 320 (3:2, HVGA)
+ // iPhone 4 : 960 x 640 (3:2, unique to Apple)
+ // iPhone 5 : 1136 x 640 (under 16:9, unique to Apple)
+ // iPad 3&4 : 2048 x 1536 (4:3, QXGA)
+ // iPad Mini: 1024 x 768 (4:3, XGA)
+ frame.setSize( 1024, 768 );
+ frame.setMinimumSize( new Dimension( 1024, 768 ) );
+ final JMenuBar menuBar = new JMenuBar();
+ final JMenu fileMenu = new JMenu( "File" );
+ fileMenu.add( new JMenuItem( new AbstractAction( "Exit" ) {
+ public void actionPerformed( final ActionEvent event ) {
+ save();
+ System.exit( 0 );
+ }
+ } ) );
+ menuBar.add( fileMenu );
+ final JMenu viewMenu = new JMenu( "View" );
+ final JMenu fontSizeMenu = FontResizeSlider.createFontResizeMenu( frame );
+ viewMenu.add( fontSizeMenu );
+ menuBar.add( viewMenu );
+ frame.setJMenuBar( menuBar );
+ System.setProperty( "apple.laf.useScreenMenuBar", "true" );
+
+ final FileSelectionPanel fileSelectionPanel = new FileSelectionPanel( "Text Document:",
+ JFileChooser.FILES_ONLY, null );
+ fileSelectionPanel.addActionListener( new FileSelectionListener() );
+ final QaClipListList qaClipListList = new QaClipListList( _qaClipListModel );
+ _qaClipListSelectionModel = qaClipListList.getSelectionModel();
+
+ final ErrorLabel errorLabel = new ErrorLabel();
+
+ final ClipperMainPanel clipperMainPanel = new ClipperMainPanel( qaClipListList );
+ clipperMainPanel.addErrorListener( errorLabel );
+
+ _qaClipLoader.addErrorListener( errorLabel );
+ _qaClipSaver.addErrorListener( errorLabel );
+
+ final JPanel qaClipListPanel = new JPanel( new BorderLayout() );
+ qaClipListPanel.add( fileSelectionPanel, BorderLayout.NORTH );
+ final JScrollPane listScrollPane = new JScrollPane( qaClipListList );
+ listScrollPane.setBorder( new CompoundBorder( new EmptyBorder( 0, 5, 5, 5 ), listScrollPane.getBorder() ) );
+ qaClipListPanel.add( listScrollPane, BorderLayout.CENTER );
+
+ final JPanel mainPanel = new JPanel( new BorderLayout() );
+ mainPanel.add( qaClipListPanel, BorderLayout.NORTH );
+ mainPanel.add( clipperMainPanel, BorderLayout.CENTER );
+ final JPanel commentErrorPanel = new JPanel( new BorderLayout() );
+ final JScrollPane commentScrollPane = new JScrollPane( _commentArea );
+ commentScrollPane.setMinimumSize( new Dimension( 0, 50 ) );
+ commentScrollPane.setPreferredSize( new Dimension( 0, 50 ) );
+ commentScrollPane.setBorder( new CompoundBorder( new EmptyBorder( 5, 5, 0, 5 ), commentScrollPane.getBorder() ) );
+ commentErrorPanel.add( commentScrollPane, BorderLayout.CENTER );
+ commentErrorPanel.add( errorLabel, BorderLayout.SOUTH );
+ mainPanel.add( commentErrorPanel, BorderLayout.SOUTH );
+// mainPanel.add( errorLabel, BorderLayout.SOUTH );
+
+ frame.addWindowListener( new WindowAdapter() {
+ @Override
+ public void windowClosing( final WindowEvent e ) {
+ save();
+ super.windowClosing( e );
+ }
+ } );
+ frame.getContentPane().add( mainPanel );
+
+ final GlobalHotkeyManager hotKeyManager = GlobalHotkeyManager.getInstance();
+ hotKeyManager.addHotKey( "PreviousQuestion", KeyStroke.getKeyStroke(
+ KeyEvent.VK_P, KeyEvent.CTRL_MASK, false ), new PreviousQuestionAction() );
+ hotKeyManager.addHotKey( "NextQuestion", KeyStroke.getKeyStroke(
+ KeyEvent.VK_N, KeyEvent.CTRL_MASK, false ), new NextQuestionAction() );
+
+ frame.setVisible( true );
+
+// load( TEXT_DOC_PATH );
+ qaClipListList.setSelectedIndex( _qaClipListModel.getSize() - 1 );
+ }
+
+ private void save() {
+ if ( _qaClipListModel.getSize() <= 1 ) {
+ return;
+ }
+ // fire a selection event to update the currently selected QaClipList
+ if ( _qaClipListSelectionModel != null ) {
+ final int newQaClipIndex = _qaClipListModel.getSize() - 1;
+ _qaClipListSelectionModel.setSelectionInterval( newQaClipIndex, newQaClipIndex );
+ }
+ final String title = _qaClipListModel.getTitle();
+ final List<QaClip> qaClips = _qaClipListModel.getQaClips();
+ final String fullText = _qaClipListModel.getFullText();
+ _qaClipSaver.saveQaClips( title, _commentArea.getText(), qaClips, fullText );
+ }
+
+ private void load( final String textFilePath ) {
+ save();
+ _qaClipListSelectionModel.clearSelection();
+ _qaClipListModel.setQaClips( new ArrayList<QaClip>( 0 ) );
+ _qaClipListModel.setTitle( textFilePath );
+ final String text = loadDocumentText( textFilePath );
+ _qaClipListModel.setFullText( text );
+ final List<QaClip> qaClips = _qaClipLoader.loadQaClips( textFilePath );
+ _qaClipListModel.setQaClips( qaClips );
+ _commentArea.setText( _qaClipLoader.getComment() );
+ final int newQaClipIndex = _qaClipListModel.getSize() - 1;
+ _qaClipListSelectionModel.setSelectionInterval( newQaClipIndex, newQaClipIndex );
+ }
+
+ static private String loadDocumentText( final String filePath ) {
+ final File textFile = new File( filePath );
+ if ( !textFile.canRead() ) {
+ return "Cannot read file " + filePath;
+ }
+ final StringBuilder sb = new StringBuilder();
+ try {
+ final BufferedReader reader = new BufferedReader( new FileReader( textFile ) );
+ final char[] buffer = new char[ 8192 ];
+ int length = reader.read( buffer, 0, buffer.length );
+ while ( length >= 0 ) {
+ sb.append( buffer, 0, length );
+ length = reader.read( buffer, 0, buffer.length );
+ }
+ reader.close();
+ } catch ( IOException ioE ) {
+ System.err.println( ioE.getMessage() );
+ return "Problem reading " + filePath + "\n" + ioE.getMessage();
+ }
+ return sb.toString();
+ }
+
+
+ private class FileSelectionListener implements ActionListener {
+ public void actionPerformed( final ActionEvent event ) {
+ if ( !event.getActionCommand().equals( FileSelectionPanel.FILE_SELECTED ) ) {
+ return;
+ }
+ final Object source = event.getSource();
+ if ( source == null || !(source instanceof FileSelectionPanel) ) {
+ return;
+ }
+ final String textFilePath = ((FileSelectionPanel)source).getFilePath();
+ load( textFilePath );
+ }
+ }
+
+
+ private class PreviousQuestionAction extends AbstractAction {
+ public void actionPerformed( final ActionEvent event ) {
+ if ( _qaClipListSelectionModel == null ) {
+ return;
+ }
+ int index = _qaClipListSelectionModel.getLeadSelectionIndex();
+ if ( index > 0 ) {
+ _qaClipListSelectionModel.setSelectionInterval( index - 1, index - 1 );
+ }
+ }
+ }
+
+ private class NextQuestionAction extends AbstractAction {
+ public void actionPerformed( final ActionEvent event ) {
+ if ( _qaClipListSelectionModel == null ) {
+ return;
+ }
+ int index = _qaClipListSelectionModel.getLeadSelectionIndex();
+ if ( index < _qaClipListModel.getSize() - 2 ) {
+ _qaClipListSelectionModel.setSelectionInterval( index + 1, index + 1 );
+ }
+ }
+ }
+
+
+ public static void main( String[] args ) {
+ new QuestionClipper();
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/SimpleTimelineWriter.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/SimpleTimelineWriter.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/SimpleTimelineWriter.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/SimpleTimelineWriter.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,71 @@
+package org.chboston.cnlp.timeline.gui.qaclipper;
+
+import org.chboston.cnlp.nlp.annotation.classtype.SemanticClassType;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+import org.chboston.cnlp.timeline.timespan.DefaultTimeSpan;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.logging.Logger;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 6/9/14
+ */
+final public class SimpleTimelineWriter {
+
+ static private final Logger LOGGER = Logger.getLogger( "SimpleTimelineWriter" );
+
+ private SimpleTimelineWriter() {
+ }
+
+ static public void writeTimeline( final String filePath, final Timeline timeline ) {
+ try ( BufferedWriter writer = new BufferedWriter( new FileWriter( filePath + ".timeline.txt" ) ) ) {
+ TimeSpan oldTimeSpan = null;
+ boolean printedTimeSpan = false;
+ for ( PointedTimeSpan timeSpan : timeline ) {
+ final TimeSpan simpleTimeSpan = new DefaultTimeSpan( timeSpan );
+ if ( !simpleTimeSpan.equals( oldTimeSpan ) ) {
+ printedTimeSpan = false;
+ oldTimeSpan = simpleTimeSpan;
+ }
+ boolean printedTimeRelation = false;
+ final Collection<Entity> entities = timeline.getEntities( timeSpan );
+ for ( Entity entity : entities ) {
+ if ( !entity.isClassType( SemanticClassType.SIGN_OR_SYMPTOM )
+ && !entity.isClassType( SemanticClassType.PROCEDURE )
+ && !entity.isClassType( SemanticClassType.DISEASE_DISORDER )
+ && !entity.isClassType( SemanticClassType.MEDICATION ) ) {
+ continue;
+ }
+ String text = entity.getSpannedTextRepresentation();
+ if ( text.indexOf( '[' ) >= 0 && !text.contains( "..." ) ) {
+ text = text.replace( "[", "" );
+ text = text.replace( "]", "" );
+ }
+ if ( !printedTimeSpan ) {
+ writer.write( timeSpan.getSpanText() );
+ writer.newLine();
+ printedTimeSpan = true;
+ }
+ if ( !printedTimeRelation ) {
+ writer.write( "\t" + timeSpan.getRelationText() );
+ writer.newLine();
+ printedTimeRelation = true;
+ }
+ writer.write( "\t\t" + text );
+ writer.newLine();
+ }
+ }
+ } catch ( IOException ioE ) {
+ LOGGER.severe( ioE.getMessage() );
+ }
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/TimelineAnaforaWriter1.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/TimelineAnaforaWriter1.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/TimelineAnaforaWriter1.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/TimelineAnaforaWriter1.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,295 @@
+package org.chboston.cnlp.timeline.gui.qaclipper;
+
+import org.chboston.cnlp.nlp.annotation.annotation.Annotation;
+import org.chboston.cnlp.nlp.annotation.attribute.Attribute;
+import org.chboston.cnlp.nlp.annotation.attribute.DefinedAttributeType;
+import org.chboston.cnlp.nlp.annotation.classtype.SemanticClassType;
+import org.chboston.cnlp.nlp.annotation.classtype.TemporalClassType;
+import org.chboston.cnlp.nlp.annotation.coreference.CoreferenceChain;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.nlp.annotation.textspan.DefaultDiscontiguousTextSpan;
+import org.chboston.cnlp.nlp.annotation.textspan.DiscontiguousTextSpan;
+import org.chboston.cnlp.nlp.annotation.textspan.TextSpan;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+import org.chboston.cnlp.timeline.timespan.DefaultTimeSpan;
+import org.chboston.cnlp.timeline.timespan.TimeSpan;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+import org.jdom.Comment;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.logging.Logger;
+
+import static org.chboston.cnlp.anafora.annotation.parser.AnaforaTag.*;
+
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 6/17/14
+ */
+final public class TimelineAnaforaWriter1 {
+
+ static private final Logger LOGGER = Logger.getLogger( "TimelineAnaforaWriter1" );
+ static private final DateFormat LONG_DATE_FORMAT = new SimpleDateFormat( "EEEE, MMMM d, yyyy" );
+
+ static private final String PARENT_TIMELINE = "Timeline";
+ static private final String TIME_SPAN_RELATION = "TimeSpanRelation";
+ static private final String TIME_SPAN = "TimeSpan";
+ static private final String TIME_SPAN_START = "StartTime";
+ static private final String TIME_SPAN_STOP = "StopTime";
+ static private final String TIME_SPAN_FUZZY = "Fuzzy";
+
+ private TimelineAnaforaWriter1() {
+ }
+
+
+ static public void writeTimeline( final String textFilePath, final Timeline timeline ) {
+ try ( Writer writer = new BufferedWriter( new FileWriter( textFilePath + ".timeline1.xml" ) ) ) {
+ final Element root = new Element( ROOT_ELEMENT.getName() );
+ final Document doc = new Document( root );
+ doc.setRootElement( root );
+ final Element annotations = new Element( ANNOTATIONS_GROUP.getName() );
+
+ TimeSpan oldTimeSpan = null;
+ for ( PointedTimeSpan timeSpan : timeline ) {
+ final TimeSpan simpleTimeSpan = new DefaultTimeSpan( timeSpan );
+ addTimeSpan( annotations, timeline, timeSpan, !simpleTimeSpan.equals( oldTimeSpan ) );
+ oldTimeSpan = simpleTimeSpan;
+ }
+
+ root.addContent( annotations );
+ final XMLOutputter outputter = new XMLOutputter();
+ outputter.setFormat( Format.getPrettyFormat() );
+ outputter.output( doc, writer );
+ } catch ( IOException ioE ) {
+ LOGGER.severe( ioE.getMessage() );
+ }
+ }
+
+
+ static private void addTimeSpan( final Element annotations, final Timeline timeline,
+ final PointedTimeSpan timeSpan, final boolean addTimeSpanElement )
+ throws IOException {
+ final String startText = LONG_DATE_FORMAT.format( new Date( timeSpan.getStartMillis() ) );
+ final String stopText = LONG_DATE_FORMAT.format( new Date( timeSpan.getStopMillis() ) );
+
+ if ( addTimeSpanElement ) {
+ annotations
+ .addContent( new Comment( "All Events Related to the Time Span " + startText + " .. " + stopText ) );
+ addSimpleTimeSpan( annotations, timeSpan );
+ }
+
+ boolean wroteLinkComment = false;
+ final Collection<Entity> entities = timeline.getEntities( timeSpan );
+ for ( Entity entity : entities ) {
+ if ( !(entity instanceof CoreferenceChain) ) {
+ continue;
+ }
+ if ( !entity.isClassType( SemanticClassType.SIGN_OR_SYMPTOM )
+ && !entity.isClassType( SemanticClassType.PROCEDURE )
+ && !entity.isClassType( SemanticClassType.DISEASE_DISORDER )
+ && !entity.isClassType( SemanticClassType.MEDICATION ) ) {
+ continue;
+ }
+ if ( !wroteLinkComment ) {
+ annotations.addContent( new Comment( "All Events " + timeSpan.getRelationText()
+ + " the Time Span " + startText + " .. " + stopText ) );
+ wroteLinkComment = true;
+ }
+ annotations.addContent( new Comment( "Entities for Coreference Event "
+ + entity.getSpannedTextRepresentation() ) );
+ addChain( annotations, (CoreferenceChain)entity );
+ addTimeSpanLink( annotations, timeSpan, entity );
+ }
+ }
+
+
+ static private void addChain( final Element annotations, final CoreferenceChain chain ) throws IOException {
+ for ( Entity link : chain ) {
+ final Element element = createEntityElement( link );
+ if ( element != null ) {
+ annotations.addContent( element );
+ }
+ }
+ final Element element = createCorefElement( chain );
+ if ( element != null ) {
+ annotations.addContent( element );
+ }
+ }
+
+ static private Element createEntityElement( final Entity entity ) throws IOException {
+ final Element element = new Element( ENTITY_ELEMENT.getName() );
+ // ID
+ element.addContent(
+ new Element( ANNOTATION_ID.getName() ).setText( getId( entity ) ) );
+ // Text Span
+ element.addContent(
+ new Element( ANNOTATION_TEXT_SPAN.getName() ).setText( createTextSpanText( entity.getTextSpan() ) ) );
+ // Class Type
+ element.addContent(
+ new Element( ANNOTATION_TYPE.getName() ).setText( entity.getClassType().getName() ) );
+ // Parent Class
+ if ( entity.isClassType( TemporalClassType.EVENT ) || entity.isClassType( TemporalClassType.TIMEX )
+ || entity.isClassType( TemporalClassType.DOCTIME ) ) {
+ element.addContent(
+ new Element( ANNOTATION_PARENT_TYPE.getName() ).setText( PARENT_TEMPORAL.getName() ) );
+ } else {
+ element.addContent(
+ new Element( ANNOTATION_PARENT_TYPE.getName() ).setText( PARENT_UMLS.getName() ) );
+ }
+ // Attributes
+ final Element properties = new Element( ANNOTATION_PROPERTIES.getName() );
+ final Collection<String> attributeNames = entity.getAttributeNames();
+ for ( String name : attributeNames ) {
+ properties.addContent( createAttributeElement( entity.getAttribute( name ) ) );
+ }
+ element.addContent( properties );
+ return element;
+ }
+
+ static private Element createCorefElement( final CoreferenceChain chain ) throws IOException {
+ final Element element = new Element( RELATION_ELEMENT.getName() );
+ // ID
+ element.addContent(
+ new Element( ANNOTATION_ID.getName() ).setText( getId( chain ) ) );
+ // Class Type
+ element.addContent(
+ new Element( ANNOTATION_TYPE.getName() ).setText( COREF_IDENTICAL.getName() ) );
+ // Parent Class
+ element.addContent(
+ new Element( ANNOTATION_PARENT_TYPE.getName() ).setText( PARENT_COREFERENCE.getName() ) );
+ // Attributes
+ final Element properties = new Element( ANNOTATION_PROPERTIES.getName() );
+ boolean isFirst = true;
+ for ( Entity entity : chain ) {
+ if ( isFirst ) {
+ properties.addContent(
+ new Element( FIRST_COREF_INSTANCE.getName() ).setText( getId( entity ) ) );
+ isFirst = false;
+ } else {
+ properties.addContent(
+ new Element( OTHER_COREF_INSTANCE.getName() ).setText( getId( entity ) ) );
+ }
+ }
+// final Collection<String> attributeNames = chain.getAttributeNames();
+// for ( String name : attributeNames ) {
+// properties.addContent( createAttributeElement( chain.getAttribute( name ) ) );
+// }
+ element.addContent( properties );
+ return element;
+ }
+
+
+ static private void addSimpleTimeSpan( final Element annotations, final TimeSpan timeSpan ) {
+ final Element element = createTimeSpanElement( timeSpan );
+ if ( element != null ) {
+ annotations.addContent( element );
+ }
+ }
+
+ static private Element createTimeSpanElement( final TimeSpan timeSpan ) {
+ final Element element = new Element( TIME_SPAN );
+ // ID
+ element.addContent(
+ new Element( ANNOTATION_ID.getName() ).setText( getId( timeSpan ) ) );
+ // Class Type - same as element type
+ element.addContent(
+ new Element( ANNOTATION_TYPE.getName() ).setText( TIME_SPAN ) );
+ // Parent Class
+ element.addContent(
+ new Element( ANNOTATION_PARENT_TYPE.getName() ).setText( PARENT_TIMELINE ) );
+ // Attributes
+ final Element properties = new Element( ANNOTATION_PROPERTIES.getName() );
+ final String startText = LONG_DATE_FORMAT.format( new Date( timeSpan.getStartMillis() ) );
+ final String stopText = LONG_DATE_FORMAT.format( new Date( timeSpan.getStopMillis() ) );
+ properties.addContent(
+ new Element( TIME_SPAN_START ).setText( startText ) );
+ properties.addContent(
+ new Element( TIME_SPAN_STOP ).setText( stopText ) );
+ if ( timeSpan.isFuzzyDate() ) {
+ properties.addContent(
+ new Element( TIME_SPAN_FUZZY ).setText( Boolean.toString( true ) ) );
+ }
+ element.addContent( properties );
+ return element;
+ }
+
+
+ static private void addTimeSpanLink( final Element annotations, final PointedTimeSpan timeSpan,
+ final Entity entity ) throws IOException {
+ final Element element = createTimeSpanLinkElement( timeSpan, entity );
+ if ( element != null ) {
+ annotations.addContent( element );
+ }
+ }
+
+ static private Element createTimeSpanLinkElement( final PointedTimeSpan timeSpan,
+ final Entity entity ) throws IOException {
+ final Element element = new Element( TIME_SPAN_RELATION );
+ // ID
+ element.addContent(
+ new Element( ANNOTATION_ID.getName() ).setText( getId( timeSpan, entity ) ) );
+ // Class Type
+ element.addContent(
+ new Element( ANNOTATION_TYPE.getName() ).setText( timeSpan.getRelationText() ) );
+ // Parent Class
+ element.addContent(
+ new Element( ANNOTATION_PARENT_TYPE.getName() ).setText( PARENT_TIMELINE ) );
+ // Attributes
+ final Element properties = new Element( ANNOTATION_PROPERTIES.getName() );
+ properties.addContent(
+ new Element( TIME_SPAN ).setText( getId( timeSpan ) ) );
+ properties.addContent(
+ new Element( ENTITY_ELEMENT.getName() ).setText( getId( entity ) ) );
+ element.addContent( properties );
+ return element;
+ }
+
+
+ static private String createTextSpanText( final TextSpan textSpan ) {
+ if ( textSpan instanceof DefaultDiscontiguousTextSpan ) {
+ final StringBuilder sb = new StringBuilder();
+ for ( TextSpan subSpan : (DiscontiguousTextSpan)textSpan ) {
+ sb.append( createTextSpanText( subSpan ) ).append( ';' );
+ }
+ sb.setLength( sb.length() - 1 );
+ return sb.toString();
+ }
+ return textSpan.getStartIndex() + "," + textSpan.getEndIndex();
+ }
+
+ static private String getId( final TimeSpan timeSpan ) {
+ return "TimeSpan" + new DefaultTimeSpan( timeSpan ).hashCode();
+ }
+
+ static private String getId( final Annotation annotation ) {
+ final Attribute idAttribute = annotation.getAttribute( DefinedAttributeType.UNIQUE_ID );
+ if ( idAttribute != null ) {
+ return idAttribute.getValue();
+ }
+ return annotation.getClassType().getName() + annotation.hashCode();
+ }
+
+ static private String getId( final TimeSpan timeSpan, final Annotation annotation ) {
+ return getId( timeSpan ) + "_" + getId( annotation );
+ }
+
+
+ static private Element createAttributeElement( final Attribute attribute ) {
+ final String name = attribute.getName().replace( "(", " " ).replace( ")", " " ).replaceAll( "\\s+", "_" );
+ return new Element( name ).setText( attribute.getValue() );
+ }
+
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/TimelineAnaforaWriter5.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/TimelineAnaforaWriter5.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/TimelineAnaforaWriter5.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/TimelineAnaforaWriter5.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,256 @@
+package org.chboston.cnlp.timeline.gui.qaclipper;
+
+import org.chboston.cnlp.nlp.annotation.attribute.AttributeUtil;
+import org.chboston.cnlp.nlp.annotation.classtype.SemanticClassType;
+import org.chboston.cnlp.nlp.annotation.classtype.TemporalClassType;
+import org.chboston.cnlp.nlp.annotation.coreference.CoreferenceChain;
+import org.chboston.cnlp.nlp.annotation.entity.Entity;
+import org.chboston.cnlp.nlp.annotation.textspan.DefaultDiscontiguousTextSpan;
+import org.chboston.cnlp.nlp.annotation.textspan.DiscontiguousTextSpan;
+import org.chboston.cnlp.nlp.annotation.textspan.TextSpan;
+import org.chboston.cnlp.timeline.timeline.Timeline;
+import org.chboston.cnlp.timeline.timespan.plus.PointedTimeSpan;
+import org.jdom.Comment;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.logging.Logger;
+
+import static org.chboston.cnlp.anafora.annotation.parser.AnaforaTag.*;
+
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 6/17/14
+ */
+final public class TimelineAnaforaWriter5 {
+
+ static private final Logger LOGGER = Logger.getLogger( "TimelineAnaforaWriter5" );
+ // static private final DateFormat SHORT_DATE_FORMAT = new SimpleDateFormat( "M/d/yyyy" );
+// static private final DateFormat SHORT_TIME_FORMAT = new SimpleDateFormat( "h:mm a" );
+ static private final DateFormat LONG_DATE_FORMAT = new SimpleDateFormat( "EEEE, MMMM d, yyyy" );
+
+ static private final DateFormat ISO_8601_DATE = new SimpleDateFormat( "yyyy-MM-dd" );
+ static private final DateFormat ISO_8601_TIME = new SimpleDateFormat( "HH:mm" );
+
+ static private final String ENTITY_TEXT = "text";
+ static private final String NEGATED = "negated";
+ static private final String UNCERTAIN = "uncertain";
+
+ // static private final String START_DATE = "startDate";
+ static private final String START_TIME = "startTime";
+ static private final String START_DIR = "startDir";
+ // static private final String STOP_DATE = "stopDate";
+ static private final String STOP_TIME = "stopTime";
+ static private final String STOP_DIR = "stopDir";
+
+ private TimelineAnaforaWriter5() {
+ }
+
+
+ static public void writeTimeline( final String textFilePath, final Timeline timeline ) {
+ try ( Writer writer = new BufferedWriter( new FileWriter( textFilePath + ".timeline5.xml" ) ) ) {
+ final Element root = new Element( ROOT_ELEMENT.getName() );
+ final Document doc = new Document( root );
+ doc.setRootElement( root );
+ final Element annotations = new Element( ANNOTATIONS_GROUP.getName() );
+
+ for ( PointedTimeSpan timeSpan : timeline ) {
+ addTimeSpan( annotations, timeline, timeSpan );
+ }
+
+ root.addContent( annotations );
+ final XMLOutputter outputter = new XMLOutputter();
+ outputter.setFormat( Format.getPrettyFormat() );
+ outputter.output( doc, writer );
+ } catch ( IOException ioE ) {
+ LOGGER.severe( ioE.getMessage() );
+ }
+ }
+
+
+ static private void addTimeSpan( final Element annotations, final Timeline timeline,
+ final PointedTimeSpan timeSpan ) throws IOException {
+ boolean wroteLinkComment = false;
+ final Collection<Entity> entities = timeline.getEntities( timeSpan );
+ for ( Entity entity : entities ) {
+ if ( !entity.isClassType( TemporalClassType.EVENT ) || !isSemanticEntity( entity ) ) {
+ continue;
+ }
+ if ( !wroteLinkComment ) {
+ writeTimeSpanComment( annotations, timeSpan );
+ wroteLinkComment = true;
+ }
+ addChain( annotations, timeSpan, (CoreferenceChain)entity );
+ }
+ for ( Entity entity : entities ) {
+ if ( !entity.isClassType( TemporalClassType.EVENT ) || isSemanticEntity( entity ) ) {
+ continue;
+ }
+ if ( !wroteLinkComment ) {
+ writeTimeSpanComment( annotations, timeSpan );
+ wroteLinkComment = true;
+ }
+ addEvent( annotations, timeSpan, entity );
+ }
+ }
+
+ static private boolean isSemanticEntity( final Entity entity ) {
+ return entity.isClassType( SemanticClassType.SIGN_OR_SYMPTOM )
+ || entity.isClassType( SemanticClassType.PROCEDURE )
+ || entity.isClassType( SemanticClassType.DISEASE_DISORDER )
+ || entity.isClassType( SemanticClassType.MEDICATION );
+ }
+
+ static private void writeTimeSpanComment( final Element annotations, final PointedTimeSpan timeSpan ) {
+ final String startText = LONG_DATE_FORMAT.format( new Date( timeSpan.getStartMillis() ) );
+ final String stopText = LONG_DATE_FORMAT.format( new Date( timeSpan.getStopMillis() ) );
+ annotations.addContent( new Comment( timeSpan.getRelationText() + " " + startText + " .. " + stopText ) );
+ }
+
+
+ static private void addChain( final Element annotations, final PointedTimeSpan timeSpan,
+ final CoreferenceChain chain ) throws IOException {
+ final Element element = createCorefElement( timeSpan, chain );
+ if ( element != null ) {
+ annotations.addContent( element );
+ }
+ }
+
+ static private Element createCorefElement( final PointedTimeSpan timeSpan, final CoreferenceChain chain )
+ throws IOException {
+ final Element element = new Element( ENTITY_ELEMENT.getName() );
+ String text = chain.getSpannedTextRepresentation();
+ if ( text.startsWith( "[" ) && text.endsWith( "]" ) && !text.contains( "] ... [" ) ) {
+ text = text.substring( 1, text.length() - 1 );
+ }
+ element.addContent(
+ new Element( ENTITY_TEXT ).setText( text ) );
+ element.addContent(
+ new Element( ANNOTATION_TEXT_SPAN.getName() ).setText( createTextSpanText( chain.getTextSpan() ) ) );
+ // negation, uncertainty
+ if ( AttributeUtil.hasNegativePolarity( chain ) ) {
+ element.addContent(
+ new Element( NEGATED ).setText( "true" ) );
+ }
+ if ( AttributeUtil.hasUncertainty( chain ) ) {
+ element.addContent(
+ new Element( UNCERTAIN ).setText( "true" ) );
+ }
+ // Class Type
+ final Collection<String> classTypes = new HashSet<>();
+ for ( Entity entity : chain ) {
+ if ( entity.getClassType() instanceof SemanticClassType ) {
+ classTypes.add( entity.getClassType().getName() );
+ }
+ }
+ final List<String> classTypeList = new ArrayList<>( classTypes );
+ if ( !classTypeList.isEmpty() ) {
+ Collections.sort( classTypeList );
+ element.addContent(
+ new Element( ANNOTATION_TYPE.getName() ).setText( classTypeList.get( 0 ) ) );
+ } else {
+ element.addContent(
+ new Element( ANNOTATION_TYPE.getName() ).setText( COREF_IDENTICAL.getName() ) );
+ }
+ // Attributes
+ final String startDate = ISO_8601_DATE.format( new Date( timeSpan.getStartMillis() ) );
+ final String startTime = ISO_8601_TIME.format( new Date( timeSpan.getStartMillis() ) );
+ final String stopDate = ISO_8601_DATE.format( new Date( timeSpan.getStopMillis() ) );
+ final String stopTime = ISO_8601_TIME.format( new Date( timeSpan.getStopMillis() ) );
+// element.addContent(
+// new Element( START_DATE ).setText( startDate ) );
+ element.addContent(
+ new Element( START_DIR ).setText( timeSpan.getStartTime().getPointer().getName() ) );
+ element.addContent(
+ new Element( START_TIME ).setText( startDate + "T" + startTime ) );
+// element.addContent(
+// new Element( STOP_DATE ).setText( stopDate ) );
+ element.addContent(
+ new Element( STOP_DIR ).setText( timeSpan.getStopTime().getPointer().getName() ) );
+ element.addContent(
+ new Element( STOP_TIME ).setText( stopDate + "T" + stopTime ) );
+ if ( classTypeList.size() > 1 ) {
+ for ( String type : classTypeList ) {
+ element.addContent(
+ new Element( "SemanticType" ).setText( type ) );
+ }
+ }
+ return element;
+ }
+
+
+ static private void addEvent( final Element annotations, final PointedTimeSpan timeSpan,
+ final Entity entity ) throws IOException {
+ final Element element = createEventElement( timeSpan, entity );
+ if ( element != null ) {
+ annotations.addContent( element );
+ }
+ }
+
+ static private Element createEventElement( final PointedTimeSpan timeSpan, final Entity entity ) throws IOException {
+ final Element element = new Element( ENTITY_ELEMENT.getName() );
+ String text = entity.getSpannedTextRepresentation();
+ if ( text.startsWith( "[" ) && text.endsWith( "]" ) && !text.contains( "] ... [" ) ) {
+ text = text.substring( 1, text.length() - 1 );
+ }
+ element.addContent(
+ new Element( ENTITY_TEXT ).setText( text ) );
+ element.addContent(
+ new Element( ANNOTATION_TEXT_SPAN.getName() ).setText( createTextSpanText( entity.getTextSpan() ) ) );
+ // negation, uncertainty
+ if ( AttributeUtil.hasNegativePolarity( entity ) ) {
+ element.addContent(
+ new Element( NEGATED ).setText( "true" ) );
+ }
+ if ( AttributeUtil.hasUncertainty( entity ) ) {
+ element.addContent(
+ new Element( UNCERTAIN ).setText( "true" ) );
+ }
+ // Class Type
+ element.addContent(
+ new Element( ANNOTATION_TYPE.getName() ).setText( "Event" ) );
+ // Attributes
+ final String startDate = ISO_8601_DATE.format( new Date( timeSpan.getStartMillis() ) );
+ final String startTime = ISO_8601_TIME.format( new Date( timeSpan.getStartMillis() ) );
+ final String stopDate = ISO_8601_DATE.format( new Date( timeSpan.getStopMillis() ) );
+ final String stopTime = ISO_8601_TIME.format( new Date( timeSpan.getStopMillis() ) );
+ // element.addContent(
+ // new Element( START_DATE ).setText( startDate ) );
+ element.addContent(
+ new Element( START_DIR ).setText( timeSpan.getStartTime().getPointer().getName() ) );
+ element.addContent(
+ new Element( START_TIME ).setText( startDate + "T" + startTime ) );
+ // element.addContent(
+ // new Element( STOP_DATE ).setText( stopDate ) );
+ element.addContent(
+ new Element( STOP_DIR ).setText( timeSpan.getStopTime().getPointer().getName() ) );
+ element.addContent(
+ new Element( STOP_TIME ).setText( stopDate + "T" + stopTime ) );
+ return element;
+ }
+
+
+ static private String createTextSpanText( final TextSpan textSpan ) {
+ if ( textSpan instanceof DefaultDiscontiguousTextSpan ) {
+ final StringBuilder sb = new StringBuilder();
+ for ( TextSpan subSpan : (DiscontiguousTextSpan)textSpan ) {
+ sb.append( createTextSpanText( subSpan ) ).append( ';' );
+ }
+ sb.setLength( sb.length() - 1 );
+ return sb.toString();
+ }
+ return textSpan.getStartIndex() + "," + textSpan.getEndIndex();
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/AbstractQaRadioPanel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/AbstractQaRadioPanel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/AbstractQaRadioPanel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/AbstractQaRadioPanel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,66 @@
+package org.chboston.cnlp.timeline.gui.qaclipper.gui;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/15/13
+ */
+abstract public class AbstractQaRadioPanel extends JPanel implements QaClipComponent {
+
+ final private JRadioButton _lowButton;
+ final private JRadioButton _medButton;
+ final private JRadioButton _highButton;
+
+ protected AbstractQaRadioPanel( final String panelTitle,
+ final String lowTitle, final String mediumTitle, final String highTitle ) {
+ super( new GridLayout( 4, 1, 5, 5 ) );
+ setBorder( new LineBorder( Color.LIGHT_GRAY, 1 ) );
+
+ final JLabel titleLabel = new JLabel( panelTitle );
+ titleLabel.setBorder( new EmptyBorder( 0, 5, 0, 0 ) );
+ titleLabel.setFont( titleLabel.getFont().deriveFont( Font.BOLD ) );
+
+ _lowButton = new JRadioButton( lowTitle );
+ _medButton = new JRadioButton( mediumTitle );
+ _highButton = new JRadioButton( highTitle );
+
+ final ButtonGroup buttonGroup = new ButtonGroup();
+ buttonGroup.add( _lowButton );
+ buttonGroup.add( _medButton );
+ buttonGroup.add( _highButton );
+
+ add( titleLabel );
+ add( _lowButton );
+ add( _medButton );
+ add( _highButton );
+ }
+
+ public void reset() {
+ _medButton.setSelected( true );
+ }
+
+ protected String getSelection() {
+ if ( _lowButton.isSelected() ) {
+ return _lowButton.getText();
+ } else if ( _highButton.isSelected() ) {
+ return _highButton.getText();
+ }
+ return _medButton.getText();
+ }
+
+ protected void setSelection( final String selection ) {
+ if ( _lowButton.getText().equals( selection ) ) {
+ _lowButton.setSelected( true );
+ } else if ( _highButton.getText().equals( selection ) ) {
+ _highButton.setSelected( true );
+ } else {
+ _medButton.setSelected( true );
+ }
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/ClipperMainPanel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/ClipperMainPanel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/ClipperMainPanel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/ClipperMainPanel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,327 @@
+package org.chboston.cnlp.timeline.gui.qaclipper.gui;
+
+import org.chboston.cnlp.gui.PartialEtchedBorder;
+import org.chboston.cnlp.gui.PositioningSplitPane;
+import org.chboston.cnlp.gui.error.ErrorListener;
+import org.chboston.cnlp.gui.error.ErrorProducer;
+import org.chboston.cnlp.nlp.annotation.textspan.DefaultTextSpan;
+import org.chboston.cnlp.timeline.gui.qaclipper.QaClip;
+
+import javax.swing.*;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.text.Caret;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/15/13
+ */
+public class ClipperMainPanel extends JPanel implements QaClipComponent {
+
+ final private JTextField _queryField;
+ final private JTextField _answerField;
+
+ final private TableTextHighlightPanel _textArea;
+ final private TextClipsTable _textClipsTable;
+ final private TextClipsTableModel _answerTableModel;
+
+ final private QaConfidencePanel _confidencePanel;
+ final private QaDifficultyPanel _difficultyPanel;
+ final private QaDocTimeRelPanel _docTimeRelPanel;
+
+
+ final private QaClipListModel _qaClipListModel;
+ final private ListSelectionModel _qaClipListSelectionModel;
+
+ final private ErrorProducer _errorProducer = new ErrorProducer();
+
+
+ public ClipperMainPanel( final QaClipListList qaClipListList ) {
+ super( new BorderLayout( 0, 5 ) );
+ setBorder( new CompoundBorder( new PartialEtchedBorder( true, true, false, false ),
+ new EmptyBorder( 5, 10, 5, 10 ) ) );
+
+ _qaClipListModel = qaClipListList.getListModel();
+ _qaClipListModel.addListDataListener( new QaListDataListener() );
+ _qaClipListSelectionModel = qaClipListList.getSelectionModel();
+ qaClipListList.addListSelectionListener( new QaListSelectionListener() );
+
+ _queryField = new JTextField();
+ final JComponent queryPanel = createQaPanel( "Question:", _queryField );
+ _answerField = new JTextField();
+ final JComponent answerPanel = createQaPanel( "Answer:", _answerField );
+
+ final JPanel qaPanel = new JPanel( new GridLayout( 1, 2, 20, 5 ) );
+ qaPanel.add( queryPanel );
+ qaPanel.add( answerPanel );
+
+ _answerTableModel = new TextClipsTableModel( _answerField );
+ _textClipsTable = new TextClipsTable( _answerTableModel );
+
+ _textArea = createTextArea( _textClipsTable );
+ final JScrollPane textScrollPane = new JScrollPane( _textArea );
+ _textArea.setVerticalScrollBar( textScrollPane.getVerticalScrollBar() );
+
+ _confidencePanel = new QaConfidencePanel();
+ _difficultyPanel = new QaDifficultyPanel();
+ final JPanel radioPanel = new JPanel( new GridLayout( 1, 2, 5, 0 ) );
+ radioPanel.add( _confidencePanel );
+ radioPanel.add( _difficultyPanel );
+
+ _docTimeRelPanel = new QaDocTimeRelPanel();
+ final JPanel bigRadioPanel = new JPanel( new BorderLayout() );
+ bigRadioPanel.add( _docTimeRelPanel, BorderLayout.NORTH );
+ bigRadioPanel.add( radioPanel, BorderLayout.SOUTH );
+
+ final PositioningSplitPane listSplitPane = new PositioningSplitPane( JSplitPane.VERTICAL_SPLIT,
+ new JScrollPane( _textClipsTable ),
+// radioPanel );
+ bigRadioPanel );
+// listSplitPane.setDividerLocation( 0.75 );
+ listSplitPane.setDividerLocation( 0.5 );
+
+ final PositioningSplitPane textListSplitPane = new PositioningSplitPane( JSplitPane.HORIZONTAL_SPLIT,
+ textScrollPane,
+ listSplitPane );
+ textListSplitPane.setDividerLocation( 0.75 );
+
+ final JButton addQaButton = new JButton( new AddQaAction() );
+ final JButton resetAllButton = new JButton( new NewAllAction() );
+ final JButton resetAnswerButton = new JButton( new NewAnswerAction() );
+ final Box buttonPanel = Box.createHorizontalBox();
+ buttonPanel.add( Box.createHorizontalGlue() );
+ buttonPanel.add( addQaButton );
+ buttonPanel.add( Box.createHorizontalStrut( 5 ) );
+ buttonPanel.add( resetAllButton );
+ buttonPanel.add( Box.createHorizontalStrut( 5 ) );
+ buttonPanel.add( resetAnswerButton );
+
+ add( qaPanel, BorderLayout.NORTH );
+ add( textListSplitPane, BorderLayout.CENTER );
+ add( buttonPanel, BorderLayout.SOUTH );
+ }
+
+ public void setQaClip( final QaClip qaClip ) {
+ _queryField.setText( qaClip.getQuestion() );
+ _answerField.setText( qaClip.getAnswer() );
+ _textClipsTable.setQaClip( qaClip );
+ _confidencePanel.setQaClip( qaClip );
+ _difficultyPanel.setQaClip( qaClip );
+ _docTimeRelPanel.setQaClip( qaClip );
+ fireErrorCleared();
+ }
+
+ public void adjustQaClip( final QaClip qaClip ) {
+ qaClip.setQuestion( _queryField.getText() );
+ qaClip.setAnswer( _answerField.getText() );
+ _textClipsTable.adjustQaClip( qaClip );
+ _confidencePanel.adjustQaClip( qaClip );
+ _difficultyPanel.adjustQaClip( qaClip );
+ _docTimeRelPanel.adjustQaClip( qaClip );
+ }
+
+ static private JComponent createQaPanel( final String title, final JTextField textField ) {
+ final Box panel = Box.createHorizontalBox();
+ panel.add( new JLabel( title ) );
+ panel.add( Box.createHorizontalStrut( 5 ) );
+ panel.add( textField );
+ return panel;
+ }
+
+ private TableTextHighlightPanel createTextArea( final JTable table ) {
+ final OrderHighlightPanel textArea = new OrderHighlightPanel( table, 5 );
+ textArea.setBorder( new EmptyBorder( 0, 5, 0, 5 ) );
+ textArea.setEditable( false );
+ textArea.setLineWrap( true );
+ textArea.setWrapStyleWord( true );
+ textArea.addKeyListener( new QaKeyListener() );
+ return textArea;
+ }
+
+ private void addQandA() {
+ final QaClip qaClip = new QaClip();
+ adjustQaClip( qaClip );
+ if ( !isQaClipComplete( qaClip ) ) {
+ return;
+ }
+ _qaClipListModel.addQaClip( qaClip );
+ final int lastQaClipIndex = _qaClipListModel.getSize() - 2;
+ _qaClipListSelectionModel.setSelectionInterval( lastQaClipIndex, lastQaClipIndex );
+ }
+
+ private void newAnswer() {
+// final QaClip qaClip = new QaClip();
+// qaClip.setQuestion( _queryField.getText() );
+// setQaClip( qaClip );
+ final String question = _queryField.getText();
+ newQandA();
+ _queryField.setText( question );
+ }
+
+ public void newQandA() {
+// final QaClip qaClip = new QaClip();
+// setQaClip( qaClip );
+ final int newQaClipIndex = _qaClipListModel.getSize() - 1;
+ _qaClipListSelectionModel.setSelectionInterval( newQaClipIndex, newQaClipIndex );
+ }
+
+
+ private boolean isQaClipComplete( final QaClip qaClip ) {
+ final String question = qaClip.getQuestion();
+ if ( question.trim().isEmpty() ) {
+ fireErrorOccurred( "Please Type a question" );
+ return false;
+ }
+ final Collection<QaClip.TextClip> textClips = qaClip.getTextClips();
+ final String answer = qaClip.getAnswer();
+ if ( answer.trim().isEmpty() && !textClips.isEmpty() ) {
+ fireErrorOccurred( "Please Type an answer" );
+ return false;
+ }
+ if ( !answer.trim().isEmpty() && textClips.isEmpty() ) {
+ fireErrorOccurred( "Please Select some Text" );
+ return false;
+ }
+ fireErrorCleared();
+ return true;
+ }
+
+ private void fireErrorOccurred( final String error ) {
+ _errorProducer.fireErrorOccurred( error );
+ }
+
+ private void fireErrorCleared() {
+ _errorProducer.fireErrorCleared();
+ }
+
+ public void addErrorListener( final ErrorListener listener ) {
+ _errorProducer.addErrorListener( listener );
+ _answerTableModel.addErrorListener( listener );
+ }
+
+
+ private class QaKeyListener extends KeyAdapter {
+ public void keyTyped( final KeyEvent event ) {
+ final char c = event.getKeyChar();
+ if ( c != 'a' ) {
+ return;
+ }
+ final String text = _textArea.getSelectedText();
+ if ( text == null || text.trim().isEmpty() ) {
+ return;
+ }
+ final Caret caret = _textArea.getCaret();
+ if ( caret == null ) {
+ return;
+ }
+ final int start = Math.min( caret.getDot(), caret.getMark() );
+ final int stop = Math.max( caret.getDot(), caret.getMark() );
+ if ( start == stop ) {
+ return;
+ }
+ _answerTableModel.addTextSpan( new DefaultTextSpan( start, stop ) );
+ }
+ }
+
+ private class AddQaAction extends AbstractAction {
+ private AddQaAction() {
+ super( "Add Q & A" );
+ }
+
+ public void actionPerformed( final ActionEvent event ) {
+ addQandA();
+ }
+ }
+
+ private class NewAnswerAction extends AbstractAction {
+ private NewAnswerAction() {
+ super( "New Answer Only" );
+ }
+
+ public void actionPerformed( final ActionEvent event ) {
+ newAnswer();
+ }
+ }
+
+ private class NewAllAction extends AbstractAction {
+ private NewAllAction() {
+ super( "New Q & A" );
+ }
+
+ public void actionPerformed( final ActionEvent event ) {
+ newQandA();
+ }
+ }
+
+ private class QaListDataListener implements ListDataListener {
+ public void intervalAdded( final ListDataEvent event ) {
+ }
+
+ public void intervalRemoved( final ListDataEvent event ) {
+ final Object source = event.getSource();
+ if ( source == null || !(source instanceof QaClipListModel) ) {
+ return;
+ }
+ if ( ((QaClipListModel)source).getSize() <= 1 ) {
+ newQandA();
+ }
+ }
+
+ public void contentsChanged( final ListDataEvent event ) {
+ final Object source = event.getSource();
+ if ( source == null || !(source instanceof QaClipListModel) ) {
+ return;
+ }
+ final String fullText = ((QaClipListModel)source).getFullText();
+ _textArea.setText( fullText );
+ _textClipsTable.setFullText( fullText );
+ }
+ }
+
+ private class QaListSelectionListener implements ListSelectionListener {
+ public void valueChanged( final ListSelectionEvent event ) {
+ if ( event == null || event.getValueIsAdjusting() ) {
+ return;
+ }
+ final Object source = event.getSource();
+ if ( !(source instanceof QaClipListList) ) {
+ return;
+ }
+ final ListSelectionModel selectionModel = ((QaClipListList)source).getSelectionModel();
+ if ( selectionModel.isSelectionEmpty() ) {
+ return;
+ }
+ final int firstIndex = event.getFirstIndex();
+ final int lastIndex = event.getLastIndex();
+ final int leadIndex = selectionModel.getLeadSelectionIndex();
+ final int oldIndex = (leadIndex != firstIndex) ? firstIndex : lastIndex;
+ if ( firstIndex != lastIndex && oldIndex < ((QaClipListList)source).getModel().getSize() - 1 ) {
+ // Was a movement up or down in the list (not from newQaClip), save the state of the old QaClip
+ final QaClip qaClip = new QaClip();
+ adjustQaClip( qaClip );
+ if ( isQaClipComplete( qaClip ) ) {
+ ((QaClipListList)source).getListModel().setQaClipAt( oldIndex, qaClip );
+ }
+ }
+ if ( leadIndex < 0 || leadIndex >= ((QaClipListList)source).getModel().getSize() ) {
+ return;
+ }
+ final Object value = ((QaClipListList)source).getModel().getElementAt( leadIndex );
+ if ( value == null || !(value instanceof QaClip) ) {
+ return;
+ }
+ setQaClip( (QaClip)value );
+ }
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/OrderColorFactory.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/OrderColorFactory.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/OrderColorFactory.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/OrderColorFactory.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,66 @@
+package org.chboston.cnlp.timeline.gui.qaclipper.gui;
+
+import java.awt.*;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/13/13
+ */
+final public class OrderColorFactory {
+ static final private int STARTING_RED = 255 + 64;
+ static final private int STARTING_GREEN = 255 + 64;
+ static final private int STARTING_BLUE = 255 + 64;
+
+ static final private Map<Integer, Color> COLOR_MAP = new HashMap<>();
+
+ private OrderColorFactory() {
+ }
+
+ static private int getColorValue( final int startingColor, final int offset ) {
+ return Math.min( 255, Math.max( 0, startingColor - offset * 64 ) );
+ }
+
+ static public Color getColor( final int order ) {
+ if ( COLOR_MAP.containsKey( order ) ) {
+ return COLOR_MAP.get( order );
+ }
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ if ( order < 12 ) {
+ green = getColorValue( STARTING_GREEN, order );
+ blue = (green == 0) ? getColorValue( STARTING_BLUE, order - 4 ) : 0;
+ red = (green == 0 && blue == 0) ? getColorValue( STARTING_RED, order - 8 ) : 0;
+ } else if ( order < 16 ) {
+ green = getColorValue( STARTING_GREEN, order - 12 );
+ blue = getColorValue( STARTING_BLUE, order - 12 );
+ red = 0;
+ } else if ( order < 20 ) {
+ green = getColorValue( STARTING_GREEN, order - 16 );
+ blue = 0;
+ red = getColorValue( STARTING_RED, order - 16 );
+ } else if ( order < 24 ) {
+ green = 0;
+ blue = getColorValue( STARTING_BLUE, order - 20 );
+ red = getColorValue( STARTING_RED, order - 20 );
+ } else {
+ red = (int)(Math.random() * 255);
+ green = (int)(Math.random() * 255);
+ blue = (int)(Math.random() * 255);
+ }
+ final Color color = new Color( red, green, blue );
+ COLOR_MAP.put( order, color );
+ return color;
+ }
+
+
+ static public Color getComplimentColor( final Color color ) {
+ if ( color.getRed() < 128 && color.getGreen() < 96 && color.getBlue() < 128 ) {
+ return Color.WHITE;
+ }
+ return Color.BLACK;
+ }
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/OrderHighlightPanel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/OrderHighlightPanel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/OrderHighlightPanel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/OrderHighlightPanel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,173 @@
+package org.chboston.cnlp.timeline.gui.qaclipper.gui;
+
+import org.chboston.cnlp.nlp.annotation.textspan.DefaultDiscontiguousTextSpan;
+import org.chboston.cnlp.nlp.annotation.textspan.DefaultTextSpan;
+import org.chboston.cnlp.nlp.annotation.textspan.DiscontiguousTextSpan;
+import org.chboston.cnlp.nlp.annotation.textspan.TextSpan;
+
+import javax.swing.*;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.TableModel;
+import javax.swing.text.*;
+import java.awt.*;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 9/13/13
+ */
+public class OrderHighlightPanel extends TableTextHighlightPanel {
+
+ private final OrderedSpanPainter _textSpanPainter = new OrderedSpanPainter();
+
+ final private Map<TextSpan, Integer> _textSpanYs;
+ final private Map<TextSpan, Integer> _textSpanOrders;
+ // final private Collection<Object> _highlightTags;
+
+ public OrderHighlightPanel( final JTable table, final int textSpanColumn ) {
+ super( table, textSpanColumn );
+ _textSpanYs = new HashMap<>();
+ _textSpanOrders = new HashMap<>();
+ // _highlightTags = new HashSet<Object>();
+ table.getModel().addTableModelListener( new PrivateModelListener() );
+ }
+
+ private void highlightTextSpan( final TextSpan textSpan ) {
+ if ( textSpan instanceof DefaultDiscontiguousTextSpan ) {
+ for ( TextSpan textSpan1 : (DiscontiguousTextSpan)textSpan ) {
+ highlightTextSpan( textSpan1 );
+ }
+ return;
+ }
+ try {
+ final Rectangle bounds = modelToView( textSpan.getStartIndex() );
+ _textSpanYs.put( textSpan, bounds.y );
+ final Object tag = getHighlighter().addHighlight( textSpan.getStartIndex(), textSpan.getEndIndex(),
+ _textSpanPainter );
+ // _highlightTags.add( tag );
+ } catch ( BadLocationException blE ) {
+ System.err.println( blE.getMessage() );
+ }
+ }
+
+ private void clear() {
+ _textSpanYs.clear();
+ _textSpanOrders.clear();
+ // _highlightTags.clear();
+ final Highlighter highlighter = getHighlighter();
+ highlighter.removeAllHighlights();
+ }
+
+ private class PrivateModelListener implements TableModelListener {
+ public void tableChanged( final TableModelEvent event ) {
+ int firstRow = event.getFirstRow();
+ if ( firstRow == -1 ) {
+ clear();
+ return;
+ }
+ final TableModel tableModel = getTable().getModel();
+ // some table events ( e.g. AbstractTableModel.fireTableChanged() ) use Integer.MAX_VALUE
+ int lastRow = Math.max( 0, Math.min( tableModel.getRowCount() - 1, event.getLastRow() ) );
+ if ( event.getType() == TableModelEvent.DELETE ) {
+ clear();
+ firstRow = 0;
+ lastRow = tableModel.getRowCount() - 1;
+ }
+ for ( int i = firstRow; i <= lastRow; i++ ) {
+ final Object value1 = tableModel.getValueAt( i, 1 );
+ if ( value1 == null ) {
+ continue;
+ }
+ final int order = (Integer)value1;
+ final Object value2 = tableModel.getValueAt( i, 5 );
+ final TextSpan textSpan = (TextSpan)value2;
+ _textSpanOrders.put( textSpan, order );
+ highlightTextSpan( textSpan );
+ }
+ }
+ }
+
+ protected void paintScrollBarTrack( final Graphics g, final Rectangle trackBounds ) {
+ if ( _textSpanYs.isEmpty() ) {
+ super.paintScrollBarTrack( g, trackBounds );
+ return;
+ }
+ final double panelHeight = OrderHighlightPanel.this.getHeight();
+ final double trackHeight = trackBounds.height;
+ final double xForm = trackHeight / panelHeight;
+ final int rowHeight = Math.max( 2, (int)(getRowHeight() * xForm) );
+ for ( Map.Entry<TextSpan, Integer> textSpanY : _textSpanYs.entrySet() ) {
+ final int order = _textSpanOrders.get( textSpanY.getKey() );
+ final Color orderColor = OrderColorFactory.getColor( order );
+ g.setColor( orderColor );
+ final int trackY = trackBounds.y + (int)(textSpanY.getValue() * xForm);
+ g.fillRect( trackBounds.x, trackY, trackBounds.width, rowHeight );
+ }
+ super.paintScrollBarTrack( g, trackBounds );
+ }
+
+ private class OrderedSpanPainter extends LayeredHighlighter.LayerPainter {
+ public void paint( final Graphics g, final int offs0, final int offs1, final Shape bounds,
+ final JTextComponent comp ) {
+ // Do nothing: this method will never be called
+ }
+
+ public Shape paintLayer( final Graphics g, final int startIndex, final int stopIndex, final Shape bounds,
+ final JTextComponent comp, final View view ) {
+ final TextSpan key = new DefaultTextSpan( startIndex, stopIndex );
+ TextSpan textSpanOrderKey = null;
+ if ( _textSpanOrders.containsKey( key ) ) {
+ textSpanOrderKey = key;
+ } else {
+ // multi-line text will not match any textspan in the map, go through all textSpans and check for contains
+ for ( TextSpan orderSpan : _textSpanOrders.keySet() ) {
+ if ( orderSpan.getIntersectionSpan( key ).equals( key ) ) {
+ textSpanOrderKey = orderSpan;
+ break;
+ }
+ }
+ }
+ if ( textSpanOrderKey == null ) {
+ return null;
+ }
+ final Integer order = _textSpanOrders.get( textSpanOrderKey );
+ if ( order == null ) {
+ return null;
+ }
+ Rectangle alloc = null;
+ if ( startIndex == view.getStartOffset() && stopIndex == view.getEndOffset() ) {
+ if ( bounds instanceof Rectangle ) {
+ alloc = (Rectangle)bounds;
+ } else {
+ alloc = bounds.getBounds();
+ }
+ } else {
+ try {
+ final Shape shape = view.modelToView( startIndex,
+ Position.Bias.Forward, stopIndex,
+ Position.Bias.Backward, bounds );
+ alloc = (shape instanceof Rectangle) ? (Rectangle)shape
+ : shape.getBounds();
+ } catch ( BadLocationException blE ) {
+ return null;
+ }
+ }
+ final Color orderColor = OrderColorFactory.getColor( order );
+ g.setColor( orderColor );
+ final FontMetrics fm = comp.getFontMetrics( comp.getFont() );
+ final int lineHeight = fm.getHeight();
+ final int lineCount = alloc.height / lineHeight;
+ for ( int i = 0; i < lineCount; i++ ) {
+ final int baseline = alloc.y + lineHeight + i * lineHeight - fm.getDescent() + 1;
+ g.drawLine( alloc.x, baseline, alloc.x + alloc.width, baseline );
+ g.drawLine( alloc.x, baseline + 1, alloc.x + alloc.width, baseline + 1 );
+ }
+ return alloc;
+ }
+ }
+
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipComponent.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipComponent.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipComponent.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipComponent.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,16 @@
+package org.chboston.cnlp.timeline.gui.qaclipper.gui;
+
+import org.chboston.cnlp.timeline.gui.qaclipper.QaClip;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/15/13
+ */
+public interface QaClipComponent {
+
+ void setQaClip( final QaClip qaClip );
+
+ void adjustQaClip( final QaClip qaClip );
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipListList.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipListList.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipListList.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipListList.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,100 @@
+package org.chboston.cnlp.timeline.gui.qaclipper.gui;
+
+import org.chboston.cnlp.timeline.gui.qaclipper.QaClip;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.border.LineBorder;
+import java.awt.*;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/16/13
+ */
+public class QaClipListList extends JList {
+
+ public QaClipListList( final QaClipListModel model ) {
+ super( model );
+ setVisibleRowCount( 3 );
+ setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
+ setCellRenderer( new QaClipListRenderer() );
+ addKeyListener( new ListKeyListener() );
+ // addListSelectionListener( new SelectionScroller() );
+ }
+
+ public QaClipListModel getListModel() {
+ return (QaClipListModel)getModel();
+ }
+
+ static private class QaClipListRenderer extends DefaultListCellRenderer {
+ static private final Border BORDER = new LineBorder( Color.LIGHT_GRAY, 1 );
+
+ public Component getListCellRendererComponent( final JList list, final Object value, final int index,
+ final boolean isSelected, final boolean cellHasFocus ) {
+ if ( value == null || !(value instanceof QaClip) ) {
+ return super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
+ }
+ if ( value.equals( QaClipListModel.NEW_QA_CLIP ) ) {
+ return super.getListCellRendererComponent( list,
+ "New Question and Answer",
+ index,
+ isSelected,
+ cellHasFocus );
+ }
+ final QaClip qaClip = (QaClip)value;
+ final String textValue = qaClip.getQuestion() + " : " + qaClip.getAnswer();
+ return super.getListCellRendererComponent( list, textValue, index, isSelected, cellHasFocus );
+ }
+
+ public Border getBorder() {
+ return BORDER;
+ }
+ }
+
+ static private class ListKeyListener extends KeyAdapter {
+ public void keyTyped( final KeyEvent event ) {
+ final Object source = event.getSource();
+ if ( source == null || !(source instanceof QaClipListList) ) {
+ return;
+ }
+ final int listSize = ((QaClipListList)source).getModel().getSize();
+ final int selectedIndex = ((QaClipListList)source).getSelectedIndex();
+ if ( selectedIndex < 0 || selectedIndex >= listSize - 1 ) {
+ return;
+ }
+ final int keyChar = event.getKeyChar();
+ if ( keyChar == KeyEvent.VK_BACK_SPACE || keyChar == KeyEvent.VK_DELETE ) {
+// ((QaClipListList) source).clearSelection();
+ ((QaClipListList)source).setSelectedIndex( ((QaClipListList)source).getListModel().getSize() - 1 );
+ ((QaClipListList)source).getListModel().removeElementAt( selectedIndex );
+// ((QaClipListList) source).setSelectedIndex( ((QaClipListList) source).getListModel().getSize()-1 );
+ }
+ }
+ }
+
+ // Not working
+ // static private class SelectionScroller implements ListSelectionListener {
+ // public void valueChanged( final ListSelectionEvent event ) {
+ // if ( event == null || event.getValueIsAdjusting() ) {
+ // return;
+ // }
+ // final Object source = event.getSource();
+ // if ( !(source instanceof QaClipListList) ) {
+ // return;
+ // }
+ // final ListSelectionModel selectionModel = ((QaClipListList)source).getSelectionModel();
+ // if ( selectionModel.isSelectionEmpty() ) {
+ // return;
+ // }
+ // final int leadIndex = selectionModel.getLeadSelectionIndex();
+ // if ( leadIndex < 0 || leadIndex >= ((QaClipListList)source).getModel().getSize() ) {
+ // return;
+ // }
+ // ((QaClipListList)source).scrollRectToVisible( new Rectangle( 0, ((QaClipListList)source).getHeight()-10, 10, 10 ) );
+ // }
+ // }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipListModel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipListModel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipListModel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaClipListModel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,93 @@
+package org.chboston.cnlp.timeline.gui.qaclipper.gui;
+
+import org.chboston.cnlp.timeline.gui.qaclipper.QaClip;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/15/13
+ */
+public class QaClipListModel extends AbstractListModel {
+
+ static public final QaClip NEW_QA_CLIP = new QaClip();
+
+ private String _title;
+ private String _fullText;
+ private List<QaClip> _qaClips = new ArrayList<>();
+
+ public void setTitle( final String title ) {
+ _title = title;
+ }
+
+ public String getTitle() {
+ return _title;
+ }
+
+ public void setFullText( final String fullText ) {
+ _fullText = fullText;
+ fireContentsChanged( this, -1, -1 );
+ }
+
+ public String getFullText() {
+ return _fullText;
+ }
+
+
+ public void setQaClips( final List<QaClip> qaClips ) {
+ final int oldSize = getSize();
+ _qaClips.clear();
+ fireIntervalRemoved( this, 0, oldSize );
+ _qaClips.addAll( qaClips );
+ fireIntervalAdded( this, 0, getSize() );
+ }
+
+ public List<QaClip> getQaClips() {
+ return Collections.unmodifiableList( _qaClips );
+ }
+
+ public void addQaClip( final QaClip qaClip ) {
+ _qaClips.add( qaClip );
+ final int index = getSize() - 1;
+ fireIntervalAdded( this, index, index );
+ }
+
+ public void removeElementAt( final int index ) {
+ if ( index < 0 || index >= getSize() - 1 ) {
+ return;
+ }
+ _qaClips.remove( index );
+ fireIntervalRemoved( this, index, index );
+ }
+
+ public void setQaClipAt( final int index, final QaClip qaClip ) {
+ if ( qaClip != null ) {
+ _qaClips.set( index, qaClip );
+ fireContentsChanged( this, index, index );
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getSize() {
+ return _qaClips.size() + 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object getElementAt( final int index ) {
+ if ( index == getSize() - 1 ) {
+ return NEW_QA_CLIP;
+ }
+ return _qaClips.get( index );
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaConfidencePanel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaConfidencePanel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaConfidencePanel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaConfidencePanel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,28 @@
+package org.chboston.cnlp.timeline.gui.qaclipper.gui;
+
+import org.chboston.cnlp.timeline.gui.qaclipper.QaClip;
+import org.chboston.cnlp.timeline.gui.qaclipper.QaClipSaver;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/15/13
+ */
+public class QaConfidencePanel extends AbstractQaRadioPanel {
+
+ public QaConfidencePanel() {
+ super( QaClipSaver.CONFIDENCE_LABEL.trim(),
+ QaClip.Confidence.Uncertain.name(), QaClip.Confidence.Medium.name(), QaClip.Confidence.Certain.name() );
+ }
+
+ public void setQaClip( final QaClip qaClip ) {
+ final QaClip.Confidence confidence = qaClip.getConfidence();
+ setSelection( confidence.name() );
+ }
+
+ public void adjustQaClip( final QaClip qaClip ) {
+ final String selection = getSelection();
+ qaClip.setConfidence( selection );
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaDifficultyPanel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaDifficultyPanel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaDifficultyPanel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaDifficultyPanel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,28 @@
+package org.chboston.cnlp.timeline.gui.qaclipper.gui;
+
+import org.chboston.cnlp.timeline.gui.qaclipper.QaClip;
+import org.chboston.cnlp.timeline.gui.qaclipper.QaClipSaver;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/15/13
+ */
+public class QaDifficultyPanel extends AbstractQaRadioPanel {
+
+ public QaDifficultyPanel() {
+ super( QaClipSaver.DIFFICULTY_LABEL.trim(),
+ QaClip.Difficulty.Easy.name(), QaClip.Difficulty.Medium.name(), QaClip.Difficulty.Difficult.name() );
+ }
+
+ public void setQaClip( final QaClip qaClip ) {
+ final QaClip.Difficulty difficulty = qaClip.getDifficulty();
+ setSelection( difficulty.name() );
+ }
+
+ public void adjustQaClip( final QaClip qaClip ) {
+ final String selection = getSelection();
+ qaClip.setDifficulty( selection );
+ }
+
+}
Added: ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaDocTimeRelPanel.java
URL: http://svn.apache.org/viewvc/ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaDocTimeRelPanel.java?rev=1660963&view=auto
==============================================================================
--- ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaDocTimeRelPanel.java (added)
+++ ctakes/sandbox/timelanes/org/chboston/cnlp/timeline/gui/qaclipper/gui/QaDocTimeRelPanel.java Thu Feb 19 18:06:13 2015
@@ -0,0 +1,85 @@
+package org.chboston.cnlp.timeline.gui.qaclipper.gui;
+
+import org.chboston.cnlp.timeline.gui.qaclipper.QaClip;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+import java.awt.*;
+
+/**
+ * Author: SPF
+ * Affiliation: CHIP-NLP
+ * Date: 10/16/13
+ */
+public class QaDocTimeRelPanel extends JPanel implements QaClipComponent {
+
+ final private JRadioButton _beforeButton;
+ final private JRadioButton _overlapButton;
+ final private JRadioButton _afterButton;
+ final private JRadioButton _naButton;
+
+ protected QaDocTimeRelPanel() {
+ super( new GridLayout( 5, 1, 5, 5 ) );
+ setBorder( new LineBorder( Color.LIGHT_GRAY, 1 ) );
+
+ final JLabel titleLabel = new JLabel( "DocTimeRel to Use:" );
+ titleLabel.setBorder( new EmptyBorder( 0, 5, 0, 0 ) );
+ titleLabel.setFont( titleLabel.getFont().deriveFont( Font.BOLD ) );
+
+ _beforeButton = new JRadioButton( QaClip.QaDocTimeRel.Before.name() );
+ _overlapButton = new JRadioButton( QaClip.QaDocTimeRel.Overlap.name() );
+ _afterButton = new JRadioButton( QaClip.QaDocTimeRel.After.name() );
+ _naButton = new JRadioButton( QaClip.QaDocTimeRel.NA.name() );
+
+ final ButtonGroup buttonGroup = new ButtonGroup();
+ buttonGroup.add( _beforeButton );
+ buttonGroup.add( _overlapButton );
+ buttonGroup.add( _afterButton );
+ buttonGroup.add( _naButton );
+
+ add( titleLabel );
+ add( _beforeButton );
+ add( _overlapButton );
+ add( _afterButton );
+ add( _naButton );
+ }
+
+ public void setQaClip( final QaClip qaClip ) {
+ final QaClip.QaDocTimeRel docTimeRel = qaClip.getQaDocTimeRel();
+ setSelection( docTimeRel.name() );
+ }
+
+ public void adjustQaClip( final QaClip qaClip ) {
+ final String selection = getSelection();
+ qaClip.setQaDocTimeRel( selection );
+ }
+
+ public void reset() {
+ _naButton.setSelected( true );
+ }
+
+ protected String getSelection() {
+ if ( _beforeButton.isSelected() ) {
+ return _beforeButton.getText();
+ } else if ( _overlapButton.isSelected() ) {
+ return _overlapButton.getText();
+ } else if ( _afterButton.isSelected() ) {
+ return _afterButton.getText();
+ }
+ return _naButton.getText();
+ }
+
+ protected void setSelection( final String selection ) {
+ if ( _beforeButton.getText().equals( selection ) ) {
+ _beforeButton.setSelected( true );
+ } else if ( _overlapButton.getText().equals( selection ) ) {
+ _overlapButton.setSelected( true );
+ } else if ( _afterButton.getText().equals( selection ) ) {
+ _afterButton.setSelected( true );
+ } else {
+ _naButton.setSelected( true );
+ }
+ }
+
+}