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 * This class is not part of the public API and could change without 033 * warning. 034 * 035 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a> 036 */ 037 public class StringUtils { 038 039 private static final String SINGLE_QUOTE = "\'"; 040 private static final String DOUBLE_QUOTE = "\""; 041 private static final char SLASH_CHAR = '/'; 042 private static final char BACKSLASH_CHAR = '\\'; 043 044 /** 045 * Perform a series of substitutions. The substitutions 046 * are performed by replacing ${variable} in the target 047 * string with the value of provided by the key "variable" 048 * in the provided hash table. 049 * <p/><p/> 050 * A key consists of the following characters: 051 * <ul> 052 * <li>letter 053 * <li>digit 054 * <li>dot character 055 * <li>hyphen character 056 * <li>plus character 057 * <li>underscore character 058 * </ul> 059 * 060 * @param argStr the argument string to be processed 061 * @param vars name/value pairs used for substitution 062 * @param isLenient ignore a key not found in vars or throw a RuntimeException? 063 * @return String target string with replacements. 064 */ 065 public static StringBuffer stringSubstitution(String argStr, Map vars, boolean isLenient) { 066 067 StringBuffer argBuf = new StringBuffer(); 068 069 if (argStr == null || argStr.length() == 0) { 070 return argBuf; 071 } 072 073 if (vars == null || vars.size() == 0) { 074 return argBuf.append(argStr); 075 } 076 077 int argStrLength = argStr.length(); 078 079 for (int cIdx = 0; cIdx < argStrLength;) { 080 081 char ch = argStr.charAt(cIdx); 082 char del = ' '; 083 084 switch (ch) { 085 086 case '$': 087 StringBuffer nameBuf = new StringBuffer(); 088 del = argStr.charAt(cIdx + 1); 089 if (del == '{') { 090 cIdx++; 091 092 for (++cIdx; cIdx < argStr.length(); ++cIdx) { 093 ch = argStr.charAt(cIdx); 094 if (ch == '_' || ch == '.' || ch == '-' || ch == '+' || Character.isLetterOrDigit(ch)) 095 nameBuf.append(ch); 096 else 097 break; 098 } 099 100 if (nameBuf.length() >= 0) { 101 102 String value; 103 Object temp = vars.get(nameBuf.toString()); 104 105 if(temp instanceof File) { 106 // for a file we have to fix the separator chars to allow 107 // cross-platform compatibility 108 value = fixFileSeparatorChar(((File) temp).getAbsolutePath()); 109 } 110 else { 111 value = (temp != null ? temp.toString() : null); 112 } 113 114 if (value != null) { 115 argBuf.append(value); 116 } else { 117 if (isLenient) { 118 // just append the unresolved variable declaration 119 argBuf.append("${").append(nameBuf.toString()).append("}"); 120 } else { 121 // complain that no variable was found 122 throw new RuntimeException("No value found for : " + nameBuf); 123 } 124 } 125 126 del = argStr.charAt(cIdx); 127 128 if (del != '}') { 129 throw new RuntimeException("Delimiter not found for : " + nameBuf); 130 } 131 } 132 133 cIdx++; 134 } 135 else { 136 argBuf.append(ch); 137 ++cIdx; 138 } 139 140 break; 141 142 default: 143 argBuf.append(ch); 144 ++cIdx; 145 break; 146 } 147 } 148 149 return argBuf; 150 } 151 152 /** 153 * Split a string into an array of strings based 154 * on a separator. 155 * 156 * @param input what to split 157 * @param splitChar what to split on 158 * @return the array of strings 159 */ 160 public static String[] split(String input, String splitChar) { 161 StringTokenizer tokens = new StringTokenizer(input, splitChar); 162 List strList = new ArrayList(); 163 while (tokens.hasMoreTokens()) { 164 strList.add(tokens.nextToken()); 165 } 166 return (String[]) strList.toArray(new String[strList.size()]); 167 } 168 169 /** 170 * Fixes the file separator char for the target platform 171 * using the following replacement. 172 * 173 * <ul> 174 * <li> '/' ==> File.separatorChar 175 * <li> '\\' ==> File.separatorChar 176 * </ul> 177 * 178 * @param arg the argument to fix 179 * @return the transformed argument 180 */ 181 public static String fixFileSeparatorChar(String arg) { 182 return arg.replace(SLASH_CHAR, File.separatorChar).replace( 183 BACKSLASH_CHAR, File.separatorChar); 184 } 185 186 /** 187 * Concatenates an array of string using a separator. 188 * 189 * @param strings the strings to concatenate 190 * @param separator the separator between two strings 191 * @return the concatenated strings 192 */ 193 public static String toString(String[] strings, String separator) { 194 StringBuffer sb = new StringBuffer(); 195 for (int i = 0; i < strings.length; i++) { 196 if (i > 0) { 197 sb.append(separator); 198 } 199 sb.append(strings[i]); 200 } 201 return sb.toString(); 202 } 203 204 /** 205 * Put quotes around the given String if necessary. 206 * <p> 207 * If the argument doesn't include spaces or quotes, return it as is. If it 208 * contains double quotes, use single quotes - else surround the argument by 209 * double quotes. 210 * </p> 211 * 212 * @param argument the argument to be quoted 213 * @return the quoted argument 214 * @throws IllegalArgumentException If argument contains both types of quotes 215 */ 216 public static String quoteArgument(final String argument) { 217 218 String cleanedArgument = argument.trim(); 219 220 // strip the quotes from both ends 221 while(cleanedArgument.startsWith(SINGLE_QUOTE) || cleanedArgument.startsWith(DOUBLE_QUOTE)) { 222 cleanedArgument = cleanedArgument.substring(1); 223 } 224 225 while(cleanedArgument.endsWith(SINGLE_QUOTE) || cleanedArgument.endsWith(DOUBLE_QUOTE)) { 226 cleanedArgument = cleanedArgument.substring(0, cleanedArgument.length() - 1); 227 } 228 229 final StringBuffer buf = new StringBuffer(); 230 if (cleanedArgument.indexOf(DOUBLE_QUOTE) > -1) { 231 if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1) { 232 throw new IllegalArgumentException( 233 "Can't handle single and double quotes in same argument"); 234 } else { 235 return buf.append(SINGLE_QUOTE).append(cleanedArgument).append( 236 SINGLE_QUOTE).toString(); 237 } 238 } else if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1 239 || cleanedArgument.indexOf(" ") > -1) { 240 return buf.append(DOUBLE_QUOTE).append(cleanedArgument).append( 241 DOUBLE_QUOTE).toString(); 242 } else { 243 return cleanedArgument; 244 } 245 } 246 247 /** 248 * Determines if this is a quoted argument - either single or 249 * double quoted. 250 * 251 * @param argument the argument to check 252 * @return true when the argument is quoted 253 */ 254 public static boolean isQuoted(final String argument) { 255 return ( argument.startsWith( SINGLE_QUOTE ) && argument.endsWith( SINGLE_QUOTE ) ) || 256 ( argument.startsWith( DOUBLE_QUOTE ) && argument.endsWith( DOUBLE_QUOTE ) ); 257 } 258 }