View Javadoc
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 }