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