    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    }