PageRenderTime 26ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/external/httpclientandroidlib/httpclientandroidlib/src/ch/boye/httpclientandroidlib/client/utils/URIBuilder.java

http://github.com/mozilla-services/android-sync
Java | 490 lines | 303 code | 43 blank | 144 comment | 66 complexity | ed0ff5fc9821a179cff2e1f7f837c6eb MD5 | raw file
Possible License(s): MPL-2.0
  1. /*
  2. * ====================================================================
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. * ====================================================================
  20. *
  21. * This software consists of voluntary contributions made by many
  22. * individuals on behalf of the Apache Software Foundation. For more
  23. * information on the Apache Software Foundation, please see
  24. * <http://www.apache.org/>.
  25. *
  26. */
  27. package ch.boye.httpclientandroidlib.client.utils;
  28. import java.net.URI;
  29. import java.net.URISyntaxException;
  30. import java.nio.charset.Charset;
  31. import java.util.ArrayList;
  32. import java.util.Iterator;
  33. import java.util.List;
  34. import ch.boye.httpclientandroidlib.Consts;
  35. import ch.boye.httpclientandroidlib.NameValuePair;
  36. import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
  37. import ch.boye.httpclientandroidlib.conn.util.InetAddressUtils;
  38. import ch.boye.httpclientandroidlib.message.BasicNameValuePair;
  39. /**
  40. * Builder for {@link URI} instances.
  41. *
  42. * @since 4.2
  43. */
  44. @NotThreadSafe
  45. public class URIBuilder {
  46. private String scheme;
  47. private String encodedSchemeSpecificPart;
  48. private String encodedAuthority;
  49. private String userInfo;
  50. private String encodedUserInfo;
  51. private String host;
  52. private int port;
  53. private String path;
  54. private String encodedPath;
  55. private String encodedQuery;
  56. private List<NameValuePair> queryParams;
  57. private String query;
  58. private String fragment;
  59. private String encodedFragment;
  60. /**
  61. * Constructs an empty instance.
  62. */
  63. public URIBuilder() {
  64. super();
  65. this.port = -1;
  66. }
  67. /**
  68. * Construct an instance from the string which must be a valid URI.
  69. *
  70. * @param string a valid URI in string form
  71. * @throws URISyntaxException if the input is not a valid URI
  72. */
  73. public URIBuilder(final String string) throws URISyntaxException {
  74. super();
  75. digestURI(new URI(string));
  76. }
  77. /**
  78. * Construct an instance from the provided URI.
  79. * @param uri
  80. */
  81. public URIBuilder(final URI uri) {
  82. super();
  83. digestURI(uri);
  84. }
  85. private List <NameValuePair> parseQuery(final String query, final Charset charset) {
  86. if (query != null && query.length() > 0) {
  87. return URLEncodedUtils.parse(query, charset);
  88. }
  89. return null;
  90. }
  91. /**
  92. * Builds a {@link URI} instance.
  93. */
  94. public URI build() throws URISyntaxException {
  95. return new URI(buildString());
  96. }
  97. private String buildString() {
  98. final StringBuilder sb = new StringBuilder();
  99. if (this.scheme != null) {
  100. sb.append(this.scheme).append(':');
  101. }
  102. if (this.encodedSchemeSpecificPart != null) {
  103. sb.append(this.encodedSchemeSpecificPart);
  104. } else {
  105. if (this.encodedAuthority != null) {
  106. sb.append("//").append(this.encodedAuthority);
  107. } else if (this.host != null) {
  108. sb.append("//");
  109. if (this.encodedUserInfo != null) {
  110. sb.append(this.encodedUserInfo).append("@");
  111. } else if (this.userInfo != null) {
  112. sb.append(encodeUserInfo(this.userInfo)).append("@");
  113. }
  114. if (InetAddressUtils.isIPv6Address(this.host)) {
  115. sb.append("[").append(this.host).append("]");
  116. } else {
  117. sb.append(this.host);
  118. }
  119. if (this.port >= 0) {
  120. sb.append(":").append(this.port);
  121. }
  122. }
  123. if (this.encodedPath != null) {
  124. sb.append(normalizePath(this.encodedPath));
  125. } else if (this.path != null) {
  126. sb.append(encodePath(normalizePath(this.path)));
  127. }
  128. if (this.encodedQuery != null) {
  129. sb.append("?").append(this.encodedQuery);
  130. } else if (this.queryParams != null) {
  131. sb.append("?").append(encodeUrlForm(this.queryParams));
  132. } else if (this.query != null) {
  133. sb.append("?").append(encodeUric(this.query));
  134. }
  135. }
  136. if (this.encodedFragment != null) {
  137. sb.append("#").append(this.encodedFragment);
  138. } else if (this.fragment != null) {
  139. sb.append("#").append(encodeUric(this.fragment));
  140. }
  141. return sb.toString();
  142. }
  143. private void digestURI(final URI uri) {
  144. this.scheme = uri.getScheme();
  145. this.encodedSchemeSpecificPart = uri.getRawSchemeSpecificPart();
  146. this.encodedAuthority = uri.getRawAuthority();
  147. this.host = uri.getHost();
  148. this.port = uri.getPort();
  149. this.encodedUserInfo = uri.getRawUserInfo();
  150. this.userInfo = uri.getUserInfo();
  151. this.encodedPath = uri.getRawPath();
  152. this.path = uri.getPath();
  153. this.encodedQuery = uri.getRawQuery();
  154. this.queryParams = parseQuery(uri.getRawQuery(), Consts.UTF_8);
  155. this.encodedFragment = uri.getRawFragment();
  156. this.fragment = uri.getFragment();
  157. }
  158. private String encodeUserInfo(final String userInfo) {
  159. return URLEncodedUtils.encUserInfo(userInfo, Consts.UTF_8);
  160. }
  161. private String encodePath(final String path) {
  162. return URLEncodedUtils.encPath(path, Consts.UTF_8);
  163. }
  164. private String encodeUrlForm(final List<NameValuePair> params) {
  165. return URLEncodedUtils.format(params, Consts.UTF_8);
  166. }
  167. private String encodeUric(final String fragment) {
  168. return URLEncodedUtils.encUric(fragment, Consts.UTF_8);
  169. }
  170. /**
  171. * Sets URI scheme.
  172. */
  173. public URIBuilder setScheme(final String scheme) {
  174. this.scheme = scheme;
  175. return this;
  176. }
  177. /**
  178. * Sets URI user info. The value is expected to be unescaped and may contain non ASCII
  179. * characters.
  180. */
  181. public URIBuilder setUserInfo(final String userInfo) {
  182. this.userInfo = userInfo;
  183. this.encodedSchemeSpecificPart = null;
  184. this.encodedAuthority = null;
  185. this.encodedUserInfo = null;
  186. return this;
  187. }
  188. /**
  189. * Sets URI user info as a combination of username and password. These values are expected to
  190. * be unescaped and may contain non ASCII characters.
  191. */
  192. public URIBuilder setUserInfo(final String username, final String password) {
  193. return setUserInfo(username + ':' + password);
  194. }
  195. /**
  196. * Sets URI host.
  197. */
  198. public URIBuilder setHost(final String host) {
  199. this.host = host;
  200. this.encodedSchemeSpecificPart = null;
  201. this.encodedAuthority = null;
  202. return this;
  203. }
  204. /**
  205. * Sets URI port.
  206. */
  207. public URIBuilder setPort(final int port) {
  208. this.port = port < 0 ? -1 : port;
  209. this.encodedSchemeSpecificPart = null;
  210. this.encodedAuthority = null;
  211. return this;
  212. }
  213. /**
  214. * Sets URI path. The value is expected to be unescaped and may contain non ASCII characters.
  215. */
  216. public URIBuilder setPath(final String path) {
  217. this.path = path;
  218. this.encodedSchemeSpecificPart = null;
  219. this.encodedPath = null;
  220. return this;
  221. }
  222. /**
  223. * Removes URI query.
  224. */
  225. public URIBuilder removeQuery() {
  226. this.queryParams = null;
  227. this.query = null;
  228. this.encodedQuery = null;
  229. this.encodedSchemeSpecificPart = null;
  230. return this;
  231. }
  232. /**
  233. * Sets URI query.
  234. * <p>
  235. * The value is expected to be encoded form data.
  236. *
  237. * @deprecated (4.3) use {@link #setParameters(List)} or {@link #setParameters(NameValuePair...)}
  238. *
  239. * @see URLEncodedUtils#parse
  240. */
  241. @Deprecated
  242. public URIBuilder setQuery(final String query) {
  243. this.queryParams = parseQuery(query, Consts.UTF_8);
  244. this.query = null;
  245. this.encodedQuery = null;
  246. this.encodedSchemeSpecificPart = null;
  247. return this;
  248. }
  249. /**
  250. * Sets URI query parameters. The parameter name / values are expected to be unescaped
  251. * and may contain non ASCII characters.
  252. * <p/>
  253. * Please note query parameters and custom query component are mutually exclusive. This method
  254. * will remove custom query if present.
  255. *
  256. * @since 4.3
  257. */
  258. public URIBuilder setParameters(final List <NameValuePair> nvps) {
  259. if (this.queryParams == null) {
  260. this.queryParams = new ArrayList<NameValuePair>();
  261. } else {
  262. this.queryParams.clear();
  263. }
  264. this.queryParams.addAll(nvps);
  265. this.encodedQuery = null;
  266. this.encodedSchemeSpecificPart = null;
  267. this.query = null;
  268. return this;
  269. }
  270. /**
  271. * Adds URI query parameters. The parameter name / values are expected to be unescaped
  272. * and may contain non ASCII characters.
  273. * <p/>
  274. * Please note query parameters and custom query component are mutually exclusive. This method
  275. * will remove custom query if present.
  276. *
  277. * @since 4.3
  278. */
  279. public URIBuilder addParameters(final List <NameValuePair> nvps) {
  280. if (this.queryParams == null) {
  281. this.queryParams = new ArrayList<NameValuePair>();
  282. }
  283. this.queryParams.addAll(nvps);
  284. this.encodedQuery = null;
  285. this.encodedSchemeSpecificPart = null;
  286. this.query = null;
  287. return this;
  288. }
  289. /**
  290. * Sets URI query parameters. The parameter name / values are expected to be unescaped
  291. * and may contain non ASCII characters.
  292. * <p/>
  293. * Please note query parameters and custom query component are mutually exclusive. This method
  294. * will remove custom query if present.
  295. *
  296. * @since 4.3
  297. */
  298. public URIBuilder setParameters(final NameValuePair... nvps) {
  299. if (this.queryParams == null) {
  300. this.queryParams = new ArrayList<NameValuePair>();
  301. } else {
  302. this.queryParams.clear();
  303. }
  304. for (final NameValuePair nvp: nvps) {
  305. this.queryParams.add(nvp);
  306. }
  307. this.encodedQuery = null;
  308. this.encodedSchemeSpecificPart = null;
  309. this.query = null;
  310. return this;
  311. }
  312. /**
  313. * Adds parameter to URI query. The parameter name and value are expected to be unescaped
  314. * and may contain non ASCII characters.
  315. * <p/>
  316. * Please note query parameters and custom query component are mutually exclusive. This method
  317. * will remove custom query if present.
  318. */
  319. public URIBuilder addParameter(final String param, final String value) {
  320. if (this.queryParams == null) {
  321. this.queryParams = new ArrayList<NameValuePair>();
  322. }
  323. this.queryParams.add(new BasicNameValuePair(param, value));
  324. this.encodedQuery = null;
  325. this.encodedSchemeSpecificPart = null;
  326. this.query = null;
  327. return this;
  328. }
  329. /**
  330. * Sets parameter of URI query overriding existing value if set. The parameter name and value
  331. * are expected to be unescaped and may contain non ASCII characters.
  332. * <p/>
  333. * Please note query parameters and custom query component are mutually exclusive. This method
  334. * will remove custom query if present.
  335. */
  336. public URIBuilder setParameter(final String param, final String value) {
  337. if (this.queryParams == null) {
  338. this.queryParams = new ArrayList<NameValuePair>();
  339. }
  340. if (!this.queryParams.isEmpty()) {
  341. for (final Iterator<NameValuePair> it = this.queryParams.iterator(); it.hasNext(); ) {
  342. final NameValuePair nvp = it.next();
  343. if (nvp.getName().equals(param)) {
  344. it.remove();
  345. }
  346. }
  347. }
  348. this.queryParams.add(new BasicNameValuePair(param, value));
  349. this.encodedQuery = null;
  350. this.encodedSchemeSpecificPart = null;
  351. this.query = null;
  352. return this;
  353. }
  354. /**
  355. * Clears URI query parameters.
  356. *
  357. * @since 4.3
  358. */
  359. public URIBuilder clearParameters() {
  360. this.queryParams = null;
  361. this.encodedQuery = null;
  362. this.encodedSchemeSpecificPart = null;
  363. return this;
  364. }
  365. /**
  366. * Sets custom URI query. The value is expected to be unescaped and may contain non ASCII
  367. * characters.
  368. * <p/>
  369. * Please note query parameters and custom query component are mutually exclusive. This method
  370. * will remove query parameters if present.
  371. *
  372. * @since 4.3
  373. */
  374. public URIBuilder setCustomQuery(final String query) {
  375. this.query = query;
  376. this.encodedQuery = null;
  377. this.encodedSchemeSpecificPart = null;
  378. this.queryParams = null;
  379. return this;
  380. }
  381. /**
  382. * Sets URI fragment. The value is expected to be unescaped and may contain non ASCII
  383. * characters.
  384. */
  385. public URIBuilder setFragment(final String fragment) {
  386. this.fragment = fragment;
  387. this.encodedFragment = null;
  388. return this;
  389. }
  390. /**
  391. * @since 4.3
  392. */
  393. public boolean isAbsolute() {
  394. return this.scheme != null;
  395. }
  396. /**
  397. * @since 4.3
  398. */
  399. public boolean isOpaque() {
  400. return this.path == null;
  401. }
  402. public String getScheme() {
  403. return this.scheme;
  404. }
  405. public String getUserInfo() {
  406. return this.userInfo;
  407. }
  408. public String getHost() {
  409. return this.host;
  410. }
  411. public int getPort() {
  412. return this.port;
  413. }
  414. public String getPath() {
  415. return this.path;
  416. }
  417. public List<NameValuePair> getQueryParams() {
  418. if (this.queryParams != null) {
  419. return new ArrayList<NameValuePair>(this.queryParams);
  420. } else {
  421. return new ArrayList<NameValuePair>();
  422. }
  423. }
  424. public String getFragment() {
  425. return this.fragment;
  426. }
  427. @Override
  428. public String toString() {
  429. return buildString();
  430. }
  431. private static String normalizePath(final String path) {
  432. String s = path;
  433. if (s == null) {
  434. return null;
  435. }
  436. int n = 0;
  437. for (; n < s.length(); n++) {
  438. if (s.charAt(n) != '/') {
  439. break;
  440. }
  441. }
  442. if (n > 1) {
  443. s = s.substring(n - 1);
  444. }
  445. return s;
  446. }
  447. }