[wpiutil] Add reflection based cleanup helper (#4919)

Co-authored-by: Starlight220 <53231611+Starlight220@users.noreply.github.com>
This commit is contained in:
Thad House
2023-05-12 21:35:39 -07:00
committed by GitHub
parent 15ba95df7e
commit c82fcb1975
5 changed files with 334 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.cleanup;
import edu.wpi.first.util.ErrorMessages;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* An object containing a Stack of AutoCloseable objects that are closed when this object is closed.
*/
public class CleanupPool implements AutoCloseable {
// Use a Deque instead of a Stack, as Stack's iterators go the wrong way, and docs
// state ArrayDeque is faster anyway.
private final Deque<AutoCloseable> m_closers = new ArrayDeque<AutoCloseable>();
/**
* Registers an object in the object stack for cleanup.
*
* @param <T> The object type
* @param object The object to register
* @return The registered object
*/
public <T extends AutoCloseable> T register(T object) {
ErrorMessages.requireNonNullParam(object, "object", "register");
m_closers.addFirst(object);
return object;
}
/**
* Removes an object from the cleanup stack.
*
* @param object the object to remove
*/
public void remove(AutoCloseable object) {
m_closers.remove(object);
}
/** Closes all objects in the stack. */
@Override
@SuppressWarnings("PMD.AvoidCatchingGenericException")
public void close() {
for (AutoCloseable autoCloseable : m_closers) {
try {
autoCloseable.close();
} catch (Exception e) {
// Swallow any exceptions on close
e.printStackTrace();
}
}
m_closers.clear();
}
}

View File

@@ -0,0 +1,50 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.cleanup;
import java.lang.reflect.Field;
/**
* Implement this interface to have access to a `reflectionCleanup` method that can be called from
* your `close` method, that will use reflection to find all `AutoCloseable` instance members and
* close them.
*/
public interface ReflectionCleanup extends AutoCloseable {
/**
* Default implementation that uses reflection to find all AutoCloseable fields not marked
* SkipCleanup and call close() on them. Call this from your `close()` method with the class level
* you want to close.
*
* @param cls the class level to clean up
*/
@SuppressWarnings("PMD.AvoidCatchingGenericException")
default void reflectionCleanup(Class<? extends ReflectionCleanup> cls) {
if (!cls.isAssignableFrom(getClass())) {
System.out.println("Passed in class is not assignable from \"this\"");
System.out.println("Expected something in the hierarchy of" + cls.getName());
System.out.println("This is " + getClass().getName());
return;
}
for (Field field : cls.getDeclaredFields()) {
if (field.isAnnotationPresent(SkipCleanup.class)) {
continue;
}
if (!AutoCloseable.class.isAssignableFrom(field.getType())) {
continue;
}
if (field.trySetAccessible()) {
try {
AutoCloseable c = (AutoCloseable) field.get(this);
if (c != null) {
c.close();
}
} catch (Exception e) {
// Ignore any exceptions
e.printStackTrace();
}
}
}
}
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util.cleanup;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SkipCleanup {}