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-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.java.tools;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import java.util.*;

import org.openide.loaders.DataFilter;
import org.openide.loaders.DataObject;

/**
 * This class implements "multihomed" DataObject.Container implementation.
 * The class serves as a container of DataObject taken out from different
 * sources. 

* The class also supports tree-like structure of named (or otherwise identified) * containers. In addition to getChildren() call mandated by DataObject.Container, * it also support getContainers() which return subordinate containers found in this * container.

* For obvious reasons, sub-containers cannot be defined as DataObjects, although it * would be lovely to do so. *

* Possible enhancements *

    *
  • Subordinate containers can be weakly referenced, If nobody is interested in * them, there's no need to keep them. *
  • DataObjects which form subordinate containers may be weakly referenced, too. * If nested container exists, it will keep those DOs in memory. Otherwise, they can * be easily searched for at the time when a nested container is requested. *
* * @author sd99038 * @version 0.1 */ public class MultiDataContainer implements DataObject.Container, PropertyChangeListener { /** * Name of "containers" property. */ public static final String PROP_CONTAINERS = "containers"; // NOI18N /** * Name of the "contents" property. */ public static final String PROP_CONTENTS = "contents"; // NOI18N /** * Empty children list for empty containers :-) */ private static final DataObject[] EMPTY_CHILDREN = {}; /** * Special placeholder value for an object that has been rejected. */ private static final Object REJECTED_OBJECT = new Object(); /** * List of children contained in this container. */ DataObject[] children; /** * Collection of DataObjects that represent contents of that particular * package. */ Collection contents; /** * Helps to filter out some DataObjects. This filter is immutable during the * container's life. However, it's behaviour is - who knows ? */ DataFilter filter; /** * True, if children need to be refreshed before the request to getChildren() * succeeds. */ boolean refreshChildren; /** * Map of nested containers; the map is keyed by container's key * (produced by createKey), its values are instances of MultiDataContainer. */ Map nestedContainers; /** * Map that maps container names to contents collections. */ Map containerContents; PropertyChangeSupport propSupport; /** * Constructs an empty container. Since the container has no sources, it * is empty. Its datafilter is set to {@link DataFilter.ALL} */ public MultiDataContainer() { this(DataFilter.ALL); } /** * Creates an empty container object with the specified DataFilter. */ public MultiDataContainer(DataFilter filter) { this(Collections.EMPTY_LIST, filter); } /** * Creates a container wrapper around a collection of source containers, * with contents filtered by the specified DataFilter. */ public MultiDataContainer(Collection sourceContainers, DataFilter filter) { this.contents = sourceContainers; this.filter = filter; this.children = EMPTY_CHILDREN; this.refreshChildren = true; this.nestedContainers = new TreeMap(); } /** * Changes contents of this container by specifying a collection of * underlying containers. The collection is considered to be ordered; * underlying Container contents are merged in this order. */ public void setContents(Collection containers) { Collection old; synchronized (this) { if (contents.equals(containers)) return; for (Iterator it = contents.iterator(); it.hasNext(); ) { DataObject.Container cont = (DataObject.Container)it.next(); cont.removePropertyChangeListener(this); } contents = Collections.unmodifiableCollection(containers); for (Iterator it = contents.iterator(); it.hasNext(); ) { DataObject.Container cont = (DataObject.Container)it.next(); cont.addPropertyChangeListener(this); } invalidateChildren(); } fireContentsChange(); } /** * Invalidates the list of children. */ private void invalidateChildren() { synchronized (this) { refreshChildren = true; } } /** * Fire a property change on both CONTENTS property and CHILDREN property, * since change in the contents more or less implies change in the children. */ private void fireContentsChange() { if (this.propSupport == null) return; propSupport.firePropertyChange(PROP_CONTENTS, null, null); fireChildrenChange(); } /** * Fires a property change on "children" property. */ private void fireChildrenChange() { if (this.propSupport == null) return; propSupport.firePropertyChange(PROP_CHILDREN, null, null); } /** * Returns a collection of DataObject.Container represented by this object. */ public Collection getContents() { return contents; } /** * Returns children DataObjects of this container. DataObjects that were * classified as non-leaves are not included in this list. */ public DataObject[] getChildren() { if (refreshChildren) { synchronized (this) { refreshChildren = false; refreshData(); } } return children; } /** * Returns a map of containers, actually pairs */ public Map getContainers() { return nestedContainers; } /** * Refreshes data structures from the current container contents. */ private void refreshData() { Set knownKeys = new HashSet(31); Collection newChildren = new LinkedList(); Map newContainers = new HashMap(31); int rejected = 0; for (Iterator it = getContents().iterator(); it.hasNext(); ) { rejected += addContainer(newContainers, knownKeys, newChildren, (DataObject.Container)it.next()); } boolean containersChanged = false; // now, we need to rebuild the map of nested containers: for (Iterator it = newContainers.entrySet().iterator(); it.hasNext(); ) { Map.Entry en = (Map.Entry)it.next(); Object k = en.getKey(); Collection sources = (Collection)en.getValue(); MultiDataContainer nested; nested = (MultiDataContainer)nestedContainers.get(k); if (nested == null) { System.err.println("creating nested container for " + k); // NOI18N nested = createContainer(sources); containersChanged = true; } else { nested.setContents(sources); } en.setValue(nested); } if (nestedContainers != null) { for (Iterator it = nestedContainers.keySet().iterator(); !containersChanged && it.hasNext(); ) { containersChanged &= newContainers.containsKey(it.next()); } } else { containersChanged |= !newContainers.isEmpty(); } synchronized (this) { this.nestedContainers = newContainers; this.children = (DataObject[])newChildren.toArray(new DataObject[newChildren.size()]); } if (containersChanged) { propSupport.firePropertyChange(PROP_CONTAINERS, null, null); } } /** * Adds data from a specific container to the data set. * If a DataObject with the same name was already added, the new one is * silently ignored. */ private int addContainer(Map containers, Set presentKeys, Collection contents, DataObject.Container folder) { System.err.println("addContainer: " + folder); // NOI18N DataObject[] children = folder.getChildren(); int rejected = 0; for (int i = 0; i < children.length; i++) { Object previous; Object key; DataObject obj = children[i]; Object o = obj; key = createKey(obj); // check whether the object is accepted. if (!filter.acceptDataObject(obj)) { System.err.println("addContainer: " + obj + " was rejected. "); // NOI18N rejected++; continue; } // decide whether the obj is a leaf or not: if (isContainer(obj)) { System.err.println("got container: " + obj + " with key " + key); // NOI18N Collection c = (Collection)containers.get(key); if (c == null) { c = new LinkedList(); containers.put(key, c); System.err.println("new container"); // NOI18N } c.add(obj); } else { // add the new object only if it is not already present // (according to its key). if (presentKeys.add(key)) contents.add(o); } } return rejected; } /** * Retrieves the filter used to filter contents of the container. * @return filter instance. */ public DataFilter getFilter() { return filter; } /** * Creates a multi container with the same operation semantics for the given * collection of source Containers. This method is to allow customized * subclasses to extend the semantics on containers found within. */ public MultiDataContainer createContainer(Collection initialSources) { return new MultiDataContainer(initialSources, getFilter()); } /** * Adds a listener to be notified when the contents change. * @throws IllegalArgumentException if the listener is null. */ public void addPropertyChangeListener(PropertyChangeListener l) throws IllegalArgumentException { if (l == null) throw new IllegalArgumentException("eee"); // NOI18N synchronized (this) { if (propSupport == null) propSupport = new PropertyChangeSupport(this); } propSupport.addPropertyChangeListener(l); } /** * Creates a key for the given DataObject. The key is then used during * collection from underlying Containers. The default implementation returns * DataObject's name. * @return key used for comparisons to detect matching DataObjects. */ protected Object createKey(DataObject d) { return d.getName(); } /** * Classifies DataObject to be a leaf or non-leaf. The default implementation * classifies anything that supplies DataObject.Container cookie as non-leaf. */ protected boolean isContainer(DataObject d) { return d.getCookie(DataObject.Container.class) != null; } /** * Removes the listener registered previously. */ public void removePropertyChangeListener(PropertyChangeListener l) { if (propSupport == null) return; propSupport.removePropertyChangeListener(l); } /** * This method is pure implementation detail and should not be used at all. * It will be eventually removed :-) */ public final void propertyChange(PropertyChangeEvent event) { if (PROP_CHILDREN.equals(event.getPropertyName())) { invalidateChildren(); fireChildrenChange(); } } }
... 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.