001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.util;
018
019import static org.apache.juneau.collections.JsonMap.*;
020import static org.apache.juneau.common.utils.StringUtils.*;
021
022import java.util.*;
023
024import org.apache.juneau.common.utils.*;
025
026/**
027 * Represents a parsed URL path-info string.
028 *
029 * <h5 class='section'>See Also:</h5><ul>
030 * </ul>
031 */
032public class UrlPath {
033
034   final String[] parts;
035   final String path;
036
037   /**
038    * Creates a new parsed {@link UrlPath} object from the specified string.
039    *
040    * @param path The path to create.  Must be <jk>null</jk> or or start with '/' per HttpServletRequest.getPathInfo().
041    * @return A new {@link UrlPath} object.
042    */
043   public static UrlPath of(String path) {
044      if (path != null && ! path.startsWith("/"))
045         throw new IllegalArgumentException("Invalid path specified. Must be null or start with '/' per HttpServletRequest.getPathInfo().");
046      return new UrlPath(path);
047   }
048
049   /**
050    * Constructor.
051    *
052    * @param path The path.
053    */
054   UrlPath(String path) {
055      this.path = path;
056      parts = path == null ? new String[0] : Utils.splita(path.substring(1), '/');
057      for (int i = 0; i < parts.length; i++)
058         parts[i] = urlDecode(parts[i]);
059   }
060
061   /**
062    * Returns the path parts.
063    *
064    * @return The path parts.
065    */
066   public String[] getParts() {
067      return parts;
068   }
069
070
071   /**
072    * Returns the filename portion of the path if there is one.
073    *
074    * <p>
075    * For example, given the path <js>"/foo/bar.txt"</js>, this returns <js>"bar.txt"</js>.
076    *
077    * @return The filename portion of the path, or <jk>null</jk> if the path doesn't match a file name.
078    */
079   public Optional<String> getFileName() {
080      if (parts.length == 0)
081         return Utils.opte();
082      String p = parts[parts.length-1];
083      if (p.indexOf('.') == -1)
084         return Utils.opte();
085      return Utils.opt(p);
086   }
087
088   /**
089    * Returns the raw path passed into this object.
090    *
091    * @return The raw path passed into this object.
092    */
093   public String getPath() {
094      return path;
095   }
096
097   /**
098    * Returns <jk>true</jk> if this path ends with a slash.
099    *
100    * @return <jk>true</jk> if this path ends with a slash.
101    */
102   public boolean isTrailingSlash() {
103      return path != null && path.endsWith("/");
104   }
105
106   @Override /* Object */
107   public String toString() {
108      return filteredMap()
109         .append("raw", path)
110         .append("parts", parts)
111         .asReadableString();
112   }
113}