001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 */
018
019 package org.apache.commons.exec.util;
020
021
022 import java.util.ArrayList;
023 import java.util.List;
024 import java.util.Map;
025 import java.util.StringTokenizer;
026 import java.io.File;
027
028 /**
029 * Supplement of commons-lang, the stringSubstitution() was in a simpler
030 * implementation available in an older commons-lang implementation.
031 *
032 * Furthermore a place to put reusable and/or ugly code.
033 *
034 * This class is not part of the public API and could change without
035 * warning.
036 *
037 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
038 */
039 public class StringUtils {
040
041 private static final String SINGLE_QUOTE = "\'";
042 private static final String DOUBLE_QUOTE = "\"";
043 private static final char SLASH_CHAR = '/';
044 private static final char BACKSLASH_CHAR = '\\';
045
046 /**
047 * Perform a series of substitutions. The substitions
048 * are performed by replacing ${variable} in the target
049 * string with the value of provided by the key "variable"
050 * in the provided hashtable.
051 *
052 * @param argStr the argument string to be processed
053 * @param vars name/value pairs used for substitution
054 * @param isLenient ignore a key not found in vars?
055 * @return String target string with replacements.
056 */
057 public static StringBuffer stringSubstitution(String argStr, Map vars, boolean isLenient) {
058
059 StringBuffer argBuf = new StringBuffer();
060
061 if (argStr == null || argStr.length() == 0) {
062 return argBuf;
063 }
064
065 if (vars == null || vars.size() == 0) {
066 return argBuf.append(argStr);
067 }
068
069 int argStrLength = argStr.length();
070
071 for (int cIdx = 0; cIdx < argStrLength;) {
072
073 char ch = argStr.charAt(cIdx);
074 char del = ' ';
075
076 switch (ch) {
077
078 case '$':
079 StringBuffer nameBuf = new StringBuffer();
080 del = argStr.charAt(cIdx + 1);
081 if (del == '{') {
082 cIdx++;
083
084 for (++cIdx; cIdx < argStr.length(); ++cIdx) {
085 ch = argStr.charAt(cIdx);
086 if (ch == '_' || ch == '.' || ch == '-' || ch == '+' || Character.isLetterOrDigit(ch))
087 nameBuf.append(ch);
088 else
089 break;
090 }
091
092 if (nameBuf.length() > 0) {
093 Object temp = vars.get(nameBuf.toString());
094 String value = (temp != null ? temp.toString() : null);
095
096 if (value != null) {
097 argBuf.append(value);
098 } else {
099 if (isLenient) {
100 // just append the unresolved variable declaration
101 argBuf.append("${").append(nameBuf.toString()).append("}");
102 } else {
103 // complain that no variable was found
104 throw new RuntimeException("No value found for : " + nameBuf);
105 }
106 }
107
108 del = argStr.charAt(cIdx);
109
110 if (del != '}') {
111 throw new RuntimeException("Delimiter not found for : " + nameBuf);
112 }
113 }
114
115 cIdx++;
116 } else {
117 argBuf.append(ch);
118 ++cIdx;
119 }
120
121 break;
122
123 default:
124 argBuf.append(ch);
125 ++cIdx;
126 break;
127 }
128 }
129
130 return argBuf;
131 }
132
133 /**
134 * Split a string into an array of strings based
135 * on a separator.
136 *
137 * @param input what to split
138 * @param splitChar what to split on
139 * @return the array of strings
140 */
141 public static String[] split(String input, String splitChar) {
142 StringTokenizer tokens = new StringTokenizer(input, splitChar);
143 List strList = new ArrayList();
144 while (tokens.hasMoreTokens()) {
145 strList.add(tokens.nextToken());
146 }
147 return (String[]) strList.toArray(new String[strList.size()]);
148 }
149
150 /**
151 * Fixes the file sperator char for the target platform
152 * using the following replacement.
153 *
154 * <ul>
155 * <li> '/' ==> File.separatorChar
156 * <li> '\\' ==> File.separatorChar
157 * </ul>
158 *
159 * @param arg the argument to fix
160 * @return the transformed argument
161 */
162 public static String fixFileSeparatorChar(String arg) {
163 return arg.replace(SLASH_CHAR, File.separatorChar).replace(
164 BACKSLASH_CHAR, File.separatorChar);
165 }
166
167 /**
168 * Concatenates an array of string using a separator.
169 *
170 * @param strings the strings to concatenate
171 * @param separator the separator between two strings
172 * @return the concatened strings
173 */
174 public static String toString(String[] strings, String separator) {
175 StringBuffer sb = new StringBuffer();
176 for (int i = 0; i < strings.length; i++) {
177 if (i > 0) {
178 sb.append(separator);
179 }
180 sb.append(strings[i]);
181 }
182 return sb.toString();
183 }
184
185 /**
186 * Put quotes around the given String if necessary.
187 * <p>
188 * If the argument doesn't include spaces or quotes, return it as is. If it
189 * contains double quotes, use single quotes - else surround the argument by
190 * double quotes.
191 * </p>
192 *
193 * @param argument the argument to be quoted
194 * @return the quoted argument
195 * @throws IllegalArgumentException If argument contains both types of quotes
196 */
197 public static String quoteArgument(final String argument) {
198
199 String cleanedArgument = argument.trim();
200
201 while(cleanedArgument.startsWith(SINGLE_QUOTE) || cleanedArgument.startsWith(DOUBLE_QUOTE)) {
202 cleanedArgument = cleanedArgument.substring(1);
203 }
204 while(cleanedArgument.endsWith(SINGLE_QUOTE) || cleanedArgument.endsWith(DOUBLE_QUOTE)) {
205 cleanedArgument = cleanedArgument.substring(0, cleanedArgument.length() - 1);
206 }
207
208 final StringBuffer buf = new StringBuffer();
209 if (cleanedArgument.indexOf(DOUBLE_QUOTE) > -1) {
210 if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1) {
211 throw new IllegalArgumentException(
212 "Can't handle single and double quotes in same argument");
213 } else {
214 return buf.append(SINGLE_QUOTE).append(cleanedArgument).append(
215 SINGLE_QUOTE).toString();
216 }
217 } else if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1
218 || cleanedArgument.indexOf(" ") > -1) {
219 return buf.append(DOUBLE_QUOTE).append(cleanedArgument).append(
220 DOUBLE_QUOTE).toString();
221 } else {
222 return cleanedArgument;
223 }
224 }
225
226 /**
227 * Determines if this is a quoted argumented - either single or
228 * double quoted.
229 *
230 * @param argument the argument to check
231 * @return true when the argument is quoted
232 */
233 public static boolean isQuoted(final String argument) {
234 return ( argument.startsWith( SINGLE_QUOTE ) || argument.startsWith( DOUBLE_QUOTE ) ) &&
235 ( argument.endsWith( SINGLE_QUOTE ) || argument.endsWith( DOUBLE_QUOTE ) );
236 }
237 }