package org.netbeans.gradle.project; import java.io.Closeable; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.jtrim.property.PropertyFactory; import org.jtrim.property.PropertySource; import org.jtrim.property.ValueConverter; import org.jtrim.utils.ExceptionHelper; import org.netbeans.gradle.project.model.NbGradleModel; import org.netbeans.gradle.project.model.NbGradleProjectTree; import org.netbeans.gradle.project.util.CloseableAction; import org.netbeans.gradle.project.util.Closeables; import org.netbeans.gradle.project.util.NbFileUtils; public final class RootProjectRegistry { private final Lock mainLock; private final Map<RootProjectKey, RegisteredProjects> rootProjects; public RootProjectRegistry() { this.mainLock = new ReentrantLock(); this.rootProjects = new HashMap<>(); } private static boolean isExplicitRootProject(NbGradleModel input) { if (!input.isRootProject()) { return false; } Path settingsFile = input.getSettingsFile(); return settingsFile != null ? Files.isRegularFile(settingsFile) : false; } private CloseableAction.Ref registerAndUpdateProjects(NbGradleModel input) { if (!isExplicitRootProject(input)) { return CloseableAction.CLOSED_REF; } return registerRootProjectModel(input); } private CloseableAction registerAsCloseableAction(final NbGradleModel input) { return new CloseableAction() { @Override public CloseableAction.Ref open() { return registerAndUpdateProjects(input); } }; } public PropertySource<CloseableAction> forProject(PropertySource<? extends NbGradleModel> currentModel) { return PropertyFactory.convert(currentModel, new ValueConverter<NbGradleModel, CloseableAction>() { @Override public CloseableAction convert(NbGradleModel input) { return registerAsCloseableAction(input); } }); } public CloseableAction.Ref registerRootProjectModel(NbGradleModel model) { final RootProjectKey key = new RootProjectKey(model); RegisteredProjects registeredProjects = new RegisteredProjects(model); final Object regId = registeredProjects.id; mainLock.lock(); try { rootProjects.put(key, registeredProjects); } finally { mainLock.unlock(); } final List<Closeable> safeRefs = new ArrayList<>(); CloseableAction.Ref result = new CloseableAction.Ref() { @Override public void close() { mainLock.lock(); try { RegisteredProjects value = rootProjects.get(key); if (value != null && value.id == regId) { rootProjects.remove(key); } Closeables.closeAll(safeRefs); } finally { mainLock.unlock(); } } }; try { safeToOpenChildren(model.getProjectDef().getRootProject(), safeRefs); } catch (Throwable ex) { result.close(); throw ex; } return result; } private static void safeToOpenChildren(NbGradleProjectTree root, Collection<? super Closeable> safeRefs) { for (NbGradleProjectTree child: root.getChildren()) { safeRefs.add(NbGradleProjectFactory.safeToOpen(child.getProjectDir())); safeToOpenChildren(child, safeRefs); } } public Path tryGetSettingsFile(File projectDir) { mainLock.lock(); try { for (Map.Entry<RootProjectKey, RegisteredProjects> entry: rootProjects.entrySet()) { if (entry.getValue().subprojects.contains(projectDir)) { return entry.getKey().settingsFile; } } return null; } finally { mainLock.unlock(); } } private static Set<File> collectProjectDirs(NbGradleProjectTree root) { Set<File> result = new HashSet<>(); collectProjectDirs(root, result); return result; } private static void collectProjectDirs(NbGradleProjectTree root, Set<? super File> result) { for (NbGradleProjectTree child: root.getChildren()) { result.add(child.getProjectDir()); collectProjectDirs(child, result); } } private static final class RegisteredProjects { private final Object id; private final Set<File> subprojects; public RegisteredProjects(NbGradleModel model) { ExceptionHelper.checkNotNullArgument(model, "model"); this.id = new Object(); NbGradleProjectTree root = model.getProjectDef().getRootProject(); this.subprojects = Collections.unmodifiableSet(collectProjectDirs(root)); } } private static final class RootProjectKey { private final Path settingsFile; private final Path projectDir; public RootProjectKey(NbGradleModel model) { this(model.getSettingsFile(), model.getProjectDir()); } public RootProjectKey(Path settingsFile, File projectDir) { this(settingsFile, NbFileUtils.asPath(projectDir)); } public RootProjectKey(Path settingsFile, Path projectDir) { ExceptionHelper.checkNotNullArgument(projectDir, "projectDir"); this.settingsFile = settingsFile; this.projectDir = projectDir; } @Override public int hashCode() { int hash = 7; hash = 29 * hash + Objects.hashCode(this.settingsFile); hash = 29 * hash + Objects.hashCode(this.projectDir); return hash; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (getClass() != obj.getClass()) return false; final RootProjectKey other = (RootProjectKey)obj; return Objects.equals(this.settingsFile, other.settingsFile) && Objects.equals(this.projectDir, other.projectDir); } } }