/*==========================================================================*\
| $Id: ShellStringUtils.java,v 1.3 2009/09/13 12:59:29 aallowat Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2009 Virginia Tech
|
| This file is part of Web-CAT Eclipse Plugins.
|
| Web-CAT 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 2 of the License, or
| (at your option) any later version.
|
| Web-CAT 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 Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package net.sf.webcat.eclipse.cxxtest.internal.options;
import java.util.ArrayList;
import java.util.regex.Matcher;
/**
* Utility functions that split a string into an array of components and join an
* array into a string using logic similar to a shell. Single and double quotes
* are handled properly, rather than simply splitting across any delimiting
* whitespace.
*
* @author Tony Allevato (Virginia Tech Computer Science)
* @author latest changes by: $Author: aallowat $
* @version $Revision: 1.3 $ $Date: 2009/09/13 12:59:29 $
*/
public class ShellStringUtils
{
// === Methods ============================================================
// ------------------------------------------------------------------------
/**
* Splits a string into an array of components. Whitespace is used as the
* delimiter, but single and double quotes are handled properly so that a
* component in quotes is extracted as a single component, regardless of
* whether it contains whitespace or not.
*
* @param string
* the String that should be split
*
* @return an array of String components extracted from the string
*/
public static String[] split(String string)
{
ArrayList<String> parts = new ArrayList<String>();
StringBuffer currentPart = new StringBuffer();
int state = STATE_BEGIN;
for(int i = 0; i < string.length(); i++)
{
char ch = string.charAt(i);
switch(state)
{
case STATE_BEGIN:
if(ch == '"')
{
currentPart.append(ch);
state = STATE_DQUOTE;
}
else if(ch == '\'')
{
currentPart.append(ch);
state = STATE_SQUOTE;
}
else if(ch == '\\')
{
state = STATE_ESCAPE_IN_PART;
}
else if(!Character.isWhitespace(ch))
{
currentPart.append(ch);
state = STATE_PART;
}
break;
case STATE_PART:
if(Character.isWhitespace(ch))
{
parts.add(currentPart.toString());
currentPart = new StringBuffer();
state = STATE_BEGIN;
}
else if(ch == '"')
{
currentPart.append(ch);
state = STATE_DQUOTE;
}
else if(ch == '\'')
{
currentPart.append(ch);
state = STATE_SQUOTE;
}
else if(ch == '\\')
{
currentPart.append(ch);
state = STATE_ESCAPE_IN_PART;
}
else
{
currentPart.append(ch);
}
break;
case STATE_ESCAPE_IN_PART:
currentPart.append(ch);
state = STATE_PART;
break;
case STATE_DQUOTE:
currentPart.append(ch);
if(ch == '"')
{
state = STATE_PART;
}
else if(ch == '\\')
{
state = STATE_ESCAPE_IN_DQUOTE;
}
break;
case STATE_ESCAPE_IN_DQUOTE:
currentPart.append(ch);
state = STATE_DQUOTE;
break;
case STATE_SQUOTE:
currentPart.append(ch);
if(ch == '\'')
{
state = STATE_PART;
}
else if(ch == '\\')
{
state = STATE_ESCAPE_IN_SQUOTE;
}
break;
case STATE_ESCAPE_IN_SQUOTE:
currentPart.append(ch);
state = STATE_SQUOTE;
break;
}
}
if(currentPart.length() > 0)
parts.add(currentPart.toString());
String[] array = new String[parts.size()];
parts.toArray(array);
return array;
}
// ------------------------------------------------------------------------
/**
* Joins an array of String components into a single whitespace-delimited
* string. If any of the elements of array themselves have spaces in them,
* they will be properly double-quoted.
*
* @param array
* an array of Strings containing the components to be joined
*
* @return a String containing the joined components
*/
public static String join(String[] array)
{
StringBuffer buffer = new StringBuffer();
if(array.length > 0)
{
buffer.append(quoteIfNecessary(array[0]));
for(int i = 1; i < array.length; i++)
{
buffer.append(' ');
buffer.append(quoteIfNecessary(array[i]));
}
}
return buffer.toString();
}
/**
* Performs shell-style quoting on a string if necessary. That is, if the
* string contains spaces, it will be surrounded by double quotes;
* otherwise, it will not. In either case, double quotes already in the
* string will be escaped with a backslash.
*
* @param str the string to quote
* @return a copy of the string that has been quoted if necessary
*/
public static String quoteIfNecessary(String str)
{
str = str.replaceAll("\"", Matcher.quoteReplacement("\\\"")); //$NON-NLS-1$ //$NON-NLS-2$
if (str.indexOf(' ') != -1)
{
return "\"" + str + "\""; //$NON-NLS-1$ //$NON-NLS-2$
}
else
{
return str;
}
}
/**
* Gets a regular expression pattern string based on a plug-in relative
* path that will match any version of that plug-in, not just the one
* provided. All other parts of the path must match exactly; the path
* from the root to the plug-in directory must be the same, and the
* portion of the path after the plug-in directory must also be to the
* exact same resource (not a child of it).
*
* This function can also be used in development, when the path to the
* plug-in is merely the workspace path, because any path that does not
* contain a plug-in version identifier will simply be returned unaltered.
*
* @param path the plug-in relative path to match
* @param isDirectory true if this path is a directory and not a file
*
* @return a regular expression that will match this path regardless of
* the version of the plug-in
*/
public static String patternForAnyVersionOfPluginRelativePath(
String path, boolean isDirectory)
{
// First escape anything that might be a metacharacter. To play it
// safe, we'll just escape anything that isn't a character, digit,
// underscore, hyphen, or forward slash (common path characters).
if (path.endsWith("/")) //$NON-NLS-1$
{
path = path.substring(0, path.length() - 1);
}
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < path.length(); i++)
{
char ch = path.charAt(i);
if (!Character.isLetterOrDigit(ch)
&& ch != '_' && ch != '/' && ch != '-')
{
buffer.append('\\');
}
buffer.append(ch);
}
if (isDirectory)
{
buffer.append("/?"); //$NON-NLS-1$
}
String escapedPath = buffer.toString();
return escapedPath.replaceFirst(VERSION_REGEX,
Matcher.quoteReplacement(VERSION_REGEX));
}
// === Static Variables ===================================================
/**
* State identifiers used by the DFA in the split method.
*/
private static final int STATE_BEGIN = 0;
private static final int STATE_PART = 1;
private static final int STATE_ESCAPE_IN_PART = 2;
private static final int STATE_DQUOTE = 3;
private static final int STATE_ESCAPE_IN_DQUOTE = 4;
private static final int STATE_SQUOTE = 5;
private static final int STATE_ESCAPE_IN_SQUOTE = 6;
private static final String VERSION_REGEX =
"_[^.]+\\.[^.]+\\.[^./]+(\\.[^/]+)?/"; //$NON-NLS-1$
}