Merge remote-tracking branch 'origin/GP-2903_ghidra1_ImmutableDomainObjects--SQUASHED'

This commit is contained in:
Ryan Kurtz 2022-12-15 06:01:56 -05:00
commit b707c2ea6b
11 changed files with 226 additions and 256 deletions

View file

@ -169,18 +169,7 @@ public class OpenProgramTask extends Task {
String path = url != null ? url.toString() : domainFile.getPathname(); String path = url != null ? url.toString() : domainFile.getPathname();
Object obj = null; Object obj = null;
try { try {
obj = domainFile.getReadOnlyDomainObject(consumer, version, taskMonitor); obj = domainFile.getReadOnlyDomainObject(consumer, version, taskMonitor);
if (obj == null) {
String errorMessage = "Can't open " + contentType + " - \"" + path + "\"";
if (version != DomainFile.DEFAULT_VERSION) {
errorMessage += " version " + version;
}
Msg.showError(this, null, "File Not Found", errorMessage);
}
} }
catch (CancelledException e) { catch (CancelledException e) {
// we don't care, the task has been cancelled // we don't care, the task has been cancelled

View file

@ -100,13 +100,17 @@ public class OpenVersionedFileDialog<T extends DomainObject> extends DataTreeDia
/** /**
* Get the selected domain object for read-only or immutable use. * Get the selected domain object for read-only or immutable use.
* If an existing open object is selected its original mode applies but consumer will * If an existing open object is selected its original mode applies but consumer will
* be added. * be added. The caller/consumer is responsible for releasing the returned domain object
* @param consumer consumer * when done using it (see {@link DomainObject#release(Object)}).
* @param readOnly true if the domain object should be opened read only, versus immutable * @param consumer domain object consumer
* @return null if a file was not selected * @param immutable true if the domain object should be opened immutable, else false for
* read-only. Immutable mode should not be used for content that will be modified. If
* read-only indicated an upgrade will always be performed if required.
* @return opened domain object or null if a file was not selected or if open failed to
* complete.
*/ */
@SuppressWarnings("unchecked") // relies on content class filter @SuppressWarnings("unchecked") // relies on content class filter
public T getDomainObject(Object consumer, boolean readOnly) { public T getDomainObject(Object consumer, boolean immutable) {
T dobj = null; T dobj = null;
if (usingOpenProgramList()) { if (usingOpenProgramList()) {
dobj = getSelectedOpenDomainObject(); dobj = getSelectedOpenDomainObject();
@ -115,20 +119,19 @@ public class OpenVersionedFileDialog<T extends DomainObject> extends DataTreeDia
} }
return dobj; return dobj;
} }
int version = DomainFile.DEFAULT_VERSION;
if (historyPanel != null) { if (historyPanel != null) {
dobj = (T) historyPanel.getSelectedVersion(consumer, readOnly); version = historyPanel.getSelectedVersionNumber();
} }
if (dobj == null) {
DomainFile domainFile = getDomainFile(); DomainFile domainFile = getDomainFile();
if (domainFile != null) { if (domainFile != null) {
GetVersionedObjectTask task = GetDomainObjectTask task =
new GetVersionedObjectTask(consumer, domainFile, DomainFile.DEFAULT_VERSION, new GetDomainObjectTask(consumer, domainFile, version, immutable);
readOnly); tool.execute(task, 1000);
tool.execute(task, 1000); return (T) task.getDomainObject();
return (T) task.getVersionedObject();
}
} }
return dobj; return null;
} }
/** /**

View file

@ -17,7 +17,6 @@ package ghidra.app.plugin.core.diff;
import java.awt.*; import java.awt.*;
import java.awt.event.*; import java.awt.event.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
@ -51,6 +50,7 @@ import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.*; import ghidra.app.util.viewer.listingpanel.*;
import ghidra.app.util.viewer.util.AddressIndexMap; import ghidra.app.util.viewer.util.AddressIndexMap;
import ghidra.app.util.viewer.util.FieldNavigator; import ghidra.app.util.viewer.util.FieldNavigator;
import ghidra.framework.main.GetDomainObjectTask;
import ghidra.framework.main.OpenVersionedFileDialog; import ghidra.framework.main.OpenVersionedFileDialog;
import ghidra.framework.model.*; import ghidra.framework.model.*;
import ghidra.framework.options.*; import ghidra.framework.options.*;
@ -60,7 +60,8 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.util.*; import ghidra.program.util.*;
import ghidra.util.*; import ghidra.util.*;
import ghidra.util.exception.*; import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.*; import ghidra.util.task.*;
import help.Help; import help.Help;
import help.HelpService; import help.HelpService;
@ -1143,7 +1144,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
tool.clearStatusInfo(); tool.clearStatusInfo();
JComponent component = dialog.getComponent(); JComponent component = dialog.getComponent();
Program dobj = dialog.getDomainObject(ProgramDiffPlugin.this, false); Program dobj = dialog.getDomainObject(ProgramDiffPlugin.this, true);
if (dobj != null) { if (dobj != null) {
if (openSecondProgram(dobj, component)) { if (openSecondProgram(dobj, component)) {
dialog.close(); dialog.close();
@ -1151,9 +1152,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
} }
return; return;
} }
displayStatus(component, "Can't Open Selected Program",
"Please select a file, not a folder.", OptionDialog.INFORMATION_MESSAGE);
}); });
dialog.showComponent(); dialog.showComponent();
} }
@ -1531,16 +1529,19 @@ public class ProgramDiffPlugin extends ProgramPlugin
} }
private boolean openSecondProgram(DomainFile df) { private boolean openSecondProgram(DomainFile df) {
if (!Program.class.isAssignableFrom(df.getDomainObjectClass())) {
Msg.error(this, "Failed to launch Diff for non-Program file: " + df.getName());
return false;
}
OpenSecondProgramTask task = new OpenSecondProgramTask(df); GetDomainObjectTask task =
new GetDomainObjectTask(this, df, DomainFile.DEFAULT_VERSION, true);
new TaskLauncher(task, tool.getToolFrame(), 500); new TaskLauncher(task, tool.getToolFrame(), 500);
// block until the task completes // block until the task completes
if (!task.wasCanceled()) { Program newProgram = (Program) task.getDomainObject();
Program newProgram = task.getDiffProgram(); if (newProgram != null) {
if (newProgram != null) { return openSecondProgram(newProgram, null);
return openSecondProgram(newProgram, null);
}
} }
return false; return false;
} }
@ -1851,71 +1852,6 @@ public class ProgramDiffPlugin extends ProgramPlugin
} }
} }
private class OpenSecondProgramTask extends Task {
private DomainFile domainFile;
private Program diffProgram;
private TaskMonitor monitor;
OpenSecondProgramTask(DomainFile domainFile) {
super("Opening Program for Diff", true, true, true);
this.domainFile = domainFile;
}
@Override
public void run(TaskMonitor tm) {
this.monitor = tm;
try {
try {
monitor.setMessage("Waiting on program file...");
diffProgram =
(Program) domainFile.getImmutableDomainObject(ProgramDiffPlugin.this,
DomainFile.DEFAULT_VERSION, monitor);
}
catch (VersionException e) {
if (e.isUpgradable()) {
try {
diffProgram =
(Program) domainFile.getReadOnlyDomainObject(ProgramDiffPlugin.this,
DomainFile.DEFAULT_VERSION, monitor);
}
catch (VersionException exc) {
Msg.showError(this, null, "Error Getting Diff Program",
"Getting read only file failed");
}
catch (IOException exc) {
if (!monitor.isCancelled()) {
Msg.showError(this, null, "Error Getting Diff Program",
"Getting read only file failed", exc);
}
}
}
else {
Msg.showError(this, null, "Error Getting Diff Program",
"File cannot be upgraded.");
}
}
catch (IOException e) {
Msg.showError(this, null, "Error Getting Diff Program",
"Getting read only file failed", e);
}
}
catch (CancelledException e) {
// For now do nothing if user cancels
}
monitor.setMessage("");
}
boolean wasCanceled() {
return monitor.isCancelled();
}
Program getDiffProgram() {
return diffProgram;
}
}
@Override @Override
public void domainObjectChanged(DomainObjectChangedEvent event) { public void domainObjectChanged(DomainObjectChangedEvent event) {
if (secondaryDiffProgram != null && diffDetailsProvider != null) { if (secondaryDiffProgram != null && diffDetailsProvider != null) {

View file

@ -20,7 +20,6 @@ import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.Map; import java.util.Map;
import javax.help.UnsupportedOperationException;
import javax.swing.Icon; import javax.swing.Icon;
import generic.theme.GIcon; import generic.theme.GIcon;
@ -45,9 +44,6 @@ import ghidra.util.task.TaskMonitor;
*/ */
public abstract class LinkHandler<T extends DomainObjectAdapterDB> extends DBContentHandler<T> { public abstract class LinkHandler<T extends DomainObjectAdapterDB> extends DBContentHandler<T> {
// TODO: Need to improve by making this meta data on file instead of database content.
// Metadata use would eliminate need for DB but we lack support for non-DB files.
public static final String URL_METADATA_KEY = "link.url"; public static final String URL_METADATA_KEY = "link.url";
// 16x16 link icon where link is placed in lower-left corner // 16x16 link icon where link is placed in lower-left corner
@ -76,15 +72,29 @@ public abstract class LinkHandler<T extends DomainObjectAdapterDB> extends DBCon
} }
} }
@SuppressWarnings("unchecked")
@Override @Override
public final T getReadOnlyObject(FolderItem item, int version, boolean okToUpgrade, public final T getReadOnlyObject(FolderItem item, int version, boolean okToUpgrade,
Object consumer, TaskMonitor monitor) Object consumer, TaskMonitor monitor)
throws IOException, VersionException, CancelledException { throws IOException, VersionException, CancelledException {
if (!okToUpgrade) { if (!okToUpgrade) {
throw new IllegalArgumentException("okToUpgrade must be true"); throw new UnsupportedOperationException("okToUpgrade must be true for link-file");
} }
return getObject(item, version, consumer, monitor, false);
}
@Override
public T getImmutableObject(FolderItem item, Object consumer, int version, int minChangeVersion,
TaskMonitor monitor) throws IOException, CancelledException, VersionException {
if (minChangeVersion != -1) {
throw new UnsupportedOperationException("minChangeVersion must be -1 for link-file");
}
return getObject(item, version, consumer, monitor, true);
}
@SuppressWarnings("unchecked")
private T getObject(FolderItem item, int version, Object consumer, TaskMonitor monitor,
boolean immutable)
throws IOException, VersionException, CancelledException {
URL url = getURL(item); URL url = getURL(item);
@ -112,10 +122,11 @@ public abstract class LinkHandler<T extends DomainObjectAdapterDB> extends DBCon
DomainFile linkedFile = (DomainFile) content; DomainFile linkedFile = (DomainFile) content;
if (!getDomainObjectClass().isAssignableFrom(linkedFile.getDomainObjectClass())) { if (!getDomainObjectClass().isAssignableFrom(linkedFile.getDomainObjectClass())) {
throw new BadLinkException( throw new BadLinkException(
"Excepted " + getDomainObjectClass() + " but linked to " + "Expected " + getDomainObjectClass() + " but linked to " +
linkedFile.getDomainObjectClass()); linkedFile.getDomainObjectClass());
} }
return (T) linkedFile.getReadOnlyDomainObject(consumer, version, monitor); return immutable ? (T) linkedFile.getImmutableDomainObject(consumer, version, monitor)
: (T) linkedFile.getReadOnlyDomainObject(consumer, version, monitor);
} }
finally { finally {
if (content != null) { if (content != null) {
@ -128,16 +139,8 @@ public abstract class LinkHandler<T extends DomainObjectAdapterDB> extends DBCon
public final T getDomainObject(FolderItem item, FileSystem userfs, long checkoutId, public final T getDomainObject(FolderItem item, FileSystem userfs, long checkoutId,
boolean okToUpgrade, boolean okToRecover, Object consumer, TaskMonitor monitor) boolean okToUpgrade, boolean okToRecover, Object consumer, TaskMonitor monitor)
throws IOException, CancelledException, VersionException { throws IOException, CancelledException, VersionException {
// Always upgrade if needed for read-only object // getReadOnlyObject or getImmutableObject should be used
return getReadOnlyObject(item, DomainFile.DEFAULT_VERSION, true, consumer, monitor); throw new UnsupportedOperationException("link-file does not support getDomainObject");
}
@Override
public T getImmutableObject(FolderItem item, Object consumer, int version, int minChangeVersion,
TaskMonitor monitor) throws IOException, CancelledException, VersionException {
//throw new UnsupportedOperationException("link-file does not support getImmutableObject");
// See GP-2903
return getReadOnlyObject(item, version, true, consumer, monitor);
} }
@Override @Override

View file

@ -0,0 +1,129 @@
/* ###
* 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.framework.main;
import java.io.IOException;
import docking.widgets.OptionDialog;
import ghidra.framework.client.ClientUtil;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainObject;
import ghidra.util.VersionExceptionHandler;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.Task;
import ghidra.util.task.TaskMonitor;
/**
* A modal task that gets a domain object for a specified version.
* Object is either open read-only or immutable.
*
* NOTE: This task is not intended to open a domain file for modification and saving back
* to a project.
*
* A file open for read-only use will be upgraded if needed and is possible. Once open it is
* important that the specified consumer be released from the domain object when done using
* the open object (see {@link DomainObject#release(Object)}).
*/
public class GetDomainObjectTask extends Task {
private Object consumer;
private DomainFile domainFile;
private int versionNumber;
private boolean immutable;
private DomainObject versionedObj;
/**
* Construct task open specified domainFile read only.
* An upgrade is performed if needed and is possible.
* @param consumer consumer of the domain object
* @param domainFile domain file
* @param versionNumber version
*/
public GetDomainObjectTask(Object consumer, DomainFile domainFile, int versionNumber) {
this(consumer, domainFile, versionNumber, false);
}
/**
* Construct task open specified domainFile read only or immutable. Immutable mode should not
* be used for content that will be modified.
* If read-only an upgrade is performed if needed, if immutable the user will be prompted
* if an upgrade should be performed if possible in which case it will open read-only.
* @param consumer consumer of the domain object
* @param domainFile domain file
* @param versionNumber version
* @param immutable true if the object should be open immutable, else read-only.
*/
public GetDomainObjectTask(Object consumer, DomainFile domainFile, int versionNumber,
boolean immutable) {
super("Get Versioned Domain Object", true, false, true);
this.consumer = consumer;
this.domainFile = domainFile;
this.versionNumber = versionNumber;
this.immutable = immutable;
}
@Override
public void run(TaskMonitor monitor) {
String contentType = domainFile.getContentType();
try {
monitor.setMessage("Getting Version " + versionNumber + " for " + domainFile.getName());
if (immutable) {
versionedObj =
domainFile.getImmutableDomainObject(consumer, versionNumber, monitor);
}
else {
// Upgrade will be performed if required
versionedObj = domainFile.getReadOnlyDomainObject(consumer, versionNumber, monitor);
}
}
catch (CancelledException e) {
// ignore
}
catch (IOException e) {
ClientUtil.handleException(AppInfo.getActiveProject().getRepository(), e,
contentType + " Open", null);
} catch (VersionException e) {
if (immutable && e.isUpgradable()) {
String detailMessage =
e.getDetailMessage() == null ? "" : "\n" + e.getDetailMessage();
String title = "Upgrade " + contentType + " Data? " + domainFile.getName();
String message = "The " + contentType + " file you are attempting to open" +
" is an older version." + detailMessage + "\n \n" +
"Would you like to Upgrade it now?";
int rc = OptionDialog.showOptionDialog(null, title, message, "Upgrade",
OptionDialog.QUESTION_MESSAGE);
if (rc == OptionDialog.OPTION_ONE) {
// try again as read-only
immutable = false;
run(monitor);
}
return;
}
VersionExceptionHandler.showVersionError(null, domainFile.getName(),
domainFile.getContentType(), contentType + " Open", e);
}
}
/**
* Return the domain object instance.
* @return domain object which was opened or null if task cancelled or failed
*/
public DomainObject getDomainObject() {
return versionedObj;
}
}

View file

@ -1,104 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.framework.main;
import java.io.IOException;
import ghidra.framework.client.ClientUtil;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainObject;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.Task;
import ghidra.util.task.TaskMonitor;
/**
* A modal task that gets a domain object for a specific version.
*
*
*/
public class GetVersionedObjectTask extends Task {
private Object consumer;
private DomainFile domainFile;
private int versionNumber;
private DomainObject versionedObj;
/**
* Constructor; task will get a read only domain object
* @param consumer consumer of the domain object
* @param domainFile domain file
* @param versionNumber version
*/
public GetVersionedObjectTask(Object consumer, DomainFile domainFile,
int versionNumber) {
this(consumer, domainFile, versionNumber, true);
}
/**
* Constructor
* @param consumer consumer of the domain object
* @param domainFile domain file
* @param versionNumber version
* @param readOnly true if the object should be read only versus
* immutable
*/
public GetVersionedObjectTask(Object consumer, DomainFile domainFile,
int versionNumber, boolean readOnly) {
super("Get Versioned Domain Object", true, false, true);
this.consumer = consumer;
this.domainFile = domainFile;
this.versionNumber = versionNumber;
}
/* (non-Javadoc)
* @see ghidra.util.task.Task#run(ghidra.util.task.TaskMonitor)
*/
@Override
public void run(TaskMonitor monitor) {
try {
monitor.setMessage("Getting Version " + versionNumber +
" for " + domainFile.getName());
versionedObj =
domainFile.getReadOnlyDomainObject(consumer, versionNumber,
monitor);
}catch (CancelledException e) {
}catch (IOException e) {
if (domainFile.isInWritableProject()) {
ClientUtil.handleException(AppInfo.getActiveProject().getRepository(), e,
"Get Versioned Object", null);
}
else {
Msg.showError(this, null,
"Error Getting Versioned Object", "Could not get version " + versionNumber +
" for " + domainFile.getName() + ": " + e, e);
}
} catch (VersionException e) {
Msg.showError(this,
null,
"Error Getting Versioned Object", "Could not get version " + versionNumber +
" for " + domainFile.getName() + ": " + e);
}
}
/**
* Return the versioned domain object.
*/
public DomainObject getVersionedObject() {
return versionedObj;
}
}

View file

@ -370,9 +370,9 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable {
} }
private DomainObject getVersionedObject(DomainFile file, int versionNumber) { private DomainObject getVersionedObject(DomainFile file, int versionNumber) {
GetVersionedObjectTask task = new GetVersionedObjectTask(this, file, versionNumber); GetDomainObjectTask task = new GetDomainObjectTask(this, file, versionNumber);
plugin.getTool().execute(task, 250); plugin.getTool().execute(task, 250);
return task.getVersionedObject(); return task.getDomainObject();
} }
//================================================================================================== //==================================================================================================

View file

@ -22,7 +22,7 @@ import java.io.IOException;
import docking.widgets.tree.GTreeNode; import docking.widgets.tree.GTreeNode;
import ghidra.app.util.FileOpenDataFlavorHandler; import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.client.*; import ghidra.framework.client.*;
import ghidra.framework.main.GetVersionedObjectTask; import ghidra.framework.main.GetDomainObjectTask;
import ghidra.framework.model.*; import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.util.task.TaskLauncher; import ghidra.util.task.TaskLauncher;
@ -35,15 +35,18 @@ public final class LocalVersionInfoHandler
VersionInfo info = (VersionInfo) obj; VersionInfo info = (VersionInfo) obj;
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath()); DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
GetVersionedObjectTask task = GetDomainObjectTask task =
new GetVersionedObjectTask(this, file, info.getVersionNumber()); new GetDomainObjectTask(this, file, info.getVersionNumber());
tool.execute(task, 250); tool.execute(task, 250);
DomainObject versionedObj = task.getVersionedObject(); DomainObject versionedObj = task.getDomainObject();
if (versionedObj != null) { if (versionedObj != null) {
DomainFile vfile = versionedObj.getDomainFile(); try {
tool.acceptDomainFiles(new DomainFile[] { vfile }); DomainFile vfile = versionedObj.getDomainFile();
versionedObj.release(this); tool.acceptDomainFiles(new DomainFile[] { vfile });
}
finally {
versionedObj.release(this);
}
} }
} }

View file

@ -38,7 +38,7 @@ import docking.widgets.OptionDialog;
import docking.widgets.table.*; import docking.widgets.table.*;
import ghidra.app.util.GenericHelpTopics; import ghidra.app.util.GenericHelpTopics;
import ghidra.framework.client.ClientUtil; import ghidra.framework.client.ClientUtil;
import ghidra.framework.main.GetVersionedObjectTask; import ghidra.framework.main.GetDomainObjectTask;
import ghidra.framework.model.*; import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.store.ItemCheckoutStatus; import ghidra.framework.store.ItemCheckoutStatus;
@ -143,31 +143,36 @@ public class VersionHistoryPanel extends JPanel implements Draggable {
} }
/** /**
* Get the domain object for the selected version. * Get the selected {@link Version}.
* @param consumer the consumer * @return selected {@link Version} or null if no selection
* @param readOnly true if read only
* @return null if there is no selection
*/ */
public DomainObject getSelectedVersion(Object consumer, boolean readOnly) { public Version getSelectedVersion() {
int row = table.getSelectedRow(); int row = table.getSelectedRow();
if (row >= 0) { if (row >= 0) {
Version version = tableModel.getVersionAt(row); return tableModel.getVersionAt(row);
return getVersionedObject(consumer, version.getVersion(), readOnly);
} }
return null; return null;
} }
/**
* Determine if a version selection has been made.
* @return true if version selection has been made, esle false
*/
public boolean isVersionSelected() { public boolean isVersionSelected() {
return !table.getSelectionModel().isSelectionEmpty(); return !table.getSelectionModel().isSelectionEmpty();
} }
/**
* Get the selected version number or {@link DomainFile#DEFAULT_VERSION} if no selection.
* @return selected version number
*/
public int getSelectedVersionNumber() { public int getSelectedVersionNumber() {
int row = table.getSelectedRow(); int row = table.getSelectedRow();
if (row >= 0) { if (row >= 0) {
Version version = tableModel.getVersionAt(row); Version version = tableModel.getVersionAt(row);
return version.getVersion(); return version.getVersion();
} }
return -1; return DomainFile.DEFAULT_VERSION;
} }
@Override @Override
@ -260,11 +265,11 @@ public class VersionHistoryPanel extends JPanel implements Draggable {
dragSource.createDefaultDragGestureRecognizer(table, dragAction, dragGestureAdapter); dragSource.createDefaultDragGestureRecognizer(table, dragAction, dragGestureAdapter);
} }
private DomainObject getVersionedObject(Object consumer, int versionNumber, boolean readOnly) { private DomainObject getVersionedObject(Object consumer, int versionNumber, boolean immutable) {
GetVersionedObjectTask task = GetDomainObjectTask task =
new GetVersionedObjectTask(consumer, domainFile, versionNumber, readOnly); new GetDomainObjectTask(consumer, domainFile, versionNumber, immutable);
tool.execute(task, 1000); tool.execute(task, 1000);
return task.getVersionedObject(); return task.getDomainObject();
} }
private void delete() { private void delete() {
@ -337,13 +342,17 @@ public class VersionHistoryPanel extends JPanel implements Draggable {
Version version = tableModel.getVersionAt(row); Version version = tableModel.getVersionAt(row);
DomainObject versionedObj = getVersionedObject(this, version.getVersion(), true); DomainObject versionedObj = getVersionedObject(this, version.getVersion(), true);
if (versionedObj != null) { if (versionedObj != null) {
if (toolName != null) { try {
tool.getToolServices().launchTool(toolName, versionedObj.getDomainFile()); if (toolName != null) {
tool.getToolServices().launchTool(toolName, versionedObj.getDomainFile());
}
else {
tool.getToolServices().launchDefaultTool(versionedObj.getDomainFile());
}
} }
else { finally {
tool.getToolServices().launchDefaultTool(versionedObj.getDomainFile()); versionedObj.release(this);
} }
versionedObj.release(this);
} }
} }

View file

@ -187,6 +187,9 @@ public interface DomainFile extends Comparable<DomainFile> {
/** /**
* Returns a new DomainObject that cannot be changed or saved to its original file. * Returns a new DomainObject that cannot be changed or saved to its original file.
* NOTE: The use of this method should generally be avoided since it can't
* handle version changes that may have occured and require a data upgrade
* (e.g., DB schema change).
* @param consumer consumer of the domain object which is responsible for * @param consumer consumer of the domain object which is responsible for
* releasing it after use. * releasing it after use.
* @param version the domain object version requested. DEFAULT_VERSION should be * @param version the domain object version requested. DEFAULT_VERSION should be

View file

@ -145,7 +145,6 @@ public class ProgramContentHandler extends DBWithUserDataContentHandler<ProgramD
} }
catch (Throwable t) { catch (Throwable t) {
Msg.error(this, "getReadOnlyObject failed", t); Msg.error(this, "getReadOnlyObject failed", t);
t.printStackTrace();
String msg = t.getMessage(); String msg = t.getMessage();
if (msg == null) { if (msg == null) {
msg = t.toString(); msg = t.toString();