KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openidex > search > SearchGroup


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
21 package org.openidex.search;
22
23
24 import java.beans.PropertyChangeEvent JavaDoc;
25 import java.beans.PropertyChangeListener JavaDoc;
26 import java.beans.PropertyChangeSupport JavaDoc;
27 import java.util.*;
28
29 import org.openide.filesystems.FileObject;
30 import org.openide.loaders.DataObject;
31 import org.openide.nodes.Node;
32 import org.openide.util.WeakListeners;
33
34
35 /**
36  * Class which groups individual search types. It provides several services
37  * to provide search on them. The services are scanning node system to
38  * provide search object for group of search types -> efficient search.
39  *
40  * @author Peter Zavadsky
41  * @author Marian Petras
42  */

43 public abstract class SearchGroup extends Object JavaDoc {
44
45     /**
46      * Property name which is fired when performing search and searched object
47      * passed criteria.
48      */

49     public static final String JavaDoc PROP_FOUND = "org.openidex.search.found"; // NOI18N
50

51     /**
52      * Property name which is fired for in for the case original <code>node</code>'s has
53      * changed the way <code>result</code> was changed based on set criteria.
54      * Interested listeners should then get the event with values
55      * <UL>
56      * <LI>property change name = PROP_RESULT
57      * <LI>property source = this search type instance
58      * <LI>old value = detail which was changed or <code>null</code> there wasn't before for the node -> new value has to be non-null
59      * for the latter case.
60      * <LI>new value = detail which was changed or null if the node was removed from the result -> old value has to be non-null
61      * for that case
62      * </UL>
63      * This allows implementation of the dynamic changing of result suggested
64      * by Jesse and Sebastian (at least partially implemented now).
65      */

66     public static final String JavaDoc PROP_RESULT = "org.openidex.search.result"; // NOI18N
67

68     
69     /** Property change support. */
70     private PropertyChangeSupport JavaDoc propChangeSupport;
71
72     /** search types added to this search group */
73     protected SearchType[] searchTypes = new SearchType[0];
74
75     /** Set of nodes on which sub-system to search. */
76     protected final Set searchRoots = new HashSet(5);
77     
78     /** Set of objects which passed the search criteria (searchtypes).*/
79     protected final Set resultObjects = new LinkedHashSet(50);
80
81     /** Flag indicating the search should be stopped. */
82     protected volatile boolean stopped = false;
83
84     private PropertyChangeListener JavaDoc propListener;
85
86
87     /**
88      * Adds a search type to this search group.
89      * If the group already contains the search type, the group is left
90      * unmodified.
91      *
92      * @param searchType search type to be added
93      */

94     protected void add(SearchType searchType) {
95
96         /* Check whether the search type is already in the list: */
97         for (int i = 0; i < searchTypes.length; i++) {
98             if (searchType.equals(searchTypes[i])) {
99                 return;
100             }
101         }
102         
103         /* Add the search type to the list: */
104         SearchType[] temp = new SearchType[searchTypes.length + 1];
105         System.arraycopy(searchTypes, 0, temp, 0, searchTypes.length);
106         temp[searchTypes.length] = searchType;
107         searchTypes = temp;
108     }
109
110     /**
111      * Returns list of search types.
112      *
113      * @return search types added to this group
114      * @see #add
115      */

116     public SearchType[] getSearchTypes() {
117         return searchTypes;
118     }
119     
120     /**
121      * Sets roots of nodes in which its interested to search.
122      * This method is called at the first search type in the possible created
123      * chain of search types.
124      */

125     public void setSearchRootNodes(Node[] roots) {
126         
127         /*
128          * Gives a chance for individual search types to exclude some
129          * node systems. E.g. CVS search type is not interested
130          * in non CVS node systems.
131          */

132         for (int i = 0; i < searchTypes.length; i++) {
133             roots = searchTypes[i].acceptSearchRootNodes(roots);
134         }
135         searchRoots.clear();
136         searchRoots.addAll(Arrays.asList(roots));
137     }
138
139     /** Gets search root nodes. */
140     public Node[] getSearchRoots() {
141         return (Node[]) searchRoots.toArray(new Node[searchRoots.size()]);
142     }
143     
144     /** Stops searching. */
145     public final void stopSearch() {
146         stopped = true;
147     }
148
149     /**
150      * Does search.
151      *
152      * @throw RuntimeException USER level annotated runtime exception
153      * on low memory condition (instead of OutOfMemoryError)
154      */

155     public void search() {
156         resultObjects.clear();
157         prepareSearch();
158         doSearch();
159     }
160
161     /**
162      * Prepares search.
163      */

164     protected void prepareSearch() {
165     }
166
167     /**
168      * Provides actual search. The subclasses implementating this method should scan the node system
169      * specified by <code>searchRoots</code>, extract search objects from them, add them
170      * to the search object set, test over all search type items in this group,
171      * in case if satisfied all it should fire <code>PROP_FOUND</code> property change and add
172      * the object to <code>resultObjects</code> set.
173      * The method implemenatation should call {@link #processSearchObject} method for each
174      * search object in the node systems.
175      */

176     protected abstract void doSearch();
177
178     /**
179      * Provides search on one search object instance. The object is added to
180      * set of searched objects and passed to all search types encapsulated by
181      * this search group. In the case the object passes all search types is added
182      * to the result set and fired an event <code>PROP_FOUND</code> about successful
183      * match to interested property change listeners.
184      *
185      * @param searchObject object to provide actuall test on it. The actual instance
186      * has to be of type returned by all <code>SearchKey.getSearchObjectType</code>
187      * returned by <code>SearchType</code> of this <code>SearchGroup</code>
188      */

189     protected void processSearchObject(Object JavaDoc searchObject) {
190         
191         /*
192          * Give chance to individual search types to exclude some
193          * non interesting search objects from search. E.g. Java data
194          * object search will be not interested in non Java data objects.
195          */

196         for (int i = 0; i < searchTypes.length; i++) {
197             if (!searchTypes[i].acceptSearchObject(searchObject)) {
198                 return;
199             }
200         }
201
202         /*
203          * Give chance to provide additional things.
204          */

205         for (int i = 0; i < searchTypes.length; i++) {
206             searchTypes[i].prepareSearchObject(searchObject);
207         }
208         
209         /* Actually test the search object against all search types. */
210         for (int i = 0; i < searchTypes.length; i++) {
211             if (!searchTypes[i].testObject(searchObject)) {
212                 return;
213             }
214         }
215
216         /*
217          * In case the search object passed the search add it to the result set
218          * and fire an event about successful search to interested listeners.
219          */

220         resultObjects.add(searchObject);
221         firePropertyChange(PROP_FOUND, null, searchObject);
222     }
223
224     
225     /** Gets node for found object. */
226     public abstract Node getNodeForFoundObject(Object JavaDoc object);
227
228     /** Getter for result object property. */
229     public Set getResultObjects() {
230         return new LinkedHashSet(resultObjects);
231     }
232
233     /** Adds property change listener. */
234     public synchronized void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
235         getPropertySupport().addPropertyChangeListener(l);
236     }
237     
238     /** Removes property change listener. */
239     public synchronized void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
240         getPropertySupport().removePropertyChangeListener(l);
241     }
242
243     /** Fires property change event. */
244     protected void firePropertyChange(String JavaDoc name, Object JavaDoc oldValue, Object JavaDoc newValue) {
245         getPropertySupport().firePropertyChange(name, oldValue, newValue);
246     }
247     
248     /** Gets lazy initialized property change support. */
249     private synchronized PropertyChangeSupport JavaDoc getPropertySupport() {
250         if(propChangeSupport == null)
251             propChangeSupport = new PropertyChangeSupport JavaDoc(this);
252         
253         return propChangeSupport;
254     }
255
256     /**
257      * Creates a search group for each type of object searchable by all
258      * the specified search types.
259      * <p>
260      * At first, a set of object types common to all search types
261      * (i.e. <code>Class</code>s representing object types, common
262      * to all search types) is computed. Then a search group is created
263      * for each of the <code>Class</code>s.
264      *
265      * @param search types to create search groups for
266      * @return created search groups
267      * @see SearchType#getSearchTypeClasses()
268      */

269     public static SearchGroup[] createSearchGroups(SearchType[] items) {
270
271         /*
272          * Build a list of Class's searchable by every search type
273          * from the specified list of search types.
274          * In other words: Build a list of Class'es common to all search types.
275          */

276         Set classSet = new HashSet(items.length);
277         for (int i = 0; i < items.length; i++) {
278             List classes = Arrays.asList(items[i].getSearchTypeClasses());
279             if (i == 0) {
280                 classSet.addAll(classes);
281             } else {
282                 classSet.retainAll(classes);
283             }
284         }
285
286         /* Try to create a search group for each of the Class'es: */
287         if (classSet.isEmpty()) {
288             return new SearchGroup[0];
289         }
290         Set groupSet = new HashSet(classSet.size());
291         for (Iterator it = classSet.iterator(); it.hasNext(); ) {
292             SearchGroup group = Registry.createSearchGroup((Class JavaDoc) it.next());
293             if (group != null) {
294                 for (int i = 0; i < items.length; i++) {
295                     group.add(items[i]);
296                 }
297                 groupSet.add(group);
298             }
299         }
300         return (SearchGroup[]) groupSet.toArray(new SearchGroup[groupSet.size()]);
301     }
302
303
304     /**
305      * Factory which creates <code>SearchGroup</code>. It's used in
306      * <code>Registry</code>
307      * @see SearchGroup.Registry
308      */

309     public interface Factory {
310         /** Creates new <code>SearchGroup</code> object. */
311         public SearchGroup createSearchGroup();
312     } // End of interface Factory.
313

314     
315     /**
316      * Registry which registers search group factories
317      * ({@link SearchGroup.Factory}) for search object types.
318      * <p>
319      * Initially, factories for search object types {@link DataObject}
320      * and {@link FileObject} are already registered
321      * (<code>DataObjectSearchGroup</code> and
322      * <code>FileObjectSearchGroup</code>).
323      *
324      * @see SearchGroup.Factory
325      * @see DataObjectSearchGroup
326      * @see FileObjectSearchGroup
327      */

328     public static final class Registry extends Object JavaDoc {
329
330         /** Private constructor so nobody could access it. */
331         private Registry() {}
332         
333         
334         /** Maps search object types to registered factories. */
335         private static final Map registry = new HashMap(2);
336
337         static {
338             registry.put(DataObject.class, new Factory() {
339                 public SearchGroup createSearchGroup() {
340                     return new DataObjectSearchGroup();
341                 }
342             });
343             registry.put(FileObject.class, new Factory() {
344                 public SearchGroup createSearchGroup() {
345                     return new FileObjectSearchGroup();
346                 }
347             });
348         }
349
350         
351         /**
352          * Registers a search group factory for a search object type
353          * (<code>Class</code>).
354          * If a factory has already been registered for the specified
355          * search object type, the old registration is kept (the registration
356          * fails).
357          *
358          * @param searchObjectClass search object type the factory is
359          * to be registered for
360          * @param factory factory to be registered
361          * @return <code>true</code> if the registration was successful,
362          * <code>false</code> if the registration failed
363          * (i.&nbsp;e. if some factory has already been registered
364          * for the specified search object type)
365          */

366         public static synchronized boolean registerSearchGroupFactory(
367                 Class JavaDoc searchObjectClass,
368                 Factory factory) {
369             Object JavaDoc oldFactory = registry.put(searchObjectClass, factory);
370             if (oldFactory != null) {
371                 
372                 /*
373                  * Oops! A factory for the specified search object class
374                  * have already been registered. Retain the old registration:
375                  */

376                 registry.put(searchObjectClass, oldFactory);
377                 return false;
378             }
379             return true;
380         }
381         
382         /**
383          * Creates a <code>SearchGroup</code> for the specified search object
384          * type.
385          * The search group is created using
386          * the {@linkplain SearchGroup.Factory factory} registered for
387          * the specified search object type.
388          *
389          * @param searchObjectType search object type to create
390          * a search group for
391          * @return search group created by the registered factory,
392          * or <code>null</code> if no factory has been registed
393          * for the specified search object type
394          * @see #registerSearchGroupFactory registerSearchGroupFactory
395          */

396         public static SearchGroup createSearchGroup(Class JavaDoc searchObjectType) {
397             Factory factory = (Factory) registry.get(searchObjectType);
398             
399             if (factory == null) {
400                 return null;
401             }
402             return factory.createSearchGroup();
403         }
404
405         /**
406          * Tests whether there is a <code>Factory</code> registered for the
407          * specified search object class type.
408          *
409          * @param searchObjectType search object type
410          * @return <code>true</code> if some factory has been registered
411          * for the specified search object type,
412          * <code>false</code> otherwise
413          */

414         public static boolean hasFactory(Class JavaDoc searchObjectType) {
415             return registry.containsKey(searchObjectType);
416         }
417         
418     } // End of class Registry.
419

420 }
421
Popular Tags