001: /*******************************************************************************
002: * Copyright (c) 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.cheatsheets.composite.model;
011:
012: import java.util.ArrayList;
013: import java.util.HashSet;
014: import java.util.List;
015: import java.util.Set;
016:
017: import org.eclipse.ui.internal.provisional.cheatsheets.ICompositeCheatSheetTask;
018: import org.eclipse.ui.internal.provisional.cheatsheets.IEditableTask;
019: import org.eclipse.ui.internal.provisional.cheatsheets.ITaskGroup;
020:
021: /**
022: * This class contains utility functions to determine the state of a task based on
023: * dependencies, parent state etc.
024: */
025:
026: public class TaskStateUtilities {
027:
028: /**
029: * Find the most recent ancestor of this task that is blocked
030: * @param task
031: * @return A blocked task or null if no ancestors are blocked
032: */
033: public static ICompositeCheatSheetTask findBlockedAncestor(
034: ICompositeCheatSheetTask task) {
035: ITaskGroup parent = ((AbstractTask) task).getParent();
036: if (parent == null) {
037: return null;
038: }
039: if (!parent.requiredTasksCompleted()) {
040: return parent;
041: }
042: return findBlockedAncestor(parent);
043: }
044:
045: /**
046: * Find the most recent ancestor of this task that is skipped
047: * @param task
048: * @return A skipped task or null if no ancestors are skipped
049: */
050: public static ICompositeCheatSheetTask findSkippedAncestor(
051: ICompositeCheatSheetTask task) {
052: ITaskGroup parent = ((AbstractTask) task).getParent();
053: if (parent == null) {
054: return null;
055: }
056: if (parent.getState() == ICompositeCheatSheetTask.SKIPPED) {
057: return parent;
058: }
059: return findSkippedAncestor(parent);
060: }
061:
062: /**
063: * Find the most recent ancestor of this task that is completed
064: * @param task
065: * @return A completed task or null if no ancestors are completed
066: */
067: public static ICompositeCheatSheetTask findCompletedAncestor(
068: ICompositeCheatSheetTask task) {
069: ITaskGroup parent = ((AbstractTask) task).getParent();
070: if (parent == null) {
071: return null;
072: }
073: if (parent.getState() == ICompositeCheatSheetTask.COMPLETED) {
074: return parent;
075: }
076: return findCompletedAncestor(parent);
077: }
078:
079: /**
080: * Determine whether a task can be skipped.
081: * A task can be skipped if it is skippable, its state is not SKIPPED or completed
082: * and it has no skipped ot completed ancestors.
083: */
084: public static boolean isSkipEnabled(ICompositeCheatSheetTask task) {
085: if (!task.isSkippable())
086: return false;
087: if (task.getState() == ICompositeCheatSheetTask.COMPLETED)
088: return false;
089: if (task.getState() == ICompositeCheatSheetTask.SKIPPED)
090: return false;
091: if (findCompletedAncestor(task) != null)
092: return false;
093: if (findSkippedAncestor(task) != null)
094: return false;
095: return true;
096: }
097:
098: /**
099: * Determine whether a task can be started
100: * Only editable tasks which are not blocked and whose ancestors
101: * are not completed can be started
102: */
103: public static boolean isStartEnabled(ICompositeCheatSheetTask task) {
104: if (!(task instanceof IEditableTask))
105: return false;
106: return isStartable(task);
107: }
108:
109: /**
110: * Determines whether a task is in a state where it has net been started and
111: * cannot be started. This is used to determine when to gray out the icon for a task.
112: */
113: public static boolean isBlocked(ICompositeCheatSheetTask task) {
114: return (task.getState() == ICompositeCheatSheetTask.NOT_STARTED && !isStartable(task));
115: }
116:
117: /**
118: * Determines whether an editable task, or a task group has anything
119: * that would prevent it or its children from being started.
120: */
121: private static boolean isStartable(ICompositeCheatSheetTask task) {
122: if (task.getState() != ICompositeCheatSheetTask.NOT_STARTED)
123: return false;
124: if (findSkippedAncestor(task) != null)
125: return false;
126: if (findCompletedAncestor(task) != null)
127: return false;
128: if (!task.requiredTasksCompleted())
129: return false;
130: if (findBlockedAncestor(task) != null)
131: return false;
132: return true;
133: }
134:
135: /**
136: * Determine which tasks need to be restarted if this tasks is restarted
137: */
138: public static AbstractTask[] getRestartTasks(
139: ICompositeCheatSheetTask task) {
140: List restartables = new ArrayList();
141: Set visited = new HashSet();
142: addRestartableTasks(restartables, task, visited);
143: return (AbstractTask[]) restartables
144: .toArray(new AbstractTask[restartables.size()]);
145: }
146:
147: private static void addRestartableTasks(List restartables,
148: ICompositeCheatSheetTask task, Set visited) {
149: if (visited.contains(task)) {
150: return;
151: }
152: visited.add(task);
153: if (task instanceof IEditableTask
154: && task.getState() != ICompositeCheatSheetTask.NOT_STARTED) {
155: restartables.add(task);
156: } else if (task.getState() == ICompositeCheatSheetTask.SKIPPED) {
157: restartables.add(task);
158: }
159:
160: // Add all children
161: ICompositeCheatSheetTask[] children = task.getSubtasks();
162: for (int i = 0; i < children.length; i++) {
163: addRestartableTasks(restartables, children[i], visited);
164: }
165:
166: // Add all dependents that are started or in progress but not skipped
167: ICompositeCheatSheetTask[] successors = ((AbstractTask) task)
168: .getSuccessorTasks();
169: for (int i = 0; i < successors.length; i++) {
170: int state = successors[i].getState();
171: if (state == ICompositeCheatSheetTask.COMPLETED
172: || state == ICompositeCheatSheetTask.IN_PROGRESS) {
173: addRestartableTasks(restartables, successors[i],
174: visited);
175: }
176: }
177: }
178:
179: }
|