blob: 812cd708b97939738535f4d11c7963f0fc5fae6d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.exec.util;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Supplement of commons-lang, the stringSubstitution() was in a simpler
* implementation available in an older commons-lang implementation.
*
* This class is not part of the public API and could change without
* warning.
*
*/
public class StringUtils {
private static final String SINGLE_QUOTE = "\'";
private static final String DOUBLE_QUOTE = "\"";
private static final char SLASH_CHAR = '/';
private static final char BACKSLASH_CHAR = '\\';
/**
* Perform a series of substitutions.
* <p>
* The substitutions are performed by replacing ${variable} in the target string with the value of provided by the
* key "variable" in the provided hash table.
* </p>
* <p>
* A key consists of the following characters:
* </p>
* <ul>
* <li>letter
* <li>digit
* <li>dot character
* <li>hyphen character
* <li>plus character
* <li>underscore character
* </ul>
*
* @param argStr
* the argument string to be processed
* @param vars
* name/value pairs used for substitution
* @param isLenient
* ignore a key not found in vars or throw a RuntimeException?
* @return String target string with replacements.
*/
public static StringBuffer stringSubstitution(final String argStr, final Map<? super String, ?> vars, final boolean isLenient) {
final StringBuffer argBuf = new StringBuffer();
if (argStr == null || argStr.isEmpty()) {
return argBuf;
}
if (vars == null || vars.isEmpty()) {
return argBuf.append(argStr);
}
final int argStrLength = argStr.length();
for (int cIdx = 0; cIdx < argStrLength;) {
char ch = argStr.charAt(cIdx);
char del = ' ';
switch (ch) {
case '$':
final StringBuilder nameBuf = new StringBuilder();
del = argStr.charAt(cIdx + 1);
if (del == '{') {
cIdx++;
for (++cIdx; cIdx < argStr.length(); ++cIdx) {
ch = argStr.charAt(cIdx);
if (ch == '_' || ch == '.' || ch == '-' || ch == '+' || Character.isLetterOrDigit(ch)) {
nameBuf.append(ch);
} else {
break;
}
}
if (nameBuf.length() >= 0) {
String value;
final Object temp = vars.get(nameBuf.toString());
if (temp instanceof File) {
// for a file we have to fix the separator chars to allow
// cross-platform compatibility
value = fixFileSeparatorChar(((File) temp).getAbsolutePath());
}
else {
value = temp != null ? temp.toString() : null;
}
if (value != null) {
argBuf.append(value);
} else {
if (isLenient) {
// just append the unresolved variable declaration
argBuf.append("${").append(nameBuf.toString()).append("}");
} else {
// complain that no variable was found
throw new RuntimeException("No value found for : " + nameBuf);
}
}
del = argStr.charAt(cIdx);
if (del != '}') {
throw new RuntimeException("Delimiter not found for : " + nameBuf);
}
}
cIdx++;
}
else {
argBuf.append(ch);
++cIdx;
}
break;
default:
argBuf.append(ch);
++cIdx;
break;
}
}
return argBuf;
}
/**
* Split a string into an array of strings based
* on a separator.
*
* @param input what to split
* @param splitChar what to split on
* @return the array of strings
*/
public static String[] split(final String input, final String splitChar) {
final StringTokenizer tokens = new StringTokenizer(input, splitChar);
final List<String> strList = new ArrayList<>();
while (tokens.hasMoreTokens()) {
strList.add(tokens.nextToken());
}
return strList.toArray(new String[strList.size()]);
}
/**
* Fixes the file separator char for the target platform
* using the following replacement.
*
* <ul>
* <li>'/' &#x2192; File.separatorChar</li>
* <li>'\\' &#x2192; File.separatorChar</li>
* </ul>
*
* @param arg the argument to fix
* @return the transformed argument
*/
public static String fixFileSeparatorChar(final String arg) {
return arg.replace(SLASH_CHAR, File.separatorChar).replace(
BACKSLASH_CHAR, File.separatorChar);
}
/**
* Concatenates an array of string using a separator.
*
* @param strings the strings to concatenate
* @param separator the separator between two strings
* @return the concatenated strings
*/
public static String toString(final String[] strings, final String separator) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < strings.length; i++) {
if (i > 0) {
sb.append(separator);
}
sb.append(strings[i]);
}
return sb.toString();
}
/**
* Put quotes around the given String if necessary.
* <p>
* If the argument doesn't include spaces or quotes, return it as is. If it
* contains double quotes, use single quotes - else surround the argument by
* double quotes.
* </p>
*
* @param argument the argument to be quoted
* @return the quoted argument
* @throws IllegalArgumentException If argument contains both types of quotes
*/
public static String quoteArgument(final String argument) {
String cleanedArgument = argument.trim();
// strip the quotes from both ends
while (cleanedArgument.startsWith(SINGLE_QUOTE) || cleanedArgument.startsWith(DOUBLE_QUOTE)) {
cleanedArgument = cleanedArgument.substring(1);
}
while (cleanedArgument.endsWith(SINGLE_QUOTE) || cleanedArgument.endsWith(DOUBLE_QUOTE)) {
cleanedArgument = cleanedArgument.substring(0, cleanedArgument.length() - 1);
}
final StringBuilder buf = new StringBuilder();
if (cleanedArgument.indexOf(DOUBLE_QUOTE) > -1) {
if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1) {
throw new IllegalArgumentException(
"Can't handle single and double quotes in same argument");
}
return buf.append(SINGLE_QUOTE).append(cleanedArgument).append(
SINGLE_QUOTE).toString();
} else if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1
|| cleanedArgument.indexOf(" ") > -1) {
return buf.append(DOUBLE_QUOTE).append(cleanedArgument).append(
DOUBLE_QUOTE).toString();
} else {
return cleanedArgument;
}
}
/**
* Determines if this is a quoted argument - either single or
* double quoted.
*
* @param argument the argument to check
* @return true when the argument is quoted
*/
public static boolean isQuoted(final String argument) {
return argument.startsWith(SINGLE_QUOTE) && argument.endsWith(SINGLE_QUOTE)
|| argument.startsWith(DOUBLE_QUOTE) && argument.endsWith(DOUBLE_QUOTE);
}
}