<?xml version='1.0' encoding='utf-8' ?>
<rss version='2.0' xmlns:dc='http://purl.org/dc/elements/1.1/'>
  <channel>
    <title>Wolfmans Howlings: Using java enum for command dispatching</title>
    <link>http://blog.wolfman.com/articles/2009/4/12/using-java-enum-for-command-dispatching</link>
    <description>A programmers Blog about Ruby, Rails and a few other issue</description>
    <language>en-us</language>
    <ttl>40</ttl>
    <item>
      <title>Using java enum for command dispatching</title>
      <description>
        &lt;p&gt;I haven't blogged much about Java even though it is my primary
        programming language. Since I have been doing a lot of Java recently I
        thought I'd post something about some of the Java idioms I've used
        over the years.&lt;/p&gt;
        
        &lt;p&gt;One I have been using a lot recently is a command dispatcher where the
        command is text.&lt;/p&gt;
        
        &lt;p&gt;In some languages you would probably use a switch statement with the
        command string being the case statement. However Java does not allow
        strings as the match part of a case.&lt;/p&gt;
        
        &lt;p&gt;A common idiom seen is to use enums (in Java 5 and greater), as they
        are constants and can be used in a switch statement.&lt;/p&gt;
        
        &lt;p&gt;Now enums are very powerful constructs in Java, they are not simply
        convenient #define work-arounds, although they are commonly used that
        way...&lt;/p&gt;
        
        &lt;pre&gt;    enum Days { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
                    SATURDAY, SUNDAY};
        
            switch(day){
              case MONDAY: do something with monday;
              case TUESDAY: do something with tuesday;
            }
        
        
        &lt;/pre&gt;
        
        &lt;p&gt;See
        &lt;a href=&quot;http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html&quot;&gt;this&lt;/a&gt;
        for all the info on enums.&lt;/p&gt;
        
        &lt;p&gt;However you can do so much more with enums...&lt;/p&gt;
        
        &lt;p&gt;I use them as a super duper switch statement to dispatch text
        commands, and the cool thing is they are pretty efficient as they use
        a hash table lookup to do the dispatch. Here's an example of a command
        line app that uses the java &lt;a href=&quot;http://www.urbanophile.com/arenn/hacking/getopt&quot;&gt;Gnu
        GetOpt&lt;/a&gt; package with long option names that
        dispatches different commands that can be typed on the command line...&lt;/p&gt;
        
        &lt;pre&gt;&lt;code&gt;&amp;gt; doit --froboz one two three
        &amp;gt; doit --bazznot glozny frobod
        &lt;/code&gt;&lt;/pre&gt;
        
        &lt;p&gt;where --froboz is a command that takes three parameters, you get the
        idea.&lt;/p&gt;
        
        &lt;pre&gt;package com.e4net;
        import gnu.getopt.Getopt;
        import gnu.getopt.LongOpt;
        
        public class Test {
            private static void usage() {
        
                System.out.println(&amp;quot;Usage: myapp [-x filename] [--command] [args...]&amp;quot;);
                System.out.println(&amp;quot;   where commands is one of:&amp;quot;);
                System.out.println(&amp;quot;   froboz param1 param2 param3&amp;quot;);
                System.out.println(&amp;quot;   bazznot param1 param2&amp;quot;);
                System.exit(1);
            }
        
            // The various supported command line commands
            // code is the short form command argument
            // size is the number of arguments needed for this command
            private enum Commands {
                froboz('f', 3) {
                    boolean doit(int off, String [] argv){
                        // do whatever froboz does with three arguments
                        System.out.println(&amp;quot;In froboz with: &amp;quot; + argv[off] + &amp;quot;, &amp;quot; + argv[off+1] + &amp;quot;, &amp;quot; + argv[off+2]);
                        return true;
                    }
                },
        
                bazznot('b', 2) {
                    boolean doit(int off, String [] argv){
                        // do whatever bazznot does with two arguments
                        System.out.println(&amp;quot;In bazznot with: &amp;quot; + argv[off] + &amp;quot;, &amp;quot; + argv[off+1]);
                        return true;
                    }
                };
        
                // Just add more commands here....
        
                private final int code, size;
                Commands(int code, int size){
                    this.code= code;
                    this.size= size;
                }
                public int code() { return code; }
                public int size() { return size; }
        
                // Do the command, off is the offset within argv the commands
                // parameters start
                abstract boolean doit(int off, String [] argv);
        
            };
        
            public static void main(String[] args) {
        
                // build the long commands from the enum
                LongOpt[] longopts = new LongOpt[Commands.values().length+1];
                longopts[0] = new LongOpt(&amp;quot;help&amp;quot;, LongOpt.NO_ARGUMENT, null, 'h');
        
                // loops through each enum and generates the long command
                // based on the enums value, the code() is the short form of
                // the command so either --froboz can be used or -f
                int off= 1;
                for(Commands c : Commands.values()){
                    longopts[off++] = new LongOpt(c.toString(), LongOpt.NO_ARGUMENT, null, c.code());
                }
        
                Getopt g = new Getopt(&amp;quot;myprog&amp;quot;, args, &amp;quot;x:&amp;quot;, longopts);
                String file= &amp;quot;default.file&amp;quot;;
                int c;
                boolean found= false;
                while ((c = g.getopt()) != -1 &amp;amp;&amp;amp; !found) {
                    int i= g.getOptind();
                    switch (c) {
                        case 'x': // get some file name with the -x
                            // option
                            file = g.getOptarg();
                            break;
        
                            // this is the help which runs with --help or -h
                        case '?':
                        case 'h':
                            usage();
                            break;
        
                        // this will lookup the command and execute it
                        default:
                            // see if in commands
                            String cmd= longopts[g.getLongind()].getName();
                            Commands cm= null;
                            try {
                                // lookup the text command in the enum
                                cm= Commands.valueOf(cmd);                      
                            } catch (IllegalArgumentException e) {
                                usage();
                            }
        
                            // check we have enough arguments for this command
                            if(args.length - i == cm.size){
                                // do the command
                                cm.doit(g.getOptind(), args);
                                found= true;
                            }else {
                                // not enough arguments so show usage
                                System.err.println(&amp;quot;Not enough arguments for: &amp;quot; + cmd);
                                usage();
                            }
                    }
                }
            }
        }
        &lt;/pre&gt;
        
        &lt;p&gt;Ok that looks like a lot of code for a command lookup/dispatcher, but
        the nice thing about it is it is fairly DRY, to add a new command you
        simply add another enum term, the rest is taken care of automatically.&lt;/p&gt;
        
        &lt;p&gt;The command is looked up in the switches default clause using the
        &lt;code&gt;valueOf&lt;/code&gt; method of an enum which basically finds the enum that
        matches the given string.&lt;/p&gt;
        
        &lt;p&gt;It can be run thusly...&lt;/p&gt;
        
        &lt;pre&gt;&lt;code&gt;&amp;gt; java -cp ./java-getopt.jar:./classes com.e4net.Test --bazznot 1 2
        In bazznot with: 1, 2
        
        &amp;gt; java -cp ./java-getopt.jar:./classes com.e4net.Test --froboz 1 2 3
        In froboz with: 1, 2, 3
        
        
        &amp;gt; java -cp ./java-getopt.jar:./classes com.e4net.Test --blahblah 1 2 3
        myprog: unrecognized option '--blahblah'
        Usage: myapp [-x filename] [--command] [args...]
            where commands is one of:
            froboz param1 param2 param3
            bazznot param1 param2
        &lt;/code&gt;&lt;/pre&gt;
        
        &lt;p&gt;A variation on this theme is if say you have an Internet server, that
        receives text commands over a socket, we want to lookup the command we
        received and dispatch the same way as we do above. This turns out to
        be easy, we use the &lt;code&gt;valueOf&lt;/code&gt; method of an enum. Below we call
        handle() with the command we got over the wire, and it is neatly
        dispatched. &lt;code&gt;valueOf()&lt;/code&gt; is the command that will do the
        lookup in a hash table, so is quite efficient if the command list is
        long.&lt;/p&gt;
        
        &lt;pre&gt;private Commands getCommand(String command) {
            Commands cmd;
            try {
                cmd= Commands.valueOf(command.toLowerCase());
            } catch (IllegalArgumentException e) {
                cmd= null;          
            }
            return cmd;
        }
        
        public boolean handle(String command, String [] args) {     
            // lookup and execute the command
            Commands cmd= getCommand(command);
            if(cmd != null) {
                return cmd.doit(0, args);
            }else{
                System.err.println(&amp;quot;handle() - Unknown command: &amp;quot; + command);
                return false;
            }       
        }
        &lt;/pre&gt;
        
        &lt;p&gt;The code for this article can be downloaded
        &lt;a href=&quot;http://blog.wolfman.com/files/Test.java&quot;&gt;from here&lt;/a&gt;&lt;/p&gt;
        
        &lt;p&gt;&lt;a href=&quot;http://technorati.com/tag/enum+switch&quot; rel=&quot;tag&quot;&gt;&lt;/a&gt;
        &lt;a href=&quot;http://technorati.com/tag/java+enum&quot; rel=&quot;tag&quot;&gt;&lt;/a&gt;&lt;/p&gt;
      </description>
      <author>Jim Morris</author>
      <pubDate>Sun, 12 Apr 2009 02:03:14 -0700</pubDate>
      <link>http://blog.wolfman.com/articles/2009/4/12/using-java-enum-for-command-dispatching</link>
      <guid isPermaLink='false'>urn:uuid:de5d09c4-8c3b-4b13-8e16-2b81ceb40de5</guid>
    </item>
  </channel>
</rss>
