View Javadoc

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