/*OsmUi is a user interface for Osmosis
Copyright (C) 2011 Verena Käfer, Peter Vollmer, Niklas Schnelle
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.osmui.util;
import java.text.ParseException;
import de.osmui.i18n.I18N;
/**
* This class is used to split an osmosis command line into tokens, while
* dealing with shell escapes
*
* @author Niklas Schnelle, Peter Vollmer, Verena Käfer
*
*/
public class CommandlineSplitter {
private enum State {
normal, inQuote, afterEscape, dead
};
private String line;
private StringBuilder sb;
private char[] quoteChars;
private char lastQuoteChar;
private char escapeChar;
private char currChar;
private int pos;
private State state;
public CommandlineSplitter(String l, char[] qC, char eC) {
line = l;
sb = new StringBuilder();
pos = 0;
state = State.normal;
quoteChars = qC;
escapeChar = eC;
lastQuoteChar = 0;
readOn();
}
/**
* Gets whether there is a new token available
*
* @return
*/
public boolean hasNext() {
return pos < line.length() || sb.length() > 0;
}
/**
* Checks whether the given char is in the quote array
* @param c
* @return
*/
private boolean isQuoteChar(char c) {
for (char ci : quoteChars) {
if (ci == c) {
return true;
}
}
return false;
}
/**
* Reads the next token
* @return true if all was ok, false if there was a parse error e.g
* no matching quote
*/
private boolean readOn() {
sb.delete(0, sb.length());
while (pos < line.length()) {
currChar = line.charAt(pos);
switch (state) {
case normal: {
if (Character.isWhitespace(currChar)) {
if (sb.length() > 0) {
pos++;
return true;
}
} else if (currChar == escapeChar) {
state = State.afterEscape;
} else if (isQuoteChar(currChar)) {
lastQuoteChar = currChar;
state = State.inQuote;
} else {
sb.append(currChar);
}
}
break;
case inQuote: {
if (isQuoteChar(currChar) && currChar == lastQuoteChar) {
lastQuoteChar = 0;
state = State.normal;
} else {
sb.append(currChar);
}
}
break;
case afterEscape: {
// At line breaks args are split even when
// escaped
if (currChar == '\n') {
state = State.normal;
if (sb.length() > 0) {
pos++;
return true;
}
} else {
sb.append(currChar);
state = State.normal;
}
}
break;
case dead:
return false;
}
pos++;
}
return (state != State.normal) ? false : true;
}
/**
* Gets the next token
* @return
* @throws ParseException
*/
public String next() throws ParseException {
String ret;
ret = sb.toString();
if (!readOn()) {
throw new ParseException(I18N.getString(
"CommandlineSplitter.Error", lastQuoteChar), pos);
}
return ret;
}
}