GP-3112: Remove TupleRecord and just use Java record.

This commit is contained in:
Dan 2023-03-03 10:34:01 -05:00
parent a9baf9f6d8
commit 7328ce48f8
4 changed files with 35 additions and 168 deletions

View file

@ -1,51 +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 generic;
import java.util.List;
import java.util.function.Function;
public interface ComparableTupleRecord<T extends ComparableTupleRecord<T>>
extends TupleRecord<T>, Comparable<T> {
List<Function<T, ? extends Comparable<?>>> getComparableFieldAccessors();
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
default List<Function<T, ?>> getFieldAccessors() {
return (List) getComparableFieldAccessors();
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
default int compareTo(T that) {
if (that == null) {
return 1;
}
int result = this.getClass().hashCode() - that.getClass().hashCode();
if (result != 0) {
return result;
}
for (Function<T, ? extends Comparable<?>> field : getComparableFieldAccessors()) {
Comparable vthis = field.apply((T) this);
Comparable vthat = field.apply(that);
result = vthis.compareTo(vthat);
if (result != 0) {
return result;
}
}
return 0;
}
}

View file

@ -1,51 +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 generic;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
public interface TupleRecord<T extends TupleRecord<T>> {
List<Function<T, ?>> getFieldAccessors();
@SuppressWarnings("unchecked")
default boolean doEquals(Object that) {
if (!this.getClass().equals(that.getClass())) {
return false;
}
for (Function<T, ?> field : getFieldAccessors()) {
if (!Objects.equals(field.apply((T) this), field.apply((T) that))) {
return false;
}
}
return true;
}
@SuppressWarnings("unchecked")
default int doHashCode() {
int hash = 1;
for (Function<T, ?> field : getFieldAccessors()) {
hash *= 31;
Object val = field.apply((T) this);
if (val == null) {
continue;
}
hash += val.hashCode();
}
return hash;
}
}

View file

@ -19,12 +19,9 @@ import java.beans.PropertyEditor;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.function.Function;
import org.apache.commons.lang3.StringUtils;
import generic.ComparableTupleRecord;
import generic.theme.GColor;
import ghidra.framework.options.annotation.*;
import ghidra.framework.plugintool.Plugin;
@ -33,71 +30,43 @@ import ghidra.util.HelpLocation;
public interface AutoOptions {
static class CategoryAndName implements ComparableTupleRecord<CategoryAndName> {
public static final List<Function<CategoryAndName, ? extends Comparable<?>>> ACCESSORS =
List.of(CategoryAndName::getCategory, CategoryAndName::getName);
private final String category;
private final String name;
record CategoryAndName(String category, String name) implements Comparable<CategoryAndName> {
protected static String getPluginPackageName(Plugin plugin) {
return plugin.getPluginDescription().getPluginPackage().getName();
}
private static String computeCategory(String[] categoryNames, Plugin plugin) {
return categoryNames.length == 0
? getPluginPackageName(plugin)
: computeName(categoryNames);
}
private static String computeName(String[] names) {
return String.join(".", names);
}
public CategoryAndName(AutoOptionDefined annotation, Plugin plugin) {
String[] categoryNames = annotation.category();
if (categoryNames.length == 0) {
this.category = getPluginPackageName(plugin);
}
else {
this.category = StringUtils.join(categoryNames, ".");
}
this.name = StringUtils.join(annotation.name(), ".");
this(computeCategory(annotation.category(), plugin), computeName(annotation.name()));
}
public CategoryAndName(AutoOptionConsumed annotation, Plugin plugin) {
// Same code because annotations cannot extend one another
String[] categoryNames = annotation.category();
if (categoryNames.length == 0) {
this.category = getPluginPackageName(plugin);
this(computeCategory(annotation.category(), plugin), computeName(annotation.name()));
}
@Override
public int compareTo(CategoryAndName that) {
int cmp;
cmp = this.category.compareTo(that.category);
if (cmp != 0) {
return cmp;
}
else {
this.category = StringUtils.join(categoryNames, ".");
cmp = this.name.compareTo(that.name);
if (cmp != 0) {
return cmp;
}
this.name = StringUtils.join(annotation.name(), ".");
}
public CategoryAndName(String category, String name) {
this.category = category;
this.name = name;
}
@Override
public List<Function<CategoryAndName, ? extends Comparable<?>>> getComparableFieldAccessors() {
return ACCESSORS;
}
public String getCategory() {
return category;
}
public String getName() {
return name;
}
@Override
public int hashCode() {
return doHashCode();
}
@Override
public boolean equals(Object obj) {
return doEquals(obj);
}
@Override
public String toString() {
return category + ":" + name;
return 0;
}
}
@ -151,8 +120,8 @@ public interface AutoOptions {
continue;
}
CategoryAndName key = new CategoryAndName(annotation, plugin);
ToolOptions options = plugin.getTool().getOptions(key.getCategory());
if (options.isRegistered(key.getName())) {
ToolOptions options = plugin.getTool().getOptions(key.category);
if (options.isRegistered(key.name)) {
continue;
}
f.setAccessible(true);
@ -194,7 +163,7 @@ public interface AutoOptions {
}
if (defaultValue instanceof GColor gColor) {
options.registerThemeColorBinding(key.getName(), gColor.getId(), help, description);
options.registerThemeColorBinding(key.name, gColor.getId(), help, description);
}
/*
else if ( is font option ) {
@ -208,10 +177,10 @@ public interface AutoOptions {
}
*/
else {
options.registerOption(key.getName(), type, defaultValue, help, description,
options.registerOption(key.name, type, defaultValue, help, description,
editor);
// TODO: Wish Ghidra would do this upon any option registration
options.putObject(key.getName(), defaultValue, type);
options.putObject(key.name, defaultValue, type);
}
}

View file

@ -204,7 +204,7 @@ public class AutoOptionsListener<R> implements OptionsChangeListener {
Set<OptionSetter<R>> settersForReceiver =
settersByOption.computeIfAbsent(key, k -> new HashSet<>());
settersForReceiver.add((OptionSetter) setter);
categories.add(key.getCategory());
categories.add(key.category());
}
}
@ -227,10 +227,10 @@ public class AutoOptionsListener<R> implements OptionsChangeListener {
public void notifyCurrentValues(PluginTool tool, R receiver) {
for (Map.Entry<CategoryAndName, Set<OptionSetter<R>>> ent : settersByOption
.entrySet()) {
.entrySet()) {
CategoryAndName key = ent.getKey();
ToolOptions options = tool.getOptions(key.getCategory());
Option opt = options.getOption(key.getName(), OptionType.NO_TYPE, null);
ToolOptions options = tool.getOptions(key.category());
Option opt = options.getOption(key.name(), OptionType.NO_TYPE, null);
if (!opt.isRegistered()) {
continue;
}
@ -315,7 +315,7 @@ public class AutoOptionsListener<R> implements OptionsChangeListener {
public AutoOptionsListener(Plugin plugin, R receiver) {
this.receiver = receiver;
this.profile = (ReceiverProfile<R>) PROFILES_BY_RECEIVER_CLASS
.computeIfAbsent(receiver.getClass(), cls -> new ReceiverProfile<>(cls, plugin));
.computeIfAbsent(receiver.getClass(), cls -> new ReceiverProfile<>(cls, plugin));
}
@Override