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    }