Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
   *
   * Copyright (c) 2010-2011 Oracle and/or its affiliates. All rights reserved.
   *
   * The contents of this file are subject to the terms of either the GNU
   * General Public License Version 2 only ("GPL") or the Common Development
   * and Distribution License("CDDL") (collectively, the "License").  You
   * may not use this file except in compliance with the License.  You can
  * obtain a copy of the License at
  * http://glassfish.java.net/public/CDDL+GPL_1_1.html
  * or packager/legal/LICENSE.txt.  See the License for the specific
  * language governing permissions and limitations under the License.
  *
  * When distributing the software, include this License Header Notice in each
  * file and include the License file at packager/legal/LICENSE.txt.
  *
  * GPL Classpath Exception:
  * Oracle designates this particular file as subject to the "Classpath"
  * exception as provided by Oracle in the GPL Version 2 section of the License
  * file that accompanied this code.
  *
  * Modifications:
  * If applicable, add the following below the License Header, with the fields
  * enclosed by brackets [] replaced by your own identifying information:
  * "Portions Copyright [year] [name of copyright owner]"
  *
  * Contributor(s):
  * If you wish your version of this file to be governed by only the CDDL or
  * only the GPL Version 2, indicate your decision by adding "[Contributor]
  * elects to include this software in this distribution under the [CDDL or GPL
  * Version 2] license."  If you don't indicate a single choice of license, a
  * recipient has the option to distribute your version of this file under
  * either the CDDL, the GPL Version 2 or to extend the choice of license to
  * its licensees as provided above.  However, if you add GPL Version 2 code
  * and therefore, elected the GPL Version 2 license, then the option applies
  * only if the new code is made subject to such option by the copyright
  * holder.
  */
 
 package com.sun.jersey.core.util;
 
 import java.util.Map;
 import java.util.Set;
A implementation similar to java.util.HashMap but supports the comparison of keys using a KeyComparator.

Author(s):
Paul.Sandoz@Sun.Com
 
 @SuppressWarnings("unchecked")
 public class KeyComparatorHashMap<K,V>
     extends AbstractMap<K,V>
     implements Map<K,V>, CloneableSerializable
 {

    
The default initial capacity - MUST be a power of two.
 
     static final int DEFAULT_INITIAL_CAPACITY = 16;

    
The maximum capacity, used if a higher value is implicitly specified by either of the constructors with arguments. MUST be a power of two <= 1<<30.
 
     static final int MAXIMUM_CAPACITY = 1 << 30;

    
The load factor used when none specified in constructor.
 
     static final float DEFAULT_LOAD_FACTOR = 0.75f;

    
The table, resized as necessary. Length MUST Always be a power of two.
 
     transient Entry<K, V>[] table;

    
The number of key-value mappings contained in this identity hash map.
 
     transient int size;
  
    
The next size value at which to resize (capacity * load factor).

Serial:
 
     int threshold;
  
    
The load factor for the hash table.

Serial:
    final float loadFactor;

    
The number of times this HashMap has been structurally modified Structural modifications are those that change the number of mappings in the HashMap or otherwise modify its internal structure (e.g., rehash). This field is used to make iterators on Collection-views of the HashMap fail-fast. (See ConcurrentModificationException).
    transient volatile int modCount;
    public int getDEFAULT_INITIAL_CAPACITY() {
        return ;
    }
    final KeyComparator<K> keyComparator;
    
    
Constructs an empty HashMap with the specified initial capacity and load factor.

Parameters:
initialCapacity The initial capacity.
loadFactor The load factor.
Throws:
java.lang.IllegalArgumentException if the initial capacity is negative or the load factor is nonpositive.
    public KeyComparatorHashMap(int initialCapacityfloat loadFactorKeyComparator<K> keyComparator) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException(ImplMessages.ILLEGAL_INITIAL_CAPACITY(initialCapacity));
        if (initialCapacity > )
            initialCapacity = ;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException(ImplMessages.ILLEGAL_LOAD_FACTOR(loadFactor));
        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity
            capacity <<= 1;
    
        this. = loadFactor;
         = (int)(capacity * loadFactor);
         = new Entry[capacity];
        init();
        
        this. = keyComparator;
    }
  
    
Constructs an empty HashMap with the specified initial capacity and the default load factor (0.75).

Parameters:
initialCapacity the initial capacity.
Throws:
java.lang.IllegalArgumentException if the initial capacity is negative.
    public KeyComparatorHashMap(int initialCapacityKeyComparator<K> keyComparator) {
        this(initialCapacitykeyComparator);
    }

    
Constructs an empty HashMap with the default initial capacity (16) and the default load factor (0.75).
    public KeyComparatorHashMap(KeyComparator<K> keyComparator) {
        this. = ;
         = new Entry[];
        init();
        
        this. = keyComparator;
    }

    
Constructs a new HashMap with the same mappings as the specified Map. The HashMap is created with default load factor (0.75) and an initial capacity sufficient to hold the mappings in the specified Map.

Parameters:
m the map whose mappings are to be placed in this map.
Throws:
java.lang.NullPointerException if the specified map is null.
    public KeyComparatorHashMap(Map<? extends K, ? extends V> m
            KeyComparator<K> keyComparator) {
        this(Math.max((int) (m.size() / ) + 1,
                      ), keyComparator);
        putAllForCreate(m);
    }
    //
    
    
Get the number of times this HashMap has been structurally modified Structural modifications are those that change the number of mappings in the HashMap or otherwise modify its internal structure (e.g., rehash).

Returns:
return the modification count.
    public int getModCount() {
        return ;
    }    
    
    // internal utilities

    
Initialization hook for subclasses. This method is called in all constructors and pseudo-constructors (clone, readObject) after HashMap has been initialized but before any entries have been inserted. (In the absence of this method, readObject would require explicit knowledge of subclasses.)
    void init() {
    }

    
Value representing null keys inside tables.
    static final Object NULL_KEY = new Object();

    
Returns internal representation for key. Use NULL_KEY if key is null.
    static <T> T maskNull(T key) {
        return key == null ? (T) : key;
    }
    static <T> boolean isNull(T key) {
        return key == ;
    }
    
Returns key represented by specified internal representation.
    static <T> T unmaskNull(T key) {
        return key ==  ? null : key;
    }

    
Returns a hash value for the specified object. In addition to the object's own hashCode, this method applies a "supplemental hash function," which defends against poor quality hash functions. This is critical because HashMap uses power-of two length hash tables.

The shift distances in this function were chosen as the result of an automated search over the entire four-dimensional search space.

    static int hash(Object x) {
        int h = x.hashCode();
        h += ~(h << 9);
        h ^=  (h >>> 14);
        h +=  (h << 4);
        h ^=  (h >>> 10);
        return h;
    }

    
Check for equality of non-null reference x and possibly-null y.
    static boolean eq(Object xObject y) {
        return x == y || x.equals(y);
    }

    
Returns index for hash code h.
    static int indexFor(int hint length) {
        return h & (length-1);
    }
 
    
Returns the number of key-value mappings in this map.

Returns:
the number of key-value mappings in this map.
    @Override
    public int size() {
        return ;
    }
  
    
Returns true if this map contains no key-value mappings.

Returns:
true if this map contains no key-value mappings.
    @Override
    public boolean isEmpty() {
        return  == 0;
    }
    int keyComparatorHash(K k) {
        return isNull(k)
                ? hash(k.hashCode())
                : hash(.hash(k));
    }
    int hash(int h) {
        h += ~(h << 9);
        h ^=  (h >>> 14);
        h +=  (h << 4);
        h ^=  (h >>> 10);
        return h;
    }
    
    
Check for equality of non-null reference x and possibly-null y.
    boolean keyComparatorEq(K x, K y) {
        if (isNull(x)) {
            return x == y;
        } else if (isNull(y)) {
            return x == y;
        } else {
            return x == y || .equals(xy);
        }
    }
    
    
Returns the value to which the specified key is mapped in this identity hash map, or null if the map contains no mapping for this key. A return value of null does not necessarily indicate that the map contains no mapping for the key; it is also possible that the map explicitly maps the key to null. The containsKey method may be used to distinguish these two cases.

Parameters:
key the key whose associated value is to be returned.
Returns:
the value to which this map maps the specified key, or null if the map contains no mapping for this key.
See also:
put(java.lang.Object,java.lang.Object)
    @Override
    public V get(Object key) {
        K k = (K)maskNull(key);
        int hash = keyComparatorHash(k);
        int i = indexFor(hash.);
        Entry<K,V> e = [i]; 
        while (true) {
            if (e == null)
                return null;
            if (e.hash == hash && keyComparatorEq(ke.key)) 
                return e.value;
            e = e.next;
        }
    }

    
Returns true if this map contains a mapping for the specified key.

Parameters:
key The key whose presence in this map is to be tested
Returns:
true if this map contains a mapping for the specified key.
    @Override
    public boolean containsKey(Object key) {
        K k = (K)maskNull(key);
        int hash = keyComparatorHash(k);
        int i = indexFor(hash.);
        Entry<K,V> e = [i]; 
        while (e != null) {
            if (e.hash == hash && keyComparatorEq(ke.key)) 
                return true;
            e = e.next;
        }
        return false;
    }

    
Returns the entry associated with the specified key in the HashMap. Returns null if the HashMap contains no mapping for this key.
    Entry<K,V> getEntry(K key) {
        K k = maskNull(key);
        int hash = keyComparatorHash(k);
        int i = indexFor(hash.);
        Entry<K,V> e = [i]; 
        while (e != null && !(e.hash == hash && keyComparatorEq(ke.key)))
            e = e.next;
        return e;
    }
  
    
Associates the specified value with the specified key in this map. If the map previously contained a mapping for this key, the old value is replaced.

Parameters:
key key with which the specified value is to be associated.
value value to be associated with the specified key.
Returns:
previous value associated with specified key, or null if there was no mapping for key. A null return can also indicate that the HashMap previously associated null with the specified key.
    @Override
    public V put(K key, V value) {
k = maskNull(key);
        int hash = keyComparatorHash(k);
        int i = indexFor(hash.);
        for (Entry<K,V> e = [i]; e != nulle = e.next) {
            if (e.hash == hash && keyComparatorEq(ke.key)) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        ++;
        addEntry(hashkvaluei);
        return null;
    }

    
This method is used instead of put by constructors and pseudoconstructors (clone, readObject). It does not resize the table, check for comodification, etc. It calls createEntry rather than addEntry.
    private void putForCreate(K key, V value) {
        K k = maskNull(key);
        int hash = keyComparatorHash(k);
        int i = indexFor(hash.);

        
Look for preexisting entry for key. This will never happen for clone or deserialize. It will only happen for construction if the input Map is a sorted map whose ordering is inconsistent w/ equals.
        for (Entry<K,V> e = [i]; e != nulle = e.next) {
            if (e.hash == hash && keyComparatorEq(ke.key)) {
                e.value = value;
                return;
            }
        }
        createEntry(hashkvaluei);
    }
    void putAllForCreate(Map<? extends K, ? extends V> m) {
        for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry<? extends K, ? extends V> e = i.next();
            putForCreate(e.getKey(), e.getValue());
        }
    }

    
Rehashes the contents of this map into a new array with a larger capacity. This method is called automatically when the number of keys in this map reaches its threshold. If current capacity is MAXIMUM_CAPACITY, this method does not resize the map, but sets threshold to Integer.MAX_VALUE. This has the effect of preventing future calls.

Parameters:
newCapacity the new capacity, MUST be a power of two; must be greater than current capacity unless current capacity is MAXIMUM_CAPACITY (in which case value is irrelevant).
    void resize(int newCapacity) {
        Entry<K,V>[] oldTable = ;
        int oldCapacity = oldTable.length;
        if (oldCapacity == ) {
             = .;
            return;
        }
        Entry<K,V>[] newTable = new Entry[newCapacity];
        transfer(newTable);
         = newTable;
         = (int)(newCapacity * );
    }

    
Transfer all entries from current table to newTable.
    void transfer(Entry<K,V>[] newTable) {
        Entry<K,V>[] src = ;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.lengthj++) {
            Entry<K,V> e = src[j];
            if (e != null) {
                src[j] = null;
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hashnewCapacity);  
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }

    
Copies all of the mappings from the specified map to this map These mappings will replace any mappings that this map had for any of the keys currently in the specified map.

Parameters:
m mappings to be stored in this map.
Throws:
java.lang.NullPointerException if the specified map is null.
    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        int numKeysToBeAdded = m.size();
        if (numKeysToBeAdded == 0)
            return;
        /*
         * Expand the map if the map if the number of mappings to be added
         * is greater than or equal to threshold.  This is conservative; the
         * obvious condition is (m.size() + size) >= threshold, but this
         * condition could result in a map with twice the appropriate capacity,
         * if the keys to be added overlap with the keys already in this map.
         * By using the conservative calculation, we subject ourself
         * to at most one extra resize.
         */
        if (numKeysToBeAdded > ) {
            int targetCapacity = (int)(numKeysToBeAdded /  + 1);
            if (targetCapacity > )
                targetCapacity = ;
            int newCapacity = .;
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;
            if (newCapacity > .)
                resize(newCapacity);
        }
        for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry<? extends K, ? extends V> e = i.next();
            put(e.getKey(), e.getValue());
        }
    }
  
    
Removes the mapping for this key from this map if present.

Parameters:
key key whose mapping is to be removed from the map.
Returns:
previous value associated with specified key, or null if there was no mapping for key. A null return can also indicate that the map previously associated null with the specified key.
    @Override
    public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }

    
Removes and returns the entry associated with the specified key in the HashMap. Returns null if the HashMap contains no mapping for this key.
    Entry<K,V> removeEntryForKey(Object key) {
        K k = (K)maskNull(key);
        int hash = keyComparatorHash(k);
        int i = indexFor(hash.);
        Entry<K,V> prev = [i];
        Entry<K,V> e = prev;
        while (e != null) {
            Entry<K,V> next = e.next;
            if (e.hash == hash && keyComparatorEq(ke.key)) {
                ++;
                --;
                if (prev == e
                    [i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }
   
        return e;
    }

    
Special version of remove for EntrySet.
    Entry<K,V> removeMapping(Object o) {
        if (!(o instanceof Map.Entry))
            return null;
        Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
        K k = (K)maskNull(entry.getKey());
        int hash = keyComparatorHash(k);
        int i = indexFor(hash.);
        Entry<K,V> prev = [i];
        Entry<K,V> e = prev;
        while (e != null) {
            Entry<K,V> next = e.next;
            if (e.hash == hash && e.equals(entry)) {
                ++;
                --;
                if (prev == e
                    [i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }
   
        return e;
    }

    
Removes all mappings from this map.
    @Override
    public void clear() {
        ++;
        Entry[] tab = ;
        for (int i = 0; i < tab.lengthi++) 
            tab[i] = null;
         = 0;
    }

    
Returns true if this map maps one or more keys to the specified value.

Parameters:
value value whose presence in this map is to be tested.
Returns:
true if this map maps one or more keys to the specified value.
    @Override
    public boolean containsValue(Object value) {
	if (value == null
            return containsNullValue();
	Entry[] tab = ;
        for (int i = 0; i < tab.length ; i++)
            for (Entry e = tab[i] ; e != null ; e = e.next)
                if (value.equals(e.value))
                    return true;
	return false;
    }

    
Special-case code for containsValue with null argument
    private boolean containsNullValue() {
	Entry[] tab = ;
        for (int i = 0; i < tab.length ; i++)
            for (Entry e = tab[i] ; e != null ; e = e.next)
                if (e.value == null)
                    return true;
	return false;
    }

    
Returns a shallow copy of this HashMap instance: the keys and values themselves are not cloned.

Returns:
a shallow copy of this map.
    @Override
    public Object clone() {
        KeyComparatorHashMap<K,V> result = null;
	try { 
	    result = (KeyComparatorHashMap<K,V>)super.clone();
catch (CloneNotSupportedException e) { 
	    // assert false;
	}
        result.table = new Entry[.];
        result.entrySet = null;
        result.modCount = 0;
        result.size = 0;
        result.init();
        result.putAllForCreate(this);
        return result;
    }
    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        final int hash;
        Entry<K,V> next;

        
Create new entry.
        Entry(int h, K k, V vEntry<K,V> n) {
             = v;
             = n;
             = k;
             = h;
        }
        public K getKey() {
            return KeyComparatorHashMap.<K>unmaskNull();
        }
        public V getValue() {
            return ;
        }
    
        public V setValue(V newValue) {
	    V oldValue = ;
             = newValue;
            return oldValue;
        }
    
        @Override
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2))) 
                    return true;
            }
            return false;
        }
    
        @Override
        public int hashCode() {
            return (== ? 0 : .hashCode()) ^
                   (==null   ? 0 : .hashCode());
        }
    
        @Override
        public String toString() {
            return getKey() + "=" + getValue();
        }

        
This method is invoked whenever the value in an entry is overwritten by an invocation of put(k,v) for a key k that's already in the HashMap.
        void recordAccess(KeyComparatorHashMap<K,V> m) {
        }

        
This method is invoked whenever the entry is removed from the table.
        void recordRemoval(KeyComparatorHashMap<K,V> m) {
        }
    }

    
Add a new entry with the specified key, value and hash code to the specified bucket. It is the responsibility of this method to resize the table if appropriate. Subclass overrides this to alter the behavior of put method.
    void addEntry(int hash, K key, V valueint bucketIndex) {
	Entry<K,V> e = [bucketIndex];
        [bucketIndex] = new Entry<K,V>(hashkeyvaluee);
        if (++ >= )
            resize(2 * .);
    }

    
Like addEntry except that this version is used when creating entries as part of Map construction or "pseudo-construction" (cloning, deserialization). This version needn't worry about resizing the table. Subclass overrides this to alter the behavior of HashMap(Map), clone, and readObject.
    void createEntry(int hash, K key, V valueint bucketIndex) {
	Entry<K,V> e = [bucketIndex];
        [bucketIndex] = new Entry<K,V>(hashkeyvaluee);
        ++;
    }
    private abstract class HashIterator<E> implements Iterator<E> {
        Entry<K,V> next;	// next entry to return
        int expectedModCount;	// For fast-fail 
        int index;		// current slot 
        Entry<K,V> current;	// current entry
        HashIterator() {
             = ;
            Entry<K,V>[] t = ;
            int i = t.length;
            Entry<K,V> n = null;
            if ( != 0) { // advance to first entry
                while (i > 0 && (n = t[--i]) == null)
                    ;
            }
             = n;
             = i;
        }
        public boolean hasNext() {
            return  != null;
        }
        Entry<K,V> nextEntry() { 
            if ( != )
                throw new ConcurrentModificationException();
            Entry<K,V> e = ;
            if (e == null
                throw new NoSuchElementException();
                
            Entry<K,V> n = e.next;
            Entry<K,V>[] t = ;
            int i = ;
            while (n == null && i > 0)
                n = t[--i];
             = i;
             = n;
            return  = e;
        }
        public void remove() {
            if ( == null)
                throw new IllegalStateException();
            if ( != )
                throw new ConcurrentModificationException();
            K k = .;
             = null;
            KeyComparatorHashMap.this.removeEntryForKey(k);
             = ;
        }
    }
    private class ValueIterator extends HashIterator<V> {
        public V next() {
            return nextEntry().;
        }
    }
    private class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }
    private class EntryIterator extends HashIterator<Map.Entry<K,V>> {
        public Map.Entry<K,V> next() {
            return nextEntry();
        }
    }
    // Subclass overrides these to alter behavior of views' iterator() method
    Iterator<K> newKeyIterator()   {
        return new KeyIterator();
    }
    Iterator<V> newValueIterator()   {
        return new ValueIterator();
    }
    Iterator<Map.Entry<K,V>> newEntryIterator()   {
        return new EntryIterator();
    }
    // Views
    private transient Set<Map.Entry<K,V>> entrySet = null;


    
Returns a collection view of the mappings contained in this map. Each element in the returned collection is a Map.Entry. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa. The collection supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Collection.remove, removeAll, retainAll, and clear operations. It does not support the add or addAll operations.

Returns:
a collection view of the mappings contained in this map.
    public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es = ;
        return (es != null ? es : ( = (Set<Map.Entry<K,V>>) (Setnew EntrySet()));
    }
    private class EntrySet extends AbstractSet/*<Map.Entry<K,V>>*/ {
        public Iterator/*<Map.Entry<K,V>>*/ iterator() {
            return newEntryIterator();
        }
        @Override
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<K,V> e = (Map.Entry<K,V>) o;
            Entry<K,V> candidate = getEntry(e.getKey());
            return candidate != null && candidate.equals(e);
        }
        @Override
        public boolean remove(Object o) {
            return removeMapping(o) != null;
        }
        public int size() {
            return ;
        }
        @Override
        public void clear() {
            KeyComparatorHashMap.this.clear();
        }
    }

    
Save the state of the HashMap instance to a stream (i.e., serialize it).

SerialData:
The capacity of the HashMap (the length of the bucket array) is emitted (int), followed by the size of the HashMap (the number of key-value mappings), followed by the key (Object) and value (Object) for each key-value mapping represented by the HashMap The key-value mappings are emitted in the order that they are returned by entrySet().iterator().
    private void writeObject(java.io.ObjectOutputStream s)
        throws IOException
    {
	// Write out the threshold, loadfactor, and any hidden stuff
	// Write out number of buckets
	// Write out size (number of Mappings)
        // Write out keys and values (alternating)
	while (i.hasNext()) { 
            Map.Entry<K,V> e = i.next();
            s.writeObject(e.getKey());
            s.writeObject(e.getValue());
        }
    }

    
Reconstitute the HashMap instance from a stream (i.e., deserialize it).
    private void readObject(java.io.ObjectInputStream s)
         throws IOExceptionClassNotFoundException
    {
	// Read in the threshold, loadfactor, and any hidden stuff
	// Read in number of buckets and allocate the bucket array;
	int numBuckets = s.readInt();
	 = new Entry[numBuckets];
        init();  // Give subclass a chance to do its thing.
	// Read in size (number of Mappings)
	int size = s.readInt();
	// Read the keys and values, and put the mappings in the HashMap
	for (int i=0; i<sizei++) {
	    K key = (K) s.readObject();
	    V value = (V) s.readObject();
	    putForCreate(keyvalue);
	}
    }
    // These methods are used when serializing HashSets
    int   capacity()     { return .; }
    float loadFactor()   { return ;   }
New to GrepCode? Check out our FAQ X