/**
* Copyright (c) 20015 by Brainwy Software Ltda. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.shared_ui.search;
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.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.python.pydev.shared_core.string.StringUtils;
public abstract class AbstractSearchResultsViewerFilter extends ViewerFilter {
protected final IMatcher stringMatcher;
private static Object[] EMPTY = new Object[0];
private Map<Object, Boolean> foundAnyCache = new HashMap<>();
private Map<Object, Object[]> cache = new HashMap<>();
public AbstractSearchResultsViewerFilter(String text, boolean wholeWord) {
stringMatcher = createMatcher(text, wholeWord);
}
@Override
public Object[] filter(Viewer viewer, Object parent, Object[] elements) {
Object[] filtered = cache.get(parent);
if (filtered == null) {
Boolean foundAny = foundAnyCache.get(parent);
if (foundAny != null && !foundAny.booleanValue()) {
filtered = EMPTY;
} else {
filtered = super.filter(viewer, parent, elements);
}
cache.put(parent, filtered);
}
return filtered;
}
@Override
public final boolean select(Viewer viewer, Object parentElement,
Object element) {
return isElementVisible(viewer, element);
}
private boolean computeAnyVisible(Viewer viewer, Object[] elements) {
boolean elementFound = false;
for (int i = 0; i < elements.length && !elementFound; i++) {
Object element = elements[i];
elementFound = isElementVisible(viewer, element);
}
return elementFound;
}
private boolean isAnyVisible(Viewer viewer, Object parent, Object[] elements) {
Object[] filtered = cache.get(parent);
if (filtered != null) {
return filtered.length > 0;
}
Boolean foundAny = foundAnyCache.get(parent);
if (foundAny == null) {
foundAny = computeAnyVisible(viewer, elements) ? Boolean.TRUE : Boolean.FALSE;
foundAnyCache.put(parent, foundAny);
}
return foundAny.booleanValue();
}
public boolean isElementSelectable(Object element) {
return element != null;
}
public boolean isElementVisible(Viewer viewer, Object element) {
return isParentMatch(viewer, element) || isLeafMatch(viewer, element);
}
protected boolean isParentMatch(Viewer viewer, Object element) {
Object[] children = ((ITreeContentProvider) ((AbstractTreeViewer) viewer)
.getContentProvider()).getChildren(element);
if ((children != null) && (children.length > 0)) {
return isAnyVisible(viewer, element, children);
}
return false;
}
public abstract boolean isLeafMatch(Viewer viewer, Object element);
public static IMatcher createMatcher(String text, boolean wholeWord) {
List<String> split = StringUtils.split(text, ',');
ArrayList<StringMatcherWithIndexSemantics> includes = new ArrayList<>(split.size());
ArrayList<StringMatcherWithIndexSemantics> excludes = new ArrayList<>(split.size());
for (String string : split) {
string = string.trim();
if (string.length() > 0) {
if (string.startsWith("!")) {
StringMatcherWithIndexSemantics matcher = new StringMatcherWithIndexSemantics(string.substring(1),
true, wholeWord);
excludes.add(matcher);
} else {
StringMatcherWithIndexSemantics matcher = new StringMatcherWithIndexSemantics(string, true,
wholeWord);
includes.add(matcher);
}
}
}
return new IncludeExcludeMatcher(includes.toArray(new StringMatcherWithIndexSemantics[0]),
excludes.toArray(new StringMatcherWithIndexSemantics[0]));
}
public static interface IMatcher {
boolean match(String text);
}
public static class IncludeExcludeMatcher implements IMatcher {
private final int strategy;
private final StringMatcherWithIndexSemantics[] includes;
private final StringMatcherWithIndexSemantics[] excludes;
private static final int ACCEPT_ALL = 0;
private static final int ONLY_INCLUDES = 1;
private static final int ONLY_EXCLUDES = 2;
private static final int EXCLUDE_AND_INCLUDES = 3;
public IncludeExcludeMatcher(StringMatcherWithIndexSemantics[] includes,
StringMatcherWithIndexSemantics[] excludes) {
this.includes = includes;
this.excludes = excludes;
if (includes.length == 0 && excludes.length == 0) {
strategy = ACCEPT_ALL;
} else if (includes.length > 0 && excludes.length == 0) {
strategy = ONLY_INCLUDES;
} else if (includes.length == 0 && excludes.length > 0) {
strategy = ONLY_EXCLUDES;
} else {
strategy = EXCLUDE_AND_INCLUDES;
}
}
@Override
public boolean match(String text) {
final int includesLen = includes.length;
final int excludesLen = excludes.length;
switch (strategy) {
case ACCEPT_ALL:
return true;
case ONLY_INCLUDES:
for (int i = 0; i < includesLen; i++) {
StringMatcherWithIndexSemantics s = includes[i];
if (s.match(text)) {
return true;
}
}
return false;
case ONLY_EXCLUDES:
for (int i = 0; i < excludesLen; i++) {
StringMatcherWithIndexSemantics s = excludes[i];
if (s.match(text)) {
return false;
}
}
return true;
case EXCLUDE_AND_INCLUDES:
// If we have includes and excludes, we'll first check if an include matches
// and then we'll remove the excludes.
for (int i = 0; i < includesLen; i++) {
StringMatcherWithIndexSemantics s = includes[i];
if (s.match(text)) {
for (i = 0; i < excludesLen; i++) {
s = excludes[i];
if (s.match(text)) {
return false;
}
}
return true;
}
}
return false;
}
throw new RuntimeException("Invalid strategy: " + strategy);
}
}
/**
* @return true if it should be added and false otherwise.
*/
public static boolean filterMatches(String text, IMatcher stringMatcher) {
return stringMatcher.match(text);
}
public void clearCache() {
cache.clear();
foundAnyCache.clear();
}
}