package bjc.utils.components; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.util.function.BiPredicate; import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import bjc.utils.data.IHolder; import bjc.utils.data.Identity; import bjc.utils.funcdata.FunctionalMap; import bjc.utils.funcdata.IList; import bjc.utils.funcdata.IMap; import bjc.utils.funcutils.FileUtils; /** * A component repository that loads its components from files in a * directory * * @author ben * * @param * The type of component being read in */ public class FileComponentRepository implements IComponentRepository { // The logger to use for storing data about this class private static final Logger CLASS_LOGGER = Logger .getLogger("FileComponentRepository"); // The internal storage of components private IMap components; // The path that all the components came from private Path sourceDirectory; /** * Create a new component repository sourcing components from files in * a directory * * An exception thrown during the loading of a component will only * cause the loading of that component to fail, but a warning will be * logged. * * @param directory * The directory to read component files from * @param componentReader * The function to use to convert files to components */ public FileComponentRepository(File directory, Function componentReader) { // Make sure we have valid arguments if (directory == null) { throw new NullPointerException("Directory must not be null"); } else if (!directory.isDirectory()) { throw new IllegalArgumentException("File " + directory + " is not a directory.\n" + "Components can only be read from a directory"); } else if (componentReader == null) { throw new NullPointerException( "Component reader must not be null"); } // Initialize our fields components = new FunctionalMap<>(); sourceDirectory = directory.toPath().toAbsolutePath(); // Marker for making sure we don't skip the parent IHolder isFirstDir = new Identity<>(true); // Predicate to use to traverse all the files in a directory, but // not recurse into sub-directories BiPredicate firstLevelTraverser = (pth, attr) -> { if (attr.isDirectory() && !isFirstDir.getValue()) { /* * Skip directories, they probably have component support * files. */ return false; } /* * Don't skip the first directory, that's the parent directory */ isFirstDir.replace(false); return true; }; // Try reading components try { FileUtils.traverseDirectory(sourceDirectory, firstLevelTraverser, (pth, attr) -> { loadComponent(componentReader, pth); // Keep loading components, even if this one failed return true; }); } catch (IOException ioex) { CLASS_LOGGER.log(Level.WARNING, ioex, () -> "Error found reading component from file."); } } @Override public IMap getAll() { return components; } @Override public ComponentType getByName(String name) { return components.get(name); } @Override public IList getList() { return components.valueList(); } @Override public String getSource() { return "Components read from directory " + sourceDirectory + "."; } /* * Load a component from a file */ private void loadComponent( Function componentReader, Path pth) { try { // Try to load the component ComponentType component = componentReader.apply(pth.toFile()); if (component == null) { throw new NullPointerException( "Component reader read null component"); } else if (!components.containsKey(component.getName())) { // We only care about the latest version of a component ComponentType oldComponent = components .put(component.getName(), component); if (oldComponent.getVersion() > component.getVersion()) { components.put(oldComponent.getName(), oldComponent); } } else { CLASS_LOGGER.warning("Found a duplicate component.\n" + "Multiple versions of the same component are not currently supported.\n" + "Only the latest version of the component" + component + " will be registered ."); } } catch (Exception ex) { CLASS_LOGGER.log(Level.WARNING, ex, () -> "Error found reading component from file " + pth.toString() + ". This component will not be loaded"); } } }