diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/task/OpenProgramTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/task/OpenProgramTask.java index 53ad559271..401d0eb881 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/task/OpenProgramTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/task/OpenProgramTask.java @@ -169,18 +169,7 @@ public class OpenProgramTask extends Task { String path = url != null ? url.toString() : domainFile.getPathname(); Object obj = null; try { - 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) { // we don't care, the task has been cancelled diff --git a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java index 0aa646b764..a7311543c8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/framework/main/OpenVersionedFileDialog.java @@ -100,13 +100,17 @@ public class OpenVersionedFileDialog extends DataTreeDia /** * 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 - * be added. - * @param consumer consumer - * @param readOnly true if the domain object should be opened read only, versus immutable - * @return null if a file was not selected + * be added. The caller/consumer is responsible for releasing the returned domain object + * when done using it (see {@link DomainObject#release(Object)}). + * @param consumer domain object consumer + * @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 - public T getDomainObject(Object consumer, boolean readOnly) { + public T getDomainObject(Object consumer, boolean immutable) { T dobj = null; if (usingOpenProgramList()) { dobj = getSelectedOpenDomainObject(); @@ -115,20 +119,19 @@ public class OpenVersionedFileDialog extends DataTreeDia } return dobj; } + int version = DomainFile.DEFAULT_VERSION; if (historyPanel != null) { - dobj = (T) historyPanel.getSelectedVersion(consumer, readOnly); + version = historyPanel.getSelectedVersionNumber(); } - if (dobj == null) { - DomainFile domainFile = getDomainFile(); - if (domainFile != null) { - GetVersionedObjectTask task = - new GetVersionedObjectTask(consumer, domainFile, DomainFile.DEFAULT_VERSION, - readOnly); - tool.execute(task, 1000); - return (T) task.getVersionedObject(); - } + + DomainFile domainFile = getDomainFile(); + if (domainFile != null) { + GetDomainObjectTask task = + new GetDomainObjectTask(consumer, domainFile, version, immutable); + tool.execute(task, 1000); + return (T) task.getDomainObject(); } - return dobj; + return null; } /** diff --git a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java index 3d26cbb5c1..725da7a851 100644 --- a/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java +++ b/Ghidra/Features/ProgramDiff/src/main/java/ghidra/app/plugin/core/diff/ProgramDiffPlugin.java @@ -17,7 +17,6 @@ package ghidra.app.plugin.core.diff; import java.awt.*; import java.awt.event.*; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.*; 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.util.AddressIndexMap; import ghidra.app.util.viewer.util.FieldNavigator; +import ghidra.framework.main.GetDomainObjectTask; import ghidra.framework.main.OpenVersionedFileDialog; import ghidra.framework.model.*; import ghidra.framework.options.*; @@ -60,7 +60,8 @@ import ghidra.program.model.address.*; import ghidra.program.model.listing.*; import ghidra.program.util.*; import ghidra.util.*; -import ghidra.util.exception.*; +import ghidra.util.exception.AssertException; +import ghidra.util.exception.CancelledException; import ghidra.util.task.*; import help.Help; import help.HelpService; @@ -1143,7 +1144,7 @@ public class ProgramDiffPlugin extends ProgramPlugin tool.clearStatusInfo(); JComponent component = dialog.getComponent(); - Program dobj = dialog.getDomainObject(ProgramDiffPlugin.this, false); + Program dobj = dialog.getDomainObject(ProgramDiffPlugin.this, true); if (dobj != null) { if (openSecondProgram(dobj, component)) { dialog.close(); @@ -1151,9 +1152,6 @@ public class ProgramDiffPlugin extends ProgramPlugin } return; } - - displayStatus(component, "Can't Open Selected Program", - "Please select a file, not a folder.", OptionDialog.INFORMATION_MESSAGE); }); dialog.showComponent(); } @@ -1531,16 +1529,19 @@ public class ProgramDiffPlugin extends ProgramPlugin } 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); // block until the task completes - if (!task.wasCanceled()) { - Program newProgram = task.getDiffProgram(); - if (newProgram != null) { - return openSecondProgram(newProgram, null); - } + Program newProgram = (Program) task.getDomainObject(); + if (newProgram != null) { + return openSecondProgram(newProgram, null); } 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 public void domainObjectChanged(DomainObjectChangedEvent event) { if (secondaryDiffProgram != null && diffDetailsProvider != null) { diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/LinkHandler.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/LinkHandler.java index 110482a6c3..644d140931 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/LinkHandler.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/LinkHandler.java @@ -20,7 +20,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Map; -import javax.help.UnsupportedOperationException; import javax.swing.Icon; import generic.theme.GIcon; @@ -45,9 +44,6 @@ import ghidra.util.task.TaskMonitor; */ public abstract class LinkHandler extends DBContentHandler { - // 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"; // 16x16 link icon where link is placed in lower-left corner @@ -76,15 +72,29 @@ public abstract class LinkHandler extends DBCon } } - @SuppressWarnings("unchecked") @Override public final T getReadOnlyObject(FolderItem item, int version, boolean okToUpgrade, Object consumer, TaskMonitor monitor) throws IOException, VersionException, CancelledException { - 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); @@ -112,10 +122,11 @@ public abstract class LinkHandler extends DBCon DomainFile linkedFile = (DomainFile) content; if (!getDomainObjectClass().isAssignableFrom(linkedFile.getDomainObjectClass())) { throw new BadLinkException( - "Excepted " + getDomainObjectClass() + " but linked to " + + "Expected " + getDomainObjectClass() + " but linked to " + linkedFile.getDomainObjectClass()); } - return (T) linkedFile.getReadOnlyDomainObject(consumer, version, monitor); + return immutable ? (T) linkedFile.getImmutableDomainObject(consumer, version, monitor) + : (T) linkedFile.getReadOnlyDomainObject(consumer, version, monitor); } finally { if (content != null) { @@ -128,16 +139,8 @@ public abstract class LinkHandler extends DBCon public final T getDomainObject(FolderItem item, FileSystem userfs, long checkoutId, boolean okToUpgrade, boolean okToRecover, Object consumer, TaskMonitor monitor) throws IOException, CancelledException, VersionException { - // Always upgrade if needed for read-only object - return getReadOnlyObject(item, DomainFile.DEFAULT_VERSION, true, consumer, monitor); - } - - @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); + // getReadOnlyObject or getImmutableObject should be used + throw new UnsupportedOperationException("link-file does not support getDomainObject"); } @Override diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/GetDomainObjectTask.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/GetDomainObjectTask.java new file mode 100644 index 0000000000..c1ad49b0ec --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/GetDomainObjectTask.java @@ -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; + } +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/GetVersionedObjectTask.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/GetVersionedObjectTask.java deleted file mode 100644 index 7b9be7b865..0000000000 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/GetVersionedObjectTask.java +++ /dev/null @@ -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; - } -} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ToolButton.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ToolButton.java index 7561d04a23..9b7b8aacaf 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ToolButton.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/ToolButton.java @@ -370,9 +370,9 @@ class ToolButton extends EmptyBorderButton implements Draggable, Droppable { } 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); - return task.getVersionedObject(); + return task.getDomainObject(); } //================================================================================================== diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/LocalVersionInfoHandler.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/LocalVersionInfoHandler.java index 294f45895d..c337b17515 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/LocalVersionInfoHandler.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/LocalVersionInfoHandler.java @@ -22,7 +22,7 @@ import java.io.IOException; import docking.widgets.tree.GTreeNode; import ghidra.app.util.FileOpenDataFlavorHandler; import ghidra.framework.client.*; -import ghidra.framework.main.GetVersionedObjectTask; +import ghidra.framework.main.GetDomainObjectTask; import ghidra.framework.model.*; import ghidra.framework.plugintool.PluginTool; import ghidra.util.task.TaskLauncher; @@ -35,15 +35,18 @@ public final class LocalVersionInfoHandler VersionInfo info = (VersionInfo) obj; DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath()); - GetVersionedObjectTask task = - new GetVersionedObjectTask(this, file, info.getVersionNumber()); + GetDomainObjectTask task = + new GetDomainObjectTask(this, file, info.getVersionNumber()); tool.execute(task, 250); - DomainObject versionedObj = task.getVersionedObject(); - + DomainObject versionedObj = task.getDomainObject(); if (versionedObj != null) { - DomainFile vfile = versionedObj.getDomainFile(); - tool.acceptDomainFiles(new DomainFile[] { vfile }); - versionedObj.release(this); + try { + DomainFile vfile = versionedObj.getDomainFile(); + tool.acceptDomainFiles(new DomainFile[] { vfile }); + } + finally { + versionedObj.release(this); + } } } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryPanel.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryPanel.java index 67e3440610..f3f3566d65 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryPanel.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/main/datatree/VersionHistoryPanel.java @@ -38,7 +38,7 @@ import docking.widgets.OptionDialog; import docking.widgets.table.*; import ghidra.app.util.GenericHelpTopics; import ghidra.framework.client.ClientUtil; -import ghidra.framework.main.GetVersionedObjectTask; +import ghidra.framework.main.GetDomainObjectTask; import ghidra.framework.model.*; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.store.ItemCheckoutStatus; @@ -143,31 +143,36 @@ public class VersionHistoryPanel extends JPanel implements Draggable { } /** - * Get the domain object for the selected version. - * @param consumer the consumer - * @param readOnly true if read only - * @return null if there is no selection + * Get the selected {@link Version}. + * @return selected {@link Version} or null if no selection */ - public DomainObject getSelectedVersion(Object consumer, boolean readOnly) { + public Version getSelectedVersion() { int row = table.getSelectedRow(); if (row >= 0) { - Version version = tableModel.getVersionAt(row); - return getVersionedObject(consumer, version.getVersion(), readOnly); + return tableModel.getVersionAt(row); } return null; } + /** + * Determine if a version selection has been made. + * @return true if version selection has been made, esle false + */ public boolean isVersionSelected() { return !table.getSelectionModel().isSelectionEmpty(); } + /** + * Get the selected version number or {@link DomainFile#DEFAULT_VERSION} if no selection. + * @return selected version number + */ public int getSelectedVersionNumber() { int row = table.getSelectedRow(); if (row >= 0) { Version version = tableModel.getVersionAt(row); return version.getVersion(); } - return -1; + return DomainFile.DEFAULT_VERSION; } @Override @@ -260,11 +265,11 @@ public class VersionHistoryPanel extends JPanel implements Draggable { dragSource.createDefaultDragGestureRecognizer(table, dragAction, dragGestureAdapter); } - private DomainObject getVersionedObject(Object consumer, int versionNumber, boolean readOnly) { - GetVersionedObjectTask task = - new GetVersionedObjectTask(consumer, domainFile, versionNumber, readOnly); + private DomainObject getVersionedObject(Object consumer, int versionNumber, boolean immutable) { + GetDomainObjectTask task = + new GetDomainObjectTask(consumer, domainFile, versionNumber, immutable); tool.execute(task, 1000); - return task.getVersionedObject(); + return task.getDomainObject(); } private void delete() { @@ -337,13 +342,17 @@ public class VersionHistoryPanel extends JPanel implements Draggable { Version version = tableModel.getVersionAt(row); DomainObject versionedObj = getVersionedObject(this, version.getVersion(), true); if (versionedObj != null) { - if (toolName != null) { - tool.getToolServices().launchTool(toolName, versionedObj.getDomainFile()); + try { + if (toolName != null) { + tool.getToolServices().launchTool(toolName, versionedObj.getDomainFile()); + } + else { + tool.getToolServices().launchDefaultTool(versionedObj.getDomainFile()); + } } - else { - tool.getToolServices().launchDefaultTool(versionedObj.getDomainFile()); + finally { + versionedObj.release(this); } - versionedObj.release(this); } } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainFile.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainFile.java index 0562ee503f..77eb2bc299 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainFile.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/model/DomainFile.java @@ -187,6 +187,9 @@ public interface DomainFile extends Comparable { /** * 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 * releasing it after use. * @param version the domain object version requested. DEFAULT_VERSION should be diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramContentHandler.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramContentHandler.java index a7d6c10c0b..480638c1ec 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramContentHandler.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/ProgramContentHandler.java @@ -145,7 +145,6 @@ public class ProgramContentHandler extends DBWithUserDataContentHandler