001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 The ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package org.conqat.lib.commons.options;
018
019import java.util.LinkedList;
020import java.util.Queue;
021
022/**
023 * This class preprocesses the command line arguments by splitting them into
024 * several tokens. It supports the GNU style syntax as described in
025 * {@link org.conqat.lib.commons.options.CommandLine}.
026 */
027public class CommandLineTokenStream {
028
029        /** Queue storing remaining short options (results from option chaining). */
030        private final Queue<Character> shortOptionQueue = new LinkedList<Character>();
031
032        /**
033         * Pending parameter (possibly remaining from the last long option read).
034         */
035        private String pendingParam = null;
036
037        /** Queue storing all remaining arguments. */
038        private final Queue<String> argQueue = new LinkedList<String>();
039
040        /** Constructs a new CommandLineTokenStream on the given arguments. */
041        public CommandLineTokenStream(String[] args) {
042                for (String a : args) {
043                        argQueue.add(a);
044                }
045        }
046
047        /** Returns whether a further token is available. */
048        public boolean hasNext() {
049                return !argQueue.isEmpty() || !shortOptionQueue.isEmpty() || pendingParam != null;
050        }
051
052        /** Returns whether the next token is the argument separator "--". */
053        public boolean nextIsSeparator() {
054                if (!shortOptionQueue.isEmpty() || pendingParam != null || argQueue.isEmpty()) {
055                        return false;
056                }
057                return argQueue.peek().equals("--");
058        }
059
060        /** Returns whether the next token is available and is a short option. */
061        public boolean nextIsShortOption() {
062                if (!shortOptionQueue.isEmpty()) {
063                        return true;
064                }
065                if (pendingParam != null || argQueue.isEmpty()) {
066                        return false;
067                }
068                String next = argQueue.peek();
069                return next.length() >= 2 && next.charAt(0) == '-' && next.charAt(1) != '-';
070        }
071
072        /** Returns whether the next token is available and is a long option. */
073        public boolean nextIsLongOption() {
074                if (!shortOptionQueue.isEmpty() || pendingParam != null || argQueue.isEmpty()) {
075                        return false;
076                }
077                return argQueue.peek().startsWith("--") && argQueue.peek().length() > 2;
078        }
079
080        /**
081         * Returns whether the next token is available and can be used as a file
082         * argument.
083         */
084        public boolean nextIsFileArgument() {
085                if (!shortOptionQueue.isEmpty() || pendingParam != null || argQueue.isEmpty()) {
086                        return false;
087                }
088                return !argQueue.peek().startsWith("-");
089        }
090
091        /**
092         * Returns whether the next token is available and can be used as a
093         * parameter to an option.
094         */
095        public boolean nextIsParameter() {
096                if (!shortOptionQueue.isEmpty()) {
097                        return false;
098                }
099                if (pendingParam != null) {
100                        return true;
101                }
102                return !argQueue.isEmpty();
103        }
104
105        /** Returns the next token as a plain string. */
106        public String next() {
107                if (!shortOptionQueue.isEmpty()) {
108                        return "-" + shortOptionQueue.poll();
109                }
110                if (pendingParam != null) {
111                        String result = pendingParam;
112                        pendingParam = null;
113                        return result;
114                }
115                return argQueue.poll();
116        }
117
118        /** Returns the next token as a short option. */
119        public char nextShortOption() {
120                if (!nextIsShortOption()) {
121                        throw new IllegalStateException("No short option available!");
122                }
123                if (shortOptionQueue.isEmpty()) {
124                        String arg = argQueue.poll();
125                        for (int i = 1; i < arg.length(); ++i) {
126                                shortOptionQueue.add(arg.charAt(i));
127                        }
128                }
129                return shortOptionQueue.poll();
130        }
131
132        /** Returns the next token as a long option. */
133        public String nextLongOption() {
134                if (!nextIsLongOption()) {
135                        throw new IllegalStateException("No long option available!");
136                }
137                String res = argQueue.poll().substring(2);
138                if (res.contains("=")) {
139                        String[] parts = res.split("=", 2);
140                        res = parts[0];
141                        pendingParam = parts[1];
142                }
143                return res;
144        }
145}