mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GT-2376: added new task monitor service
This commit is contained in:
parent
538cbc1226
commit
f57af0b730
28 changed files with 1363 additions and 558 deletions
|
@ -1513,12 +1513,6 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
|
|||
return Math.max(dominantMonitor.getProgress(), slaveMonitor.getProgress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
dominantMonitor.reportIssue(issue);
|
||||
slaveMonitor.reportIssue(issue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
dominantMonitor.cancel();
|
||||
|
@ -1535,16 +1529,6 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
|
|||
dominantMonitor.addCancelledListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
dominantMonitor.addIssueListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
dominantMonitor.removeIssueListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
dominantMonitor.setCancelEnabled(enable);
|
||||
|
|
|
@ -18,9 +18,9 @@ package ghidra.app.util.headless;
|
|||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.*;
|
||||
import ghidra.util.task.CancelledListener;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Monitor used by Headless Analyzer for "timeout" functionality
|
||||
|
@ -99,11 +99,6 @@ public class HeadlessTimedTaskMonitor implements TaskMonitor {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
timer.cancel(); // Terminate the timer thread
|
||||
|
@ -120,16 +115,6 @@ public class HeadlessTimedTaskMonitor implements TaskMonitor {
|
|||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
// stub
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.concurrent.atomic.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
/**
|
||||
* Tests for the {@link TaskMonitorService}
|
||||
*/
|
||||
public class TaskMonitorServiceTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that in a single-threaded environment, the {@link TaskMonitorService}
|
||||
* returns the correct instance of {@link TaskMonitor}. In this case, the initial
|
||||
* monitor is registered by the {@link TaskLauncher task launcher}; the next call
|
||||
* to {@link TaskMonitorService#getMonitor() getMonitor} should return the same.
|
||||
*/
|
||||
@Test
|
||||
public void testSingleThread() {
|
||||
|
||||
TaskLauncher.launch(new Task("task1") {
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
TaskMonitor newMonitor = TaskMonitorService.getMonitor();
|
||||
assertSame(newMonitor, monitor);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that in a single-threaded environment, the {@link TaskMonitorService}
|
||||
* returns the same instance of {@link TaskMonitor} each time one is
|
||||
* requested.
|
||||
* <p>
|
||||
* Note that the first time a monitor is requested it will always return the primary
|
||||
* monitor that allows progress changes. Each subsequent time it will return the
|
||||
* secondary monitor; these secondary monitors is what this test is verifying.
|
||||
*/
|
||||
@Test
|
||||
public void testSingleThreadSecondaryMonitors() {
|
||||
|
||||
TaskLauncher.launch(new Task("task1") {
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
// First monitor requested is always the primary - just make the call so
|
||||
// we can get to retrieving some secondary monitors
|
||||
TaskMonitorService.getMonitor();
|
||||
|
||||
TaskMonitor secondaryMonitor1 = TaskMonitorService.getMonitor();
|
||||
TaskMonitor secondaryMonitor2 = TaskMonitorService.getMonitor();
|
||||
|
||||
assertEquals(secondaryMonitor1, secondaryMonitor2);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that in a multi-threaded environment, the {@link TaskMonitorService}
|
||||
* returns a different instance of {@link TaskMonitor} for each thread.
|
||||
*/
|
||||
@Test
|
||||
public void testMultipleThreads() {
|
||||
|
||||
AtomicReference<TaskMonitor> localMonitor1 = new AtomicReference<>();
|
||||
AtomicReference<TaskMonitor> localMonitor2 = new AtomicReference<>();
|
||||
|
||||
TaskLauncher.launch(new Task("task1") {
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
localMonitor1.set(TaskMonitorService.getMonitor());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
TaskLauncher.launch(new Task("task2") {
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
localMonitor2.set(TaskMonitorService.getMonitor());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
|
||||
assertNotSame(localMonitor1.get(), localMonitor2.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that if we try to register a monitor while on the Swing thread an exception will be
|
||||
* thrown.
|
||||
*/
|
||||
@Test
|
||||
public void testSetMonitorOnSwingThread() {
|
||||
|
||||
SystemUtilities.runSwingNow(() -> {
|
||||
|
||||
try {
|
||||
TaskMonitorService.register(TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// expected catch
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Successful register of monitor on Swing thread (should not have been allowed)");
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that if a client attempts to set a monitor on a thread that already has a monitor,
|
||||
* an exception will be thrown.
|
||||
* <p>
|
||||
* Note: The first monitor is registered behind the scenes by the task launcher
|
||||
*/
|
||||
@Test
|
||||
public void testRegisterMultipleMonitors() {
|
||||
|
||||
TaskLauncher.launch(new Task("task") {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
try {
|
||||
TaskMonitorService.register(TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// expected catch
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Successful register of new monitor (should not have been allowed)");
|
||||
}
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that unique monitor id's are correctly assigned to each thread that registers
|
||||
* a monitor
|
||||
*/
|
||||
@Test
|
||||
public void testMonitorIds() {
|
||||
|
||||
AtomicInteger monitor1Id = new AtomicInteger();
|
||||
AtomicInteger monitor2Id = new AtomicInteger();
|
||||
AtomicInteger monitor3Id = new AtomicInteger();
|
||||
|
||||
Thread thread1 =
|
||||
new Thread(() -> monitor1Id.set(TaskMonitorService.register(TaskMonitor.DUMMY)));
|
||||
|
||||
Thread thread2 =
|
||||
new Thread(() -> monitor2Id.set(TaskMonitorService.register(TaskMonitor.DUMMY)));
|
||||
|
||||
Thread thread3 =
|
||||
new Thread(() -> monitor3Id.set(TaskMonitorService.register(TaskMonitor.DUMMY)));
|
||||
|
||||
thread1.start();
|
||||
thread2.start();
|
||||
thread3.start();
|
||||
|
||||
waitForTasks();
|
||||
|
||||
boolean areIdsDifferent =
|
||||
(monitor1Id.get() != monitor2Id.get()) && (monitor2Id.get() != monitor3Id.get());
|
||||
assertTrue(areIdsDifferent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that if a client attempts to remove a monitor with an invalid id the request
|
||||
* will fail
|
||||
*/
|
||||
@Test
|
||||
public void testRemoveMonitorFail() {
|
||||
|
||||
AtomicBoolean removed = new AtomicBoolean(true);
|
||||
|
||||
// All monitor id's are positive integers; this is guaranteed to generated a failure
|
||||
final int BOGUS_ID = -1;
|
||||
|
||||
Thread thread1 = new Thread(() -> {
|
||||
TaskMonitorService.register(TaskMonitor.DUMMY);
|
||||
try {
|
||||
TaskMonitorService.remove(BOGUS_ID);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// expected catch
|
||||
removed.set(false);
|
||||
}
|
||||
});
|
||||
|
||||
thread1.start();
|
||||
|
||||
waitForTasks();
|
||||
|
||||
assertFalse(removed.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a monitor can be successfully removed given a correct id
|
||||
*/
|
||||
@Test
|
||||
public void testRemoveMonitorSuccess() {
|
||||
|
||||
AtomicBoolean removed = new AtomicBoolean(false);
|
||||
|
||||
AtomicInteger monitorId = new AtomicInteger();
|
||||
|
||||
Thread thread1 = new Thread(() -> {
|
||||
monitorId.set(TaskMonitorService.register(TaskMonitor.DUMMY));
|
||||
|
||||
try {
|
||||
TaskMonitorService.remove(monitorId.get());
|
||||
removed.set(true);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// should not be here
|
||||
fail();
|
||||
}
|
||||
});
|
||||
|
||||
thread1.start();
|
||||
|
||||
waitForTasks();
|
||||
|
||||
assertTrue(removed.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the first monitor returned from the service will be the
|
||||
* primary monitor
|
||||
*/
|
||||
@Test
|
||||
public void testRetrievePrimaryMonitor() {
|
||||
|
||||
TaskLauncher.launch(new Task("task") {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
TaskMonitor monitor1 = TaskMonitorService.getMonitor();
|
||||
assertTrue(monitor1 instanceof TaskDialog);
|
||||
}
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that any subsequent requests after the initial request will result in
|
||||
* a {@link SecondaryTaskDialog} being returned
|
||||
*/
|
||||
@Test
|
||||
public void testRetrieveSecondaryMonitor() {
|
||||
|
||||
TaskLauncher.launch(new Task("task") {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
TaskMonitor monitor1 = TaskMonitorService.getMonitor();
|
||||
assertTrue(monitor1 instanceof TaskDialog);
|
||||
|
||||
monitor1 = TaskMonitorService.getMonitor();
|
||||
assertTrue(monitor1 instanceof SecondaryTaskMonitor);
|
||||
}
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that once a monitor has been reset, the next call to retrieve a monitor will
|
||||
* return it as a primary
|
||||
*/
|
||||
@Test
|
||||
public void testMonitorReset() {
|
||||
|
||||
TaskLauncher.launch(new Task("task") {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
TaskMonitor monitor1 = TaskMonitorService.getMonitor();
|
||||
assertTrue(monitor1 instanceof TaskDialog);
|
||||
|
||||
monitor1 = TaskMonitorService.getMonitor();
|
||||
assertTrue(monitor1 instanceof SecondaryTaskMonitor);
|
||||
|
||||
monitor1.reset();
|
||||
|
||||
monitor1 = TaskMonitorService.getMonitor();
|
||||
assertTrue(monitor1 instanceof TaskDialog);
|
||||
}
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
}
|
||||
}
|
|
@ -67,6 +67,7 @@ public class DialogComponentProvider
|
|||
protected JButton dismissButton;
|
||||
private boolean isAlerting;
|
||||
private JLabel statusLabel;
|
||||
private JLabel subStatusLabel;
|
||||
private JPanel statusProgPanel; // contains status panel and progress panel
|
||||
private Timer showTimer;
|
||||
private TaskScheduler taskScheduler;
|
||||
|
@ -608,6 +609,11 @@ public class DialogComponentProvider
|
|||
public void setStatusText(String text) {
|
||||
setStatusText(text, MessageType.INFO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubStatusText(String text) {
|
||||
setSubStatusText(text, MessageType.INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text in the dialog's status line using the specified message type to control
|
||||
|
@ -621,12 +627,24 @@ public class DialogComponentProvider
|
|||
setStatusText(message, type, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubStatusText(String message, MessageType type) {
|
||||
setSubStatusText(message, type, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusText(String message, MessageType type, boolean alert) {
|
||||
|
||||
String text = StringUtils.isBlank(message) ? " " : message;
|
||||
SystemUtilities.runIfSwingOrPostSwingLater(() -> doSetStatusText(text, type, alert));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSubStatusText(String message, MessageType type, boolean alert) {
|
||||
|
||||
String text = StringUtils.isBlank(message) ? " " : message;
|
||||
SystemUtilities.runIfSwingOrPostSwingLater(() -> doSetSubStatusText(text, type, alert));
|
||||
}
|
||||
|
||||
private void doSetStatusText(String text, MessageType type, boolean alert) {
|
||||
|
||||
|
@ -641,6 +659,20 @@ public class DialogComponentProvider
|
|||
alertMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private void doSetSubStatusText(String text, MessageType type, boolean alert) {
|
||||
|
||||
SystemUtilities.assertThisIsTheSwingThread(
|
||||
"Setting text must be performed on the Swing thread");
|
||||
|
||||
subStatusLabel.setText(text);
|
||||
subStatusLabel.setForeground(getStatusColor(type));
|
||||
updateStatusToolTip();
|
||||
|
||||
if (alert) {
|
||||
alertMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals for this dialog to visually draw the user's attention to the status text
|
||||
|
@ -679,11 +711,13 @@ public class DialogComponentProvider
|
|||
// normal Swing mechanism may not have yet happened).
|
||||
mainPanel.validate();
|
||||
statusLabel.setVisible(false); // disable painting in this dialog so we don't see double
|
||||
subStatusLabel.setVisible(false);
|
||||
Animator animator = AnimationUtils.pulseComponent(statusLabel, 1);
|
||||
animator.addTarget(new TimingTargetAdapter() {
|
||||
@Override
|
||||
public void end() {
|
||||
statusLabel.setVisible(true);
|
||||
subStatusLabel.setVisible(true);
|
||||
alertFinishedCallback.call();
|
||||
isAlerting = false;
|
||||
}
|
||||
|
@ -755,12 +789,7 @@ public class DialogComponentProvider
|
|||
private void showProgressBar(String localTitle, boolean hasProgress, boolean canCancel) {
|
||||
taskMonitorComponent.setTaskName(localTitle);
|
||||
taskMonitorComponent.showProgress(hasProgress);
|
||||
if (canCancel) {
|
||||
taskMonitorComponent.showCancelButton(true);
|
||||
}
|
||||
else {
|
||||
taskMonitorComponent.showCancelButton(false);
|
||||
}
|
||||
taskMonitorComponent.setCancelButtonVisibility(canCancel);
|
||||
progressCardLayout.show(statusProgPanel, PROGRESS);
|
||||
rootPanel.validate();
|
||||
}
|
||||
|
@ -799,18 +828,29 @@ public class DialogComponentProvider
|
|||
public void clearStatusText() {
|
||||
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
|
||||
statusLabel.setText(" ");
|
||||
subStatusLabel.setText(" ");
|
||||
updateStatusToolTip();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the current status in the dialogs status line=
|
||||
* Returns the current status in the dialogs status line
|
||||
*
|
||||
* @return the status text
|
||||
*/
|
||||
public String getStatusText() {
|
||||
return statusLabel.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the secondary status message
|
||||
*
|
||||
* @return the secondary status message
|
||||
*/
|
||||
public String getSubStatusText() {
|
||||
return subStatusLabel.getText();
|
||||
}
|
||||
|
||||
protected JLabel getStatusLabel() {
|
||||
return statusLabel;
|
||||
}
|
||||
|
@ -901,6 +941,14 @@ public class DialogComponentProvider
|
|||
updateStatusToolTip();
|
||||
}
|
||||
});
|
||||
|
||||
subStatusLabel = new JLabel();
|
||||
subStatusLabel.setName("subStatusLabel");
|
||||
subStatusLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
subStatusLabel.setForeground(Color.blue);
|
||||
subStatusLabel.setFont(subStatusLabel.getFont().deriveFont(Font.ITALIC));
|
||||
subStatusLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
subStatusLabel.setFont(subStatusLabel.getFont().deriveFont(11.0f));
|
||||
|
||||
// use a strut panel so the size of the message area does not change if we make
|
||||
// the message label not visible
|
||||
|
@ -908,6 +956,7 @@ public class DialogComponentProvider
|
|||
|
||||
panel.add(Box.createVerticalStrut(height), BorderLayout.WEST);
|
||||
panel.add(statusLabel, BorderLayout.CENTER);
|
||||
panel.add(subStatusLabel, BorderLayout.SOUTH);
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.util.AnimatedIcon;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Panel that displays an animation of the Ghidra dragon chomping bits.
|
||||
*/
|
||||
public class ChompingBitsAnimationPanel extends JPanel {
|
||||
|
||||
public ChompingBitsAnimationPanel() {
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
List<Icon> iconList = new ArrayList<>();
|
||||
iconList.add(ResourceManager.loadImage("images/eatbits1.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/eatbits2.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/eatbits3.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/eatbits4.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/eatbits5.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/eatbits6.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/eatbits7.png"));
|
||||
AnimatedIcon icon = new AnimatedIcon(iconList, 200, 0);
|
||||
setSize(new Dimension(200, 100));
|
||||
add(new JLabel(icon));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.util.AnimatedIcon;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Panel that displays an animation of a spinning hourglass
|
||||
*/
|
||||
public class HourglassAnimationPanel extends JPanel {
|
||||
|
||||
public HourglassAnimationPanel() {
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
List<Icon> iconList = new ArrayList<>();
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_01.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_02.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_02.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_03.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_03.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_04.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_04.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_05.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_05.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_06.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_06.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_07.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_07.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_08.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_08.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_09.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_10.png"));
|
||||
iconList.add(ResourceManager.loadImage("images/hourglass24_11.png"));
|
||||
AnimatedIcon progressIcon = new AnimatedIcon(iconList, 150, 0);
|
||||
|
||||
add (new JLabel(progressIcon), BorderLayout.CENTER);
|
||||
}
|
||||
}
|
|
@ -245,7 +245,7 @@ public class RunManager {
|
|||
* @param showCancel true means to show the cancel button
|
||||
*/
|
||||
public void showCancelButton(boolean showCancel) {
|
||||
monitor.showCancelButton(showCancel);
|
||||
monitor.setCancelButtonVisibility(showCancel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
/**
|
||||
* {@link TaskMonitor} that restricts users from being able to update the progress bar. The class
|
||||
* is initialized with another, fully-featured monitor and forwards all requests to it,
|
||||
* but squashes calls to methods that are not allowed.
|
||||
* <p>
|
||||
* Note: Two instances of this class are deemed equal if they have the same {@link #parentMonitor},
|
||||
* hence the override of {@link #hashCode()} and {@link #equals(Object)}.
|
||||
*/
|
||||
public class SecondaryTaskMonitor implements TaskMonitor {
|
||||
|
||||
private TaskMonitor parentMonitor;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param parentMonitor the fully-functional task monitor this is based off of
|
||||
*/
|
||||
public SecondaryTaskMonitor(TaskMonitor parentMonitor) {
|
||||
this.parentMonitor = parentMonitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to ensure that clients who have this type of monitor will only update the
|
||||
* secondary message when using this method
|
||||
*
|
||||
* @param message the message string to display
|
||||
*/
|
||||
@Override
|
||||
public void setMessage(String message) {
|
||||
if (parentMonitor instanceof TaskDialog) {
|
||||
((TaskDialog) parentMonitor).setSecondaryMessage(message);
|
||||
}
|
||||
parentMonitor.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
parentMonitor.setCancelEnabled(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInitialized(boolean init) {
|
||||
parentMonitor.setInitialized(init);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelEnabled() {
|
||||
return parentMonitor.isCancelEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return parentMonitor.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void cancel() {
|
||||
parentMonitor.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clearCanceled() {
|
||||
parentMonitor.clearCanceled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCanceled() throws CancelledException {
|
||||
parentMonitor.checkCanceled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCancelledListener(CancelledListener listener) {
|
||||
parentMonitor.addCancelledListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCancelledListener(CancelledListener listener) {
|
||||
parentMonitor.removeCancelledListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaximum() {
|
||||
return parentMonitor.getMaximum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getProgress() {
|
||||
return parentMonitor.getProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShowProgressValue(boolean showProgressValue) {
|
||||
// squash
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(long value) {
|
||||
// squash
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(long max) {
|
||||
// squash
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaximum(long max) {
|
||||
// squash
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndeterminate(boolean indeterminate) {
|
||||
// squash
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementProgress(long incrementAmount) {
|
||||
// squash
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((parentMonitor == null) ? 0 : parentMonitor.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SecondaryTaskMonitor other = (SecondaryTaskMonitor) obj;
|
||||
if (!Objects.equals(parentMonitor, other.parentMonitor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -15,20 +15,20 @@
|
|||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.DockingWindowManager;
|
||||
import docking.util.AnimatedIcon;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.timer.GTimer;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Dialog that is displayed to show activity for a Task that is running outside of the
|
||||
|
@ -36,25 +36,48 @@ import resources.ResourceManager;
|
|||
*/
|
||||
public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
||||
|
||||
/** Timer used to give the task a chance to complete */
|
||||
private static final int SLEEPY_TIME = 10;
|
||||
|
||||
/** Amount of time to wait before showing the monitor dialog */
|
||||
private final static int MAX_DELAY = 200000;
|
||||
|
||||
public final static int DEFAULT_WIDTH = 275;
|
||||
|
||||
private Timer showTimer;
|
||||
private TaskMonitorComponent monitorComponent;
|
||||
private AtomicInteger taskID = new AtomicInteger();
|
||||
private boolean canCancel;
|
||||
private Runnable updateMessage;
|
||||
private Runnable closeDialog;
|
||||
private Runnable enableCancelButton;
|
||||
private String newMessage;
|
||||
private boolean cancelState = true;
|
||||
private Component centerOnComp;
|
||||
private Runnable shouldCancelRunnable;
|
||||
private boolean done;
|
||||
private boolean taskDone;
|
||||
private JPanel mainPanel;
|
||||
private ChompingBitsAnimationPanel chompingBitsPanel;
|
||||
private TaskMonitorComponent monitorComponent;
|
||||
|
||||
/** Creates new TaskDialog
|
||||
/** Runnable that updates the primary message label in the dialog */
|
||||
private Runnable updatePrimaryMessageRunnable;
|
||||
|
||||
/** Runnable that updates the secondary message label in the dialog */
|
||||
private Runnable updateSecondaryMessageRunnable;
|
||||
|
||||
/** If not null, then the value of the string has yet to be rendered */
|
||||
private String newPrimaryMessage;
|
||||
|
||||
/** If not null, then the value of the string has yet to be rendered */
|
||||
private String newSecondaryMessage;
|
||||
|
||||
/**
|
||||
* Indicates if this monitor has been initialized for progress updates. If this value
|
||||
* is set to true, the {@link TaskMonitorService} will not return the monitor to
|
||||
* another caller (only one client should be able to update progress at a time).
|
||||
*/
|
||||
private AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
|
||||
private SecondaryTaskMonitor secondaryTaskMonitor;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param centerOnComp component to be centered over when shown,
|
||||
* otherwise center over parent. If both centerOnComp and parent
|
||||
* are null, dialog will be centered on screen.
|
||||
|
@ -65,7 +88,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
task.hasProgress());
|
||||
}
|
||||
|
||||
/** Creates a new TaskDialog.
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param task the Task that this dialog will be associated with
|
||||
*/
|
||||
public TaskDialog(Task task) {
|
||||
|
@ -73,18 +98,20 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Construct new TaskDialog.
|
||||
* Constructor
|
||||
*
|
||||
* @param title title for the dialog
|
||||
* @param canCancel true if the task can be canceled
|
||||
* @param isModal true if the dialog should be modal
|
||||
* @param hasProgress true if the dialog should show a progress bar
|
||||
*/
|
||||
public TaskDialog(String title, boolean canCancel, boolean isModal, boolean hasProgress) {
|
||||
this(null, title, isModal, canCancel, true /*hasProgress*/);
|
||||
this(null, title, isModal, canCancel, hasProgress);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct new TaskDialog.
|
||||
* Constructor
|
||||
*
|
||||
* @param centerOnComp component to be centered over when shown, otherwise center over
|
||||
* parent. If both centerOnComp is null, then the active window will be used
|
||||
* @param title title for the dialog
|
||||
|
@ -99,8 +126,21 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
setup(canCancel, hasProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return initialized.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInitialized(boolean init) {
|
||||
this.initialized.set(init);
|
||||
}
|
||||
|
||||
private void setup(boolean canCancel, boolean hasProgress) {
|
||||
this.canCancel = canCancel;
|
||||
monitorComponent = new TaskMonitorComponent(false, false);
|
||||
chompingBitsPanel = new ChompingBitsAnimationPanel();
|
||||
|
||||
setCancelEnabled(canCancel);
|
||||
setRememberLocation(false);
|
||||
setRememberSize(false);
|
||||
setTransient(true);
|
||||
|
@ -108,13 +148,18 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
close();
|
||||
dispose();
|
||||
};
|
||||
updateMessage = () -> {
|
||||
setStatusText(newMessage);
|
||||
updatePrimaryMessageRunnable = () -> {
|
||||
setStatusText(newPrimaryMessage);
|
||||
synchronized (TaskDialog.this) {
|
||||
newMessage = null;
|
||||
newPrimaryMessage = null;
|
||||
}
|
||||
};
|
||||
updateSecondaryMessageRunnable = () -> {
|
||||
setSubStatusText(newSecondaryMessage);
|
||||
synchronized (TaskDialog.this) {
|
||||
newSecondaryMessage = null;
|
||||
}
|
||||
};
|
||||
enableCancelButton = () -> TaskDialog.super.setCancelEnabled(cancelState);
|
||||
shouldCancelRunnable = () -> {
|
||||
int currentTaskID = taskID.get();
|
||||
|
||||
|
@ -124,9 +169,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
}
|
||||
};
|
||||
|
||||
monitorComponent = new TaskMonitorComponent(false, false);
|
||||
mainPanel = new JPanel(new BorderLayout());
|
||||
addWorkPanel(mainPanel);
|
||||
|
||||
if (hasProgress) {
|
||||
installProgressMonitor();
|
||||
}
|
||||
|
@ -142,6 +187,11 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
setHelpLocation(new HelpLocation("Tool", "TaskDialog"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dialog asking the user if they really, really want to cancel the task
|
||||
*
|
||||
* @return true if the task should be cancelled
|
||||
*/
|
||||
protected boolean promptToVerifyCancel() {
|
||||
boolean userSaysYes = OptionDialog.showYesNoDialog(getComponent(), "Cancel?",
|
||||
"Do you really want to cancel \"" + getTitle() + "\"?") == OptionDialog.OPTION_ONE;
|
||||
|
@ -150,34 +200,24 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates the main work panel for the dialog
|
||||
* Adds the panel that contains the progress bar to the dialog
|
||||
*/
|
||||
private void installProgressMonitor() {
|
||||
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
|
||||
mainPanel.removeAll();
|
||||
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(20, 10, 5, 10));
|
||||
panel.add(monitorComponent);
|
||||
mainPanel.add(panel, BorderLayout.NORTH);
|
||||
|
||||
mainPanel.add(monitorComponent, BorderLayout.CENTER);
|
||||
repack();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the panel that contains the chomping bits animation to the dialog. This should only be
|
||||
* called if the dialog has no need to display progress.
|
||||
*/
|
||||
private void installActivityDisplay() {
|
||||
SystemUtilities.runIfSwingOrPostSwingLater(() -> {
|
||||
mainPanel.removeAll();
|
||||
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setSize(new Dimension(200, 100));
|
||||
String[] filenames = { "images/eatbits1.png", "images/eatbits2.png",
|
||||
"images/eatbits3.png", "images/eatbits4.png", "images/eatbits5.png",
|
||||
"images/eatbits6.png", "images/eatbits7.png" };
|
||||
panel.add(
|
||||
new GIconLabel(new AnimatedIcon(ResourceManager.loadImages(filenames), 200, 0)));
|
||||
mainPanel.add(panel, BorderLayout.CENTER);
|
||||
|
||||
mainPanel.add(chompingBitsPanel, BorderLayout.CENTER);
|
||||
repack();
|
||||
});
|
||||
}
|
||||
|
@ -200,7 +240,9 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
monitorComponent.setShowProgressValue(showProgressValue);
|
||||
}
|
||||
|
||||
/** Sets the percentage done.
|
||||
/**
|
||||
* Sets the percentage done
|
||||
*
|
||||
* @param param The percentage of the task completed.
|
||||
*/
|
||||
@Override
|
||||
|
@ -250,62 +292,58 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
monitorComponent.setIndeterminate(indeterminate);
|
||||
}
|
||||
|
||||
/** Called if the user presses the cancel button on
|
||||
* the dialog
|
||||
*/
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
synchronized (this) {
|
||||
if (!monitorComponent.isCancelEnabled() || monitorComponent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(shouldCancelRunnable);
|
||||
}
|
||||
|
||||
/** Sets the message in the TaskDialog dialog
|
||||
* @param str The message string to be displayed
|
||||
*/
|
||||
@Override
|
||||
synchronized public void setMessage(String str) {
|
||||
boolean invoke = (newMessage == null);
|
||||
newMessage = str;
|
||||
boolean invoke = (newPrimaryMessage == null);
|
||||
if (invoke) {
|
||||
SwingUtilities.invokeLater(updateMessage);
|
||||
newPrimaryMessage = str;
|
||||
SwingUtilities.invokeLater(updatePrimaryMessageRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the enable state of the Cancel button.
|
||||
* @param enable the state to set the cancel button.
|
||||
* Updates the secondary message on the task monitor
|
||||
*
|
||||
* @param str the string to update
|
||||
*/
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
if (canCancel) {
|
||||
monitorComponent.setCancelEnabled(enable);
|
||||
SwingUtilities.invokeLater(enableCancelButton);
|
||||
synchronized public void setSecondaryMessage(String str) {
|
||||
boolean invoke = (newSecondaryMessage == null);
|
||||
if (invoke) {
|
||||
newSecondaryMessage = str;
|
||||
SwingUtilities.invokeLater(updateSecondaryMessageRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
monitorComponent.setCancelEnabled(enable);
|
||||
super.setCancelEnabled(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelEnabled() {
|
||||
return canCancel && cancelState;
|
||||
return super.isCancelEnabled();
|
||||
}
|
||||
|
||||
public synchronized void taskProcessed() {
|
||||
done = true;
|
||||
taskDone = true;
|
||||
monitorComponent.notifyChangeListeners();
|
||||
SwingUtilities.invokeLater(closeDialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() {
|
||||
done = false;
|
||||
taskDone = false;
|
||||
taskID.incrementAndGet();
|
||||
}
|
||||
|
||||
public synchronized boolean isCompleted() {
|
||||
return done;
|
||||
return taskDone;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -393,7 +431,7 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
|
||||
@Override
|
||||
public synchronized void cancel() {
|
||||
if (!canCancel || monitorComponent.isCancelled()) {
|
||||
if (monitorComponent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// Mark as cancelled, must be detected by task which should terminate
|
||||
|
@ -432,18 +470,11 @@ public class TaskDialog extends DialogComponentProvider implements TaskMonitor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
monitorComponent.addIssueListener(listener);
|
||||
public synchronized TaskMonitor getSecondaryMonitor() {
|
||||
if (secondaryTaskMonitor == null) {
|
||||
secondaryTaskMonitor = new SecondaryTaskMonitor(this);
|
||||
}
|
||||
return secondaryTaskMonitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
monitorComponent.removeIssueListener(listener);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
monitorComponent.reportIssue(issue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,75 +24,79 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
import javax.swing.*;
|
||||
|
||||
import docking.ToolTipManager;
|
||||
import docking.util.AnimatedIcon;
|
||||
import docking.widgets.EmptyBorderButton;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.label.GDHtmlLabel;
|
||||
import docking.widgets.label.GIconLabel;
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.layout.VerticalLayout;
|
||||
import resources.Icons;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Component that contains a progress bar, a progress icon, and a cancel
|
||||
* button to cancel the task that is associated with this task monitor.
|
||||
* <p>
|
||||
* By default the progress bar and progress icon (spinning globe) are visible.
|
||||
*/
|
||||
public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
||||
|
||||
private WeakSet<CancelledListener> listeners =
|
||||
WeakDataStructureFactory.createCopyOnReadWeakSet();
|
||||
private WeakSet<IssueListener> issueListeners;
|
||||
private JButton cancelButton;
|
||||
private JPanel eastButtonPanel;
|
||||
|
||||
private JProgressBar progressBar;
|
||||
private JButton cancelButton;
|
||||
|
||||
private JPanel cancelPanel;
|
||||
private JPanel progressBarPanel;
|
||||
private JPanel mainContentPanel;
|
||||
private JPanel progressPanel;
|
||||
private JPanel activeProgressPanel;
|
||||
private JLabel imageLabel;
|
||||
|
||||
private String progressMessage;
|
||||
private String taskName;
|
||||
|
||||
private JLabel messageLabel;
|
||||
|
||||
private volatile boolean isCancelled;
|
||||
private String message;
|
||||
|
||||
|
||||
private long lastProgress = -1;
|
||||
private long progress;
|
||||
private long lastMax = -1;
|
||||
private long max;
|
||||
private long lastMaxProgress = -1;
|
||||
private long maxProgress;
|
||||
private long scaleFactor = 1;
|
||||
|
||||
private Runnable updateProgressPanelRunnable;
|
||||
private Runnable updateCancelButtonRunnable;
|
||||
private Runnable updateToolTipRunnable;
|
||||
private JLabel messageLabel;
|
||||
private Runnable shouldCancelRunnable;
|
||||
|
||||
private boolean showingProgress = true;
|
||||
private boolean showingIcon = true;
|
||||
private boolean showingCancelButton = true;
|
||||
private boolean cancelEnabled = true;
|
||||
private boolean paintProgressValue = true;
|
||||
|
||||
private AtomicBoolean isIndeterminate = new AtomicBoolean(false);
|
||||
private AtomicInteger taskID = new AtomicInteger();
|
||||
|
||||
private Timer updateTimer;
|
||||
private Runnable shouldCancelRunnable;
|
||||
|
||||
private boolean paintProgressValue = true;
|
||||
|
||||
private NumberFormat percentFormat = NumberFormat.getPercentInstance();
|
||||
private long scaleFactor = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new TaskMonitorComponent.
|
||||
* @param l listener that is notified when the task completes or the
|
||||
* user cancels the task
|
||||
* Constructor
|
||||
*/
|
||||
|
||||
public TaskMonitorComponent() {
|
||||
this(true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param includeTextField if true, the dialog can display a status progressMessage with progress details
|
||||
* @param includeCancelButton if true, a cancel button will be displayed
|
||||
*/
|
||||
public TaskMonitorComponent(boolean includeTextField, boolean includeCancelButton) {
|
||||
updateProgressPanelRunnable = () -> updateProgressPanel();
|
||||
updateCancelButtonRunnable = () -> updateCancelButton();
|
||||
|
@ -113,13 +117,25 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
|
||||
buildProgressPanel(includeTextField, includeCancelButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCancelledListener(CancelledListener mcl) {
|
||||
listeners.add(mcl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset this monitor so that it can be reused.
|
||||
*/
|
||||
public synchronized void reset() {
|
||||
isCancelled = false;
|
||||
taskID.incrementAndGet();
|
||||
@Override
|
||||
public void removeCancelledListener(CancelledListener mcl) {
|
||||
listeners.remove(mcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementProgress(long incrementAmount) {
|
||||
setProgress(progress + incrementAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -136,7 +152,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
|
||||
@Override
|
||||
public synchronized void setMessage(String message) {
|
||||
this.message = message;
|
||||
this.progressMessage = message;
|
||||
startUpdateTimer();
|
||||
}
|
||||
|
||||
|
@ -148,13 +164,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
progress = value;
|
||||
startUpdateTimer();
|
||||
}
|
||||
|
||||
private synchronized void startUpdateTimer() {
|
||||
if (!updateTimer.isRunning()) {
|
||||
updateTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize(long maxValue) {
|
||||
setMaximum(maxValue);
|
||||
|
@ -163,13 +173,13 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
|
||||
@Override
|
||||
public void setMaximum(long max) {
|
||||
this.max = max;
|
||||
if (progress > this.max) {
|
||||
this.maxProgress = max;
|
||||
if (progress > this.maxProgress) {
|
||||
progress = max;
|
||||
}
|
||||
startUpdateTimer();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the <code>indeterminate</code> property of the progress bar,
|
||||
* which determines whether the progress bar is in determinate
|
||||
|
@ -201,17 +211,6 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if {@link #setIndeterminate(boolean)} with a value of <tt>true</tt> has
|
||||
* been called.
|
||||
*
|
||||
* @return true if {@link #setIndeterminate(boolean)} with a value of <tt>true</tt> has
|
||||
* been called.
|
||||
*/
|
||||
public boolean isIndeterminate() {
|
||||
return isIndeterminate.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setCancelEnabled(boolean enable) {
|
||||
if (cancelEnabled != enable) {
|
||||
|
@ -243,67 +242,7 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
isCancelled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the progress bar should be visible.
|
||||
* @param b true if the progress bar should be visible
|
||||
*/
|
||||
public synchronized void showProgress(boolean b) {
|
||||
if (b != showingProgress) {
|
||||
showingProgress = b;
|
||||
SystemUtilities.runSwingLater(updateProgressPanelRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the task; the name shows up in the tool tip for
|
||||
* the cancel button.
|
||||
* @param name the name of the task
|
||||
*/
|
||||
public void setTaskName(String name) {
|
||||
taskName = name;
|
||||
SystemUtilities.runSwingLater(updateToolTipRunnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or not show the cancel button according to the showCancel param.
|
||||
*/
|
||||
public void showCancelButton(boolean showCancel) {
|
||||
|
||||
if (showCancel == showingCancelButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (showCancel) {
|
||||
add(eastButtonPanel, BorderLayout.EAST);
|
||||
}
|
||||
else {
|
||||
remove(eastButtonPanel);
|
||||
}
|
||||
showingCancelButton = showCancel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or not show the progress icon (spinning globe) according to
|
||||
* the showIcon param.
|
||||
*/
|
||||
public void showProgressIcon(final boolean showIcon) {
|
||||
if (showIcon == showingIcon) {
|
||||
return;
|
||||
}
|
||||
Runnable r = () -> {
|
||||
if (showIcon) {
|
||||
activeProgressPanel.add(imageLabel, BorderLayout.EAST);
|
||||
}
|
||||
else {
|
||||
activeProgressPanel.remove(imageLabel);
|
||||
}
|
||||
showingIcon = showIcon;
|
||||
};
|
||||
|
||||
SystemUtilities.runSwingNow(r);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setShowProgressValue(boolean showProgressValue) {
|
||||
this.paintProgressValue = showProgressValue;
|
||||
|
@ -312,19 +251,121 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
|
||||
@Override
|
||||
public long getMaximum() {
|
||||
return max;
|
||||
return maxProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset this monitor so that it can be reused
|
||||
*/
|
||||
@Override
|
||||
public synchronized void reset() {
|
||||
isCancelled = false;
|
||||
taskID.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if {@link #setIndeterminate(boolean)} with a value of <tt>true</tt> has
|
||||
* been called.
|
||||
*
|
||||
* @return true if {@link #setIndeterminate(boolean)} with a value of <tt>true</tt> has
|
||||
* been called.
|
||||
*/
|
||||
public boolean isIndeterminate() {
|
||||
return isIndeterminate.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the progress bar should be visible
|
||||
*
|
||||
* @param show true if the progress bar should be visible
|
||||
*/
|
||||
public synchronized void showProgress(boolean show) {
|
||||
if (show != showingProgress) {
|
||||
showingProgress = show;
|
||||
SystemUtilities.runSwingLater(updateProgressPanelRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the task; the name shows up in the tool tip for
|
||||
* the cancel button.
|
||||
*
|
||||
* @param name the name of the task
|
||||
*/
|
||||
public void setTaskName(String name) {
|
||||
taskName = name;
|
||||
SystemUtilities.runSwingLater(updateToolTipRunnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the visibility of the cancel button
|
||||
*
|
||||
* @param visible if true, show the cancel button; false otherwise
|
||||
*/
|
||||
public void setCancelButtonVisibility(boolean visible) {
|
||||
|
||||
if (visible == showingCancelButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
add(cancelPanel, BorderLayout.EAST);
|
||||
}
|
||||
else {
|
||||
remove(cancelPanel);
|
||||
}
|
||||
|
||||
repaint();
|
||||
showingCancelButton = visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visibility of the progress icon
|
||||
*
|
||||
* @param visible if true, display the progress icon
|
||||
*/
|
||||
public void showProgressIcon(boolean visible) {
|
||||
if (visible == showingIcon) {
|
||||
return;
|
||||
}
|
||||
Runnable r = () -> {
|
||||
if (visible) {
|
||||
mainContentPanel.add(progressPanel, BorderLayout.EAST);
|
||||
}
|
||||
else {
|
||||
mainContentPanel.remove(progressPanel);
|
||||
}
|
||||
showingIcon = visible;
|
||||
};
|
||||
|
||||
SystemUtilities.runSwingNow(r);
|
||||
}
|
||||
|
||||
protected void notifyChangeListeners() {
|
||||
Runnable r = () -> {
|
||||
for (CancelledListener mcl : listeners) {
|
||||
mcl.cancelled();
|
||||
}
|
||||
};
|
||||
SwingUtilities.invokeLater(r);
|
||||
}
|
||||
|
||||
private synchronized void startUpdateTimer() {
|
||||
if (!updateTimer.isRunning()) {
|
||||
updateTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void update() {
|
||||
|
||||
if (message != null) {
|
||||
messageLabel.setText(message);
|
||||
message = null;
|
||||
if (progressMessage != null) {
|
||||
messageLabel.setText(progressMessage);
|
||||
progressMessage = null;
|
||||
}
|
||||
|
||||
if (max != lastMax) {
|
||||
setMaxValueInProgressBar(max);
|
||||
lastMax = max;
|
||||
if (maxProgress != lastMaxProgress) {
|
||||
setMaxValueInProgressBar(maxProgress);
|
||||
lastMaxProgress = maxProgress;
|
||||
}
|
||||
|
||||
if (progress != lastProgress) {
|
||||
|
@ -379,10 +420,10 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
|
||||
private synchronized void updateProgressPanel() {
|
||||
if (showingProgress) {
|
||||
progressPanel.add(progressBar, BorderLayout.NORTH);
|
||||
progressBarPanel.add(progressBar, BorderLayout.NORTH);
|
||||
}
|
||||
else {
|
||||
progressPanel.remove(progressBar);
|
||||
progressBarPanel.remove(progressBar);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,24 +465,24 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
progressBar.setStringPainted(true);
|
||||
ToolTipManager.sharedInstance().registerComponent(progressBar);
|
||||
|
||||
createAnimatedIcon();
|
||||
progressPanel = new HourglassAnimationPanel();
|
||||
|
||||
progressPanel = new JPanel(new VerticalLayout(0));
|
||||
progressPanel.add(progressBar);
|
||||
progressBarPanel = new JPanel(new VerticalLayout(0));
|
||||
progressBarPanel.add(progressBar);
|
||||
if (includeTextField) {
|
||||
progressPanel.add(messageLabel);
|
||||
progressBarPanel.add(messageLabel);
|
||||
progressBar.setPreferredSize(new Dimension(180, 12));
|
||||
}
|
||||
else {
|
||||
progressBar.setBorderPainted(true);
|
||||
Dimension size = progressBar.getPreferredSize();
|
||||
progressPanel.setBorder(BorderFactory.createEmptyBorder(
|
||||
(imageLabel.getPreferredSize().height - size.height) / 2, 0, 0, 8));
|
||||
progressBarPanel.setBorder(BorderFactory.createEmptyBorder(
|
||||
(progressPanel.getPreferredSize().height - size.height) / 2, 0, 0, 8));
|
||||
}
|
||||
|
||||
activeProgressPanel = new JPanel(new BorderLayout());
|
||||
activeProgressPanel.add(progressPanel, BorderLayout.CENTER);
|
||||
activeProgressPanel.add(imageLabel, BorderLayout.EAST);
|
||||
mainContentPanel = new JPanel(new BorderLayout());
|
||||
mainContentPanel.add(progressBarPanel, BorderLayout.CENTER);
|
||||
mainContentPanel.add(progressPanel, BorderLayout.EAST);
|
||||
|
||||
ImageIcon icon = Icons.STOP_ICON;
|
||||
cancelButton = new EmptyBorderButton(icon);
|
||||
|
@ -452,86 +493,15 @@ public class TaskMonitorComponent extends JPanel implements TaskMonitor {
|
|||
cancelButton.setFocusable(false);
|
||||
cancelButton.setRolloverEnabled(true);
|
||||
|
||||
add(activeProgressPanel, BorderLayout.CENTER);
|
||||
add(mainContentPanel, BorderLayout.CENTER);
|
||||
|
||||
if (includeCancelButton) {
|
||||
eastButtonPanel = new JPanel();
|
||||
eastButtonPanel.setLayout(new BoxLayout(eastButtonPanel, BoxLayout.Y_AXIS));
|
||||
eastButtonPanel.add(Box.createVerticalGlue());
|
||||
eastButtonPanel.add(cancelButton);
|
||||
eastButtonPanel.add(Box.createVerticalGlue());
|
||||
add(eastButtonPanel, BorderLayout.EAST);
|
||||
}
|
||||
}
|
||||
|
||||
private void createAnimatedIcon() {
|
||||
String[] filenames = { "images/hourglass24_01.png", "images/hourglass24_02.png",
|
||||
"images/hourglass24_02.png", "images/hourglass24_03.png", "images/hourglass24_03.png",
|
||||
"images/hourglass24_04.png", "images/hourglass24_04.png", "images/hourglass24_05.png",
|
||||
"images/hourglass24_05.png", "images/hourglass24_06.png", "images/hourglass24_06.png",
|
||||
"images/hourglass24_07.png", "images/hourglass24_07.png", "images/hourglass24_08.png",
|
||||
"images/hourglass24_08.png", "images/hourglass24_09.png", "images/hourglass24_10.png",
|
||||
"images/hourglass24_11.png" };
|
||||
imageLabel =
|
||||
new GIconLabel(new AnimatedIcon(ResourceManager.loadImages(filenames), 150, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementProgress(long incrementAmount) {
|
||||
setProgress(progress + incrementAmount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
protected void notifyChangeListeners() {
|
||||
Runnable r = () -> {
|
||||
synchronized (listeners) {
|
||||
for (CancelledListener mcl : listeners) {
|
||||
mcl.cancelled();
|
||||
}
|
||||
}
|
||||
};
|
||||
SwingUtilities.invokeLater(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCancelledListener(CancelledListener mcl) {
|
||||
synchronized (listeners) {
|
||||
listeners.add(mcl);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCancelledListener(CancelledListener mcl) {
|
||||
synchronized (listeners) {
|
||||
listeners.remove(mcl);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
if (issueListeners == null) {
|
||||
issueListeners = WeakDataStructureFactory.createCopyOnWriteWeakSet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
if (issueListeners != null) {
|
||||
issueListeners.remove(listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
if (issueListeners != null) {
|
||||
for (IssueListener listener : issueListeners) {
|
||||
listener.issueReported(issue);
|
||||
}
|
||||
cancelPanel = new JPanel();
|
||||
cancelPanel.setLayout(new BoxLayout(cancelPanel, BoxLayout.Y_AXIS));
|
||||
cancelPanel.add(Box.createVerticalGlue());
|
||||
cancelPanel.add(cancelButton);
|
||||
cancelPanel.add(Box.createVerticalGlue());
|
||||
add(cancelPanel, BorderLayout.EAST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.junit.After;
|
|||
import org.junit.Test;
|
||||
|
||||
import docking.test.AbstractDockingTest;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
public class TaskDialogTest extends AbstractDockingTest {
|
||||
|
||||
|
@ -94,6 +95,89 @@ public class TaskDialogTest extends AbstractDockingTest {
|
|||
assertTrue(dialogSpy.wasShown());
|
||||
assertSwingThreadFinishedBeforeTask();
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that if the dialog cancel button is activated, the task is cancelled
|
||||
*/
|
||||
@Test
|
||||
public void testTaskCancel() throws Exception {
|
||||
SlowModalTask task = new SlowModalTask();
|
||||
TaskDialogSpy dialogSpy = launchTask(task);
|
||||
|
||||
dialogSpy.doShow();
|
||||
|
||||
waitForTask();
|
||||
|
||||
assertFalse(dialogSpy.isCancelled());
|
||||
dialogSpy.cancel();
|
||||
assertTrue(dialogSpy.isCancelled());
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that if the task does not allow cancellation, the cancel button on the GUI
|
||||
* is disabled
|
||||
*/
|
||||
@Test
|
||||
public void testTaskNoCancel() throws Exception {
|
||||
SlowModalTask task = new SlowModalTask();
|
||||
TaskDialogSpy dialogSpy = launchTask(task);
|
||||
|
||||
dialogSpy.doShow();
|
||||
dialogSpy.setCancelEnabled(false);
|
||||
|
||||
waitForTask();
|
||||
|
||||
assertFalse(dialogSpy.isCancelEnabled());
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that the progress value can be successfully updated
|
||||
* after using the {@link TaskMonitorService} to retrieve a monitor.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateProgressSuccess() throws Exception {
|
||||
|
||||
TaskLauncher.launch(new Task("task") {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
TaskMonitor monitor1 = TaskMonitorService.getMonitor();
|
||||
long val = monitor1.getProgress();
|
||||
|
||||
monitor1.setProgress(10);
|
||||
val = monitor1.getProgress();
|
||||
assertEquals(val, 10);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that the progress value will NOT be updated if the caller is a
|
||||
* secondary monitor. As a bonus, this also verifies that the Task Launcher does
|
||||
* not lock the task for future progress updates when a new task is launched.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdatePogressFail() throws Exception {
|
||||
|
||||
TaskLauncher.launch(new Task("task") {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
TaskMonitor monitor1 = TaskMonitorService.getMonitor();
|
||||
TaskMonitor monitor2 = TaskMonitorService.getMonitor();
|
||||
|
||||
// Update should be accepted
|
||||
monitor1.setProgress(10);
|
||||
|
||||
// Update should fail
|
||||
monitor2.setProgress(20);
|
||||
|
||||
long val = monitor2.getProgress();
|
||||
assertEquals(val, 10);
|
||||
}
|
||||
});
|
||||
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
private void assertSwingThreadBlockedForTask() {
|
||||
TDEvent lastEvent = eventQueue.peekLast();
|
||||
|
@ -160,7 +244,7 @@ public class TaskDialogTest extends AbstractDockingTest {
|
|||
public TaskDialogSpy(Task task) {
|
||||
super(task);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void doShow() {
|
||||
shown.set(true);
|
||||
|
|
|
@ -18,9 +18,9 @@ package generic.concurrent;
|
|||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.*;
|
||||
import ghidra.util.task.CancelledListener;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* This is the FutureTask that will be used to call the {@link QCallback} to work on
|
||||
|
@ -131,11 +131,6 @@ class FutureTaskMonitor<I, R> extends FutureTask<R> implements TaskMonitor {
|
|||
return currentProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
boolean result = super.cancel(mayInterruptIfRunning);
|
||||
|
@ -191,16 +186,6 @@ class FutureTaskMonitor<I, R> extends FutureTask<R> implements TaskMonitor {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static class ChainedCancelledListener implements CancelledListener {
|
||||
private volatile CancelledListener listener1;
|
||||
private volatile CancelledListener listener2;
|
||||
|
|
|
@ -41,6 +41,30 @@ public interface StatusListener {
|
|||
* @param alert true to grab the user's attention
|
||||
*/
|
||||
void setStatusText(String text, MessageType type, boolean alert);
|
||||
|
||||
/**
|
||||
* Sets the subtask text
|
||||
*
|
||||
* @param text the text to set
|
||||
*/
|
||||
void setSubStatusText(String text);
|
||||
|
||||
/**
|
||||
* Sets the subtask text
|
||||
*
|
||||
* @param text the text to set
|
||||
* @param type the message type
|
||||
*/
|
||||
void setSubStatusText(String text, MessageType type);
|
||||
|
||||
/**
|
||||
* Sets the subtask text
|
||||
*
|
||||
* @param text the text to set
|
||||
* @param type the message type
|
||||
* @param alert if true, an alert will be generated
|
||||
*/
|
||||
void setSubStatusText(String text, MessageType type, boolean alert);
|
||||
|
||||
/**
|
||||
* Clear the current status - same as setStatusText("")
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import ghidra.util.Issue;
|
||||
|
||||
/**
|
||||
* A monitor that is designed for sub-tasks, where the outer task handles reporting messages and
|
||||
* progress. This class is really just for checking cancelled.
|
||||
|
@ -88,11 +86,6 @@ public class CancelOnlyWrappingTaskMonitor extends WrappingTaskMonitor {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
// ignore
|
||||
|
|
|
@ -112,14 +112,19 @@ public abstract class Task implements MonitoredRunnable {
|
|||
/**
|
||||
* When an object implementing interface <code>Runnable</code> is used to create a thread,
|
||||
* starting the thread causes the object's <code>run</code> method to be called in that
|
||||
* separately executing thread
|
||||
* separately executing thread.
|
||||
* <p>
|
||||
* Note that the monitor is handed to the {@link TaskMonitorService} here so it will be
|
||||
* available to any users request it via {@link TaskMonitorService#getMonitor() getMonitor}.
|
||||
*
|
||||
* @param monitor The TaskMonitor
|
||||
* @param monitor the task monitor
|
||||
*/
|
||||
@Override
|
||||
public final void monitoredRun(TaskMonitor monitor) {
|
||||
this.taskMonitor = monitor;
|
||||
|
||||
int monitorId = TaskMonitorService.register(monitor);
|
||||
|
||||
// this will be removed from SystemUtilities in Task.run() after the task is finished
|
||||
TaskUtilities.addTrackedTask(this, monitor);
|
||||
|
||||
|
@ -136,9 +141,14 @@ public abstract class Task implements MonitoredRunnable {
|
|||
getTaskTitle() + " - Uncaught Exception: " + t.toString(), t);
|
||||
}
|
||||
finally {
|
||||
// this is put into SystemUtilities by the TaskLauncher
|
||||
|
||||
// This should not be necessary since the thread local object will be cleaned up
|
||||
// by the GC when the thread terminates, but it's here in case we ever use this
|
||||
// in conjunction with thread pools and have to manually remove them.
|
||||
TaskMonitorService.remove(monitorId);
|
||||
|
||||
TaskUtilities.removeTrackedTask(this);
|
||||
this.taskMonitor = null;
|
||||
this.taskMonitor = null;
|
||||
}
|
||||
|
||||
notifyTaskListeners(isCancelled);
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
@ -163,19 +162,4 @@ public class TaskMonitorAdapter implements TaskMonitor {
|
|||
public synchronized void removeCancelledListener(CancelledListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import java.util.HashSet;
|
|||
import java.util.Set;
|
||||
|
||||
import generic.concurrent.ConcurrentListenerSet;
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
public class TaskMonitorSplitter {
|
||||
|
@ -176,20 +175,5 @@ public class TaskMonitorSplitter {
|
|||
listener.cancelled();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
parent.addIssueListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
parent.removeIssueListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
parent.reportIssue(issue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import ghidra.generic.function.Callback;
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.TimeoutException;
|
||||
|
@ -184,11 +183,6 @@ public class TimeoutTaskMonitor implements TaskMonitor {
|
|||
return delegate.getProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
delegate.reportIssue(issue);
|
||||
}
|
||||
|
||||
private void timeout() {
|
||||
didTimeout.set(true);
|
||||
timeoutCallback.call();
|
||||
|
@ -225,14 +219,4 @@ public class TimeoutTaskMonitor implements TaskMonitor {
|
|||
public void clearCanceled() {
|
||||
delegate.clearCanceled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
delegate.addIssueListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
delegate.removeIssueListener(listener);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
/**
|
||||
|
@ -91,11 +90,6 @@ public class WrappingTaskMonitor implements TaskMonitor {
|
|||
return delegate.getProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
delegate.reportIssue(issue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
delegate.cancel();
|
||||
|
@ -125,14 +119,4 @@ public class WrappingTaskMonitor implements TaskMonitor {
|
|||
public void clearCanceled() {
|
||||
delegate.clearCanceled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
delegate.addIssueListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
delegate.removeIssueListener(listener);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,7 @@
|
|||
*/
|
||||
package ghidra.framework.data;
|
||||
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
|
@ -35,7 +32,6 @@ class LockingTaskMonitor implements TaskMonitor {
|
|||
private boolean showProgressValue = true;
|
||||
private String msg;
|
||||
private MyTaskDialog taskDialog;
|
||||
private WeakSet<IssueListener> issueListeners;
|
||||
|
||||
/**
|
||||
* Constructs a locking task handler for a locked dobj. The setCompleted() method must be
|
||||
|
@ -263,28 +259,4 @@ class LockingTaskMonitor implements TaskMonitor {
|
|||
public void removeCancelledListener(CancelledListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
if (issueListeners == null) {
|
||||
issueListeners = WeakDataStructureFactory.createCopyOnWriteWeakSet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
if (issueListeners != null) {
|
||||
issueListeners.remove(listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
if (issueListeners != null) {
|
||||
for (IssueListener listener : issueListeners) {
|
||||
listener.issueReported(issue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
package ghidra.framework.task;
|
||||
|
||||
import ghidra.framework.task.gui.GProgressBar;
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.*;
|
||||
import ghidra.util.task.CancelledListener;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Implementation of a TaskMontor that can be "attached" to a GProgressBar.
|
||||
|
@ -123,11 +123,6 @@ public class GTaskMonitor implements TaskMonitor, CancelledListener {
|
|||
return progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
// do nothing for now;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
if (cancelEnabled) {
|
||||
|
@ -145,16 +140,6 @@ public class GTaskMonitor implements TaskMonitor, CancelledListener {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
// not currently supported
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
// not currently supported
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
cancelEnabled = enable;
|
||||
|
|
|
@ -281,9 +281,11 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||
throws IOException, VersionException, LanguageNotFoundException, CancelledException {
|
||||
|
||||
super(dbh, "Untitled", 500, 1000, consumer);
|
||||
|
||||
if (monitor == null) {
|
||||
monitor = TaskMonitorAdapter.DUMMY_MONITOR;
|
||||
monitor = TaskMonitorAdapter.DUMMY;
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
int id = startTransaction("create program");
|
||||
|
@ -294,6 +296,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
|
|||
VersionException dbVersionExc = initializeDatabase(openMode);
|
||||
|
||||
VersionException languageVersionExc = null;
|
||||
|
||||
try {
|
||||
language = DefaultLanguageService.getLanguageService().getLanguage(languageID);
|
||||
languageVersionExc = checkLanguageVersion(openMode);
|
||||
|
|
|
@ -28,18 +28,19 @@ public interface LanguageProvider extends ExtensionPoint {
|
|||
|
||||
/**
|
||||
* Returns the language with the given name or null if no language has that name
|
||||
* @param name the name of the language to be retrieved
|
||||
* @return the language with the given name
|
||||
*
|
||||
* @param languageId the name of the language to be retrieved
|
||||
* @return the {@link Language} with the given name
|
||||
*/
|
||||
Language getLanguage(LanguageID languageId);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of language descriptions provided by this provider
|
||||
*/
|
||||
LanguageDescription[] getLanguageDescriptions();
|
||||
|
||||
/**
|
||||
* @return true if one of more laguages or language description failed to load
|
||||
* @return true if one of more languages or language description failed to load
|
||||
* properly.
|
||||
*/
|
||||
boolean hadLoadFailure();
|
||||
|
|
|
@ -27,12 +27,13 @@ import java.util.List;
|
|||
public interface LanguageService {
|
||||
|
||||
/**
|
||||
* Returns the language with the given language ID.
|
||||
* @param languageID the ID of language to retrieve.
|
||||
* @throws LanguageNotFoundException if no language can be found for the given ID.
|
||||
* Returns the language with the given language ID
|
||||
* @param languageID the ID of language to retrieve
|
||||
* @return the {@link Language} matching the given ID
|
||||
* @throws LanguageNotFoundException if no language can be found for the given ID
|
||||
*/
|
||||
Language getLanguage(LanguageID languageID) throws LanguageNotFoundException;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default Language to use for the given processor;
|
||||
* @param processor the processor for which to get a language.
|
||||
|
@ -59,13 +60,14 @@ public interface LanguageService {
|
|||
/**
|
||||
* Returns all known language descriptions which satisfy the criteria identify by the
|
||||
* non-null parameters. A null value implies a don't-care wildcard value.
|
||||
* @param processor
|
||||
* @param endianess
|
||||
* @param size
|
||||
* @param variant
|
||||
* @return
|
||||
* @deprecated
|
||||
* @param processor the processor for which to get a language
|
||||
* @param endianess big or little
|
||||
* @param size processor address space size (in bits)
|
||||
* @param variant the processor version (usually 'default')
|
||||
* @return the language descriptions that fit the parameters
|
||||
* @deprecated use {@link #getLanguageDescriptions(Processor)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
List<LanguageDescription> getLanguageDescriptions(Processor processor, Endian endianess,
|
||||
Integer size, String variant);
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ import generic.jar.ResourceFile;
|
|||
import ghidra.app.plugin.processors.sleigh.SleighLanguageProvider;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.TaskMonitorService;
|
||||
|
||||
/**
|
||||
* Default Language service used gather up all the languages that were found
|
||||
|
@ -43,6 +45,7 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
|
|||
|
||||
/**
|
||||
* Returns the single instance of the DefaultLanguageService.
|
||||
* @return the language service
|
||||
*/
|
||||
public static synchronized LanguageService getLanguageService() {
|
||||
if (languageService == null) {
|
||||
|
@ -94,13 +97,23 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
|
|||
*/
|
||||
@Override
|
||||
public Language getLanguage(LanguageID languageID) throws LanguageNotFoundException {
|
||||
LanguageInfo info = languageMap.get(languageID);
|
||||
|
||||
if (info == null) {
|
||||
throw new LanguageNotFoundException(languageID);
|
||||
TaskMonitor monitor = TaskMonitorService.getMonitor();
|
||||
monitor.setMessage("Retrieving language: " + languageID);
|
||||
|
||||
try {
|
||||
LanguageInfo info = languageMap.get(languageID);
|
||||
|
||||
if (info == null) {
|
||||
throw new LanguageNotFoundException(languageID);
|
||||
}
|
||||
|
||||
Language lang = info.getLanguage();
|
||||
return lang;
|
||||
}
|
||||
finally {
|
||||
monitor.setMessage("");
|
||||
}
|
||||
|
||||
return info.getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,8 +174,9 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
|
|||
private static boolean languageMatchesExternalProcessor(LanguageDescription description,
|
||||
String externalProcessorName, String externalTool) {
|
||||
boolean result = false;
|
||||
if (externalProcessorName == null)
|
||||
if (externalProcessorName == null) {
|
||||
result = true;
|
||||
}
|
||||
else if (externalTool != null) {
|
||||
List<String> extNames = description.getExternalNames(externalTool);
|
||||
if (extNames != null) {
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
class StubTaskMonitor implements TaskMonitor {
|
||||
|
@ -83,12 +82,6 @@ class StubTaskMonitor implements TaskMonitor {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportIssue(Issue issue) {
|
||||
// stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
// stub
|
||||
|
@ -107,18 +100,6 @@ class StubTaskMonitor implements TaskMonitor {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addIssueListener(IssueListener listener) {
|
||||
// stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIssueListener(IssueListener listener) {
|
||||
// stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelEnabled(boolean enable) {
|
||||
// stub
|
||||
|
|
|
@ -15,49 +15,93 @@
|
|||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import ghidra.util.Issue;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
/**
|
||||
* <CODE>TaskMonitor</CODE> provides an interface by means of which a
|
||||
* potentially long running task can show its progress and also check if the user
|
||||
* has cancelled the operation. Operations that support a task monitor should periodically
|
||||
* has cancelled the operation.
|
||||
* <p>
|
||||
* Operations that support a task monitor should periodically
|
||||
* check to see if the operation has been cancelled and abort. If possible, the
|
||||
* operation should also provide periodic progress information. If it can estimate a
|
||||
* percentage done, then it should use the <code>setProgress(int)</code> method,
|
||||
* otherwise it
|
||||
* should just call the <code>setMessage(String)</code> method.
|
||||
* otherwise it should just call the <code>setMessage(String)</code> method.
|
||||
*/
|
||||
public interface TaskMonitor {
|
||||
|
||||
public static final TaskMonitor DUMMY = new StubTaskMonitor();
|
||||
|
||||
/**
|
||||
* A value to indicate that this monitor has no progress value set.
|
||||
*/
|
||||
/** A value to indicate that this monitor has no progress value set */
|
||||
public static final int NO_PROGRESS_VALUE = -1;
|
||||
|
||||
/**
|
||||
* Returns true if the user has cancelled the operation.
|
||||
* Returns true if the user has cancelled the operation
|
||||
*
|
||||
* @return true if the user has cancelled the operation
|
||||
*/
|
||||
public boolean isCancelled();
|
||||
|
||||
/**
|
||||
* True (the default) signals to paint the progress information inside of the progress bar.
|
||||
* Returns true if the monitor has been initialized
|
||||
*
|
||||
* @return true if the monitor has been initialized
|
||||
*/
|
||||
public default boolean isInitialized() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initialization state of the monitor
|
||||
*
|
||||
* @param init true for initialized, false otherwise
|
||||
*/
|
||||
public default void setInitialized(boolean init) {
|
||||
// do nothing - this is defaulted for backward compatibility so current
|
||||
// task monitor implementations do not have to change
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the monitor to an uninitialized state. This will result in the primary
|
||||
* monitor being returned from the {@link TaskMonitorService} on the next
|
||||
* invocation.
|
||||
*/
|
||||
public default void reset() {
|
||||
synchronized (this) {
|
||||
setMessage("");
|
||||
setProgress(0);
|
||||
setMaximum(0);
|
||||
setInitialized(false);
|
||||
clearCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* True (the default) signals to paint the progress information inside of the progress bar
|
||||
*
|
||||
* @param showProgressValue true to paint the progress value; false to not
|
||||
*/
|
||||
public void setShowProgressValue(boolean showProgressValue);
|
||||
|
||||
/**
|
||||
* Sets a message giving additional information about the current
|
||||
* progress.
|
||||
* @param message more information
|
||||
* Sets the message displayed on the task monitor
|
||||
*
|
||||
* @param message the message to display
|
||||
*/
|
||||
public void setMessage(String message);
|
||||
|
||||
/**
|
||||
* Returns a version of this monitor that cannot have its progress state changed. This is
|
||||
* meant for sub-tasks that should not be allowed to hijack task progress.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public default TaskMonitor getSecondaryMonitor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current progress value.
|
||||
* Sets the current progress value
|
||||
* @param value progress value
|
||||
*/
|
||||
public void setProgress(long value);
|
||||
|
@ -71,7 +115,7 @@ public interface TaskMonitor {
|
|||
public void initialize(long max);
|
||||
|
||||
/**
|
||||
* Set the progress maximum value.
|
||||
* Set the progress maximum value
|
||||
* <p><b>
|
||||
* Note: setting this value will reset the progress to be the max if the progress is currently
|
||||
* greater than the new new max value.
|
||||
|
@ -80,88 +124,70 @@ public interface TaskMonitor {
|
|||
public void setMaximum(long max);
|
||||
|
||||
/**
|
||||
* Returns the current maximum value for progress.
|
||||
* @return
|
||||
* Returns the current maximum value for progress
|
||||
* @return the maximum progress value
|
||||
*/
|
||||
public long getMaximum();
|
||||
|
||||
/**
|
||||
* An indeterminate task monitor may choose to show an animation instead of updating progress.
|
||||
* An indeterminate task monitor may choose to show an animation instead of updating progress
|
||||
* @param indeterminate true if indeterminate
|
||||
*/
|
||||
public void setIndeterminate(boolean indeterminate);
|
||||
|
||||
/**
|
||||
* Check to see if this monitor has been canceled.
|
||||
* @throws CancelledException if monitor has been cancelled.
|
||||
* Check to see if this monitor has been canceled
|
||||
* @throws CancelledException if monitor has been cancelled
|
||||
*/
|
||||
public void checkCanceled() throws CancelledException;
|
||||
|
||||
/**
|
||||
* A convenience method to increment the current progress by the given value.
|
||||
* @param incrementAmount The amount by which to increment the progress.
|
||||
* A convenience method to increment the current progress by the given value
|
||||
* @param incrementAmount The amount by which to increment the progress
|
||||
*/
|
||||
public void incrementProgress(long incrementAmount);
|
||||
|
||||
/**
|
||||
* Returns the current progress value or {@link #NO_PROGRESS_VALUE} if there is no value
|
||||
* set.
|
||||
* set
|
||||
* @return the current progress value or {@link #NO_PROGRESS_VALUE} if there is no value
|
||||
* set.
|
||||
* set
|
||||
*/
|
||||
public long getProgress();
|
||||
|
||||
/**
|
||||
* Notify that an issue occurred while processing.
|
||||
* @param issue the issue that was encountered
|
||||
*/
|
||||
public void reportIssue(Issue issue);
|
||||
|
||||
/**
|
||||
* Cancel the task.
|
||||
* Cancel the task
|
||||
*/
|
||||
public void cancel();
|
||||
|
||||
/**
|
||||
* Add cancelled listener.
|
||||
* @param listener
|
||||
* Add cancelled listener
|
||||
* @param listener the cancel listener
|
||||
*/
|
||||
public void addCancelledListener(CancelledListener listener);
|
||||
|
||||
/**
|
||||
* Remove cancelled listener.
|
||||
* @param listener
|
||||
* Remove cancelled listener
|
||||
* @param listener the cancel listener
|
||||
*/
|
||||
public void removeCancelledListener(CancelledListener listener);
|
||||
|
||||
/**
|
||||
* Set the enablement of the Cancel button.
|
||||
* Set the enablement of the Cancel button
|
||||
* @param enable true means to enable the cancel button
|
||||
*/
|
||||
public void setCancelEnabled(boolean enable);
|
||||
|
||||
/**
|
||||
* Returns true if cancel ability is enabled
|
||||
* @return true if cancel ability is enabled
|
||||
*/
|
||||
public boolean isCancelEnabled();
|
||||
|
||||
/**
|
||||
* Clear the cancellation so that this TaskMonitor may be reused.
|
||||
* Clear the cancellation so that this TaskMonitor may be reused
|
||||
*
|
||||
*/
|
||||
public void clearCanceled();
|
||||
|
||||
/**
|
||||
* Add an issue listener to this monitor.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void addIssueListener(IssueListener listener);
|
||||
|
||||
/**
|
||||
* Removes an issue listener to this monitor.
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void removeIssueListener(IssueListener listener);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.util.task;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
/**
|
||||
* Provides access to the {@link TaskMonitor} instance for the current thread. The first
|
||||
* time a monitor is requested via {@link #getMonitor()}, a "primary" monitor (one
|
||||
* that allows updating of task progress and status messages) is returned; all
|
||||
* subsequent requests will return a "secondary" monitor, which only allows
|
||||
* status message updates. This is to keep the progress bar from being updated
|
||||
* simultaneously by multiple parties.
|
||||
* <p>
|
||||
* Note: {@link TaskMonitor monitor} instances are registered with this service via the
|
||||
* {@link #register(TaskMonitor) setMonitor} call, and will be available to that thread until
|
||||
* the {@link #remove(int) remove} method is called.
|
||||
* <p>
|
||||
* Note: Because monitor instances are managed by a {@link ThreadLocal} object, they will be
|
||||
* cleaned up automatically by the GC when the thread is terminated.
|
||||
*/
|
||||
public class TaskMonitorService {
|
||||
|
||||
/**
|
||||
* The {@link TaskMonitor} instance. ThreadLocal ensures that each thread has access
|
||||
* to its own monitor.
|
||||
*/
|
||||
private static ThreadLocal<TaskMonitor> localMonitor = new ThreadLocal<TaskMonitor>() {
|
||||
|
||||
/**
|
||||
* Force the initial value to be null so users will have to call
|
||||
* {@link TaskMonitorService#register(TaskMonitor) register} to assign one
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
@Override
|
||||
protected TaskMonitor initialValue() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Unique id for each thread monitor that is assigned when a monitor is
|
||||
* {@link #register(TaskMonitor) registered}. This is to ensure that only clients who have
|
||||
* a valid id can remove a monitor.
|
||||
*/
|
||||
private static ThreadLocal<Integer> localMonitorId = new ThreadLocal<Integer>() {
|
||||
|
||||
@Override
|
||||
protected Integer initialValue() {
|
||||
return nextId.getAndIncrement();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains the next unique id for the monitor; this is updated each time a new monitor
|
||||
* is registered with the service.
|
||||
*/
|
||||
private static final AtomicInteger nextId = new AtomicInteger(0);
|
||||
|
||||
/**
|
||||
* Returns the task monitor for the current thread. If one has not yet been registered,
|
||||
* a {@link StubTaskMonitor stub monitor} is returned.
|
||||
*
|
||||
* @return the task monitor
|
||||
*/
|
||||
public synchronized static TaskMonitor getMonitor() {
|
||||
|
||||
if (localMonitor.get() == null) {
|
||||
|
||||
// If no monitor is available, just return a stub. The alternative is to throw an
|
||||
// exception but this isn't considered an error condition in all cases.
|
||||
localMonitor.set(new StubTaskMonitor());
|
||||
}
|
||||
|
||||
// If the monitor has already been initialized, return the secondary monitor to prevent
|
||||
// the caller from hijacking the progress bar
|
||||
if (localMonitor.get().isInitialized()) {
|
||||
return localMonitor.get().getSecondaryMonitor();
|
||||
}
|
||||
|
||||
// This ensures that the next time this method is called, the service
|
||||
// will return the secondary monitor
|
||||
localMonitor.get().setInitialized(true);
|
||||
|
||||
return localMonitor.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given monitor for this thread
|
||||
*
|
||||
* @param monitor the task monitor to register
|
||||
* @return the unique id for the monitor
|
||||
*/
|
||||
public static int register(TaskMonitor monitor) {
|
||||
|
||||
// Don't allow callers to register a monitor if on the swing thread
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
throw new IllegalArgumentException("Attempting to set a monitor in the Swing thread!");
|
||||
}
|
||||
|
||||
// Don't allow users to register a monitor if there is already one registered for this
|
||||
// thread
|
||||
if (localMonitor.get() != null) {
|
||||
throw new IllegalArgumentException("Task monitor already assigned to this thread");
|
||||
}
|
||||
|
||||
localMonitor.set(monitor);
|
||||
|
||||
return localMonitorId.get();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the monitor from the thread local object. To protect against clients cavalierly
|
||||
* removing monitors, a valid monitor id must be provided; this is generated at the time
|
||||
* of monitor {@link #register(TaskMonitor) registration}.
|
||||
* <p>
|
||||
* Note: This should generally not need to be called as the GC will clean up thread local
|
||||
* objects when the associated thread is finished.
|
||||
*
|
||||
* @param monitorId the unique ID for the monitor to be removed
|
||||
*/
|
||||
public static void remove(int monitorId) {
|
||||
|
||||
if (monitorId != localMonitorId.get()) {
|
||||
throw new IllegalArgumentException("Invalid monitor id for this thread: " + monitorId);
|
||||
}
|
||||
|
||||
localMonitor.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the constructor - this should not be instantiated
|
||||
*/
|
||||
private TaskMonitorService() {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue