/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.execution.configurations; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; /** * Splits input String to tokens being aware of quoted tokens ("foo bar") and escaped spaces & quotes (\"foo\ bar\"), * usually used for splitting command line to separate arguments that may contain space symbols. * Space and quote are the only symbols that can be escaped */ public class CommandLineTokenizer extends StringTokenizer { private static final String DEFAULT_DELIMITERS = " \t\n\r\f"; // keep source level 1.4 private List myTokens = new ArrayList(); private int myCurrentToken = 0; private boolean myHandleEscapedWhitespaces = false; public CommandLineTokenizer(String str) { this(str, false); } public CommandLineTokenizer(String str, boolean handleEscapedWhitespaces) { super(str, DEFAULT_DELIMITERS, true); myHandleEscapedWhitespaces = handleEscapedWhitespaces; parseTokens(); } /** * @deprecated Do not pass custom delimiters to the CommandLineTokenizer as it may break its logic */ @Deprecated() public CommandLineTokenizer(String str, String delim) { super(str, delim, true); parseTokens(); } @Override public boolean hasMoreTokens() { return myCurrentToken < myTokens.size(); } @Override public String nextToken() { return (String) myTokens.get(myCurrentToken++); } public String peekNextToken() { return (String) myTokens.get(myCurrentToken); } @Override public int countTokens() { return myTokens.size() - myCurrentToken; } @Override public String nextToken(String delim) { throw new UnsupportedOperationException(); } private void parseTokens() { String token; while ((token = nextTokenInternal()) != null) { myTokens.add(token); } } private String nextTokenInternal() { String nextToken; do { if (super.hasMoreTokens()) { nextToken = super.nextToken(); } else { nextToken = null; } } while (nextToken != null && nextToken.length() == 1 && DEFAULT_DELIMITERS.indexOf(nextToken.charAt(0)) >= 0); if (nextToken == null) { return null; } int i; int quotationMarks = 0; final StringBuilder buffer = new StringBuilder(); do { while ((i = nextToken.indexOf('"')) >= 0) { boolean isEscapedQuote = isEscapedAtPos(nextToken, i); if (!isEscapedQuote) quotationMarks++; buffer.append(nextToken.substring(0, isEscapedQuote ? i - 1 : i)); if (isEscapedQuote) buffer.append('"'); nextToken = nextToken.substring(i + 1); } boolean isEscapedWhitespace = false; if (myHandleEscapedWhitespaces && quotationMarks == 0 && nextToken.endsWith("\\") && super.hasMoreTokens()) { isEscapedWhitespace = true; buffer.append(nextToken.substring(0, nextToken.length() - 1)); buffer.append(super.nextToken()); } else { buffer.append(nextToken); } if ((isEscapedWhitespace || (quotationMarks & 1) == 1) && super.hasMoreTokens()) { nextToken = super.nextToken(); } else { nextToken = null; } } while (nextToken != null); return buffer.toString(); } private static boolean isEscapedAtPos(String token, int pos) { int escapeCount = 0; --pos; while (pos >= 0 && token.charAt(pos) == '\\') { ++escapeCount; --pos; } return (escapeCount & 1) == 1; } }