001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: GetOpt.java 1225436 2011-12-29 05:09:31Z mrglavas $
020     */
021    
022    package org.apache.xalan.xsltc.cmdline.getopt; 
023    
024    import java.util.ArrayList;
025    import java.util.List;
026    import java.util.ListIterator;
027    
028    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
029    
030    
031    /**
032    * GetOpt is a Java equivalent to the C getopt() library function
033    * discussed in man page getopt(3C). It provides command line
034    * parsing for Java applications. It supports the most rules of the
035    * command line standard (see man page intro(1)) including stacked
036    * options such as '-sxm' (which is equivalent to -s -x -m); it
037    * handles special '--' option that signifies the end of options.
038    * Additionally this implementation of getopt will check for
039    * mandatory arguments to options such as in the case of
040    * '-d <file>' it will throw a MissingOptArgException if the 
041    * option argument '<file>' is not included on the commandline.
042    * getopt(3C) does not check for this. 
043     * @author G Todd Miller 
044    */
045    public class GetOpt{
046        public GetOpt(String[] args, String optString){
047            theOptions = new ArrayList();            
048            int currOptIndex = 0; 
049            theCmdArgs = new ArrayList(); 
050            theOptionMatcher = new OptionMatcher(optString);
051            // fill in the options list
052            for(int i=0; i<args.length; i++){
053                String token = args[i];
054                int tokenLength = token.length();
055                if(token.equals("--")){         // end of opts
056                    currOptIndex = i+1;         // set index of first operand
057                    break;                      // end of options
058                }
059                else if(token.startsWith("-") && tokenLength == 2){ 
060                    // simple option token such as '-s' found
061                    theOptions.add(new Option(token.charAt(1)));    
062                }
063                else if(token.startsWith("-") && tokenLength > 2){
064                    // stacked options found, such as '-shm'
065                    // iterate thru the tokens after the dash and
066                    // add them to theOptions list
067                    for(int j=1; j<tokenLength; j++){
068                        theOptions.add(new Option(token.charAt(j)));
069                    }
070                }           
071                else if(!token.startsWith("-")){
072                    // case 1- there are not options stored yet therefore
073                    // this must be an command argument, not an option argument
074                    if(theOptions.size() == 0){
075                        currOptIndex = i;
076                        break;              // stop processing options
077                    }
078                    else {
079                        // case 2- 
080                        // there are options stored, check to see if
081                        // this arg belong to the last arg stored   
082                        int indexoflast=0;
083                        indexoflast = theOptions.size()-1;
084                        Option op = (Option)theOptions.get(indexoflast);
085                        char opLetter = op.getArgLetter();
086                        if(!op.hasArg() && theOptionMatcher.hasArg(opLetter)){
087                            op.setArg(token);
088                        }
089                        else{
090                            // case 3 - 
091                            // the last option stored does not take
092                            // an argument, so again, this argument
093                            // must be a command argument, not 
094                            // an option argument
095                            currOptIndex = i;
096                            break;                  // end of options 
097                        }
098                    }
099                }// end option does not start with "-"
100            } // end for args loop
101    
102            //  attach an iterator to list of options 
103            theOptionsIterator = theOptions.listIterator();
104    
105            // options are done, now fill out cmd arg list with remaining args
106            for(int i=currOptIndex; i<args.length; i++){
107                String token = args[i];
108                theCmdArgs.add(token);
109            }
110        }
111    
112    
113        /**
114        * debugging routine to print out all options collected
115        */
116        public void printOptions(){
117            for(ListIterator it=theOptions.listIterator(); it.hasNext();){
118                Option opt = (Option)it.next();
119                System.out.print("OPT =" + opt.getArgLetter());
120                String arg = opt.getArgument();
121                if(arg != null){
122                   System.out.print(" " + arg);
123                }
124                System.out.println();
125            }
126        }
127    
128        /**
129        * gets the next option found in the commandline. Distinguishes
130        * between two bad cases, one case is when an illegal option
131        * is found, and then other case is when an option takes an
132        * argument but no argument was found for that option.
133        * If the option found was not declared in the optString, then 
134        * an IllegalArgumentException will be thrown (case 1). 
135        * If the next option found has been declared to take an argument, 
136        * and no such argument exists, then a MissingOptArgException
137        * is thrown (case 2).
138        * @return int - the next option found.
139        * @throws IllegalArgumentException, MissingOptArgException. 
140        */
141        public int getNextOption() throws IllegalArgumentException, 
142            MissingOptArgException
143        {
144            int retval = -1;
145            if(theOptionsIterator.hasNext()){
146                theCurrentOption = (Option)theOptionsIterator.next();
147                char c = theCurrentOption.getArgLetter();
148                boolean shouldHaveArg = theOptionMatcher.hasArg(c);
149                String arg = theCurrentOption.getArgument();
150                if(!theOptionMatcher.match(c)) {
151                    ErrorMsg msg = new ErrorMsg(ErrorMsg.ILLEGAL_CMDLINE_OPTION_ERR,
152                                                new Character(c));
153                    throw (new IllegalArgumentException(msg.toString()));
154                }
155                else if(shouldHaveArg && (arg == null)) {
156                    ErrorMsg msg = new ErrorMsg(ErrorMsg.CMDLINE_OPT_MISSING_ARG_ERR,
157                                                new Character(c));
158                    throw (new MissingOptArgException(msg.toString()));
159                }
160                retval = c;
161            }
162            return retval;
163        }
164    
165        /**
166        * gets the argument for the current parsed option. For example,
167        * in case of '-d <file>', if current option parsed is 'd' then
168        * getOptionArg() would return '<file>'.
169        * @return String - argument for current parsed option.
170        */
171        public String getOptionArg(){
172            String retval = null;
173            String tmp = theCurrentOption.getArgument();
174            char c = theCurrentOption.getArgLetter();
175            if(theOptionMatcher.hasArg(c)){
176                retval = tmp;
177            }
178            return retval;  
179        }
180    
181        /**
182        * gets list of the commandline arguments. For example, in command
183        * such as 'cmd -s -d file file2 file3 file4'  with the usage
184        * 'cmd [-s] [-d <file>] <file>...', getCmdArgs() would return
185        * the list {file2, file3, file4}.
186        * @return String[] - list of command arguments that may appear
187        *                    after options and option arguments.
188        */
189        public String[] getCmdArgs(){
190            String[] retval = new String[theCmdArgs.size()];
191            int i=0;
192            for(ListIterator it=theCmdArgs.listIterator(); it.hasNext();){
193                retval[i++] = (String)it.next();
194            }
195            return retval;
196        }
197    
198    
199        private Option theCurrentOption = null;
200        private ListIterator theOptionsIterator; 
201        private List theOptions = null;
202        private List theCmdArgs = null;
203        private OptionMatcher theOptionMatcher = null;
204    
205        ///////////////////////////////////////////////////////////
206        //
207        //   Inner Classes
208        //
209        ///////////////////////////////////////////////////////////
210    
211        // inner class to model an option
212        static class Option{
213            private char theArgLetter;
214            private String theArgument = null;
215            public Option(char argLetter) { theArgLetter = argLetter; }
216            public void setArg(String arg) { 
217                theArgument = arg;
218            }
219            public boolean hasArg() { return (theArgument != null); } 
220            public char getArgLetter() { return theArgLetter; }
221            public String getArgument() { return theArgument; }
222        } // end class Option
223    
224    
225        // inner class to query optString for a possible option match,
226        // and whether or not a given legal option takes an argument. 
227        //  
228        static class OptionMatcher{
229            public OptionMatcher(String optString){
230                theOptString = optString;   
231            }
232            public boolean match(char c){
233                boolean retval = false;
234                if(theOptString.indexOf(c) != -1){
235                    retval = true;
236                }
237                return retval;      
238            }
239            public boolean hasArg(char c){
240                boolean retval = false;
241                int index = theOptString.indexOf(c)+1; 
242                if (index == theOptString.length()){
243                    // reached end of theOptString
244                    retval = false;
245                }
246                else if(theOptString.charAt(index) == ':'){
247                    retval = true;
248                }
249                return retval;
250            }
251            private String theOptString = null;
252        } // end class OptionMatcher
253    }// end class GetOpt
254