View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.util;
20  
21  import java.util.BitSet;
22  import java.util.HashMap;
23  import java.util.Map;
24  import java.util.StringTokenizer;
25  
26  
27  /* ------------------------------------------------------------ */
28  /**
29   * Internet address map to object
30   * <p>
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.
33   * </p>
34   * <pre>
35   *     nnn  - an absolute value (0-255)
36   * mmm-nnn  - an inclusive range of absolute values, 
37   *            with following shorthand notations:
38   *              nnn- =&gt; nnn-255
39   *             -nnn  =&gt; 0-nnn
40   *             -     =&gt; 0-255
41   *          a,b,...  - a list of wildcard specifications
42   * </pre>
43   * @param <TYPE> the Map Entry value type
44   * @deprecated
45   */
46  @SuppressWarnings("serial")
47  public class IPAddressMap<TYPE> extends HashMap<String, TYPE>
48  {
49      private final HashMap<String,IPAddrPattern> _patterns = new HashMap<String,IPAddrPattern>();
50  
51      /* --------------------------------------------------------------- */
52      /** Construct empty IPAddressMap.
53       */
54      public IPAddressMap()
55      {
56          super(11);
57      }
58     
59      /* --------------------------------------------------------------- */
60      /** Construct empty IPAddressMap.
61       * 
62       * @param capacity initial capacity
63       */
64      public IPAddressMap(int capacity)
65      {
66          super (capacity);
67      }
68  
69      /* ------------------------------------------------------------ */
70      /**
71       * Insert a new internet address into map
72       * 
73       * @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
74       */
75      @Override
76      public TYPE put(String addrSpec, TYPE object)
77          throws IllegalArgumentException
78      {
79          if (addrSpec == null || addrSpec.trim().length() == 0)
80              throw new IllegalArgumentException("Invalid IP address pattern: "+addrSpec);
81          
82          String spec = addrSpec.trim();
83          if (_patterns.get(spec) == null)
84              _patterns.put(spec,new IPAddrPattern(spec));
85          
86          return super.put(spec, object);
87      }
88      
89      /* ------------------------------------------------------------ */
90      /**
91       * Retrieve the object mapped to the specified internet address literal
92       * 
93       * @see java.util.HashMap#get(java.lang.Object)
94       */
95      @Override
96      public TYPE get(Object key)
97      {
98          return super.get(key);
99      }
100     
101     /* ------------------------------------------------------------ */
102     /**
103      * Retrieve the first object that is associated with the specified 
104      * internet address by taking into account the wildcard specifications.
105      * 
106      * @param addr internet address
107      * @return associated object
108      */
109     public TYPE match(String addr)
110     {
111         Map.Entry<String, TYPE> entry = getMatch(addr);
112         return entry==null ? null : entry.getValue();
113     }
114     
115     /* ------------------------------------------------------------ */
116     /**
117      * Retrieve the first map entry that is associated with the specified 
118      * internet address by taking into account the wildcard specifications.
119      * 
120      * @param addr internet address
121      * @return map entry associated
122      */
123     public Map.Entry<String, TYPE> getMatch(String addr)
124     {
125         if (addr != null)
126         {
127             for(Map.Entry<String, TYPE> entry: super.entrySet())
128             {
129                 if (_patterns.get(entry.getKey()).match(addr))
130                 {
131                     return entry;
132                 }
133             }
134         }
135         return null;
136     }
137     
138     /* ------------------------------------------------------------ */
139     /**
140      * Retrieve a lazy list of map entries associated with specified
141      * internet address by taking into account the wildcard specifications.
142      * 
143      * @param addr  internet address
144      * @return lazy list of map entries
145      */
146     public Object getLazyMatches(String addr)
147     {
148         if (addr == null)
149             return LazyList.getList(super.entrySet());
150         
151         Object entries = null;
152         for(Map.Entry<String, TYPE> entry: super.entrySet())
153         {
154             if (_patterns.get(entry.getKey()).match(addr))
155             {
156                 entries = LazyList.add(entries,entry);
157             }
158         }
159         return entries;        
160     }
161     
162     /* ------------------------------------------------------------ */
163     /**
164      * IPAddrPattern
165      * 
166      * Represents internet address wildcard. 
167      * Matches the wildcard to provided internet address.
168      */
169     private static class IPAddrPattern
170     {
171         private final OctetPattern[] _octets = new OctetPattern[4];
172         /* ------------------------------------------------------------ */
173         /**
174          * Create new IPAddrPattern
175          * 
176          * @param value internet address wildcard specification
177          * @throws IllegalArgumentException if wildcard specification is invalid
178          */
179         public IPAddrPattern(String value)
180             throws IllegalArgumentException
181         {
182             if (value == null || value.trim().length() == 0)
183                 throw new IllegalArgumentException("Invalid IP address pattern: "+value);
184                 
185             try
186             {
187                 StringTokenizer parts = new StringTokenizer(value, ".");
188                 
189                 String part;
190                 for (int idx=0; idx<4; idx++)
191                 {
192                     part = parts.hasMoreTokens() ? parts.nextToken().trim() : "0-255";
193                     
194                     int len = part.length();
195                     if (len == 0 && parts.hasMoreTokens())
196                         throw new IllegalArgumentException("Invalid IP address pattern: "+value);
197                     
198                     _octets[idx] = new OctetPattern(len==0 ? "0-255" : part);
199                 }
200             }
201             catch (IllegalArgumentException ex)
202             {
203                 throw new IllegalArgumentException("Invalid IP address pattern: "+value, ex);
204             }
205         }
206         
207         /* ------------------------------------------------------------ */
208         /**
209          * Match the specified internet address against the wildcard
210          * 
211          * @param value internet address
212          * @return true if specified internet address matches wildcard specification
213          * 
214          * @throws IllegalArgumentException if specified internet address is invalid
215          */
216         public boolean match(String value)
217             throws IllegalArgumentException
218         {
219             if (value == null || value.trim().length() == 0)
220                 throw new IllegalArgumentException("Invalid IP address: "+value);
221             
222             try
223             {
224                 StringTokenizer parts = new StringTokenizer(value, ".");
225                 
226                 boolean result = true;
227                 for (int idx=0; idx<4; idx++)
228                 {
229                     if (!parts.hasMoreTokens())
230                         throw new IllegalArgumentException("Invalid IP address: "+value);
231                         
232                     if (!(result &= _octets[idx].match(parts.nextToken())))
233                         break;
234                 }
235                 return result;
236             }
237             catch (IllegalArgumentException ex)
238             {
239                 throw new IllegalArgumentException("Invalid IP address: "+value, ex);
240             }
241         }
242     }
243         
244     /* ------------------------------------------------------------ */
245     /**
246      * OctetPattern
247      * 
248      * Represents a single octet wildcard.
249      * Matches the wildcard to the specified octet value.
250      */
251     private static class OctetPattern extends BitSet
252     {
253         private final BitSet _mask = new BitSet(256);
254         
255         /* ------------------------------------------------------------ */
256         /**
257          * Create new OctetPattern
258          * 
259          * @param octetSpec octet wildcard specification
260          * @throws IllegalArgumentException if wildcard specification is invalid
261          */
262         public OctetPattern(String octetSpec)
263             throws IllegalArgumentException
264         {
265             try
266             {
267                 if (octetSpec != null)
268                 {
269                     String spec = octetSpec.trim();
270                     if(spec.length() == 0)
271                     {
272                         _mask.set(0,255);
273                     }
274                     else
275                     {
276                         StringTokenizer parts = new StringTokenizer(spec,",");
277                         while (parts.hasMoreTokens())
278                         {
279                             String part = parts.nextToken().trim();
280                             if (part.length() > 0)
281                             {
282                                 if (part.indexOf('-') < 0)
283                                 {
284                                     Integer value = Integer.valueOf(part);
285                                     _mask.set(value);
286                                 }
287                                 else
288                                 {
289                                     int low = 0, high = 255;
290                                     
291                                     String[] bounds = part.split("-",-2);
292                                     if (bounds.length != 2)
293                                     {
294                                         throw new IllegalArgumentException("Invalid octet spec: "+octetSpec);
295                                     }
296                                     
297                                     if (bounds[0].length() > 0)
298                                     {
299                                         low = Integer.parseInt(bounds[0]);
300                                     }
301                                     if (bounds[1].length() > 0)
302                                     {
303                                         high = Integer.parseInt(bounds[1]);
304                                     }
305                                     
306                                     if (low > high)
307                                     {
308                                         throw new IllegalArgumentException("Invalid octet spec: "+octetSpec);
309                                     }
310                                     
311                                     _mask.set(low, high+1);
312                                 }
313                             }
314                         }
315                     }
316                 }
317             }
318             catch (NumberFormatException ex)
319             {
320                 throw new IllegalArgumentException("Invalid octet spec: "+octetSpec, ex);
321             }
322         }
323         
324         /* ------------------------------------------------------------ */
325         /**
326          * Match specified octet value against the wildcard
327          * 
328          * @param value octet value
329          * @return true if specified octet value matches the wildcard
330          * @throws IllegalArgumentException if specified octet value is invalid
331          */
332         public boolean match(String value)
333             throws IllegalArgumentException
334         {
335             if (value == null || value.trim().length() == 0)
336                 throw new IllegalArgumentException("Invalid octet: "+value);
337 
338             try
339             {
340                 int number = Integer.parseInt(value);
341                 return match(number);
342             }
343             catch (NumberFormatException ex)
344             {
345                 throw new IllegalArgumentException("Invalid octet: "+value);
346             }
347         }
348         
349         /* ------------------------------------------------------------ */
350         /**
351          * Match specified octet value against the wildcard
352          * 
353          * @param number octet value
354          * @return true if specified octet value matches the wildcard
355          * @throws IllegalArgumentException if specified octet value is invalid
356          */
357         public boolean match(int number)
358             throws IllegalArgumentException
359         {
360             if (number < 0 || number > 255)
361                 throw new IllegalArgumentException("Invalid octet: "+number);
362             
363             return _mask.get(number);
364         }
365     }   
366 }