diff options
author | Markus Duft | 2016-11-25 13:41:41 +0000 |
---|---|---|
committer | Sarika Sinha | 2017-01-13 03:30:26 +0000 |
commit | 1c1d17b82a223fb8fcc69b4883a71b8744899ccb (patch) | |
tree | c202e984d42407b607ac29cf95168bd6c04ffc29 | |
parent | fb136251cd02ed0b4812d1c65923b3abe79a1d40 (diff) | |
download | eclipse.platform.debug-1c1d17b82a223fb8fcc69b4883a71b8744899ccb.tar.gz eclipse.platform.debug-1c1d17b82a223fb8fcc69b4883a71b8744899ccb.tar.xz eclipse.platform.debug-1c1d17b82a223fb8fcc69b4883a71b8744899ccb.zip |
Launch Groups: Support to wait for output on child stdout
This adds support to listen for output on the console of the launched
item before continuing the launch.
Bug: 508420
Change-Id: I84027592c82b546ddf60c415bcc7047d9bc5490a
Signed-off-by: Markus Duft <markus.duft@ssi-schaefer.com>
13 files changed, 407 insertions, 51 deletions
diff --git a/org.eclipse.debug.core/META-INF/MANIFEST.MF b/org.eclipse.debug.core/META-INF/MANIFEST.MF index e306ac17c..5e1017435 100644 --- a/org.eclipse.debug.core/META-INF/MANIFEST.MF +++ b/org.eclipse.debug.core/META-INF/MANIFEST.MF @@ -15,6 +15,7 @@ Export-Package: org.eclipse.debug.core, org.eclipse.debug.internal.core;x-friends:="org.eclipse.debug.ui,org.eclipse.debug.tests,org.eclipse.debug.examples.mixedmode", org.eclipse.debug.internal.core.commands;x-friends:="org.eclipse.debug.ui", org.eclipse.debug.internal.core.groups, + org.eclipse.debug.internal.core.groups.observer;x-internal:=true, org.eclipse.debug.internal.core.sourcelookup;x-friends:="org.eclipse.debug.ui", org.eclipse.debug.internal.core.sourcelookup.containers;x-friends:="org.eclipse.debug.ui", org.eclipse.debug.internal.core.variables;x-friends:="org.eclipse.debug.ui,org.eclipse.jdt.debug.ui" diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java index 48bec126b..e3bb0ccc7 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.java @@ -32,20 +32,19 @@ public class DebugCoreMessages extends NLS { public static String DebugPlugin_8; public static String DebugPlugin_Eclipse_runtime_does_not_support_working_directory_2; public static String EnvironmentVariableResolver_0; + public static String GroupLaunchConfigurationDelegate_waiting; + public static String GroupLaunchConfigurationDelegate_failedWait; public static String GroupLaunchConfigurationDelegate_Delay; public static String GroupLaunchConfigurationDelegate_Delaying; public static String GroupLaunchConfigurationDelegate_Launching; public static String GroupLaunchConfigurationDelegate_mode_debug; - public static String GroupLaunchConfigurationDelegate_mode_inherit; - public static String GroupLaunchConfigurationDelegate_mode_profile; - public static String GroupLaunchConfigurationDelegate_mode_run; - public static String GroupLaunchConfigurationDelegate_None; public static String GroupLaunchConfigurationDelegate_Wait_until_terminated; public static String GroupLaunchConfigurationDelegate_Waiting_for_termination; + public static String GroupLaunchElement_outputRegexp; public static String LaunchConfiguration_0; public static String LaunchConfiguration_11; public static String LaunchConfiguration_13; diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties index 0e20ad951..eb9e35f51 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/DebugCoreMessages.properties @@ -26,6 +26,8 @@ DebugPlugin_6=An exception occurred in asynchronous runnable. DebugPlugin_7=An exception occurred while filtering debug events. DebugPlugin_8=An exception occurred while dispatching debug events. EnvironmentVariableResolver_0=Environment variable not specified +GroupLaunchConfigurationDelegate_waiting=Waiting for ''{0}'' on the console of ''{1}''. +GroupLaunchConfigurationDelegate_failedWait=Failed to await output ''{0}'' on the console of ''{1}''. GroupLaunchConfigurationDelegate_Delay=Delay GroupLaunchConfigurationDelegate_Delaying=Delaying next launch by {0} seconds GroupLaunchConfigurationDelegate_Launching=Launching ''{0}'' @@ -36,6 +38,7 @@ GroupLaunchConfigurationDelegate_mode_run=run GroupLaunchConfigurationDelegate_None=None GroupLaunchConfigurationDelegate_Wait_until_terminated=Wait until terminated GroupLaunchConfigurationDelegate_Waiting_for_termination=Waiting for termination of ''{0}'' +GroupLaunchElement_outputRegexp=Wait for console output (regexp) SystemPropertyResolver_0=System property not specified InputStreamMonitor_label=Input Stream Monitor Launch_terminate_failed=Terminate failed diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java index 2c1b519c6..e611a4686 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunch.java @@ -91,7 +91,8 @@ public class GroupLaunch extends Launch implements ILaunchesListener2 { } if (subLaunches.size() == 0) { - return false; + return fLaunched; // in case we're done launching and there is + // nobody -> terminated } for (ILaunch launch : subLaunches.keySet()) { diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java index fccd5f8c3..69885d7cd 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchConfigurationDelegate.java @@ -19,6 +19,10 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; @@ -33,10 +37,13 @@ import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.IStatusHandler; import org.eclipse.debug.core.model.ILaunchConfigurationDelegate2; +import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.LaunchConfigurationDelegate; import org.eclipse.debug.internal.core.DebugCoreMessages; import org.eclipse.debug.internal.core.IInternalDebugCoreConstants; import org.eclipse.debug.internal.core.groups.GroupLaunchElement.GroupElementPostLaunchAction; +import org.eclipse.debug.internal.core.groups.observer.ProcessObserver; +import org.eclipse.debug.internal.core.groups.observer.StreamObserver; import org.eclipse.osgi.util.NLS; /** @@ -193,11 +200,46 @@ public class GroupLaunchConfigurationDelegate extends LaunchConfigurationDelegat } break; + case OUTPUT_REGEXP: + String regexp = (String) le.actionParam; + if (regexp != null) { + monitor.subTask(NLS.bind(DebugCoreMessages.GroupLaunchConfigurationDelegate_waiting, regexp, subLaunch.getLaunchConfiguration().getName())); + if (!waitForOutputMatching(subLaunch, monitor, regexp)) { + throw new RuntimeException(NLS.bind(DebugCoreMessages.GroupLaunchConfigurationDelegate_failedWait, regexp, le.name)); + } + } + + break; + default: assert false : "new post launch action type is missing logic"; //$NON-NLS-1$ } } + // blocks until a specific string is in the log output + private boolean waitForOutputMatching(ILaunch launch, IProgressMonitor m, String regexp) { + int processCount = launch.getProcesses().length; + final ExecutorService executor = Executors.newCachedThreadPool(); + final CountDownLatch countDownLatch = new CountDownLatch(processCount); + Future<Integer> process = null; + for (IProcess p : launch.getProcesses()) { + process = executor.submit(new ProcessObserver(m, p, countDownLatch)); + executor.submit(new StreamObserver(m, p, regexp, countDownLatch)); + } + try { + countDownLatch.await(); + } catch (InterruptedException e) { + // should not happen at all! + } + executor.shutdown(); + // process terminated before condition + if (process == null || process.isDone()) { + return false; + } + // condition matched + return true; + } + /* * (non-Javadoc) * @see @@ -293,6 +335,9 @@ public class GroupLaunchConfigurationDelegate extends LaunchConfigurationDelegat DebugPlugin.log(exc); } } + if (action == GroupElementPostLaunchAction.OUTPUT_REGEXP) { + actionParam = attrs.get(getProp(index, ACTION_PARAM_PROP)); + } el.action = action; el.actionParam = actionParam; if (attrs.containsKey(getProp(index, ADOPT_PROP))) { diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java index 767f90514..5a409b8c8 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/GroupLaunchElement.java @@ -34,7 +34,8 @@ public class GroupLaunchElement { public static enum GroupElementPostLaunchAction { NONE(DebugCoreMessages.GroupLaunchConfigurationDelegate_None), // WAIT_FOR_TERMINATION(DebugCoreMessages.GroupLaunchConfigurationDelegate_Wait_until_terminated), // - DELAY(DebugCoreMessages.GroupLaunchConfigurationDelegate_Delay); + DELAY(DebugCoreMessages.GroupLaunchConfigurationDelegate_Delay), // + OUTPUT_REGEXP(DebugCoreMessages.GroupLaunchElement_outputRegexp); private final String description; diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/ProcessObserver.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/ProcessObserver.java new file mode 100644 index 000000000..7261d9234 --- /dev/null +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/ProcessObserver.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2012, 2016 SSI Schaefer 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: + * SSI Schaefer + *******************************************************************************/ +package org.eclipse.debug.internal.core.groups.observer; + +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.model.IProcess; + +/** + * The {@code ProcessObserver} observes a given {@linkplain IProcess process} instance and notifies + * a {@linkplain CountDownLatch synchronization object} when the process terminates. + */ +public final class ProcessObserver implements Callable<Integer> { + private final IProcess p; + private final IProgressMonitor pMonitor; + private final CountDownLatch countDownLatch; + + public ProcessObserver(IProgressMonitor monitor, IProcess p, CountDownLatch countDownLatch) { + this.p = p; + this.pMonitor = monitor; + this.countDownLatch = countDownLatch; + } + + @Override + public Integer call() throws Exception { + try { + while (!p.isTerminated() && !pMonitor.isCanceled()) { + TimeUnit.MILLISECONDS.sleep(250); + + if (countDownLatch.getCount() == 0) { + break; + } + } + // check if terminated or timeout + if (p.isTerminated()) { + return p.getExitValue(); + } + return 0; + } finally { + countDownLatch.countDown(); + } + } +} diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/StreamObserver.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/StreamObserver.java new file mode 100644 index 000000000..437382149 --- /dev/null +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/groups/observer/StreamObserver.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2012, 2016 SSI Schaefer 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: + * SSI Schaefer + *******************************************************************************/ +package org.eclipse.debug.internal.core.groups.observer; + +import java.util.concurrent.CountDownLatch; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.IStreamListener; +import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.core.model.IStreamMonitor; +import org.eclipse.debug.core.model.IStreamsProxy; + +/** + * The {@code StreamObserver} observes a given {@linkplain IStreamsProxy output stream} instance and + * notifies a {@linkplain CountDownLatch synchronization object} when a given string appears in the + * output. + */ +public class StreamObserver implements Runnable { + private final String stringPattern; + private final IProcess process; + private final CountDownLatch countDownLatch; + private final IProgressMonitor pMonitor; + + public StreamObserver(IProgressMonitor monitor, IProcess process, String pattern, + CountDownLatch countDownLatch) { + this.process = process; + this.pMonitor = monitor; + this.stringPattern = pattern; + this.countDownLatch = countDownLatch; + } + + @Override + public void run() { + // append wild card if not provided + StringBuilder patternBuilder = new StringBuilder(); + if (!stringPattern.startsWith(".*")) { //$NON-NLS-1$ + patternBuilder.append(".*"); //$NON-NLS-1$ + } + patternBuilder.append(stringPattern); + if (!stringPattern.endsWith(".*")) { //$NON-NLS-1$ + patternBuilder.append(".*"); //$NON-NLS-1$ + } + // create pattern and start listening to the output + final Pattern pattern = Pattern.compile(patternBuilder.toString(), Pattern.MULTILINE); + final IStreamMonitor outputStreamMonitor = process.getStreamsProxy() + .getOutputStreamMonitor(); + outputStreamMonitor.addListener(new IStreamListener() { + @Override + public void streamAppended(String text, IStreamMonitor monitor) { + if (countDownLatch.getCount() == 0) { + outputStreamMonitor.removeListener(this); + return; + } + + Matcher matcher = pattern.matcher(text); + if (!matcher.find() && !pMonitor.isCanceled()) { + return; + } + countDownLatch.countDown(); + } + }); + } +} diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/launching/LaunchGroupTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/launching/LaunchGroupTests.java index b046728c6..b2bb6ce27 100644 --- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/launching/LaunchGroupTests.java +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/launching/LaunchGroupTests.java @@ -3,7 +3,11 @@ package org.eclipse.debug.tests.launching; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.core.runtime.CoreException; @@ -13,8 +17,11 @@ import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchListener; import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.core.IStreamListener; import org.eclipse.debug.core.model.IDisconnect; import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.core.model.IStreamMonitor; +import org.eclipse.debug.core.model.IStreamsProxy; import org.eclipse.debug.internal.core.groups.GroupLaunchConfigurationDelegate; import org.eclipse.debug.internal.core.groups.GroupLaunchElement; import org.eclipse.debug.internal.core.groups.GroupLaunchElement.GroupElementPostLaunchAction; @@ -149,23 +156,7 @@ public class LaunchGroupTests extends AbstractLaunchTest { if (l.getLaunchConfiguration().contentsEqual(t1)) { // add a dummy process, otherwise the launch never // terminates... - InvocationHandler handler = new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - String name = method.getName(); - if (name.equals("equals")) { //$NON-NLS-1$ - return args.length == 1 && proxy == args[0]; - } - if (name.equals("getStreamsProxy")) { //$NON-NLS-1$ - return null; - } - return Boolean.TRUE; - } - }; - IProcess process = (IProcess) Proxy.newProxyInstance(LaunchGroupTests.class.getClassLoader(), new Class[] { - IProcess.class, - IDisconnect.class }, handler); - l.addProcess(process); + attachDummyProcess(l); l.terminate(); } } @@ -174,6 +165,7 @@ public class LaunchGroupTests extends AbstractLaunchTest { e.printStackTrace(); } } + }.start(); // attention: need to do this before launching! @@ -192,6 +184,7 @@ public class LaunchGroupTests extends AbstractLaunchTest { assertTrue("history[2] should be Test1", history[2].contentsEqual(t1)); //$NON-NLS-1$ } + public void testAdopt() throws Exception { final ILaunchConfiguration t1 = getLaunchConfiguration("Test1"); //$NON-NLS-1$ final ILaunchConfiguration grp = createLaunchGroup(DEF_GRP_NAME, createLaunchGroupElement(t1, GroupElementPostLaunchAction.NONE, null, false), createLaunchGroupElement(t1, GroupElementPostLaunchAction.NONE, null, true)); @@ -237,4 +230,166 @@ public class LaunchGroupTests extends AbstractLaunchTest { assertTrue("Test1 should be launched only once", launchCount.get() == 1); //$NON-NLS-1$ } + public void testWaitForOutput() throws Exception { + String testOutput = "TestOutput"; //$NON-NLS-1$ + + final ILaunchConfiguration t1 = getLaunchConfiguration("Test1"); //$NON-NLS-1$ + ILaunchConfiguration t2 = getLaunchConfiguration("Test2"); //$NON-NLS-1$ + ILaunchConfiguration grp = createLaunchGroup(DEF_GRP_NAME, createLaunchGroupElement(t1, GroupElementPostLaunchAction.OUTPUT_REGEXP, testOutput, false), createLaunchGroupElement(t2, GroupElementPostLaunchAction.NONE, null, false)); + + // attach a dummy process to the launch once it is launched. + final DummyAttachListener attachListener = new DummyAttachListener(t1); + getLaunchManager().addLaunchListener(attachListener); + + // start a thread that will produce output on the dummy process after + // some time + new Thread("Output Producer") { //$NON-NLS-1$ + @Override + public void run() { + try { + // wait some time before causing the group to continue + Thread.sleep(2000); + attachListener.getStream().write("TestOutput"); //$NON-NLS-1$ + } catch (Exception e) { + e.printStackTrace(); + } + } + }.start(); + + long start = System.currentTimeMillis(); + + // attention: need to do this before launching! + LaunchHistory runHistory = getRunLaunchHistory(); + + // launching the group should block until the output is produced + grp.launch(ILaunchManager.RUN_MODE, new NullProgressMonitor()); + getLaunchManager().removeLaunchListener(attachListener); + + assertTrue("output was not awaited", (System.currentTimeMillis() - start) > 2000); //$NON-NLS-1$ + + ILaunchConfiguration[] history = runHistory.getHistory(); + assertTrue("history should be size 3", history.length == 3); //$NON-NLS-1$ + assertTrue("history[0] should be Test Group", history[0].contentsEqual(grp)); //$NON-NLS-1$ + assertTrue("history[1] should be Test2", history[1].contentsEqual(t2)); //$NON-NLS-1$ + assertTrue("history[2] should be Test1", history[2].contentsEqual(t1)); //$NON-NLS-1$ + } + + protected Set<ILaunch> findRunningLaunch(ILaunchConfiguration cfg) { + Set<ILaunch> result = new HashSet<>(); + for (ILaunch l : getLaunchManager().getLaunches()) { + if (l.isTerminated()) { + continue; + } + if (l.getLaunchConfiguration().equals(cfg)) { + result.add(l); + } + } + return result; + } + + private static DummyStream attachDummyProcess(final ILaunch l) { + final DummyStream dummy = new DummyStream(); + final InvocationHandler streamProxyHandler = new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String name = method.getName(); + if (name.equals("getOutputStreamMonitor")) { //$NON-NLS-1$ + return dummy; + } + return null; + } + }; + + final InvocationHandler handler = new InvocationHandler() { + boolean terminated = false; + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String name = method.getName(); + if (name.equals("equals")) { //$NON-NLS-1$ + return args.length == 1 && proxy == args[0]; + } + if (name.equals("getStreamsProxy")) { //$NON-NLS-1$ + return Proxy.newProxyInstance(LaunchGroupTests.class.getClassLoader(), new Class[] { + IStreamsProxy.class }, streamProxyHandler); + } + if (name.equals("getLaunch")) { //$NON-NLS-1$ + return l; + } + if (name.equals("getLabel")) { //$NON-NLS-1$ + return l.getLaunchConfiguration().getName(); + } + if (name.equals("getAttribute")) { //$NON-NLS-1$ + return null; + } + if (name.equals("isTerminated")) { //$NON-NLS-1$ + return terminated; + } + if (name.equals("terminate")) { //$NON-NLS-1$ + terminated = true; + } + return Boolean.TRUE; + } + }; + IProcess process = (IProcess) Proxy.newProxyInstance(LaunchGroupTests.class.getClassLoader(), new Class[] { + IProcess.class, IDisconnect.class }, handler); + l.addProcess(process); + return dummy; + } + + private static final class DummyStream implements IStreamMonitor { + + private final List<IStreamListener> listeners = new ArrayList<>(); + + @Override + public void addListener(IStreamListener listener) { + listeners.add(listener); + } + + @Override + public String getContents() { + return null; + } + + @Override + public void removeListener(IStreamListener listener) { + listeners.remove(listener); + } + + public void write(String s) { + for (IStreamListener l : listeners) { + l.streamAppended(s, null); + } + } + + } + + private static final class DummyAttachListener implements ILaunchListener { + + private ILaunchConfiguration cfg; + private DummyStream stream; + + public DummyAttachListener(ILaunchConfiguration cfg) { + this.cfg = cfg; + } + + public DummyStream getStream() { + return stream; + } + + @Override + public void launchRemoved(ILaunch launch) { + } + + @Override + public void launchAdded(ILaunch launch) { + if (launch.getLaunchConfiguration().equals(cfg)) + stream = attachDummyProcess(launch); + } + + @Override + public void launchChanged(ILaunch launch) { + } + + } + } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.java index 49e6ab536..4cb94c20b 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.java @@ -257,6 +257,9 @@ public class DebugUIMessages extends NLS { public static String GroupLaunchConfigurationSelectionDialog_9; public static String GroupLaunchConfigurationSelectionDialog_adoptText; public static String GroupLaunchConfigurationSelectionDialog_adoptTooltip; + public static String GroupLaunchConfigurationSelectionDialog_errorNoRegexp; + public static String GroupLaunchConfigurationSelectionDialog_regexp; + public static String GroupLaunchConfigurationTabGroup_0; public static String GroupLaunchConfigurationTabGroup_1; public static String GroupLaunchConfigurationTabGroup_10; public static String GroupLaunchConfigurationTabGroup_12; diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.properties b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.properties index 912758da5..e082ca5a5 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.properties +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIMessages.properties @@ -130,6 +130,9 @@ GroupLaunchConfigurationSelectionDialog_14=Add one or more launch configurations GroupLaunchConfigurationSelectionDialog_15=Edit an existing entry in the launch group GroupLaunchConfigurationSelectionDialog_adoptText=Adopt launch if already running GroupLaunchConfigurationSelectionDialog_adoptTooltip=Instead of launching a new process, adds the running launch to the group. +GroupLaunchConfigurationSelectionDialog_errorNoRegexp=Enter a regular expression to wait for +GroupLaunchConfigurationSelectionDialog_regexp=Regular Expression: +GroupLaunchConfigurationTabGroup_0=Wait for output matching "{0}" GroupLaunchConfigurationTabGroup_1=&Up GroupLaunchConfigurationTabGroup_2=Do&wn GroupLaunchConfigurationTabGroup_3=&Edit... diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationSelectionDialog.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationSelectionDialog.java index 8ae809995..d79565184 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationSelectionDialog.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationSelectionDialog.java @@ -39,6 +39,7 @@ import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.ILaunchGroup; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; @@ -81,8 +82,8 @@ class GroupLaunchConfigurationSelectionDialog extends TitleAreaDialog implements private ViewerFilter emptyTypeFilter; private IStructuredSelection fInitialSelection; private ComboControlledStackComposite fStackComposite; - private Label fDelayAmountLabel; - private Text fDelayAmountWidget; // in seconds + private Label fActionParamLabel; + private Text fActionParamWidget; // in seconds private boolean fForEditing; // true if dialog was opened to edit an entry, // otherwise it was opened to add one private ILaunchConfigurationType groupType; @@ -236,6 +237,7 @@ class GroupLaunchConfigurationSelectionDialog extends TitleAreaDialog implements combo.add(GroupElementPostLaunchAction.NONE.getDescription()); combo.add(GroupElementPostLaunchAction.WAIT_FOR_TERMINATION.getDescription()); combo.add(GroupElementPostLaunchAction.DELAY.getDescription()); + combo.add(GroupElementPostLaunchAction.OUTPUT_REGEXP.getDescription()); combo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { @@ -247,36 +249,46 @@ class GroupLaunchConfigurationSelectionDialog extends TitleAreaDialog implements }); combo.setText(action.getDescription()); - fDelayAmountLabel = new Label(comp, SWT.NONE); - fDelayAmountLabel.setText(DebugUIMessages.GroupLaunchConfigurationSelectionDialog_9); - - fDelayAmountWidget = new Text(comp, SWT.SINGLE | SWT.BORDER); - GridData gridData = new GridData(); - gridData.widthHint = convertWidthInCharsToPixels(8); - fDelayAmountWidget.setLayoutData(gridData); - fDelayAmountWidget.addModifyListener(new ModifyListener() { + fActionParamLabel = new Label(comp, SWT.NONE); + fActionParamWidget = new Text(comp, SWT.SINGLE | SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).applyTo(fActionParamWidget); + fActionParamWidget.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { String text = ((Text) e.widget).getText(); - try { - actionParam = Integer.valueOf(text); - } catch (NumberFormatException exc) { - actionParam = null; + if (action == GroupElementPostLaunchAction.DELAY) { + try { + actionParam = Integer.valueOf(text); + } catch (NumberFormatException exc) { + actionParam = null; + } + } else if (action == GroupElementPostLaunchAction.OUTPUT_REGEXP) { + actionParam = text; } validate(); } }); if (actionParam instanceof Integer) { - fDelayAmountWidget.setText(((Integer) actionParam).toString()); + fActionParamWidget.setText(((Integer) actionParam).toString()); + } else if (actionParam instanceof String) { + fActionParamWidget.setText(actionParam.toString()); } showHideDelayAmountWidgets(); } private void showHideDelayAmountWidgets() { - final boolean visible = action == GroupElementPostLaunchAction.DELAY; - fDelayAmountLabel.setVisible(visible); - fDelayAmountWidget.setVisible(visible); + final boolean visible = (action == GroupElementPostLaunchAction.DELAY || action == GroupElementPostLaunchAction.OUTPUT_REGEXP); + fActionParamLabel.setVisible(visible); + fActionParamWidget.setVisible(visible); + + if (action == GroupElementPostLaunchAction.DELAY) { + fActionParamLabel.setText(DebugUIMessages.GroupLaunchConfigurationSelectionDialog_9); + } else if (action == GroupElementPostLaunchAction.OUTPUT_REGEXP) { + fActionParamLabel.setText(DebugUIMessages.GroupLaunchConfigurationSelectionDialog_regexp); + } + + fActionParamLabel.getParent().layout(); } public ILaunchConfiguration[] getSelectedLaunchConfigurations() { @@ -389,6 +401,11 @@ class GroupLaunchConfigurationSelectionDialog extends TitleAreaDialog implements isValid = (actionParam instanceof Integer) && ((Integer) actionParam > 0); setErrorMessage(isValid ? null : DebugUIMessages.GroupLaunchConfigurationSelectionDialog_10); } + + if (action == GroupElementPostLaunchAction.OUTPUT_REGEXP) { + isValid = actionParam instanceof String && !((String) actionParam).isEmpty(); + setErrorMessage(isValid ? null : DebugUIMessages.GroupLaunchConfigurationSelectionDialog_errorNoRegexp); + } } if (ok_button != null) { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationTabGroup.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationTabGroup.java index 692c681a8..2dca579c2 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationTabGroup.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/groups/GroupLaunchConfigurationTabGroup.java @@ -155,17 +155,18 @@ public class GroupLaunchConfigurationTabGroup extends AbstractLaunchConfiguratio if (columnIndex == 2) { GroupElementPostLaunchAction action = el.action; switch (action) { - case NONE: - return ""; //$NON-NLS-1$ - case WAIT_FOR_TERMINATION: + case NONE: + return ""; //$NON-NLS-1$ + case WAIT_FOR_TERMINATION: return action.getDescription(); - case DELAY: - final Object actionParam = el.actionParam; - return NLS.bind(DebugUIMessages.GroupLaunchConfigurationTabGroup_13, - actionParam instanceof Integer ? Integer.toString((Integer) actionParam) : "?"); //$NON-NLS-1$ - default: - assert false : "new post launch action missing logic here"; //$NON-NLS-1$ - return ""; //$NON-NLS-1$ + case DELAY: + final Object actionParam = el.actionParam; + return NLS.bind(DebugUIMessages.GroupLaunchConfigurationTabGroup_13, actionParam instanceof Integer ? Integer.toString((Integer) actionParam) : "?"); //$NON-NLS-1$ + case OUTPUT_REGEXP: + return NLS.bind(DebugUIMessages.GroupLaunchConfigurationTabGroup_0, el.actionParam); + default: + assert false : "new post launch action missing logic here"; //$NON-NLS-1$ + return ""; //$NON-NLS-1$ } } return null; |