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    package org.apache.camel.component.exec.impl;
018    
019    import java.util.ArrayList;
020    import java.util.List;
021    import java.util.StringTokenizer;
022    
023    /**
024     * Utility class for parsing, used by the Camel Exec component.<br>
025     * Note: the class should be dropped, when the the commons-exec library
026     * implements similar functionality.
027     */
028    public final class ExecParseUtils {
029    
030        public static final String WHITESPACE = " ";
031    
032        public static final String QUOTE_CHAR = "\"";
033    
034        private ExecParseUtils() {
035        }
036    
037        /**
038         * Splits the input line string by {@link #WHITESPACE}. Supports quoting the
039         * white-spaces with a {@link #QUOTE_CHAR}. A quote itself can also be
040         * enclosed within #{@link #QUOTE_CHAR}#{@link #QUOTE_CHAR}. More than two
041         * double-quotes in a sequence is not allowed. Nested quotes are not
042         * allowed.<br>
043         * E.g. The string
044         * <code>"arg 1"  arg2<code> will return the tokens <code>arg 1</code>,
045         * <code>arg2</code><br>
046         * The string
047         * <code>""arg 1""  "arg2" arg 3<code> will return the tokens <code>"arg 1"</code>
048         * , <code>arg2</code>,<code>arg</code> and <code>3</code> <br>
049         * 
050         * @param input the input to split.
051         * @return a not-null list of tokens
052         */
053        public static List<String> splitToWhiteSpaceSeparatedTokens(String input) {
054            if (input == null) {
055                return new ArrayList<String>();
056            }
057            StringTokenizer tokenizer = new StringTokenizer(input.trim(), QUOTE_CHAR + WHITESPACE, true);
058            List<String> tokens = new ArrayList<String>();
059    
060            StringBuilder quotedText = new StringBuilder();
061    
062            while (tokenizer.hasMoreTokens()) {
063                String token = tokenizer.nextToken();
064                if (QUOTE_CHAR.equals(token)) {
065                    // if we have a quote, add the next tokens to the quoted text
066                    // until the quoting has finished
067                    quotedText.append(QUOTE_CHAR);
068                    String buffer = quotedText.toString();
069                    if (isSingleQuoted(buffer) || isDoubleQuoted(buffer)) {
070                        tokens.add(buffer.substring(1, buffer.length() - 1));
071                        quotedText = new StringBuilder();
072                    }
073                } else if (WHITESPACE.equals(token)) {
074                    // a white space, if in quote, add the white space, otherwise
075                    // skip it
076                    if (quotedText.length() > 0) {
077                        quotedText.append(WHITESPACE);
078                    }
079                } else {
080                    if (quotedText.length() > 0) {
081                        quotedText.append(token);
082                    } else {
083                        tokens.add(token);
084                    }
085                }
086            }
087            if (quotedText.length() > 0) {
088                throw new IllegalArgumentException("Invalid quoting found in args " + quotedText);
089            }
090            return tokens;
091        }
092    
093        /**
094         * Tests if the input is enclosed within {@link #QUOTE_CHAR} characters
095         * 
096         * @param input a not null String
097         * @return true if the regular expression is matched
098         */
099        protected static boolean isSingleQuoted(String input) {
100            if (input == null || input.trim().length() == 0) {
101                return false;
102            }
103            return input.matches("(^" + QUOTE_CHAR + "{1}([^" + QUOTE_CHAR + "]+)" + QUOTE_CHAR + "{1})");
104        }
105    
106        /**
107         * Tests if the input is enclosed within a double-{@link #QUOTE_CHAR} string
108         * 
109         * @param input a not null String
110         * @return true if the regular expression is matched
111         */
112        protected static boolean isDoubleQuoted(String input) {
113            if (input == null || input.trim().length() == 0) {
114                return false;
115            }
116            return input.matches("(^" + QUOTE_CHAR + "{2}([^" + QUOTE_CHAR + "]+)" + QUOTE_CHAR + "{2})");
117        }
118    }