blob: 7cbbcab9279170f6204823ff6a44e71b1c329ab0 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
/* ------------------------------------------------------------ */
/**
* Internet address map to object
* <p>
* Internet addresses may be specified as absolute address or as a combination of
* four octet wildcard specifications (a.b.c.d) that are defined as follows.
* </p>
* <pre>
* nnn - an absolute value (0-255)
* mmm-nnn - an inclusive range of absolute values,
* with following shorthand notations:
* nnn- => nnn-255
* -nnn => 0-nnn
* - => 0-255
* a,b,... - a list of wildcard specifications
* </pre>
*/
@SuppressWarnings("serial")
public class IPAddressMap<TYPE> extends HashMap<String, TYPE>
{
private final HashMap<String,IPAddrPattern> _patterns = new HashMap<String,IPAddrPattern>();
/* --------------------------------------------------------------- */
/** Construct empty IPAddressMap.
*/
public IPAddressMap()
{
super(11);
}
/* --------------------------------------------------------------- */
/** Construct empty IPAddressMap.
*
* @param capacity initial capacity
*/
public IPAddressMap(int capacity)
{
super (capacity);
}
/* ------------------------------------------------------------ */
/**
* Insert a new internet address into map
*
* @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
*/
@Override
public TYPE put(String addrSpec, TYPE object)
throws IllegalArgumentException
{
if (addrSpec == null || addrSpec.trim().length() == 0)
throw new IllegalArgumentException("Invalid IP address pattern: "+addrSpec);
String spec = addrSpec.trim();
if (_patterns.get(spec) == null)
_patterns.put(spec,new IPAddrPattern(spec));
return super.put(spec, object);
}
/* ------------------------------------------------------------ */
/**
* Retrieve the object mapped to the specified internet address literal
*
* @see java.util.HashMap#get(java.lang.Object)
*/
@Override
public TYPE get(Object key)
{
return super.get(key);
}
/* ------------------------------------------------------------ */
/**
* Retrieve the first object that is associated with the specified
* internet address by taking into account the wildcard specifications.
*
* @param addr internet address
* @return associated object
*/
public TYPE match(String addr)
{
Map.Entry<String, TYPE> entry = getMatch(addr);
return entry==null ? null : entry.getValue();
}
/* ------------------------------------------------------------ */
/**
* Retrieve the first map entry that is associated with the specified
* internet address by taking into account the wildcard specifications.
*
* @param addr internet address
* @return map entry associated
*/
public Map.Entry<String, TYPE> getMatch(String addr)
{
if (addr != null)
{
for(Map.Entry<String, TYPE> entry: super.entrySet())
{
if (_patterns.get(entry.getKey()).match(addr))
{
return entry;
}
}
}
return null;
}
/* ------------------------------------------------------------ */
/**
* Retrieve a lazy list of map entries associated with specified
* internet address by taking into account the wildcard specifications.
*
* @param addr internet address
* @return lazy list of map entries
*/
public Object getLazyMatches(String addr)
{
if (addr == null)
return LazyList.getList(super.entrySet());
Object entries = null;
for(Map.Entry<String, TYPE> entry: super.entrySet())
{
if (_patterns.get(entry.getKey()).match(addr))
{
entries = LazyList.add(entries,entry);
}
}
return entries;
}
/* ------------------------------------------------------------ */
/**
* IPAddrPattern
*
* Represents internet address wildcard.
* Matches the wildcard to provided internet address.
*/
private static class IPAddrPattern
{
private final OctetPattern[] _octets = new OctetPattern[4];
/* ------------------------------------------------------------ */
/**
* Create new IPAddrPattern
*
* @param value internet address wildcard specification
* @throws IllegalArgumentException if wildcard specification is invalid
*/
public IPAddrPattern(String value)
throws IllegalArgumentException
{
if (value == null || value.trim().length() == 0)
throw new IllegalArgumentException("Invalid IP address pattern: "+value);
try
{
StringTokenizer parts = new StringTokenizer(value, ".");
String part;
for (int idx=0; idx<4; idx++)
{
part = parts.hasMoreTokens() ? parts.nextToken().trim() : "0-255";
int len = part.length();
if (len == 0 && parts.hasMoreTokens())
throw new IllegalArgumentException("Invalid IP address pattern: "+value);
_octets[idx] = new OctetPattern(len==0 ? "0-255" : part);
}
}
catch (IllegalArgumentException ex)
{
throw new IllegalArgumentException("Invalid IP address pattern: "+value, ex);
}
}
/* ------------------------------------------------------------ */
/**
* Match the specified internet address against the wildcard
*
* @param value internet address
* @return true if specified internet address matches wildcard specification
*
* @throws IllegalArgumentException if specified internet address is invalid
*/
public boolean match(String value)
throws IllegalArgumentException
{
if (value == null || value.trim().length() == 0)
throw new IllegalArgumentException("Invalid IP address: "+value);
try
{
StringTokenizer parts = new StringTokenizer(value, ".");
boolean result = true;
for (int idx=0; idx<4; idx++)
{
if (!parts.hasMoreTokens())
throw new IllegalArgumentException("Invalid IP address: "+value);
if (!(result &= _octets[idx].match(parts.nextToken())))
break;
}
return result;
}
catch (IllegalArgumentException ex)
{
throw new IllegalArgumentException("Invalid IP address: "+value, ex);
}
}
}
/* ------------------------------------------------------------ */
/**
* OctetPattern
*
* Represents a single octet wildcard.
* Matches the wildcard to the specified octet value.
*/
private static class OctetPattern extends BitSet
{
private final BitSet _mask = new BitSet(256);
/* ------------------------------------------------------------ */
/**
* Create new OctetPattern
*
* @param octetSpec octet wildcard specification
* @throws IllegalArgumentException if wildcard specification is invalid
*/
public OctetPattern(String octetSpec)
throws IllegalArgumentException
{
try
{
if (octetSpec != null)
{
String spec = octetSpec.trim();
if(spec.length() == 0)
{
_mask.set(0,255);
}
else
{
StringTokenizer parts = new StringTokenizer(spec,",");
while (parts.hasMoreTokens())
{
String part = parts.nextToken().trim();
if (part.length() > 0)
{
if (part.indexOf('-') < 0)
{
Integer value = Integer.valueOf(part);
_mask.set(value);
}
else
{
int low = 0, high = 255;
String[] bounds = part.split("-",-2);
if (bounds.length != 2)
{
throw new IllegalArgumentException("Invalid octet spec: "+octetSpec);
}
if (bounds[0].length() > 0)
{
low = Integer.parseInt(bounds[0]);
}
if (bounds[1].length() > 0)
{
high = Integer.parseInt(bounds[1]);
}
if (low > high)
{
throw new IllegalArgumentException("Invalid octet spec: "+octetSpec);
}
_mask.set(low, high+1);
}
}
}
}
}
}
catch (NumberFormatException ex)
{
throw new IllegalArgumentException("Invalid octet spec: "+octetSpec, ex);
}
}
/* ------------------------------------------------------------ */
/**
* Match specified octet value against the wildcard
*
* @param value octet value
* @return true if specified octet value matches the wildcard
* @throws IllegalArgumentException if specified octet value is invalid
*/
public boolean match(String value)
throws IllegalArgumentException
{
if (value == null || value.trim().length() == 0)
throw new IllegalArgumentException("Invalid octet: "+value);
try
{
int number = Integer.parseInt(value);
return match(number);
}
catch (NumberFormatException ex)
{
throw new IllegalArgumentException("Invalid octet: "+value);
}
}
/* ------------------------------------------------------------ */
/**
* Match specified octet value against the wildcard
*
* @param number octet value
* @return true if specified octet value matches the wildcard
* @throws IllegalArgumentException if specified octet value is invalid
*/
public boolean match(int number)
throws IllegalArgumentException
{
if (number < 0 || number > 255)
throw new IllegalArgumentException("Invalid octet: "+number);
return _mask.get(number);
}
}
}