001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau;
014
015import static org.apache.juneau.internal.StringUtils.*;
016
017import org.apache.juneau.annotation.*;
018import org.apache.juneau.json.*;
019import org.apache.juneau.parser.*;
020
021/**
022 * Represents a URL broken into authority/context-root/servlet-path/path-info parts.
023 *
024 * <p>
025 * A typical request against a URL takes the following form:
026 * <p class='bcode w800'>
027 *    http://host:port/context-root/servlet-path/path-info
028 *    |   authority   |  context   |  resource  |  path  |
029 *    +--------------------------------------------------+
030 * </p>
031 *
032 * <p>
033 * This class allows you to convert URL strings to absolute (e.g. <js>"http://host:port/foo/bar"</js>) or root-relative
034 * (e.g. <js>"/foo/bar"</js>) URLs.
035 */
036@Bean
037public class UriContext {
038
039   /**
040    * Default URI context.
041    *
042    * <p>
043    * No information about authority, servlet-root, context-root, or path-info is known.
044    */
045   public static final UriContext DEFAULT = new UriContext();
046
047   @SuppressWarnings("javadoc")
048   public final String authority, contextRoot, servletPath, pathInfo, parentPath;
049
050   // Lazy-initialized fields.
051   private String aContextRoot, rContextRoot, aServletPath, rResource, aPathInfo, rPath;
052
053   /**
054    * Constructor.
055    *
056    * <p>
057    * Leading and trailing slashes are trimmed of all parameters.
058    *
059    * <p>
060    * Any parameter can be <jk>null</jk>.  Blanks and nulls are equivalent.
061    *
062    * @param authority
063    *    The authority portion of URL (e.g. <js>"http://hostname:port"</js>)
064    * @param contextRoot
065    *    The context root of the application (e.g. <js>"/context-root"</js>, or <js>"context-root"</js>)
066    * @param servletPath
067    *    The servlet path (e.g. <js>"/servlet-path"</js>, or <js>"servlet-path"</js>)
068    * @param pathInfo
069    *    The path info (e.g. <js>"/path-info"</js>, or <js>"path-info"</js>)
070    */
071   @BeanConstructor(properties="authority,contextRoot,servletPath,pathInfo")
072   public UriContext(String authority, String contextRoot, String servletPath, String pathInfo) {
073      this.authority = nullIfEmpty(trimSlashes(authority));
074      this.contextRoot = nullIfEmpty(trimSlashes(contextRoot));
075      this.servletPath = nullIfEmpty(trimSlashes(servletPath));
076      this.pathInfo = nullIfEmpty(trimSlashes(pathInfo));
077      this.parentPath = this.pathInfo == null || this.pathInfo.indexOf('/') == -1 ? null
078         : this.pathInfo.substring(0, this.pathInfo.lastIndexOf('/'));
079   }
080
081   /**
082    * Default constructor.
083    *
084    * <p>
085    * All <jk>null</jk> values.
086    */
087   public UriContext() {
088      this(null, null, null, null);
089   }
090
091   /**
092    * String constructor.
093    *
094    * <p>
095    * Input string is a JSON object with the following format:
096    * <js>{authority:'xxx',contextRoot:'xxx',servletPath:'xxx',pathInfo:'xxx'}</js>
097    *
098    * @param s
099    *    The input string.
100    *    <br>Example: <js>{authority:'http://localhost:10000',contextRoot:'/myContext',servletPath:'/myServlet',pathInfo:'/foo'}</js>
101    * @throws ParseException
102    *    If input string is not a valid JSON object.
103    */
104   public UriContext(String s) throws ParseException {
105      ObjectMap m = new ObjectMap(s);
106      this.authority = nullIfEmpty(trimSlashes(m.getString("authority")));
107      this.contextRoot = nullIfEmpty(trimSlashes(m.getString("contextRoot")));
108      this.servletPath = nullIfEmpty(trimSlashes(m.getString("servletPath")));
109      this.pathInfo = nullIfEmpty(trimSlashes(m.getString("pathInfo")));
110      this.parentPath = this.pathInfo == null || this.pathInfo.indexOf('/') == -1 ? null
111         : this.pathInfo.substring(0, this.pathInfo.lastIndexOf('/'));
112   }
113
114   /**
115    * Returns the absolute URI of just the authority portion of this URI context.
116    *
117    * <p>
118    * Example:  <js>"http://hostname:port"</js>
119    *
120    * <p>
121    * If the authority is null/empty, returns <js>"/"</js>.
122    *
123    * @return
124    *    The absolute URI of just the authority portion of this URI context.
125    *    Never <jk>null</jk>.
126    */
127   public String getAbsoluteAuthority() {
128      return authority == null ? "/" : authority;
129   }
130
131   /**
132    * Returns the absolute URI of the context-root portion of this URI context.
133    *
134    * <p>
135    * Example:  <js>"http://hostname:port/context-root"</js>
136    *
137    * @return
138    *    The absolute URI of the context-root portion of this URI context.
139    *    Never <jk>null</jk>.
140    */
141   public String getAbsoluteContextRoot() {
142      if (aContextRoot == null) {
143         if (authority == null)
144            aContextRoot = getRootRelativeContextRoot();
145         else
146            aContextRoot = (
147               contextRoot == null
148               ? authority
149               : (authority + '/' + contextRoot)
150            );
151      }
152      return aContextRoot;
153   }
154
155   /**
156    * Returns the root-relative URI of the context portion of this URI context.
157    *
158    * <p>
159    * Example:  <js>"/context-root"</js>
160    *
161    * @return
162    *    The root-relative URI of the context portion of this URI context.
163    *    Never <jk>null</jk>.
164    */
165   public String getRootRelativeContextRoot() {
166      if (rContextRoot == null)
167         rContextRoot = contextRoot == null ? "/" : ('/' + contextRoot);
168      return rContextRoot;
169   }
170
171   /**
172    * Returns the absolute URI of the resource portion of this URI context.
173    *
174    * <p>
175    * Example:  <js>"http://hostname:port/context-root/servlet-path"</js>
176    *
177    * @return
178    *    The absolute URI of the resource portion of this URI context.
179    *    Never <jk>null</jk>.
180    */
181   public String getAbsoluteServletPath() {
182      if (aServletPath == null) {
183         if (authority == null)
184            aServletPath = getRootRelativeServletPath();
185         else {
186            if (contextRoot == null)
187               aServletPath = (
188                  servletPath == null
189                  ? authority
190                  : authority + '/' + servletPath
191               );
192            else
193               aServletPath = (
194                  servletPath == null
195                  ? (authority + '/' + contextRoot)
196                  : (authority + '/' + contextRoot + '/' + servletPath)
197               );
198         }
199      }
200      return aServletPath;
201   }
202
203   /**
204    * Returns the root-relative URI of the resource portion of this URI context.
205    *
206    * <p>
207    * Example:  <js>"/context-root/servlet-path"</js>
208    *
209    * @return
210    *    The root-relative URI of the resource portion of this URI context.
211    *    Never <jk>null</jk>.
212    */
213   public String getRootRelativeServletPath() {
214      if (rResource == null) {
215         if (contextRoot == null)
216            rResource = (
217               servletPath == null
218               ? "/"
219               : ('/' + servletPath)
220            );
221         else
222            rResource = (
223               servletPath == null
224               ? ('/' + contextRoot)
225               : ('/' + contextRoot + '/' + servletPath)
226            );
227      }
228      return rResource;
229   }
230
231   /**
232    * Returns the parent of the URL returned by {@link #getAbsoluteServletPath()}.
233    *
234    * @return The parent of the URL returned by {@link #getAbsoluteServletPath()}.
235    */
236   public String getAbsoluteServletPathParent() {
237      return getParent(getAbsoluteServletPath());
238   }
239
240   /**
241    * Returns the parent of the URL returned by {@link #getRootRelativeServletPath()}.
242    *
243    * @return The parent of the URL returned by {@link #getRootRelativeServletPath()}.
244    */
245   public String getRootRelativeServletPathParent() {
246      return getParent(getRootRelativeServletPath());
247   }
248
249   /**
250    * Returns the absolute URI of the path portion of this URI context.
251    *
252    * <p>
253    * Example:  <js>"http://hostname:port/context-root/servlet-path/path-info"</js>
254    *
255    * @return
256    *    The absolute URI of the path portion of this URI context.
257    *    Never <jk>null</jk>.
258    */
259   public String getAbsolutePathInfo() {
260      if (aPathInfo == null) {
261         if (authority == null)
262            aPathInfo = getRootRelativePathInfo();
263         else {
264            if (contextRoot == null) {
265               if (servletPath == null)
266                  aPathInfo = (
267                     pathInfo == null
268                     ? authority : (authority + '/' + pathInfo)
269                  );
270               else
271                  aPathInfo = (
272                     pathInfo == null
273                     ? (authority + '/' + servletPath)
274                     : (authority + '/' + servletPath + '/' + pathInfo)
275                  );
276            } else {
277               if (servletPath == null)
278                  aPathInfo = (
279                     pathInfo == null
280                     ? authority + '/' + contextRoot
281                     : (authority + '/' + contextRoot + '/' + pathInfo)
282                  );
283               else
284                  aPathInfo = (
285                     pathInfo == null
286                     ? (authority + '/' + contextRoot + '/' + servletPath)
287                     : (authority + '/' + contextRoot + '/' + servletPath + '/' + pathInfo)
288                  );
289            }
290         }
291      }
292      return aPathInfo;
293   }
294
295   /**
296    * Returns the root-relative URI of the path portion of this URI context.
297    *
298    * <p>
299    * Example:  <js>"/context-root/servlet-path/path-info"</js>
300    *
301    * @return
302    *    The root-relative URI of the path portion of this URI context.
303    *    Never <jk>null</jk>.
304    */
305   public String getRootRelativePathInfo() {
306      if (rPath == null) {
307         if (contextRoot == null) {
308            if (servletPath == null)
309               rPath = (
310                  pathInfo == null
311                  ? "/"
312                  : ('/' + pathInfo)
313               );
314            else
315               rPath = (
316                  pathInfo == null
317                  ? ('/' + servletPath)
318                  : ('/' + servletPath + '/' + pathInfo)
319               );
320         } else {
321            if (servletPath == null)
322               rPath = (
323                  pathInfo == null
324                  ? ('/' + contextRoot)
325                  : ('/' + contextRoot + '/' + pathInfo)
326               );
327            else
328               rPath = (
329                  pathInfo == null
330                  ? ('/' + contextRoot + '/' + servletPath)
331                  : ('/' + contextRoot + '/' + servletPath + '/' + pathInfo)
332               );
333         }
334      }
335      return rPath;
336   }
337
338   /**
339    * Returns the parent of the URL returned by {@link #getAbsolutePathInfo()}.
340    *
341    * @return The parent of the URL returned by {@link #getAbsolutePathInfo()}.
342    */
343   public String getAbsolutePathInfoParent() {
344      return getParent(getAbsolutePathInfo());
345   }
346
347   /**
348    * Returns the parent of the URL returned by {@link #getRootRelativePathInfo()}.
349    *
350    * @return The parent of the URL returned by {@link #getRootRelativePathInfo()}.
351    */
352   public String getRootRelativePathInfoParent() {
353      return getParent(getRootRelativePathInfo());
354   }
355
356   private static String getParent(String uri) {
357      int i = uri.lastIndexOf('/');
358      if (i <= 1)
359         return "/";
360      return uri.substring(0, i);
361   }
362
363   @Override /* Object */
364   public String toString() {
365      return SimpleJsonSerializer.DEFAULT.toString(this);
366   }
367}