alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */


package org.openide.util.lookup;

import java.io.*;
import java.lang.ref.WeakReference;
import java.util.*;

import org.openide.util.Lookup;

/** ArrayStorage of Pairs from AbstractLookup.
 * @author  Jaroslav Tulach
 */
final class ArrayStorage extends Object implements Comparator, Serializable, AbstractLookup.Storage {
    private static final long serialVersionUID = 543532453221L;
    
    /** default trashold */
    static final Integer DEFAULT_TRASH = new Integer (11);
    
    /** list of items */
    private Object content;
    /** linked list of refernces to results */
    private transient AbstractLookup.ReferenceToResult results;
    
    /** Constructor
     */
    public ArrayStorage() {
        this (DEFAULT_TRASH);
    }
    
    /** Constructs new ArrayStorage */
    public ArrayStorage (Integer treshhold) {
        this.content = treshhold;
    }

    /* JST-PENDING:
    private void writeObject (ObjectOutputStream oos) throws IOException {
        oos.writeObject(object);
        Iterator it = interfaces.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry e = (Map.Entry)it.next();
            Class c = (Class)e.getKey();
            oos.writeObject(c.getName());
            Object o = e.getValue();
            if (!(o instanceof Collection) && !(o instanceof AbstractLookup.Pair)) throw new ClassCastException(String.valueOf(o));
            oos.writeObject(o);
        }
        oos.writeObject(null);
    }

    private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException {
        object = (Node)ois.readObject();
        interfaces = new WeakHashMap();
        String clazz;
        ClassLoader l = (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class);
        while ((clazz = (String)ois.readObject()) != null) {
            Object o = ois.readObject();
            if (!(o instanceof Collection) && !(o instanceof AbstractLookup.Pair)) throw new ClassCastException(String.valueOf(o));
            Class c = Class.forName(clazz, false, l);
            interfaces.put(c, o);
        }
    }
    */

    /** Adds an item into the tree.
    * @param item to add
    * @return true if the Item has been added for the first time or false if some other
    *    item equal to this one already existed in the lookup
    */
    public boolean add(AbstractLookup.Pair item, Object transaction) {
        Transaction changed = (Transaction)transaction;
        Object[] arr = changed.current;
        
        if (changed.arr == null) {
            // just simple add of one item
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] == null) {
                    arr[i] = item;
                    changed.add (item);
                    return true;
                }
                if (arr[i].equals (item)) {
                    // reassign the item number
                    item.setIndex (null, ((AbstractLookup.Pair)arr[i]).getIndex ());
                    // already there, but update it
                    arr[i] = item;
                    return false;
                }
            }

            // cannot happen as the beginTransaction ensured we can finish 
            // correctly
            throw new IllegalStateException ();
        } else {
            // doing remainAll after that, let Transaction hold the new array
            int newIndex = changed.addPair (item);
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] == null) {
                    changed.add (item);
                    return true;
                }
                if (arr[i].equals (item)) {
                    // already there
                    if (i != newIndex) {
                        // change in index
                        changed.add (item);
                        return false;
                    } else {
                        // no change
                        return false;
                    }
                }
            }
            // if not found in the original array
            changed.add (item);
            return true;
        }
    }
 
    /** Removes an item.
    */
    public void remove(AbstractLookup.Pair item, Object transaction) {
        Transaction changed = (Transaction)transaction;
        Object[] arr = changed.current;
        
        int found = -1;
        for (int i = 0; i < arr.length; ) {
            if (arr[i] == null) {
                // end of task
                return;
            }
            
            if (found == -1 && arr[i].equals (item)) {
                // already there
                ((AbstractLookup.Pair)arr[i]).setIndex (null, -1);
                changed.add (arr[i]);
                found = i;
            }
            
            i++;
            
            if (found != -1) {
                if (i < arr.length) {
                    // moving the array
                    arr[i - 1] = arr[i];
                } else {
                    arr[i - 1] = null;
                }
            }
        }
    }

    /** Removes all items that are not present in the provided collection.
    * @param retain Pair -> AbstractLookup.Info map
    * @param notify set of Classes that has possibly changed
    */
    public void retainAll(Map retain, Object transaction) {
        Transaction changed = (Transaction)transaction;
        Object[] arr = changed.current;

        for (int from = 0; from < arr.length; from++) {
            if (! (arr[from] instanceof AbstractLookup.Pair)) {
                // end of content
                break;
            }
            AbstractLookup.Pair p = (AbstractLookup.Pair)arr[from];
            
            AbstractLookup.Info info = (AbstractLookup.Info)retain.get (p);
            if (info == null) {
                // was removed
                /*
            if (info != null) {
                if (info.index < arr.length) {
                    newArr[info.index] = p;
                }
                
                if (p.getIndex() != info.index) {
                    p.setIndex (null, info.index);
                    changed.add (p);
                }
            } else {
                // removed
                 */
                changed.add (p);
            }
        }
    }


    /** Queries for instances of given class.
    * @param clazz the class to check
    * @return enumeration of Item
    * @see #unsorted
    */
    public Enumeration lookup (final Class clazz) {
        class CheckEn implements org.openide.util.Enumerations.Processor {
            public Object process (Object o, Collection ignore) {
                boolean ok;
                if (o instanceof AbstractLookup.Pair) {
                    ok = clazz == null || ((AbstractLookup.Pair)o).instanceOf(clazz);
                } else {
                    ok = false;
                }
                return ok ? o : null;
            }
        }
        
        if (content instanceof Object[]) {
            Enumeration all = org.openide.util.Enumerations.array (
                (Object[])content
            );
            return org.openide.util.Enumerations.filter (all, new CheckEn ());
        } else {
            return org.openide.util.Enumerations.empty();
        }
    }

    //
    // Implementation of comparator for AbstractLookup.Pair
    //
    
    
    /** Compares two items.
    */
    public int compare(Object obj, Object obj1) {
        AbstractLookup.Pair i1 = (AbstractLookup.Pair)obj;
        AbstractLookup.Pair i2 = (AbstractLookup.Pair)obj1;

        int result = i1.getIndex() - i2.getIndex();
        if (result == 0) {
            if (i1 != i2) {
                java.io.ByteArrayOutputStream bs = new java.io.ByteArrayOutputStream ();
                java.io.PrintStream ps = new java.io.PrintStream (bs);

                ps.println (
                    "Please report this exception as issue http://www.netbeans.org/issues/show_bug.cgi?id=13779 " + // NOI18N
                    "Pair1: " + i1 + " pair2: " + i2 + " index1: " + i1.getIndex() + " index2: " + i2.getIndex() // NOI18N
                    + " item1: " + i1.getInstance () + " item2: " + i2.getInstance () // NOI18N
                    + " id1: " + Integer.toHexString (System.identityHashCode (i1)) // NOI18N
                    + " id2: " + Integer.toHexString (System.identityHashCode (i2)) // NOI18N
                );

                //print (ps, false);
                ps.close ();

                throw new IllegalStateException (bs.toString ());
            }
            return 0;
        }

        return result;
    }
    
    /** Associates another result with this storage.
     */
    public AbstractLookup.ReferenceToResult registerReferenceToResult(AbstractLookup.ReferenceToResult newRef) {
        AbstractLookup.ReferenceToResult prev = this.results;
        this.results = newRef;
        return prev;
    }
    
    /** Cleanup the references
     */
    public AbstractLookup.ReferenceToResult cleanUpResult(Lookup.Template templ) {
        AbstractLookup.ReferenceIterator it = new AbstractLookup.ReferenceIterator (this.results);
        while (it.next());
        return this.results = it.first ();
    }
    
    /** We use a hash set of all modified Pair to handle the transaction */
    public Object beginTransaction(int ensure) {
        return new Transaction (ensure, content);
    }
    
    /** Extract all results.
     */
    public void endTransaction(Object transaction, Set modified) {
        Transaction changed = (Transaction)transaction;
        
        AbstractLookup.ReferenceIterator it = new AbstractLookup.ReferenceIterator (this.results);
        if (changed.arr == null) {
            // either add or remove, only check the content of check HashSet
            while (it.next()) {
                AbstractLookup.ReferenceToResult ref = it.current ();
                Iterator pairs = changed.iterator();
                while (pairs.hasNext()) {
                    AbstractLookup.Pair p = (AbstractLookup.Pair)pairs.next ();
                    if (AbstractLookup.matches (ref.template, p, true)) {
                        modified.add (ref.getResult());
                    }
                }
            }
        } else {
            // do full check of changes
            while (it.next()) {
                AbstractLookup.ReferenceToResult ref = it.current ();
                
                int oldIndex = -1;
                int newIndex = -1;
                
                for (;;) {
                    oldIndex = findMatching (ref.template, changed.current, oldIndex);
                    newIndex = findMatching (ref.template, changed.arr, newIndex);
                    if (oldIndex == -1 && newIndex == -1) {
                        break;
                    }
                    if (
                        oldIndex == -1 || 
                        newIndex == -1 ||
                        !changed.current[oldIndex].equals (changed.arr[newIndex])
                    ) {
                        modified.add (ref.getResult ());
                        break;
                    }
                }
            }
        }
        this.results = it.first ();
        this.content = changed.newContent ();
    }
    
    private static int findMatching (Lookup.Template t, Object[] arr, int from) {
        while (++from < arr.length) {
            if (arr[from] instanceof AbstractLookup.Pair) {
                if (AbstractLookup.matches(t, (AbstractLookup.Pair)arr[from], true)) {
                    return from;
                }
            }
        }
        return -1;
    }

    
    /** HashSet with additional field for new array which is callocated
     * in case we are doing replace to hold all new items.
     */
    private static final class Transaction extends HashSet {
        /** array with current objects */
        public final Object[] current;
        /** array with new objects */
        public final Object[] arr;
        /** number of objects in the array */
        private int cnt;
        
        
        public Transaction (int ensure, Object currentContent) {
            Integer trashold;
            Object[] arr;
            if (currentContent instanceof Integer) {
                trashold = (Integer)currentContent;
                arr = null;
            } else {
                arr = (Object[])currentContent;
                if (arr[arr.length - 1] instanceof Integer) {
                    trashold = (Integer)arr[arr.length - 1];
                } else {
                    // nowhere to grow we have reached the limit
                    trashold = null;
                }
            }
            
            int maxSize = trashold == null ? arr.length : trashold.intValue();
            
            if (ensure > maxSize) {
                throw new UnsupportedOperationException ();
            }
            if (ensure == -1) {
                // remove => it is ok
                this.current = (Object[])currentContent;
                this.arr = null;
                return;
            }
            
            if (ensure == -2) {
                // adding one
                if (arr == null) {
                    // first time add, let's allocate the array
                    arr = new Object[2];
                    arr[1] = trashold;
                } else {
                    if (arr[arr.length - 1] instanceof AbstractLookup.Pair) {
                        // we are full
                        throw new UnsupportedOperationException ();
                    } else {
                        // ensure we have allocated enough space
                        if (arr[arr.length - 2] != null) {
                            // double the array
                            int newSize = (arr.length - 1) * 2;
                            if (newSize > maxSize) {
                                newSize = maxSize;
                                if (newSize <= arr.length) {
                                    // no space to get in
                                    throw new UnsupportedOperationException ();
                                }
                                arr = new Object[newSize];
                            } else {
                                // still a lot of space
                                arr = new Object[newSize + 1];
                                arr[newSize] = trashold;
                            }
                            // copy content of original array without the last Integer into 
                            // the new one
                            System.arraycopy(currentContent, 0, arr, 0, ((Object[])currentContent).length - 1);
                        }
                    }
                }
                this.current = arr; 
                this.arr = null;
            } else {
                // allocate array for complete replacement
                if (ensure == maxSize) {
                    this.arr = new Object[ensure];
                } else {
                    this.arr = new Object[ensure + 1];
                    this.arr[ensure] = trashold;
                }
                this.current = currentContent instanceof Object[] ? (Object[])currentContent : new Object[0];
            }
        }
        
        public int addPair (AbstractLookup.Pair p) {
            p.setIndex (null, cnt);
            arr[cnt++] = p;
            return p.getIndex ();
        }
        
        public Object newContent () {
            return arr == null ? current : arr;
        }
    } // end of Transaction
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.