home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / Extras / OSpace / jgl.exe / jgl_2_0 / src / COM / objectspace / jgl / OrderedMap.java < prev    next >
Encoding:
Java Source  |  1997-03-14  |  16.8 KB  |  531 lines

  1. // Copyright(c) 1996,1997 ObjectSpace, Inc.
  2. // Portions Copyright(c) 1995, 1996 Hewlett-Packard Company.
  3.  
  4. package COM.objectspace.jgl;
  5.  
  6. import java.util.Enumeration;
  7. import java.io.ObjectInputStream;
  8. import java.io.ObjectOutputStream;
  9. import java.io.IOException;
  10.  
  11. /**
  12.  * An OrderedMap is an associative container that manages a set of ordered
  13.  * key/value pairs. The pairs are ordered by key, using a comparator. By
  14.  * default, a HashComparator is used, which orders keys based on their hash
  15.  * value. By default, only one value may be associated with a particular key.
  16.  * An OrderedMap's underlying data structure allows you to very efficiently
  17.  * find all of the values associated with a particular key.
  18.  * <p>
  19.  * An OrderedMap is useful for implementing a collection of one-to-one and
  20.  * one-to-many mappings.
  21.  * <p>
  22.  * Strictly speaking, there is no reason why null is not a valid key. However,
  23.  * most comparators (including the default HashComparator) will fail and throw
  24.  * an exception if you attempt to add a null key because they cast the key to
  25.  * a class and then activate one of its methods. It is perfectly possible to
  26.  * hand-craft a comparator that will accept null as a valid key.
  27.  * <p>
  28.  * There are many different approaches that could be used to implementing an
  29.  * associative container. For example, most of the older libraries used a
  30.  * hashing scheme for positioning and retrieving items. This implementation
  31.  * uses a data structure called a red-black tree. A red-black tree is a binary
  32.  * search tree that uses an extra field in every node to store the node's
  33.  * color. Red-black trees constrain the way that nodes may be colored in such a
  34.  * way that the tree remains reasonably balanced. This property is important
  35.  * for ensuring a good overall performance - red-black trees guarantee that the
  36.  * worst case performance for the most common dynamic set operations is
  37.  * O( log N ). One conseqence of using a binary tree for storage of data is
  38.  * the items remain in a sorted order. This allows JGL users to iterate through
  39.  * an associative container and access its elements in a sequenced manner. Each
  40.  * node of the red-black tree holds a Pair( key, value ). The comparator is
  41.  * used to order the Pairs based only on their keys.
  42.  * <p>
  43.  * Insertion does not affect iterators or references.
  44.  * <p>
  45.  * Removal only invalidates the iterators and references to the removed
  46.  * elements.
  47.  * <p>
  48.  * @see COM.objectspace.jgl.BinaryPredicate
  49.  * @see COM.objectspace.jgl.examples.OrderedMapExamples
  50.  * @version 2.0.2
  51.  * @author ObjectSpace, Inc.
  52.  */
  53.  
  54. public class OrderedMap extends Map
  55.   {
  56.   transient Tree myTree;
  57.  
  58.   /**
  59.    * Construct myself to be an empty OrderedMap that orders its keys based on
  60.    * their hash value and does not allow duplicates.
  61.    */
  62.   public OrderedMap()
  63.     {
  64.     myTree = new Tree( true, false, this );
  65.     }
  66.  
  67.   /**
  68.    * Construct myself to be an empty OrderedMap that orders its keys based on
  69.    * their hash value and conditionally allows duplicates.
  70.    * @param allowDuplicates true if duplicates are allowed.
  71.    */
  72.   public OrderedMap( boolean allowDuplicates )
  73.     {
  74.     myTree = new Tree( true, allowDuplicates, this );
  75.     }
  76.  
  77.  
  78.   /**
  79.    * Construct myself to be an empty OrderedMap that orders its keys using
  80.    * a specified binary predicate and does not allow duplicates.
  81.    * @param comparator The predicate for ordering keys.
  82.    */
  83.   public OrderedMap( BinaryPredicate comparator )
  84.     {
  85.     myTree = new Tree( true, false, comparator, this );
  86.     }
  87.  
  88.   /**
  89.    * Construct myself to be an empty OrderedMap that orders its keys using
  90.    * a specified binary predicate and conditionally allows duplicates.
  91.    * @param comparator The predicate for ordering keys.
  92.    * @param allowDuplicates true if duplicates are allowed.
  93.    */
  94.   public OrderedMap( BinaryPredicate comparator, boolean allowDuplicates )
  95.     {
  96.     myTree = new Tree( true, allowDuplicates, comparator, this );
  97.     }
  98.  
  99.   /**
  100.    * Construct myself to be a shallow copy of an existing OrderedMap.
  101.    * @param map The OrderedMap to copy.
  102.    */
  103.   public OrderedMap( OrderedMap map )
  104.     {
  105.     synchronized( map )
  106.       {
  107.       myTree = new Tree( map.myTree );
  108.       }
  109.     }
  110.  
  111.   /**
  112.    * Return true if duplicates are allowed.
  113.    */
  114.   public boolean allowsDuplicates()
  115.     {
  116.     return myTree.myInsertAlways;
  117.     }
  118.  
  119.   /**
  120.    * Return a shallow copy of myself.
  121.    */
  122.   public synchronized Object clone()
  123.     {
  124.     return new OrderedMap( this );
  125.     }
  126.  
  127.   /**
  128.    * Become a shallow copy of an existing OrderedMap.
  129.    * @param map The OrderedMap that I shall become a shallow copy of.
  130.    */
  131.   public synchronized void copy( OrderedMap map )
  132.     {
  133.     synchronized( map )
  134.       {
  135.       myTree.copy( map.myTree );
  136.       }
  137.     }
  138.  
  139.   /**
  140.    * Return a string that describes me.
  141.    */
  142.   public synchronized String toString()
  143.     {
  144.     return Printing.toString( this, "OrderedMap" );
  145.     }
  146.  
  147.   /**
  148.    * Return an Enumeration of my values
  149.    */
  150.   public synchronized Enumeration elements()
  151.     {
  152.     return myTree.beginMap( OrderedMapIterator.VALUE );
  153.     }
  154.  
  155.   /**
  156.    * Return an iterator positioned at my first pair.
  157.    */
  158.   public ForwardIterator start()
  159.     {
  160.     return begin();
  161.     }
  162.  
  163.   /**
  164.    * Return an iterator positioned immediately afer my last pair.
  165.    */
  166.   public ForwardIterator finish()
  167.     {
  168.     return end();
  169.     }
  170.  
  171.   /**
  172.    * Return an iterator positioned at my first pair.
  173.    */
  174.   public synchronized OrderedMapIterator begin()
  175.     {
  176.     return myTree.beginMap( OrderedMapIterator.PAIR );
  177.     }
  178.  
  179.   /**
  180.    * Return an iterator positioned immediately after my last pair.
  181.    */
  182.   public synchronized OrderedMapIterator end()
  183.     {
  184.     return myTree.endMap( OrderedMapIterator.PAIR );
  185.     }
  186.  
  187.   /**
  188.    * Return true if I contain no entries.
  189.    */
  190.   public boolean isEmpty()
  191.     {
  192.     return myTree.size == 0;
  193.     }
  194.  
  195.   /**
  196.    * Return the number of entries that I contain.
  197.    */
  198.   public int size()
  199.     {
  200.     return myTree.size;
  201.     }
  202.  
  203.   /**
  204.    * Return the maximum number of entries that I can contain.
  205.    */
  206.   public int maxSize()
  207.     {
  208.     return myTree.maxSize();
  209.     }
  210.  
  211.   /**
  212.    * Return true if I'm equal to another object.
  213.    * @param object The object to compare myself against.
  214.    */
  215.   public boolean equals( Object object )
  216.     {
  217.     return object instanceof OrderedMap && equals( (OrderedMap)object );
  218.     }
  219.  
  220.   /**
  221.    * Return true if I contain the same items in the same order as
  222.    * another OrderedMap. Use equals() to compare the individual elements.
  223.    * @param map The OrderedMap to compare myself against.
  224.    */
  225.   public synchronized boolean equals( OrderedMap map )
  226.     {
  227.     synchronized( map )
  228.       {
  229.       return Comparing.equal( this, map );
  230.       }
  231.     }
  232.  
  233.   /**
  234.    * Return my hash code for support of hashing containers
  235.    */
  236.   public synchronized int hashCode()
  237.     {
  238.     ForwardIterator start = myTree.beginMap( OrderedMapIterator.KEY );
  239.     ForwardIterator end = myTree.endMap( OrderedMapIterator.KEY );
  240.     return Hashing.orderedHash( start, end );
  241.     }
  242.  
  243.   /**
  244.    * Swap my contents with another OrderedMap.
  245.    * @param map The OrderedMap that I will swap my contents with.
  246.    */
  247.   public synchronized void swap( OrderedMap map )
  248.     {
  249.     synchronized( map )
  250.       {
  251.       Tree tmp = myTree;
  252.       myTree = map.myTree;
  253.       map.myTree = tmp;
  254.       }
  255.     }
  256.  
  257.   /**
  258.    * Remove all of my elements.
  259.    */
  260.   public synchronized void clear()
  261.     {
  262.     myTree.clear();
  263.     }
  264.  
  265.   /**
  266.    * If I contain key/value pair(s) that matches a particular key,
  267.    * remove them all and return the number of pairs removed.
  268.    * @param key The key of the pair(s) to be removed.
  269.    * @return The first object removed or null if the container is unchanged.
  270.    */
  271.   public synchronized Object remove( Object object )
  272.     {
  273.     return myTree.remove( object ).first;
  274.     }
  275.  
  276.   /**
  277.    * If I contain key/value pair(s) that matches a particular key,
  278.    * remove at most a given number and return the number of pairs removed.
  279.    * @param key The key of the pair(s) to be removed.
  280.    * @param count The maximum number of objects to remove.
  281.    * @return The number of pairs removed.
  282.    */
  283.   public synchronized int remove( Object key, int count )
  284.     {
  285.     Pair result = myTree.remove( key, count );
  286.     return ( (Number)result.second ).intValue();
  287.     }
  288.  
  289.   /**
  290.    * Remove the element at a particular position and return its value.
  291.    * @param pos An Enumeration positioned at the element to remove.
  292.    * @exception IllegalArgumentException is the Enumeration isn't an
  293.    * OrderedMapIterator for this OrderedMap object.
  294.    * @return The value that was removed or null if none.
  295.    */
  296.   public synchronized Object remove( Enumeration pos )
  297.     {
  298.     if ( ! (pos instanceof OrderedMapIterator) )
  299.       throw new IllegalArgumentException( "Enumeration not an OrderedMapIterator" );
  300.  
  301.     if ( ((OrderedMapIterator)pos).myOrderedMap != this )
  302.       throw new IllegalArgumentException( "Enumeration not for this OrderedMap" );
  303.  
  304.     Tree.TreeNode node = myTree.remove( ((OrderedMapIterator)pos).myNode );
  305.     return ( node == null ? null : node.object );
  306.     }
  307.  
  308.   /**
  309.    * Remove the elements within a specified range, returning the first value
  310.    * of the key in that range.
  311.    * @param first An iterator positioned at the first element to remove.
  312.    * @param last An iterator positioned immediately after the last element to remove.
  313.    * @exception IllegalArgumentException is the Enumeration isn't an
  314.    * OrderedMapIterator for this OrderedMap object.
  315.    * @return Return the number of pairs removed.
  316.    */
  317.   public synchronized int remove( Enumeration first, Enumeration last )
  318.     {
  319.     if ( ( ! (first instanceof OrderedMapIterator) ) ||
  320.         ( ! (last instanceof OrderedMapIterator) ) )
  321.       throw new IllegalArgumentException( "Enumeration not an OrderedMapIterator" );
  322.  
  323.     if ( ( ((OrderedMapIterator)first).myOrderedMap != this ) ||
  324.         ( ((OrderedMapIterator)last).myOrderedMap != this ) )
  325.       throw new IllegalArgumentException( "Enumeration not for this OrderedMap" );
  326.  
  327.     Pair result = myTree.remove( ((OrderedMapIterator)first).myNode,
  328.                           ((OrderedMapIterator)last).myNode );
  329.     return ( (Number)result.second ).intValue();
  330.     }
  331.  
  332.   /**
  333.    * Find a key/value pair based on its key and return its position.
  334.    * If the pair is not found, return end().
  335.    * @param object The object to locate.
  336.    */
  337.   public synchronized OrderedMapIterator find( Object key )
  338.     {
  339.     return new OrderedMapIterator( myTree, myTree.find( key ), this, OrderedMapIterator.PAIR );
  340.     }
  341.  
  342.   /**
  343.    * Return the number of key/value pairs that match a particular key.
  344.    * @param key The key to match against.
  345.    */
  346.   public synchronized int count( Object key )
  347.     {
  348.     return myTree.count( key );
  349.     }
  350.  
  351.   /**
  352.    * Return the number of values that match a given object.
  353.    * @param value The value to match against.
  354.    */
  355.   public synchronized int countValues( Object value )
  356.     {
  357.     return Counting.count( myTree.beginMap( OrderedMapIterator.VALUE ),
  358.                            myTree.endMap( OrderedMapIterator.VALUE ),
  359.                            value );
  360.     }
  361.  
  362.   /**
  363.    * Return an iterator positioned at the first location that a
  364.    * pair with a specified key could be inserted without violating the ordering
  365.    * criteria. If no such location is found, return an iterator positioned at end().
  366.    * @param key The key.
  367.    */
  368.   public synchronized OrderedMapIterator lowerBound( Object key )
  369.     {
  370.     return new OrderedMapIterator( myTree, myTree.lowerBound( key ), this, OrderedMapIterator.PAIR );
  371.     }
  372.  
  373.   /**
  374.    * Return an iterator positioned at the last location that
  375.    * a pair with a specified key could be inserted without violating the ordering
  376.    * criteria. If no such location is found, return an iterator positioned at end().
  377.    * @param key The key.
  378.    */
  379.   public synchronized OrderedMapIterator upperBound( Object key )
  380.     {
  381.     return new OrderedMapIterator( myTree, myTree.upperBound( key ), this, OrderedMapIterator.PAIR );
  382.     }
  383.  
  384.   /**
  385.    * Return a pair of iterators whose first element is equal to
  386.    * lowerBound() and whose second element is equal to upperBound().
  387.    * @param object The object whose bounds are to be found.
  388.    */
  389.   public synchronized Range equalRange( Object object )
  390.     {
  391.     Pair range = myTree.equalRange( object );
  392.     return new Range
  393.       (
  394.       new OrderedMapIterator( myTree, (Tree.TreeNode)range.first, this, OrderedMapIterator.PAIR ),
  395.       new OrderedMapIterator( myTree, (Tree.TreeNode)range.second, this, OrderedMapIterator.PAIR )
  396.       );
  397.     }
  398.  
  399.   /**
  400.    * Return my comparator.
  401.    */
  402.   public BinaryPredicate getComparator()
  403.     {
  404.     return myTree.myComparator;
  405.     }
  406.  
  407.   /**
  408.    * Return the value associated with key, or null if the key does not exist.
  409.    * @param key The key to search against.
  410.    */
  411.   public synchronized Object get( Object key )
  412.     {
  413.     return myTree.get( key );
  414.     }
  415.  
  416.   /**
  417.    * If the key doesn't exist, associate the value with the key and return null,
  418.    * otherwise replace the first value associated with the key and return the old value.
  419.    * @param key The key.
  420.    * @param value The value.
  421.    * @exception java.lang.NullPointerException If the key or value is null.
  422.    */
  423.   public synchronized Object put( Object key, Object value )
  424.     {
  425.     if ( key == null || value == null )
  426.       throw new NullPointerException();
  427.  
  428.     Tree.InsertResult result = myTree.put( new Pair( key, value ) );
  429.     if ( result.ok )
  430.       return null;
  431.  
  432.     Pair pair = (Pair)result.node.object;
  433.     Object previous = pair.second;
  434.     pair.second = value;
  435.     return previous;
  436.     }
  437.  
  438.   /**
  439.    * Assume that the specified object is a Pair whose first field is a key and whose
  440.    * second field is a value. If the key doesn't exist or duplicates are allowed,
  441.    * associate the value with the key and return null, otherwise don't modify the map and
  442.    * return the current value associated with the key.
  443.    * @param object The pair to add.
  444.    * @exception java.lang.IllegalArgumentException If the object is not a Pair
  445.    * @exception java.lang.NullPointerException If the object is null or if the first
  446.    * or second items in the pair are null.
  447.    */
  448.   public Object add( Object object )
  449.     {
  450.     if ( object == null )
  451.       throw new NullPointerException();
  452.  
  453.     if ( !(object instanceof Pair) )
  454.       throw new IllegalArgumentException( "object is not Pair" );
  455.  
  456.     if ( ((Pair)object).first == null || ((Pair)object).second == null )
  457.       throw new NullPointerException();
  458.  
  459.     Pair pair = (Pair) object;
  460.     return add( pair.first, pair.second );
  461.     }
  462.  
  463.   /**
  464.    * If the key doesn't exist or duplicates are allowed, associate the value with the
  465.    * key and return null, otherwise don't modify the map and return the current value
  466.    * associated with the key.
  467.    * @param key The key.
  468.    * @param value The value.
  469.    * @exception java.lang.NullPointerException If the key or value is null.
  470.    */
  471.   public synchronized Object add( Object key, Object value )
  472.     {
  473.     if ( key == null || value == null )
  474.       throw new NullPointerException();
  475.  
  476.     Tree.InsertResult result = myTree.insert( new Pair( key, value ) );
  477.     return result.ok ? null : ( (Pair)result.node.object).second;
  478.     }
  479.  
  480.   /**
  481.    * Return an Enumeration of all my keys.
  482.    */
  483.   public synchronized Enumeration keys()
  484.     {
  485.     return myTree.beginMap( OrderedMapIterator.KEY );
  486.     }
  487.  
  488.   /**
  489.    * Return an Enumeration of all my keys that are associated with a particular value.
  490.    * @param value The value to match.
  491.    */
  492.   public synchronized Enumeration keys( Object value )
  493.     {
  494.     return myTree.keys( value ).elements();
  495.     }
  496.  
  497.   /**
  498.    * Return an Enumeration of all my values that are associated with a particular key.
  499.    * @param key The key to match.
  500.    */
  501.   public synchronized Enumeration values( Object key )
  502.     {
  503.     return myTree.values( key ).elements();
  504.     }
  505.  
  506.   private synchronized void writeObject( ObjectOutputStream stream ) throws IOException
  507.     {
  508.     stream.defaultWriteObject();
  509.  
  510.     stream.writeBoolean( allowsDuplicates() );
  511.     stream.writeObject( getComparator() );
  512.  
  513.     stream.writeInt( size() );
  514.     Copying.copy( begin(), end(), new ObjectOutputStreamIterator( stream ) );
  515.     }
  516.  
  517.   private void readObject( ObjectInputStream stream ) throws IOException, ClassNotFoundException
  518.     {
  519.     stream.defaultReadObject();
  520.  
  521.     boolean allowDuplicates = stream.readBoolean();
  522.     BinaryPredicate comparator = (BinaryPredicate)stream.readObject();
  523.  
  524.     myTree = new Tree( true, allowDuplicates, comparator, this );
  525.  
  526.     int count = stream.readInt();
  527.     while ( count-- > 0 )
  528.       add( stream.readObject() );
  529.     }
  530.   }
  531.