2 // ========================================================================
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.util;
21 import java.util.BitSet;
22 import java.util.HashMap;
24 import java.util.StringTokenizer;
27 /* ------------------------------------------------------------ */
29 * Internet address map to object
31 * Internet addresses may be specified as absolute address or as a combination of
32 * four octet wildcard specifications (a.b.c.d) that are defined as follows.
35 * nnn - an absolute value (0-255)
36 * mmm-nnn - an inclusive range of absolute values,
37 * with following shorthand notations:
41 * a,b,... - a list of wildcard specifications
44 @SuppressWarnings("serial")
45 public class IPAddressMap<TYPE> extends HashMap<String, TYPE>
47 private final HashMap<String,IPAddrPattern> _patterns = new HashMap<String,IPAddrPattern>();
49 /* --------------------------------------------------------------- */
50 /** Construct empty IPAddressMap.
57 /* --------------------------------------------------------------- */
58 /** Construct empty IPAddressMap.
60 * @param capacity initial capacity
62 public IPAddressMap(int capacity)
67 /* ------------------------------------------------------------ */
69 * Insert a new internet address into map
71 * @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
74 public TYPE put(String addrSpec, TYPE object)
75 throws IllegalArgumentException
77 if (addrSpec == null || addrSpec.trim().length() == 0)
78 throw new IllegalArgumentException("Invalid IP address pattern: "+addrSpec);
80 String spec = addrSpec.trim();
81 if (_patterns.get(spec) == null)
82 _patterns.put(spec,new IPAddrPattern(spec));
84 return super.put(spec, object);
87 /* ------------------------------------------------------------ */
89 * Retrieve the object mapped to the specified internet address literal
91 * @see java.util.HashMap#get(java.lang.Object)
94 public TYPE get(Object key)
96 return super.get(key);
99 /* ------------------------------------------------------------ */
101 * Retrieve the first object that is associated with the specified
102 * internet address by taking into account the wildcard specifications.
104 * @param addr internet address
105 * @return associated object
107 public TYPE match(String addr)
109 Map.Entry<String, TYPE> entry = getMatch(addr);
110 return entry==null ? null : entry.getValue();
113 /* ------------------------------------------------------------ */
115 * Retrieve the first map entry that is associated with the specified
116 * internet address by taking into account the wildcard specifications.
118 * @param addr internet address
119 * @return map entry associated
121 public Map.Entry<String, TYPE> getMatch(String addr)
125 for(Map.Entry<String, TYPE> entry: super.entrySet())
127 if (_patterns.get(entry.getKey()).match(addr))
136 /* ------------------------------------------------------------ */
138 * Retrieve a lazy list of map entries associated with specified
139 * internet address by taking into account the wildcard specifications.
141 * @param addr internet address
142 * @return lazy list of map entries
144 public Object getLazyMatches(String addr)
147 return LazyList.getList(super.entrySet());
149 Object entries = null;
150 for(Map.Entry<String, TYPE> entry: super.entrySet())
152 if (_patterns.get(entry.getKey()).match(addr))
154 entries = LazyList.add(entries,entry);
160 /* ------------------------------------------------------------ */
164 * Represents internet address wildcard.
165 * Matches the wildcard to provided internet address.
167 private static class IPAddrPattern
169 private final OctetPattern[] _octets = new OctetPattern[4];
170 /* ------------------------------------------------------------ */
172 * Create new IPAddrPattern
174 * @param value internet address wildcard specification
175 * @throws IllegalArgumentException if wildcard specification is invalid
177 public IPAddrPattern(String value)
178 throws IllegalArgumentException
180 if (value == null || value.trim().length() == 0)
181 throw new IllegalArgumentException("Invalid IP address pattern: "+value);
185 StringTokenizer parts = new StringTokenizer(value, ".");
188 for (int idx=0; idx<4; idx++)
190 part = parts.hasMoreTokens() ? parts.nextToken().trim() : "0-255";
192 int len = part.length();
193 if (len == 0 && parts.hasMoreTokens())
194 throw new IllegalArgumentException("Invalid IP address pattern: "+value);
196 _octets[idx] = new OctetPattern(len==0 ? "0-255" : part);
199 catch (IllegalArgumentException ex)
201 throw new IllegalArgumentException("Invalid IP address pattern: "+value, ex);
205 /* ------------------------------------------------------------ */
207 * Match the specified internet address against the wildcard
209 * @param value internet address
210 * @return true if specified internet address matches wildcard specification
212 * @throws IllegalArgumentException if specified internet address is invalid
214 public boolean match(String value)
215 throws IllegalArgumentException
217 if (value == null || value.trim().length() == 0)
218 throw new IllegalArgumentException("Invalid IP address: "+value);
222 StringTokenizer parts = new StringTokenizer(value, ".");
224 boolean result = true;
225 for (int idx=0; idx<4; idx++)
227 if (!parts.hasMoreTokens())
228 throw new IllegalArgumentException("Invalid IP address: "+value);
230 if (!(result &= _octets[idx].match(parts.nextToken())))
235 catch (IllegalArgumentException ex)
237 throw new IllegalArgumentException("Invalid IP address: "+value, ex);
242 /* ------------------------------------------------------------ */
246 * Represents a single octet wildcard.
247 * Matches the wildcard to the specified octet value.
249 private static class OctetPattern extends BitSet
251 private final BitSet _mask = new BitSet(256);
253 /* ------------------------------------------------------------ */
255 * Create new OctetPattern
257 * @param octetSpec octet wildcard specification
258 * @throws IllegalArgumentException if wildcard specification is invalid
260 public OctetPattern(String octetSpec)
261 throws IllegalArgumentException
265 if (octetSpec != null)
267 String spec = octetSpec.trim();
268 if(spec.length() == 0)
274 StringTokenizer parts = new StringTokenizer(spec,",");
275 while (parts.hasMoreTokens())
277 String part = parts.nextToken().trim();
278 if (part.length() > 0)
280 if (part.indexOf('-') < 0)
282 Integer value = Integer.valueOf(part);
287 int low = 0, high = 255;
289 String[] bounds = part.split("-",-2);
290 if (bounds.length != 2)
292 throw new IllegalArgumentException("Invalid octet spec: "+octetSpec);
295 if (bounds[0].length() > 0)
297 low = Integer.parseInt(bounds[0]);
299 if (bounds[1].length() > 0)
301 high = Integer.parseInt(bounds[1]);
306 throw new IllegalArgumentException("Invalid octet spec: "+octetSpec);
309 _mask.set(low, high+1);
316 catch (NumberFormatException ex)
318 throw new IllegalArgumentException("Invalid octet spec: "+octetSpec, ex);
322 /* ------------------------------------------------------------ */
324 * Match specified octet value against the wildcard
326 * @param value octet value
327 * @return true if specified octet value matches the wildcard
328 * @throws IllegalArgumentException if specified octet value is invalid
330 public boolean match(String value)
331 throws IllegalArgumentException
333 if (value == null || value.trim().length() == 0)
334 throw new IllegalArgumentException("Invalid octet: "+value);
338 int number = Integer.parseInt(value);
339 return match(number);
341 catch (NumberFormatException ex)
343 throw new IllegalArgumentException("Invalid octet: "+value);
347 /* ------------------------------------------------------------ */
349 * Match specified octet value against the wildcard
351 * @param number octet value
352 * @return true if specified octet value matches the wildcard
353 * @throws IllegalArgumentException if specified octet value is invalid
355 public boolean match(int number)
356 throws IllegalArgumentException
358 if (number < 0 || number > 255)
359 throw new IllegalArgumentException("Invalid octet: "+number);
361 return _mask.get(number);