1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27
28 package org.apache.http.protocol;
29
30 import java.util.HashSet;
31 import java.util.LinkedHashMap;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import java.util.Set;
35
36 import org.apache.http.annotation.Contract;
37 import org.apache.http.annotation.ThreadingBehavior;
38 import org.apache.http.util.Args;
39
40 /**
41 * Maintains a map of objects keyed by a request URI pattern.
42 * <br>
43 * Patterns may have three formats:
44 * <ul>
45 * <li>{@code *}</li>
46 * <li>{@code *<uri>}</li>
47 * <li>{@code <uri>*}</li>
48 * </ul>
49 * <br>
50 * This class can be used to resolve an object matching a particular request
51 * URI.
52 *
53 * @param <T> The type of registered objects.
54 * @since 4.0
55 */
56 @Contract(threading = ThreadingBehavior.SAFE)
57 public class UriPatternMatcher<T> {
58
59 private final Map<String, T> map;
60
61 public UriPatternMatcher() {
62 super();
63 this.map = new LinkedHashMap<String, T>();
64 }
65
66 /**
67 * Returns a {@link Set} view of the mappings contained in this matcher.
68 *
69 * @return a set view of the mappings contained in this matcher.
70 *
71 * @see Map#entrySet()
72 * @since 4.4.9
73 */
74 public synchronized Set<Entry<String, T>> entrySet() {
75 return new HashSet<Entry<String, T>>(map.entrySet());
76 }
77
78 /**
79 * Registers the given object for URIs matching the given pattern.
80 *
81 * @param pattern the pattern to register the handler for.
82 * @param obj the object.
83 */
84 public synchronized void register(final String pattern, final T obj) {
85 Args.notNull(pattern, "URI request pattern");
86 this.map.put(pattern, obj);
87 }
88
89 /**
90 * Removes registered object, if exists, for the given pattern.
91 *
92 * @param pattern the pattern to unregister.
93 */
94 public synchronized void unregister(final String pattern) {
95 if (pattern == null) {
96 return;
97 }
98 this.map.remove(pattern);
99 }
100
101 /**
102 * @deprecated (4.1) do not use
103 */
104 @Deprecated
105 public synchronized void setHandlers(final Map<String, T> map) {
106 Args.notNull(map, "Map of handlers");
107 this.map.clear();
108 this.map.putAll(map);
109 }
110
111 /**
112 * @deprecated (4.1) do not use
113 */
114 @Deprecated
115 public synchronized void setObjects(final Map<String, T> map) {
116 Args.notNull(map, "Map of handlers");
117 this.map.clear();
118 this.map.putAll(map);
119 }
120
121 /**
122 * @deprecated (4.1) do not use
123 */
124 @Deprecated
125 public synchronized Map<String, T> getObjects() {
126 return this.map;
127 }
128
129 /**
130 * Looks up an object matching the given request path.
131 *
132 * @param path the request path
133 * @return object or {@code null} if no match is found.
134 */
135 public synchronized T lookup(final String path) {
136 Args.notNull(path, "Request path");
137 // direct match?
138 T obj = this.map.get(path);
139 if (obj == null) {
140 // pattern match?
141 String bestMatch = null;
142 for (final String pattern : this.map.keySet()) {
143 if (matchUriRequestPattern(pattern, path)) {
144 // we have a match. is it any better?
145 if (bestMatch == null
146 || (bestMatch.length() < pattern.length())
147 || (bestMatch.length() == pattern.length() && pattern.endsWith("*"))) {
148 obj = this.map.get(pattern);
149 bestMatch = pattern;
150 }
151 }
152 }
153 }
154 return obj;
155 }
156
157 /**
158 * Tests if the given request path matches the given pattern.
159 *
160 * @param pattern the pattern
161 * @param path the request path
162 * @return {@code true} if the request URI matches the pattern,
163 * {@code false} otherwise.
164 */
165 protected boolean matchUriRequestPattern(final String pattern, final String path) {
166 if (pattern.equals("*")) {
167 return true;
168 }
169 return
170 (pattern.endsWith("*") && path.startsWith(pattern.substring(0, pattern.length() - 1))) ||
171 (pattern.startsWith("*") && path.endsWith(pattern.substring(1, pattern.length())));
172 }
173
174 @Override
175 public String toString() {
176 return this.map.toString();
177 }
178
179 }