001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.protocol; 029 030import java.util.HashMap; 031import java.util.Map; 032 033import org.apache.http.annotation.ThreadingBehavior; 034import org.apache.http.annotation.Contract; 035import org.apache.http.util.Args; 036 037/** 038 * Maintains a map of objects keyed by a request URI pattern. 039 * <br> 040 * Patterns may have three formats: 041 * <ul> 042 * <li>{@code *}</li> 043 * <li>{@code *<uri>}</li> 044 * <li>{@code <uri>*}</li> 045 * </ul> 046 * <br> 047 * This class can be used to resolve an object matching a particular request 048 * URI. 049 * 050 * @since 4.0 051 */ 052@Contract(threading = ThreadingBehavior.SAFE) 053public class UriPatternMatcher<T> { 054 055 private final Map<String, T> map; 056 057 public UriPatternMatcher() { 058 super(); 059 this.map = new HashMap<String, T>(); 060 } 061 062 /** 063 * Registers the given object for URIs matching the given pattern. 064 * 065 * @param pattern the pattern to register the handler for. 066 * @param obj the object. 067 */ 068 public synchronized void register(final String pattern, final T obj) { 069 Args.notNull(pattern, "URI request pattern"); 070 this.map.put(pattern, obj); 071 } 072 073 /** 074 * Removes registered object, if exists, for the given pattern. 075 * 076 * @param pattern the pattern to unregister. 077 */ 078 public synchronized void unregister(final String pattern) { 079 if (pattern == null) { 080 return; 081 } 082 this.map.remove(pattern); 083 } 084 085 /** 086 * @deprecated (4.1) do not use 087 */ 088 @Deprecated 089 public synchronized void setHandlers(final Map<String, T> map) { 090 Args.notNull(map, "Map of handlers"); 091 this.map.clear(); 092 this.map.putAll(map); 093 } 094 095 /** 096 * @deprecated (4.1) do not use 097 */ 098 @Deprecated 099 public synchronized void setObjects(final Map<String, T> map) { 100 Args.notNull(map, "Map of handlers"); 101 this.map.clear(); 102 this.map.putAll(map); 103 } 104 105 /** 106 * @deprecated (4.1) do not use 107 */ 108 @Deprecated 109 public synchronized Map<String, T> getObjects() { 110 return this.map; 111 } 112 113 /** 114 * Looks up an object matching the given request path. 115 * 116 * @param path the request path 117 * @return object or {@code null} if no match is found. 118 */ 119 public synchronized T lookup(final String path) { 120 Args.notNull(path, "Request path"); 121 // direct match? 122 T obj = this.map.get(path); 123 if (obj == null) { 124 // pattern match? 125 String bestMatch = null; 126 for (final String pattern : this.map.keySet()) { 127 if (matchUriRequestPattern(pattern, path)) { 128 // we have a match. is it any better? 129 if (bestMatch == null 130 || (bestMatch.length() < pattern.length()) 131 || (bestMatch.length() == pattern.length() && pattern.endsWith("*"))) { 132 obj = this.map.get(pattern); 133 bestMatch = pattern; 134 } 135 } 136 } 137 } 138 return obj; 139 } 140 141 /** 142 * Tests if the given request path matches the given pattern. 143 * 144 * @param pattern the pattern 145 * @param path the request path 146 * @return {@code true} if the request URI matches the pattern, 147 * {@code false} otherwise. 148 */ 149 protected boolean matchUriRequestPattern(final String pattern, final String path) { 150 if (pattern.equals("*")) { 151 return true; 152 } else { 153 return 154 (pattern.endsWith("*") && path.startsWith(pattern.substring(0, pattern.length() - 1))) || 155 (pattern.startsWith("*") && path.endsWith(pattern.substring(1, pattern.length()))); 156 } 157 } 158 159 @Override 160 public String toString() { 161 return this.map.toString(); 162 } 163 164}