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 */
014public interface ReflectionCleanup extends AutoCloseable {
015  /**
016   * Default implementation that uses reflection to find all AutoCloseable fields not marked
017   * SkipCleanup and call close() on them. Call this from your `close()` method with the class level
018   * you want to close.
019   *
020   * @param cls the class level to clean up
021   */
022  @SuppressWarnings("PMD.AvoidCatchingGenericException")
023  default void reflectionCleanup(Class<? extends ReflectionCleanup> cls) {
024    if (!cls.isAssignableFrom(getClass())) {
025      System.out.println("Passed in class is not assignable from \"this\"");
026      System.out.println("Expected something in the hierarchy of" + cls.getName());
027      System.out.println("This is " + getClass().getName());
028      return;
029    }
030    for (Field field : cls.getDeclaredFields()) {
031      if (field.isAnnotationPresent(SkipCleanup.class)) {
032        continue;
033      }
034      if (!AutoCloseable.class.isAssignableFrom(field.getType())) {
035        continue;
036      }
037      if (field.trySetAccessible()) {
038        try {
039          AutoCloseable c = (AutoCloseable) field.get(this);
040          if (c != null) {
041            c.close();
042          }
043        } catch (Exception e) {
044          // Ignore any exceptions
045          e.printStackTrace();
046        }
047      }
048    }
049  }
050}