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 2017/04/06 02:12:06 UTC

svn commit: r1790337 - in /ctakes/trunk: ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/ ctakes-gui-res/src/main/resources/org/apache/ctakes/gui/pipeline/icon/ ctakes-gui/src/main/java/org/apache/ctakes/gui/component/ ctakes-gui/src/main/jav...

Author: seanfinan
Date: Thu Apr  6 02:12:06 2017
New Revision: 1790337

URL: http://svn.apache.org/viewvc?rev=1790337&view=rev
Log:
ctakes-424 : improved markup
ctakes-424 : added load and package buttons
ctakes-424 : changed validate icon
ctakes-424 : added header comment

Added:
    ctakes/trunk/ctakes-gui-res/src/main/resources/org/apache/ctakes/gui/pipeline/icon/Folder_Blue.png   (with props)
    ctakes/trunk/ctakes-gui-res/src/main/resources/org/apache/ctakes/gui/pipeline/icon/RunReady.png   (with props)
    ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/component/TextLineNumber.java
Removed:
    ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/piper/PiperValidator.java
Modified:
    ctakes/trunk/ctakes-core/src/main/java/org/apache/ctakes/core/pipeline/PiperFileReader.java
    ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/component/PositionedSplitPane.java
    ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/MainPanel2.java
    ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/piper/PiperTextFilter.java

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=1790337&r1=1790336&r2=1790337&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 Thu Apr  6 02:12:06 2017
@@ -151,15 +151,15 @@ final public class PiperFileReader {
       }
    }
 
-   public void parsePipelineLine( final String line ) throws UIMAException {
+   public boolean parsePipelineLine( final String line ) throws UIMAException {
       if ( line.isEmpty() || line.startsWith( "//" ) || line.startsWith( "#" ) || line.startsWith( "!" ) ) {
-         return;
+         return true;
       }
       final int spaceIndex = line.indexOf( ' ' );
       if ( spaceIndex < 0 ) {
-         addToPipeline( line, "" );
+         return addToPipeline( line, "" );
       } else {
-         addToPipeline( line.substring( 0, spaceIndex ), line.substring( spaceIndex + 1 ).trim() );
+         return addToPipeline( line.substring( 0, spaceIndex ), line.substring( spaceIndex + 1 ).trim() );
       }
    }
 
@@ -175,20 +175,20 @@ final public class PiperFileReader {
     * @param parameter specified by second word in the file line
     * @throws UIMAException if the command could not be executed
     */
-   private void addToPipeline( final String command, final String parameter ) throws UIMAException {
+   private boolean addToPipeline( final String command, final String parameter ) throws UIMAException {
       switch ( command ) {
          case "load":
             loadPipelineFile( parameter );
-            break;
+            return true;
          case "package":
             _userPackages.add( parameter );
-            break;
+            return true;
          case "set":
             _builder.set( splitParameters( parameter ) );
-            break;
+            return true;
          case "cli":
             _builder.set( getCliParameters( parameter ) );
-            break;
+            return true;
          case "reader":
             if ( hasParameters( parameter ) ) {
                final String[] component_parameters = splitFromParameters( parameter );
@@ -198,14 +198,14 @@ final public class PiperFileReader {
             } else {
                _builder.reader( getReaderClass( parameter ) );
             }
-            break;
+            return true;
          case "readFiles":
             if ( parameter.isEmpty() ) {
                _builder.readFiles();
             } else {
                _builder.readFiles( parameter );
             }
-            break;
+            return true;
          case "add":
             if ( hasParameters( parameter ) ) {
                final String[] component_parameters = splitFromParameters( parameter );
@@ -215,7 +215,7 @@ final public class PiperFileReader {
             } else {
                _builder.add( getComponentClass( parameter ) );
             }
-            break;
+            return true;
          case "addLogged":
             if ( hasParameters( parameter ) ) {
                final String[] component_parameters = splitFromParameters( parameter );
@@ -225,7 +225,7 @@ final public class PiperFileReader {
             } else {
                _builder.addLogged( getComponentClass( parameter ) );
             }
-            break;
+            return true;
          case "addDescription":
             if ( hasParameters( parameter ) ) {
                final String[] descriptor_parameters = splitFromParameters( parameter );
@@ -237,7 +237,7 @@ final public class PiperFileReader {
                final AnalysisEngineDescription description = createDescription( parameter );
                _builder.addDescription( description );
             }
-            break;
+            return true;
          case "addLast":
             if ( hasParameters( parameter ) ) {
                final String[] component_parameters = splitFromParameters( parameter );
@@ -247,22 +247,23 @@ final public class PiperFileReader {
             } else {
                _builder.addLast( getComponentClass( parameter ) );
             }
-            break;
+            return true;
          case "collectCuis":
             _builder.collectCuis();
-            break;
+            return true;
          case "collectEntities":
             _builder.collectEntities();
-            break;
+            return true;
          case "writeXmis":
             if ( parameter.isEmpty() ) {
                _builder.writeXMIs();
             } else {
                _builder.writeXMIs( parameter );
             }
-            break;
+            return true;
          default:
-            LOGGER.error( "Unknown Command: " + command );
+            LOGGER.error( "Unknown Piper Command: " + command );
+            return false;
       }
    }
 

Added: ctakes/trunk/ctakes-gui-res/src/main/resources/org/apache/ctakes/gui/pipeline/icon/Folder_Blue.png
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-gui-res/src/main/resources/org/apache/ctakes/gui/pipeline/icon/Folder_Blue.png?rev=1790337&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ctakes/trunk/ctakes-gui-res/src/main/resources/org/apache/ctakes/gui/pipeline/icon/Folder_Blue.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: ctakes/trunk/ctakes-gui-res/src/main/resources/org/apache/ctakes/gui/pipeline/icon/RunReady.png
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-gui-res/src/main/resources/org/apache/ctakes/gui/pipeline/icon/RunReady.png?rev=1790337&view=auto
==============================================================================
Binary file - no diff available.

Propchange: ctakes/trunk/ctakes-gui-res/src/main/resources/org/apache/ctakes/gui/pipeline/icon/RunReady.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Modified: ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/component/PositionedSplitPane.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/component/PositionedSplitPane.java?rev=1790337&r1=1790336&r2=1790337&view=diff
==============================================================================
--- ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/component/PositionedSplitPane.java (original)
+++ ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/component/PositionedSplitPane.java Thu Apr  6 02:12:06 2017
@@ -13,6 +13,7 @@ final public class PositionedSplitPane e
 
    static private final Logger LOGGER = Logger.getLogger( "PositionedSplitPane" );
 
+   private final Object LOCKER = new Object();
    private boolean _isLocationSet;
    private int _pixelLocation = -1;
    private double _proportionalLocation = -1d;
@@ -82,15 +83,17 @@ final public class PositionedSplitPane e
     */
    @Override
    public void paint( final Graphics g ) {
-      if ( !_isLocationSet || getDividerLocation() < 0 ) {
-         if ( _pixelLocation > 0 ) {
-            super.setDividerLocation( _pixelLocation );
-         } else if ( _proportionalLocation > 0 ) {
-            super.setDividerLocation( _proportionalLocation );
+      synchronized ( LOCKER ) {
+         if ( !_isLocationSet ) {
+            if ( _pixelLocation > 0 ) {
+               super.setDividerLocation( _pixelLocation );
+            } else if ( _proportionalLocation > 0 ) {
+               super.setDividerLocation( _proportionalLocation );
+            }
+            _isLocationSet = true;
          }
-         _isLocationSet = true;
+         super.paint( g );
       }
-      super.paint( g );
    }
 
 }

Added: ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/component/TextLineNumber.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/component/TextLineNumber.java?rev=1790337&view=auto
==============================================================================
--- ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/component/TextLineNumber.java (added)
+++ ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/component/TextLineNumber.java Thu Apr  6 02:12:06 2017
@@ -0,0 +1,435 @@
+package org.apache.ctakes.gui.component;
+
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.MatteBorder;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.*;
+import java.awt.*;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author SPF , chip-nlp
+ * @version %I%
+ * @since 4/5/2017
+ */
+public class TextLineNumber extends JPanel
+      implements CaretListener, DocumentListener, PropertyChangeListener {
+
+   public final static float LEFT = 0.0f;
+   public final static float CENTER = 0.5f;
+   public final static float RIGHT = 1.0f;
+
+   private final static Border OUTER = new MatteBorder( 0, 0, 0, 2, Color.GRAY );
+
+   private final static int HEIGHT = Integer.MAX_VALUE - 1000000;
+
+   //  Text component this TextTextLineNumber component is in sync with
+
+   private JTextComponent component;
+
+   //  Properties that can be changed
+
+   private boolean updateFont;
+   private int borderGap;
+   private Color currentLineForeground;
+   private float digitAlignment;
+   private int minimumDisplayDigits;
+
+   //  Keep history information to reduce the number of times the component
+   //  needs to be repainted
+
+   private int lastDigits;
+   private int lastHeight;
+   private int lastLine;
+
+   private Map<String, FontMetrics> fonts;
+
+   /**
+    * Create a line number component for a text component. This minimum
+    * display width will be based on 3 digits.
+    *
+    * @param component the related text component
+    */
+   public TextLineNumber( JTextComponent component ) {
+      this( component, 3 );
+   }
+
+   /**
+    * Create a line number component for a text component.
+    *
+    * @param component            the related text component
+    * @param minimumDisplayDigits the number of digits used to calculate
+    *                             the minimum width of the component
+    */
+   public TextLineNumber( JTextComponent component, int minimumDisplayDigits ) {
+      this.component = component;
+
+      setFont( component.getFont() );
+      setForeground( Color.DARK_GRAY );
+
+      setBorderGap( 5 );
+      setCurrentLineForeground( Color.MAGENTA );
+      setDigitAlignment( RIGHT );
+      setMinimumDisplayDigits( minimumDisplayDigits );
+
+      component.getDocument().addDocumentListener( this );
+      component.addCaretListener( this );
+      component.addPropertyChangeListener( "font", this );
+   }
+
+   /**
+    * Gets the update font property
+    *
+    * @return the update font property
+    */
+   public boolean getUpdateFont() {
+      return updateFont;
+   }
+
+   /**
+    * Set the update font property. Indicates whether this Font should be
+    * updated automatically when the Font of the related text component
+    * is changed.
+    *
+    * @param updateFont when true update the Font and repaint the line
+    *                   numbers, otherwise just repaint the line numbers.
+    */
+   public void setUpdateFont( boolean updateFont ) {
+      this.updateFont = updateFont;
+   }
+
+   /**
+    * Gets the border gap
+    *
+    * @return the border gap in pixels
+    */
+   public int getBorderGap() {
+      return borderGap;
+   }
+
+   /**
+    * The border gap is used in calculating the left and right insets of the
+    * border. Default value is 5.
+    *
+    * @param borderGap the gap in pixels
+    */
+   public void setBorderGap( int borderGap ) {
+      this.borderGap = borderGap;
+      Border inner = new EmptyBorder( 0, borderGap, 0, borderGap );
+      setBorder( new CompoundBorder( OUTER, inner ) );
+      lastDigits = 0;
+      setPreferredWidth();
+   }
+
+   /**
+    * Gets the current line rendering Color
+    *
+    * @return the Color used to render the current line number
+    */
+   public Color getCurrentLineForeground() {
+      return currentLineForeground == null ? getForeground() : currentLineForeground;
+   }
+
+   /**
+    * The Color used to render the current line digits. Default is Coolor.RED.
+    *
+    * @param currentLineForeground the Color used to render the current line
+    */
+   public void setCurrentLineForeground( Color currentLineForeground ) {
+      this.currentLineForeground = currentLineForeground;
+   }
+
+   /**
+    * Gets the digit alignment
+    *
+    * @return the alignment of the painted digits
+    */
+   public float getDigitAlignment() {
+      return digitAlignment;
+   }
+
+   /**
+    * Specify the horizontal alignment of the digits within the component.
+    * Common values would be:
+    * <ul>
+    * <li>TextLineNumber.LEFT
+    * <li>TextLineNumber.CENTER
+    * <li>TextLineNumber.RIGHT (default)
+    * </ul>
+    *
+    * @param digitAlignment LEFT, CENTER, of RIGHT
+    */
+   public void setDigitAlignment( float digitAlignment ) {
+      this.digitAlignment =
+            digitAlignment > 1.0f ? 1.0f : digitAlignment < 0.0f ? -1.0f : digitAlignment;
+   }
+
+   /**
+    * Gets the minimum display digits
+    *
+    * @return the minimum display digits
+    */
+   public int getMinimumDisplayDigits() {
+      return minimumDisplayDigits;
+   }
+
+   /**
+    * Specify the mimimum number of digits used to calculate the preferred
+    * width of the component. Default is 3.
+    *
+    * @param minimumDisplayDigits the number digits used in the preferred
+    *                             width calculation
+    */
+   public void setMinimumDisplayDigits( int minimumDisplayDigits ) {
+      this.minimumDisplayDigits = minimumDisplayDigits;
+      setPreferredWidth();
+   }
+
+   /**
+    * Calculate the width needed to display the maximum line number
+    */
+   private void setPreferredWidth() {
+      Element root = component.getDocument().getDefaultRootElement();
+      int lines = root.getElementCount();
+      int digits = Math.max( String.valueOf( lines ).length(), minimumDisplayDigits );
+
+      //  Update sizes when number of digits in the line number changes
+
+      if ( lastDigits != digits ) {
+         lastDigits = digits;
+         FontMetrics fontMetrics = getFontMetrics( getFont() );
+         int width = fontMetrics.charWidth( '0' ) * digits;
+         Insets insets = getInsets();
+         int preferredWidth = insets.left + insets.right + width;
+
+         Dimension d = getPreferredSize();
+         d.setSize( preferredWidth, HEIGHT );
+         setPreferredSize( d );
+         setSize( d );
+      }
+   }
+
+   /**
+    * Draw the line numbers
+    */
+   @Override
+   public void paintComponent( Graphics g ) {
+      super.paintComponent( g );
+
+      //	Determine the width of the space available to draw the line number
+
+      FontMetrics fontMetrics = component.getFontMetrics( component.getFont() );
+      Insets insets = getInsets();
+      int availableWidth = getSize().width - insets.left - insets.right;
+
+      //  Determine the rows to draw within the clipped bounds.
+
+      Rectangle clip = g.getClipBounds();
+      int rowStartOffset = component.viewToModel( new Point( 0, clip.y ) );
+      int endOffset = component.viewToModel( new Point( 0, clip.y + clip.height ) );
+
+      while ( rowStartOffset <= endOffset ) {
+         try {
+            if ( isCurrentLine( rowStartOffset ) ) {
+               g.setColor( getCurrentLineForeground() );
+            } else {
+               g.setColor( getForeground() );
+            }
+
+            //  Get the line number as a string and then determine the
+            //  "X" and "Y" offsets for drawing the string.
+
+            String lineNumber = getTextLineNumber( rowStartOffset );
+            int stringWidth = fontMetrics.stringWidth( lineNumber );
+            int x = getOffsetX( availableWidth, stringWidth ) + insets.left;
+            int y = getOffsetY( rowStartOffset, fontMetrics );
+            g.drawString( lineNumber, x, y );
+
+            //  Move to the next row
+
+            rowStartOffset = Utilities.getRowEnd( component, rowStartOffset ) + 1;
+         } catch ( Exception e ) {
+            break;
+         }
+      }
+   }
+
+   /*
+    *  We need to know if the caret is currently positioned on the line we
+    *  are about to paint so the line number can be highlighted.
+    */
+   private boolean isCurrentLine( int rowStartOffset ) {
+      int caretPosition = component.getCaretPosition();
+      Element root = component.getDocument().getDefaultRootElement();
+
+      if ( root.getElementIndex( rowStartOffset ) == root.getElementIndex( caretPosition ) ) {
+         return true;
+      } else {
+         return false;
+      }
+   }
+
+   /*
+    *	Get the line number to be drawn. The empty string will be returned
+    *  when a line of text has wrapped.
+    */
+   protected String getTextLineNumber( int rowStartOffset ) {
+      Element root = component.getDocument().getDefaultRootElement();
+      int index = root.getElementIndex( rowStartOffset );
+      Element line = root.getElement( index );
+
+      if ( line.getStartOffset() == rowStartOffset ) {
+         return String.valueOf( index + 1 );
+      } else {
+         return "";
+      }
+   }
+
+   /*
+    *  Determine the X offset to properly align the line number when drawn
+    */
+   private int getOffsetX( int availableWidth, int stringWidth ) {
+      return (int)((availableWidth - stringWidth) * digitAlignment);
+   }
+
+   /*
+    *  Determine the Y offset for the current row
+    */
+   private int getOffsetY( int rowStartOffset, FontMetrics fontMetrics )
+         throws BadLocationException {
+      //  Get the bounding rectangle of the row
+
+      Rectangle r = component.modelToView( rowStartOffset );
+      int lineHeight = fontMetrics.getHeight();
+      int y = r.y + r.height;
+      int descent = 0;
+
+      //  The text needs to be positioned above the bottom of the bounding
+      //  rectangle based on the descent of the font(s) contained on the row.
+
+      if ( r.height == lineHeight )  // default font is being used
+      {
+         descent = fontMetrics.getDescent();
+      } else  // We need to check all the attributes for font changes
+      {
+         if ( fonts == null ) {
+            fonts = new HashMap<String, FontMetrics>();
+         }
+
+         Element root = component.getDocument().getDefaultRootElement();
+         int index = root.getElementIndex( rowStartOffset );
+         Element line = root.getElement( index );
+
+         for ( int i = 0; i < line.getElementCount(); i++ ) {
+            Element child = line.getElement( i );
+            AttributeSet as = child.getAttributes();
+            String fontFamily = (String)as.getAttribute( StyleConstants.FontFamily );
+            Integer fontSize = (Integer)as.getAttribute( StyleConstants.FontSize );
+            String key = fontFamily + fontSize;
+
+            FontMetrics fm = fonts.get( key );
+
+            if ( fm == null ) {
+               Font font = new Font( fontFamily, Font.PLAIN, fontSize );
+               fm = component.getFontMetrics( font );
+               fonts.put( key, fm );
+            }
+
+            descent = Math.max( descent, fm.getDescent() );
+         }
+      }
+
+      return y - descent;
+   }
+
+   //
+//  Implement CaretListener interface
+//
+   @Override
+   public void caretUpdate( CaretEvent e ) {
+      //  Get the line the caret is positioned on
+
+      int caretPosition = component.getCaretPosition();
+      Element root = component.getDocument().getDefaultRootElement();
+      int currentLine = root.getElementIndex( caretPosition );
+
+      //  Need to repaint so the correct line number can be highlighted
+
+      if ( lastLine != currentLine ) {
+         repaint();
+         lastLine = currentLine;
+      }
+   }
+
+   //
+//  Implement DocumentListener interface
+//
+   @Override
+   public void changedUpdate( DocumentEvent e ) {
+      documentChanged();
+   }
+
+   @Override
+   public void insertUpdate( DocumentEvent e ) {
+      documentChanged();
+   }
+
+   @Override
+   public void removeUpdate( DocumentEvent e ) {
+      documentChanged();
+   }
+
+   /*
+    *  A document change may affect the number of displayed lines of text.
+    *  Therefore the lines numbers will also change.
+    */
+   private void documentChanged() {
+      //  View of the component has not been updated at the time
+      //  the DocumentEvent is fired
+
+      SwingUtilities.invokeLater( new Runnable() {
+         @Override
+         public void run() {
+            try {
+               int endPos = component.getDocument().getLength();
+               Rectangle rect = component.modelToView( endPos );
+
+               if ( rect != null && rect.y != lastHeight ) {
+                  setPreferredWidth();
+                  repaint();
+                  lastHeight = rect.y;
+               }
+            } catch ( BadLocationException ex ) { /* nothing to do */ }
+         }
+      } );
+   }
+
+   //
+//  Implement PropertyChangeListener interface
+//
+   @Override
+   public void propertyChange( PropertyChangeEvent evt ) {
+      if ( evt.getNewValue() instanceof Font ) {
+         if ( updateFont ) {
+            Font newFont = (Font)evt.getNewValue();
+            setFont( newFont );
+            lastDigits = 0;
+            setPreferredWidth();
+         } else {
+            repaint();
+         }
+      }
+   }
+
+
+}

Modified: ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/MainPanel2.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/MainPanel2.java?rev=1790337&r1=1790336&r2=1790337&view=diff
==============================================================================
--- ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/MainPanel2.java (original)
+++ ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/MainPanel2.java Thu Apr  6 02:12:06 2017
@@ -1,10 +1,7 @@
 package org.apache.ctakes.gui.pipeline;
 
 import org.apache.ctakes.core.pipeline.PipeBitInfo;
-import org.apache.ctakes.gui.component.DisablerPane;
-import org.apache.ctakes.gui.component.LoggerPanel;
-import org.apache.ctakes.gui.component.PositionedSplitPane;
-import org.apache.ctakes.gui.component.SmoothTipList;
+import org.apache.ctakes.gui.component.*;
 import org.apache.ctakes.gui.pipeline.bit.PipeBitFinder;
 import org.apache.ctakes.gui.pipeline.bit.available.AvailablesListModel;
 import org.apache.ctakes.gui.pipeline.bit.info.*;
@@ -29,9 +26,12 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import static javax.swing.JOptionPane.PLAIN_MESSAGE;
@@ -61,8 +61,12 @@ final class MainPanel2 extends JPanel {
    private JButton _runButton;
    private JButton _helpButton;
 
-   private JButton _setButton;
+   private JTextPane _textPane;
+
    private JButton _addButton;
+   private JButton _setButton;
+   private JButton _loadButton;
+   private JButton _packageButton;
 
 
    MainPanel2() {
@@ -81,6 +85,7 @@ final class MainPanel2 extends JPanel {
       _chooser.setFileFilter( new FileNameExtensionFilter( "Pipeline Definition (Piper) File", "piper" ) );
       _chooser.setFileView( new PiperFileView() );
 
+      createNewPiper();
    }
 
 
@@ -120,8 +125,11 @@ final class MainPanel2 extends JPanel {
    private JComponent createEastPanel() {
       _piperDocument = new DefaultStyledDocument();
       _piperTextFilter = new PiperTextFilter( _piperDocument );
-      final JTextPane _textPane = new JTextPane( _piperDocument );
+      _textPane = new JTextPane( _piperDocument );
+      _textPane.putClientProperty( "caretWidth", 2 );
       final JScrollPane scroll = new JScrollPane( _textPane );
+      final TextLineNumber lineNumber = new TextLineNumber( _textPane, 2 );
+      scroll.setRowHeaderView( lineNumber );
       scroll.setMinimumSize( new Dimension( 100, 10 ) );
       return scroll;
    }
@@ -203,10 +211,7 @@ final class MainPanel2 extends JPanel {
       final JToolBar toolBar = new JToolBar( SwingConstants.VERTICAL );
       toolBar.setFloatable( false );
       toolBar.setRollover( true );
-
-      _setButton = addVerticalButton( toolBar, "Set Global Parameter" );
-      _setButton.addActionListener( new SetAction() );
-
+      toolBar.addSeparator( new Dimension( 0, 30 ) );
 
       final AddAction addAction = new AddAction();
       final ParameterTableModel parameterModel = _infoPanel.getParameterModel();
@@ -214,15 +219,59 @@ final class MainPanel2 extends JPanel {
       _addButton = addVerticalButton( toolBar, "Add selected Pipe Bit" );
       _addButton.addActionListener( addAction );
       _addButton.setEnabled( false );
+      toolBar.addSeparator( new Dimension( 0, 60 ) );
+      _setButton = addVerticalButton( toolBar, "Set Global Parameter" );
+      _setButton.addActionListener( new SetAction() );
+
+      toolBar.add( Box.createVerticalGlue() );
+      _loadButton = addVerticalButton( toolBar, "Load SubPiper" );
+      _loadButton.addActionListener( new LoadAction() );
+
+      _packageButton = addVerticalButton( toolBar, "Add Package" );
+      _packageButton.addActionListener( new PackageAction() );
 
       SwingUtilities.invokeLater( new CommandIconLoader() );
       return toolBar;
    }
 
 
+   private void createNewPiper() {
+      final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern( "MMMM dd, yyyy" );
+      final LocalDate date = LocalDate.now();
+      final String text = "//       ***  Piper File  ***\n" +
+                          "//       Created by " + System.getProperty( "user.name" ) + "\n" +
+                          "//       on " + date.format( dateFormatter ) + "\n\n";
+      try {
+         _piperDocument.remove( 0, _piperDocument.getLength() );
+         _piperDocument.insertString( 0, text, null );
+         _textPane.setCaretPosition( _piperDocument.getLength() );
+      } catch ( BadLocationException blE ) {
+         LOGGER.warn( blE.getMessage() );
+      }
+      _runButton.setEnabled( false );
+   }
 
 
-
+   private int getInsertCaret() throws BadLocationException {
+      int caret = _textPane.getCaretPosition();
+      if ( caret > 1 && _piperDocument.getText( caret - 1, 1 ).charAt( 0 ) == '\n' ) {
+         return caret;
+      }
+      final int length = _piperDocument.getLength() - caret;
+      final String docText = _piperDocument.getText( caret, length );
+      boolean careted = false;
+      for ( int i = 0; i < length - 1; i++ ) {
+         if ( docText.charAt( i ) == '\n' ) {
+            caret += i + 1;
+            careted = true;
+            break;
+         }
+      }
+      if ( !careted ) {
+         caret = _piperDocument.getLength();
+      }
+      return caret;
+   }
 
 
 
@@ -255,7 +304,7 @@ final class MainPanel2 extends JPanel {
          final String newPng = "NewPiper.png";
          final String openPng = "OpenPiper.png";
          final String savePng = "SavePiper.png";
-         final String validatePng = "CheckRun.png";
+         final String validatePng = "RunReady.png";
          final String runPng = "RunPiper.png";
          final String helpPng = "Help_32.png";
          final Icon newIcon = IconLoader.loadIcon( dir + newPng );
@@ -276,11 +325,7 @@ final class MainPanel2 extends JPanel {
    private final class NewPiperAction implements ActionListener {
       @Override
       public void actionPerformed( final ActionEvent event ) {
-         try {
-            _piperDocument.remove( 0, _piperDocument.getLength() );
-         } catch ( BadLocationException blE ) {
-            LOGGER.warn( blE.getMessage() );
-         }
+         createNewPiper();
       }
    }
 
@@ -305,6 +350,7 @@ final class MainPanel2 extends JPanel {
          } catch ( BadLocationException blE ) {
             LOGGER.warn( blE.getMessage() );
          }
+         _runButton.setEnabled( false );
       }
    }
 
@@ -326,8 +372,6 @@ final class MainPanel2 extends JPanel {
             }
             final String text = _piperDocument.getText( 0, _piperDocument.getLength() );
             Files.write( Paths.get( path ), text.getBytes() );
-            _piperDocument.remove( 0, _piperDocument.getLength() );
-            _piperDocument.insertString( 0, text, null );
          } catch ( BadLocationException | IOException multE ) {
             LOGGER.warn( multE.getMessage() );
          }
@@ -350,11 +394,12 @@ final class MainPanel2 extends JPanel {
    private final class ValidateAction implements ActionListener {
       @Override
       public void actionPerformed( final ActionEvent event ) {
-         if ( _piperTextFilter == null || _addButton == null ) {
+         if ( _piperTextFilter == null || _runButton == null ) {
             return;
          }
+         LOGGER.info( "Validating Piper File ..." );
          final boolean valid = _piperTextFilter.validateText();
-         _addButton.setEnabled( valid );
+         _runButton.setEnabled( valid );
       }
    }
 
@@ -400,13 +445,18 @@ final class MainPanel2 extends JPanel {
       @Override
       public void run() {
          final String dir = "org/apache/ctakes/gui/pipeline/icon/";
-//         final String arrow = "BlueRightArrow.png";
-         final String setPng = "Parameters.png";
          final String addPng = "PlusMark.png";
-         final Icon setIcon = IconLoader.loadIcon( dir + setPng );
+         final String setPng = "Parameters.png";
+         final String loadPng = "BlueGearYellowGear.png";
+         final String packagePng = "Folder_Blue.png";
          final Icon addIcon = IconLoader.loadIcon( dir + addPng );
-         _setButton.setIcon( setIcon );
+         final Icon setIcon = IconLoader.loadIcon( dir + setPng );
+         final Icon loadIcon = IconLoader.loadIcon( dir + loadPng );
+         final Icon packageIcon = IconLoader.loadIcon( dir + packagePng );
          _addButton.setIcon( addIcon );
+         _setButton.setIcon( setIcon );
+         _loadButton.setIcon( loadIcon );
+         _packageButton.setIcon( packageIcon );
       }
    }
 
@@ -419,13 +469,43 @@ final class MainPanel2 extends JPanel {
             // Would require tracking.  Or maybe a constant parse and track of variables set when the formatting is done.
             // Then the "add" button would need to be enabled accordingly ...
             // and the parameter table could display the mapped value.
-            _piperDocument.insertString( _piperDocument.getLength(), "set ", null );
+            final int caret = getInsertCaret();
+            _piperDocument.insertString( caret, "\n// Set a global value\nset ", null );
          } catch ( BadLocationException blE ) {
             LOGGER.error( blE.getMessage() );
          }
+         _runButton.setEnabled( false );
       }
    }
 
+   private final class LoadAction implements ActionListener {
+      @Override
+      public void actionPerformed( final ActionEvent event ) {
+         try {
+            final int caret = getInsertCaret();
+            _piperDocument.insertString( caret, "\n// Load a Piper file containing a partial Pipeline\nload ", null );
+         } catch ( BadLocationException blE ) {
+            LOGGER.error( blE.getMessage() );
+         }
+         _runButton.setEnabled( false );
+      }
+   }
+
+   private final class PackageAction implements ActionListener {
+      @Override
+      public void actionPerformed( final ActionEvent event ) {
+         try {
+            final int caret = getInsertCaret();
+            _piperDocument
+                  .insertString( caret, "\n// Add a Package that contains Pipe Bits or Piper files\npackage ", null );
+         } catch ( BadLocationException blE ) {
+            LOGGER.error( blE.getMessage() );
+         }
+         _runButton.setEnabled( false );
+      }
+   }
+
+   static private final Function<String, String> maybeQuote = t -> t.contains( " " ) ? "\"" + t + "\"" : t;
 
    private final class AddAction implements ActionListener, TableModelListener {
       @Override
@@ -453,20 +533,26 @@ final class MainPanel2 extends JPanel {
                  !Arrays.equals( value, holder.getParameter( i ).defaultValue() ) ) {
                helpSb.append( "#   " ).append( holder.getParameterName( i ) )
                      .append( "  " ).append( holder.getParameterDescription( i ) ).append( "\n" );
-               final String valueText = Arrays.stream( value ).collect( Collectors.joining( "," ) );
+               final String valueText = Arrays.stream( value ).map( maybeQuote ).collect( Collectors.joining( "," ) );
                parmSb.append( " " ).append( holder.getParameterName( i ) ).append( "=" ).append( valueText );
             }
          }
          sb.append( helpSb.toString() );
-         sb.append( "add " ).append( _infoPanel.getPipeBitClass().getName() );
+         final PipeBitInfo info = _infoPanel.getPipeBitInfo();
+         if ( info.role() == PipeBitInfo.Role.READER ) {
+            sb.append( "reader " );
+         } else {
+            sb.append( "add " );
+         }
+         sb.append( _infoPanel.getPipeBitClass().getName() );
          sb.append( parmSb.toString() ).append( "\n" );
          try {
-//            _piperDocument.insertString( _textPane.getCaretPosition(), sb.toString(), null );
-            // tODO publicize textpane
-            _piperDocument.insertString( _piperDocument.getLength(), sb.toString(), null );
+            final int caret = getInsertCaret();
+            _piperDocument.insertString( caret, sb.toString(), null );
          } catch ( BadLocationException blE ) {
             LOGGER.error( blE.getMessage() );
          }
+         _runButton.setEnabled( false );
       }
 
       @Override

Modified: ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/piper/PiperTextFilter.java
URL: http://svn.apache.org/viewvc/ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/piper/PiperTextFilter.java?rev=1790337&r1=1790336&r2=1790337&view=diff
==============================================================================
--- ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/piper/PiperTextFilter.java (original)
+++ ctakes/trunk/ctakes-gui/src/main/java/org/apache/ctakes/gui/pipeline/piper/PiperTextFilter.java Thu Apr  6 02:12:06 2017
@@ -49,6 +49,9 @@ final public class PiperTextFilter exten
    public void insertString( final FilterBypass fb, final int begin, final String text, final AttributeSet attr )
          throws BadLocationException {
       super.insertString( fb, begin, text, attr );
+//      if ( shouldValidate( fb.getDocument(), begin, text.length() ) ) {
+//         validateText();
+//      } else
       if ( shouldReformat( fb.getDocument(), begin, text.length() ) ) {
          formatText( fb.getDocument() );
       }
@@ -62,6 +65,9 @@ final public class PiperTextFilter exten
                         final AttributeSet attrs )
          throws BadLocationException {
       super.replace( fb, begin, length, text, attrs );
+//      if ( shouldValidate( fb.getDocument(), begin, length ) ) {
+//         validateText();
+//      } else
       if ( shouldReformat( fb.getDocument(), begin, length ) ) {
          formatText( fb.getDocument() );
       }
@@ -80,6 +86,13 @@ final public class PiperTextFilter exten
       }
    }
 
+   static private boolean shouldValidate( final Document document, final int begin, final int length )
+         throws BadLocationException {
+      final int testLength = Math.min( length + 2, document.getLength() - begin );
+      final String deltaText = document.getText( begin, testLength );
+      return deltaText.contains( "\n" );
+   }
+
    public boolean validateText() {
       final ExecutorService executor = Executors.newSingleThreadExecutor();
       Future<Boolean> valid = executor.submit( (Callable<Boolean>)_textValidator );
@@ -124,51 +137,79 @@ final public class PiperTextFilter exten
       }
 
       private void createStyles() {
-         createStyle( "PLAIN", Color.BLACK, "PLAIN" );
-         createStyle( "COMMENT", Color.GRAY, "COMMENT" );
-         final Style error = createStyle( "ERROR", Color.RED, "ERROR" );
-         StyleConstants.setStrikeThrough( error, true );
+         createStyle( "PLAIN", Color.BLACK, false, "PLAIN" );
+         final Style comment = createStyle( "COMMENT", Color.GRAY, false, "COMMENT" );
+         StyleConstants.setItalic( comment, true );
+         final Style error = createStyle( "ERROR", Color.RED, false, "ERROR" );
+         StyleConstants.setUnderline( error, true );
+         createStyle( "PIPE_BIT", Color.BLUE, false, "PIPE_BIT" );
+         createStyle( "BOLD_PIPE_BIT", Color.BLUE, "BOLD_PIPE_BIT" );
          createStyle( "PARAMETER", Color.YELLOW, "PARAMETER" );
          createStyle( "LOAD", Color.MAGENTA, "load" );
          createStyle( "PACKAGE", Color.YELLOW.darker(), "package" );
          createStyle( "SET", Color.ORANGE.darker(), "set", "cli" );
          createStyle( "READER", Color.GREEN.darker().darker(), "reader", "readFiles" );
          createStyle( "ADD", Color.CYAN.darker().darker(), "add", "addLogged", "addDescription", "addLast" );
-         createStyle( "WRITE_XMI", Color.BLUE, "writeXmis", "collectCuis", "collectEntities" );
+         createStyle( "WRITE_XMI", Color.BLUE.darker(), "writeXmis", "collectCuis", "collectEntities" );
       }
 
       private Style createStyle( final String name, final Color color, final String... keys ) {
+         return createStyle( name, color, true, keys );
+      }
+
+      private Style createStyle( final String name, final Color color, final boolean bold, final String... keys ) {
          final Style style = _document.addStyle( name, null );
          StyleConstants.setForeground( style, color );
+         if ( bold ) {
+            StyleConstants.setBold( style, true );
+         }
          Arrays.stream( keys ).forEach( k -> _styles.put( k, style ) );
          return style;
       }
 
-      void formatLine( final int begin, final int end ) throws BadLocationException {
+      boolean formatLine( final int begin, final int end ) throws BadLocationException {
          final int length = end - begin;
          if ( length <= 0 ) {
-            return;
+            return true;
          }
          final String text = _document.getText( begin, length );
          if ( text.startsWith( "#" ) || text.startsWith( "//" ) || text.startsWith( "!" ) ) {
             _document.setCharacterAttributes( begin, length, _styles.get( "COMMENT" ), true );
-            return;
+            return true;
          }
          int commandEnd = text.indexOf( ' ' );
          if ( commandEnd < 0 ) {
             commandEnd = length;
          }
-         final Style commandStyle = getCommandStyle( text.substring( 0, commandEnd ) );
+         final String command = text.substring( 0, commandEnd );
+         final Style commandStyle = getCommandStyle( command );
          _document.setCharacterAttributes( begin, commandEnd, commandStyle, true );
          if ( length > commandEnd ) {
-            _document.setCharacterAttributes( begin + commandEnd, length - commandEnd, _styles.get( "PLAIN" ), true );
+            int styleEnd = commandEnd + 1;
+            if ( command.equals( "reader" ) || command.startsWith( "add" ) ) {
+               final int bitStart = commandEnd + 1;
+               int bitEnd = text.indexOf( ' ', bitStart );
+               if ( bitEnd < 0 ) {
+                  bitEnd = length;
+               }
+               int bitBold = bitStart;
+               final String pipeBitText = text.substring( bitStart, bitEnd );
+               final int dotIndex = pipeBitText.lastIndexOf( '.' );
+               if ( dotIndex > 0 ) {
+                  bitBold = bitStart + dotIndex + 1;
+                  _document.setCharacterAttributes(
+                        begin + bitStart, bitBold - bitStart, _styles.get( "PIPE_BIT" ), true );
+               }
+               _document
+                     .setCharacterAttributes( begin + bitBold, bitEnd - bitBold, _styles.get( "BOLD_PIPE_BIT" ), true );
+               styleEnd = bitEnd;
+            }
+            _document.setCharacterAttributes( begin + styleEnd, length - styleEnd, _styles.get( "PLAIN" ), true );
          }
-//         int nextSpace = text.indexOf( ' ', commandEnd );
-//         while ( nextSpace > 0 ) {
-//
-//
-//            nextSpace = text.indexOf( ' ', nextSpace + 1 );
-//         }
+         if ( commandStyle.equals( _styles.get( "ERROR" ) ) ) {
+            return false;
+         }
+         return true;
       }
 
       private Style getCommandStyle( final String command ) {
@@ -178,7 +219,8 @@ final public class PiperTextFilter exten
          }
          return style;
       }
-//      private Style getParameterStyle( final String parameter ) {
+
+      //      private Style getParameterStyle( final String parameter ) {
 //         final Style style = _styles.get( command );
 //         if ( style == null ) {
 //            return _styles.get( "ERROR" );
@@ -190,6 +232,7 @@ final public class PiperTextFilter exten
 
    static private final class TextValidator extends TextFormatter implements Callable<Boolean> {
       final private PiperFileReader _reader;
+      private boolean _haveReader;
 
       private TextValidator( final StyledDocument document ) {
          super( document );
@@ -198,6 +241,7 @@ final public class PiperTextFilter exten
 
       @Override
       public Boolean call() {
+         _haveReader = false;
          boolean valid = true;
          try {
             final String text = _document.getText( 0, _document.getLength() );
@@ -208,6 +252,8 @@ final public class PiperTextFilter exten
                if ( text.charAt( i ) == '\n' ) {
                   if ( validateLine( lineBegin, i ) ) {
                      formatLine( lineBegin, i );
+                  } else {
+                     valid = false;
                   }
                   lineBegin = i + 1;
                   lineEnded = true;
@@ -223,25 +269,36 @@ final public class PiperTextFilter exten
          }
          _document.setCharacterAttributes( _document.getLength(), _document.getLength(), _styles.get( "PLAIN" ), true );
          _reader.getBuilder().clear();
+         if ( !_haveReader ) {
+            LOGGER.warn( "No Reader specified" );
+            return false;
+         }
          return valid;
       }
 
       private boolean validateLine( final int begin, final int end ) throws BadLocationException {
          final int length = end - begin;
          if ( length <= 0 ) {
-            return false;
+            return true;
          }
          final String text = _document.getText( begin, length );
          if ( text.startsWith( "#" ) || text.startsWith( "//" ) || text.startsWith( "!" ) ) {
             return true;
+         } else if ( text.startsWith( "readFiles " ) || text.startsWith( "reader " ) ) {
+            if ( _haveReader ) {
+               LOGGER.warn( "More than one Reader specified" );
+               _document.setCharacterAttributes( begin, end, _styles.get( "ERROR" ), true );
+               return false;
+            }
+            _haveReader = true;
          }
          try {
-            _reader.parsePipelineLine( text );
+            return _reader.parsePipelineLine( text );
          } catch ( UIMAException uE ) {
+            LOGGER.warn( uE.getMessage() );
             _document.setCharacterAttributes( begin, end, _styles.get( "ERROR" ), true );
             return false;
          }
-         return true;
       }
    }