1b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien/* 2b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Copyright 2007 Netflix, Inc. 3b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * 4b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Licensed under the Apache License, Version 2.0 (the "License"); 5b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * you may not use this file except in compliance with the License. 6b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * You may obtain a copy of the License at 7b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * 8b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * http://www.apache.org/licenses/LICENSE-2.0 9b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * 10b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Unless required by applicable law or agreed to in writing, software 11b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * distributed under the License is distributed on an "AS IS" BASIS, 12b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * See the License for the specific language governing permissions and 14b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * limitations under the License. 15b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */ 16b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 17b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienpackage net.oauth; 18b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 19b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.ByteArrayOutputStream; 20b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.IOException; 21b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.OutputStream; 22b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.UnsupportedEncodingException; 23b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.net.URLDecoder; 24b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.net.URLEncoder; 25b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.ArrayList; 26b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.HashMap; 27b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.List; 28b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.Map; 29b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 30b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien/** 31b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * @author John Kristian 32b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * @hide 33b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */ 34b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienpublic class OAuth { 35b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 36b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String VERSION_1_0 = "1.0"; 37b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 38b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** The encoding used to represent characters as bytes. */ 39b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String ENCODING = "UTF-8"; 40b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 41b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** The MIME type for a sequence of OAuth parameters. */ 42b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String FORM_ENCODED = "application/x-www-form-urlencoded"; 43b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 44b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_CONSUMER_KEY = "oauth_consumer_key"; 45b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_TOKEN = "oauth_token"; 46b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_TOKEN_SECRET = "oauth_token_secret"; 47b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_SIGNATURE_METHOD = "oauth_signature_method"; 48b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_SIGNATURE = "oauth_signature"; 49b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_TIMESTAMP = "oauth_timestamp"; 50b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_NONCE = "oauth_nonce"; 51b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_VERSION = "oauth_version"; 52b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 53b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String HMAC_SHA1 = "HMAC-SHA1"; 54b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String RSA_SHA1 = "RSA-SHA1"; 55b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 56b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static class Problems { 57b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String TOKEN_NOT_AUTHORIZED = "token_not_authorized"; 58b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String INVALID_USED_NONCE = "invalid_used_nonce"; 59b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String SIGNATURE_INVALID = "signature_invalid"; 60b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String INVALID_EXPIRED_TOKEN = "invalid_expired_token"; 61b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String INVALID_CONSUMER_KEY = "invalid_consumer_key"; 62b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String CONSUMER_KEY_REFUSED = "consumer_key_refused"; 63b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String TIMESTAMP_REFUSED = "timestamp_refused"; 64b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String PARAMETER_REJECTED = "parameter_rejected"; 65b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String PARAMETER_ABSENT = "parameter_absent"; 66b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String VERSION_REJECTED = "version_rejected"; 67b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String SIGNATURE_METHOD_REJECTED = "signature_method_rejected"; 68b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 69b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_PARAMETERS_ABSENT = "oauth_parameters_absent"; 70b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_PARAMETERS_REJECTED = "oauth_parameters_rejected"; 71b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_ACCEPTABLE_TIMESTAMPS = "oauth_acceptable_timestamps"; 72b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static final String OAUTH_ACCEPTABLE_VERSIONS = "oauth_acceptable_versions"; 73b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 74b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 75b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** Return true if the given Content-Type header means FORM_ENCODED. */ 76b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static boolean isFormEncoded(String contentType) { 77b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (contentType == null) { 78b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return false; 79b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 80b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien int semi = contentType.indexOf(";"); 81b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (semi >= 0) { 82b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien contentType = contentType.substring(0, semi); 83b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 84b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return FORM_ENCODED.equalsIgnoreCase(contentType.trim()); 85b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 86b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 87b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** 88b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Construct a form-urlencoded document containing the given sequence of 89b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * name/value pairs. Use OAuth percent encoding (not exactly the encoding 90b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * mandated by HTTP). 91b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */ 92b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static String formEncode(Iterable<? extends Map.Entry> parameters) 93b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien throws IOException { 94b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien ByteArrayOutputStream b = new ByteArrayOutputStream(); 95b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien formEncode(parameters, b); 96b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return new String(b.toByteArray()); 97b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 98b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 99b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** 100b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Write a form-urlencoded document into the given stream, containing the 101b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * given sequence of name/value pairs. 102b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */ 103b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static void formEncode(Iterable<? extends Map.Entry> parameters, 104b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien OutputStream into) throws IOException { 105b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (parameters != null) { 106b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien boolean first = true; 107b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien for (Map.Entry parameter : parameters) { 108b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (first) { 109b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien first = false; 110b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } else { 111b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien into.write('&'); 112b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 113b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien into.write(percentEncode(toString(parameter.getKey())) 114b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien .getBytes()); 115b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien into.write('='); 116b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien into.write(percentEncode(toString(parameter.getValue())) 117b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien .getBytes()); 118b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 119b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 120b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 121b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 122b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** Parse a form-urlencoded document. */ 123b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static List<Parameter> decodeForm(String form) { 124b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien List<Parameter> list = new ArrayList<Parameter>(); 125b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (!isEmpty(form)) { 126b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien for (String nvp : form.split("\\&")) { 127b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien int equals = nvp.indexOf('='); 128b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien String name; 129b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien String value; 130b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (equals < 0) { 131b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien name = decodePercent(nvp); 132b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien value = null; 133b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } else { 134b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien name = decodePercent(nvp.substring(0, equals)); 135b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien value = decodePercent(nvp.substring(equals + 1)); 136b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 137b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien list.add(new Parameter(name, value)); 138b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 139b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 140b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return list; 141b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 142b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 143b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** Construct a &-separated list of the given values, percentEncoded. */ 144b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static String percentEncode(Iterable values) { 145b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien StringBuilder p = new StringBuilder(); 146b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien for (Object v : values) { 147b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (p.length() > 0) { 148b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien p.append("&"); 149b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 150b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien p.append(OAuth.percentEncode(toString(v))); 151b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 152b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return p.toString(); 153b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 154b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 155b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static String percentEncode(String s) { 156b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (s == null) { 157b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return ""; 158b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 159b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien try { 160b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return URLEncoder.encode(s, ENCODING) 161b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien // OAuth encodes some characters differently: 162b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien .replace("+", "%20").replace("*", "%2A") 163b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien .replace("%7E", "~"); 164b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien // This could be done faster with more hand-crafted code. 165b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } catch (UnsupportedEncodingException wow) { 166b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien throw new RuntimeException(wow.getMessage(), wow); 167b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 168b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 169b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 170b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static String decodePercent(String s) { 171b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien try { 172b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return URLDecoder.decode(s, ENCODING); 173b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien // This implements http://oauth.pbwiki.com/FlexibleDecoding 174b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } catch (java.io.UnsupportedEncodingException wow) { 175b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien throw new RuntimeException(wow.getMessage(), wow); 176b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 177b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 178b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 179b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** 180b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Construct a Map containing a copy of the given parameters. If several 181b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * parameters have the same name, the Map will contain the first value, 182b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * only. 183b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */ 184b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static Map<String, String> newMap(Iterable<? extends Map.Entry> from) { 185b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien Map<String, String> map = new HashMap<String, String>(); 186b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (from != null) { 187b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien for (Map.Entry f : from) { 188b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien String key = toString(f.getKey()); 189b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (!map.containsKey(key)) { 190b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien map.put(key, toString(f.getValue())); 191b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 192b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 193b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 194b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return map; 195b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 196b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 197b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** Construct a list of Parameters from name, value, name, value... */ 198b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static List<Parameter> newList(String... parameters) { 199b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien List<Parameter> list = new ArrayList<Parameter>(parameters.length / 2); 200b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien for (int p = 0; p + 1 < parameters.length; p += 2) { 201b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien list.add(new Parameter(parameters[p], parameters[p + 1])); 202b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 203b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return list; 204b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 205b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 206b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** A name/value pair. */ 207b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static class Parameter implements Map.Entry<String, String> { 208b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 209b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public Parameter(String key, String value) { 210b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien this.key = key; 211b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien this.value = value; 212b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 213b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 214b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien private final String key; 215b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 216b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien private String value; 217b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 218b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public String getKey() { 219b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return key; 220b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 221b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 222b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public String getValue() { 223b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return value; 224b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 225b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 226b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public String setValue(String value) { 227b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien try { 228b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return this.value; 229b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } finally { 230b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien this.value = value; 231b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 232b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 233b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 234b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien @Override 235b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public String toString() { 236b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return percentEncode(getKey()) + '=' + percentEncode(getValue()); 237b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 238b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 239b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien @Override 240b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public int hashCode() 241b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien { 242b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien final int prime = 31; 243b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien int result = 1; 244b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien result = prime * result + ((key == null) ? 0 : key.hashCode()); 245b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien result = prime * result + ((value == null) ? 0 : value.hashCode()); 246b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return result; 247b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 248b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 249b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien @Override 250b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public boolean equals(Object obj) 251b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien { 252b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (this == obj) 253b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return true; 254b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (obj == null) 255b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return false; 256b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (getClass() != obj.getClass()) 257b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return false; 258b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien final Parameter that = (Parameter) obj; 259b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (key == null) { 260b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (that.key != null) 261b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return false; 262b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } else if (!key.equals(that.key)) 263b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return false; 264b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (value == null) { 265b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (that.value != null) 266b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return false; 267b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } else if (!value.equals(that.value)) 268b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return false; 269b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return true; 270b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 271b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 272b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 273b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien private static final String toString(Object from) { 274b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return (from == null) ? null : from.toString(); 275b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 276b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 277b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien /** 278b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Construct a URL like the given one, but with the given parameters added 279b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * to its query string. 280b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */ 281b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static String addParameters(String url, String... parameters) 282b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien throws IOException { 283b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return addParameters(url, newList(parameters)); 284b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 285b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 286b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static String addParameters(String url, 287b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien Iterable<? extends Map.Entry<String, String>> parameters) 288b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien throws IOException { 289b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien String form = formEncode(parameters); 290b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien if (form == null || form.length() <= 0) { 291b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return url; 292b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } else { 293b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return url + ((url.indexOf("?") < 0) ? '?' : '&') + form; 294b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 295b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 296b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien 297b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien public static boolean isEmpty(String str) { 298b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien return (str == null) || (str.length() == 0); 299b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien } 300b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien} 301