mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-18 16:40:08 +00:00
GP-1948 Refactor program tree for improved performance during import
This commit is contained in:
parent
8e8b193ac1
commit
60c47844c0
@ -15,12 +15,15 @@
|
||||
*/
|
||||
package db;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface RecordTranslator {
|
||||
|
||||
/**
|
||||
* Translate the indicated old database record into a current database record.
|
||||
* @param oldRecord the old database record.
|
||||
* @return the new data base record in the form required for the current database version.
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
DBRecord translateRecord(DBRecord oldRecord);
|
||||
DBRecord translateRecord(DBRecord oldRecord) throws IOException;
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ package ghidra.program.database.module;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
|
||||
import db.Field;
|
||||
import db.DBRecord;
|
||||
import db.Field;
|
||||
import ghidra.program.database.DBObjectCache;
|
||||
import ghidra.program.database.DatabaseObject;
|
||||
import ghidra.program.model.address.*;
|
||||
@ -37,16 +37,17 @@ class FragmentDB extends DatabaseObject implements ProgramFragment {
|
||||
|
||||
private DBRecord record;
|
||||
private ModuleManager moduleMgr;
|
||||
private GroupDBAdapter adapter;
|
||||
private AddressSetView addrSet;
|
||||
private FragmentDBAdapter fragmentAdapter;
|
||||
private ParentChildDBAdapter parentChildAdapter;
|
||||
private AddressSet addrSet;
|
||||
private Lock lock;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param moduleMgr
|
||||
* @param cache
|
||||
* @param record
|
||||
* @param addrSet
|
||||
* @param moduleMgr module manager
|
||||
* @param cache fragment DB cache
|
||||
* @param record fragment record
|
||||
* @param addrSet fragment address set
|
||||
*/
|
||||
FragmentDB(ModuleManager moduleMgr, DBObjectCache<FragmentDB> cache, DBRecord record,
|
||||
AddressSet addrSet) {
|
||||
@ -54,14 +55,15 @@ class FragmentDB extends DatabaseObject implements ProgramFragment {
|
||||
this.moduleMgr = moduleMgr;
|
||||
this.record = record;
|
||||
this.addrSet = addrSet;
|
||||
adapter = moduleMgr.getGroupDBAdapter();
|
||||
fragmentAdapter = moduleMgr.getFragmentAdapter();
|
||||
parentChildAdapter = moduleMgr.getParentChildAdapter();
|
||||
lock = moduleMgr.getLock();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean refresh() {
|
||||
try {
|
||||
DBRecord rec = adapter.getFragmentRecord(key);
|
||||
DBRecord rec = fragmentAdapter.getFragmentRecord(key);
|
||||
if (rec != null) {
|
||||
record = rec;
|
||||
addrSet = moduleMgr.getFragmentAddressSet(key);
|
||||
@ -91,7 +93,7 @@ class FragmentDB extends DatabaseObject implements ProgramFragment {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
return record.getString(TreeManager.FRAGMENT_COMMENTS_COL);
|
||||
return record.getString(FragmentDBAdapter.FRAGMENT_COMMENTS_COL);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@ -103,7 +105,7 @@ class FragmentDB extends DatabaseObject implements ProgramFragment {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
return record.getString(TreeManager.FRAGMENT_NAME_COL);
|
||||
return record.getString(FragmentDBAdapter.FRAGMENT_NAME_COL);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@ -115,7 +117,8 @@ class FragmentDB extends DatabaseObject implements ProgramFragment {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
Field[] keys = adapter.getParentChildKeys(-key, TreeManager.CHILD_ID_COL);
|
||||
Field[] keys =
|
||||
parentChildAdapter.getParentChildKeys(-key, ParentChildDBAdapter.CHILD_ID_COL);
|
||||
return keys.length;
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -154,11 +157,11 @@ class FragmentDB extends DatabaseObject implements ProgramFragment {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkDeleted();
|
||||
String oldComments = record.getString(TreeManager.FRAGMENT_COMMENTS_COL);
|
||||
String oldComments = record.getString(FragmentDBAdapter.FRAGMENT_COMMENTS_COL);
|
||||
if (oldComments == null || !oldComments.equals(comment)) {
|
||||
record.setString(TreeManager.FRAGMENT_COMMENTS_COL, comment);
|
||||
record.setString(FragmentDBAdapter.FRAGMENT_COMMENTS_COL, comment);
|
||||
try {
|
||||
adapter.updateFragmentRecord(record);
|
||||
fragmentAdapter.updateFragmentRecord(record);
|
||||
moduleMgr.commentsChanged(oldComments, this);
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -177,19 +180,19 @@ class FragmentDB extends DatabaseObject implements ProgramFragment {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
DBRecord r = adapter.getFragmentRecord(name);
|
||||
DBRecord r = fragmentAdapter.getFragmentRecord(name);
|
||||
if (r != null) {
|
||||
if (key != r.getKey()) {
|
||||
throw new DuplicateNameException(name + " already exists");
|
||||
}
|
||||
return; // no changes
|
||||
}
|
||||
if (adapter.getModuleRecord(name) != null) {
|
||||
if (fragmentAdapter.getFragmentRecord(name) != null) {
|
||||
throw new DuplicateNameException(name + " already exists");
|
||||
}
|
||||
String oldName = record.getString(TreeManager.FRAGMENT_NAME_COL);
|
||||
record.setString(TreeManager.FRAGMENT_NAME_COL, name);
|
||||
adapter.updateFragmentRecord(record);
|
||||
String oldName = record.getString(FragmentDBAdapter.FRAGMENT_NAME_COL);
|
||||
record.setString(FragmentDBAdapter.FRAGMENT_NAME_COL, name);
|
||||
fragmentAdapter.updateFragmentRecord(record);
|
||||
moduleMgr.nameChanged(oldName, this);
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -456,16 +459,17 @@ class FragmentDB extends DatabaseObject implements ProgramFragment {
|
||||
}
|
||||
|
||||
void addRange(AddressRange range) {
|
||||
addrSet = addrSet.union(new AddressSet(range));
|
||||
addrSet.add(range);
|
||||
}
|
||||
|
||||
void removeRange(AddressRange range) {
|
||||
addrSet = addrSet.subtract(new AddressSet(range));
|
||||
addrSet.delete(range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return addrSet.toString();
|
||||
String name = record.getString(FragmentDBAdapter.FRAGMENT_NAME_COL);
|
||||
return name + ": " + addrSet.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,58 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
abstract class FragmentDBAdapter {
|
||||
|
||||
private static final String FRAGMENT_TABLE_NAME = "Fragment Table";
|
||||
|
||||
static final int FRAGMENT_NAME_COL = FragmentDBAdapterV0.V0_FRAGMENT_NAME_COL;
|
||||
static final int FRAGMENT_COMMENTS_COL = FragmentDBAdapterV0.V0_FRAGMENT_COMMENTS_COL;
|
||||
|
||||
/**
|
||||
* Gets an adapter for working with the program tree fragment database table.
|
||||
* @param handle handle to the database to be accessed.
|
||||
* @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE).
|
||||
* @param treeID associated program tree ID
|
||||
* @return fragment table adapter
|
||||
* @throws VersionException if the database handle's version doesn't match the expected version.
|
||||
* @throws IOException if there is a problem accessing the database.
|
||||
*/
|
||||
static FragmentDBAdapter getAdapter(DBHandle handle, int openMode, long treeID)
|
||||
throws VersionException, IOException {
|
||||
return new FragmentDBAdapterV0(handle, openMode == DBConstants.CREATE, treeID);
|
||||
}
|
||||
|
||||
static final String getTableName(long treeID) {
|
||||
return FRAGMENT_TABLE_NAME + treeID;
|
||||
}
|
||||
|
||||
abstract DBRecord createFragmentRecord(long parentModuleID, String name) throws IOException;
|
||||
|
||||
abstract DBRecord getFragmentRecord(long key) throws IOException;
|
||||
|
||||
abstract DBRecord getFragmentRecord(String name) throws IOException;
|
||||
|
||||
abstract void updateFragmentRecord(DBRecord record) throws IOException;
|
||||
|
||||
abstract boolean removeFragmentRecord(long childID) throws IOException;
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
class FragmentDBAdapterV0 extends FragmentDBAdapter {
|
||||
|
||||
static final int V0_VERSION = 0;
|
||||
|
||||
static final int V0_FRAGMENT_NAME_COL = 0;
|
||||
static final int V0_FRAGMENT_COMMENTS_COL = 1;
|
||||
|
||||
static final Schema V0_FRAGMENT_SCHEMA =
|
||||
new Schema(V0_VERSION, "Key", new Field[] { StringField.INSTANCE, StringField.INSTANCE },
|
||||
new String[] { "Name", "Comments" });
|
||||
|
||||
private Table fragmentTable;
|
||||
|
||||
/**
|
||||
* Gets a version 0 adapter for the program tree fragment database table.
|
||||
* @param handle handle to the database containing the table.
|
||||
* @param create true if this constructor should create the table.
|
||||
* @param treeID associated program tree ID
|
||||
* @throws VersionException if the the table's version does not match the expected version
|
||||
* for this adapter.
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
public FragmentDBAdapterV0(DBHandle handle, boolean create, long treeID)
|
||||
throws VersionException, IOException {
|
||||
|
||||
String tableName = getTableName(treeID);
|
||||
|
||||
if (create) {
|
||||
fragmentTable = handle.createTable(tableName, V0_FRAGMENT_SCHEMA,
|
||||
new int[] { V0_FRAGMENT_NAME_COL });
|
||||
}
|
||||
else {
|
||||
fragmentTable = handle.getTable(tableName);
|
||||
if (fragmentTable == null) {
|
||||
throw new VersionException("Missing Table: " + tableName);
|
||||
}
|
||||
int version = fragmentTable.getSchema().getVersion();
|
||||
if (version != V0_VERSION) {
|
||||
throw new VersionException(VersionException.NEWER_VERSION, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord createFragmentRecord(long parentFragmentID, String name) throws IOException {
|
||||
long key = fragmentTable.getKey();
|
||||
if (key == 0) {
|
||||
key = 1;
|
||||
}
|
||||
DBRecord record = V0_FRAGMENT_SCHEMA.createRecord(key);
|
||||
record.setString(V0_FRAGMENT_NAME_COL, name);
|
||||
fragmentTable.putRecord(record);
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord getFragmentRecord(long key) throws IOException {
|
||||
return fragmentTable.getRecord(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord getFragmentRecord(String name) throws IOException {
|
||||
Field[] keys = fragmentTable.findRecords(new StringField(name), V0_FRAGMENT_NAME_COL);
|
||||
if (keys.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (keys.length > 1) {
|
||||
throw new AssertException("Found " + keys.length + " fragments named " + name);
|
||||
}
|
||||
return fragmentTable.getRecord(keys[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateFragmentRecord(DBRecord record) throws IOException {
|
||||
fragmentTable.putRecord(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean removeFragmentRecord(long childID) throws IOException {
|
||||
return fragmentTable.deleteRecord(childID);
|
||||
}
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.Field;
|
||||
import db.DBRecord;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Adapter to access the module, fragment, and parent/child database tables.
|
||||
*
|
||||
*
|
||||
*/
|
||||
interface GroupDBAdapter {
|
||||
|
||||
/**
|
||||
* Create the root module for a tree; the module ID for the root is 0.
|
||||
* @param name the name of the program.
|
||||
* @return record for the root module; should never be null
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord createRootModule(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Create a new module.
|
||||
* @param parentModuleID ID of parent module
|
||||
* @param name module name
|
||||
* @return record for the module
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
* @throws DuplicateNameException if a module or fragment already exists
|
||||
* having the given name
|
||||
*/
|
||||
DBRecord createModule(long parentModuleID, String name)
|
||||
throws IOException, DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Get the record for the module with the given key.
|
||||
* @param module ID
|
||||
* @return record for the module; null if the record was not found
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord getModuleRecord(long moduleID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the module record with the given name.
|
||||
* @param name module name
|
||||
* @return module record; null if no module exists with the given name
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord getModuleRecord(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Update the module table with the given record.
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
void updateModuleRecord(DBRecord record) throws IOException;
|
||||
|
||||
/**
|
||||
* Create a new fragment
|
||||
* @param parentModuleID ID of parent module
|
||||
* @param name fragment name
|
||||
* @return record for the fragment
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
* @throws DuplicateNameException if a module or fragment already exists
|
||||
* having the given name
|
||||
*/
|
||||
DBRecord createFragment(long parentModuleID, String name)
|
||||
throws IOException, DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Get the record for the fragment with the given key.
|
||||
* @param fragID
|
||||
* @return
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord getFragmentRecord(long fragID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the fragment record with the given name.
|
||||
* @param name fragment name
|
||||
* @return fragment record; null if no fragment exists with the given
|
||||
* name
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord getFragmentRecord(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Update the fragment table with the given record.
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
void updateFragmentRecord(DBRecord record) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the record in the Parent/Child table.
|
||||
* @param parentID module ID of the parent
|
||||
* @param childID childID
|
||||
* @return record; null if the record was not found
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord getParentChildRecord(long parentID, long childID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the keys in the Parent/Child table that are indexed on the given
|
||||
* indexed column and have the value of ID.
|
||||
* @param ID value of indexed column
|
||||
* @param indexedCol column that is indexed in the table to do the lookup
|
||||
* @return zero-length array if no records were found
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
Field[] getParentChildKeys(long ID, int indexedCol) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the Parent/Child record with the given key.
|
||||
* @return record or null if the record does not exist
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord getParentChildRecord(long key) throws IOException;
|
||||
|
||||
/**
|
||||
* Create a new Parent/Child record.
|
||||
* @param parentID module ID of the parent
|
||||
* @param childID ID for the child
|
||||
* @return record or nul if the record does not exist
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord addParentChildRecord(long parentID, long childID) throws IOException;
|
||||
|
||||
/**
|
||||
* Remove the record with the given key in the Parent/Child table.
|
||||
* @return true if the record was deleted
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
boolean removeParentChildRecord(long key) throws IOException;
|
||||
|
||||
/**
|
||||
* Update the Parent/Child table with the given record.
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
void updateParentChildRecord(DBRecord record) throws IOException;
|
||||
|
||||
/**
|
||||
* Remove the fragment record.
|
||||
* @param childID
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
boolean removeFragmentRecord(long childID) throws IOException;
|
||||
|
||||
/**
|
||||
* Remove the module record.
|
||||
* @param childID
|
||||
* @return true if the record was removed
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
boolean removeModuleRecord(long childID) throws IOException;
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* Version 0 implementation for the GroupDBAdapter.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class GroupDBAdapterV0 implements GroupDBAdapter {
|
||||
|
||||
private Table moduleTable;
|
||||
private Table fragmentTable;
|
||||
private Table parentChildTable;
|
||||
|
||||
GroupDBAdapterV0(DBHandle handle, String moduleTableName, String fragTableName,
|
||||
String parentChildTableName) throws VersionException {
|
||||
|
||||
moduleTable = handle.getTable(moduleTableName);
|
||||
fragmentTable = handle.getTable(fragTableName);
|
||||
parentChildTable = handle.getTable(parentChildTableName);
|
||||
testVersion(moduleTable, 0, moduleTableName);
|
||||
testVersion(fragmentTable, 0, fragTableName);
|
||||
testVersion(parentChildTable, 0, parentChildTableName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord createModule(long parentModuleID, String name)
|
||||
throws IOException, DuplicateNameException {
|
||||
|
||||
if (getModuleRecord(name) != null || getFragmentRecord(name) != null) {
|
||||
throw new DuplicateNameException(name + " already exists");
|
||||
}
|
||||
DBRecord record = TreeManager.MODULE_SCHEMA.createRecord(moduleTable.getKey());
|
||||
record.setString(TreeManager.MODULE_NAME_COL, name);
|
||||
moduleTable.putRecord(record);
|
||||
|
||||
// insert record for parent/child table
|
||||
DBRecord pcRec = TreeManager.PARENT_CHILD_SCHEMA.createRecord(parentChildTable.getKey());
|
||||
pcRec.setLongValue(TreeManager.PARENT_ID_COL, parentModuleID);
|
||||
pcRec.setLongValue(TreeManager.CHILD_ID_COL, record.getKey());
|
||||
parentChildTable.putRecord(pcRec);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord createFragment(long parentModuleID, String name)
|
||||
throws IOException, DuplicateNameException {
|
||||
|
||||
if (getFragmentRecord(name) != null || getModuleRecord(name) != null) {
|
||||
throw new DuplicateNameException(name + " already exists");
|
||||
}
|
||||
long key = fragmentTable.getKey();
|
||||
if (key == 0) {
|
||||
key = 1;
|
||||
}
|
||||
DBRecord record = TreeManager.FRAGMENT_SCHEMA.createRecord(key);
|
||||
record.setString(TreeManager.FRAGMENT_NAME_COL, name);
|
||||
fragmentTable.putRecord(record);
|
||||
|
||||
DBRecord pcRec = TreeManager.PARENT_CHILD_SCHEMA.createRecord(parentChildTable.getKey());
|
||||
pcRec.setLongValue(TreeManager.PARENT_ID_COL, parentModuleID);
|
||||
// negative value to indicate fragment
|
||||
pcRec.setLongValue(TreeManager.CHILD_ID_COL, -key);
|
||||
parentChildTable.putRecord(pcRec);
|
||||
|
||||
return record;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord getFragmentRecord(long key) throws IOException {
|
||||
return fragmentTable.getRecord(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord getModuleRecord(long key) throws IOException {
|
||||
return moduleTable.getRecord(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.module.GroupDBAdapter#getParentChildRecord(long, long)
|
||||
* @param childID negative value if child is a fragment
|
||||
*/
|
||||
@Override
|
||||
public DBRecord getParentChildRecord(long parentID, long childID) throws IOException {
|
||||
Field[] keys =
|
||||
parentChildTable.findRecords(new LongField(parentID), TreeManager.PARENT_ID_COL);
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
DBRecord pcRec = parentChildTable.getRecord(keys[i]);
|
||||
if (pcRec.getLongValue(TreeManager.CHILD_ID_COL) == childID) {
|
||||
return pcRec;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord addParentChildRecord(long moduleID, long childID) throws IOException {
|
||||
|
||||
DBRecord pcRec = TreeManager.PARENT_CHILD_SCHEMA.createRecord(parentChildTable.getKey());
|
||||
pcRec.setLongValue(TreeManager.PARENT_ID_COL, moduleID);
|
||||
pcRec.setLongValue(TreeManager.CHILD_ID_COL, childID);
|
||||
parentChildTable.putRecord(pcRec);
|
||||
return pcRec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeParentChildRecord(long key) throws IOException {
|
||||
return parentChildTable.deleteRecord(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field[] getParentChildKeys(long parentID, int indexedCol) throws IOException {
|
||||
return parentChildTable.findRecords(new LongField(parentID), indexedCol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord getFragmentRecord(String name) throws IOException {
|
||||
Field[] keys =
|
||||
fragmentTable.findRecords(new StringField(name), TreeManager.FRAGMENT_NAME_COL);
|
||||
if (keys.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (keys.length > 1) {
|
||||
throw new AssertException("Found " + keys.length + " fragments named " + name);
|
||||
}
|
||||
return fragmentTable.getRecord(keys[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord getModuleRecord(String name) throws IOException {
|
||||
Field[] keys = moduleTable.findRecords(new StringField(name), TreeManager.MODULE_NAME_COL);
|
||||
if (keys.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (keys.length > 1) {
|
||||
throw new AssertException("Found " + keys.length + " modules named " + name);
|
||||
}
|
||||
return moduleTable.getRecord(keys[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord getParentChildRecord(long key) throws IOException {
|
||||
return parentChildTable.getRecord(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateModuleRecord(DBRecord record) throws IOException {
|
||||
moduleTable.putRecord(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFragmentRecord(DBRecord record) throws IOException {
|
||||
fragmentTable.putRecord(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateParentChildRecord(DBRecord record) throws IOException {
|
||||
parentChildTable.putRecord(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord createRootModule(String name) throws IOException {
|
||||
DBRecord record = TreeManager.MODULE_SCHEMA.createRecord(0);
|
||||
record.setString(TreeManager.MODULE_NAME_COL, name);
|
||||
moduleTable.putRecord(record);
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFragmentRecord(long childID) throws IOException {
|
||||
return fragmentTable.deleteRecord(childID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeModuleRecord(long childID) throws IOException {
|
||||
return moduleTable.deleteRecord(childID);
|
||||
}
|
||||
|
||||
private void testVersion(Table table, int expectedVersion, String name)
|
||||
throws VersionException {
|
||||
|
||||
if (table == null) {
|
||||
throw new VersionException(name + " not found");
|
||||
}
|
||||
int versionNumber = table.getSchema().getVersion();
|
||||
if (versionNumber != expectedVersion) {
|
||||
throw new VersionException(
|
||||
name + ": Expected Version " + expectedVersion + ", got " + versionNumber);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -18,8 +18,8 @@ package ghidra.program.database.module;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import db.Field;
|
||||
import db.DBRecord;
|
||||
import db.Field;
|
||||
import ghidra.program.database.DBObjectCache;
|
||||
import ghidra.program.database.DatabaseObject;
|
||||
import ghidra.program.model.address.*;
|
||||
@ -37,7 +37,10 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
|
||||
private DBRecord record;
|
||||
private ModuleManager moduleMgr;
|
||||
private GroupDBAdapter adapter;
|
||||
private ModuleDBAdapter moduleAdapter;
|
||||
private FragmentDBAdapter fragmentAdapter;
|
||||
private ParentChildDBAdapter parentChildAdapter;
|
||||
|
||||
private int childCount; // cache the count so we don't have to access
|
||||
// database records
|
||||
private Lock lock;
|
||||
@ -48,30 +51,27 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
* @param moduleMgr module manager
|
||||
* @param cache ModuleDB cache
|
||||
* @param record database record for this module
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
ModuleDB(ModuleManager moduleMgr, DBObjectCache<ModuleDB> cache, DBRecord record) {
|
||||
ModuleDB(ModuleManager moduleMgr, DBObjectCache<ModuleDB> cache, DBRecord record)
|
||||
throws IOException {
|
||||
super(cache, record.getKey());
|
||||
this.moduleMgr = moduleMgr;
|
||||
this.record = record;
|
||||
adapter = moduleMgr.getGroupDBAdapter();
|
||||
updateChildCount();
|
||||
childCount = record.getIntValue(ModuleDBAdapter.MODULE_CHILD_COUNT_COL);
|
||||
moduleAdapter = moduleMgr.getModuleAdapter();
|
||||
fragmentAdapter = moduleMgr.getFragmentAdapter();
|
||||
parentChildAdapter = moduleMgr.getParentChildAdapter();
|
||||
lock = moduleMgr.getLock();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean refresh() {
|
||||
try {
|
||||
DBRecord rec = adapter.getModuleRecord(key);
|
||||
DBRecord rec = moduleAdapter.getModuleRecord(key);
|
||||
if (rec != null) {
|
||||
record = rec;
|
||||
childCount = 0;
|
||||
try {
|
||||
Field[] keys = adapter.getParentChildKeys(key, TreeManager.PARENT_ID_COL);
|
||||
childCount = keys.length;
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
}
|
||||
childCount = rec.getIntValue(ModuleDBAdapter.MODULE_CHILD_COUNT_COL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -89,15 +89,15 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
FragmentDB frag = (FragmentDB) fragment;
|
||||
long fragID = frag.getKey();
|
||||
// add a row to the parent/child table
|
||||
DBRecord parentChildRecord = adapter.getParentChildRecord(key, -fragID);
|
||||
DBRecord parentChildRecord = parentChildAdapter.getParentChildRecord(key, -fragID);
|
||||
if (parentChildRecord != null) {
|
||||
throw new DuplicateGroupException(
|
||||
frag.getName() + " already exists a child of " + getName());
|
||||
}
|
||||
|
||||
DBRecord pcRec = adapter.addParentChildRecord(key, -fragID);
|
||||
updateChildCount();
|
||||
updateOrderField(pcRec);
|
||||
DBRecord pcRec = parentChildAdapter.addParentChildRecord(key, -fragID);
|
||||
updateChildCount(1);
|
||||
updateOrderField(pcRec, childCount - 1);
|
||||
moduleMgr.fragmentAdded(key, frag);
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -118,7 +118,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
ModuleDB moduleDB = (ModuleDB) module;
|
||||
long moduleID = moduleDB.getKey();
|
||||
|
||||
DBRecord parentChildRecord = adapter.getParentChildRecord(key, moduleID);
|
||||
DBRecord parentChildRecord = parentChildAdapter.getParentChildRecord(key, moduleID);
|
||||
if (parentChildRecord != null) {
|
||||
throw new DuplicateGroupException(
|
||||
module.getName() + " already exists a child of " + getName());
|
||||
@ -128,9 +128,9 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
getName() + " is already a descendant of " + module.getName());
|
||||
}
|
||||
|
||||
DBRecord pcRec = adapter.addParentChildRecord(key, moduleID);
|
||||
updateChildCount();
|
||||
updateOrderField(pcRec);
|
||||
DBRecord pcRec = parentChildAdapter.addParentChildRecord(key, moduleID);
|
||||
updateChildCount(1);
|
||||
updateOrderField(pcRec, childCount - 1);
|
||||
moduleMgr.moduleAdded(key, module);
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -171,11 +171,16 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkDeleted();
|
||||
DBRecord parentChildRecord = adapter.createFragment(key, fragmentName);
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(parentChildRecord);
|
||||
DBRecord pcRec = adapter.getParentChildRecord(key, -frag.getKey());
|
||||
updateChildCount();
|
||||
updateOrderField(pcRec);
|
||||
if (moduleAdapter.getModuleRecord(fragmentName) != null ||
|
||||
fragmentAdapter.getFragmentRecord(fragmentName) != null) {
|
||||
throw new DuplicateNameException(fragmentName + " already exists");
|
||||
}
|
||||
DBRecord fragmentRecord = fragmentAdapter.createFragmentRecord(key, fragmentName);
|
||||
DBRecord pcRec = parentChildAdapter.addParentChildRecord(key, -fragmentRecord.getKey()); // negative value to indicates fragment
|
||||
updateChildCount(1);
|
||||
updateOrderField(pcRec, childCount - 1);
|
||||
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(fragmentRecord);
|
||||
moduleMgr.fragmentAdded(key, frag);
|
||||
return frag;
|
||||
}
|
||||
@ -194,11 +199,17 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkDeleted();
|
||||
DBRecord moduleRecord = adapter.createModule(key, moduleName);
|
||||
if (moduleAdapter.getModuleRecord(moduleName) != null ||
|
||||
fragmentAdapter.getFragmentRecord(moduleName) != null) {
|
||||
throw new DuplicateNameException(moduleName + " already exists");
|
||||
}
|
||||
|
||||
DBRecord moduleRecord = moduleAdapter.createModuleRecord(key, moduleName);
|
||||
DBRecord pcRec = parentChildAdapter.addParentChildRecord(key, moduleRecord.getKey());
|
||||
|
||||
ModuleDB moduleDB = moduleMgr.getModuleDB(moduleRecord);
|
||||
DBRecord pcRec = adapter.getParentChildRecord(key, moduleDB.key);
|
||||
updateChildCount();
|
||||
updateOrderField(pcRec);
|
||||
updateChildCount(1);
|
||||
updateOrderField(pcRec, childCount - 1);
|
||||
moduleMgr.moduleAdded(key, moduleDB);
|
||||
return moduleDB;
|
||||
}
|
||||
@ -218,9 +229,14 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
checkIsValid();
|
||||
List<DBRecord> list = getParentChildRecords();
|
||||
Group[] kids = new Group[list.size()];
|
||||
if (kids.length != childCount) {
|
||||
// data inconsistency
|
||||
throw new IOException("Inconsistent module child count (" + kids.length + " vs. " +
|
||||
childCount + "): " + getName());
|
||||
}
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DBRecord rec = list.get(i);
|
||||
long childID = rec.getLongValue(TreeManager.CHILD_ID_COL);
|
||||
long childID = rec.getLongValue(ParentChildDBAdapter.CHILD_ID_COL);
|
||||
if (childID < 0) {
|
||||
kids[i] = moduleMgr.getFragmentDB(-childID);
|
||||
}
|
||||
@ -228,7 +244,6 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
kids[i] = moduleMgr.getModuleDB(childID);
|
||||
}
|
||||
}
|
||||
childCount = kids.length;
|
||||
return kids;
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -245,7 +260,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
return record.getString(TreeManager.MODULE_COMMENTS_COL);
|
||||
return record.getString(ModuleDBAdapter.MODULE_COMMENTS_COL);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@ -259,9 +274,13 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
checkIsValid();
|
||||
return findFirstAddress(this);
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -269,20 +288,19 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
DBRecord fragmentRecord = adapter.getFragmentRecord(name);
|
||||
DBRecord fragmentRecord = fragmentAdapter.getFragmentRecord(name);
|
||||
DBRecord pcRec = null;
|
||||
if (fragmentRecord != null) {
|
||||
long fragID = fragmentRecord.getKey();
|
||||
pcRec = adapter.getParentChildRecord(key, -fragID);
|
||||
pcRec = parentChildAdapter.getParentChildRecord(key, -fragmentRecord.getKey());
|
||||
}
|
||||
else {
|
||||
fragmentRecord = adapter.getModuleRecord(name);
|
||||
if (fragmentRecord != null) {
|
||||
pcRec = adapter.getParentChildRecord(key, fragmentRecord.getKey());
|
||||
DBRecord moduleRecord = moduleAdapter.getModuleRecord(name);
|
||||
if (moduleRecord != null) {
|
||||
pcRec = parentChildAdapter.getParentChildRecord(key, moduleRecord.getKey());
|
||||
}
|
||||
}
|
||||
if (pcRec != null) {
|
||||
return pcRec.getIntValue(TreeManager.ORDER_COL);
|
||||
return pcRec.getIntValue(ParentChildDBAdapter.ORDER_COL);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -301,9 +319,13 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
checkIsValid();
|
||||
return findLastAddress(this);
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -313,9 +335,13 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
checkIsValid();
|
||||
return findMaxAddress(this, null);
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -325,9 +351,13 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
checkIsValid();
|
||||
return findMinAddress(this, null);
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -363,13 +393,18 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
if (!(fragment instanceof FragmentDB)) {
|
||||
return false;
|
||||
}
|
||||
FragmentDB frag = (FragmentDB) fragment;
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
FragmentDB frag = (FragmentDB) fragment;
|
||||
return moduleMgr.isDescendant(-frag.getKey(), key);
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -397,19 +432,20 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
boolean foundName = false;
|
||||
Group group = null;
|
||||
|
||||
// TODO: this could be slow and may need monitor
|
||||
List<DBRecord> list = getParentChildRecords();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DBRecord rec = list.get(i);
|
||||
long childID = rec.getLongValue(TreeManager.CHILD_ID_COL);
|
||||
long childID = rec.getLongValue(ParentChildDBAdapter.CHILD_ID_COL);
|
||||
String childName = null;
|
||||
DBRecord childRec = null;
|
||||
if (childID < 0) {
|
||||
childRec = adapter.getFragmentRecord(-childID);
|
||||
childName = childRec.getString(TreeManager.FRAGMENT_NAME_COL);
|
||||
childRec = fragmentAdapter.getFragmentRecord(-childID);
|
||||
childName = childRec.getString(FragmentDBAdapter.FRAGMENT_NAME_COL);
|
||||
}
|
||||
else {
|
||||
childRec = adapter.getModuleRecord(childID);
|
||||
childName = childRec.getString(TreeManager.MODULE_NAME_COL);
|
||||
childRec = moduleAdapter.getModuleRecord(childID);
|
||||
childName = childRec.getString(ModuleDBAdapter.MODULE_NAME_COL);
|
||||
}
|
||||
if (childName.equals(name)) {
|
||||
foundName = true;
|
||||
@ -445,18 +481,19 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkDeleted();
|
||||
DBRecord rec = adapter.getFragmentRecord(name);
|
||||
DBRecord rec = fragmentAdapter.getFragmentRecord(name);
|
||||
boolean deleteChild = false;
|
||||
|
||||
if (rec != null) {
|
||||
// make sure that I am a parent of this child
|
||||
long childID = rec.getKey();
|
||||
DBRecord pcRec = adapter.getParentChildRecord(key, -childID);
|
||||
DBRecord pcRec = parentChildAdapter.getParentChildRecord(key, -childID);
|
||||
if (pcRec == null) {
|
||||
// check for module record
|
||||
return removeModuleRecord(name);
|
||||
}
|
||||
Field[] keys = adapter.getParentChildKeys(-childID, TreeManager.CHILD_ID_COL);
|
||||
Field[] keys = parentChildAdapter.getParentChildKeys(-childID,
|
||||
ParentChildDBAdapter.CHILD_ID_COL);
|
||||
if (keys.length == 1) {
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(childID);
|
||||
if (!frag.isEmpty()) {
|
||||
@ -479,18 +516,19 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
|
||||
private boolean removeModuleRecord(String name) throws IOException, NotEmptyException {
|
||||
|
||||
DBRecord rec = adapter.getModuleRecord(name);
|
||||
DBRecord rec = moduleAdapter.getModuleRecord(name);
|
||||
if (rec == null) {
|
||||
return false;
|
||||
}
|
||||
boolean deleteChild = false;
|
||||
long childID = rec.getKey();
|
||||
DBRecord pcRec = adapter.getParentChildRecord(key, childID);
|
||||
DBRecord pcRec = parentChildAdapter.getParentChildRecord(key, childID);
|
||||
if (pcRec == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Field[] keys = adapter.getParentChildKeys(childID, TreeManager.CHILD_ID_COL);
|
||||
Field[] keys =
|
||||
parentChildAdapter.getParentChildKeys(childID, ParentChildDBAdapter.CHILD_ID_COL);
|
||||
if (keys.length == 1) {
|
||||
ProgramModule module = moduleMgr.getModuleDB(childID);
|
||||
if (module.getNumChildren() > 0) {
|
||||
@ -527,12 +565,16 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
}
|
||||
ModuleDB oldModuleDB = (ModuleDB) oldParent;
|
||||
|
||||
DBRecord oldPcRec = adapter.getParentChildRecord(oldModuleDB.key, childID);
|
||||
adapter.removeParentChildRecord(oldPcRec.getKey());
|
||||
DBRecord newPcRec = adapter.addParentChildRecord(key, childID);
|
||||
++childCount;
|
||||
updateOrderField(newPcRec);
|
||||
DBRecord oldPcRec = parentChildAdapter.getParentChildRecord(oldModuleDB.key, childID);
|
||||
parentChildAdapter.removeParentChildRecord(oldPcRec.getKey());
|
||||
oldModuleDB.updateChildCount(-1);
|
||||
|
||||
DBRecord newPcRec = parentChildAdapter.addParentChildRecord(key, childID);
|
||||
updateChildCount(1);
|
||||
updateOrderField(newPcRec, childCount - 1);
|
||||
|
||||
oldModuleDB.resetChildOrder();
|
||||
|
||||
moduleMgr.childReparented(group, oldParent.getName(), getName());
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -558,7 +600,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
return record.getString(TreeManager.MODULE_NAME_COL);
|
||||
return record.getString(ModuleDBAdapter.MODULE_NAME_COL);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@ -570,7 +612,8 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
Field[] keys = adapter.getParentChildKeys(key, TreeManager.CHILD_ID_COL);
|
||||
Field[] keys =
|
||||
parentChildAdapter.getParentChildKeys(key, ParentChildDBAdapter.CHILD_ID_COL);
|
||||
return keys.length;
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -602,11 +645,11 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkDeleted();
|
||||
String oldComments = record.getString(TreeManager.MODULE_COMMENTS_COL);
|
||||
String oldComments = record.getString(ModuleDBAdapter.MODULE_COMMENTS_COL);
|
||||
if (oldComments == null || !oldComments.equals(comment)) {
|
||||
record.setString(TreeManager.MODULE_COMMENTS_COL, comment);
|
||||
record.setString(ModuleDBAdapter.MODULE_COMMENTS_COL, comment);
|
||||
try {
|
||||
adapter.updateModuleRecord(record);
|
||||
moduleAdapter.updateModuleRecord(record);
|
||||
moduleMgr.commentsChanged(oldComments, this);
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -628,19 +671,19 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
moduleMgr.getProgram().setName(name);
|
||||
return;
|
||||
}
|
||||
DBRecord r = adapter.getModuleRecord(name);
|
||||
DBRecord r = moduleAdapter.getModuleRecord(name);
|
||||
if (r != null) {
|
||||
if (key != r.getKey()) {
|
||||
throw new DuplicateNameException(name + " already exists");
|
||||
}
|
||||
return; // no changes
|
||||
}
|
||||
if (adapter.getFragmentRecord(name) != null) {
|
||||
if (fragmentAdapter.getFragmentRecord(name) != null) {
|
||||
throw new DuplicateNameException(name + " already exists");
|
||||
}
|
||||
String oldName = record.getString(TreeManager.MODULE_NAME_COL);
|
||||
record.setString(TreeManager.MODULE_NAME_COL, name);
|
||||
adapter.updateModuleRecord(record);
|
||||
String oldName = record.getString(ModuleDBAdapter.MODULE_NAME_COL);
|
||||
record.setString(ModuleDBAdapter.MODULE_NAME_COL, name);
|
||||
moduleAdapter.updateModuleRecord(record);
|
||||
moduleMgr.nameChanged(oldName, this);
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -659,7 +702,7 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
|
||||
private boolean contains(long childID) {
|
||||
try {
|
||||
DBRecord rec = adapter.getParentChildRecord(key, childID);
|
||||
DBRecord rec = parentChildAdapter.getParentChildRecord(key, childID);
|
||||
return rec != null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -668,24 +711,27 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean removeChild(long childID, DBRecord pcRec, boolean isFragment, boolean deleteChild)
|
||||
private boolean removeChild(long childID, DBRecord pcRec, boolean isFragment,
|
||||
boolean deleteChild)
|
||||
throws IOException {
|
||||
|
||||
adapter.removeParentChildRecord(pcRec.getKey());
|
||||
parentChildAdapter.removeParentChildRecord(pcRec.getKey());
|
||||
updateChildCount(-1);
|
||||
|
||||
String name = null;
|
||||
boolean success = true;
|
||||
if (isFragment) {
|
||||
DBRecord fragRec = adapter.getFragmentRecord(childID);
|
||||
name = fragRec.getString(TreeManager.FRAGMENT_NAME_COL);
|
||||
DBRecord fragRec = fragmentAdapter.getFragmentRecord(childID);
|
||||
name = fragRec.getString(FragmentDBAdapter.FRAGMENT_NAME_COL);
|
||||
if (deleteChild) {
|
||||
success = adapter.removeFragmentRecord(childID);
|
||||
success = fragmentAdapter.removeFragmentRecord(childID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBRecord mrec = adapter.getModuleRecord(childID);
|
||||
name = mrec.getString(TreeManager.MODULE_NAME_COL);
|
||||
DBRecord mrec = moduleAdapter.getModuleRecord(childID);
|
||||
name = mrec.getString(ModuleDBAdapter.MODULE_NAME_COL);
|
||||
if (deleteChild) {
|
||||
success = adapter.removeModuleRecord(childID);
|
||||
success = moduleAdapter.removeModuleRecord(childID);
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
@ -700,11 +746,12 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
* Get sorted list based on child order column.
|
||||
*/
|
||||
private List<DBRecord> getParentChildRecords() throws IOException {
|
||||
Field[] keys = adapter.getParentChildKeys(key, TreeManager.PARENT_ID_COL);
|
||||
Field[] keys =
|
||||
parentChildAdapter.getParentChildKeys(key, ParentChildDBAdapter.PARENT_ID_COL);
|
||||
List<DBRecord> list = new ArrayList<DBRecord>();
|
||||
Comparator<DBRecord> c = new ParentChildRecordComparator();
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
DBRecord rec = adapter.getParentChildRecord(keys[i].getLongValue());
|
||||
DBRecord rec = parentChildAdapter.getParentChildRecord(keys[i].getLongValue());
|
||||
int index = Collections.binarySearch(list, rec, c);
|
||||
if (index < 0) {
|
||||
index = -index - 1;
|
||||
@ -720,167 +767,132 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
private void updateChildOrder(List<DBRecord> list) throws IOException {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DBRecord pcRec = list.get(i);
|
||||
pcRec.setIntValue(TreeManager.ORDER_COL, i);
|
||||
adapter.updateParentChildRecord(pcRec);
|
||||
if (i != pcRec.getIntValue(ParentChildDBAdapter.ORDER_COL)) {
|
||||
pcRec.setIntValue(ParentChildDBAdapter.ORDER_COL, i);
|
||||
parentChildAdapter.updateParentChildRecord(pcRec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resetChildOrder() throws IOException {
|
||||
List<DBRecord> list = getParentChildRecords();
|
||||
updateChildOrder(list);
|
||||
updateChildCount();
|
||||
}
|
||||
|
||||
private void updateOrderField(DBRecord pcRec) throws IOException {
|
||||
int orderValue = getNumChildren() - 1;
|
||||
if (orderValue < 0) {
|
||||
orderValue = 0;
|
||||
}
|
||||
pcRec.setIntValue(TreeManager.ORDER_COL, orderValue);
|
||||
adapter.updateParentChildRecord(pcRec);
|
||||
private void updateOrderField(DBRecord pcRec, int orderValue) throws IOException {
|
||||
pcRec.setIntValue(ParentChildDBAdapter.ORDER_COL, orderValue);
|
||||
parentChildAdapter.updateParentChildRecord(pcRec);
|
||||
}
|
||||
|
||||
private Address findFirstAddress(ModuleDB module) {
|
||||
try {
|
||||
List<DBRecord> list = module.getParentChildRecords();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DBRecord rec = list.get(i);
|
||||
long childID = rec.getLongValue(TreeManager.CHILD_ID_COL);
|
||||
if (childID < 0) {
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(-childID);
|
||||
if (!frag.isEmpty()) {
|
||||
return frag.getMinAddress();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ModuleDB m = moduleMgr.getModuleDB(childID);
|
||||
Address addr = findFirstAddress(m);
|
||||
if (addr != null) {
|
||||
return addr;
|
||||
}
|
||||
private Address findFirstAddress(ModuleDB module) throws IOException {
|
||||
List<DBRecord> list = module.getParentChildRecords();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DBRecord rec = list.get(i);
|
||||
long childID = rec.getLongValue(ParentChildDBAdapter.CHILD_ID_COL);
|
||||
if (childID < 0) {
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(-childID);
|
||||
if (!frag.isEmpty()) {
|
||||
return frag.getMinAddress();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ModuleDB m = moduleMgr.getModuleDB(childID);
|
||||
Address addr = findFirstAddress(m);
|
||||
if (addr != null) {
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Address findLastAddress(ModuleDB module) {
|
||||
try {
|
||||
List<DBRecord> list = module.getParentChildRecords();
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
DBRecord rec = list.get(i);
|
||||
long childID = rec.getLongValue(TreeManager.CHILD_ID_COL);
|
||||
if (childID < 0) {
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(-childID);
|
||||
if (!frag.isEmpty()) {
|
||||
return frag.getMaxAddress();
|
||||
}
|
||||
private Address findLastAddress(ModuleDB module) throws IOException {
|
||||
List<DBRecord> list = module.getParentChildRecords();
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
DBRecord rec = list.get(i);
|
||||
long childID = rec.getLongValue(ParentChildDBAdapter.CHILD_ID_COL);
|
||||
if (childID < 0) {
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(-childID);
|
||||
if (!frag.isEmpty()) {
|
||||
return frag.getMaxAddress();
|
||||
}
|
||||
else {
|
||||
ModuleDB m = moduleMgr.getModuleDB(childID);
|
||||
Address addr = findLastAddress(m);
|
||||
if (addr != null) {
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ModuleDB m = moduleMgr.getModuleDB(childID);
|
||||
Address addr = findLastAddress(m);
|
||||
if (addr != null) {
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
private Address findMinAddress(ModuleDB module, Address addr) {
|
||||
private Address findMinAddress(ModuleDB module, Address addr) throws IOException {
|
||||
Address minAddr = addr;
|
||||
|
||||
try {
|
||||
List<DBRecord> list = module.getParentChildRecords();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DBRecord rec = list.get(i);
|
||||
long childID = rec.getLongValue(TreeManager.CHILD_ID_COL);
|
||||
Address childMinAddr = null;
|
||||
if (childID < 0) {
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(-childID);
|
||||
if (!frag.isEmpty()) {
|
||||
childMinAddr = frag.getMinAddress();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ModuleDB m = moduleMgr.getModuleDB(childID);
|
||||
childMinAddr = findMinAddress(m, addr);
|
||||
}
|
||||
if (childMinAddr != null && minAddr == null) {
|
||||
minAddr = childMinAddr;
|
||||
}
|
||||
else if (childMinAddr != null && childMinAddr.compareTo(minAddr) < 0) {
|
||||
minAddr = childMinAddr;
|
||||
List<DBRecord> list = module.getParentChildRecords();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DBRecord rec = list.get(i);
|
||||
long childID = rec.getLongValue(ParentChildDBAdapter.CHILD_ID_COL);
|
||||
Address childMinAddr = null;
|
||||
if (childID < 0) {
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(-childID);
|
||||
if (!frag.isEmpty()) {
|
||||
childMinAddr = frag.getMinAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
else {
|
||||
ModuleDB m = moduleMgr.getModuleDB(childID);
|
||||
childMinAddr = findMinAddress(m, addr);
|
||||
}
|
||||
if (childMinAddr != null && minAddr == null) {
|
||||
minAddr = childMinAddr;
|
||||
}
|
||||
else if (childMinAddr != null && childMinAddr.compareTo(minAddr) < 0) {
|
||||
minAddr = childMinAddr;
|
||||
}
|
||||
}
|
||||
return minAddr;
|
||||
|
||||
}
|
||||
|
||||
private Address findMaxAddress(ModuleDB module, Address addr) {
|
||||
private Address findMaxAddress(ModuleDB module, Address addr) throws IOException {
|
||||
Address maxAddr = addr;
|
||||
|
||||
try {
|
||||
List<DBRecord> list = module.getParentChildRecords();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DBRecord rec = list.get(i);
|
||||
long childID = rec.getLongValue(TreeManager.CHILD_ID_COL);
|
||||
Address childMaxAddr = null;
|
||||
if (childID < 0) {
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(-childID);
|
||||
if (!frag.isEmpty()) {
|
||||
childMaxAddr = frag.getMaxAddress();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ModuleDB m = moduleMgr.getModuleDB(childID);
|
||||
childMaxAddr = findMaxAddress(m, addr);
|
||||
}
|
||||
if (childMaxAddr != null && maxAddr == null) {
|
||||
maxAddr = childMaxAddr;
|
||||
}
|
||||
else if (childMaxAddr != null && childMaxAddr.compareTo(maxAddr) > 0) {
|
||||
maxAddr = childMaxAddr;
|
||||
List<DBRecord> list = module.getParentChildRecords();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
DBRecord rec = list.get(i);
|
||||
long childID = rec.getLongValue(ParentChildDBAdapter.CHILD_ID_COL);
|
||||
Address childMaxAddr = null;
|
||||
if (childID < 0) {
|
||||
FragmentDB frag = moduleMgr.getFragmentDB(-childID);
|
||||
if (!frag.isEmpty()) {
|
||||
childMaxAddr = frag.getMaxAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
else {
|
||||
ModuleDB m = moduleMgr.getModuleDB(childID);
|
||||
childMaxAddr = findMaxAddress(m, addr);
|
||||
}
|
||||
if (childMaxAddr != null && maxAddr == null) {
|
||||
maxAddr = childMaxAddr;
|
||||
}
|
||||
else if (childMaxAddr != null && childMaxAddr.compareTo(maxAddr) > 0) {
|
||||
maxAddr = childMaxAddr;
|
||||
}
|
||||
}
|
||||
return maxAddr;
|
||||
|
||||
}
|
||||
|
||||
private void updateChildCount() {
|
||||
checkIsValid();
|
||||
childCount = 0;
|
||||
try {
|
||||
Field[] keys = adapter.getParentChildKeys(key, TreeManager.PARENT_ID_COL);
|
||||
childCount = keys.length;
|
||||
}
|
||||
catch (IOException e) {
|
||||
moduleMgr.dbError(e);
|
||||
}
|
||||
private void updateChildCount(int change) throws IOException {
|
||||
childCount += change;
|
||||
record.setIntValue(ModuleDBAdapter.MODULE_CHILD_COUNT_COL, childCount);
|
||||
moduleAdapter.updateModuleRecord(record);
|
||||
}
|
||||
|
||||
private class ParentChildRecordComparator implements Comparator<DBRecord> {
|
||||
|
||||
@Override
|
||||
public int compare(DBRecord r1, DBRecord r2) {
|
||||
int index1 = r1.getIntValue(TreeManager.ORDER_COL);
|
||||
int index2 = r2.getIntValue(TreeManager.ORDER_COL);
|
||||
int index1 = r1.getIntValue(ParentChildDBAdapter.ORDER_COL);
|
||||
int index2 = r2.getIntValue(ParentChildDBAdapter.ORDER_COL);
|
||||
if (index1 < index2) {
|
||||
return -1;
|
||||
}
|
||||
@ -889,7 +901,6 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -907,4 +918,8 @@ class ModuleDB extends DatabaseObject implements ProgramModule {
|
||||
return moduleMgr.getTreeID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return record.getString(ModuleDBAdapter.MODULE_NAME_COL);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,121 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
abstract class ModuleDBAdapter {
|
||||
|
||||
private static final String MODULE_TABLE_NAME = "Module Table";
|
||||
|
||||
static final int MODULE_NAME_COL = ModuleDBAdapterV1.V1_MODULE_NAME_COL;
|
||||
static final int MODULE_COMMENTS_COL = ModuleDBAdapterV1.V1_MODULE_COMMENTS_COL;
|
||||
static final int MODULE_CHILD_COUNT_COL = ModuleDBAdapterV1.V1_MODULE_CHILD_COUNT_COL;
|
||||
|
||||
/**
|
||||
* Gets an adapter for working with the program tree module database table.
|
||||
* @param moduleMgr module manager for corresponding program tree.
|
||||
* @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE).
|
||||
* @param monitor task monitor
|
||||
* @return module table adapter
|
||||
* @throws VersionException if the database handle's version doesn't match the expected version.
|
||||
* @throws IOException if there is a problem accessing the database.
|
||||
* @throws CancelledException if task cancelled
|
||||
*/
|
||||
static ModuleDBAdapter getAdapter(ModuleManager moduleMgr, int openMode, TaskMonitor monitor)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
long treeID = moduleMgr.getTreeID();
|
||||
DBHandle handle = moduleMgr.getDatabaseHandle();
|
||||
try {
|
||||
return new ModuleDBAdapterV1(handle, openMode == DBConstants.CREATE, treeID);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
// V0 read-only is slow - force upgrade
|
||||
if (!e.isUpgradable() || openMode != DBConstants.UPGRADE) {
|
||||
throw e;
|
||||
}
|
||||
ModuleDBAdapter adapter = findReadOnlyAdapter(moduleMgr);
|
||||
return upgrade(moduleMgr, adapter, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
private static ModuleDBAdapter upgrade(ModuleManager moduleMgr, ModuleDBAdapter oldAdapter,
|
||||
TaskMonitor monitor) throws IOException, CancelledException {
|
||||
long treeID = moduleMgr.getTreeID();
|
||||
DBHandle handle = moduleMgr.getDatabaseHandle();
|
||||
DBHandle tmpHandle = new DBHandle();
|
||||
long id = tmpHandle.startTransaction();
|
||||
ModuleDBAdapter tmpAdapter = null;
|
||||
try {
|
||||
tmpAdapter = new ModuleDBAdapterV1(tmpHandle, true, treeID);
|
||||
RecordIterator it = oldAdapter.getRecords();
|
||||
while (it.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
DBRecord rec = it.next();
|
||||
tmpAdapter.updateModuleRecord(rec);
|
||||
}
|
||||
handle.deleteTable(getTableName(treeID));
|
||||
|
||||
ModuleDBAdapter newAdapter = new ModuleDBAdapterV1(handle, true, treeID);
|
||||
it = tmpAdapter.getRecords();
|
||||
while (it.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
DBRecord rec = it.next();
|
||||
newAdapter.updateModuleRecord(rec);
|
||||
}
|
||||
return newAdapter;
|
||||
}
|
||||
catch (VersionException e) {
|
||||
throw new RuntimeException(e); // unexpected exception
|
||||
}
|
||||
finally {
|
||||
tmpHandle.endTransaction(id, true);
|
||||
tmpHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static ModuleDBAdapter findReadOnlyAdapter(ModuleManager moduleMgr)
|
||||
throws VersionException, IOException {
|
||||
|
||||
long treeID = moduleMgr.getTreeID();
|
||||
DBHandle handle = moduleMgr.getDatabaseHandle();
|
||||
ParentChildDBAdapter parentChildAdapter = moduleMgr.getParentChildAdapter();
|
||||
|
||||
return new ModuleDBAdapterV0(handle, treeID, parentChildAdapter);
|
||||
}
|
||||
|
||||
static final String getTableName(long treeID) {
|
||||
return MODULE_TABLE_NAME + treeID;
|
||||
}
|
||||
|
||||
abstract DBRecord createModuleRecord(long parentModuleID, String name) throws IOException;
|
||||
|
||||
abstract DBRecord getModuleRecord(long key) throws IOException;
|
||||
|
||||
abstract DBRecord getModuleRecord(String name) throws IOException;
|
||||
|
||||
abstract RecordIterator getRecords() throws IOException;
|
||||
|
||||
abstract void updateModuleRecord(DBRecord record) throws IOException;
|
||||
|
||||
abstract boolean removeModuleRecord(long childID) throws IOException;
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
class ModuleDBAdapterV0 extends ModuleDBAdapter implements RecordTranslator {
|
||||
|
||||
static final int V0_VERSION = 0;
|
||||
|
||||
static final int V0_MODULE_NAME_COL = 0;
|
||||
static final int V0_MODULE_COMMENTS_COL = 1;
|
||||
|
||||
// V0 Schema - retain for documentation
|
||||
//
|
||||
// static final Schema V0_MODULE_SCHEMA =
|
||||
// new Schema(V0_VERSION, "Key", new Field[] { StringField.INSTANCE, StringField.INSTANCE },
|
||||
// new String[] { "Name", "Comments" });
|
||||
|
||||
private Table moduleTable;
|
||||
private ParentChildDBAdapter parentChildAdapter;
|
||||
|
||||
/**
|
||||
* Gets a version 0 adapter for the program tree module database table (read-only).
|
||||
* @param handle handle to the database containing the table.
|
||||
* @param treeID associated program tree ID
|
||||
* @param parentChildAdapter parent/child database adapter
|
||||
* @throws VersionException if the the table's version does not match the expected version
|
||||
* for this adapter.
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
public ModuleDBAdapterV0(DBHandle handle, long treeID, ParentChildDBAdapter parentChildAdapter)
|
||||
throws VersionException, IOException {
|
||||
|
||||
this.parentChildAdapter = parentChildAdapter;
|
||||
|
||||
String tableName = getTableName(treeID);
|
||||
moduleTable = handle.getTable(tableName);
|
||||
if (moduleTable == null) {
|
||||
throw new VersionException("Missing Table: " + tableName);
|
||||
}
|
||||
int version = moduleTable.getSchema().getVersion();
|
||||
if (version != V0_VERSION) {
|
||||
throw new VersionException(VersionException.NEWER_VERSION, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord createModuleRecord(long parentModuleID, String name) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord getModuleRecord(long key) throws IOException {
|
||||
return translateRecord(moduleTable.getRecord(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord getModuleRecord(String name) throws IOException {
|
||||
Field[] keys = moduleTable.findRecords(new StringField(name), V0_MODULE_NAME_COL);
|
||||
if (keys.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (keys.length > 1) {
|
||||
throw new AssertException("Found " + keys.length + " modules named " + name);
|
||||
}
|
||||
return translateRecord(moduleTable.getRecord(keys[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
RecordIterator getRecords() throws IOException {
|
||||
return new TranslatedRecordIterator(moduleTable.iterator(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateModuleRecord(DBRecord record) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean removeModuleRecord(long childID) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord translateRecord(DBRecord oldRec) throws IOException {
|
||||
if (oldRec == null) {
|
||||
return null;
|
||||
}
|
||||
DBRecord rec = ModuleDBAdapterV1.V1_MODULE_SCHEMA.createRecord(oldRec.getKey());
|
||||
|
||||
rec.setString(MODULE_NAME_COL, oldRec.getString(V0_MODULE_NAME_COL));
|
||||
rec.setString(MODULE_COMMENTS_COL, oldRec.getString(V0_MODULE_COMMENTS_COL));
|
||||
|
||||
// Count child associations for the module
|
||||
Field[] keys =
|
||||
parentChildAdapter.getParentChildKeys(rec.getKey(), ParentChildDBAdapter.PARENT_ID_COL);
|
||||
rec.setIntValue(MODULE_CHILD_COUNT_COL, keys.length);
|
||||
return rec;
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
class ModuleDBAdapterV1 extends ModuleDBAdapter {
|
||||
|
||||
static final int V1_VERSION = 1;
|
||||
|
||||
static final int V1_MODULE_NAME_COL = 0;
|
||||
static final int V1_MODULE_COMMENTS_COL = 1;
|
||||
static final int V1_MODULE_CHILD_COUNT_COL = 2;
|
||||
|
||||
static final Schema V1_MODULE_SCHEMA =
|
||||
new Schema(V1_VERSION, "Key",
|
||||
new Field[] { StringField.INSTANCE, StringField.INSTANCE, IntField.INSTANCE },
|
||||
new String[] { "Name", "Comments", "ChildCount" });
|
||||
|
||||
private Table moduleTable;
|
||||
|
||||
/**
|
||||
* Gets a version 0 adapter for the program tree module database table.
|
||||
* @param handle handle to the database containing the table.
|
||||
* @param create true if this constructor should create the table.
|
||||
* @param treeID associated program tree ID
|
||||
* @throws VersionException if the the table's version does not match the expected version
|
||||
* for this adapter.
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
public ModuleDBAdapterV1(DBHandle handle, boolean create, long treeID)
|
||||
throws VersionException, IOException {
|
||||
|
||||
String tableName = getTableName(treeID);
|
||||
|
||||
if (create) {
|
||||
moduleTable = handle.createTable(tableName, V1_MODULE_SCHEMA,
|
||||
new int[] { V1_MODULE_NAME_COL });
|
||||
}
|
||||
else {
|
||||
moduleTable = handle.getTable(tableName);
|
||||
if (moduleTable == null) {
|
||||
throw new VersionException("Missing Table: " + tableName);
|
||||
}
|
||||
int version = moduleTable.getSchema().getVersion();
|
||||
if (version != V1_VERSION) {
|
||||
throw new VersionException(version < V1_VERSION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord createModuleRecord(long parentModuleID, String name) throws IOException {
|
||||
DBRecord record = V1_MODULE_SCHEMA.createRecord(moduleTable.getKey());
|
||||
record.setString(V1_MODULE_NAME_COL, name);
|
||||
moduleTable.putRecord(record);
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord getModuleRecord(long key) throws IOException {
|
||||
return moduleTable.getRecord(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord getModuleRecord(String name) throws IOException {
|
||||
Field[] keys = moduleTable.findRecords(new StringField(name), V1_MODULE_NAME_COL);
|
||||
if (keys.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (keys.length > 1) {
|
||||
throw new AssertException("Found " + keys.length + " modules named " + name);
|
||||
}
|
||||
return moduleTable.getRecord(keys[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
RecordIterator getRecords() throws IOException {
|
||||
return moduleTable.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateModuleRecord(DBRecord record) throws IOException {
|
||||
moduleTable.putRecord(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean removeModuleRecord(long childID) throws IOException {
|
||||
return moduleTable.deleteRecord(childID);
|
||||
}
|
||||
}
|
@ -40,12 +40,15 @@ import ghidra.util.task.TaskMonitor;
|
||||
*/
|
||||
class ModuleManager {
|
||||
|
||||
static final String FRAGMENT_ADDRESS_TABLE_NAME = "Fragment Addresses";
|
||||
|
||||
private AddressMap addrMap;
|
||||
private long treeID;
|
||||
private GroupDBAdapter adapter;
|
||||
private ModuleDBAdapter moduleAdapter;
|
||||
private FragmentDBAdapter fragmentAdapter;
|
||||
private ParentChildDBAdapter parentChildAdapter;
|
||||
private DBObjectCache<ModuleDB> moduleCache;
|
||||
private DBObjectCache<FragmentDB> fragCache;
|
||||
private ProgramDB program;
|
||||
private TreeManager treeMgr;
|
||||
private HashSet<String> nameSet;
|
||||
private AddressRangeMapDB fragMap;
|
||||
@ -58,43 +61,111 @@ class ModuleManager {
|
||||
|
||||
static long ROOT_MODULE_ID = 0;
|
||||
|
||||
ModuleManager(TreeManager treeMgr, DBRecord rec, ProgramDB program, boolean createTables)
|
||||
throws IOException {
|
||||
/**
|
||||
* Construct a program tree module manager
|
||||
* @param treeMgr program tree manager
|
||||
* @param rec program tree record
|
||||
* @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE).
|
||||
* @param monitor task monitor
|
||||
* @throws IOException if a database IO error occurs
|
||||
* @throws CancelledException if monitor cancelled (upgrade case only)
|
||||
* @throws VersionException if opening an existing program tree and an underlying table
|
||||
* schema version differs from the expected version.
|
||||
*/
|
||||
ModuleManager(TreeManager treeMgr, DBRecord rec, int openMode, TaskMonitor monitor)
|
||||
throws IOException, CancelledException, VersionException {
|
||||
|
||||
this.treeMgr = treeMgr;
|
||||
this.treeID = rec.getKey();
|
||||
this.record = rec;
|
||||
this.program = program;
|
||||
lock = treeMgr.getLock();
|
||||
versionTag = new Object();
|
||||
DBHandle handle = treeMgr.getDatabaseHandle();
|
||||
addrMap = treeMgr.getAddressMap();
|
||||
nameSet = new HashSet<>();
|
||||
errHandler = treeMgr.getErrorHandler();
|
||||
fragMap = new AddressRangeMapDB(handle, addrMap, lock,
|
||||
TreeManager.getFragAddressTableName(treeID), errHandler, LongField.INSTANCE, true);
|
||||
if (createTables) {
|
||||
createDBTables(handle);
|
||||
}
|
||||
findAdapters(handle);
|
||||
|
||||
initializeAdapters(openMode, monitor);
|
||||
|
||||
moduleCache = new DBObjectCache<>(100);
|
||||
fragCache = new DBObjectCache<>(100);
|
||||
|
||||
if (createTables) {
|
||||
if (openMode == DBConstants.CREATE) {
|
||||
createRootModule();
|
||||
}
|
||||
}
|
||||
|
||||
static String getFragAddressTableName(long treeID) {
|
||||
return FRAGMENT_ADDRESS_TABLE_NAME + treeID;
|
||||
}
|
||||
|
||||
private void initializeAdapters(int openMode, TaskMonitor monitor)
|
||||
throws CancelledException, IOException, VersionException {
|
||||
|
||||
DBHandle handle = treeMgr.getDatabaseHandle();
|
||||
|
||||
VersionException versionExc = null;
|
||||
|
||||
try {
|
||||
parentChildAdapter = ParentChildDBAdapter.getAdapter(handle, openMode, treeID);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
versionExc = e.combine(versionExc);
|
||||
}
|
||||
try {
|
||||
// ParentChildDBAdapter must be available for upgrade use
|
||||
moduleAdapter = ModuleDBAdapter.getAdapter(this, openMode, monitor);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
versionExc = e.combine(versionExc);
|
||||
}
|
||||
try {
|
||||
fragmentAdapter = FragmentDBAdapter.getAdapter(handle, openMode, treeID);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
versionExc = e.combine(versionExc);
|
||||
}
|
||||
|
||||
if (addrMap.isUpgraded()) {
|
||||
if (openMode == DBConstants.UPDATE) {
|
||||
versionExc = (new VersionException(true)).combine(versionExc);
|
||||
}
|
||||
else if (openMode == DBConstants.UPGRADE) {
|
||||
addressUpgrade(handle, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
fragMap = new AddressRangeMapDB(handle, addrMap, lock,
|
||||
getFragAddressTableName(treeID), errHandler, LongField.INSTANCE, true);
|
||||
|
||||
if (versionExc != null) {
|
||||
throw versionExc;
|
||||
}
|
||||
}
|
||||
|
||||
long getTreeID() {
|
||||
return treeID;
|
||||
}
|
||||
|
||||
ModuleDBAdapter getModuleAdapter() {
|
||||
return moduleAdapter;
|
||||
}
|
||||
|
||||
FragmentDBAdapter getFragmentAdapter() {
|
||||
return fragmentAdapter;
|
||||
}
|
||||
|
||||
ParentChildDBAdapter getParentChildAdapter() {
|
||||
return parentChildAdapter;
|
||||
}
|
||||
|
||||
Lock getLock() {
|
||||
return lock;
|
||||
}
|
||||
|
||||
static void addressUpgrade(TreeManager treeMgr, long treeID, String name, AddressMap addrMap,
|
||||
TaskMonitor monitor) throws IOException, CancelledException {
|
||||
private void addressUpgrade(DBHandle handle, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
DBHandle handle = treeMgr.getDatabaseHandle();
|
||||
ErrorHandler errHandler = treeMgr.getErrorHandler();
|
||||
String mapName = TreeManager.getFragAddressTableName(treeID);
|
||||
String mapName = getFragAddressTableName(treeID);
|
||||
|
||||
AddressRangeMapDB map = new AddressRangeMapDB(handle, addrMap.getOldAddressMap(),
|
||||
treeMgr.getLock(), mapName, errHandler, LongField.INSTANCE, true);
|
||||
@ -102,6 +173,7 @@ class ModuleManager {
|
||||
return;
|
||||
}
|
||||
|
||||
String name = record.getString(ProgramTreeDBAdapter.TREE_NAME_COL);
|
||||
monitor.setMessage("Upgrading Program Tree (" + name + ")...");
|
||||
|
||||
// Upgrade ranges into temporary map
|
||||
@ -155,7 +227,7 @@ class ModuleManager {
|
||||
void setName(String name) {
|
||||
lock.acquire();
|
||||
try {
|
||||
record.setString(TreeManager.TREE_NAME_COL, name);
|
||||
record.setString(ProgramTreeDBAdapter.TREE_NAME_COL, name);
|
||||
treeMgr.updateTreeRecord(record, false);
|
||||
}
|
||||
finally {
|
||||
@ -180,15 +252,11 @@ class ModuleManager {
|
||||
*
|
||||
*/
|
||||
private void createRootModule() throws IOException {
|
||||
DBRecord rootRecord = adapter.createRootModule(program.getName());
|
||||
DBRecord rootRecord = moduleAdapter.createModuleRecord(0, getProgram().getName());
|
||||
ModuleDB root = new ModuleDB(this, moduleCache, rootRecord);
|
||||
nameSet.add(root.getName());
|
||||
}
|
||||
|
||||
GroupDBAdapter getGroupDBAdapter() {
|
||||
return adapter;
|
||||
}
|
||||
|
||||
void dbError(IOException e) {
|
||||
errHandler.dbError(e);
|
||||
}
|
||||
@ -198,14 +266,13 @@ class ModuleManager {
|
||||
try {
|
||||
ModuleDB root = getModuleDB(ROOT_MODULE_ID);
|
||||
DBRecord rec = root.getRecord();
|
||||
rec.setString(TreeManager.MODULE_NAME_COL, newName);
|
||||
adapter.updateModuleRecord(rec);
|
||||
rec.setString(ModuleDBAdapter.MODULE_NAME_COL, newName);
|
||||
moduleAdapter.updateModuleRecord(rec);
|
||||
treeMgr.updateTreeRecord(record);
|
||||
nameChanged(oldName, root);
|
||||
}
|
||||
catch (IOException e) {
|
||||
errHandler.dbError(e);
|
||||
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@ -217,12 +284,12 @@ class ModuleManager {
|
||||
}
|
||||
|
||||
ProgramModule getModule(String name) throws IOException {
|
||||
DBRecord moduleRecord = adapter.getModuleRecord(name);
|
||||
DBRecord moduleRecord = moduleAdapter.getModuleRecord(name);
|
||||
return getModuleDB(moduleRecord);
|
||||
}
|
||||
|
||||
ProgramFragment getFragment(String name) throws IOException {
|
||||
DBRecord fragmentRecord = adapter.getFragmentRecord(name);
|
||||
DBRecord fragmentRecord = fragmentAdapter.getFragmentRecord(name);
|
||||
return getFragmentDB(fragmentRecord);
|
||||
}
|
||||
|
||||
@ -356,7 +423,7 @@ class ModuleManager {
|
||||
treeMgr.updateTreeRecord(record);
|
||||
|
||||
// generate an event...
|
||||
program.programTreeChanged(treeID, ChangeManager.DOCR_FRAGMENT_MOVED, null,
|
||||
getProgram().programTreeChanged(treeID, ChangeManager.DOCR_FRAGMENT_MOVED, null,
|
||||
new AddressRangeImpl(fromAddr, rangeEnd),
|
||||
new AddressRangeImpl(toAddr, toAddr.addNoWrap(length - 1)));
|
||||
|
||||
@ -372,7 +439,7 @@ class ModuleManager {
|
||||
ProgramModule parent = getModuleDB(parentID);
|
||||
nameSet.add(fragment.getName());
|
||||
treeMgr.updateTreeRecord(record);
|
||||
program.programTreeChanged(treeID, ChangeManager.DOCR_GROUP_ADDED, null, parent,
|
||||
getProgram().programTreeChanged(treeID, ChangeManager.DOCR_GROUP_ADDED, null, parent,
|
||||
fragment);
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -386,12 +453,11 @@ class ModuleManager {
|
||||
|
||||
void moduleAdded(long parentID, ProgramModule module) {
|
||||
lock.acquire();
|
||||
|
||||
try {
|
||||
ProgramModule parent = getModuleDB(parentID);
|
||||
nameSet.add(module.getName());
|
||||
treeMgr.updateTreeRecord(record);
|
||||
program.programTreeChanged(treeID, ChangeManager.DOCR_GROUP_ADDED, null, parent,
|
||||
getProgram().programTreeChanged(treeID, ChangeManager.DOCR_GROUP_ADDED, null, parent,
|
||||
module);
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -417,7 +483,8 @@ class ModuleManager {
|
||||
|
||||
}
|
||||
treeMgr.updateTreeRecord(record);
|
||||
program.programTreeChanged(treeID, ChangeManager.DOCR_GROUP_REMOVED, null, parentModule,
|
||||
getProgram().programTreeChanged(treeID, ChangeManager.DOCR_GROUP_REMOVED, null,
|
||||
parentModule,
|
||||
childName);
|
||||
|
||||
}
|
||||
@ -431,7 +498,7 @@ class ModuleManager {
|
||||
try {
|
||||
treeMgr.updateTreeRecord(record);
|
||||
|
||||
program.programTreeChanged(treeID, ChangeManager.DOCR_GROUP_COMMENT_CHANGED, null,
|
||||
getProgram().programTreeChanged(treeID, ChangeManager.DOCR_GROUP_COMMENT_CHANGED, null,
|
||||
oldComments, group);
|
||||
|
||||
}
|
||||
@ -447,7 +514,7 @@ class ModuleManager {
|
||||
nameSet.remove(oldName);
|
||||
nameSet.add(group.getName());
|
||||
treeMgr.updateTreeRecord(record);
|
||||
program.programTreeChanged(treeID, ChangeManager.DOCR_GROUP_RENAMED, null, oldName,
|
||||
getProgram().programTreeChanged(treeID, ChangeManager.DOCR_GROUP_RENAMED, null, oldName,
|
||||
group);
|
||||
|
||||
}
|
||||
@ -457,17 +524,24 @@ class ModuleManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if specified id is a descendant of moduleID.
|
||||
* Perform recursive check to determine if specified id is a child or decendant
|
||||
* of the specified module.
|
||||
* @param id descendent child id (positive for module, negative for fragment)
|
||||
* @param moduleID module id (positive)
|
||||
* @return true if specified id is a descendant of moduleID.
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
boolean isDescendant(long id, long moduleID) throws IOException {
|
||||
|
||||
Field[] keys = adapter.getParentChildKeys(moduleID, TreeManager.PARENT_ID_COL);
|
||||
Field[] keys =
|
||||
parentChildAdapter.getParentChildKeys(moduleID, ParentChildDBAdapter.PARENT_ID_COL);
|
||||
if (keys.length == 0) {
|
||||
return false;
|
||||
}
|
||||
for (Field key : keys) {
|
||||
DBRecord parentChildRecord = adapter.getParentChildRecord(key.getLongValue());
|
||||
long childID = parentChildRecord.getLongValue(TreeManager.CHILD_ID_COL);
|
||||
DBRecord parentChildRecord =
|
||||
parentChildAdapter.getParentChildRecord(key.getLongValue());
|
||||
long childID = parentChildRecord.getLongValue(ParentChildDBAdapter.CHILD_ID_COL);
|
||||
|
||||
if (childID == id) {
|
||||
return true;
|
||||
@ -504,7 +578,7 @@ class ModuleManager {
|
||||
if (frag != null) {
|
||||
return frag;
|
||||
}
|
||||
DBRecord fragmentRecord = adapter.getFragmentRecord(fragID);
|
||||
DBRecord fragmentRecord = fragmentAdapter.getFragmentRecord(fragID);
|
||||
return createFragmentDB(fragmentRecord);
|
||||
|
||||
}
|
||||
@ -513,38 +587,24 @@ class ModuleManager {
|
||||
}
|
||||
}
|
||||
|
||||
ModuleDB getModuleDB(DBRecord moduleRecord) {
|
||||
lock.acquire();
|
||||
try {
|
||||
if (moduleRecord == null) {
|
||||
return null;
|
||||
}
|
||||
ModuleDB moduleDB = moduleCache.get(moduleRecord.getKey());
|
||||
if (moduleDB != null) {
|
||||
return moduleDB;
|
||||
}
|
||||
return createModuleDB(moduleRecord);
|
||||
|
||||
ModuleDB getModuleDB(DBRecord moduleRecord) throws IOException {
|
||||
if (moduleRecord == null) {
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
ModuleDB moduleDB = moduleCache.get(moduleRecord.getKey());
|
||||
if (moduleDB != null) {
|
||||
return moduleDB;
|
||||
}
|
||||
return createModuleDB(moduleRecord);
|
||||
}
|
||||
|
||||
ModuleDB getModuleDB(long moduleID) throws IOException {
|
||||
lock.acquire();
|
||||
try {
|
||||
ModuleDB moduleDB = moduleCache.get(moduleID);
|
||||
if (moduleDB != null) {
|
||||
return moduleDB;
|
||||
}
|
||||
DBRecord moduleRecord = adapter.getModuleRecord(moduleID);
|
||||
return createModuleDB(moduleRecord);
|
||||
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
ModuleDB moduleDB = moduleCache.get(moduleID);
|
||||
if (moduleDB != null) {
|
||||
return moduleDB;
|
||||
}
|
||||
DBRecord moduleRecord = moduleAdapter.getModuleRecord(moduleID);
|
||||
return createModuleDB(moduleRecord);
|
||||
}
|
||||
|
||||
String getTreeName() {
|
||||
@ -552,17 +612,21 @@ class ModuleManager {
|
||||
}
|
||||
|
||||
CodeUnitIterator getCodeUnits(FragmentDB fragmentDB) {
|
||||
return program.getListing().getCodeUnits(fragmentDB, true);
|
||||
return getProgram().getListing().getCodeUnits(fragmentDB, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move code units in the range to the destination fragment.
|
||||
* @param destFrag destination fragment
|
||||
* @param min min address
|
||||
* @param max max address
|
||||
* @throws NotFoundException if address range not fully contained within program memory
|
||||
*/
|
||||
void move(FragmentDB destFrag, Address min, Address max) throws NotFoundException {
|
||||
|
||||
lock.acquire();
|
||||
try {
|
||||
if (!program.getMemory().contains(min, max)) {
|
||||
if (!getProgram().getMemory().contains(min, max)) {
|
||||
throw new NotFoundException(
|
||||
"Address range for " + min + ", " + max + " is not contained in memory");
|
||||
}
|
||||
@ -603,7 +667,7 @@ class ModuleManager {
|
||||
}
|
||||
treeMgr.updateTreeRecord(record);
|
||||
|
||||
program.programTreeChanged(treeID, ChangeManager.DOCR_CODE_MOVED, null, min, max);
|
||||
getProgram().programTreeChanged(treeID, ChangeManager.DOCR_CODE_MOVED, null, min, max);
|
||||
|
||||
}
|
||||
finally {
|
||||
@ -632,26 +696,29 @@ class ModuleManager {
|
||||
|
||||
void childReordered(ModuleDB parentModule, Group child) {
|
||||
treeMgr.updateTreeRecord(record);
|
||||
program.programTreeChanged(treeID, ChangeManager.DOCR_MODULE_REORDERED, parentModule, child,
|
||||
getProgram().programTreeChanged(treeID, ChangeManager.DOCR_MODULE_REORDERED, parentModule,
|
||||
child,
|
||||
child);
|
||||
}
|
||||
|
||||
void childReparented(Group group, String oldParentName, String newParentName) {
|
||||
treeMgr.updateTreeRecord(record);
|
||||
program.programTreeChanged(treeID, ChangeManager.DOCR_GROUP_REPARENTED, group,
|
||||
getProgram().programTreeChanged(treeID, ChangeManager.DOCR_GROUP_REPARENTED, group,
|
||||
oldParentName, newParentName);
|
||||
}
|
||||
|
||||
String[] getParentNames(long childID) {
|
||||
lock.acquire();
|
||||
try {
|
||||
Field[] keys = adapter.getParentChildKeys(childID, TreeManager.CHILD_ID_COL);
|
||||
Field[] keys =
|
||||
parentChildAdapter.getParentChildKeys(childID, ParentChildDBAdapter.CHILD_ID_COL);
|
||||
String[] names = new String[keys.length];
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
DBRecord parentChildRecord = adapter.getParentChildRecord(keys[i].getLongValue());
|
||||
DBRecord mrec = adapter.getModuleRecord(
|
||||
parentChildRecord.getLongValue(TreeManager.PARENT_ID_COL));
|
||||
names[i] = mrec.getString(TreeManager.MODULE_NAME_COL);
|
||||
DBRecord parentChildRecord =
|
||||
parentChildAdapter.getParentChildRecord(keys[i].getLongValue());
|
||||
DBRecord mrec = moduleAdapter.getModuleRecord(
|
||||
parentChildRecord.getLongValue(ParentChildDBAdapter.PARENT_ID_COL));
|
||||
names[i] = mrec.getString(ModuleDBAdapter.MODULE_NAME_COL);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
@ -668,12 +735,14 @@ class ModuleManager {
|
||||
ProgramModule[] getParents(long childID) {
|
||||
lock.acquire();
|
||||
try {
|
||||
Field[] keys = adapter.getParentChildKeys(childID, TreeManager.CHILD_ID_COL);
|
||||
Field[] keys =
|
||||
parentChildAdapter.getParentChildKeys(childID, ParentChildDBAdapter.CHILD_ID_COL);
|
||||
ProgramModule[] modules = new ProgramModule[keys.length];
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
DBRecord parentChildRecord = adapter.getParentChildRecord(keys[i].getLongValue());
|
||||
DBRecord mrec = adapter.getModuleRecord(
|
||||
parentChildRecord.getLongValue(TreeManager.PARENT_ID_COL));
|
||||
DBRecord parentChildRecord =
|
||||
parentChildAdapter.getParentChildRecord(keys[i].getLongValue());
|
||||
DBRecord mrec = moduleAdapter.getModuleRecord(
|
||||
parentChildRecord.getLongValue(ParentChildDBAdapter.PARENT_ID_COL));
|
||||
modules[i] = getModuleDB(mrec);
|
||||
}
|
||||
return modules;
|
||||
@ -687,41 +756,7 @@ class ModuleManager {
|
||||
return new ProgramModule[0];
|
||||
}
|
||||
|
||||
private void findAdapters(DBHandle handle) throws IOException {
|
||||
try {
|
||||
adapter = new GroupDBAdapterV0(handle, getModuleTableName(), getFragmentTableName(),
|
||||
getParentChildTableName());
|
||||
}
|
||||
catch (VersionException e) {
|
||||
throw new IOException("Module tables created with newer version");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param handle
|
||||
*/
|
||||
private void createDBTables(DBHandle handle) throws IOException {
|
||||
handle.createTable(getModuleTableName(), TreeManager.MODULE_SCHEMA,
|
||||
new int[] { TreeManager.MODULE_NAME_COL });
|
||||
handle.createTable(getFragmentTableName(), TreeManager.FRAGMENT_SCHEMA,
|
||||
new int[] { TreeManager.FRAGMENT_NAME_COL });
|
||||
handle.createTable(getParentChildTableName(), TreeManager.PARENT_CHILD_SCHEMA,
|
||||
new int[] { TreeManager.PARENT_ID_COL, TreeManager.CHILD_ID_COL });
|
||||
}
|
||||
|
||||
private String getModuleTableName() {
|
||||
return TreeManager.getModuleTableName(treeID);
|
||||
}
|
||||
|
||||
private String getFragmentTableName() {
|
||||
return TreeManager.getFragmentTableName(treeID);
|
||||
}
|
||||
|
||||
private String getParentChildTableName() {
|
||||
return TreeManager.getParentChildTableName(treeID);
|
||||
}
|
||||
|
||||
private ModuleDB createModuleDB(DBRecord moduleRecord) {
|
||||
private ModuleDB createModuleDB(DBRecord moduleRecord) throws IOException {
|
||||
if (moduleRecord != null) {
|
||||
ModuleDB moduleDB;
|
||||
moduleDB = new ModuleDB(this, moduleCache, moduleRecord);
|
||||
@ -769,14 +804,13 @@ class ModuleManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidateCache() {
|
||||
void invalidateCache() {
|
||||
lock.acquire();
|
||||
try {
|
||||
versionTag = new Object();
|
||||
moduleCache.invalidate();
|
||||
fragCache.invalidate();
|
||||
record = treeMgr.getTreeRecord(treeID);
|
||||
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@ -788,19 +822,23 @@ class ModuleManager {
|
||||
return versionTag;
|
||||
}
|
||||
|
||||
ProgramDB getProgram() {
|
||||
return program;
|
||||
long getModificationNumber() {
|
||||
return record.getLongValue(ProgramTreeDBAdapter.MODIFICATION_NUM_COL);
|
||||
}
|
||||
|
||||
public long getModificationNumber() {
|
||||
return record.getLongValue(TreeManager.MODIFICATION_NUM_COL);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
void dispose() throws IOException {
|
||||
fragMap.dispose();
|
||||
DBHandle handle = treeMgr.getDatabaseHandle();
|
||||
handle.deleteTable(ParentChildDBAdapter.getTableName(treeID));
|
||||
handle.deleteTable(ModuleDBAdapter.getTableName(treeID));
|
||||
handle.deleteTable(FragmentDBAdapter.getTableName(treeID));
|
||||
}
|
||||
|
||||
public long getTreeID() {
|
||||
return treeID;
|
||||
ProgramDB getProgram() {
|
||||
return treeMgr.getProgram();
|
||||
}
|
||||
|
||||
DBHandle getDatabaseHandle() {
|
||||
return treeMgr.getDatabaseHandle();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,101 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
abstract class ParentChildDBAdapter {
|
||||
|
||||
private static final String PARENT_CHILD_TABLE_NAME = "Parent/Child Relationships";
|
||||
|
||||
static final int PARENT_ID_COL = ParentChildDBAdapterV0.V0_PARENT_ID_COL;
|
||||
static final int CHILD_ID_COL = ParentChildDBAdapterV0.V0_CHILD_ID_COL;
|
||||
static final int ORDER_COL = ParentChildDBAdapterV0.V0_ORDER_COL;
|
||||
|
||||
/**
|
||||
* Gets an adapter for working with the program tree parent/child database table.
|
||||
* @param handle handle to the database to be accessed.
|
||||
* @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE).
|
||||
* @param treeID associated program tree ID
|
||||
* @return module table adapter
|
||||
* @throws VersionException if the database handle's version doesn't match the expected version.
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
static ParentChildDBAdapter getAdapter(DBHandle handle, int openMode, long treeID)
|
||||
throws VersionException, IOException {
|
||||
return new ParentChildDBAdapterV0(handle, openMode == DBConstants.CREATE, treeID);
|
||||
}
|
||||
|
||||
static final String getTableName(long treeID) {
|
||||
return PARENT_CHILD_TABLE_NAME + treeID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add parent/child record
|
||||
* @param parentModuleID parent module ID
|
||||
* @param childId child ID. Module ID are positive, fragment IDs must be negative.
|
||||
* @return parent/child record
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
abstract DBRecord addParentChildRecord(long parentModuleID, long childId) throws IOException;
|
||||
|
||||
/**
|
||||
* Get parent/child record
|
||||
* @param parentID parent module ID
|
||||
* @param childID childId child ID. Module ID are positive, fragment IDs must be negative.
|
||||
* @return parent/child record or null if not found
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
abstract DBRecord getParentChildRecord(long parentID, long childID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get parent/child record for specified record key
|
||||
* @param key parent/child record key
|
||||
* @return parent/child record or null if not found
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
abstract DBRecord getParentChildRecord(long key) throws IOException;
|
||||
|
||||
/**
|
||||
* Update parent/child record
|
||||
* @param record parent/child record
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
abstract void updateParentChildRecord(DBRecord record) throws IOException;
|
||||
|
||||
/**
|
||||
* Remove parent/child record
|
||||
* @param key record key
|
||||
* @return true if deleted else false if not found
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
abstract boolean removeParentChildRecord(long key) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the parent/child record keys which correspond to those records
|
||||
* containing the specified parent or child id as determined by the
|
||||
* specified index column
|
||||
* @param id parent or child id as determined by indexCol
|
||||
* @param indexCol {@link #CHILD_ID_COL} or {@link #PARENT_ID_COL}
|
||||
* @return parent/child record keys
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
abstract Field[] getParentChildKeys(long id, int indexCol) throws IOException;
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
public class ParentChildDBAdapterV0 extends ParentChildDBAdapter {
|
||||
|
||||
static final int V0_VERSION = 0;
|
||||
|
||||
static final int V0_PARENT_ID_COL = 0;
|
||||
static final int V0_CHILD_ID_COL = 1;
|
||||
static final int V0_ORDER_COL = 2;
|
||||
|
||||
static final Schema V0_PARENT_CHILD_SCHEMA = new Schema(V0_VERSION, "Key",
|
||||
new Field[] { LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE },
|
||||
new String[] { "Parent ID", "Child ID", "Child Index" });
|
||||
|
||||
private Table parentChildTable;
|
||||
|
||||
/**
|
||||
* Gets a version 0 adapter for the program tree parent/child database table.
|
||||
* @param handle handle to the database containing the table.
|
||||
* @param create true if this constructor should create the table.
|
||||
* @param treeID associated program tree ID
|
||||
* @throws VersionException if the the table's version does not match the expected version
|
||||
* for this adapter.
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
public ParentChildDBAdapterV0(DBHandle handle, boolean create, long treeID)
|
||||
throws VersionException, IOException {
|
||||
|
||||
String tableName = getTableName(treeID);
|
||||
|
||||
if (create) {
|
||||
parentChildTable = handle.createTable(tableName, V0_PARENT_CHILD_SCHEMA,
|
||||
new int[] { V0_PARENT_ID_COL, V0_CHILD_ID_COL });
|
||||
}
|
||||
else {
|
||||
parentChildTable = handle.getTable(tableName);
|
||||
if (parentChildTable == null) {
|
||||
throw new VersionException("Missing Table: " + tableName);
|
||||
}
|
||||
int version = parentChildTable.getSchema().getVersion();
|
||||
if (version != V0_VERSION) {
|
||||
throw new VersionException(VersionException.NEWER_VERSION, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord addParentChildRecord(long moduleID, long childID) throws IOException {
|
||||
DBRecord pcRec = V0_PARENT_CHILD_SCHEMA.createRecord(parentChildTable.getKey());
|
||||
pcRec.setLongValue(V0_PARENT_ID_COL, moduleID);
|
||||
pcRec.setLongValue(V0_CHILD_ID_COL, childID); // negative value to indicate fragment
|
||||
parentChildTable.putRecord(pcRec);
|
||||
return pcRec;
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord getParentChildRecord(long parentID, long childID) throws IOException {
|
||||
Field[] keys =
|
||||
parentChildTable.findRecords(new LongField(childID), V0_CHILD_ID_COL);
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
DBRecord pcRec = parentChildTable.getRecord(keys[i]);
|
||||
if (pcRec.getLongValue(V0_PARENT_ID_COL) == parentID) {
|
||||
return pcRec;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean removeParentChildRecord(long key) throws IOException {
|
||||
return parentChildTable.deleteRecord(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord getParentChildRecord(long key) throws IOException {
|
||||
return parentChildTable.getRecord(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateParentChildRecord(DBRecord record) throws IOException {
|
||||
parentChildTable.putRecord(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
Field[] getParentChildKeys(long parentID, int indexedCol) throws IOException {
|
||||
return parentChildTable.findRecords(new LongField(parentID), indexedCol);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
abstract class ProgramTreeDBAdapter {
|
||||
|
||||
static final String PROGRAM_TREE_TABLE_NAME = "Trees";
|
||||
|
||||
static final int TREE_NAME_COL = ProgramTreeDBAdapterV0.V0_TREE_NAME_COL;
|
||||
static final int MODIFICATION_NUM_COL = ProgramTreeDBAdapterV0.V0_MODIFICATION_NUM_COL;
|
||||
|
||||
/**
|
||||
* Gets an adapter for working with the program tree database table.
|
||||
* @param handle handle to the database to be accessed.
|
||||
* @param openMode the mode this adapter is to be opened for (CREATE, UPDATE, READ_ONLY, UPGRADE).
|
||||
* @return program tree table adapter
|
||||
* @throws VersionException if the database handle's version doesn't match the expected version.
|
||||
* @throws IOException if there is a problem accessing the database.
|
||||
*/
|
||||
static ProgramTreeDBAdapter getAdapter(DBHandle handle, int openMode)
|
||||
throws VersionException, IOException {
|
||||
return new ProgramTreeDBAdapterV0(handle, openMode == DBConstants.CREATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record for Tree table.
|
||||
* @param name name of the tree
|
||||
* @return record for the tree
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
abstract DBRecord createRecord(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Delete the record for the specified tree ID.
|
||||
* @param treeID tree record ID
|
||||
* @return true if the tree record was successfully deleted
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
abstract boolean deleteRecord(long treeID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the record for the given tree ID.
|
||||
* @param treeID tree record ID
|
||||
* @return tree record or null if not found
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
abstract DBRecord getRecord(long treeID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the record for the tree with the given name.
|
||||
* @param name tree name
|
||||
* @return tree record or null if not found
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
abstract DBRecord getRecord(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Get an iterator over all tree records.
|
||||
* @return record iterator
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
abstract RecordIterator getRecords() throws IOException;
|
||||
|
||||
/**
|
||||
* Update the tree table with the given record.
|
||||
* @param record tree record
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
abstract void updateRecord(DBRecord record) throws IOException;
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
public class ProgramTreeDBAdapterV0 extends ProgramTreeDBAdapter {
|
||||
|
||||
static final int V0_VERSION = 0;
|
||||
|
||||
static final int V0_TREE_NAME_COL = 0;
|
||||
static final int V0_MODIFICATION_NUM_COL = 1;
|
||||
|
||||
static final Schema V0_PROGRAM_TREE_SCHEMA =
|
||||
new Schema(V0_VERSION, "Key", new Field[] { StringField.INSTANCE, LongField.INSTANCE },
|
||||
new String[] { "Name", "Modification Number" });
|
||||
|
||||
private Table programTreeTable;
|
||||
|
||||
/**
|
||||
* Gets a version 0 adapter for the program tree database table.
|
||||
* @param handle handle to the database containing the table.
|
||||
* @param create true if this constructor should create the table.
|
||||
* @throws VersionException if the the table's version does not match the expected version
|
||||
* for this adapter.
|
||||
* @throws IOException if database IO error occurs
|
||||
*/
|
||||
public ProgramTreeDBAdapterV0(DBHandle handle, boolean create)
|
||||
throws VersionException, IOException {
|
||||
|
||||
if (create) {
|
||||
programTreeTable = handle.createTable(PROGRAM_TREE_TABLE_NAME, V0_PROGRAM_TREE_SCHEMA,
|
||||
new int[] { V0_TREE_NAME_COL });
|
||||
}
|
||||
else {
|
||||
programTreeTable = handle.getTable(PROGRAM_TREE_TABLE_NAME);
|
||||
if (programTreeTable == null) {
|
||||
throw new VersionException("Missing Table: " + PROGRAM_TREE_TABLE_NAME);
|
||||
}
|
||||
int version = programTreeTable.getSchema().getVersion();
|
||||
if (version != V0_VERSION) {
|
||||
throw new VersionException(VersionException.NEWER_VERSION, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord createRecord(String name) throws IOException {
|
||||
DBRecord record = V0_PROGRAM_TREE_SCHEMA.createRecord(programTreeTable.getKey());
|
||||
record.setString(V0_TREE_NAME_COL, name);
|
||||
record.setLongValue(V0_MODIFICATION_NUM_COL, 0);
|
||||
programTreeTable.putRecord(record);
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean deleteRecord(long treeID) throws IOException {
|
||||
return programTreeTable.deleteRecord(treeID);
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord getRecord(long treeID) throws IOException {
|
||||
return programTreeTable.getRecord(treeID);
|
||||
}
|
||||
|
||||
@Override
|
||||
DBRecord getRecord(String name) throws IOException {
|
||||
Field[] keys =
|
||||
programTreeTable.findRecords(new StringField(name), V0_TREE_NAME_COL);
|
||||
if (keys.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (keys.length > 1) {
|
||||
throw new AssertException("Found " + keys.length + " trees named " + name);
|
||||
}
|
||||
return programTreeTable.getRecord(keys[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
RecordIterator getRecords() throws IOException {
|
||||
return programTreeTable.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateRecord(DBRecord record) throws IOException {
|
||||
programTreeTable.putRecord(record);
|
||||
}
|
||||
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.DBRecord;
|
||||
import db.RecordIterator;
|
||||
|
||||
/**
|
||||
*
|
||||
* Adapter to access the Tree database table that has a Tree ID (key) and
|
||||
* a tree name.
|
||||
*
|
||||
*
|
||||
*/
|
||||
interface TreeDBAdapter {
|
||||
|
||||
/**
|
||||
* Create a new record for Tree table.
|
||||
* @param name name of the tree
|
||||
* @return record for the tree
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord createRecord(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Delete the record for the tree and all associated tables.
|
||||
* @param treeID key
|
||||
* @return true if the tree was successfully deleted
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
boolean deleteRecord(long treeID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the record for the given tree ID.
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord getRecord(long treeID) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the record for the tree with the given name.
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
DBRecord getRecord(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Get an iterator over all tree records.
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
RecordIterator getRecords() throws IOException;
|
||||
|
||||
/**
|
||||
* Update the tree table with the given record.
|
||||
* @throws IOException if there was a problem accessing the database
|
||||
*/
|
||||
void updateRecord(DBRecord record) throws IOException;
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
/* ###
|
||||
* 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.program.database.module;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.*;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
/**
|
||||
*
|
||||
* Version 0 of the adapter to access the tree table.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class TreeDBAdapterV0 implements TreeDBAdapter {
|
||||
|
||||
private Table treeTable;
|
||||
private DBHandle handle;
|
||||
|
||||
TreeDBAdapterV0(DBHandle handle) throws VersionException {
|
||||
this.handle = handle;
|
||||
treeTable = handle.getTable(TreeManager.TREE_TABLE_NAME);
|
||||
testVersion(0);
|
||||
}
|
||||
|
||||
private void testVersion(int expectedVersion) throws VersionException {
|
||||
|
||||
if (treeTable == null) {
|
||||
throw new VersionException("Tree table not found");
|
||||
}
|
||||
int versionNumber = treeTable.getSchema().getVersion();
|
||||
if (versionNumber != expectedVersion) {
|
||||
throw new VersionException(VersionException.NEWER_VERSION, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord createRecord(String name) throws IOException {
|
||||
DBRecord record = TreeManager.TREE_SCHEMA.createRecord(treeTable.getKey());
|
||||
record.setString(TreeManager.TREE_NAME_COL, name);
|
||||
record.setLongValue(TreeManager.MODIFICATION_NUM_COL, 0);
|
||||
treeTable.putRecord(record);
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteRecord(long treeID) throws IOException {
|
||||
|
||||
if (treeTable.deleteRecord(treeID)) {
|
||||
handle.deleteTable(TreeManager.getFragmentTableName(treeID));
|
||||
handle.deleteTable(TreeManager.getModuleTableName(treeID));
|
||||
handle.deleteTable(TreeManager.getParentChildTableName(treeID));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord getRecord(long treeID) throws IOException {
|
||||
return treeTable.getRecord(treeID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBRecord getRecord(String name) throws IOException {
|
||||
Field[] keys = treeTable.findRecords(new StringField(name), TreeManager.TREE_NAME_COL);
|
||||
if (keys.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (keys.length > 1) {
|
||||
throw new AssertException("Found " + keys.length + " trees named " + name);
|
||||
}
|
||||
return treeTable.getRecord(keys[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecordIterator getRecords() throws IOException {
|
||||
return treeTable.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRecord(DBRecord record) throws IOException {
|
||||
treeTable.putRecord(record);
|
||||
}
|
||||
|
||||
}
|
@ -46,78 +46,25 @@ public class TreeManager implements ManagerDB {
|
||||
|
||||
private AddressMap addrMap;
|
||||
private Map<String, ModuleManager> treeMap; // name mapped to ModuleManager
|
||||
private TreeDBAdapter adapter;
|
||||
private ProgramTreeDBAdapter treeAdapter;
|
||||
private ProgramDB program;
|
||||
private DBHandle handle;
|
||||
private ErrorHandler errHandler;
|
||||
private int openMode;
|
||||
private Lock lock;
|
||||
|
||||
static final String TREE_TABLE_NAME = "Trees";
|
||||
static final int TREE_NAME_COL = 0;
|
||||
static final int MODIFICATION_NUM_COL = 1;
|
||||
|
||||
static final String MODULE_TABLE_NAME = "Module Table";
|
||||
static final int MODULE_NAME_COL = 0;
|
||||
static final int MODULE_COMMENTS_COL = 1;
|
||||
|
||||
static final String FRAGMENT_TABLE_NAME = "Fragment Table";
|
||||
static final int FRAGMENT_NAME_COL = 0;
|
||||
static final int FRAGMENT_COMMENTS_COL = 1;
|
||||
|
||||
static final String PARENT_CHILD_TABLE_NAME = "Parent/Child Relationships";
|
||||
static final int PARENT_ID_COL = 0;
|
||||
static final int CHILD_ID_COL = 1;
|
||||
static final int ORDER_COL = 2;
|
||||
|
||||
static final String FRAGMENT_ADDRESS_TABLE_NAME = "Fragment Addresses";
|
||||
|
||||
static final Schema TREE_SCHEMA = createTreeSchema();
|
||||
static final Schema MODULE_SCHEMA = createModuleSchema();
|
||||
static final Schema FRAGMENT_SCHEMA = createFragmentSchema();
|
||||
static final Schema PARENT_CHILD_SCHEMA = createParentChildSchema();
|
||||
|
||||
private static Schema createTreeSchema() {
|
||||
return new Schema(0, "Key", new Field[] { StringField.INSTANCE, LongField.INSTANCE },
|
||||
new String[] { "Name", "Modification Number" });
|
||||
}
|
||||
|
||||
private static Schema createModuleSchema() {
|
||||
return new Schema(0, "Key", new Field[] { StringField.INSTANCE, StringField.INSTANCE },
|
||||
new String[] { "Name", "Comments" });
|
||||
}
|
||||
|
||||
private static Schema createFragmentSchema() {
|
||||
return new Schema(0, "Key", new Field[] { StringField.INSTANCE, StringField.INSTANCE },
|
||||
new String[] { "Name", "Comments" });
|
||||
}
|
||||
|
||||
private static Schema createParentChildSchema() {
|
||||
return new Schema(0, "Key",
|
||||
new Field[] { LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE },
|
||||
new String[] { "Parent ID", "Child ID", "Child Index" });
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Construct a new TreeManager.
|
||||
*
|
||||
* @param handle
|
||||
* database handle
|
||||
* @param errHandler
|
||||
* error handler
|
||||
* @param addrMap
|
||||
* map to convert addresses to longs and longs to addresses
|
||||
* @param openMode
|
||||
* the open mode for the program.
|
||||
* @param lock
|
||||
* the program synchronization lock
|
||||
* @param monitor
|
||||
* Task monitor for upgrading
|
||||
* @throws IOException
|
||||
* if a database io error occurs.
|
||||
* @throws VersionException
|
||||
* if the database version is different from the expected version
|
||||
* @param handle database handle
|
||||
* @param errHandler error handler
|
||||
* @param addrMap map to convert addresses to longs and longs to addresses
|
||||
* @param openMode the open mode for the program.
|
||||
* @param lock the program synchronization lock
|
||||
* @param monitor Task monitor for upgrading
|
||||
* @throws IOException if a database io error occurs.
|
||||
* @throws VersionException if the database version is different from the expected version
|
||||
* @throws CancelledException if instantiation has been cancelled
|
||||
*/
|
||||
public TreeManager(DBHandle handle, ErrorHandler errHandler, AddressMap addrMap, int openMode,
|
||||
Lock lock, TaskMonitor monitor)
|
||||
@ -126,67 +73,28 @@ public class TreeManager implements ManagerDB {
|
||||
this.handle = handle;
|
||||
this.errHandler = errHandler;
|
||||
this.addrMap = addrMap;
|
||||
this.openMode = openMode;
|
||||
this.lock = lock;
|
||||
if (openMode == DBConstants.CREATE) {
|
||||
createDBTables(handle);
|
||||
}
|
||||
findAdapters(handle);
|
||||
treeMap = new HashMap<>();
|
||||
|
||||
if (addrMap.isUpgraded()) {
|
||||
if (openMode == DBConstants.UPDATE) {
|
||||
throw new VersionException(true);
|
||||
}
|
||||
if (openMode == DBConstants.UPGRADE) {
|
||||
addressUpgrade(monitor);
|
||||
}
|
||||
}
|
||||
treeAdapter = ProgramTreeDBAdapter.getAdapter(handle, openMode);
|
||||
|
||||
initTreeMap(openMode, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the program.
|
||||
*/
|
||||
@Override
|
||||
public void setProgram(ProgramDB program) {
|
||||
this.program = program;
|
||||
try {
|
||||
populateTreeMap(false);
|
||||
if (openMode == DBConstants.CREATE) {
|
||||
createDefaultTree();
|
||||
openMode = -1; // clear openMode flag
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
program.dbError(e);
|
||||
|
||||
if (treeMap.isEmpty()) {
|
||||
createDefaultTree();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.ManagerDB#programReady(int, int, ghidra.util.task.TaskMonitor)
|
||||
*/
|
||||
@Override
|
||||
public void programReady(int openMode1, int currentRevision, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade the address maps associated with each program tree.
|
||||
* @param monitor
|
||||
* @throws CancelledException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void addressUpgrade(TaskMonitor monitor) throws CancelledException, IOException {
|
||||
RecordIterator iter = adapter.getRecords();
|
||||
while (iter.hasNext()) {
|
||||
DBRecord rec = iter.next();
|
||||
long key = rec.getKey();
|
||||
String treeName = rec.getString(TREE_NAME_COL);
|
||||
ModuleManager.addressUpgrade(this, key, treeName, addrMap, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
public void imageBaseChanged(boolean commit) {
|
||||
lock.acquire();
|
||||
try {
|
||||
@ -216,11 +124,13 @@ public class TreeManager implements ManagerDB {
|
||||
throw new DuplicateNameException(
|
||||
"Root module named " + treeName + " already exists");
|
||||
}
|
||||
DBRecord record = adapter.createRecord(treeName);
|
||||
ModuleManager m = new ModuleManager(this, record, program, true);
|
||||
DBRecord record = treeAdapter.createRecord(treeName);
|
||||
ModuleManager m =
|
||||
new ModuleManager(this, record, DBConstants.CREATE, TaskMonitor.DUMMY);
|
||||
treeMap.put(treeName, m);
|
||||
addMemoryBlocks(m);
|
||||
if (openMode != DBConstants.CREATE) {
|
||||
if (program != null) {
|
||||
// no notification for initial default tree
|
||||
program.programTreeAdded(record.getKey(), ChangeManager.DOCR_TREE_CREATED, null,
|
||||
treeName);
|
||||
}
|
||||
@ -228,7 +138,9 @@ public class TreeManager implements ManagerDB {
|
||||
}
|
||||
catch (IOException e) {
|
||||
errHandler.dbError(e);
|
||||
|
||||
}
|
||||
catch (VersionException | CancelledException e) {
|
||||
throw new RuntimeException(e); // unexpected exception
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@ -238,6 +150,7 @@ public class TreeManager implements ManagerDB {
|
||||
|
||||
/**
|
||||
* Get the root module of the tree with the given name.
|
||||
* @param treeName tree name
|
||||
* @return root module, or null if there is no tree with the
|
||||
* given name
|
||||
*/
|
||||
@ -265,10 +178,10 @@ public class TreeManager implements ManagerDB {
|
||||
*/
|
||||
public ProgramModule getDefaultRootModule() {
|
||||
try {
|
||||
RecordIterator iter = adapter.getRecords();
|
||||
RecordIterator iter = treeAdapter.getRecords();
|
||||
while (iter.hasNext()) {
|
||||
DBRecord record = iter.next();
|
||||
String name = record.getString(TREE_NAME_COL);
|
||||
String name = record.getString(ProgramTreeDBAdapter.TREE_NAME_COL);
|
||||
return getRootModule(name);
|
||||
}
|
||||
}
|
||||
@ -285,12 +198,12 @@ public class TreeManager implements ManagerDB {
|
||||
public String[] getTreeNames() {
|
||||
String[] names = new String[treeMap.size()];
|
||||
try {
|
||||
RecordIterator iter = adapter.getRecords();
|
||||
RecordIterator iter = treeAdapter.getRecords();
|
||||
|
||||
int index = 0;
|
||||
while (iter.hasNext()) {
|
||||
DBRecord record = iter.next();
|
||||
names[index] = record.getString(TREE_NAME_COL);
|
||||
names[index] = record.getString(ProgramTreeDBAdapter.TREE_NAME_COL);
|
||||
++index;
|
||||
}
|
||||
return names;
|
||||
@ -334,14 +247,15 @@ public class TreeManager implements ManagerDB {
|
||||
|
||||
/**
|
||||
* Remove the tree with the given name.
|
||||
* @param treeName tree name
|
||||
* @return true if the tree was removed
|
||||
*/
|
||||
public boolean removeTree(String treeName) {
|
||||
lock.acquire();
|
||||
try {
|
||||
if (treeMap.containsKey(treeName)) {
|
||||
DBRecord rec = adapter.getRecord(treeName);
|
||||
adapter.deleteRecord(rec.getKey());
|
||||
DBRecord rec = treeAdapter.getRecord(treeName);
|
||||
treeAdapter.deleteRecord(rec.getKey());
|
||||
ModuleManager mm = treeMap.remove(treeName);
|
||||
mm.dispose();
|
||||
program.programTreeChanged(rec.getKey(), ChangeManager.DOCR_TREE_REMOVED, null,
|
||||
@ -439,6 +353,8 @@ public class TreeManager implements ManagerDB {
|
||||
|
||||
/**
|
||||
* Add a memory block with the given range.
|
||||
* @param name memory block name (name of new fragment)
|
||||
* @param range memory block address range
|
||||
*/
|
||||
public void addMemoryBlock(String name, AddressRange range) {
|
||||
lock.acquire();
|
||||
@ -457,10 +373,6 @@ public class TreeManager implements ManagerDB {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a memory block with the given range
|
||||
* @throws CancelledException
|
||||
*/
|
||||
@Override
|
||||
public void deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
@ -484,15 +396,6 @@ public class TreeManager implements ManagerDB {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a memory block to new place.
|
||||
* @param fromAddr old place
|
||||
* @param toAddr new place
|
||||
* @param length the length of the address range to move
|
||||
* @param monitor the current task monitor
|
||||
* @throws AddressOverflowException if an address overflow occurs.
|
||||
* @throws CancelledException if the task is cancelled.
|
||||
*/
|
||||
@Override
|
||||
public void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
|
||||
throws AddressOverflowException, CancelledException {
|
||||
@ -510,7 +413,7 @@ public class TreeManager implements ManagerDB {
|
||||
}
|
||||
// rebuild the map
|
||||
treeMap.clear();
|
||||
populateTreeMap(false);
|
||||
refreshTreeMap(false);
|
||||
}
|
||||
catch (IOException e) {
|
||||
errHandler.dbError(e);
|
||||
@ -530,9 +433,9 @@ public class TreeManager implements ManagerDB {
|
||||
|
||||
String getTreeName(long treeID) {
|
||||
try {
|
||||
DBRecord record = adapter.getRecord(treeID);
|
||||
DBRecord record = treeAdapter.getRecord(treeID);
|
||||
if (record != null) {
|
||||
return record.getString(TREE_NAME_COL);
|
||||
return record.getString(ProgramTreeDBAdapter.TREE_NAME_COL);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
@ -545,22 +448,6 @@ public class TreeManager implements ManagerDB {
|
||||
return errHandler;
|
||||
}
|
||||
|
||||
static String getModuleTableName(long treeID) {
|
||||
return MODULE_TABLE_NAME + treeID;
|
||||
}
|
||||
|
||||
static String getFragmentTableName(long treeID) {
|
||||
return FRAGMENT_TABLE_NAME + treeID;
|
||||
}
|
||||
|
||||
static String getParentChildTableName(long treeID) {
|
||||
return PARENT_CHILD_TABLE_NAME + treeID;
|
||||
}
|
||||
|
||||
static String getFragAddressTableName(long treeID) {
|
||||
return FRAGMENT_ADDRESS_TABLE_NAME + treeID;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* Method addMemoryBlocks; called when a new module manager is
|
||||
@ -583,17 +470,37 @@ public class TreeManager implements ManagerDB {
|
||||
|
||||
/**
|
||||
* Populate the map with existing tree views.
|
||||
* @throws VersionException if a DB schema version differs from expected version
|
||||
* @throws CancelledException if operation cancelled
|
||||
*/
|
||||
private void populateTreeMap(boolean ignoreModificationNumber) throws IOException {
|
||||
private void initTreeMap(int openMode, TaskMonitor monitor)
|
||||
throws IOException, CancelledException, VersionException {
|
||||
treeMap = new HashMap<>();
|
||||
RecordIterator iter = treeAdapter.getRecords();
|
||||
while (iter.hasNext()) {
|
||||
DBRecord rec = iter.next();
|
||||
String treeName = rec.getString(ProgramTreeDBAdapter.TREE_NAME_COL);
|
||||
ModuleManager mm = new ModuleManager(this, rec, openMode, monitor);
|
||||
treeMap.put(treeName, mm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-Populate the map with existing tree views following an invalidation (e.g., undo, redo, memory movement).
|
||||
* @param ignoreModificationNumber if true all existing module managers will be invalidated, otherwise
|
||||
* only those module managers whose modification number does not match the corresponding tree modification
|
||||
* number will be invalidated.
|
||||
*/
|
||||
private void refreshTreeMap(boolean ignoreModificationNumber) throws IOException {
|
||||
Map<String, ModuleManager> oldTreeMap = treeMap;
|
||||
treeMap = new HashMap<>();
|
||||
|
||||
RecordIterator iter = adapter.getRecords();
|
||||
RecordIterator iter = treeAdapter.getRecords();
|
||||
while (iter.hasNext()) {
|
||||
DBRecord rec = iter.next();
|
||||
long key = rec.getKey();
|
||||
String treeName = rec.getString(TREE_NAME_COL);
|
||||
long modNumber = rec.getLongValue(MODIFICATION_NUM_COL);
|
||||
String treeName = rec.getString(ProgramTreeDBAdapter.TREE_NAME_COL);
|
||||
long modNumber = rec.getLongValue(ProgramTreeDBAdapter.MODIFICATION_NUM_COL);
|
||||
ModuleManager mm = oldTreeMap.get(treeName);
|
||||
if (mm != null) {
|
||||
oldTreeMap.remove(treeName);
|
||||
@ -608,7 +515,12 @@ public class TreeManager implements ManagerDB {
|
||||
}
|
||||
}
|
||||
if (mm == null) {
|
||||
mm = new ModuleManager(this, rec, program, false);
|
||||
try {
|
||||
mm = new ModuleManager(this, rec, DBConstants.UPDATE, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (VersionException | CancelledException e) {
|
||||
throw new RuntimeException(e); // unexpected exception
|
||||
}
|
||||
}
|
||||
treeMap.put(treeName, mm);
|
||||
}
|
||||
@ -619,19 +531,11 @@ public class TreeManager implements ManagerDB {
|
||||
}
|
||||
}
|
||||
|
||||
private void findAdapters(DBHandle dbHandle) throws VersionException {
|
||||
adapter = new TreeDBAdapterV0(dbHandle);
|
||||
}
|
||||
|
||||
private void createDBTables(DBHandle dbHandle) throws IOException {
|
||||
dbHandle.createTable(TREE_TABLE_NAME, TREE_SCHEMA, new int[] { TREE_NAME_COL });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCache(boolean all) throws IOException {
|
||||
lock.acquire();
|
||||
try {
|
||||
populateTreeMap(all);
|
||||
refreshTreeMap(all);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@ -659,10 +563,10 @@ public class TreeManager implements ManagerDB {
|
||||
void updateTreeRecord(DBRecord record, boolean updateModificationNumber) {
|
||||
try {
|
||||
if (updateModificationNumber) {
|
||||
record.setLongValue(MODIFICATION_NUM_COL,
|
||||
record.getLongValue(MODIFICATION_NUM_COL) + 1);
|
||||
record.setLongValue(ProgramTreeDBAdapter.MODIFICATION_NUM_COL,
|
||||
record.getLongValue(ProgramTreeDBAdapter.MODIFICATION_NUM_COL) + 1);
|
||||
}
|
||||
adapter.updateRecord(record);
|
||||
treeAdapter.updateRecord(record);
|
||||
}
|
||||
catch (IOException e) {
|
||||
errHandler.dbError(e);
|
||||
@ -671,7 +575,7 @@ public class TreeManager implements ManagerDB {
|
||||
|
||||
DBRecord getTreeRecord(long treeID) {
|
||||
try {
|
||||
return adapter.getRecord(treeID);
|
||||
return treeAdapter.getRecord(treeID);
|
||||
}
|
||||
catch (IOException e) {
|
||||
errHandler.dbError(e);
|
||||
@ -684,6 +588,7 @@ public class TreeManager implements ManagerDB {
|
||||
createRootModule(DEFAULT_TREE_NAME);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
throw new RuntimeException(e); // unexpected exception
|
||||
}
|
||||
}
|
||||
|
||||
@ -701,4 +606,8 @@ public class TreeManager implements ManagerDB {
|
||||
return lock;
|
||||
}
|
||||
|
||||
ProgramDB getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -66,7 +66,8 @@ public interface ProgramModule extends Group {
|
||||
* @return int index or -1 if this Module does not have a child
|
||||
* with the given name
|
||||
*/
|
||||
public int getIndex(String name);
|
||||
public int getIndex(String name);
|
||||
|
||||
/**
|
||||
* Adds the given module as a child of this module.
|
||||
* <P>
|
||||
|
Loading…
Reference in New Issue
Block a user