All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.util.StringTable Maven / Gradle / Ivy

There is a newer version: 24.03
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.tangosol.util;


import java.io.Serializable;

import java.text.Collator;
import java.text.CollationKey;

import java.util.Collection;
import java.util.Enumeration;
import java.util.Vector;


/**
* Like a hash table, but built specifically for strings.  Enumerates contents
* in order.
*
* @author Cameron Purdy
* @version 1.00 09/03/97
*/
final public class StringTable
        extends Base
        implements Cloneable, Serializable
    {
    // ----- constructors ---------------------------------------------------

    /**
    * Construct a string table using all defaults.
    */
    public StringTable()
        {
        tree = new Tree();
        }

    /**
    * Construct a string table using the specified collator.
    *
    * @param collator an instance of java.text.Collator to use to compare
    *        strings
    *
    * @see java.text.Collator
    */
    public StringTable(Collator collator)
        {
        this.tree     = new Tree();
        this.collator = collator;
        }

    /**
    * Construct a string table using the specified collator strength.
    *
    * @param nStrength a valid value for java.text.Collator
    *
    * @see java.text.Collator#setStrength
    * @see java.text.Collator#PRIMARY
    * @see java.text.Collator#SECONDARY
    * @see java.text.Collator#TERTIARY
    * @see java.text.Collator#IDENTICAL
    */
    public StringTable(int nStrength)
        {
        tree     = new Tree();
        collator = Collator.getInstance();
        collator.setStrength(nStrength);
        }


    // ----- StringTable interface ------------------------------------------

    /**
    * Add the passed string to the table.
    *
    * @param string the string to add to the table
    */
    public void add(String string)
        {
        tree.add(createKey(string));
        }

    /**
    * Add the passed string to the table and associate the passed value with
    * the string.  If the string is already in the table, the passed value
    * will replace the current value stored with the string.
    *
    * @param string the string to add to the table
    * @param value  the object to associate with the string
    */
    public void put(String string, Object value)
        {
        tree.put(createKey(string), value);
        }

    /**
    * Find the specified string in the table and return its value.
    *
    * @param string the string to look for in the table
    *
    * @return the associated value or null if the string is not in the table
    */
    public Object get(String string)
        {
        return tree.get(createKey(string));
        }

    /**
    * Determine if the passed string is in the table.
    *
    * @param string the string to look for in the table
    *
    * @return true if the string is in the table, false otherwise
    */
    public boolean contains(String string)
        {
        return tree.contains(createKey(string));
        }

    /**
    * Remove the specified string from the table, returning its associated value.
    *
    * @param string the string to look for in the table
    *
    * @return the associated value (which can be null) or null if the string is
    *         not in the table
    */
    public Object remove(String string)
        {
        return tree.remove(createKey(string));
        }

    /**
    * Remove all strings from the table.
    */
    public void clear()
        {
        tree.clear();
        }

    /**
    * Determine the number of strings in the table.
    *
    * @return the number of strings in the table
    */
    public int getSize()
        {
        return tree.getSize();
        }

    /**
    * Test for empty table.
    *
    * @return true if table has no strings
    */
    public boolean isEmpty()
        {
        return tree.isEmpty();
        }

    /**
    * Get the table's strings.
    *
    * @return an array of strings.
    */
    public String[] strings()
        {
        synchronized (tree)
            {
            int cNodes = tree.getSize();
            if (cNodes == 0)
                {
                return NO_STRINGS;
                }

            String[] asKey = new String[cNodes];

            int cStrings = 0;
            Enumeration enmr = tree.getUnsynchronizedKeyEnumerator();
            while (enmr.hasMoreElements())
                {
                asKey[cStrings++] = enmr.nextElement().toString();
                }

            if (cStrings != cNodes)
                {
                throw new IllegalStateException("iterated " + cStrings + " strings in a " + cNodes + "-entry table!");
                }

            return asKey;
            }
        }

    /**
    * Get the table's strings that start with a specific string.
    *
    * @return an array of strings.
    */
    public String[] stringsStartingWith(String prefix)
        {
        int cchPrefix = prefix.length();
        if (cchPrefix < 1)
            {
            return strings();
            }

        Vector strings = new Vector();
        synchronized (tree)
            {
            if (collator == null)
                {
                Enumeration enmr = tree.getUnsynchronizedKeyEnumerator(createKey(prefix));
                while (enmr.hasMoreElements())
                    {
                    String sKey = enmr.nextElement().toString();
                    if (sKey.startsWith(prefix))
                        {
                        strings.addElement(sKey);
                        }
                    else
                        {
                        break;
                        }
                    }
                }
            else
                {
                CollationKey ckPrefix = collator.getCollationKey(prefix);
                Enumeration enmr = tree.getUnsynchronizedKeyEnumerator(new CollatedKey(ckPrefix));
                while (enmr.hasMoreElements())
                    {
                    String sKey = enmr.nextElement().toString();
                    if (sKey.length() < cchPrefix)
                        {
                        break;
                        }

                    CollationKey ck = collator.getCollationKey(sKey.substring(0, cchPrefix));
                    if (ck.compareTo(ckPrefix) == 0)
                        {
                        strings.addElement(sKey);
                        }
                    else
                        {
                        break;
                        }
                    }
                }
            }

        int cStrings = strings.size();
        if (cStrings == 0)
            {
            return NO_STRINGS;
            }

        String[] asKey = new String[cStrings];
        strings.copyInto(asKey);
        return asKey;
        }

    /**
    * Enumerate the table's strings.
    *
    * @return an enumerator of the table's strings
    */
    public Enumeration keys()
        {
        if (isEmpty())
            {
            return NullImplementation.getEnumeration();
            }
        else
            {
            return new SimpleEnumerator(strings());
            }
        }

    /**
    * Create an enumerator for the values in the table.
    *
    * @return an enumerator of the table's values (in the same order that the
    *         strings were returned)
    */
    public Enumeration elements()
        {
        return tree.elements();
        }

    /**
    * Create a key for the specified string.
    *
    * @param sKey  the string to create a key for
    *
    * @return an orderable key
    */
    private Comparable createKey(String sKey)
        {
        return (collator == null ? (Comparable) sKey
                                 : new CollatedKey(collator.getCollationKey(sKey)));
        }

     /**
     * Adds all of the nodes in the specified StringTable to this StringTable if they are not
     * already present.  This operation effectively modifies this StringTable
     * so that its value is the union of the two StringTables.  The behavior
     * of this operation is unspecified if the specified StringTable is
     * modified while the operation is in progress.
     *
     * @return true if this StringTable changed as a result of the call.
     * @see java.util.Collection#addAll(Collection)
     */
    public boolean addAll(StringTable that)
        {
        return this.tree.addAll(that.tree);
        }

     /**
     * Puts all of the nodes in the specified StringTable to this StringTable including the ones
     * that are already present.  This operation effectively modifies this StringTable
     * so that its value is the union of the two StringTables.  The behavior
     * of this operation is unspecified if the specified StringTable is
     * modified while the operation is in progress.
     */
    public void putAll(StringTable that)
        {
        this.tree.putAll(that.tree);
        }

     /**
     * Retains only the nodes in this StringTable that are contained in the specified
     * StringTable.  In other words, removes from this StringTable all of its nodes that are not
     * contained in the specified StringTable.  This operation effectively modifies this
     * StringTable so that its value is the intersection of the two StringTables.
     *
     * @return true if this Collection changed as a result of the call.
     * @see java.util.Collection#retainAll(Collection)
     */
    public boolean retainAll(StringTable that)
        {
        return this.tree.retainAll(that.tree);
        }

    /**
     * Removes from this StringTable all of its nodes that are contained in the
     * specified StringTable.  This operation effectively modifies this StringTable so that
     * its value is the asymmetric set difference of the two StringTables.
     *
     * @return true if this StringTable changed as a result of the call.
     * @see java.util.Collection#removeAll(Collection)
     */
    public boolean removeAll(StringTable that)
        {
        return this.tree.removeAll(that.tree);
        }


    // ----- Object methods -------------------------------------------------

    /**
    * Provide a string representation of the string table.
    */
    public synchronized String toString()
        {
        return "StringTable("
            + (collator == null ? "" : collator.toString())
            + ','
            + tree.toString()
            + ")";
        }

    /**
    * Test for equality of two string tables.
    *
    * @param obj  the object to compare to
    *
    * @return true if the both objects are string tables with the same
    *         keys and elements
    */
    public boolean equals(Object obj)
        {
        if (this == obj)
            {
            return true;
            }

        if (!(obj instanceof StringTable))
            {
            return false;
            }

        StringTable that = (StringTable) obj;
        if (this.collator != that.collator)
            {
            if (this.collator == null || !this.collator.equals(that.collator))
                {
                return false;
                }
            }

        // to sync on two arbitrary objects in a non-arbitrary order, use the
        // jvm's hash code to order the two objects, lessening the chance for
        // arbitrary synchronization order to "1 in 4 billion"
        int nHashThis = System.identityHashCode(this);
        int nHashThat = System.identityHashCode(that);
        Object o1 = this;
        Object o2 = that;
        if (nHashThis > nHashThat)
            {
            o1 = that;
            o2 = this;
            }
        synchronized (o1)
            {
            synchronized (o2)
                {
                return this.tree.equals(that.tree);
                }
            }
        }

    /**
    * Test for equality of the keys in two string tables.
    *
    * @param that  the string table to compare to
    *
    * @return true if the both string tables have the same keys
    */
    public boolean keysEquals(StringTable that)
        {
        if (this == that)
            {
            return true;
            }

        Tree thisTree = this.tree;
        Tree thatTree = that.tree;
        if (thisTree.getSize() == thatTree.getSize())
            {
            if (thisTree.getSize() == 0)
                {
                return true;
                }
            }
        else
            {
            return false;
            }

        if (this.collator != that.collator)
            {
            if (this.collator == null || !this.collator.equals(that.collator))
                {
                return false;
                }
            }

        synchronized (this)
            {
            synchronized (that)
                {
                // perform an in-order traversal, comparing each node's keys
                Enumeration thisEnum = thisTree.getUnsynchronizedKeyEnumerator();
                Enumeration thatEnum = thatTree.getUnsynchronizedKeyEnumerator();

                while (thisEnum.hasMoreElements() && thatEnum.hasMoreElements())
                    {
                    if (!thisEnum.nextElement().equals(thatEnum.nextElement()))
                        {
                        return false;
                        }
                    }

                return (thisEnum.hasMoreElements() == thatEnum.hasMoreElements());
                }
            }
        }


    // ----- Cloneable interface --------------------------------------------

    /**
    * Make a new string table with a copy of the tree and a copy of the
    * collator.
    */
    public synchronized Object clone()
        {
        StringTable that = new StringTable();

        that.tree = (Tree) this.tree.clone();
        if (this.collator != null)
            {
            that.collator = (Collator) this.collator.clone();
            }

        return that;
        }


    // ----- internal -------------------------------------------------------

    /**
    * The name of this class.
    */
    private static final String CLASS = "StringTable";

    /**
    * Avoid allocating immutable array of strings.
    */
    private static final String[] NO_STRINGS = new String[0];

    /**
    * The string table delegates all storage details to the Tree class.
    */
    private Tree tree;

    /**
    * The string table delegates all ordering details to the Collator class.
    */
    private Collator collator;
    }


/**
* Local class CollatedKey is the orderable representation of the string.
* Using a collator optimization, the string itself is not the key value;
* rather, a CollationKey object is used.  A CollationKey is a sort of pre-
* processed equivalent of a string and the applicable collation rules,
* allowing comparisons to take place on the CollationKey directly without
* having to re-analyze the string each time.
*/
final class CollatedKey implements Comparable, Serializable
    {
    // ----- constructors -------------------------------------------

    /**
    * Create a key for the specified string.
    */
    public CollatedKey(CollationKey value)
        {
        this.value = value;
        }


    // ----- orderable interface ------------------------------------

    /**
    * Compare two keys for equality.
    *
    * @param obj a CollatedKey object to compare to
    *
    * @return true if this CollatedKey object and the other CollatedKey
    *         object are equal
    */
    public boolean equals(Object obj)
        {
        if (obj instanceof CollatedKey)
            {
            CollationKey otherValue = ((CollatedKey) obj).value;
            if (otherValue != lastValue)
                {
                lastValue  = otherValue;
                lastResult = value.compareTo(otherValue);
                }

            return (lastResult == 0);
            }

        return false;
        }

    /**
    * Compare two keys to determine which is ordered first.
    *
    * @param obj a CollatedKey object to compare to
    *
    * @return 0 if equal, <0 if this preceds that, >0 otherwise
    */
    public int compareTo(Object obj)
        {
        CollationKey otherValue = ((CollatedKey) obj).value;
        if (otherValue != lastValue)
            {
            lastValue  = otherValue;
            lastResult = value.compareTo(otherValue);
            }

        return lastResult;
        }


    // ----- Object methods -----------------------------------------

    /**
    * Get the string that this key represents.  This is the string that
    * was passed to the constructor.
    *
    * @return the string key used to construct this object
    */
    public String toString()
        {
        return value.getSourceString();
        }


    // ----- internal -----------------------------------------------

    /**
    * The name of this class.
    */
    private static final String CLASS = "CollatedKey";

    /**
    * The key value.  This is an immutable field.
    */
    private CollationKey value;

    /**
    * The last value that this key was compared to.
    */
    private CollationKey lastValue;

    /**
    * The result of the last comparison.
    */
    private int          lastResult;
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy