diff --git a/src/main/java/org/jabref/gui/util/FilteredListProxy.java b/src/main/java/org/jabref/gui/util/FilteredListProxy.java index 5faa613ebc1..7584d30cf41 100644 --- a/src/main/java/org/jabref/gui/util/FilteredListProxy.java +++ b/src/main/java/org/jabref/gui/util/FilteredListProxy.java @@ -16,15 +16,24 @@ public class FilteredListProxy { private static final Logger LOGGER = LoggerFactory.getLogger(FilteredListProxy.class); - - private FilteredListProxy() { - } + private static boolean initialized = false; + private static Method BEGIN_CHANGE_METHOD; + private static Method END_CHANGE_METHOD; + private static Method NEXT_ADD_METHOD; + private static Method NEXT_UPDATE_METHOD; + private static Method NEXT_REMOVE_METHOD; + private static Method REFILTER_METHOD; + private static Method ENSURE_SIZE_METHOD; + private static Method GET_PREDICATE_IMPL_METHOD; + private static Field FILTERED_FIELD; + private static Field SIZE_FIELD; public static void refilterListReflection(FilteredList filteredList) { try { - Method refilter = FilteredList.class.getDeclaredMethod("refilter"); - refilter.setAccessible(true); - refilter.invoke(filteredList); + if (!initialized) { + initReflection(); + } + REFILTER_METHOD.invoke(filteredList); } catch (Exception e) { LOGGER.warn("Could not refilter list", e); } @@ -32,23 +41,22 @@ public static void refilterListReflection(FilteredList f public static void refilterListReflection(FilteredList filteredList, int sourceFrom, int sourceTo) { try { + if (!initialized) { + initReflection(); + } if (sourceFrom < 0 || sourceTo > filteredList.getSource().size() || sourceFrom > sourceTo) { throw new IndexOutOfBoundsException(); } - invoke(filteredList, ObservableListBase.class, "beginChange"); - - invoke(filteredList, FilteredList.class, "ensureSize", filteredList.getSource().size()); + BEGIN_CHANGE_METHOD.invoke(filteredList); + ENSURE_SIZE_METHOD.invoke(filteredList, filteredList.getSource().size()); @SuppressWarnings("unchecked") - Predicate predicateImpl = (Predicate) invoke(filteredList, FilteredList.class, "getPredicateImpl"); + Predicate predicateImpl = (Predicate) GET_PREDICATE_IMPL_METHOD.invoke(filteredList); ListIterator it = filteredList.getSource().listIterator(sourceFrom); - Field filteredField = getField("filtered"); - int[] filtered = (int[]) filteredField.get(filteredList); - - Field sizeField = getField("size"); - int size = (int) sizeField.get(filteredList); + int[] filtered = (int[]) FILTERED_FIELD.get(filteredList); + int size = (int) SIZE_FIELD.get(filteredList); for (int i = sourceFrom; i < sourceTo; ++i) { BibEntryTableViewModel el = it.next(); @@ -60,54 +68,57 @@ public static void refilterListReflection(FilteredList f * 3. not passed before and now -> nextAdd * 4. not passed before and not now -> do nothing */ if (passedBefore && passedNow) { - invoke(filteredList, ObservableListBase.class, "nextUpdate", pos); + NEXT_UPDATE_METHOD.invoke(filteredList, pos); } else if (passedBefore) { - invoke(filteredList, ObservableListBase.class, "nextRemove", pos, el); + NEXT_REMOVE_METHOD.invoke(filteredList, pos, el); System.arraycopy(filtered, pos + 1, filtered, pos, size - pos - 1); size--; } else if (passedNow) { int insertionPoint = ~pos; System.arraycopy(filtered, insertionPoint, filtered, insertionPoint + 1, size - insertionPoint); filtered[insertionPoint] = i; - invoke(filteredList, ObservableListBase.class, "nextAdd", insertionPoint, insertionPoint + 1); + NEXT_ADD_METHOD.invoke(filteredList, insertionPoint, insertionPoint + 1); size++; } } // Write back - filteredField.set(filteredList, filtered); - sizeField.set(filteredList, size); + FILTERED_FIELD.set(filteredList, filtered); + SIZE_FIELD.set(filteredList, size); - invoke(filteredList, ObservableListBase.class, "endChange"); + END_CHANGE_METHOD.invoke(filteredList); } catch (ReflectiveOperationException e) { LOGGER.warn("Could not refilter list", e); } } - /** - * We directly invoke the specified method on the given filteredList - */ - private static Object invoke(FilteredList filteredList, Class clazz, String methodName, Object... params) throws ReflectiveOperationException { - // Determine the parameter types for the method lookup - Class[] paramTypes = new Class[params.length]; - for (int i = 0; i < params.length; i++) { - paramTypes[i] = params[i].getClass(); - if (paramTypes[i] == Integer.class) { - // quick hack, because int is converted to Object when calling this method. - paramTypes[i] = int.class; - } - } - Method method = clazz.getDeclaredMethod(methodName, paramTypes); - method.setAccessible(true); - return method.invoke(filteredList, params); - } + private static void initReflection() throws NoSuchMethodException, NoSuchFieldException { + BEGIN_CHANGE_METHOD = ObservableListBase.class.getDeclaredMethod("beginChange"); + END_CHANGE_METHOD = ObservableListBase.class.getDeclaredMethod("endChange"); + NEXT_ADD_METHOD = ObservableListBase.class.getDeclaredMethod("nextAdd", int.class, int.class); + NEXT_UPDATE_METHOD = ObservableListBase.class.getDeclaredMethod("nextUpdate", int.class); + NEXT_REMOVE_METHOD = ObservableListBase.class.getDeclaredMethod("nextRemove", int.class, Object.class); + + REFILTER_METHOD = FilteredList.class.getDeclaredMethod("refilter"); + ENSURE_SIZE_METHOD = FilteredList.class.getDeclaredMethod("ensureSize", int.class); + GET_PREDICATE_IMPL_METHOD = FilteredList.class.getDeclaredMethod("getPredicateImpl"); + + FILTERED_FIELD = FilteredList.class.getDeclaredField("filtered"); + SIZE_FIELD = FilteredList.class.getDeclaredField("size"); + + BEGIN_CHANGE_METHOD.setAccessible(true); + END_CHANGE_METHOD.setAccessible(true); + NEXT_ADD_METHOD.setAccessible(true); + NEXT_UPDATE_METHOD.setAccessible(true); + NEXT_REMOVE_METHOD.setAccessible(true); + + REFILTER_METHOD.setAccessible(true); + ENSURE_SIZE_METHOD.setAccessible(true); + GET_PREDICATE_IMPL_METHOD.setAccessible(true); + + FILTERED_FIELD.setAccessible(true); + SIZE_FIELD.setAccessible(true); - /** - * Get the class field (we need it for read and write later) - */ - private static Field getField(String fieldName) throws ReflectiveOperationException { - Field field = FilteredList.class.getDeclaredField(fieldName); - field.setAccessible(true); - return field; + initialized = true; } }