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-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
package org.netbeans.modules.java.ui.nodes.elements;

import org.openide.nodes.Node;
import org.openide.nodes.Children;
import org.openide.util.RequestProcessor;
import org.openide.util.Mutex;
import org.openide.ErrorManager;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.netbeans.modules.javacore.JMManager;

import javax.jmi.reflect.JmiException;
import javax.jmi.reflect.InvalidObjectException;
import java.util.*;

/**
 * provides children (jmi elements and their nodes) in async way. It should
 * be used if you cannot create keys and nodes in the AWT event queue. 
 */
final class ChildrenProvider implements Runnable {
        
    /**
     * computes elements (children), creates their nodes, presents them.
     */ 
    interface KeyHandler {
        /**
         * subclasses implement this to supply child keys
         * @return keys
         */ 
        List collectKeys();
        
        /**
         * creates nodes for a given key. The method is invoked inside
         * Children.MUTEX and MDR transaction.
         * @param key the key
         * @return child nodes for this key or null if there should be no 
         *         nodes for this key
         */ 
        Node[] prepareNodes(Object key);
        
        /**
         * subclasses implement this to add nodes to Children.
         * Children.Key.setKeys() will be most often implementation.
         * @param keys child keys
         * @param nodes list of nodes corresponding to keys
         */ 
        void presentKeys(List/**/ keys, List/**/ nodes);
    }
        
    /**
     * processor computing keys and their nodes to not block
     * the awt event queue
     */ 
    private static final RequestProcessor RP = new RequestProcessor("Java Children Provider"); // NOI18N
        
    private RequestProcessor.Task currentTask;
        
    private Map/**/ nodes = initNodeMap();
    
    /** keys */
    private List elements;
        
    private final KeyHandler keyHandler;

    public ChildrenProvider(KeyHandler keyHandler) {
        this.keyHandler = keyHandler;
    }

    public void run() {
        List/**/ nodesNew;
        try {
            keys = keyHandler.collectKeys();
            nodesNew = createNodeMap(nodes, keys);
        } catch (InvalidObjectException e) {
            // some element is invalid. MDR will notify listeners about that change later
            keys = Collections.EMPTY_LIST;
            nodesNew = Collections.EMPTY_MAP;
        } catch (JmiException e) {
            keys = Collections.EMPTY_LIST;
            nodesNew = Collections.EMPTY_MAP;
            ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
        }

        synchronized(this) {
            elements = keys;
            nodes = nodesNew;
        }
        presentKeys(keys, nodesNew);
    }

    private void presentKeys(final List keys, final Map nodesNew) {
        List nodes = new ArrayList(keys.size());
        for (Iterator it = keys.iterator(); it.hasNext();) {
            Object key = it.next();
            Node[] ns = (Node[]) nodesNew.get(key);
            nodes.add(ns);
            
        }
        keyHandler.presentKeys(keys, nodes);
    }

    private Map/**/ initNodeMap() {
        Map/**/ nodesNew = new WeakHashMap/**/();
        return nodesNew;
    }
        
    /**
     * non-blocking operation that recomputes keys and update their nodes
     */ 
    public synchronized void recomputeChildren() {
        if (currentTask == null) {
            currentTask = RP.post(this);
        } else {
            currentTask.schedule(0);
        }
    }
    
    /**
     * gets node for element. It is a non-blocking operation
     * @param element element as a key
     * @return node or null if the node has not been created yet
     */ 
    public Node getNode(Object element) {
        Node[] ns = null;
        synchronized (this) {
            ns = (Node[]) nodes.get(element);
        }
        return ns == null? null: ns[0];
    }
    
    /**
     * gets keys; blocking opreration
     * @return keys
     */ 
    public Collection getKeys() {
        computeChildren();
        synchronized (this) {
            return elements;
        }
    }
    
    /**
     * waits untill the keys and nodes are ready.
     */ 
    public void waitFinished() {
        computeChildren();
    }
    
    /**
     * stops the running task and sets children as empty collection 
     */ 
    public void clear() {
        RP.post(new CleanTask());
    }
    
    private void clearImpl() {
        synchronized (this) {
            elements = Collections.EMPTY_LIST;
            nodes = Collections.EMPTY_MAP;
        }
        presentKeys(elements, nodes);
    }
        
    private void computeChildren() {
        synchronized (this) {
            if (currentTask == null) {
                recomputeChildren();
            }
        }
        currentTask.waitFinished();
    }

    private Map/**/ createNodeMap(
            final Map/**/ nodes, final List keys) throws JmiException {
        final Map/**/ nodesNew = initNodeMap();
        if (keys.isEmpty()) {
            return nodesNew;
        }
        
        JmiException ex = (JmiException) Children.MUTEX.readAccess(new Mutex.Action() {
            public Object run() {
                try {
                    JMManager.getTransactionMutex().addPriorityThread();
                    JavaMetamodel.getDefaultRepository().beginTrans(false);
                    try {
                        for (Iterator it = keys.iterator(); it.hasNext();) {
                            Object el = it.next();
                            Node[] ns = (Node[]) nodes.get(el);
                            if (ns == null) {
                                ns = keyHandler.prepareNodes(el);
                            }
                            nodesNew.put(el, ns);
                        }
                    } finally {
                        JavaMetamodel.getDefaultRepository().endTrans();
                    }
                } catch (JmiException ex) {
                    return ex;
                }
                return null;
            }

        });
        
        if (ex != null) {
            throw ex;
        }
        return nodesNew;
    }
    
    private final class CleanTask implements Runnable {
        
        public void run() {
            clearImpl();
        }

    }
        
}
... 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.