Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.owasp.esapi.codecs;
import java.io.IOException;
import java.io.PushbackReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.owasp.esapi.util.NullSafe;
/**
* Trie implementation for CharSequence keys. This uses HashMaps for each
* level instead of the traditional array. This is done as with unicode,
* each level's array would be 64k entries.
*
* NOTE:
*
*
@see java.util.Map.remove(Object) is not supported.
*
* If deletion support is added, the max key length will need work or removal.
*
*
Null values are not supported.
*
*
* @author Ed Schaller
*/
public class HashTrie implements Trie
{
private static class Entry implements Map.Entry
{
private CharSequence key;
private T value;
Entry(CharSequence key, T value)
{
this.key = key;
this.value = value;
}
/**
* Convenience instantiator.
* @param key The key for the new instance
* @param keyLength The length of the key to use
* @param value The value for the new instance
* @return null if key or value is null
* new Entry(key,value) if {@link CharSequence#length()} == keyLength
* new Entry(key.subSequence(0,keyLength),value) otherwise
*/
static Entry newInstanceIfNeeded(CharSequence key, int keyLength, T value)
{
if(value == null || key == null)
return null;
if(key.length() > keyLength)
key = key.subSequence(0,keyLength);
return new Entry(key,value);
}
/**
* Convenience instantiator.
* @param key The key for the new instance
* @param value The value for the new instance
* @return null if key or value is null
* new Entry(key,value) otherwise
*/
static Entry newInstanceIfNeeded(CharSequence key, T value)
{
if(value == null || key == null)
return null;
return new Entry(key,value);
}
/*************/
/* Map.Entry */
/*************/
public CharSequence getKey()
{
return key;
}
public T getValue()
{
return value;
}
public T setValue(T value)
{
throw new UnsupportedOperationException();
}
/********************/
/* java.lang.Object */
/********************/
public boolean equals(Map.Entry other)
{
return (NullSafe.equals(key, other.getKey()) && NullSafe.equals(value, other.getValue()));
}
@Override
public boolean equals(Object o)
{
if(o instanceof Map.Entry)
return equals((Map.Entry)o);
return false;
}
@Override
public int hashCode()
{
return NullSafe.hashCode(key) ^ NullSafe.hashCode(value);
}
@Override
public String toString()
{
return NullSafe.toString(key) + " => " + NullSafe.toString(value);
}
}
/**
* Node inside the trie.
*/
private static class Node
{
private T value = null;
private Map> nextMap;
/**
* Create a new Map for a node level. This is here so
* that if the underlying * Map implementation needs to
* be switched it is easily done.
* @return A new Map for use.
*/
private static Map> newNodeMap()
{
return new HashMap>();
}
/**
* Create a new Map for a node level. This is here so
* that if the underlying * Map implementation needs to
* be switched it is easily done.
* @param prev Previous map to use to populate the
* new map.
* @return A new Map for use.
*/
private static Map> newNodeMap(Map> prev)
{
return new HashMap>(prev);
}
/**
* Set the value for the key terminated at this node.
* @param value The value for this key.
*/
void setValue(T value)
{
this.value = value;
}
/**
* Get the node for the specified character.
* @param ch The next character to look for.
* @return The node requested or null if it is not
* present.
*/
Node getNextNode(Character ch)
{
if(nextMap == null)
return null;
return nextMap.get(ch);
}
/**
* Recursively add a key.
* @param key The key being added.
* @param pos The position in key that is being handled
* at this level.
*/
T put(CharSequence key, int pos, T addValue)
{
Node nextNode;
Character ch;
T old;
if(key.length() == pos)
{ // at terminating node
old = value;
setValue(addValue);
return old;
}
ch = key.charAt(pos);
if(nextMap == null)
{
nextMap = newNodeMap();
nextNode = new Node();
nextMap.put(ch, nextNode);
}
else if((nextNode = nextMap.get(ch))==null)
{
nextNode = new Node();
nextMap.put(ch,nextNode);
}
return nextNode.put(key,pos+1,addValue);
}
/**
* Recursively lookup a key's value.
* @param key The key being looked up.
* @param pos The position in the key that is being
* looked up at this level.
* @return The value associated with the key or null if none exists.
*/
T get(CharSequence key, int pos)
{
Node nextNode;
if(key.length() <= pos) // <= instead of == just in case
return value; // no value is null which is also not found
if((nextNode = getNextNode(key.charAt(pos)))==null)
return null;
return nextNode.get(key,pos+1);
}
/**
* Recursively lookup the longest key match.
* @param key The key being looked up.
* @param pos The position in the key that is being
* looked up at this level.
* @return The Entry associated with the longest key match or null if none exists.
*/
Entry getLongestMatch(CharSequence key, int pos)
{
Node nextNode;
Entry ret;
if(key.length() <= pos) // <= instead of == just in case
return Entry.newInstanceIfNeeded(key,value);
if((nextNode = getNextNode(key.charAt(pos)))==null)
{ // last in trie... return ourselves
return Entry.newInstanceIfNeeded(key,pos,value);
}
if((ret = nextNode.getLongestMatch(key, pos+1))!=null)
return ret;
return Entry.newInstanceIfNeeded(key,pos,value);
}
/**
* Recursively lookup the longest key match.
* @param keyIn Where to read the key from
* @param key The key that is being looked up at this level.
* @return The Entry associated with the longest key
* match or null if none exists.
*/
Entry getLongestMatch(PushbackReader keyIn, StringBuilder key) throws IOException
{
Node nextNode;
Entry ret;
int c;
char ch;
int prevLen;
// read next key char and append to key...
if((c = keyIn.read())<0)
// end of input, return what we have currently
return Entry.newInstanceIfNeeded(key,value);
ch = (char)c;
prevLen = key.length();
key.append(ch);
if((nextNode = getNextNode(ch))==null)
{ // last in trie... return ourselves
return Entry.newInstanceIfNeeded(key,value);
}
if((ret = nextNode.getLongestMatch(keyIn, key))!=null)
return ret;
// undo reading of key char and appending to key...
key.setLength(prevLen);
keyIn.unread(c);
return Entry.newInstanceIfNeeded(key,value);
}
/**
* Recursively rebuild the internal maps.
*/
void remap()
{
if(nextMap == null)
return;
nextMap = newNodeMap(nextMap);
for(Node node : nextMap.values())
node.remap();
}
/**
* Recursively search for a value.
* @param toFind The value to search for
* @return true if the value was found
* false otherwise
*/
boolean containsValue(Object toFind)
{
if(value != null && toFind.equals(value))
return true;
if(nextMap == null)
return false;
for(Node node : nextMap.values())
if(node.containsValue(toFind))
return true;
return false;
}
/**
* Recursively build values.
* @param values List being built.
* @return true if the value was found
* false otherwise
*/
Collection values(Collection values)
{
if(value != null)
values.add(value);
if(nextMap == null)
return values;
for(Node node : nextMap.values())
node.values(values);
return values;
}
/**
* Recursively build a key set.
* @param key StringBuilder with our key.
* @param keys Set to add to
* @return keys with additions
*/
Set keySet(StringBuilder key, Set keys)
{
int len = key.length();
if(value != null)
// MUST toString here
keys.add(key.toString());
if(nextMap != null && nextMap.size() > 0)
{
key.append('X');
for(Map.Entry> entry : nextMap.entrySet())
{
key.setCharAt(len,entry.getKey());
entry.getValue().keySet(key,keys);
}
key.setLength(len);
}
return keys;
}
/**
* Recursively build a entry set.
* @param key StringBuilder with our key.
* @param entries Set to add to
* @return entries with additions
*/
Set> entrySet(StringBuilder key, Set> entries)
{
int len = key.length();
if(value != null)
// MUST toString here
entries.add(new Entry(key.toString(),value));
if(nextMap != null && nextMap.size() > 0)
{
key.append('X');
for(Map.Entry> entry : nextMap.entrySet())
{
key.setCharAt(len,entry.getKey());
entry.getValue().entrySet(key,entries);
}
key.setLength(len);
}
return entries;
}
}
private Node root;
private int maxKeyLen;
private int size;
public HashTrie()
{
clear();
}
/**
* Get the key value entry who's key is the longest prefix match.
* @param key The key to lookup
* @return Entry with the longest matching key.
*/
public Map.Entry getLongestMatch(CharSequence key)
{
if(root == null || key == null)
return null;
return root.getLongestMatch(key, 0);
}
/**
* Get the key value entry who's key is the longest prefix match.
* @param keyIn Pushback reader to read the key from. This should
* have a buffer at least as large as {@link #getMaxKeyLength()}
* or an IOException may be thrown backing up.
* @return Entry with the longest matching key.
* @throws IOException if keyIn.read() or keyIn.unread() does.
*/
public Map.Entry getLongestMatch(PushbackReader keyIn) throws IOException
{
if(root == null || keyIn == null)
return null;
return root.getLongestMatch(keyIn, new StringBuilder());
}
/**
* Get the maximum key length.
* @return max key length.
*/
public int getMaxKeyLength()
{
return maxKeyLen;
}
/*****************/
/* java.util.Map */
/*****************/
/**
* Clear all entries.
*/
public void clear()
{
root = null;
maxKeyLen = -1;
size = 0;
}
/** {@inheritDoc} */
public boolean containsKey(Object key)
{
return (get(key) != null);
}
/** {@inheritDoc} */
public boolean containsValue(Object value)
{
if(root == null)
return false;
return root.containsValue(value);
}
/**
* Add mapping.
* @param key The mapping's key.
* @value value The mapping's value
* @throws NullPointerException if key or value is null.
*/
public T put(CharSequence key, T value) throws NullPointerException
{
int len;
T old;
if(key == null)
throw new NullPointerException("Null keys are not handled");
if(value == null)
throw new NullPointerException("Null values are not handled");
if(root == null)
root = new Node();
if((old = root.put(key,0,value))!=null)
return old;
// after in case of replacement
if((len = key.length()) > maxKeyLen)
maxKeyLen = len;
size++;
return null;
}
/**
* Remove a entry.
* @return previous value
* @throws UnsupportedOperationException always.
*/
public T remove(Object key) throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
/** {@inheritDoc} */
public void putAll(Map map)
{
for(Map.Entry entry : map.entrySet())
put(entry.getKey(),entry.getValue());
}
/** {@inheritDoc} */
public Set keySet()
{
Set keys = new HashSet(size);
if(root == null)
return keys;
return root.keySet(new StringBuilder(), keys);
}
/** {@inheritDoc} */
public Collection values()
{
ArrayList values = new ArrayList(size());
if(root == null)
return values;
return root.values(values);
}
/** {@inheritDoc} */
public Set> entrySet()
{
Set> entries = new HashSet>(size());
if(root == null)
return entries;
return root.entrySet(new StringBuilder(), entries);
}
/**
* Get the value for a key.
* @param key The key to look up.
* @return The value for key or null if the key is not found.
*/
public T get(Object key)
{
if(root == null || key == null)
return null;
if(!(key instanceof CharSequence))
return null;
return root.get((CharSequence)key,0);
}
/**
* Get the number of entries.
* @return the number or entries.
*/
public int size()
{
return size;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other)
{
if(other == null)
return false;
if(!(other instanceof Map))
return false;
// per spec
return entrySet().equals(((Map)other).entrySet());
}
/** {@inheritDoc} */
@Override
public int hashCode()
{
// per spec
return entrySet().hashCode();
}
/** {@inheritDoc} */
@Override
public String toString()
{
StringBuilder sb;
boolean first;
if(isEmpty())
return "{}";
sb = new StringBuilder();
first = true;
sb.append("{ ");
for(Map.Entry entry : entrySet())
{
if(first)
first = false;
else
sb.append(", ");
sb.append(entry.toString());
}
sb.append(" }");
return sb.toString();
}
/** {@inheritDoc} */
public boolean isEmpty()
{
return(size() == 0);
}
}