| /******************************************************************************* |
| * Copyright (c) 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.dialogs; |
| |
| import java.text.BreakIterator; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.jface.viewers.AbstractTreeViewer; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.StructuredViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerFilter; |
| import org.eclipse.ui.internal.misc.StringMatcher; |
| |
| /** |
| * A filter used in conjunction with <code>FilteredTree</code>. In order to |
| * determine if a node should be filtered it uses the content provider of the |
| * tree to do pattern matching on its children. This causes the entire tree |
| * structure to be realized. |
| * |
| * @see org.eclipse.ui.dialogs.FilteredTree |
| * @since 3.2 |
| */ |
| public class PatternFilter extends ViewerFilter { |
| /* |
| * Cache of filtered elements in the tree |
| */ |
| private Map cache = new HashMap(); |
| |
| /** |
| * Whether to include a leading wildcard for all provided patterns. A |
| * trailing wildcard is always included. |
| */ |
| private boolean includeLeadingWildcard = false; |
| |
| /** |
| * The string pattern matcher used for this pattern filter. |
| */ |
| private StringMatcher matcher; |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.viewers.ViewerFilter#filter(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object[]) |
| */ |
| public final Object[] filter(Viewer viewer, Object parent, Object[] elements) { |
| if (matcher == null) { |
| return elements; |
| } |
| |
| Object[] filtered = (Object[]) cache.get(parent); |
| if (filtered == null) { |
| filtered = super.filter(viewer, parent, elements); |
| cache.put(parent, filtered); |
| } |
| return filtered; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) |
| */ |
| public final boolean select(Viewer viewer, Object parentElement, |
| Object element) { |
| return isElementVisible(viewer, element); |
| } |
| |
| /** |
| * Sets whether a leading wildcard should be attached to each pattern |
| * string. |
| * |
| * @param includeLeadingWildcard |
| * Whether a leading wildcard should be added. |
| */ |
| public final void setIncludeLeadingWildcard( |
| final boolean includeLeadingWildcard) { |
| this.includeLeadingWildcard = includeLeadingWildcard; |
| } |
| |
| /** |
| * The pattern string for which this filter should select |
| * elements in the viewer. |
| * |
| * @param patternString |
| */ |
| public void setPattern(String patternString) { |
| cache.clear(); |
| if (patternString == null || patternString.equals("")) { //$NON-NLS-1$ |
| matcher = null; |
| } else { |
| String pattern = patternString + "*"; //$NON-NLS-1$ |
| if (includeLeadingWildcard) { |
| pattern = "*" + pattern; //$NON-NLS-1$ |
| } |
| matcher = new StringMatcher(pattern, true, false); |
| } |
| } |
| |
| /** |
| * Answers whether the given String matches the pattern. |
| * |
| * @param string the String to test |
| * |
| * @return whether the string matches the pattern |
| */ |
| private boolean match(String string) { |
| if (matcher == null) { |
| return true; |
| } |
| return matcher.match(string); |
| } |
| |
| /** |
| * Answers whether the given element is a valid selection in |
| * the filtered tree. For example, if a tree has items that |
| * are categorized, the category itself may not be a valid |
| * selection since it is used merely to organize the elements. |
| * |
| * @param element |
| * @return true if this element is eligible for automatic selection |
| */ |
| public boolean isElementSelectable(Object element){ |
| return element != null; |
| } |
| |
| /** |
| * Answers whether the given element in the given viewer matches |
| * the filter pattern. This is a default implementation that will |
| * show a leaf element in the tree based on whether the provided |
| * filter text matches the text of the given element's text, or that |
| * of it's children (if the element has any). |
| * |
| * Subclasses may override this method. |
| * |
| * @param viewer the tree viewer in which the element resides |
| * @param element the element in the tree to check for a match |
| * |
| * @return true if the element matches the filter pattern |
| */ |
| public boolean isElementVisible(Viewer viewer, Object element){ |
| return isParentMatch(viewer, element) || isLeafMatch(viewer, element); |
| } |
| |
| /** |
| * Check if the parent (category) is a match to the filter text. The default |
| * behavior returns true if the element has at least one child element that is |
| * a match with the filter text. |
| * |
| * Subclasses may override this method. |
| * |
| * @param viewer the viewer that contains the element |
| * @param element the tree element to check |
| * @return true if the given element has children that matches the filter text |
| */ |
| protected boolean isParentMatch(Viewer viewer, Object element){ |
| Object[] children = ((ITreeContentProvider) ((AbstractTreeViewer) viewer) |
| .getContentProvider()).getChildren(element); |
| |
| if ((children != null) && (children.length > 0)) { |
| return filter(viewer, element, children).length > 0; |
| } |
| return false; |
| } |
| |
| /** |
| * Check if the current (leaf) element is a match with the filter text. |
| * The default behavior checks that the label of the element is a match. |
| * |
| * Subclasses should override this method. |
| * |
| * @param viewer the viewer that contains the element |
| * @param element the tree element to check |
| * @return true if the given element's label matches the filter text |
| */ |
| protected boolean isLeafMatch(Viewer viewer, Object element){ |
| String labelText = ((ILabelProvider) ((StructuredViewer) viewer) |
| .getLabelProvider()).getText(element); |
| |
| if(labelText == null) { |
| return false; |
| } |
| return wordMatches(labelText); |
| } |
| |
| /** |
| * Take the given filter text and break it down into words using a |
| * BreakIterator. |
| * |
| * @param text |
| * @return an array of words |
| */ |
| private String[] getWords(String text){ |
| List words = new ArrayList(); |
| // Break the text up into words, separating based on whitespace and |
| // common punctuation. |
| // Previously used String.split(..., "\\W"), where "\W" is a regular |
| // expression (see the Javadoc for class Pattern). |
| // Need to avoid both String.split and regular expressions, in order to |
| // compile against JCL Foundation (bug 80053). |
| // Also need to do this in an NL-sensitive way. The use of BreakIterator |
| // was suggested in bug 90579. |
| BreakIterator iter = BreakIterator.getWordInstance(); |
| iter.setText(text); |
| int i = iter.first(); |
| while (i != java.text.BreakIterator.DONE && i < text.length()) { |
| int j = iter.following(i); |
| if (j == java.text.BreakIterator.DONE) { |
| j = text.length(); |
| } |
| // match the word |
| if (Character.isLetterOrDigit(text.charAt(i))) { |
| String word = text.substring(i, j); |
| words.add(word); |
| } |
| i = j; |
| } |
| return (String[]) words.toArray(new String[words.size()]); |
| } |
| |
| /** |
| * Return whether or not if any of the words in text satisfy the |
| * match critera. |
| * |
| * @param text the text to match |
| * @return boolean <code>true</code> if one of the words in text |
| * satisifes the match criteria. |
| */ |
| protected boolean wordMatches(String text) { |
| if (text == null) { |
| return false; |
| } |
| |
| //If the whole text matches we are all set |
| if(match(text)) { |
| return true; |
| } |
| |
| // Otherwise check if any of the words of the text matches |
| String[] words = getWords(text); |
| for (int i = 0; i < words.length; i++) { |
| String word = words[i]; |
| if (match(word)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| } |