/*******************************************************************************
* Copyright (c) 2011, 2013 Wind River Systems, Inc. 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.ui.search;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.tcf.te.ui.interfaces.ISearchMatcher;
import org.eclipse.tcf.te.ui.interfaces.ISearchable;
/**
* The search engine which uses DFS(depth-first search) algorithm
* to search elements that matches a specified matcher.
*/
public class DepthFirstSearcher extends AbstractSearcher {
private static final int START_INDEX = -1;
private static final int END_INDEX = -2;
// A stack element used to store the current context and the children index
private class StackElement {
Object node;
int index;
public StackElement(Object node, int index) {
this.node = node;
this.index = index;
}
}
// The searching stack in which searching contexts are stored.
private final LinkedList<StackElement> fSearchStack = new LinkedList<StackElement>();
// The searching direction.
private boolean fForeward;
/**
* Create a depth-first searcher with the specified viewer and a
* matcher.
*
* @param viewer The tree viewer.
* @param matcher The search matcher used match a single tree node.
*/
public DepthFirstSearcher(TreeViewer viewer, ISearchable searchable) {
super(viewer, searchable);
}
/*
* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.interfaces.ITreeSearcher#setStartPath(org.eclipse.jface.viewers.TreePath)
*/
@Override
public void setStartPath(TreePath path) {
fSearchStack.clear();
if (path == null) {
Object obj = fViewer.getInput();
path = new TreePath(new Object[] { obj });
}
initSearchContext(path);
}
/**
* Set the searching direction.
*
* @param foreward searching direction.
*/
public void setForeward(boolean foreward) {
fForeward = foreward;
}
/**
* Populate the stacks with initial path.
*
* @param start The initial path.
*/
private void initSearchContext(TreePath start) {
int count = start.getSegmentCount();
for (int i = 0; i < count; i++) {
Object element = start.getSegment(i);
//Push a stack element with initial index as START_INDEX.
fSearchStack.addLast(new StackElement(element, START_INDEX));
if (i > 0) {
IProgressMonitor monitor = new NullProgressMonitor();
Object parent = start.getSegment(i-1);
Object[] children = getUpdatedChildren(parent, monitor);
for (int j = 0; j < children.length; j++) {
if (children[j] == element) {
StackElement parentStack = fSearchStack.get(i - 1);
//Assign the stack element's index with element index in the children list.
parentStack.index = j;
break;
}
}
}
}
}
/**
* Search the tree using a matcher using DFS algorithm.
*
* @param monitor The monitor reporting the progress.
* @return The tree path whose leaf node satisfies the searching rule.
*/
@Override
public TreePath searchNext(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
TreePath result = null;
ISearchMatcher matcher = fSearchable.getMatcher();
while (!fSearchStack.isEmpty() && result == null && !monitor.isCanceled()) { //Search util the stack is empty or the result is found.
StackElement top = fSearchStack.getLast(); //Get the top stack element.
if(!fForeward && top.index == END_INDEX || fForeward && top.index == START_INDEX){
String elementText = fSearchable.getElementText(top.node);
monitor.subTask(elementText);
result = matcher.match(top.node) ? this.createContextPath() : null;
}
if (top.index == END_INDEX) {//If the top index is END_INDEX, it means the node has been finished.
fSearchStack.removeLast(); //Then discard it.
} else {
Object[] children = getUpdatedChildren(top.node, monitor);//Get current node's children.
if (children != null && children.length > 0) {//If there are some children.
if(fForeward && top.index == children.length-1 || !fForeward && top.index == 0){
//If this is the last index
top.index = END_INDEX;
}else{
//Increase or decrease the index according to the direction.
if(top.index == START_INDEX)
top.index = fForeward ? 0 : children.length - 1;
else
top.index = fForeward ? top.index + 1 : top.index - 1;
//Push the child at the index with START_INDEX into the stack.
fSearchStack.addLast(new StackElement(children[top.index], START_INDEX));
}
} else {//If there's no children.
top.index = END_INDEX;//Assign the index with END_INDEX.
}
}
}
if(monitor.isCanceled()) {
throw new InterruptedException();
}
return result;
}
/**
* Create a path using the current elements of the stack.
*
* @return The tree path representing the path of the stack.
*/
private TreePath createContextPath() {
StackElement[] contexts = new StackElement[fSearchStack.size()];
fSearchStack.toArray(contexts);
Object[] elements = new Object[contexts.length];
for (int i = 0; i < contexts.length; i++) {
elements[i] = contexts[i].node;
}
return new TreePath(elements);
}
}