//----------------------------------------------------------------------------
// COMPONENT NAME: LPEX Editor
//
// © Copyright IBM Corporation 2006, 2007
// All Rights Reserved.
//
// DESCRIPTION:
// WordsCommand - sample user-defined command (words)
//----------------------------------------------------------------------------

package com.ibm.lpex.samples;

import com.ibm.lpex.core.LpexCommand;
import com.ibm.lpex.core.LpexDocumentLocation;
import com.ibm.lpex.core.LpexView;

/**
 * Sample command <b>words</b> - count the words in the current view.
 * Displays the number of visible words and non-space characters in the text.
 * If a selection is in effect, only the selected text is considered.
 *
 * <p>A simple scanning for letters and digits is used, rather than a
 * BreakIterator, which is sufficient for most types of programming source
 * documents.  A word can be optionally defined to consist of any consecutive
 * non-whitespace characters.</p>
 *
 * <p>Here is the WordsCommand
 * <a href="doc-files/WordsCommand.java.html">source code</a>.</p>
 *
 * <p>To run this sample:
 * <ul>
 *  <li>Define this user command via an editor preference page, where available,
 *    or from the editor command line:
 *    <pre>set commandClass.words com.ibm.lpex.samples.WordsCommand</pre></li>
 *  <li>Run it from the editor command line:
 *    <pre>words [any]</pre></li>
 * </ul></p>
 *
 * @see com.ibm.lpex.samples All the samples
 */
public class WordsCommand implements LpexCommand
{
 /**
  * Runs this command.  Displays the number of words, characters, and
  * lines in the visible text or selection.
  *
  * @param lpexView the document view in which the command was issued
  * @param parameters optional parameter: "?" for help, or "any" to define
  *                   a word as a string of any non-whitespace characters
  */
 public boolean doCommand(LpexView lpexView, String parameters)
 {
  if (lpexView != null)
   {
    // word == any string of consecutive non-space(s)?
    boolean wordIsAny = false;

    parameters = parameters.trim();
    if (parameters.length() != 0)
     {
      if ("?".equals(parameters))
       {
        lpexView.doCommand("set messageText Syntax: words [any]");
        return true;
       }
      if ("any".equals(parameters))
       {
        wordIsAny = true;
       }
      else
       {
        lpexView.doCommand("set messageText \"" + parameters +
                           "\" is not a correct parameter for the words command.");
        return false;
       }
     }

    int words = 0;
    int characters = 0;
    int lines = 0;

    // determine the range of elements to set
    int firstElement, lastElement;
    boolean selection;
    // 1.- visible selection in this view
    if (lpexView.queryOn("block.inView") && lpexView.queryOn("block.anythingSelected"))
     {
      selection = true;
      int linesBeforeStart = lpexView.queryInt("lines.beforeStart");
      firstElement = lpexView.queryInt("block.topElement") - linesBeforeStart;
      lastElement  = lpexView.queryInt("block.bottomElement") - linesBeforeStart;
     }
    // 2.- no selected text (is visible) in this view
    else
     {
      selection = false;
      firstElement = 1;
      lastElement = lpexView.elements();
     }

    // go through all the visible text lines in range
    LpexDocumentLocation loc = new LpexDocumentLocation(1, 1);
    for (int e = firstElement; e <= lastElement; e++)
     {
      if (!lpexView.show(e))
       {
        loc.element = e;
        if (lpexView.queryOn("visible", loc))
         {
          lines++;
          String text = selection? lpexView.query("block.elementText", loc) :
                                   lpexView.elementText(e);
          boolean inWord = false;
          for (int i = 0; i < text.length(); i++)
           {
            char c = text.charAt(i); // TODO should handle surrogates (JDK5)!
            if (Character.isWhitespace(c))
             {
              inWord = false;
             }
            else
             {
              characters++;
              if (wordIsAny || Character.isLetterOrDigit(c))
               {
                if (!inWord)
                 {
                  inWord = true;
                  words++;
                 }
               }
              else
               {
                inWord = false;
               }
             }
           }
         }
       }
     }

    // simple display of the result
    lpexView.doCommand("set messageText words: " +
     (words == 1?      "1 word, " : words + " words, ") +
     (characters == 1? "1 non-space character, " : characters + " non-space characters, ") +
     (lines == 1?      "1 line." : lines + " lines."));
   }

  return true;
 }
}