ResourceManager.java

  1. /*
  2.  * @copyright defined in LICENSE.txt
  3.  */

  4. package ship.build;

  5. import static hera.util.FilepathUtils.getCanonicalForm;
  6. import static hera.util.ObjectUtils.equal;
  7. import static hera.util.ObjectUtils.nvl;
  8. import static java.util.Arrays.asList;
  9. import static java.util.Collections.EMPTY_LIST;
  10. import static java.util.Collections.unmodifiableSet;
  11. import static org.slf4j.LoggerFactory.getLogger;

  12. import hera.server.ServerEvent;
  13. import hera.server.ServerListener;
  14. import hera.util.FilepathUtils;
  15. import java.io.File;
  16. import java.nio.file.Path;
  17. import java.nio.file.Paths;
  18. import java.util.Collection;
  19. import java.util.Collections;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.Map;
  23. import java.util.Objects;
  24. import java.util.Optional;
  25. import java.util.Set;
  26. import lombok.Getter;
  27. import lombok.RequiredArgsConstructor;
  28. import lombok.Setter;
  29. import org.slf4j.Logger;
  30. import ship.ProjectFile;
  31. import ship.build.res.BuildResource;
  32. import ship.build.res.PackageResource;
  33. import ship.build.res.Project;
  34. import ship.build.res.Source;
  35. import ship.build.res.TestResource;
  36. import ship.util.FileWatcher;

  37. @RequiredArgsConstructor
  38. public class ResourceManager implements ServerListener {
  39.   protected static final Set<Integer> eventFilter =
  40.       unmodifiableSet(new HashSet(asList(FileWatcher.ANY_CHANGED)));

  41.   protected final transient Logger logger = getLogger(getClass());

  42.   protected final Map<String, Resource> cache = new HashMap<>();

  43.   @Getter
  44.   protected final Project project;

  45.   @Getter
  46.   @Setter
  47.   protected PackageManager packageManager = new PackageManager();

  48.   protected final Set<ResourceChangeListener> resourceChangeListeners = new HashSet<>();

  49.   public void addResourceChangeListener(final ResourceChangeListener listener) {
  50.     resourceChangeListeners.add(listener);
  51.   }

  52.   public void removeResourceChangeListener(final ResourceChangeListener listener) {
  53.     resourceChangeListeners.remove(listener);
  54.   }

  55.   protected void fireEvent(final ResourceChangeEvent event) {
  56.     for (final ResourceChangeListener listener : resourceChangeListeners) {
  57.       listener.handle(event);
  58.     }
  59.   }

  60.   /**
  61.    * Return resource for {@code base}.
  62.    *
  63.    * @param path resource base
  64.    *
  65.    * @return resource for base
  66.    */
  67.   public synchronized Resource getResource(final String path) {
  68.     logger.trace("Path: {}", path);
  69.     final ProjectFile projectFile = project.getProjectFile();
  70.     final String canonicalPath = getCanonicalForm(path);
  71.     logger.trace("Canonical base: {}", canonicalPath);
  72.     final Resource cached = cache.get(canonicalPath);
  73.     if (null != cached) {
  74.       return cached;
  75.     }
  76.     final Resource created = create(canonicalPath);
  77.     cache.put(canonicalPath, created);
  78.     logger.debug("{} added", canonicalPath);
  79.     return created;
  80.   }

  81.   protected boolean isTarget(final String canonicalPath) {
  82.     final ProjectFile projectFile = project.getProjectFile();
  83.     return equal(canonicalPath, getCanonicalForm(projectFile.getTarget()));
  84.   }

  85.   protected boolean isTest(final String canonicalPath) {
  86.     final ProjectFile projectFile = project.getProjectFile();
  87.     return nvl(projectFile.getTests(), Collections.<String>emptyList())
  88.         .stream().map(FilepathUtils::getCanonicalForm).anyMatch(canonicalPath::equals);
  89.   }

  90.   protected Resource create(final String canonicalPath) {
  91.     if (isTarget(canonicalPath)) {
  92.       return new BuildResource(project, canonicalPath);
  93.     } else if (isTest(canonicalPath)) {
  94.       return new TestResource(project, canonicalPath);
  95.     } else if (canonicalPath.endsWith(".lua")) {
  96.       return new Source(project, canonicalPath);
  97.     } else {
  98.       return new Resource(project, canonicalPath);
  99.     }

  100.   }

  101.   public Resource getPackage(final String packageName) {
  102.     final ResourceManager newResourceManager = packageManager.find(packageName);
  103.     return new PackageResource(newResourceManager);
  104.   }

  105.   @Override
  106.   public void handle(final ServerEvent event) {
  107.     if (!eventFilter.contains(event.getType())) {
  108.       logger.trace("Unhandled event: {}", event);
  109.       return;
  110.     }

  111.     final Collection<File> files = nvl((Collection<File>) event.getNewData(), EMPTY_LIST);
  112.     final Optional<Resource> cachedResourceOpt = files.stream().map(file -> {
  113.       final String path = getCanonicalForm(file.getAbsolutePath());
  114.       final String projectPath =
  115.           getCanonicalForm(project.getPath().toAbsolutePath().toString());
  116.       final Path relativePath =
  117.           Paths.get(projectPath).relativize(Paths.get(path));
  118.       logger.trace("Project path: {}, Path: {}, Relative path: {}",
  119.           projectPath, path, relativePath);
  120.       final String relativePathStr = relativePath.toString();
  121.       logger.debug("Relative path: {}", relativePath);

  122.       final String canonicalPath = getCanonicalForm(relativePathStr);
  123.       logger.trace("Project relative path: {}", canonicalPath);
  124.       return cache.get(canonicalPath);
  125.     }).filter(Objects::nonNull).findFirst();
  126.     if (cachedResourceOpt.isPresent()) {
  127.       final Resource cached = cachedResourceOpt.get();
  128.       logger.info("{} changed: {}", cached, event.getType());
  129.       fireEvent(new ResourceChangeEvent(cached));
  130.     }
  131.   }
  132. }