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.Utils.*; 022 023import java.util.*; 024 025import org.apache.juneau.commons.collections.*; 026import org.apache.juneau.commons.utils.*; 027 028/** 029 * Represents a URL path pattern match. 030 * 031 * For example, given the pattern <js>"/foo/{bar}/*"</js> and the path <js>"/foo/123/baz/qux"</js>, this match gives 032 * you a map containing <js>"{bar:123}"</js> and a remainder string containing <js>"baz/qux"</js>. 033 * 034 */ 035public class UrlPathMatch { 036 037 private final int matchedParts; 038 private final String path; 039 private final Map<String,String> vars; 040 041 /** 042 * Constructor. 043 * 044 * @param path The path being matched against. Can be <jk>null</jk>. 045 * @param matchedParts The number of parts that were matched against the path. 046 * @param keys The variable keys. Can be <jk>null</jk>. 047 * @param values The variable values. Can be <jk>null</jk>. 048 */ 049 protected UrlPathMatch(String path, int matchedParts, String[] keys, String[] values) { 050 this.path = path; 051 this.matchedParts = matchedParts; 052 this.vars = keys == null ? mape() : new SimpleMap<>(keys, values); 053 } 054 055 /** 056 * Returns the part of the URL that the pattern matched against. 057 * 058 * @return 059 * The part of the URL that the pattern matched against. 060 * <br>Can be <jk>null</jk> if nothing matched. 061 * <br>Otherwise, always starts with <js>'/'</js>. 062 */ 063 public String getPrefix() { 064 var c = 0; 065 for (var j = 0; j < matchedParts; j++) { 066 c = path.indexOf('/', c + 1); 067 if (c == -1) 068 c = path.length(); 069 } 070 return Utils.nullIfEmpty(path.substring(0, c)); 071 } 072 073 /** 074 * Returns the remainder of the path after the pattern match has been made. 075 * 076 * <p> 077 * Same as {#link {@link #getSuffix()} but trims the leading slash if there is one. 078 * 079 * @return The remainder of the path after the pattern match has been made. 080 */ 081 public String getRemainder() { 082 var suffix = getSuffix(); 083 if (ne(suffix) && suffix.charAt(0) == '/') 084 suffix = suffix.substring(1); 085 return suffix; 086 } 087 088 /** 089 * Returns the remainder of the URL after the pattern was matched. 090 * 091 * @return 092 * The remainder of the URL after the pattern was matched. 093 * <br>Can be <jk>null</jk> if nothing remains to be matched. 094 * <br>Otherwise, always starts with <js>'/'</js>. 095 */ 096 public String getSuffix() { 097 var s = path; 098 for (var j = 0; j < matchedParts; j++) { 099 var k = s.indexOf('/', 1); 100 if (k == -1) 101 return null; 102 s = s.substring(k); 103 } 104 return s; 105 } 106 107 /** 108 * Returns a map of the path variables and values. 109 * 110 * @return 111 * An unmodifiable map of variable keys/values. 112 * <br>Returns an empty map if no variables were found in the path. 113 */ 114 public Map<String,String> getVars() { return vars; } 115 116 /** 117 * Returns <jk>true</jk> if any of the variable values are blank. 118 * 119 * @return <jk>true</jk> if any of the variable values are blank. 120 */ 121 public boolean hasEmptyVars() { 122 for (var v : vars.values()) 123 if (isEmpty(v)) 124 return true; 125 return false; 126 } 127 128 /** 129 * Returns <jk>true</jk> if this match contains one or more variables. 130 * 131 * @return <jk>true</jk> if this match contains one or more variables. 132 */ 133 public boolean hasVars() { 134 return ! vars.isEmpty(); 135 } 136 137 protected FluentMap<String,Object> properties() { 138 // @formatter:off 139 return filteredBeanPropertyMap() 140 .a("r", getRemainder()) 141 .a("v", getVars()); 142 // @formatter:on 143 } 144 145 @Override /* Overridden from Object */ 146 public String toString() { 147 return r(properties()); 148 } 149}