This project has retired. For details please refer to its Attic page.
UrlBuilder xref

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF 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.apache.chemistry.opencmis.commons.impl;
20  
21  import java.io.UnsupportedEncodingException;
22  import java.net.URI;
23  import java.net.URLEncoder;
24  
25  import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
26  import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
27  import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection;
28  import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
29  import org.apache.chemistry.opencmis.commons.enums.VersioningState;
30  
31  /**
32   * Utility class that helps building URLs.
33   */
34  public class UrlBuilder {
35  
36      private final StringBuilder urlPart;
37      private final StringBuilder queryPart;
38  
39      /**
40       * Constructor.
41       * 
42       * @param url
43       *            initial URL
44       */
45      public UrlBuilder(String url) {
46          if (url == null) {
47              throw new IllegalArgumentException("URL must be set");
48          }
49  
50          urlPart = new StringBuilder();
51          queryPart = new StringBuilder();
52  
53          int qm = url.indexOf('?');
54          if (qm == -1) {
55              urlPart.append(url);
56          } else {
57              urlPart.append(url.substring(0, qm));
58              if (qm < url.length()) {
59                  queryPart.append(url.substring(qm + 1));
60              }
61          }
62      }
63  
64      /**
65       * Constructor.
66       * 
67       * @param scheme
68       *            scheme
69       * @param host
70       *            host
71       * @param port
72       *            port
73       * @param path
74       *            path
75       */
76      public UrlBuilder(String scheme, String host, int port, String path) {
77  
78          if ("http".equalsIgnoreCase(scheme) && (port == 80)) {
79              port = -1;
80          }
81          if ("https".equalsIgnoreCase(scheme) && (port == 443)) {
82              port = -1;
83          }
84  
85          urlPart = new StringBuilder();
86          queryPart = new StringBuilder();
87  
88          urlPart.append(scheme);
89          urlPart.append("://");
90          urlPart.append(host);
91          if (port > 0) {
92              urlPart.append(":").append(port);
93          }
94          if (path != null) {
95              urlPart.append(path);
96          }
97      }
98  
99      /**
100      * Copy constructor.
101      */
102     public UrlBuilder(UrlBuilder urlBuilder) {
103         urlPart = new StringBuilder(urlBuilder.urlPart);
104         queryPart = new StringBuilder(urlBuilder.queryPart);
105     }
106 
107     /**
108      * Adds a parameter to the URL.
109      * 
110      * @param name
111      *            parameter name
112      * @param value
113      *            parameter value
114      */
115     public UrlBuilder addParameter(String name, Object value) {
116         if ((name == null) || (value == null)) {
117             return this;
118         }
119 
120         String valueStr = normalizeParameter(value);
121 
122         if (queryPart.length() > 0) {
123             queryPart.append('&');
124         }
125         queryPart.append(name);
126         queryPart.append('=');
127         try {
128             queryPart.append(URLEncoder.encode(valueStr, "UTF-8"));
129         } catch (UnsupportedEncodingException e) {
130         }
131 
132         return this;
133     }
134 
135     /**
136      * Adds a path segment to the URL.
137      * 
138      * @param pathSegment
139      *            the path segment.
140      */
141     public UrlBuilder addPathSegment(String pathSegment) {
142         return addPathPart(pathSegment, true);
143     }
144 
145     /**
146      * Adds a path to the URL.
147      * 
148      * @param path
149      *            the path
150      */
151     public UrlBuilder addPath(String path) {
152         return addPathPart(path, false);
153     }
154 
155     protected UrlBuilder addPathPart(String part, boolean quoteSlash) {
156         if (part == null || part.length() == 0) {
157             return this;
158         }
159         if (urlPart.charAt(urlPart.length() - 1) != '/') {
160             urlPart.append('/');
161         }
162         if (part.charAt(0) == '/') {
163             part = part.substring(1);
164         }
165         urlPart.append(quoteURIPathComponent(part, quoteSlash));
166 
167         return this;
168     }
169 
170     public static char[] RFC7232_RESERVED = ";?:@&=+$,[]".toCharArray();
171 
172     public static String quoteURIPathComponent(String s, boolean quoteSlash) {
173         if (s.length() == 0) {
174             return s;
175         }
176         // reuse the URI class which knows a lot about escaping
177         URI uri;
178         try {
179             // fake scheme so that a colon is not mistaken as a scheme
180             uri = new URI("x", s, null);
181         } catch (Exception e) {
182             throw new IllegalArgumentException("Illegal characters in: " + s, e);
183         }
184         String r = uri.toASCIIString().substring(2); // remove x:
185         // quote some additional reserved characters to be safe
186         for (char c : RFC7232_RESERVED) {
187             if (r.indexOf(c) >= 0) {
188                 r = r.replace("" + c, "%" + Integer.toHexString(c));
189             }
190         }
191         if (quoteSlash && r.indexOf('/') >= 0) {
192             r = r.replace("/", "%2F");
193         }
194         return r;
195     }
196 
197     /**
198      * Converts an object to a String that can be used as a parameter value.
199      */
200     public static String normalizeParameter(Object value) {
201         if (value == null) {
202             return null;
203         } else if (value instanceof IncludeRelationships) {
204             return ((IncludeRelationships) value).value();
205         } else if (value instanceof VersioningState) {
206             return ((VersioningState) value).value();
207         } else if (value instanceof UnfileObject) {
208             return ((UnfileObject) value).value();
209         } else if (value instanceof RelationshipDirection) {
210             return ((RelationshipDirection) value).value();
211         } else if (value instanceof ReturnVersion) {
212             return ((ReturnVersion) value).value();
213         } else if (value instanceof AclPropagation) {
214             return ((AclPropagation) value).value();
215         }
216 
217         return value.toString();
218     }
219 
220     @Override
221     public String toString() {
222         return urlPart.toString() + (queryPart.length() == 0 ? "" : "?" + queryPart.toString());
223     }
224 }