Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 0d6a4f6039fdcd12f2831399d5ccaae07d4bf41d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*******************************************************************************
 * Copyright (c) 2005, 2019 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.ui.internal.cheatsheets.composite.model;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.ui.internal.provisional.cheatsheets.ICompositeCheatSheetTask;
import org.eclipse.ui.internal.provisional.cheatsheets.ITaskGroup;

public class SuccesorTaskFinder {

	private AbstractTask currentTask;
	ICompositeCheatSheetTask bestLaterTask;
	ICompositeCheatSheetTask bestEarlierTask;
	private boolean seenThisTask;

	public SuccesorTaskFinder(ICompositeCheatSheetTask task) {
		currentTask = (AbstractTask)task;
	}

	/**
	 * Find the next recommended task or tasks to be completed.
	 * Algorithm - visualize the tree as having its root at the top,
	 * children below and to the left of their parents and then
	 * search the tree from left to right. Look for
	 * the best predecessor which is the first task to the
	 * left of this task that is runnable and the best successor
	 * which is the first task to the
	 * right of this task which is runnable.
	 * @param task The task which was just completed
	 * @return An array of tasks which can be started
	 */
    public ICompositeCheatSheetTask[] getRecommendedSuccessors()
    {
    	// TODO this code could be moved to TaskGroup
    	if (ITaskGroup.CHOICE.equals(currentTask.getKind())) {
    		// For a choice if more than one child is runnable return it
			List<ICompositeCheatSheetTask> runnableChoices = findRunnableChoices();
    		if (runnableChoices.size() != 0) {
				return runnableChoices.toArray(new ICompositeCheatSheetTask[runnableChoices.size()]);
    		}
    	}
    	return getBestSuccessor();
    }

	private List<ICompositeCheatSheetTask> findRunnableChoices() {
		List<ICompositeCheatSheetTask> result = new ArrayList<>();
		if (isStartable(currentTask)) {
			ICompositeCheatSheetTask[] subtasks = currentTask.getSubtasks();
			for (ICompositeCheatSheetTask subtask : subtasks) {
				if (isStartable(subtask)) {
					result.add(subtask);
				}
			}
		}
		return result;
	}

	private boolean isStartable(ICompositeCheatSheetTask task) {
		int state = task.getState();
		return (state != ICompositeCheatSheetTask.COMPLETED &&
			    state != ICompositeCheatSheetTask.SKIPPED &&
			    task.requiredTasksCompleted());
	}

	private ICompositeCheatSheetTask[] getBestSuccessor() {
		bestLaterTask = null;
    	bestEarlierTask = null;
    	seenThisTask = false;
		searchRunnableChildren(currentTask.getCompositeCheatSheet().getRootTask());
		// If there is a task which is found later in the tree return
		// that, otherwise an earlier task.
		if (bestLaterTask != null) {
			return new ICompositeCheatSheetTask[] {bestLaterTask};
		}
		if (bestEarlierTask != null) {
			return new ICompositeCheatSheetTask[] {bestEarlierTask};
		}
		return new ICompositeCheatSheetTask[0];
	}

	private void searchRunnableChildren(ICompositeCheatSheetTask task) {
		// Don't search completed tasks or their children
		// and stop searching if we've already found the best successor
		if (bestLaterTask != null) {
			return;
		}
		if (task == currentTask) {
			seenThisTask = true;
		}
		if (task.getState() == ICompositeCheatSheetTask.COMPLETED ||
			task.getState() == ICompositeCheatSheetTask.SKIPPED ) {
			if (isTaskAncestor(task, currentTask)) {
				seenThisTask = true;
			}
			return;
		}

		if ( isStartable(task) && task != currentTask) {
			if (seenThisTask) {
				if (bestLaterTask == null) {
					bestLaterTask = task;
				}
			} else {
				if (bestEarlierTask == null) {
					bestEarlierTask = task;
				}
			}
		}

		ICompositeCheatSheetTask[] subtasks = task.getSubtasks();
		for (ICompositeCheatSheetTask subtask : subtasks) {
			searchRunnableChildren(subtask);
		}

	}

	private boolean isTaskAncestor(ICompositeCheatSheetTask ancestorCandididate, ICompositeCheatSheetTask task) {
		ICompositeCheatSheetTask nextTask = task;
		while (nextTask != null) {
			if (nextTask == ancestorCandididate) {
				return true;
			}
			nextTask = nextTask.getParent();
		}
		return false;
	}
}

Back to the top