1 package gnu.dtools.ritopt;
7 * $Id: Options.java 1234 2005-07-30 14:49:05Z mortenalver $
13 import net.sf.jabref.*;
16 * This class functions as a repository for options and their modules. It
17 * facilitates registration of options and modules, as well as processing of
20 * Information such as help, usage, and versions are displayed
21 * when the respective --help and --version options are specified.
22 * The --menu option will invoke the built-in menu.<p>
24 * In the example below, the program processes three simple options.
27 * public class AboutMe {
29 * private static StringOption name = new StringOption( "Ryan" );
30 * private static IntOption age = new IntOption( 19 );
31 * private static DoubleOption bankBalance = new DoubleOption( 15.15 );
33 * public static void main( String args[] ) {
34 * Options repo = new Options( "java AboutMe" );
35 * repo.register( "name", 'n', name, "The person's name." );
36 * repo.register( "age", 'a', age, "The person's age." );
37 * repo.register( "balance", 'b', "The person's bank balance.",
39 * repo.process( args );
40 g * System.err.println( "" + name + ", age " + age + " has a " +
41 * " bank balance of " + bankBalance + "." );
49 * Copyright (C) Damian Ryan Eads, 2001. All Rights Reserved.
51 * ritopt is free software; you can redistribute it and/or modify
52 * it under the terms of the GNU General Public License as published by
53 * the Free Software Foundation; either version 2 of the License, or
54 * (at your option) any later version.
56 * ritopt is distributed in the hope that it will be useful,
57 * but WITHOUT ANY WARRANTY; without even the implied warranty of
58 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59 * GNU General Public License for more details.
61 * You should have received a copy of the GNU General Public License
62 * along with ritopt; if not, write to the Free Software
63 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
69 public class Options implements OptionRegistrar, OptionModuleRegistrar,
73 * The default verbosity.
76 public static final int DEFAULT_VERBOSITY = 3;
79 * This boolean defines whether options are deprecated by default.
82 public static final boolean DEFAULT_DEPRECATED = false;
85 * The default reason for deprecation.
88 public static final String DEFAULT_REASON = "No reason given.";
91 * The default general module name.
94 public static final String DEFAULT_GENERAL_MODULE_NAME = "General";
97 * This boolean defines whether usage should be displayed.
100 public static final boolean DEFAULT_DISPLAY_USAGE = false; // Mod. Morten A.
103 * This boolean defines whether the menu should be used.
106 public static final boolean DEFAULT_USE_MENU = false; // Mod. Morten A.
109 * The default program name that is display in the usage.
112 public static final String DEFAULT_PROGRAM_NAME = "java program";
115 * The default option file.
118 public static final String DEFAULT_OPTION_FILENAME = "default.opt";
121 * The current verbosity.
124 private int verbosity;
127 * The program to display in the usage.
130 private String usageProgram;
133 * The version to display in the usage.
136 private String version;
139 * The default option filename if an option file is not specified.
142 private String defaultOptionFilename;
145 * This flag defines whether to display usage when help is displayed.
148 private boolean displayUsage;
151 * This boolean defines whether the menu should be used.
154 private boolean useMenu;
157 * When this flag is true, debugging information is displayed.
160 private boolean debugFlag;
163 * The current module being processed.
166 private OptionModule currentModule;
169 * The general option module.
172 private OptionModule generalModule;
175 * A map of option modules.
178 private java.util.HashMap modules;
181 * The help method is invoked when this option is invoked.
184 private NotifyOption helpOption;
187 * The built-in menu system is invoked when this option is invoked.
190 private NotifyOption menuOption;
193 * Version information is displayed when this option is specified.
196 private NotifyOption versionOption;
199 * An instance of the built-in menu.
202 private OptionMenu menu;
205 * Create an option repository.
209 this( DEFAULT_PROGRAM_NAME );
213 * Create an option repository and associated it with a program name.
215 * @param programName A program name like "java Balloons".
218 public Options( String programName ) {
219 verbosity = DEFAULT_VERBOSITY;
220 displayUsage = DEFAULT_DISPLAY_USAGE;
221 useMenu = DEFAULT_USE_MENU;
222 defaultOptionFilename = DEFAULT_OPTION_FILENAME;
223 usageProgram = programName;
224 modules = new HashMap();
225 menu = new OptionMenu( this );
226 helpOption = new NotifyOption( this, "help", "" );
227 versionOption = new NotifyOption( this, "version", "" );
228 version = "Version 1.0";
229 menuOption = new NotifyOption( menu, "menu", "" );
230 generalModule = new OptionModule( DEFAULT_GENERAL_MODULE_NAME );
231 currentModule = generalModule;
233 // Mod. Morten A. ------------------------------------------------
234 register( "version", 'v',
235 "Displays version information.", versionOption );
236 /*register( "help", 'h', "Displays help for each option.", helpOption );
237 register( "menu", 'm', "Displays the built-in interactive menu.",
239 // End mod. Morten A. ------------------------------------------------
243 * Returns the help information as a string.
245 * @return The help information.
248 public String getHelp() {
249 String retval = (displayUsage ? getUsage() + "\n\n" : "" ) +
251 //"Use --menu to invoke the interactive built-in menu.\n\n" +
252 Option.getHelpHeader() + "\n\n" + generalModule.getHelp();
253 Iterator it = modules.values().iterator();
254 while ( it.hasNext() ) {
255 OptionModule module = (OptionModule)it.next();
256 retval += "\n\nOption Listing for " + module.getName() + "\n";
257 retval += module.getHelp() + "\n";
263 * Returns usage information of this program.
265 * @return The usage information.
268 public String getUsage() {
269 return getUsageProgram()
270 + " @optionfile :module: OPTIONS ... :module: OPTIONS";
274 * Returns the program name displayed in the usage.
276 * @param The program name.
279 public String getUsageProgram() {
284 * Returns the version of the program.
286 * @param The version.
289 public String getVersion() {
294 * Returns the option filename to load or write to if one is not
297 * @return The default option filename.
300 public String getDefaultOptionFilename() {
301 return defaultOptionFilename;
305 * Returns whether debugging information should be displayed.
307 * @return A boolean indicating whether to display help information.
310 public boolean getDebugFlag() {
315 * Returns whether the help information should display usage.
317 * @return A boolean indicating whether help should display usage.
320 public boolean shouldDisplayUsage() {
325 * Returns whether the built-in menu system can be invoked.
327 * @return A boolean indicating whether the build-in menu system
331 public boolean shouldUseMenu() {
336 * Sets whether usage can be displayed.
338 * @param b A boolean value indicating that usage can be displayed.
341 public void setDisplayUsage( boolean b ) {
346 * Sets whether the built-in menu system can be used.
348 * @param b A boolean value indicating whether the built-in menu
349 * system can be used.
352 public void setUseMenu( boolean b ) {
357 * Sets the program to display when the usage is displayed.
359 * @param program The program displayed during usage.
362 public void setUsageProgram( String program ) {
363 usageProgram = program;
367 * Sets the version of the program.
369 * @param version The version.
372 public void setVersion( String version ) {
373 this.version = version;
377 * Sets the option file to use when an option file is not specified.
379 * @param fn The filename of the default option file.
382 public void setDefaultOptionFilename( String fn ) {
383 defaultOptionFilename = fn;
387 * Sets the debugging flag.
389 * @param flag The value to set the debugging flag.
391 public void setDebugFlag( boolean flag ) {
396 * Displays the program's help which includes a description of each
397 * option. The usage is display if the usage flag is set to true.
400 public void displayHelp() {
401 System.err.println( getHelp() );
405 * Displays the version of the program.
408 public void displayVersion() {
409 System.err.println( getVersion() +" (build " +Globals.BUILD +")");
413 * Register an option into the repository as a long option.
415 * @param longOption The long option name.
416 * @param option The option to register.
419 public void register( String longOption, Option option ) {
420 generalModule.register( longOption, option );
424 * Register an option into the repository as a short option.
426 * @param shortOption The short option name.
427 * @param option The option to register.
430 public void register( char shortOption, Option option ) {
431 generalModule.register( shortOption, option );
435 * Register an option into the repository both as a short and long option.
437 * @param longOption The long option name.
438 * @param shortOption The short option name.
439 * @param option The option to register.
442 public void register( String longOption, char shortOption,
444 generalModule.register( longOption, shortOption, option );
448 * Register an option into the repository both as a short and long option.
449 * Initialize its description with the description passed.
451 * @param longOption The long option name.
452 * @param shortOption The short option name.
453 * @param description The description of the option.
454 * @param option The option to register.
457 public void register( String longOption, char shortOption,
458 String description, Option option ) {
459 generalModule.register( longOption, shortOption, description, option );
463 * Register an option into the repository both as a short and long option.
464 * Initialize its description with the description passed.
466 * @param longOption The long option name.
467 * @param shortOption The short option name.
468 * @param description The description of the option.
469 * @param option The option to register.
470 * @param deprecated A boolean indicating whether an option should
474 public void register( String longOption, char shortOption,
475 String description, Option option,
476 boolean deprecated ) {
477 generalModule.register( longOption, shortOption, description, option,
482 * Register an option module based on its name.
484 * @param module The option module to register.
487 public void register( OptionModule module ) {
488 register( module.getName(), module );
492 * Register an option module and associate it with the name passed.
494 * @param name The name associated with the option module.
495 * @param module The option module to register.
498 public void register( String name, OptionModule module ) {
499 modules.put( name.toLowerCase(), module );
503 * Process a string of values representing the invoked options. After
504 * all the options are processed, any leftover arguments are returned.
506 * @param args The arguments to process.
508 * @return The leftover arguments.
511 public String[] process( String args[] )
513 String []retval = new String[0];
515 retval = processOptions( args );
517 catch ( OptionException e ) {
518 System.err.println( "Error: " + e.getMessage() );
521 catch ( Exception e ) {
522 System.err.println( "Error: Unexpected Error in ritopt Processing." +
529 * Retrieves an option module based on the name passed.
531 * @param name The name referring to the option module.
533 * @return The option module. Null is returned if the module does not
537 public OptionModule getModule( String name ) {
538 return (OptionModule)modules.get( name.toLowerCase() );
542 * Returns a boolean indicating whether an option module exists.
544 * @param name The name referring to the option module.
546 * @return A boolean value indicating whether the module exists.
549 public boolean moduleExists( String name ) {
550 return getModule( name ) != null;
554 * Receives NotifyOption events. If the event command equals "help"
555 * or "version", the appropriate display methods are invoked.
557 * @param event The event object containing information about the
561 public void optionInvoked( OptionEvent event ) {
562 if ( event.getCommand().equals( "help" ) ) {
565 else if ( event.getCommand().equals( "version" ) ) {
571 * Process a string representing the invoked options. This string
572 * gets split according to how they would be split when passed to
573 * a main method. The split array of options gets passed to a
574 * private method for processing. After all the options are processed,
575 * any leftover arguments are returned.
577 * @param str The arguments to process.
579 * @return The leftover arguments.
582 public String[] process( String str ) {
583 return process( split( str ) );
587 * Splits a string representing command line arguments into several
590 * @param split The string to split.
592 * @return The splitted string.
595 public String[] split( String str ) {
596 StringBuffer buf = new StringBuffer( str.length() );
597 java.util.List l = new java.util.ArrayList();
598 int scnt = Utility.count( str, '"' );
600 if ( ((double)scnt) / 2.0 != (double)(scnt / 2) ) {
601 throw new OptionProcessingException( "Expecting an end quote." );
603 for ( int n = 0; n < str.length(); n++ ) {
604 if ( str.charAt( n ) == '"' ) {
607 else if ( str.charAt( n ) == ' ' && !q ) {
608 l.add( buf.toString() );
609 buf = new StringBuffer( str.length() );
612 buf.append( str.charAt( n ) );
615 if ( buf.length() != 0 ) {
616 l.add( buf.toString() );
618 Iterator it = l.iterator();
619 String retval[] = new String[ l.size() ];
621 while ( it.hasNext() ) {
622 retval[ n++ ] = (String)it.next();
628 * Writes all options and their modules out to an options file.
630 * @param filename The options filename to write.
633 public void writeOptionFile( String filename ) {
634 BufferedOutputStream writer = null;
637 currentModule = generalModule;
640 new BufferedOutputStream( new FileOutputStream( filename ) );
641 PrintStream ps = new PrintStream( writer );
642 generalModule.writeFileToPrintStream( ps );
643 it = modules.values().iterator();
644 while ( it.hasNext() ) {
645 OptionModule module = (OptionModule)it.next();
646 module.writeFileToPrintStream( ps );
649 catch ( IOException e ) {
650 throw new OptionProcessingException( e.getMessage() );
654 if ( writer != null )
657 catch( IOException e ) {
658 throw new OptionProcessingException( e.getMessage() );
664 * Loads all options and their modules from an options file.
666 * @param filename The options filename to write.
669 public void loadOptionFile( String filename ) {
670 BufferedReader reader = null;
672 currentModule = generalModule;
674 reader = new BufferedReader( new FileReader( filename ) );
675 while ( ( line = reader.readLine() ) != null ) {
676 line = Utility.stripComments( line, '\"', ';' );
680 catch ( IOException e ) {
681 throw new OptionProcessingException( e.getMessage() );
685 if ( reader != null )
688 catch( IOException e ) {
689 throw new OptionProcessingException( e.getMessage() );
695 * Processes an array of strings representing command line arguments.
697 * @param The arguments to process.
699 * @return The leftover arguments.
702 private String[] processOptions( String args[] ) {
703 String retval[] = null;
704 String moduleName = "general";
705 String optionFile = "";
706 char shortOption = '\0';
707 String longOption = "";
708 for ( int n = 0; n < args.length && retval == null; n++ ) {
709 boolean moduleInvoked = false;
710 boolean shortOptionInvoked = false;
711 boolean longOptionInvoked = false;
712 boolean readOptionFileInvoked = false;
713 boolean writeOptionFileInvoked = false;
714 if ( args[ n ].length() >= 1 ) {
715 char fc = args[ n ].charAt( 0 );
716 moduleInvoked = fc == ':';
717 readOptionFileInvoked = fc == '@';
718 writeOptionFileInvoked = fc == '%';
720 if ( args[ n ].length() >= 2 ) {
721 String s = args[ n ].substring( 0, 2 );
722 shortOptionInvoked = ( !s.equals( "--" ) &&
723 s.charAt( 0 ) == '-' );
724 longOptionInvoked = ( s.equals( "--" ) );
727 System.err.println( "Short Option: " + shortOptionInvoked );
728 System.err.println( "Long Option: " + longOptionInvoked );
729 System.err.println( "Module: " + moduleInvoked );
730 System.err.println( "Load Option File: " +
731 readOptionFileInvoked );
732 System.err.println( "Write Option File: "
733 + writeOptionFileInvoked );
735 if ( moduleInvoked ) {
736 if ( args[ n ].charAt( args[ n ].length() - 1 ) != ':' ) {
737 System.err.println( args[ n ] );
739 OptionProcessingException(
740 "Module arguments must start"
741 + " with : and end with :."
745 moduleName = args[n].substring( 1,
748 if ( moduleName.length() == 0
749 || moduleName.equals( "general" ) ) {
750 moduleName = "general";
751 currentModule = generalModule;
754 currentModule = getModule( moduleName );
756 if ( currentModule == null )
757 throw new OptionProcessingException( "Module '" +
759 "' does not exist." );
761 System.err.println( "Module: " + moduleName );
764 moduleInvoked = false;
766 else if ( readOptionFileInvoked ) {
767 optionFile = Utility.trim( args[ n ].substring( 1 ) );
768 if ( optionFile.equals( "@" )
769 || optionFile.length() == 0 )
770 optionFile = defaultOptionFilename;
772 System.err.println( "Option file: '" + optionFile + "'." );
774 loadOptionFile( optionFile );
776 else if ( shortOptionInvoked ) {
777 shortOption = args[ n ].charAt( 1 );
778 if ( !Utility.isAlphaNumeric( shortOption ) ) {
779 throw new OptionProcessingException(
780 "A short option must be alphanumeric. -" + shortOption
781 + " is not acceptable." );
784 System.err.println( "Short option text: " + shortOption );
786 char delim = ( args[ n ].length() >= 3 ) ?
787 args[ n ].charAt( 2 ) : '\0';
788 if ( delim == '+' || delim == '-' ) {
789 currentModule.action( shortOption, delim );
791 else if ( delim == '=' ) {
792 currentModule.action( shortOption,
793 args[ n ].substring( 3 ) );
795 else if ( delim == '\0' ) {
798 if ( n < args.length - 1 ) {
799 dpeek = args[ n + 1 ].charAt( 0 );
800 if ( !Utility.contains( args[ n + 1 ].charAt( 0 ),
802 dtext = args[ n + 1 ];
806 currentModule.action( shortOption, dtext );
808 else if ( Utility.isAlphaNumeric( delim ) ) {
809 for ( int j = 1; j < args[ n ].length(); j++ ) {
810 if ( Utility.isAlphaNumeric( args[ n ].charAt( j ) ) ) {
811 currentModule.action( shortOption, "+" );
814 throw new OptionProcessingException(
815 "A short option must be alphanumeric. -"
816 + shortOption + " is not acceptable." );
821 else if ( longOptionInvoked ) {
822 char lastchar = args[ n ].charAt( args[ n ].length() - 1 );
823 int eqindex = args[ n ].indexOf( "=" );
824 if ( eqindex != -1 ) {
825 longOption = args[ n ].substring( 2, eqindex );
826 String value = args[ n ].substring( eqindex + 1 );
827 currentModule.action( longOption, value );
829 else if ( Utility.contains( lastchar, "+-" ) ) {
830 longOption = args[ n ].substring( 2,
831 args[ n ].length() - 1 );
832 currentModule.action( longOption, lastchar );
835 longOption = args[ n ].substring( 2 );
838 if ( n < args.length - 1 && args[ n + 1 ].length() > 0 ) {
839 dpeek = args[ n + 1 ].charAt( 0 );
840 if ( !Utility.contains( args[ n + 1 ].charAt( 0 ),
842 dtext = args[ n + 1 ];
846 currentModule.action( longOption, dtext );
849 System.err.println( "long option: " + longOption );
852 else if ( writeOptionFileInvoked ) {
853 optionFile = Utility.trim( args[ n ].substring( 1 ) );
854 if ( optionFile.equals( "%" )
855 || optionFile.length() == 0 )
856 optionFile = defaultOptionFilename;
858 System.err.println( "Option file: '" + optionFile + "'." );
860 writeOptionFile( optionFile );
863 retval = new String[ args.length - n ];
864 for ( int j = n; j < args.length; j++ ) {
865 retval[ j - n ] = args[ j ];
869 if ( retval == null ) retval = new String[ 0 ];