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 */
017package org.apache.camel.util;
018
019import java.util.ArrayList;
020import java.util.List;
021
022/**
023 * Utility class for parsing quoted string which is intended for parameters, separated by comma.
024 */
025public final class StringQuoteHelper {
026
027    private StringQuoteHelper() {
028    }
029
030    /**
031     * Returns the text wrapped double quotes
032     */
033    public static String doubleQuote(String text) {
034        return quote(text, "\"");
035    }
036
037    /**
038     * Returns the text wrapped single quotes
039     */
040    public static String singleQuote(String text) {
041        return quote(text, "'");
042    }
043
044    /**
045     * Wraps the text in the given quote text
046     *
047     * @param text the text to wrap in quotes
048     * @param quote the quote text added to the prefix and postfix of the text
049     *
050     * @return the text wrapped in the given quotes
051     */
052    public static String quote(String text, String quote) {
053        return quote + text + quote;
054    }
055
056
057    /**
058     * Splits the input safely honoring if values is enclosed in quotes.
059     * <p/>
060     * Though this method does not support double quoting values. A quoted value
061     * must start with the same start and ending quote, which is either a single
062     * quote or double quote value.
063     * <p/>
064     * Will <i>trim</i> each splitted value by default.
065     *
066     * @param input    the input
067     * @param separator the separator char to split the input, for example a comma.
068     * @return the input splitted, or <tt>null</tt> if the input is null.
069     */
070    public static String[] splitSafeQuote(String input, char separator) {
071        return splitSafeQuote(input, separator, true);
072    }
073
074    /**
075     * Splits the input safely honoring if values is enclosed in quotes.
076     * <p/>
077     * Though this method does not support double quoting values. A quoted value
078     * must start with the same start and ending quote, which is either a single
079     * quote or double quote value.
080     * \
081     * @param input    the input
082     * @param separator the separator char to split the input, for example a comma.
083     * @param trim      whether to trim each splitted value
084     * @return the input splitted, or <tt>null</tt> if the input is null.
085     */
086    public static String[] splitSafeQuote(String input, char separator, boolean trim) {
087        if (input == null) {
088            return null;
089        }
090
091        if (input.indexOf(separator) == -1) {
092            // no separator in data, so return single string with input as is
093            return new String[]{trim ? input.trim() : input};
094        }
095
096        List<String> answer = new ArrayList<String>();
097        StringBuilder sb = new StringBuilder();
098
099        boolean singleQuoted = false;
100        boolean doubleQuoted = false;
101        boolean skipLeadingWhitespace = true;
102
103        for (int i = 0; i < input.length(); i++) {
104            char ch = input.charAt(i);
105            char prev = i > 0 ? input.charAt(i - 1) : 0;
106            boolean isQuoting = singleQuoted || doubleQuoted;
107
108            if (!doubleQuoted &&  ch == '\'') {
109                if (singleQuoted && prev == ch && sb.length() == 0) {
110                    // its an empty quote so add empty text
111                    answer.add("");
112                }
113                // special logic needed if this quote is the end
114                if (i == input.length() - 1) {
115                    if (singleQuoted && sb.length() > 0) {
116                        String text = sb.toString();
117                        // do not trim a quoted string
118                        answer.add(text);
119                        sb.setLength(0);
120                    }
121                }
122                singleQuoted = !singleQuoted;
123                continue;
124            } else if (!singleQuoted && ch == '"') {
125                if (doubleQuoted && prev == ch && sb.length() == 0) {
126                    // its an empty quote so add empty text
127                    answer.add("");
128                }
129                // special logic needed if this quote is the end
130                if (i == input.length() - 1) {
131                    if (doubleQuoted && sb.length() > 0) {
132                        String text = sb.toString();
133                        // do not trim a quoted string
134                        answer.add(text);
135                        sb.setLength(0);
136                    }
137                }
138                doubleQuoted = !doubleQuoted;
139                continue;
140            } else if (!isQuoting && ch == ' ') {
141                if (skipLeadingWhitespace) {
142                    continue;
143                }
144            } else if (!isQuoting && ch == separator) {
145                // add as answer if we are not in a quote
146                if (sb.length() > 0) {
147                    String text = sb.toString();
148                    if (trim) {
149                        text = text.trim();
150                    }
151                    answer.add(text);
152                    sb.setLength(0);
153                }
154                // we should avoid adding the separator 
155                continue;
156            }
157
158            sb.append(ch);
159        }
160
161        // any leftover
162        if (sb.length() > 0) {
163            String text = sb.toString();
164            if (trim) {
165                text = text.trim();
166            }
167            answer.add(text);
168        }
169
170        return answer.toArray(new String[answer.size()]);
171    }
172
173}