<commit_before><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
   "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" []>
<chapter id="plugin.dev">
	<title>Developing a Plugin</title>
	<para>
      Part of Forge's architecture is to allow extensions to be created with extreme 
      ease, using the same programming model that you would use for any CDI or Java EE
      application; you should quickly recognize the annotation-driven patterns and
      practices applied.
   </para>
   <para>
      A Forge plugin could be as simple as a tool to print output to the console, or
      as complex as deploying an application to a server, 'tweet'ing the status of your
      latest source-code commit, or even sending commands to a home-automation system! 
      The sky is the limit.
   </para>

   <section id="plugin.dev.interface">
      <title>Implementing the Plugin interface</title>
      <para>
         Your class must implement the <literal>org.jboss.seam.forge.shell.plugins.Plugin</literal> interface.
         <programlisting>import org.jboss.seam.forge.shell.plugins.Plugin;
         
public class ExamplePlugin implements Plugin
{
   // your commands will go here
}</programlisting>
      </para>
   </section>
   <section id="plugin.dev.naming">
      <title>Naming your plugin</title>
      Each plugin should be given a name. This is done by adding the 
      <literal>@javax.inject.Named</literal> annotation to your plugin class.
      
      By default, if no <literal>@Named</literal> annotation is found, the 
      lower-case Class name will be used; for instance, our <literal>ExamplePlugin</literal>,
      above, would be executed by typing:
      
      <programlisting>$ exampleplugin</programlisting>
      
      Now we will add a name to our plugin.
      <programlisting>@Named("example")
public class ExamplePlugin implements Plugin
{
   // your commands will go here
}</programlisting>
               
      Our named <literal>@Named("example") ExamplePlugin</literal> would be executed by typing:
      <programlisting>$ example</programlisting>
   </section>
   
   <section id="plugin.dev.classpath">
      <title>Ensure all required classes are on the CLASSPATH</title>
      <para>
         All imports must be available on the <literal>CLASSPATH</literal>. If your Plugin depends 
         on classes that are not provided by Forge, then you must either package
         those classes in the JAR file containing your Plugin (for instance, using the
         maven <ulink url="http://maven.apache.org/plugins/maven-shade-plugin/">shade plugin</ulink>),
         or you must ensure that the required dependencies are also placed on the 
         <literal>CLASSPATH</literal> (typically in the <literal>$FORGE_HOME/lib</literal> folder)
      </para>
   </section>
   
   <section id="plugin.dev.installing">
      <title>Make your Plugin available to Forge</title>
      <para>
         After following all of the steps in <link linkend="plugin.dev">this section</link>,
         you should now be ready to install your Plugin into the Forge environment. This<commit_msg>Added docs for command options and piping - both still need more work<commit_after><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
   "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" []>
<chapter id="plugin.dev">
	<title>Developing a Plugin</title>
	<para>
      Part of Forge's architecture is to allow extensions to be created with extreme 
      ease, using the same programming model that you would use for any CDI or Java EE
      application; you should quickly recognize the annotation-driven patterns and
      practices applied.
   </para>
   <para>
      A Forge plugin could be as simple as a tool to print output to the console, or
      as complex as deploying an application to a server, 'tweet'ing the status of your
      latest source-code commit, or even sending commands to a home-automation system! 
      The sky is the limit.
   </para>

   <section id="plugin.dev.interface">
      <title>Implementing the Plugin interface</title>
      <para>
         Your class must implement the <literal>org.jboss.seam.forge.shell.plugins.Plugin</literal> interface.
         <programlisting>import org.jboss.seam.forge.shell.plugins.Plugin;
         
public class ExamplePlugin implements Plugin
{
}</programlisting>
      </para>
   </section>
   
   <section id="plugin.dev.commands">
      <title>Add commands to your plugin</title>
      <para>
         Now that you have implemented the <literal>Plugin</literal> interface, it's time
         to add some functionality. This is done by adding "Commands" to your plugin class.
         Commands are plain Java methods in your plugin Class. Plugin methods must be annotated
         as either a <literal>@DefaultCommand</literal>, the method to be invoked if the plugin
         is called by name (with no additional commands), or <literal>@Command(name="...")</literal>,
         in which case a the plugin name and command name must both be used to invoke the method.
      </para>
      <para>
         Commands also accept <literal>@Options</literal> parameters as arguments. These will
         be described later on.
      </para>
      
      <section id="plugin.dev.commands.default">
         <title>Default commands</title>
         <para>
            Default commands must be annotated with <literal>@DefaultCommand</literal>, and are
            not named; you may still provide help text or command metadata.
         </para>
         <para>
            The following default command would be executed by executing the plugin by its name:
            
            <programlisting>public class ExamplePlugin implements Plugin
{
   @DefaultCommand
   public void exampleDefaultCommand( @Option String opt )
   {
      // this method will be invoked, and 'opt' will be passed from the command line
   }
}</programlisting>
            <programlisting>$ exampleplugin forgeMe</programlisting>
            In this case, the value of '<literal>opt</literal>' will be "forgeMe".
        </para>
      </section>
      
      <section id="plugin.dev.commands.named">
         <title>Named commands</title>
         <para>
            Named commands must, to little surprise, be given a name with which they are
            invoked. This is done by placing the <literal>@Command(name="...")</literal> annotation
            on a public Java method in your <literal>Plugin</literal> class.
         </para>
         <para>
            The following command would be executed by executing the plugin by its name,
            followed by the name of the command:
         
            <programlisting>public class ExamplePlugin implements Plugin
{
   @Command(name="perform")
   public void exampleCommand( @Option(required=false) String opt, PipeOut out)
   {
       out.println(">> the command \"perform\" was invoked with the value: " + opt);
   }
}</programlisting>
            <programlisting>$ exampleplugin perform
>> the command "perform" was invoked with the value: null</programlisting>
         </para>
         <para>
            Notice that our command method has a parameter called "<literal>PipeOut</literal>,"
            in addition to our 'opt' parameter. <literal>PipeOut</literal> is a special 
            parameter, which can be placed in any order, that provides access to a
            variety of shell output functions, including enabling color and controlling 
            piping between plugins.
         </para>
         <para>
            Along with <literal>PipeOut</literal>, there is also a 
            <literal>@PipeIn InputStream stream</literal> annotation,
            which is used to inject a piped input stream (output from another
            Plugin's <literal>PipeOut</literal>.) These concepts will be described more in the
            section on <link linkend="plugin.dev.piping">piping</link>.
         </para>
      </section>
   </section>
   
   <section id="plugin.dev.naming">
      <title>Naming your plugin</title>
      Each plugin should be given a name. This is done by adding the 
      <literal>@javax.inject.Named</literal> annotation to your plugin class.
      
      By default, if no <literal>@Named</literal> annotation is found, the 
      lower-case Class name will be used; for instance, our <literal>ExamplePlugin</literal>,
      above, would be executed by typing:
      
      <programlisting>$ exampleplugin </programlisting>
      
      Now we will add a name to our plugin.
      <programlisting>@Named("example")
public class ExamplePlugin implements Plugin
{
   // commands
}</programlisting>
               
      Our named <literal>@Named("example") ExamplePlugin</literal> would be executed by typing:
      <programlisting>$ example </programlisting>
   </section>
   
   <section id="plugin.dev.piping">
      <title>Piping output between plugins</title>
      
      <para>
         Much like a standard UNIX-style shell, the Forge shell supports piping IO between
         executables; however in the case of forge, piping actually occurs between plugins,
         commands, for example:
      </para>
      
      <programlisting>$ cat /home/username/.forge/config | grep automatic 
@/* Automatically generated config file */;</programlisting>

      <para>
         This might look like a typical BASH command, but if you run forge and try it,
         you might be surprised to find that the results are the same as on your system
         command prompt, and in this example, we are demonstrating the pipe.
      </para>      

      <para>
         In order to enable piping in your plugins, you must use one or both of the
         <literal>@PipeIn InputStream stream</literal> or <literal>PipeOut out</literal>
         command arguments. Notice that <literal>PipeOut</literal> is a java type that 
         must be used as a Method parameter, whereas <literal>@PipeIn</literal> is 
         an annotation that must be placed on a Java <literal>InputStream</literal> 
         Method parameter. 
      </para>
      
      <para>
         Take the '<literal>grep</literal>' command itself, for example, which supports
         two methods of invocation: Invocation on one or more <literal>Resource&lt;?&gt;</literal>
         objects, or invocation on a piped <literal>InputStream</literal>.
      </para>
      
      <para>
         Notice that if no piping was invoked, the piped <literal>InputStream</literal>
         will be null.
      </para>
      
      <programlisting><![CDATA[@Named("grep")
@Topic("File & Resources")
@Help("print lines matching a pattern")
public class GrepPlugin implements Plugin
{
   @DefaultCommand
   public void run(
         @PipeIn final InputStream pipeIn,
         @Option(name = "ignore-case", shortName = "i", help = "ignore case distinctions in both patterns and input", flagOnly = true)
         boolean ignoreCase,
         @Option(name = "regexp", shortName = "e", help = "match using a regular expression")
         String regExp,
         @Option(description = "PATTERN") String pattern,
         @Option(description = "FILE ...") Resource<?>[] resources,
         final PipeOut pipeOut
   ) throws IOException
   {
      Pattern matchPattern;
      if (regExp != null)
      {
         if (ignoreCase)
         {
            regExp = regExp.toLowerCase();
         }
         matchPattern = Pattern.compile(regExp);
      }
      else if (pattern == null)
      {
         throw new RuntimeException("you must specify a pattern");
      }
      else
      {
         if (ignoreCase)
         {
            pattern = pattern.toLowerCase();
         }
         matchPattern = Pattern.compile(".*" + pattern + ".*");
      }

      if (resources != null)
      {
         for (Resource<?> r : resources)
         {
            InputStream inputStream = r.getResourceInputStream();
            try
            {
               match(inputStream, matchPattern, pipeOut, ignoreCase);
            }
            finally
            {
               inputStream.close();
            }
         }
      }
      else if (pipeIn != null)
      {
         match(pipeIn, matchPattern, pipeOut, ignoreCase);
      }
      else
      {
         throw new RuntimeException("arguments required");
      }
   }

   private void match(InputStream instream, Pattern pattern, PipeOut out, boolean caseInsensitive) throws IOException
   {
      StringAppender buf = new StringAppender();

      int c;
      while ((c = instream.read()) != -1)
      {
         switch (c)
         {
         case '\r':
         case '\n':
            String s = caseInsensitive ? buf.toString().toLowerCase() : buf.toString();

            if (pattern.matcher(s).matches())
            {
               out.println(s);
            }
            buf.reset();
            break;
         default:
            buf.append((char) c);
            break;
         }
      }
   }
}]]></programlisting>
      

   </section>
   
   <section id="plugin.dev.classpath">
      <title>Ensure all required classes are on the CLASSPATH</title>
      <para>
         All imports must be available on the <literal>CLASSPATH</literal>. If your Plugin depends 
         on classes that are not provided by Forge, then you must either package
         those classes in the JAR file containing your Plugin (for instance, using the
         maven <ulink url="http://maven.apache.org/plugins/maven-shade-plugin/">shade plugin</ulink>),
         or you must ensure that the required dependencies are also placed on the 
         <literal>CLASSPATH</literal> (typically in the <literal>$FORGE_HOME/lib</literal> folder)
      </para>
   </section>
   
   <section id="plugin.dev.installing">
      <title>Make your Plugin available to Forge</title>
      <para>
         After following all of the steps in <link linkend="plugin.dev">this section</link>,
         you should now be ready to install your Plugin into the Forge environment. This
{'fraction_non_alphanumeric': 0.09282879818594104, 'fraction_numerical': 0.0010629251700680273, 'mean_word_length': 2.4556807051909892, 'pattern_counts': {'":': 0, '<': 220, '<?xml version=': 2, '>': 224, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3133766', 'n_tokens_mistral': 3728, 'n_tokens_neox': 3616, 'n_words': 1385}