StringUtils.java
/*
* 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.Objects;
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[] EMPTY_STRING_ARRAY = {};
private static final String SINGLE_QUOTE = "\'";
private static final String DOUBLE_QUOTE = "\"";
private static final char SLASH_CHAR = '/';
private static final char BACKSLASH_CHAR = '\\';
/**
* Fixes the file separator char for the target platform using the following replacement.
* <ul>
* <li>'/' → File.separatorChar</li>
* <li>'\\' → 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);
}
/**
* 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);
}
/**
* 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();
}
if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1 || cleanedArgument.indexOf(" ") > -1) {
return buf.append(DOUBLE_QUOTE).append(cleanedArgument).append(DOUBLE_QUOTE).toString();
}
return cleanedArgument;
}
/**
* 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(EMPTY_STRING_ARRAY);
}
/**
* 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)) {
break;
}
nameBuf.append(ch);
}
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 = Objects.toString(temp, null);
}
if (value != null) {
argBuf.append(value);
} else {
if (!isLenient) {
// complain that no variable was found
throw new IllegalArgumentException("No value found for : " + nameBuf);
}
// just append the unresolved variable declaration
argBuf.append("${").append(nameBuf.toString()).append("}");
}
del = argStr.charAt(cIdx);
if (del != '}') {
throw new IllegalArgumentException("Delimiter not found for : " + nameBuf);
}
}
} else {
argBuf.append(ch);
}
cIdx++;
break;
default:
argBuf.append(ch);
++cIdx;
break;
}
}
return argBuf;
}
/**
* 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.
* @deprecated Use {@link String#join(CharSequence, CharSequence...)}.
*/
@Deprecated
public static String toString(final String[] strings, final String separator) {
return String.join(separator, strings);
}
}