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
019/**
020 * Helper methods for working with Strings. 
021 */
022public final class StringHelper {
023
024    /**
025     * Constructor of utility class should be private.
026     */
027    private StringHelper() {
028    }
029    
030    /**
031     * Ensures that <code>s</code> is friendly for a URL or file system.
032     * 
033     * @param s String to be sanitized.
034     * @return sanitized version of <code>s</code>.
035     * @throws NullPointerException if <code>s</code> is <code>null</code>.
036     */
037    public static String sanitize(String s) {
038        return s
039            .replace(':', '-')
040            .replace('_', '-')
041            .replace('.', '-')
042            .replace('/', '-')
043            .replace('\\', '-');
044    }
045
046    /**
047     * Counts the number of times the given char is in the string
048     *
049     * @param s  the string
050     * @param ch the char
051     * @return number of times char is located in the string
052     */
053    public static int countChar(String s, char ch) {
054        if (ObjectHelper.isEmpty(s)) {
055            return 0;
056        }
057
058        int matches = 0;
059        for (int i = 0; i < s.length(); i++) {
060            char c = s.charAt(i);
061            if (ch == c) {
062                matches++;
063            }
064        }
065
066        return matches;
067    }
068
069    public static String removeQuotes(String s) {
070        if (ObjectHelper.isEmpty(s)) {
071            return s;
072        }
073
074        s = s.replaceAll("'", "");
075        s = s.replaceAll("\"", "");
076        return s;
077    }
078
079    public static String removeLeadingAndEndingQuotes(String s) {
080        if (ObjectHelper.isEmpty(s)) {
081            return s;
082        }
083
084        String copy = s.trim();
085        if (copy.startsWith("'") && copy.endsWith("'")) {
086            return copy.substring(1, copy.length() - 1);
087        }
088        if (copy.startsWith("\"") && copy.endsWith("\"")) {
089            return copy.substring(1, copy.length() - 1);
090        }
091
092        // no quotes, so return as-is
093        return s;
094    }
095    
096    public static boolean isQuoted(String s) {
097        if (ObjectHelper.isEmpty(s)) {
098            return false;
099        }
100
101        if (s.startsWith("'") && s.endsWith("'")) {
102            return true;
103        }
104        if (s.startsWith("\"") && s.endsWith("\"")) {
105            return true;
106        }
107
108        return false;
109    }
110
111    /**
112     * Encodes the text into safe XML by replacing < > and & with XML tokens
113     *
114     * @param text  the text
115     * @return the encoded text
116     */
117    public static String xmlEncode(String text) {
118        if (text == null) {
119            return "";
120        }
121        // must replace amp first, so we dont replace &lt; to amp later
122        return text.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
123    }
124
125    /**
126     * Determines if the string has at least one letter in upper case
127     * @param text the text
128     * @return <tt>true</tt> if at least one letter is upper case, <tt>false</tt> otherwise
129     */
130    public static boolean hasUpperCase(String text) {
131        if (text == null) {
132            return false;
133        }
134
135        for (int i = 0; i < text.length(); i++) {
136            char ch = text.charAt(i);
137            if (Character.isUpperCase(ch)) {
138                return true;
139            }
140        }
141
142        return false;
143    }
144
145    /**
146     * Does the expression have the language start token?
147     *
148     * @param expression the expression
149     * @param language the name of the language, such as simple
150     * @return <tt>true</tt> if the expression contains the start token, <tt>false</tt> otherwise
151     */
152    public static boolean hasStartToken(String expression, String language) {
153        if (expression == null) {
154            return false;
155        }
156
157        // for the simple language the expression start token could be "${"
158        if ("simple".equalsIgnoreCase(language) && expression.indexOf("${") >= 0) {
159            return true;
160        }
161
162        if (language != null && expression.indexOf("$" + language + "{") >= 0) {
163            return true;
164        }
165
166        return false;
167    }
168
169    /**
170     * Replaces all the from tokens in the given input string.
171     * <p/>
172     * This implementation is not recursive, not does it check for tokens in the replacement string.
173     *
174     * @param input  the input string
175     * @param from   the from string, must <b>not</b> be <tt>null</tt> or empty
176     * @param to     the replacement string, must <b>not</b> be empty
177     * @return the replaced string, or the input string if no replacement was needed
178     * @throws IllegalArgumentException if the input arguments is invalid
179     */
180    public static String replaceAll(String input, String from, String to) {
181        if (ObjectHelper.isEmpty(input)) {
182            return input;
183        }
184        if (from == null) {
185            throw new IllegalArgumentException("from cannot be null");
186        }
187        if (to == null) {
188            // to can be empty, so only check for null
189            throw new IllegalArgumentException("to cannot be null");
190        }
191
192        // fast check if there is any from at all
193        if (!input.contains(from)) {
194            return input;
195        }
196
197        final int len = from.length();
198        final int max = input.length();
199        StringBuilder sb = new StringBuilder(max);
200        for (int i = 0; i < max;) {
201            if (i + len <= max) {
202                String token = input.substring(i, i + len);
203                if (from.equals(token)) {
204                    sb.append(to);
205                    // fast forward
206                    i = i + len;
207                    continue;
208                }
209            }
210
211            // append single char
212            sb.append(input.charAt(i));
213            // forward to next
214            i++;
215        }
216        return sb.toString();
217    }
218
219}