View Javadoc

1   /**
2    *
3    * Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
4    *
5    * ====================================================================
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   * ====================================================================
18   */
19  package org.jclouds.util;
20  
21  import static com.google.common.base.Preconditions.checkNotNull;
22  import static com.google.common.io.ByteStreams.toByteArray;
23  import static com.google.common.io.Closeables.closeQuietly;
24  import static org.jclouds.util.Patterns.CHAR_TO_ENCODED_PATTERN;
25  import static org.jclouds.util.Patterns.CHAR_TO_PATTERN;
26  import static org.jclouds.util.Patterns.PLUS_PATTERN;
27  import static org.jclouds.util.Patterns.STAR_PATTERN;
28  import static org.jclouds.util.Patterns.TOKEN_TO_PATTERN;
29  import static org.jclouds.util.Patterns.URL_ENCODED_PATTERN;
30  import static org.jclouds.util.Patterns._7E_PATTERN;
31  
32  import java.io.ByteArrayInputStream;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.UnsupportedEncodingException;
36  import java.net.URLDecoder;
37  import java.net.URLEncoder;
38  import java.util.Map;
39  import java.util.Map.Entry;
40  import java.util.regex.Matcher;
41  import java.util.regex.Pattern;
42  
43  import javax.annotation.Resource;
44  
45  import org.jclouds.logging.Logger;
46  
47  import com.google.common.base.Charsets;
48  
49  /**
50   * 
51   * 
52   * @author Adrian Cole
53   */
54  public class Strings2 {
55  
56     /**
57      * Web browsers do not always handle '+' characters well, use the well-supported '%20' instead.
58      */
59     public static String urlEncode(String in, char... skipEncode) {
60        if (isUrlEncoded(in))
61           return in;
62        try {
63           String returnVal = URLEncoder.encode(in, "UTF-8");
64           returnVal = Strings2.replaceAll(returnVal, '+', PLUS_PATTERN, "%20");
65           returnVal = Strings2.replaceAll(returnVal, '*', STAR_PATTERN, "%2A");
66           returnVal = Strings2.replaceAll(returnVal, _7E_PATTERN, "~");
67           for (char c : skipEncode) {
68              returnVal = Strings2.replaceAll(returnVal, CHAR_TO_ENCODED_PATTERN.get(c), c + "");
69           }
70           return returnVal;
71        } catch (UnsupportedEncodingException e) {
72           throw new IllegalStateException("Bad encoding on input: " + in, e);
73        }
74     }
75  
76     public static boolean isUrlEncoded(String in) {
77        return URL_ENCODED_PATTERN.matcher(in).matches();
78     }
79  
80     public static String urlDecode(String in) {
81        try {
82           return URLDecoder.decode(in, "UTF-8");
83        } catch (UnsupportedEncodingException e) {
84           throw new IllegalStateException("Bad encoding on input: " + in, e);
85        }
86     }
87  
88     public static String replaceTokens(String value, Iterable<Entry<String, String>> tokenValues) {
89        for (Entry<String, String> tokenValue : tokenValues) {
90           value = Strings2.replaceAll(value, TOKEN_TO_PATTERN.get(tokenValue.getKey()), tokenValue.getValue());
91        }
92        return value;
93     }
94  
95     public static String replaceAll(String returnVal, Pattern pattern, String replace) {
96        Matcher m = pattern.matcher(returnVal);
97        returnVal = m.replaceAll(replace);
98        return returnVal;
99     }
100 
101    public static String replaceAll(String input, char ifMatch, Pattern pattern, String replacement) {
102       if (input.indexOf(ifMatch) != -1) {
103          input = pattern.matcher(input).replaceAll(replacement);
104       }
105       return input;
106    }
107 
108    public static String replaceAll(String input, char match, String replacement) {
109       if (input.indexOf(match) != -1) {
110          input = CHAR_TO_PATTERN.get(match).matcher(input).replaceAll(replacement);
111       }
112       return input;
113    }
114 
115    public static final String UTF8_ENCODING = "UTF-8";
116 
117    public static String toStringAndClose(InputStream input) throws IOException {
118       checkNotNull(input, "input");
119       try {
120          return new String(toByteArray(input), Charsets.UTF_8);
121       } catch (IOException e) {
122          logger.warn(e, "Failed to read from stream");
123          return null;
124       } catch (NullPointerException e) {
125          return null;
126       } finally {
127          closeQuietly(input);
128       }
129    }
130 
131    public static InputStream toInputStream(String in) {
132       return new ByteArrayInputStream(in.getBytes(Charsets.UTF_8));
133    }
134 
135    /**
136     * Encode the given string with the given encoding, if possible. If the encoding fails with
137     * {@link UnsupportedEncodingException}, log a warning and fall back to the system's default
138     * encoding.
139     * 
140     * @param str
141     *           what to encode
142     * @param charsetName
143     *           the name of a supported {@link java.nio.charset.Charset </code>charset<code>}
144     * @return properly encoded String.
145     */
146    public static byte[] encodeString(String str, String charsetName) {
147       try {
148          return str.getBytes(charsetName);
149       } catch (UnsupportedEncodingException e) {
150          logger.warn(e, "Failed to encode string to bytes with encoding " + charsetName
151                + ". Falling back to system's default encoding");
152          return str.getBytes();
153       }
154    }
155 
156    @Resource
157    private static Logger logger = Logger.NULL;
158 
159    /**
160     * Encode the given string with the UTF-8 encoding, the sane default. In the very unlikely event
161     * the encoding fails with {@link UnsupportedEncodingException}, log a warning and fall back to
162     * the system's default encoding.
163     * 
164     * @param str
165     *           what to encode
166     * @return properly encoded String.
167     */
168    public static byte[] encodeString(String str) {
169       return encodeString(str, UTF8_ENCODING);
170    }
171 
172    /**
173     * replaces tokens that are expressed as <code>{token}</code>
174     * 
175     * <p/>
176     * ex. if input is "hello {where}"<br/>
177     * and replacements is "where" -> "world" <br/>
178     * then replaceTokens returns "hello world"
179     * 
180     * @param input
181     *           source to replace
182     * @param replacements
183     *           token/value pairs
184     */
185    public static String replaceTokens(String input, Map<String, String> replacements) {
186       Matcher matcher = Patterns.TOKEN_PATTERN.matcher(input);
187       StringBuilder builder = new StringBuilder();
188       int i = 0;
189       while (matcher.find()) {
190          String replacement = replacements.get(matcher.group(1));
191          builder.append(input.substring(i, matcher.start()));
192          if (replacement == null)
193             builder.append(matcher.group(0));
194          else
195             builder.append(replacement);
196          i = matcher.end();
197       }
198       builder.append(input.substring(i, input.length()));
199       return builder.toString();
200    }
201 
202 }