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.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.StringUtils.*;
021import static org.apache.juneau.commons.utils.ThrowableUtils.*;
022import static org.apache.juneau.commons.utils.Utils.*;
023
024import java.util.*;
025
026import org.apache.juneau.commons.collections.*;
027import org.apache.juneau.commons.utils.*;
028
029/**
030 * Represents a parsed URL path-info string.
031 *
032 */
033public class UrlPath {
034
035   /**
036    * Creates a new parsed {@link UrlPath} object from the specified string.
037    *
038    * @param path The path to create.  Must be <jk>null</jk> or or start with '/' per HttpServletRequest.getPathInfo().
039    * @return A new {@link UrlPath} object.
040    */
041   public static UrlPath of(String path) {
042      if (nn(path) && ! path.startsWith("/"))
043         throw illegalArg("Invalid path specified. Must be null or start with '/' per HttpServletRequest.getPathInfo().");
044      return new UrlPath(path);
045   }
046
047   final String[] parts;
048
049   final String path;
050
051   /**
052    * Constructor.
053    *
054    * @param path The path.
055    */
056   UrlPath(String path) {
057      this.path = path;
058      parts = path == null ? new String[0] : StringUtils.splita(path.substring(1), '/');
059      for (var i = 0; i < parts.length; i++)
060         parts[i] = urlDecode(parts[i]);
061   }
062
063   /**
064    * Returns the filename portion of the path if there is one.
065    *
066    * <p>
067    * For example, given the path <js>"/foo/bar.txt"</js>, this returns <js>"bar.txt"</js>.
068    *
069    * @return The filename portion of the path, or <jk>null</jk> if the path doesn't match a file name.
070    */
071   public Optional<String> getFileName() {
072      if (parts.length == 0)
073         return opte();
074      String p = parts[parts.length - 1];
075      if (p.indexOf('.') == -1)
076         return opte();
077      return opt(p);
078   }
079
080   /**
081    * Returns the path parts.
082    *
083    * @return The path parts.
084    */
085   public String[] getParts() { return parts; }
086
087   /**
088    * Returns the raw path passed into this object.
089    *
090    * @return The raw path passed into this object.
091    */
092   public String getPath() { return path; }
093
094   /**
095    * Returns <jk>true</jk> if this path ends with a slash.
096    *
097    * @return <jk>true</jk> if this path ends with a slash.
098    */
099   public boolean isTrailingSlash() { return nn(path) && path.endsWith("/"); }
100
101   protected FluentMap<String,Object> properties() {
102      // @formatter:off
103      return filteredBeanPropertyMap()
104         .a("parts", parts)
105         .a("raw", path);
106      // @formatter:on
107   }
108
109   @Override /* Overridden from Object */
110   public String toString() {
111      return r(properties());
112   }
113}