001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     * All rights reserved.
003     *
004     * Redistribution and use in source and binary forms, with or without
005     * modification, are permitted provided that the following conditions are met:
006     *
007     * 1. Redistributions of source code must retain the above copyright notice,
008     * this list of conditions and the following disclaimer.
009     *
010     * 2. Redistributions in binary form must reproduce the above copyright notice,
011     * this list of conditions and the following disclaimer in the documentation
012     * and/or other materials provided with the distribution.
013     *
014     * 3. Neither the name of the copyright holder nor the names of its
015     * contributors may be used to endorse or promote products derived from this
016     * software without specific prior written permission.
017     *
018     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
022     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028     * POSSIBILITY OF SUCH DAMAGE.
029     */
030    
031    import java.util.*;
032    
033    aspect AddOptionsToProgram {
034      public Options Program.options = new Options();
035      public Options Program.options() {
036        return options;
037      }
038    
039      inh Program ASTNode.program();
040      eq Program.getChild().program() = this;
041    }
042    
043    public class Options {
044      static class Option {
045        public String name;
046        public boolean hasValue;
047        public boolean isCollection;
048        public Option(String name, boolean hasValue, boolean isCollection) {
049          this.name = name;
050          this.hasValue = hasValue;
051          this.isCollection = isCollection;
052        }
053      }
054      private final Map<String,Object> options = new HashMap<String,Object>();
055      private final Map<String,Option> optionDescriptions = new HashMap<String,Option>();
056    
057      private final Set<String> files = new LinkedHashSet<String>();
058    
059      public Collection<String> files() {
060        return files;
061      }
062    
063      public void initOptions() {
064        options.clear();
065        optionDescriptions.clear();
066        files.clear();
067      }
068    
069      public void addKeyOption(String name) {
070        if (optionDescriptions.containsKey(name)) {
071          throw new Error("Command line definition error: option description for " + name + " is multiply declared");
072        }
073        optionDescriptions.put(name, new Option(name, false, false));
074      }
075    
076      public void addKeyValueOption(String name) {
077        if (optionDescriptions.containsKey(name)) {
078          throw new Error("Command line definition error: option description for " + name + " is multiply declared");
079        }
080        optionDescriptions.put(name, new Option(name, true, false));
081      }
082    
083      public void addKeyCollectionOption(String name) {
084        if (optionDescriptions.containsKey(name)) {
085          throw new Error("Command line definition error: option description for " + name + " is multiply declared");
086        }
087        optionDescriptions.put(name, new Option(name, true, true));
088      }
089    
090      public void addOptionDescription(String name, boolean value) {
091        if (optionDescriptions.containsKey(name)) {
092          throw new Error("Command line definition error: option description for " + name + " is multiply declared");
093        }
094        optionDescriptions.put(name, new Option(name, value, false));
095      }
096      public void addOptionDescription(String name, boolean value, boolean isCollection) {
097        if (optionDescriptions.containsKey(name)) {
098          throw new Error("Command line definition error: option description for " + name + " is multiply declared");
099        }
100        optionDescriptions.put(name, new Option(name, value, isCollection));
101      }
102    
103      /**
104       * Parse options from command-line arguments.
105       * Options starting with the at-sign are treated as argument files.
106       * The argument file is expanded in place before parsing other arguments.
107       *
108       * @param args command-line arguments
109       */
110      public void addOptions(String[] args) {
111        java.util.List<String> argList = new ArrayList<String>();
112    
113        // expand argument files
114        for (int i = 0; i < args.length; i++) {
115          String arg = args[i];
116          if (arg.length() > 1 && arg.startsWith("@")) {
117            if (arg.startsWith("@@")) {
118              // escape the double at
119              argList.add(arg.substring(1));
120            } else {
121              String fileName = arg.substring(1);
122              try {
123                java.io.StreamTokenizer tokenizer = new java.io.StreamTokenizer(new java.io.FileReader(fileName));
124                tokenizer.resetSyntax();
125                tokenizer.whitespaceChars(' ',' ');
126                tokenizer.whitespaceChars('\t','\t');
127                tokenizer.whitespaceChars('\f','\f');
128                tokenizer.whitespaceChars('\n','\n');
129                tokenizer.whitespaceChars('\r','\r');
130                tokenizer.wordChars(33,255);
131                tokenizer.commentChar('#');
132                tokenizer.quoteChar('"');
133                tokenizer.quoteChar('\'');
134                while (tokenizer.nextToken() != tokenizer.TT_EOF) {
135                  argList.add(tokenizer.sval);
136                }
137              } catch (java.io.FileNotFoundException e) {
138                System.err.println("Argument file not found: " + fileName);
139              } catch (java.io.IOException e) {
140                System.err.println("Exception: "+e.getMessage());
141              }
142            }
143          } else {
144            argList.add(arg);
145          }
146        }
147    
148        Iterator<String> all = argList.iterator();
149        while (all.hasNext()) {
150          String arg = all.next();
151          if (arg.startsWith("-")) {
152            if (!optionDescriptions.containsKey(arg)) {
153              throw new Error("Command line argument error: option " + arg + " is not defined");
154            }
155            Option o = (Option) optionDescriptions.get(arg);
156    
157            if (!o.isCollection && options.containsKey(arg)) {
158              throw new Error("Command line argument error: option " + arg + " is multiply defined");
159            }
160    
161            if (o.hasValue) {
162              String value = null;
163              if (!all.hasNext()) {
164                throw new Error("Command line argument error: value missing for key " + arg);
165              }
166              value = all.next();
167              if (value.startsWith("-")) {
168                throw new Error("Command line argument error: expected value for key " + arg
169                    + ", but found option " + value);
170              }
171    
172              if (o.isCollection) {
173                Collection<String> c = (Collection<String>) options.get(arg);
174                if (c == null) {
175                  c = new ArrayList<String>();
176                }
177                c.add(value);
178                options.put(arg, c);
179              } else {
180                options.put(arg, value);
181              }
182            } else {
183              options.put(arg, null);
184            }
185          } else {
186            files.add(arg);
187          }
188        }
189      }
190    
191      public boolean hasOption(String name) {
192        return options.containsKey(name);
193      }
194      public void setOption(String name) {
195        options.put(name, null);
196      }
197      public boolean hasValueForOption(String name) {
198        return options.containsKey(name) && options.get(name) != null;
199      }
200      public String getValueForOption(String name) {
201        if (!hasValueForOption(name)) {
202          throw new Error("Command line argument error: key " + name + " does not have a value");
203        }
204        return (String) options.get(name);
205      }
206      public void setValueForOption(String value, String option) {
207        options.put(option, value);
208      }
209      public Collection getValueCollectionForOption(String name) {
210        if (!hasValueForOption(name)) {
211          throw new Error("Command line argument error: key " + name + " does not have a value");
212        }
213        return (Collection) options.get(name);
214      }
215    
216      public boolean verbose() {
217        return hasOption("-verbose");
218      }
219    }