KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > dialogs > PatternFilter


1 /*******************************************************************************
2  * Copyright (c) 2004, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.ui.dialogs;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.List JavaDoc;
16 import java.util.Map JavaDoc;
17
18 import org.eclipse.jface.viewers.AbstractTreeViewer;
19 import org.eclipse.jface.viewers.ILabelProvider;
20 import org.eclipse.jface.viewers.ITreeContentProvider;
21 import org.eclipse.jface.viewers.StructuredViewer;
22 import org.eclipse.jface.viewers.Viewer;
23 import org.eclipse.jface.viewers.ViewerFilter;
24 import org.eclipse.ui.internal.misc.StringMatcher;
25
26 import com.ibm.icu.text.BreakIterator;
27
28 /**
29  * A filter used in conjunction with <code>FilteredTree</code>. In order to
30  * determine if a node should be filtered it uses the content provider of the
31  * tree to do pattern matching on its children. This causes the entire tree
32  * structure to be realized.
33  *
34  * @see org.eclipse.ui.dialogs.FilteredTree
35  * @since 3.2
36  */

37 public class PatternFilter extends ViewerFilter {
38     /*
39      * Cache of filtered elements in the tree
40      */

41     private Map JavaDoc cache = new HashMap JavaDoc();
42     
43     /*
44      * Maps parent elements to TRUE or FALSE
45      */

46     private Map JavaDoc foundAnyCache = new HashMap JavaDoc();
47     
48     private boolean useCache = false;
49     
50     /**
51      * Whether to include a leading wildcard for all provided patterns. A
52      * trailing wildcard is always included.
53      */

54     private boolean includeLeadingWildcard = false;
55
56     /**
57      * The string pattern matcher used for this pattern filter.
58      */

59     private StringMatcher matcher;
60     
61     private boolean useEarlyReturnIfMatcherIsNull = true;
62     
63     private static Object JavaDoc[] EMPTY = new Object JavaDoc[0];
64
65     /* (non-Javadoc)
66      * @see org.eclipse.jface.viewers.ViewerFilter#filter(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object[])
67      */

68     public final Object JavaDoc[] filter(Viewer viewer, Object JavaDoc parent, Object JavaDoc[] elements) {
69         // we don't want to optimize if we've extended the filter ... this
70
// needs to be addressed in 3.4
71
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=186404
72
if (matcher == null && useEarlyReturnIfMatcherIsNull) {
73             return elements;
74         }
75
76         if (!useCache) {
77             return super.filter(viewer, parent, elements);
78         }
79         
80         Object JavaDoc[] filtered = (Object JavaDoc[]) cache.get(parent);
81         if (filtered == null) {
82             Boolean JavaDoc foundAny = (Boolean JavaDoc) foundAnyCache.get(parent);
83             if (foundAny != null && !foundAny.booleanValue()) {
84                 filtered = EMPTY;
85             } else {
86                 filtered = super.filter(viewer, parent, elements);
87             }
88             cache.put(parent, filtered);
89         }
90         return filtered;
91     }
92
93     /**
94      * Returns true if any of the elements makes it through the filter.
95      * This method uses caching if enabled; the computation is done in
96      * computeAnyVisible.
97      *
98      * @param viewer
99      * @param parent
100      * @param elements the elements (must not be an empty array)
101      * @return true if any of the elements makes it through the filter.
102      */

103     private boolean isAnyVisible(Viewer viewer, Object JavaDoc parent, Object JavaDoc[] elements) {
104         if (matcher == null) {
105             return true;
106         }
107         
108         if (!useCache) {
109             return computeAnyVisible(viewer, elements);
110         }
111         
112         Object JavaDoc[] filtered = (Object JavaDoc[]) cache.get(parent);
113         if (filtered != null) {
114             return filtered.length > 0;
115         }
116         Boolean JavaDoc foundAny = (Boolean JavaDoc) foundAnyCache.get(parent);
117         if (foundAny == null) {
118             foundAny = computeAnyVisible(viewer, elements) ? Boolean.TRUE : Boolean.FALSE;
119             foundAnyCache.put(parent, foundAny);
120         }
121         return foundAny.booleanValue();
122     }
123
124     /**
125      * Returns true if any of the elements makes it through the filter.
126      * @param viewer
127      * @param elements
128      * @return
129      */

130     private boolean computeAnyVisible(Viewer viewer, Object JavaDoc[] elements) {
131         boolean elementFound = false;
132         for (int i = 0; i < elements.length && !elementFound; i++) {
133             Object JavaDoc element = elements[i];
134             elementFound = isElementVisible(viewer, element);
135         }
136         return elementFound;
137     }
138     
139     /* (non-Javadoc)
140      * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
141      */

142     public final boolean select(Viewer viewer, Object JavaDoc parentElement,
143             Object JavaDoc element) {
144         return isElementVisible(viewer, element);
145     }
146     
147     /**
148      * Sets whether a leading wildcard should be attached to each pattern
149      * string.
150      *
151      * @param includeLeadingWildcard
152      * Whether a leading wildcard should be added.
153      */

154     public final void setIncludeLeadingWildcard(
155             final boolean includeLeadingWildcard) {
156         this.includeLeadingWildcard = includeLeadingWildcard;
157     }
158
159     /**
160      * The pattern string for which this filter should select
161      * elements in the viewer.
162      *
163      * @param patternString
164      */

165     public void setPattern(String JavaDoc patternString) {
166         // these 2 strings allow the PatternFilter to be extended in
167
// 3.3 - https://bugs.eclipse.org/bugs/show_bug.cgi?id=186404
168
if ("org.eclipse.ui.keys.optimization.true".equals(patternString)) { //$NON-NLS-1$
169
useEarlyReturnIfMatcherIsNull = true;
170             return;
171         } else if ("org.eclipse.ui.keys.optimization.false".equals(patternString)) { //$NON-NLS-1$
172
useEarlyReturnIfMatcherIsNull = false;
173             return;
174         }
175         clearCaches();
176         if (patternString == null || patternString.equals("")) { //$NON-NLS-1$
177
matcher = null;
178         } else {
179             String JavaDoc pattern = patternString + "*"; //$NON-NLS-1$
180
if (includeLeadingWildcard) {
181                 pattern = "*" + pattern; //$NON-NLS-1$
182
}
183             matcher = new StringMatcher(pattern, true, false);
184         }
185     }
186
187     /**
188      * Clears the caches used for optimizing this filter. Needs to be called whenever
189      * the tree content changes.
190      */

191     /* package */ void clearCaches() {
192         cache.clear();
193         foundAnyCache.clear();
194     }
195
196     /**
197      * Answers whether the given String matches the pattern.
198      *
199      * @param string the String to test
200      *
201      * @return whether the string matches the pattern
202      */

203     private boolean match(String JavaDoc string) {
204         if (matcher == null) {
205             return true;
206         }
207         return matcher.match(string);
208     }
209     
210     /**
211      * Answers whether the given element is a valid selection in
212      * the filtered tree. For example, if a tree has items that
213      * are categorized, the category itself may not be a valid
214      * selection since it is used merely to organize the elements.
215      *
216      * @param element
217      * @return true if this element is eligible for automatic selection
218      */

219     public boolean isElementSelectable(Object JavaDoc element){
220         return element != null;
221     }
222     
223     /**
224      * Answers whether the given element in the given viewer matches
225      * the filter pattern. This is a default implementation that will
226      * show a leaf element in the tree based on whether the provided
227      * filter text matches the text of the given element's text, or that
228      * of it's children (if the element has any).
229      *
230      * Subclasses may override this method.
231      *
232      * @param viewer the tree viewer in which the element resides
233      * @param element the element in the tree to check for a match
234      *
235      * @return true if the element matches the filter pattern
236      */

237     public boolean isElementVisible(Viewer viewer, Object JavaDoc element){
238         return isParentMatch(viewer, element) || isLeafMatch(viewer, element);
239     }
240     
241     /**
242      * Check if the parent (category) is a match to the filter text. The default
243      * behavior returns true if the element has at least one child element that is
244      * a match with the filter text.
245      *
246      * Subclasses may override this method.
247      *
248      * @param viewer the viewer that contains the element
249      * @param element the tree element to check
250      * @return true if the given element has children that matches the filter text
251      */

252     protected boolean isParentMatch(Viewer viewer, Object JavaDoc element){
253         Object JavaDoc[] children = ((ITreeContentProvider) ((AbstractTreeViewer) viewer)
254                 .getContentProvider()).getChildren(element);
255
256         if ((children != null) && (children.length > 0)) {
257             return isAnyVisible(viewer, element, children);
258         }
259         return false;
260     }
261     
262     /**
263      * Check if the current (leaf) element is a match with the filter text.
264      * The default behavior checks that the label of the element is a match.
265      *
266      * Subclasses should override this method.
267      *
268      * @param viewer the viewer that contains the element
269      * @param element the tree element to check
270      * @return true if the given element's label matches the filter text
271      */

272     protected boolean isLeafMatch(Viewer viewer, Object JavaDoc element){
273         String JavaDoc labelText = ((ILabelProvider) ((StructuredViewer) viewer)
274                 .getLabelProvider()).getText(element);
275         
276         if(labelText == null) {
277             return false;
278         }
279         return wordMatches(labelText);
280     }
281     
282     /**
283      * Take the given filter text and break it down into words using a
284      * BreakIterator.
285      *
286      * @param text
287      * @return an array of words
288      */

289     private String JavaDoc[] getWords(String JavaDoc text){
290         List JavaDoc words = new ArrayList JavaDoc();
291         // Break the text up into words, separating based on whitespace and
292
// common punctuation.
293
// Previously used String.split(..., "\\W"), where "\W" is a regular
294
// expression (see the Javadoc for class Pattern).
295
// Need to avoid both String.split and regular expressions, in order to
296
// compile against JCL Foundation (bug 80053).
297
// Also need to do this in an NL-sensitive way. The use of BreakIterator
298
// was suggested in bug 90579.
299
BreakIterator iter = BreakIterator.getWordInstance();
300         iter.setText(text);
301         int i = iter.first();
302         while (i != java.text.BreakIterator.DONE && i < text.length()) {
303             int j = iter.following(i);
304             if (j == java.text.BreakIterator.DONE) {
305                 j = text.length();
306             }
307             // match the word
308
if (Character.isLetterOrDigit(text.charAt(i))) {
309                 String JavaDoc word = text.substring(i, j);
310                 words.add(word);
311             }
312             i = j;
313         }
314         return (String JavaDoc[]) words.toArray(new String JavaDoc[words.size()]);
315     }
316     
317     /**
318      * Return whether or not if any of the words in text satisfy the
319      * match critera.
320      *
321      * @param text the text to match
322      * @return boolean <code>true</code> if one of the words in text
323      * satisifes the match criteria.
324      */

325     protected boolean wordMatches(String JavaDoc text) {
326         if (text == null) {
327             return false;
328         }
329         
330         //If the whole text matches we are all set
331
if(match(text)) {
332             return true;
333         }
334         
335         // Otherwise check if any of the words of the text matches
336
String JavaDoc[] words = getWords(text);
337         for (int i = 0; i < words.length; i++) {
338             String JavaDoc word = words[i];
339             if (match(word)) {
340                 return true;
341             }
342         }
343
344         return false;
345     }
346
347     /**
348      * Can be called by the filtered tree to turn on caching.
349      *
350      * @param useCache The useCache to set.
351      */

352     void setUseCache(boolean useCache) {
353         this.useCache = useCache;
354     }
355 }
356
Popular Tags