001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.util.cleanup;
006
007import java.lang.reflect.Field;
008
009/**
010 * Implement this interface to have access to a `reflectionCleanup` method that can be called from
011 * your `close` method, that will use reflection to find all `AutoCloseable` instance members and
012 * close them.
013 */
014@SuppressWarnings("PMD.ImplicitFunctionalInterface")
015public interface ReflectionCleanup extends AutoCloseable {
016  /**
017   * Default implementation that uses reflection to find all AutoCloseable fields not marked
018   * SkipCleanup and call close() on them. Call this from your `close()` method with the class level
019   * you want to close.
020   *
021   * @param cls the class level to clean up
022   */
023  @SuppressWarnings("PMD.AvoidCatchingGenericException")
024  default void reflectionCleanup(Class<? extends ReflectionCleanup> cls) {
025    if (!cls.isAssignableFrom(getClass())) {
026      System.out.println("Passed in class is not assignable from \"this\"");
027      System.out.println("Expected something in the hierarchy of" + cls.getName());
028      System.out.println("This is " + getClass().getName());
029      return;
030    }
031    for (Field field : cls.getDeclaredFields()) {
032      if (field.isAnnotationPresent(SkipCleanup.class)) {
033        continue;
034      }
035      if (!AutoCloseable.class.isAssignableFrom(field.getType())) {
036        continue;
037      }
038      if (field.trySetAccessible()) {
039        try {
040          AutoCloseable c = (AutoCloseable) field.get(this);
041          if (c != null) {
042            c.close();
043          }
044        } catch (Exception e) {
045          // Ignore any exceptions
046          e.printStackTrace();
047        }
048      }
049    }
050  }
051}