Skip to main content
summaryrefslogtreecommitdiffstats
blob: 48441b465918c634a93dfa6ac4869dea820d9797 (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*******************************************************************************
 * Copyright (c) 2012, 2016 Oracle. 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:
 *     Oracle - initial API and implementation
 ******************************************************************************/
package org.eclipse.jpt.common.utility.internal.command;

import java.util.ArrayList;
import org.eclipse.jpt.common.utility.command.Command;
import org.eclipse.jpt.common.utility.command.CommandContext;
import org.eclipse.jpt.common.utility.command.RepeatingCommand;
import org.eclipse.jpt.common.utility.exception.ExceptionHandler;
import org.eclipse.jpt.common.utility.internal.StackTrace;
import org.eclipse.jpt.common.utility.internal.ObjectTools;

/**
 * Wrap a repeating {@link Command}.
 */
public class RepeatingCommandWrapper
	implements RepeatingCommand
{
	/**
	 * The client-supplied command that performs the execution. It may
	 * trigger further calls to {@link #execute()} (i.e. the <em>wrapped</em>
	 * command's execution may recurse back to the client code that executes the
	 * <em>wrapper</em> command).
	 */
	/* CU private */ final Command command;

	/**
	 * The command executed whenever the wrapped {@link #command} must be
	 * executed and it is not already executing. If the wrapped {@link #command}
	 * is already executing, it will simply be re-executed directly (once it has
	 * completed its current execution), as opposed to calling the start
	 * command.
	 */
	private final Command startCommand;

	/**
	 * The client-supplied command context that provides the context for the
	 * {@link #startCommand start command}. By default, the start command is
	 * executed directly; but this context provides a hook for executing the
	 * {@link #startCommand start command} asynchronously; after which,
	 * subsequent overlapping executions are executed synchronously.
	 */
	private final CommandContext startCommandContext;

	/**
	 * This handles the exceptions thrown by the <em>wrapped</em> command.
	 */
	final ExceptionHandler exceptionHandler;

	/**
	 * The command wrapper's state.
	 */
	final RepeatingCommandState state;

	/**
	 * List of stack traces for each (repeating) invocation of the command,
	 * starting with the initial invocation. The list is cleared with each
	 * initial invocation of the command.
	 */
	private final ArrayList<StackTrace> stackTraces = DEBUG ? new ArrayList<>() : null;
	// see RepeatingCommandWrapperTests.testDEBUG()
	private static final boolean DEBUG = false;


	// ********** construction **********

	/**
	 * Construct a repeating command wrapper that executes the specified
	 * command. Any exceptions thrown by the command will be handled by the
	 * specified exception handler.
	 */
	public RepeatingCommandWrapper(Command command, ExceptionHandler exceptionHandler) {
		this(command, DefaultCommandContext.instance(), exceptionHandler);
	}

	/**
	 * Construct a repeating command wrapper that executes the specified
	 * command and uses the specified command context to execute the wrapped
	 * command whenever it is not already executing.
	 * Any exceptions thrown by the command will be handled by the
	 * specified exception handler.
	 */
	public RepeatingCommandWrapper(Command command, CommandContext startCommandContext, ExceptionHandler exceptionHandler) {
		super();
		if ((command == null) || (startCommandContext == null) || (exceptionHandler == null)) {
			throw new NullPointerException();
		}
		this.command = command;
		this.startCommandContext = startCommandContext;
		this.startCommand = this.buildStartCommand();
		this.exceptionHandler = exceptionHandler;
		this.state = this.buildState();
	}

	private Command buildStartCommand() {
		return new StartCommand();
	}

	private RepeatingCommandState buildState() {
		return new RepeatingCommandState();
	}


	// ********** RepeatingCommand implementation **********

	public void start() {
		this.state.start();
	}

	/**
	 * It is possible to come back here if the wrapped command recurses
	 * to the client and triggers another execution.
	 */
	public synchronized void execute() {
		if (this.state.isReadyToStartExecutionCycle()) {
			if (DEBUG) {
				this.stackTraces.clear();
				this.stackTraces.add(new StackTrace());
			}
			this.executeStartCommand();
		} else {
			if (DEBUG) {
				this.stackTraces.add(new StackTrace());
			}
		}
	}

	/* private protected */ void executeStartCommand() {
		this.startCommandContext.execute(this.startCommand);
	}

	public void stop() throws InterruptedException {
		this.state.stop();
	}

	/**
	 * The start command.
	 * @see #startCommandContext
	 */
	/* CU private */ class StartCommand
		implements Command
	{
		public void execute() {
			RepeatingCommandWrapper.this.execute_();
		}
		@Override
		public String toString() {
			return ObjectTools.toString(this, RepeatingCommandWrapper.this.command);
		}
	}

	/**
	 * This method will be called only once per execution cycle.
	 * Any further calls to {@link #execute()} will
	 * simply set the {@link #state} to "repeat",
	 * causing the command to execute again.
	 */
	/* CU private */ void execute_() {
		if ( ! this.state.wasStoppedBeforeFirstExecutionCouldStart()) {
			do {
				this.executeCommand();
			} while (this.state.isRepeat());
		}
	}

	/**
	 * Execute the client-supplied command. Do not allow any unhandled
	 * exceptions to kill the wrapper. Pass to the exception handler.
	 * @see NotifyingRepeatingCommandWrapper
	 */
	/* private protected */ void executeCommand() {
		try {
			this.command.execute();
		} catch (Throwable ex) {
			this.exceptionHandler.handleException(ex);
		}
	}

	@Override
	public String toString() {
		return ObjectTools.toString(this, this.command);
	}
}

Back to the top