Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 56 additions & 45 deletions src/main/java/org/jabref/gui/util/FilteredListProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,47 @@

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

public static void refilterListReflection(FilteredList<BibEntryTableViewModel> 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<BibEntryTableViewModel> predicateImpl = (Predicate<BibEntryTableViewModel>) invoke(filteredList, FilteredList.class, "getPredicateImpl");
Predicate<BibEntryTableViewModel> predicateImpl = (Predicate<BibEntryTableViewModel>) GET_PREDICATE_IMPL_METHOD.invoke(filteredList);
ListIterator<? extends BibEntryTableViewModel> 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();
Expand All @@ -60,54 +68,57 @@ public static void refilterListReflection(FilteredList<BibEntryTableViewModel> 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;
}
}