GP-1948 Refactor program tree for improved performance during import

This commit is contained in:
ghidra1 2022-04-28 12:23:37 -04:00
parent 8e8b193ac1
commit 60c47844c0
19 changed files with 1403 additions and 1074 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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>