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 2018/02/03 18:28:51 UTC

svn commit: r1823051 - in /ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core: ae/ cc/ cc/pretty/html/ cc/pretty/plaintext/ patient/ pipeline/

Author: seanfinan
Date: Sat Feb  3 18:28:51 2018
New Revision: 1823051

URL: http://svn.apache.org/viewvc?rev=1823051&view=rev
Log:
NamedEngine simple interface, default method
Extracted some file writing to allow more versatile extension
AbstractFileWriter and subclasses
PatientNoteStore now handles engine registration and pop methods
Piper files / cli now allow shortcut --htmlOut, similar to --xmiOut

Added:
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/ae/NamedEngine.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractFileWriter.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractJCasFileWriter.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/AbstractPatientFileWriter.java
Modified:
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractOutputFileWriter.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/FileTreeXmiWriter.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/pretty/html/HtmlTextWriter.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/pretty/plaintext/PrettyTextWriterFit.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/AbstractPatientConsumer.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/CliOptionalsHandler.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PipelineBuilder.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileReader.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileRunner.java
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/StandardCliOptions.java

Added: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/ae/NamedEngine.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/ae/NamedEngine.java?rev=1823051&view=auto
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/ae/NamedEngine.java (added)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/ae/NamedEngine.java Sat Feb  3 18:28:51 2018
@@ -0,0 +1,14 @@
+package org.apache.ctakes.core.ae;
+
+/**
+ * @author SPF , chip-nlp
+ * @version %I%
+ * @since 2/3/2018
+ */
+public interface NamedEngine {
+
+   default String getEngineName() {
+      return getClass().getSimpleName();
+   }
+
+}

Added: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractFileWriter.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractFileWriter.java?rev=1823051&view=auto
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractFileWriter.java (added)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractFileWriter.java Sat Feb  3 18:28:51 2018
@@ -0,0 +1,207 @@
+package org.apache.ctakes.core.cc;
+
+
+import org.apache.ctakes.core.config.ConfigParameterConstants;
+import org.apache.ctakes.core.util.DocumentIDAnnotationUtil;
+import org.apache.ctakes.typesystem.type.structured.DocumentIdPrefix;
+import org.apache.ctakes.typesystem.type.structured.DocumentPath;
+import org.apache.log4j.Logger;
+import org.apache.uima.UimaContext;
+import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
+import org.apache.uima.fit.component.JCasAnnotator_ImplBase;
+import org.apache.uima.fit.descriptor.ConfigurationParameter;
+import org.apache.uima.fit.util.JCasUtil;
+import org.apache.uima.jcas.JCas;
+import org.apache.uima.resource.ResourceInitializationException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * @author SPF , chip-nlp
+ * @version %I%
+ * @since 9/16/2016
+ */
+abstract public class AbstractFileWriter<T> extends JCasAnnotator_ImplBase {
+
+   static private final Logger LOGGER = Logger.getLogger( "AbstractOutputFileWriter" );
+
+   /**
+    * Name of configuration parameter that must be set to the path of a directory into which the
+    * output files will be written.
+    */
+   @ConfigurationParameter(
+         name = ConfigParameterConstants.PARAM_OUTPUTDIR,
+         description = ConfigParameterConstants.DESC_OUTPUTDIR
+   )
+   private File _outputRootDir;
+
+   @ConfigurationParameter(
+         name = ConfigParameterConstants.PARAM_SUBDIR,
+         description = ConfigParameterConstants.DESC_SUBDIR,
+         mandatory = false,
+         defaultValue = ""
+   )
+   private String _subDirectory;
+
+   static private final Object DATA_LOCK = new Object();
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void initialize( final UimaContext context ) throws ResourceInitializationException {
+      super.initialize( context );
+      if ( _subDirectory != null && !_subDirectory.isEmpty() ) {
+         final File subDirectory = new File( _outputRootDir, _subDirectory );
+         if ( !subDirectory.exists() ) {
+            subDirectory.mkdirs();
+         }
+      } else if ( !_outputRootDir.exists() ) {
+         _outputRootDir.mkdirs();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void process( final JCas jcas ) throws AnalysisEngineProcessException {
+      final String documentId = DocumentIDAnnotationUtil.getDocumentIdForFile( jcas );
+      final String outputDir = getOutputDirectory( jcas, getRootDirectory(), documentId );
+      final String fileName = getSourceFileName( jcas, documentId );
+      try {
+         synchronized ( DATA_LOCK ) {
+            createData( jcas );
+            final T data = getData();
+            writeFile( data, outputDir, documentId, fileName );
+            writeComplete( data );
+         }
+      } catch ( IOException ioE ) {
+         throw new AnalysisEngineProcessException( ioE );
+      }
+   }
+
+   /**
+    * @param jCas the jcas passed to the process( jcas ) method.
+    */
+   abstract protected void createData( JCas jCas );
+
+   /**
+    * @return the data to be written.
+    */
+   abstract protected T getData();
+
+   /**
+    * called after writing is complete
+    *
+    * @param data -
+    */
+   abstract protected void writeComplete( T data );
+
+   /**
+    * Write information into a file named based upon the document id and located based upon the document id prefix.
+    *
+    * @param data       data to be written
+    * @param outputDir  output directory
+    * @param documentId some id for the cas document
+    * @param fileName   name for the output file
+    * @throws IOException if anything goes wrong
+    */
+   abstract public void writeFile( final T data,
+                                   final String outputDir,
+                                   final String documentId,
+                                   final String fileName ) throws IOException;
+
+   /**
+    * @return the root directory set with the PARAM_OUTPUTDIR parameter
+    */
+   protected String getRootDirectory() {
+      return _outputRootDir.getPath();
+   }
+
+   /**
+    * @return the subdirectory set with the PARAM_SUBDIR parameter
+    */
+   protected String getSimpleSubDirectory() {
+      return _subDirectory;
+   }
+
+   /**
+    * @param jcas       ye olde
+    * @param rootPath   the root path for all output subdirectories and files
+    * @param documentId some id for the cas document
+    * @return the full output path up to but not including the fileName
+    */
+   protected String getOutputDirectory( final JCas jcas, final String rootPath, final String documentId ) {
+      String subDirectory = getSubdirectory( jcas, documentId );
+      if ( subDirectory == null || subDirectory.isEmpty() ) {
+         return rootPath;
+      }
+      final File outputDir = new File( rootPath + "/" + subDirectory );
+      outputDir.mkdirs();
+      return outputDir.getPath();
+   }
+
+   /**
+    * @param jCas       ye olde
+    * @param documentId some id for the cas document
+    * @return a subdirectory based upon the {@link DocumentIdPrefix} stored in the cas, or none if none
+    */
+   protected String getSubdirectory( final JCas jCas, final String documentId ) {
+      String subDirectory = "";
+      String subSeparator = "";
+      final String simpleSubDir = getSimpleSubDirectory();
+      if ( simpleSubDir != null && !simpleSubDir.isEmpty() ) {
+         subDirectory = simpleSubDir;
+         subSeparator = "/";
+      }
+      final Collection<DocumentIdPrefix> prefices = JCasUtil.select( jCas, DocumentIdPrefix.class );
+      if ( prefices == null || prefices.isEmpty() ) {
+         LOGGER.debug( "No subdirectory information for " + documentId );
+         return subDirectory;
+      }
+      for ( DocumentIdPrefix prefix : prefices ) {
+         String docSubDirectory = prefix.getDocumentIdPrefix();
+         if ( docSubDirectory != null && !docSubDirectory.isEmpty() ) {
+            return subDirectory + subSeparator + docSubDirectory;
+         }
+      }
+      LOGGER.debug( "No subdirectory information for " + documentId );
+      return subDirectory;
+   }
+
+   /**
+    * @param jCas ye olde
+    * @return the full path to the file containing the processed text, or an empty string ("") if unknown
+    */
+   protected String getSourceFilePath( final JCas jCas ) {
+      final Collection<DocumentPath> documentPaths = JCasUtil.select( jCas, DocumentPath.class );
+      if ( documentPaths == null || documentPaths.isEmpty() ) {
+         return "";
+      }
+      for ( DocumentPath documentPath : documentPaths ) {
+         final String path = documentPath.getDocumentPath();
+         if ( path != null && !path.isEmpty() ) {
+            return path;
+         }
+      }
+      return "";
+   }
+
+   /**
+    * @param jcas       ye olde
+    * @param documentId some id for the cas document
+    * @return a filename based upon the documentId
+    */
+   protected String getSourceFileName( final JCas jcas, final String documentId ) {
+      final String path = getSourceFilePath( jcas );
+      if ( path != null && !path.isEmpty() ) {
+         return new File( path ).getName();
+      }
+      return documentId;
+   }
+
+
+}

Added: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractJCasFileWriter.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractJCasFileWriter.java?rev=1823051&view=auto
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractJCasFileWriter.java (added)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractJCasFileWriter.java Sat Feb  3 18:28:51 2018
@@ -0,0 +1,45 @@
+package org.apache.ctakes.core.cc;
+
+
+import org.apache.uima.jcas.JCas;
+
+/**
+ * Writes information pulled directly from the jcas sent to the standard process( JCas ) method.
+ * Replaces poorly named AbstractOutputFileWriter.
+ *
+ * @author SPF , chip-nlp
+ * @version %I%
+ * @since 9/16/2016
+ */
+abstract public class AbstractJCasFileWriter extends AbstractFileWriter<JCas> {
+
+   private JCas _jCas;
+
+   /**
+    * Sets data to be written to the jcas.
+    *
+    * @param jCas ye olde
+    */
+   @Override
+   protected void createData( final JCas jCas ) {
+      _jCas = jCas;
+   }
+
+   /**
+    * @return the JCas.
+    */
+   @Override
+   protected JCas getData() {
+      return _jCas;
+   }
+
+   /**
+    * called after writing is complete
+    *
+    * @param data -
+    */
+   @Override
+   protected void writeComplete( final JCas data ) {
+   }
+
+}

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractOutputFileWriter.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractOutputFileWriter.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractOutputFileWriter.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/AbstractOutputFileWriter.java Sat Feb  3 18:28:51 2018
@@ -1,179 +1,16 @@
 package org.apache.ctakes.core.cc;
 
 
-import org.apache.ctakes.core.config.ConfigParameterConstants;
-import org.apache.ctakes.core.util.DocumentIDAnnotationUtil;
-import org.apache.ctakes.typesystem.type.structured.DocumentIdPrefix;
-import org.apache.ctakes.typesystem.type.structured.DocumentPath;
-import org.apache.log4j.Logger;
-import org.apache.uima.UimaContext;
-import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
-import org.apache.uima.cas.CAS;
-import org.apache.uima.cas.CASException;
-import org.apache.uima.fit.component.CasConsumer_ImplBase;
-import org.apache.uima.fit.descriptor.ConfigurationParameter;
-import org.apache.uima.fit.util.JCasUtil;
-import org.apache.uima.jcas.JCas;
-import org.apache.uima.resource.ResourceInitializationException;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
-
 /**
+ * @deprecated Use AbstractJCasFileWriter instead.
  * @author SPF , chip-nlp
  * @version %I%
  * @since 9/16/2016
  */
-abstract public class AbstractOutputFileWriter extends CasConsumer_ImplBase {
-
-   static private final Logger LOGGER = Logger.getLogger( "AbstractOutputFileWriter" );
+@Deprecated
+abstract public class AbstractOutputFileWriter extends AbstractJCasFileWriter {
 
 
-   /**
-    * Name of configuration parameter that must be set to the path of a directory into which the
-    * output files will be written.
-    */
-   @ConfigurationParameter(
-         name = ConfigParameterConstants.PARAM_OUTPUTDIR,
-         description = ConfigParameterConstants.DESC_OUTPUTDIR
-   )
-   private File _outputRootDir;
-
-   @ConfigurationParameter(
-         name = ConfigParameterConstants.PARAM_SUBDIR,
-         description = ConfigParameterConstants.DESC_SUBDIR,
-         mandatory = false,
-         defaultValue = ""
-   )
-   private String _subDirectory;
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void initialize( final UimaContext context ) throws ResourceInitializationException {
-      super.initialize( context );
-      if ( _subDirectory != null && !_subDirectory.isEmpty() ) {
-         final File subDirectory = new File( _outputRootDir, _subDirectory );
-         if ( !subDirectory.exists() ) {
-            subDirectory.mkdirs();
-         }
-      } else if ( !_outputRootDir.exists() ) {
-         _outputRootDir.mkdirs();
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void process( final CAS cas ) throws AnalysisEngineProcessException {
-      JCas jcas;
-      try {
-         jcas = cas.getJCas();
-      } catch ( CASException casE ) {
-         throw new AnalysisEngineProcessException( casE );
-      }
-      final String documentId = DocumentIDAnnotationUtil.getDocumentIdForFile( jcas );
-      final String outputDir = getOutputDirectory( jcas, _outputRootDir.getPath(), documentId );
-      final String fileName = getSourceFileName( jcas, documentId );
-      try {
-         writeFile( jcas, outputDir, documentId, fileName );
-      } catch ( IOException ioE ) {
-         throw new AnalysisEngineProcessException( ioE );
-      }
-   }
-
-
-   /**
-    * Write information into a file named based upon the document id and located based upon the document id prefix.
-    *
-    * @param jCas       ye olde
-    * @param outputDir  output directory
-    * @param documentId some id for the cas document
-    * @param fileName   name for the output file
-    * @throws IOException if anything goes wrong
-    */
-   abstract public void writeFile( final JCas jCas,
-                                   final String outputDir,
-                                   final String documentId,
-                                   final String fileName ) throws IOException;
-
-
-   /**
-    * @param jcas       ye olde
-    * @param rootPath   the root path for all output subdirectories and files
-    * @param documentId some id for the cas document
-    * @return the full output path up to but not including the fileName
-    */
-   protected String getOutputDirectory( final JCas jcas, final String rootPath, final String documentId ) {
-      String subDirectory = getSubdirectory( jcas, documentId );
-      if ( subDirectory == null || subDirectory.isEmpty() ) {
-         return rootPath;
-      }
-      final File outputDir = new File( rootPath + "/" + subDirectory );
-      outputDir.mkdirs();
-      return outputDir.getPath();
-   }
-
-   /**
-    * @param jCas       ye olde
-    * @param documentId some id for the cas document
-    * @return a subdirectory based upon the {@link DocumentIdPrefix} stored in the cas, or none if none
-    */
-   protected String getSubdirectory( final JCas jCas, final String documentId ) {
-      String subDirectory = "";
-      String subSeparator = "";
-      if ( _subDirectory != null && !_subDirectory.isEmpty() ) {
-         subDirectory = _subDirectory;
-         subSeparator = "/";
-      }
-      final Collection<DocumentIdPrefix> prefices = JCasUtil.select( jCas, DocumentIdPrefix.class );
-      if ( prefices == null || prefices.isEmpty() ) {
-         LOGGER.debug( "No subdirectory information for " + documentId );
-         return subDirectory;
-      }
-      for ( DocumentIdPrefix prefix : prefices ) {
-         String docSubDirectory = prefix.getDocumentIdPrefix();
-         if ( docSubDirectory != null && !docSubDirectory.isEmpty() ) {
-            return subDirectory + subSeparator + docSubDirectory;
-         }
-      }
-      LOGGER.debug( "No subdirectory information for " + documentId );
-      return subDirectory;
-   }
-
-   /**
-    * @param jCas ye olde
-    * @return the full path to the file containing the processed text, or an empty string ("") if unknown
-    */
-   protected String getSourceFilePath( final JCas jCas ) {
-      final Collection<DocumentPath> documentPaths = JCasUtil.select( jCas, DocumentPath.class );
-      if ( documentPaths == null || documentPaths.isEmpty() ) {
-         return "";
-      }
-      for ( DocumentPath documentPath : documentPaths ) {
-         final String path = documentPath.getDocumentPath();
-         if ( path != null && !path.isEmpty() ) {
-            return path;
-         }
-      }
-      return "";
-   }
-
-   /**
-    * @param jcas       ye olde
-    * @param documentId some id for the cas document
-    * @return a filename based upon the documentId
-    */
-   protected String getSourceFileName( final JCas jcas, final String documentId ) {
-      final String path = getSourceFilePath( jcas );
-      if ( path != null && !path.isEmpty() ) {
-         return new File( path ).getName();
-      }
-      return documentId;
-   }
 
 
 }

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/FileTreeXmiWriter.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/FileTreeXmiWriter.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/FileTreeXmiWriter.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/FileTreeXmiWriter.java Sat Feb  3 18:28:51 2018
@@ -28,7 +28,7 @@ import java.io.*;
       usables = { PipeBitInfo.TypeProduct.DOCUMENT_ID_PREFIX }
 )
 // TODO Create and extend AbstractInputFileReader  a'la the abstract writer
-final public class FileTreeXmiWriter extends AbstractOutputFileWriter {
+final public class FileTreeXmiWriter extends AbstractJCasFileWriter {
 
    static private final Logger LOGGER = Logger.getLogger( "FileTreeXmiWriter" );
 

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/pretty/html/HtmlTextWriter.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/pretty/html/HtmlTextWriter.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/pretty/html/HtmlTextWriter.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/pretty/html/HtmlTextWriter.java Sat Feb  3 18:28:51 2018
@@ -1,7 +1,7 @@
 package org.apache.ctakes.core.cc.pretty.html;
 
 
-import org.apache.ctakes.core.cc.AbstractOutputFileWriter;
+import org.apache.ctakes.core.cc.AbstractJCasFileWriter;
 import org.apache.ctakes.core.cc.pretty.SemanticGroup;
 import org.apache.ctakes.core.pipeline.PipeBitInfo;
 import org.apache.ctakes.core.util.DocumentIDAnnotationUtil;
@@ -53,7 +53,7 @@ import static org.apache.ctakes.core.pip
       dependencies = { DOCUMENT_ID, SENTENCE, BASE_TOKEN },
       usables = { DOCUMENT_ID_PREFIX, IDENTIFIED_ANNOTATION, EVENT, TIMEX, TEMPORAL_RELATION }
 )
-final public class HtmlTextWriter extends AbstractOutputFileWriter {
+final public class HtmlTextWriter extends AbstractJCasFileWriter {
 
    // TODO https://www.w3schools.com/howto/howto_css_switch.asp
    // TODO https://www.w3schools.com/html/tryit.asp?filename=tryhtml_layout_flexbox

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/pretty/plaintext/PrettyTextWriterFit.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/pretty/plaintext/PrettyTextWriterFit.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/pretty/plaintext/PrettyTextWriterFit.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/cc/pretty/plaintext/PrettyTextWriterFit.java Sat Feb  3 18:28:51 2018
@@ -2,7 +2,7 @@ package org.apache.ctakes.core.cc.pretty
 
 //import org.apache.log4j.Logger;
 
-import org.apache.ctakes.core.cc.AbstractOutputFileWriter;
+import org.apache.ctakes.core.cc.AbstractJCasFileWriter;
 import org.apache.ctakes.core.pipeline.PipeBitInfo;
 import org.apache.uima.UimaContext;
 import org.apache.uima.jcas.JCas;
@@ -32,7 +32,7 @@ import static org.apache.ctakes.core.pip
       dependencies = { DOCUMENT_ID, SENTENCE, BASE_TOKEN },
       usables = { DOCUMENT_ID_PREFIX, IDENTIFIED_ANNOTATION, EVENT, TIMEX, TEMPORAL_RELATION }
 )
-final public class PrettyTextWriterFit extends AbstractOutputFileWriter {
+final public class PrettyTextWriterFit extends AbstractJCasFileWriter {
 
 
    // delegate

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/AbstractPatientConsumer.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/AbstractPatientConsumer.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/AbstractPatientConsumer.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/AbstractPatientConsumer.java Sat Feb  3 18:28:51 2018
@@ -1,6 +1,8 @@
 package org.apache.ctakes.core.patient;
 
+import org.apache.ctakes.core.ae.NamedEngine;
 import org.apache.ctakes.core.pipeline.PipeBitInfo;
+import org.apache.ctakes.core.util.SourceMetadataUtil;
 import org.apache.log4j.Logger;
 import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
 import org.apache.uima.fit.component.JCasAnnotator_ImplBase;
@@ -24,7 +26,7 @@ import java.util.Collection;
       name = "AbstractPatientConsumer",
       description = "Abstract Engine to take action on a patient level instead of document level.", role = PipeBitInfo.Role.ANNOTATOR
 )
-abstract public class AbstractPatientConsumer extends JCasAnnotator_ImplBase {
+abstract public class AbstractPatientConsumer extends JCasAnnotator_ImplBase implements NamedEngine {
 
    static public final String REMOVE_PATIENT = "RemovePatient";
 
@@ -41,6 +43,7 @@ abstract public class AbstractPatientCon
    protected AbstractPatientConsumer( final String aeName, final String action ) {
       _action = action;
       _logger = Logger.getLogger( aeName );
+      PatientNoteStore.getInstance().registerEngine( getEngineName() );
    }
 
    /**
@@ -48,13 +51,7 @@ abstract public class AbstractPatientCon
     */
    @Override
    public void process( final JCas jCas ) throws AnalysisEngineProcessException {
-      final Collection<String> completedPatientIds = PatientNoteStore.getInstance().getCompletedPatientIds();
-      for ( String id : completedPatientIds ) {
-         process( id );
-         if ( _removePatient ) {
-            PatientNoteStore.getInstance().removePatient( id );
-         }
-      }
+      processPatients();
    }
 
    /**
@@ -63,30 +60,16 @@ abstract public class AbstractPatientCon
    @Override
    public void collectionProcessComplete() throws AnalysisEngineProcessException {
       super.collectionProcessComplete();
-      final Collection<String> allPatientIds = PatientNoteStore.getInstance().getStoredPatientIds();
-      for ( String id : allPatientIds ) {
-         process( id );
-         if ( _removePatient ) {
-            PatientNoteStore.getInstance().removePatient( id );
-         }
-      }
+      processPatients();
    }
 
-   /**
-    * Logs start and finish and calls {@link #processPatientCas(JCas)}.
-    *
-    * @param patientName -
-    * @throws AnalysisEngineProcessException if subclass has a problem processing.
-    */
-   private void process( final String patientName ) throws AnalysisEngineProcessException {
-      if ( patientName == null ) {
-         return;
+   protected void processPatients() throws AnalysisEngineProcessException {
+      final Collection<JCas> completedCases = PatientNoteStore.getInstance().popPatientCases( getEngineName() );
+      for ( JCas patientCas : completedCases ) {
+         final String patientName = SourceMetadataUtil.getPatientIdentifier( patientCas );
+         _logger.info( _action + " for patient " + patientName + " ..." );
+         processPatientCas( patientCas );
       }
-      _logger.info( _action + " for patient " + patientName + " ..." );
-
-      processPatientCas( PatientNoteStore.getInstance().getFullPatientCas( patientName ) );
-
-      _logger.info( "Finished." );
    }
 
    /**

Added: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/AbstractPatientFileWriter.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/AbstractPatientFileWriter.java?rev=1823051&view=auto
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/AbstractPatientFileWriter.java (added)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/AbstractPatientFileWriter.java Sat Feb  3 18:28:51 2018
@@ -0,0 +1,84 @@
+package org.apache.ctakes.core.patient;
+
+import org.apache.ctakes.core.ae.NamedEngine;
+import org.apache.ctakes.core.cc.AbstractFileWriter;
+import org.apache.log4j.Logger;
+import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
+import org.apache.uima.jcas.JCas;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+
+
+/**
+ * Writes data for patient level Jcas.
+ *
+ * @author SPF , chip-nlp
+ * @version %I%
+ * @since 2/3/2018
+ */
+abstract public class AbstractPatientFileWriter
+      extends AbstractFileWriter<Collection<JCas>>
+      implements NamedEngine {
+
+   static private final Logger LOGGER = Logger.getLogger( "AbstractPatientFileWriter" );
+
+   private final Collection<JCas> _patientCases = new HashSet<>();
+
+   protected void AbstractFileWriter() {
+      PatientNoteStore.getInstance().registerEngine( getEngineName() );
+   }
+
+   /**
+    * @param jCas the jcas passed to the process( jcas ) method.
+    */
+   @Override
+   protected void createData( final JCas jCas ) {
+      _patientCases.addAll( PatientNoteStore.getInstance().popPatientCases( getEngineName() ) );
+   }
+
+   /**
+    * @return completed patient JCases
+    */
+   @Override
+   protected Collection<JCas> getData() {
+      return _patientCases;
+   }
+
+   /**
+    * called after writing is complete
+    *
+    * @param data -
+    */
+   @Override
+   protected void writeComplete( final Collection<JCas> data ) {
+      _patientCases.clear();
+   }
+
+   /**
+    * @param jCas       ignored
+    * @param documentId ignored
+    * @return the subdirectory set with the PARAM_SUBDIR parameter
+    */
+   @Override
+   protected String getSubdirectory( final JCas jCas, final String documentId ) {
+      return getSimpleSubDirectory();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public void collectionProcessComplete() throws AnalysisEngineProcessException {
+      super.collectionProcessComplete();
+      final String outputDir = getOutputDirectory( null, getRootDirectory(), "" );
+      createData( null );
+      try {
+         writeFile( getData(), outputDir, "", "" );
+      } catch ( IOException ioE ) {
+         throw new AnalysisEngineProcessException( ioE );
+      }
+   }
+
+}

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/patient/PatientNoteStore.java Sat Feb  3 18:28:51 2018
@@ -32,6 +32,8 @@ public enum PatientNoteStore {
 
    static private final Logger LOGGER = Logger.getLogger( "PatientNoteStore" );
 
+   private final Collection<String> _registeredEngines;
+   private final Map<String, Collection<String>> _enginesRun;
    private final Map<String, JCas> _patientMap;
    private final Map<String, Collection<ViewInfo>> _patientViewInfos;
    private final Map<String, Integer> _wantedDocCounts;
@@ -40,6 +42,8 @@ public enum PatientNoteStore {
     * private
     */
    PatientNoteStore() {
+      _registeredEngines = new HashSet<>();
+      _enginesRun = new HashMap<>();
       _patientMap = new HashMap<>();
       _patientViewInfos = new HashMap<>();
       _wantedDocCounts = new HashMap<>();
@@ -47,9 +51,15 @@ public enum PatientNoteStore {
 
    /////////////////    Get available patient, document, view names   ///////////////
 
+   synchronized public void registerEngine( final String engineName ) {
+      _registeredEngines.add( engineName );
+   }
+
    /**
+    * @deprecated use pop methods
     * @return identifiers for all stored patients
     */
+   @Deprecated
    synchronized public Collection<String> getStoredPatientIds() {
       return _patientMap.keySet().stream()
             .sorted()
@@ -324,6 +334,7 @@ public enum PatientNoteStore {
    /////////////////    patient cleanup - careful !   ///////////////
 
    /**
+    * Use popPatientCas instead to automate cleanup
     * @param patientId -
     */
    synchronized public JCas getFullPatientCas( final String patientId ) {
@@ -331,6 +342,61 @@ public enum PatientNoteStore {
    }
 
    /**
+    * @param engineName engine requesting a completed patient jcas
+    * @return a patient jcas or null if none is available for the given engine
+    */
+   synchronized public JCas popPatientCas( final String engineName ) {
+      if ( !_registeredEngines.contains( engineName ) ) {
+         throw new IllegalArgumentException( "Engine not registered to use patients " + engineName );
+      }
+      final Collection<String> completedPatientIds = getCompletedPatientIds();
+      for ( String patientId : completedPatientIds ) {
+         final JCas patientCas = popPatientCas( patientId, engineName );
+         if ( patientCas != null ) {
+            return patientCas;
+         }
+      }
+      return null;
+   }
+
+   /**
+    * @param engineName engine requesting a completed patient jcas
+    * @return a patient jcas or null if none is available for the given engine
+    */
+   synchronized public Collection<JCas> popPatientCases( final String engineName ) {
+      if ( !_registeredEngines.contains( engineName ) ) {
+         throw new IllegalArgumentException( "Engine not registered to use patients " + engineName );
+      }
+      final Collection<String> completedPatientIds = new ArrayList<>( getCompletedPatientIds() );
+      return completedPatientIds.stream()
+                                .map( id -> popPatientCas( id, engineName ) )
+                                .filter( Objects::nonNull )
+                                .collect( Collectors.toList() );
+   }
+
+   /**
+    * @param patientId  -
+    * @param engineName engine requesting a completed patient jcas
+    * @return the patient jcas for the patient id or null if it isn't available for the given engine
+    */
+   synchronized public JCas popPatientCas( final String patientId, final String engineName ) {
+      if ( !_registeredEngines.contains( engineName ) ) {
+         throw new IllegalArgumentException( "Engine not registered to use patients " + engineName );
+      }
+      final Collection<String> enginesRun = _enginesRun.computeIfAbsent( patientId, n -> new HashSet<>() );
+      final boolean newRun = enginesRun.add( engineName );
+      if ( !newRun ) {
+         return null;
+      }
+      final JCas patientCas = _patientMap.get( patientId );
+      if ( enginesRun.size() == _registeredEngines.size() ) {
+         removePatient( patientId );
+      }
+      return patientCas;
+   }
+
+
+   /**
     * @param patientId identifier of patient to remove from cache
     */
    synchronized public void removePatient( final String patientId ) {

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/CliOptionalsHandler.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/CliOptionalsHandler.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/CliOptionalsHandler.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/CliOptionalsHandler.java Sat Feb  3 18:28:51 2018
@@ -37,6 +37,8 @@ final public class CliOptionalsHandler {
             return optionals.getUmlsPassword();
          case "xmiOut":
             return optionals.getXmiOutDirectory();
+         case "htmlOut":
+            return optionals.getHtmlOutDirectory();
          case "a":
             return optionals.getOption_a();
          case "b":

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PipelineBuilder.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PipelineBuilder.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PipelineBuilder.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PipelineBuilder.java Sat Feb  3 18:28:51 2018
@@ -2,6 +2,7 @@ package org.apache.ctakes.core.pipeline;
 
 
 import org.apache.ctakes.core.cc.FileTreeXmiWriter;
+import org.apache.ctakes.core.cc.pretty.html.HtmlTextWriter;
 import org.apache.ctakes.core.config.ConfigParameterConstants;
 import org.apache.ctakes.core.cr.FileTreeReader;
 import org.apache.ctakes.core.util.PropertyAeFactory;
@@ -311,6 +312,31 @@ final public class PipelineBuilder {
             ConfigParameterConstants.PARAM_OUTPUTDIR, outputDirectory );
    }
 
+   /**
+    * Adds ae that writes an html file at the end of the pipeline.
+    * Relies upon {@link ConfigParameterConstants#PARAM_OUTPUTDIR} having been specified
+    * Use of this method is order-specific.
+    *
+    * @return this PipelineBuilder
+    * @throws ResourceInitializationException if the html writer engine cannot be created
+    */
+   public PipelineBuilder writeHtml() throws ResourceInitializationException {
+      return addLast( HtmlTextWriter.class, Collections.emptyList() );
+   }
+
+   /**
+    * Adds ae that writes an html file at the end of the pipeline.
+    * Use of this method is order-specific.
+    *
+    * @param outputDirectory directory in which html files should be written
+    * @return this PipelineBuilder
+    * @throws ResourceInitializationException if the html writer engine cannot be created
+    */
+   public PipelineBuilder writeHtml( final String outputDirectory ) throws ResourceInitializationException {
+      return addLast( HtmlTextWriter.class, Collections.emptyList(),
+            ConfigParameterConstants.PARAM_OUTPUTDIR, outputDirectory );
+   }
+
    public PipelineBuilder threads( final int threadCount ) {
       if ( threadCount <= 1 ) {
          if ( threadCount < 1 ) {

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileReader.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileReader.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileReader.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileReader.java Sat Feb  3 18:28:51 2018
@@ -240,6 +240,13 @@ final public class PiperFileReader {
                _builder.writeXMIs( info );
             }
             return true;
+         case "writeHtml":
+            if ( info.isEmpty() ) {
+               _builder.writeHtml();
+            } else {
+               _builder.writeHtml( info );
+            }
+            return true;
          default:
             LOGGER.error( "Unknown Piper Command: " + command );
             return false;

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileRunner.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileRunner.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileRunner.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileRunner.java Sat Feb  3 18:28:51 2018
@@ -49,10 +49,14 @@ final public class PiperFileRunner {
          final String outputDir = options.getOutputDirectory();
          // if xmi output directory is set but standard output directory is not, use xmi out as standard out
          final String xmiOutDir = options.getXmiOutDirectory();
+         // if html output directory is set but standard output directory is not, use html out as standard out
+         final String htmlOutDir = options.getHtmlOutDirectory();
          if ( !outputDir.isEmpty() ) {
             builder.set( ConfigParameterConstants.PARAM_OUTPUTDIR, outputDir );
          } else if ( !xmiOutDir.isEmpty() ) {
             builder.set( ConfigParameterConstants.PARAM_OUTPUTDIR, xmiOutDir );
+         } else if ( !htmlOutDir.isEmpty() ) {
+            builder.set( ConfigParameterConstants.PARAM_OUTPUTDIR, htmlOutDir );
          }
          // load the piper file
          reader.setCliOptionals( options );
@@ -68,6 +72,12 @@ final public class PiperFileRunner {
                builder.writeXMIs( xmiOutDir );
             }
          }
+         if ( !htmlOutDir.isEmpty() ) {
+            if ( builder.getAeNames().stream().map( String::toLowerCase )
+                        .noneMatch( n -> n.contains( "htmlwriter" ) ) ) {
+               builder.writeHtml( htmlOutDir );
+            }
+         }
          // run the pipeline
          builder.run();
       } catch ( UIMAException | IOException multE ) {

Modified: ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/StandardCliOptions.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/StandardCliOptions.java?rev=1823051&r1=1823050&r2=1823051&view=diff
==============================================================================
--- ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/StandardCliOptions.java (original)
+++ ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/StandardCliOptions.java Sat Feb  3 18:28:51 2018
@@ -45,6 +45,12 @@ interface StandardCliOptions {
    String getXmiOutDirectory();
 
    @Option(
+         longName = "htmlOut",
+         description = "path to the directory where html files are to be written.  Adds HtmlWriter to pipeline.",
+         defaultValue = "" )
+   String getHtmlOutDirectory();
+
+   @Option(
          shortName = "l",
          longName = "lookupXml",
          description = "path to the xml file containing information for dictionary lookup configuration.",