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

soot.util.HashChain Maven / Gradle / Ivy

There is a newer version: 1.12.0
Show newest version
/* Soot - a J*va Optimization Framework
 * Copyright (C) 1999 Patrice Pominville
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the Sable Research Group and others 1997-1999.  
 * See the 'credits' file distributed with Soot for the complete list of
 * contributors.  (Soot is distributed at http://www.sable.mcgill.ca/soot)
 */


package soot.util;

import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

/** Reference implementation of the Chain interface, 
    using a HashMap as the underlying structure. */
public class HashChain extends AbstractCollection
    implements Chain 
{
    private final HashMap map = new HashMap(); 
    private E firstItem;
    private E lastItem;
    private long stateCount = 0;  
    private HashMap objectIndexes = null; // RoboVM note: Added

    /**
     * Lazily creates the objectIndexes Map.
     * RoboVM note: Added in RoboVM.
     */
    private HashMap getObjectIndexes() {
        if (objectIndexes == null) {
            Object[] array = toArray();
            objectIndexes = new HashMap<>(array.length);
            for (int i = 0; i < array.length; i++) {
                objectIndexes.put((E) array[i], i);
            }
        }
        return objectIndexes;
    }

    /** Erases the contents of the current HashChain. */
    public void clear() 
    {
        stateCount++;
        firstItem = lastItem = null;
        map.clear();
        objectIndexes = null; // RoboVM note: Added
    }

    public void swapWith(E out, E in)
    {
        insertBefore(in, out);
        remove(out);
        objectIndexes = null; // RoboVM note: Added
    }
    
    /** Adds the given object to this HashChain. */
    public boolean add(E item) 
    {
        addLast(item);
        return true;
    }

    /** Returns an unbacked list containing the contents of the given Chain. */
    public static List toList(Chain c)
    {
        Iterator it = c.iterator();
        List list = new ArrayList();
        
        while(it.hasNext()) {
            list.add(it.next());
        }
        
        return list;
    }

    /** Constructs an empty HashChain. */
    public HashChain()
    { 
        firstItem = lastItem = null;
    }

    /** Constructs a HashChain filled with the contents of the src Chain. */
    public HashChain(Chain src) {
      this();

      for (E e : src) {
        add(e);
      }
    }

    public boolean follows(E someObject, E someReferenceObject)
    {
        // RoboVM note: Optimized using objectIndexes Map
        if (someObject == null) {
            return true;
        }
        Integer idx1 = getObjectIndexes().get(someObject);
        if (idx1 == null) {
            throw new NoSuchElementException("HashChain.follows(...) with object that is not in the chain: " + someObject.toString() );
        }
        Integer idx2 = getObjectIndexes().get(someReferenceObject);
        if (idx2 == null) {
            return true;
        }
        return idx1 > idx2;
    }

    // RoboVM note: Kept the old follows() to be able to test against the new version.
    public boolean followsOld(E someObject, E someReferenceObject)
    {
        Iterator it = iterator(someObject);
        while(it.hasNext()) {

            if(it.next() == someReferenceObject)
                return false;
        }
        return true;
    }
    
    public boolean contains(Object o)
    {
        return map.containsKey(o);
    }

    public boolean containsAll(Collection c)
    {
        Iterator it = c.iterator();
        while (it.hasNext())
            if (!(map.containsKey(it.next())))
                return false;
        
        return true;
    }
    
    public void insertAfter(E toInsert, E point)
    {
        if (toInsert == null)
            throw new RuntimeException("Bad idea! You tried to insert "
                                       + " a null object into a Chain!");

        if(map.containsKey(toInsert))
            throw new RuntimeException("Chain already contains object.");
        stateCount++;
        Link temp = map.get(point);
        
        Link newLink = temp.insertAfter(toInsert);
        map.put(toInsert, newLink);    
        objectIndexes = null; // RoboVM note: Added
    }

    public void insertAfter(List toInsert, E point)
    {
        // if the list is null, treat it as an empty list
        if (toInsert == null)
            throw new RuntimeException("Warning! You tried to insert "
                                       + "a null list into a Chain!");            

        E previousPoint = point;
        Iterator it = toInsert.iterator();
        while (it.hasNext())
            {
                E o = it.next();
                insertAfter(o, previousPoint);
                previousPoint = o;
            }
        objectIndexes = null; // RoboVM note: Added
    }
    
    public void insertAfter(Chain toInsert, E point)
    {
        // if the list is null, treat it as an empty list
        if (toInsert == null)
            throw new RuntimeException("Warning! You tried to insert "
                                       + "a null list into a Chain!");            

        E previousPoint = point;
        Iterator it = toInsert.iterator();
        while (it.hasNext())
            {
                E o = it.next();
                insertAfter(o, previousPoint);
                previousPoint = o;
            }
        objectIndexes = null; // RoboVM note: Added
    }

    public void insertBefore(E toInsert, E point)
    {
        if (toInsert == null)
            throw new RuntimeException("Bad idea! You tried to insert "
                                       + "a null object into a Chain!");

        if(map.containsKey(toInsert))
            throw new RuntimeException("Chain already contains object.");
        Link temp = map.get(point);
        if(temp==null) {
        	throw new RuntimeException("Insertion point not found in chain!");
        }
        stateCount++;
        
        Link newLink = temp.insertBefore(toInsert);
        map.put(toInsert, newLink);
        objectIndexes = null; // RoboVM note: Added
    }
    
    public void insertBefore(List toInsert, E point)
    {
        // if the list is null, treat it as an empty list
        if (toInsert == null)
            throw new RuntimeException("Warning! You tried to insert "
                                       + "a null list into a Chain!");

        Iterator it = toInsert.iterator();
        while (it.hasNext())
            {
                E o = it.next();
                insertBefore(o, point);
            }
        objectIndexes = null; // RoboVM note: Added
    }

    public void insertBefore(Chain toInsert, E point)
    {
        // if the list is null, treat it as an empty list
        if (toInsert == null)
            throw new RuntimeException("Warning! You tried to insert "
                                       + "a null list into a Chain!");

        Iterator it = toInsert.iterator();
        while (it.hasNext())
            {
                E o = it.next();
                insertBefore(o, point);
            }
        objectIndexes = null; // RoboVM note: Added
    }

    public static HashChain listToHashChain(List list) {
        HashChain c = new HashChain();
        Iterator it = list.iterator();
        while (it.hasNext()) 
            c.addLast(it.next());
        return c;
    }
  
    public boolean remove(Object item)
    { 
        if (item == null)
            throw new RuntimeException("Bad idea! You tried to remove "
                                       + " a null object from a Chain!");

        stateCount++;
        /*
         * 4th April 2005 Nomair A Naeem
         * map.get(obj) can return null
         * only return true if this is non null
         * else return false
         */
        if(map.get(item) != null){
        	Link link = map.get(item);
        
        	link.unlinkSelf();
        	map.remove(item);
                objectIndexes = null; // RoboVM note: Added
        	return true;
        }
        return false;
    }

    public void addFirst(E item)
    {
        if (item == null)
            throw new RuntimeException("Bad idea!  You tried to insert "
                                       + "a null object into a Chain!");

        stateCount++;
        Link newLink, temp;

        if(map.containsKey(item))
            throw new RuntimeException("Chain already contains object.");

        if(firstItem != null) {
            temp = map.get(firstItem);
            newLink = temp.insertBefore(item);
        } else {
            newLink = new Link(item);
            firstItem = lastItem = item;
        }
        map.put(item, newLink);
        objectIndexes = null; // RoboVM note: Added
    }

    public void addLast(E item)
    {
        if (item == null)
            throw new RuntimeException("Bad idea! You tried to insert "
                                       + " a null object into a Chain!");

        stateCount++;
        Link newLink, temp;
        if(map.containsKey(item))
            throw new RuntimeException("Chain already contains object: " 
                                       + item);
        
        if(lastItem != null) {
            temp = map.get(lastItem);
            newLink = temp.insertAfter(item);   
        } else {
            newLink = new Link(item);
            firstItem = lastItem = item;
        }            
        map.put(item, newLink);
        objectIndexes = null; // RoboVM note: Added
    }
    
    public void removeFirst()
    {
        stateCount++;
        Object item = firstItem;
        map.get(firstItem).unlinkSelf();
        map.remove(item);
        objectIndexes = null; // RoboVM note: Added
    }

    public void removeLast()
    {
        stateCount++;
        Object item = lastItem;
        map.get(lastItem).unlinkSelf();
        map.remove(item);
        objectIndexes = null; // RoboVM note: Added
    }

    public E getFirst()
    {         
        if(firstItem == null) 
            throw new NoSuchElementException();
        return firstItem; 
    }
    
    public E getLast()
    { 
        if(lastItem == null) 
            throw new NoSuchElementException();
        return lastItem; 
    }

    public E getSuccOf(E point)
        throws NoSuchElementException
    {
        Link link = map.get(point);
        try {
            link = link.getNext();
        }
        catch (NullPointerException e) {
            throw new NoSuchElementException();
        }
        if(link == null) 
            return null;
        
        return link.getItem();
    } 
    
    public E getPredOf(E point)
        throws NoSuchElementException
    {
        Link link = map.get(point);
        if(point == null)
            throw new RuntimeException("trying to hash null value.");

        try {
            link = link.getPrevious();
        }
        catch (NullPointerException e) {
            throw new NoSuchElementException();
        }
        
        if(link == null) 
            return null;
        else
            return link.getItem();
    } 
    
    public Iterator snapshotIterator() 
    {
	List l = new ArrayList( map.size()); 
	
	l.addAll(this);

        return l.iterator();
    }
   
    public Iterator snapshotIterator( Object item)
    {
        List l = new ArrayList( map.size());
	
        Iterator it = new LinkIterator( item);
        while (it.hasNext())
            l.add( it.next());
	
        return l.iterator();
    }

    public Iterator iterator(){ return new LinkIterator(firstItem); }
    public Iterator iterator(E item) 
    {
        return new LinkIterator(item);
    }

    /** 

Returns an iterator ranging from head to * tail, inclusive.

If tail is the element immediately preceding head in this HashChain, the returned iterator will iterate 0 times (a special case to allow the specification of an empty range of elements). Otherwise if tail is not one of the elements following head, the returned iterator will iterate past the end of the HashChain, provoking a {@link NoSuchElementException}.

@throws NoSuchElementException if head is not an element of the chain. */ public Iterator iterator(E head, E tail) { if (head != null && this.getPredOf(head) == tail) { // special case hack, so empty ranges iterate 0 times return new LinkIterator(null, null); } else { return new LinkIterator(head, tail); } } public int size(){ return map.size(); } /** Returns a textual representation of the contents of this Chain. */ public String toString() { StringBuffer strBuf = new StringBuffer(); Iterator it = iterator(); boolean b = false; strBuf.append("["); while(it.hasNext()) { if (!b) b = true; else strBuf.append(", "); strBuf.append(it.next().toString()); } strBuf.append("]"); return strBuf.toString(); } class Link implements Serializable { private Link nextLink; private Link previousLink; private X item; public Link(X item) { this.item = item; nextLink = previousLink = null; } public Link getNext() { return nextLink;} public Link getPrevious() { return previousLink;} public void setNext(Link link) { this.nextLink = link;} public void setPrevious(Link link) { this.previousLink = link;} public void unlinkSelf() { bind(previousLink, nextLink); } public Link insertAfter(X item) { Link newLink = new Link(item); bind(newLink, nextLink); bind(this, newLink); return newLink; } public Link insertBefore(X item) { Link newLink = new Link(item); bind(previousLink, newLink); bind(newLink, this); return newLink; } private void bind(Link a, Link b) { if(a == null) { if(b != null) firstItem = b.getItem(); else firstItem = null; } else a.setNext(b); if(b == null){ if(a != null) lastItem = a.getItem(); else lastItem = null; } else b.setPrevious(a); } public X getItem() { return item; } public String toString() { if(item != null) return item.toString(); else return "Link item is null" + super.toString(); } } class LinkIterator implements Iterator { private Link currentLink; boolean state; // only when this is true can remove() be called // (in accordance w/ iterator semantics) private X destination; private long iteratorStateCount; public LinkIterator(X item) { Link nextLink = map.get(item); if (nextLink == null && item != null) throw new NoSuchElementException("HashChain.LinkIterator(obj) with obj that is not in the chain: " + item.toString() ); currentLink = new Link(null); currentLink.setNext(nextLink); state = false; destination = null; iteratorStateCount = stateCount; } public LinkIterator(X from, X to) { this(from); destination = to; } public boolean hasNext() { if(stateCount != iteratorStateCount) { throw new ConcurrentModificationException(); } if(destination == null) return (currentLink.getNext() != null); else // Ignore whether (currentLink.getNext() == null), so // next() will produce a NoSuchElementException if // destination is not in the chain. return (destination != currentLink.getItem()); } public X next() throws NoSuchElementException { if(stateCount != iteratorStateCount) throw new ConcurrentModificationException(); Link temp = currentLink.getNext(); if(temp == null) { String exceptionMsg; if(destination != null && destination != currentLink.getItem()) exceptionMsg = "HashChain.LinkIterator.next() reached end of chain without reaching specified tail unit"; else exceptionMsg = "HashChain.LinkIterator.next() called past the end of the Chain"; throw new NoSuchElementException(exceptionMsg); } currentLink = temp; state = true; return currentLink.getItem(); } public void remove() throws IllegalStateException { if(stateCount != iteratorStateCount) throw new ConcurrentModificationException(); stateCount++; iteratorStateCount++; if(!state) throw new IllegalStateException(); else { currentLink.unlinkSelf(); map.remove(currentLink.getItem()); state = false; HashChain.this.objectIndexes = null; // RoboVM note: Added } } public String toString() { if(currentLink == null) return "Current object under iterator is null" + super.toString(); else return currentLink.toString(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy