KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > tools > MultiDataContainer


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.java.tools;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.beans.PropertyChangeSupport JavaDoc;
25
26 import java.util.*;
27
28 import org.openide.loaders.DataFilter;
29 import org.openide.loaders.DataObject;
30
31 /**
32  * This class implements "multihomed" DataObject.Container implementation.
33  * The class serves as a container of DataObject taken out from different
34  * sources. <P>
35  * The class also supports tree-like structure of named (or otherwise identified)
36  * containers. In addition to getChildren() call mandated by DataObject.Container,
37  * it also support getContainers() which return subordinate containers found in this
38  * container. <P>
39  * For obvious reasons, sub-containers cannot be defined as DataObjects, although it
40  * would be lovely to do so.
41  * <P>
42  * <STRONG>Possible enhancements</STRONG>
43  * <UL>
44  * <LI>Subordinate containers can be weakly referenced, If nobody is interested in
45  * them, there's no need to keep them.
46  * <LI>DataObjects which form subordinate containers may be weakly referenced, too.
47  * If nested container exists, it will keep those DOs in memory. Otherwise, they can
48  * be easily searched for at the time when a nested container is requested.
49  * </UL>
50  *
51  * @author sd99038
52  * @version 0.1
53  */

54 public class MultiDataContainer implements DataObject.Container, PropertyChangeListener JavaDoc {
55     /**
56      * Name of "containers" property.
57      */

58     public static final String JavaDoc PROP_CONTAINERS = "containers"; // NOI18N
59

60     /**
61      * Name of the "contents" property.
62      */

63     public static final String JavaDoc PROP_CONTENTS = "contents"; // NOI18N
64

65     /**
66      * Empty children list for empty containers :-)
67      */

68     private static final DataObject[] EMPTY_CHILDREN = {};
69     
70     /**
71      * List of children contained in this container.
72      */

73     DataObject[] children;
74
75     /**
76      * Collection of DataObjects that represent contents of that particular
77      * package.
78      */

79     Collection contents;
80     
81     /**
82      * Helps to filter out some DataObjects. This filter is immutable during the
83      * container's life. However, it's behaviour is - who knows ?
84      */

85     DataFilter filter;
86     
87     /**
88      * True, if children need to be refreshed before the request to getChildren()
89      * succeeds.
90      */

91     boolean refreshChildren;
92     
93     /**
94      * Map of nested containers; the map is keyed by container's key
95      * (produced by createKey), its values are instances of MultiDataContainer.
96      */

97     Map nestedContainers;
98     
99     /**
100      * Map that maps container names to contents collections.
101      */

102     Map containerContents;
103     
104     PropertyChangeSupport JavaDoc propSupport;
105
106     /**
107      * Constructs an empty container. Since the container has no sources, it
108      * is empty. Its datafilter is set to {@link DataFilter.ALL}
109      */

110     public MultiDataContainer() {
111         this(DataFilter.ALL);
112     }
113     
114     /**
115      * Creates an empty container object with the specified DataFilter.
116      */

117     public MultiDataContainer(DataFilter filter) {
118         this(Collections.EMPTY_LIST, filter);
119     }
120     
121     /**
122      * Creates a container wrapper around a collection of source containers,
123      * with contents filtered by the specified DataFilter.
124      */

125     public MultiDataContainer(Collection sourceContainers, DataFilter filter) {
126         this.contents = sourceContainers;
127         this.filter = filter;
128         this.children = EMPTY_CHILDREN;
129         this.refreshChildren = true;
130         this.nestedContainers = new TreeMap();
131     }
132     
133     /**
134      * Changes contents of this container by specifying a collection of
135      * underlying containers. The collection is considered to be ordered;
136      * underlying Container contents are merged in this order.
137      */

138     public void setContents(Collection containers) {
139         
140         synchronized (this) {
141             if (contents.equals(containers))
142                 return;
143             for (Iterator it = contents.iterator(); it.hasNext(); ) {
144                 DataObject.Container cont = (DataObject.Container)it.next();
145                 cont.removePropertyChangeListener(this);
146             }
147             contents = Collections.unmodifiableCollection(containers);
148             for (Iterator it = contents.iterator(); it.hasNext(); ) {
149                 DataObject.Container cont = (DataObject.Container)it.next();
150                 cont.addPropertyChangeListener(this);
151             }
152             invalidateChildren();
153         }
154         fireContentsChange();
155     }
156     
157     /**
158      * Invalidates the list of children.
159      */

160     private void invalidateChildren() {
161         synchronized (this) {
162             refreshChildren = true;
163         }
164     }
165
166     /**
167      * Fire a property change on both CONTENTS property and CHILDREN property,
168      * since change in the contents more or less implies change in the children.
169      */

170     private void fireContentsChange() {
171         if (this.propSupport == null)
172             return;
173         propSupport.firePropertyChange(PROP_CONTENTS, null, null);
174         fireChildrenChange();
175     }
176
177     /**
178      * Fires a property change on "children" property.
179      */

180     private void fireChildrenChange() {
181         if (this.propSupport == null)
182             return;
183         propSupport.firePropertyChange(PROP_CHILDREN, null, null);
184     }
185
186     /**
187      * Returns a collection of DataObject.Container represented by this object.
188      */

189     public Collection getContents() {
190         return contents;
191     }
192  
193     /**
194      * Returns children DataObjects of this container. DataObjects that were
195      * classified as non-leaves are not included in this list.
196      */

197     public DataObject[] getChildren() {
198         if (refreshChildren) {
199             synchronized (this) {
200                 refreshChildren = false;
201                 refreshData();
202             }
203         }
204         return children;
205     }
206
207     /**
208      * Returns a map of containers, actually pairs <name, container>
209      */

210     public Map getContainers() {
211         return nestedContainers;
212     }
213     
214     /**
215      * Refreshes data structures from the current container contents.
216      */

217     private void refreshData() {
218         Set knownKeys = new HashSet(31);
219         Collection newChildren = new LinkedList();
220         Map newContainers = new HashMap(31);
221         
222         int rejected = 0;
223
224         for (Iterator it = getContents().iterator(); it.hasNext(); ) {
225             rejected += addContainer(newContainers, knownKeys, newChildren, (DataObject.Container)it.next());
226         }
227         
228         boolean containersChanged = false;
229         
230         // now, we need to rebuild the map of nested containers:
231
for (Iterator it = newContainers.entrySet().iterator(); it.hasNext(); ) {
232             Map.Entry en = (Map.Entry)it.next();
233             Object JavaDoc k = en.getKey();
234             Collection sources = (Collection)en.getValue();
235             MultiDataContainer nested;
236             
237             nested = (MultiDataContainer)nestedContainers.get(k);
238             if (nested == null) {
239                 System.err.println("creating nested container for " + k); // NOI18N
240
nested = createContainer(sources);
241                 containersChanged = true;
242             } else {
243                 nested.setContents(sources);
244             }
245             en.setValue(nested);
246         }
247         if (nestedContainers != null) {
248             for (Iterator it = nestedContainers.keySet().iterator(); !containersChanged && it.hasNext(); ) {
249                 containersChanged &= newContainers.containsKey(it.next());
250             }
251         } else {
252             containersChanged |= !newContainers.isEmpty();
253         }
254         
255         synchronized (this) {
256             this.nestedContainers = newContainers;
257             this.children = (DataObject[])newChildren.toArray(new DataObject[newChildren.size()]);
258         }
259         if (containersChanged) {
260             propSupport.firePropertyChange(PROP_CONTAINERS, null, null);
261         }
262     }
263     
264     /**
265      * Adds data from a specific container to the data set.
266      * If a DataObject with the same name was already added, the new one is
267      * silently ignored.
268      */

269     private int addContainer(Map containers, Set presentKeys, Collection contents,
270         DataObject.Container folder) {
271
272         System.err.println("addContainer: " + folder); // NOI18N
273
DataObject[] children = folder.getChildren();
274         int rejected = 0;
275         
276         for (int i = 0; i < children.length; i++) {
277             Object JavaDoc key;
278             DataObject obj = children[i];
279             Object JavaDoc o = obj;
280             
281             key = createKey(obj);
282             // check whether the object is accepted.
283
if (!filter.acceptDataObject(obj)) {
284                 System.err.println("addContainer: " + obj + " was rejected. "); // NOI18N
285
rejected++;
286                 continue;
287             }
288
289             // decide whether the obj is a leaf or not:
290
if (isContainer(obj)) {
291                 System.err.println("got container: " + obj + " with key " + key); // NOI18N
292
Collection c = (Collection)containers.get(key);
293                 if (c == null) {
294                     c = new LinkedList();
295                     containers.put(key, c);
296                     System.err.println("new container"); // NOI18N
297
}
298                 c.add(obj);
299             } else {
300                 // add the new object only if it is not already present
301
// (according to its key).
302
if (presentKeys.add(key))
303                     contents.add(o);
304             }
305         }
306         return rejected;
307     }
308
309     /**
310      * Retrieves the filter used to filter contents of the container.
311      * @return filter instance.
312      */

313     public DataFilter getFilter() {
314         return filter;
315     }
316     
317     /**
318      * Creates a multi container with the same operation semantics for the given
319      * collection of source Containers. This method is to allow customized
320      * subclasses to extend the semantics on containers found within.
321      */

322     public MultiDataContainer createContainer(Collection initialSources) {
323         return new MultiDataContainer(initialSources, getFilter());
324     }
325
326     /**
327      * Adds a listener to be notified when the contents change.
328      * @throws IllegalArgumentException if the listener is null.
329      */

330     public void addPropertyChangeListener(PropertyChangeListener JavaDoc l)
331         throws IllegalArgumentException JavaDoc {
332         if (l == null)
333             throw new IllegalArgumentException JavaDoc("eee"); // NOI18N
334
synchronized (this) {
335             if (propSupport == null)
336                 propSupport = new PropertyChangeSupport JavaDoc(this);
337         }
338         propSupport.addPropertyChangeListener(l);
339     }
340
341     /**
342      * Creates a key for the given DataObject. The key is then used during
343      * collection from underlying Containers. The default implementation returns
344      * DataObject's name.
345      * @return key used for comparisons to detect matching DataObjects.
346      */

347     protected Object JavaDoc createKey(DataObject d) {
348         return d.getName();
349     }
350     
351     /**
352      * Classifies DataObject to be a leaf or non-leaf. The default implementation
353      * classifies anything that supplies DataObject.Container cookie as non-leaf.
354      */

355     protected boolean isContainer(DataObject d) {
356         return d.getCookie(DataObject.Container.class) != null;
357     }
358     
359     /**
360      * Removes the listener registered previously.
361      */

362     public void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
363         if (propSupport == null)
364             return;
365         propSupport.removePropertyChangeListener(l);
366     }
367     
368     /**
369      * This method is pure implementation detail and should not be used at all.
370      * It will be eventually removed :-)
371      */

372     public final void propertyChange(PropertyChangeEvent JavaDoc event) {
373         if (PROP_CHILDREN.equals(event.getPropertyName())) {
374             invalidateChildren();
375             fireChildrenChange();
376         }
377     }
378 }
379
Popular Tags