package org.netbeans.gradle.project.java.model;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jtrim.concurrent.GenericUpdateTaskExecutor;
import org.jtrim.concurrent.TaskExecutor;
import org.jtrim.concurrent.TaskExecutors;
import org.jtrim.concurrent.UpdateTaskExecutor;
import org.jtrim.event.ListenerRef;
import org.jtrim.event.ListenerRegistries;
import org.jtrim.property.MutableProperty;
import org.jtrim.property.PropertyFactory;
import org.jtrim.property.PropertySource;
import org.jtrim.property.ValueConverter;
import org.jtrim.swing.concurrent.SwingTaskExecutor;
import org.jtrim.utils.ExceptionHelper;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.gradle.model.java.JavaClassPaths;
import org.netbeans.gradle.model.java.JavaSourceSet;
import org.netbeans.gradle.project.java.JavaExtension;
import org.netbeans.gradle.project.properties.NbProperties;
import org.netbeans.gradle.project.util.NbConsumer;
import org.netbeans.gradle.project.util.NbFunction;
import org.netbeans.gradle.project.util.NbTaskExecutors;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
public final class JavaProjectDependencies {
private static final PropertySource<Map<File, JavaProjectDependencyDef>> NO_DEPENDENCIES
= PropertyFactory.constSource(Collections.<File, JavaProjectDependencyDef>emptyMap());
private final JavaExtension javaExt;
private final UpdateTaskExecutor updateExecutor;
private final MutableProperty<TranslatedDependencies> translatedDependencies;
private final PropertySource<Map<File, JavaProjectDependencyDef>> translatedJavaDependenciesMap;
private final PropertySource<Map<File, JavaProjectDependencyDef>> filteredTranslatedJavaDependenciesMap;
public JavaProjectDependencies(JavaExtension javaExt) {
this(javaExt, NbTaskExecutors.DEFAULT_EXECUTOR);
}
public JavaProjectDependencies(JavaExtension javaExt, TaskExecutor executor) {
ExceptionHelper.checkNotNullArgument(javaExt, "javaExt");
this.javaExt = javaExt;
this.updateExecutor = new GenericUpdateTaskExecutor(TaskExecutors.inOrderSimpleExecutor(executor));
this.translatedDependencies = PropertyFactory
.memPropertyConcurrent(null, true, SwingTaskExecutor.getStrictExecutor(true));
this.translatedJavaDependenciesMap = NbProperties.propertyOfProperty(translatedDependencies, new NbFunction<TranslatedDependencies, PropertySource<Map<File, JavaProjectDependencyDef>>>() {
@Override
public PropertySource<Map<File, JavaProjectDependencyDef>> apply(TranslatedDependencies src) {
return src != null
? new ProjectDepedencyDefProperty(src.translatedDependencies)
: NO_DEPENDENCIES;
}
});
this.filteredTranslatedJavaDependenciesMap = PropertyFactory.convert(translatedJavaDependenciesMap, new ValueConverter<Map<File, JavaProjectDependencyDef>, Map<File, JavaProjectDependencyDef>>() {
@Override
public Map<File, JavaProjectDependencyDef> convert(Map<File, JavaProjectDependencyDef> input) {
return Maps.filterValues(input, new Predicate<JavaProjectDependencyDef>() {
@Override
public boolean apply(JavaProjectDependencyDef input) {
return input != null;
}
});
}
});
}
private static final class ProjectDepedencyDefProperty
implements
PropertySource<Map<File, JavaProjectDependencyDef>> {
private final Map<File, ProjectDependencyCandidate> src;
public ProjectDepedencyDefProperty(Map<File, ProjectDependencyCandidate> src) {
this.src = src != null
? src
: Collections.<File, ProjectDependencyCandidate>emptyMap();
}
@Override
public Map<File, JavaProjectDependencyDef> getValue() {
return Maps.transformValues(src, new Function<ProjectDependencyCandidate, JavaProjectDependencyDef>() {
@Override
public JavaProjectDependencyDef apply(ProjectDependencyCandidate input) {
return input.projectDependency().getValue();
}
});
}
@Override
public ListenerRef addChangeListener(Runnable listener) {
List<ListenerRef> result = new ArrayList<>(src.size());
for (ProjectDependencyCandidate candidate: src.values()) {
candidate.projectDependency().addChangeListener(listener);
}
return ListenerRegistries.combineListenerRefs(result);
}
}
public void updateDependencies() {
updateExecutor.execute(new Runnable() {
@Override
public void run() {
updateDependenciesNow();
}
});
}
public PropertySource<Map<File, JavaProjectDependencyDef>> translatedDependencies() {
return filteredTranslatedJavaDependenciesMap;
}
public JavaProjectDependencyDef tryGetDependency(File output) {
return translatedJavaDependenciesMap.getValue().get(output);
}
public void forAllCandidates(NbConsumer<? super ProjectDependencyCandidate> task) {
TranslatedDependencies value = translatedDependencies.getValue();
if (value != null) {
for (ProjectDependencyCandidate candidate: value.translatedDependencies.values()) {
task.accept(candidate);
}
}
}
private void updateDependenciesNow() {
// This method is never called concurrently due to the update executor.
NbJavaModule currentModule = javaExt.getCurrentModel().getMainModule();
TranslatedDependencies currentTranslatedDependencies = translatedDependencies.getValue();
if (currentTranslatedDependencies != null && currentTranslatedDependencies.source == currentModule) {
return;
}
this.translatedDependencies.setValue(new TranslatedDependencies(currentModule, translateDependencies(currentModule)));
}
private static Map<File, ProjectDependencyCandidate> translateDependencies(NbJavaModule module) {
Map<File, ProjectDependencyCandidate> result = new HashMap<>();
for (JavaSourceSet sourceSet: module.getSources()) {
JavaClassPaths classpaths = sourceSet.getClasspaths();
Set<File> compileClasspaths = classpaths.getCompileClasspaths();
for (File dependency: compileClasspaths) {
tryTranslateDependency(dependency, result);
}
for (File dependency: classpaths.getRuntimeClasspaths()) {
if (!compileClasspaths.contains(dependency)) {
tryTranslateDependency(dependency, result);
}
}
}
return Collections.unmodifiableMap(result);
}
private static void tryTranslateDependency(File dependency, Map<File, ProjectDependencyCandidate> result) {
ProjectDependencyCandidate translated = tryTranslateDependency(dependency);
if (translated != null) {
result.put(dependency, translated);
}
}
private static ProjectDependencyCandidate tryTranslateDependency(File dependency) {
FileObject dependencyObj = FileUtil.toFileObject(dependency);
if (dependencyObj == null) {
return null;
}
Project owner = FileOwnerQuery.getOwner(dependencyObj);
if (owner == null) {
return null;
}
return new ProjectDependencyCandidate(owner, dependency);
}
private static final class TranslatedDependencies {
public final NbJavaModule source;
public final Map<File, ProjectDependencyCandidate> translatedDependencies;
public TranslatedDependencies(
NbJavaModule source,
Map<File, ProjectDependencyCandidate> translatedDependencies) {
this.source = source;
this.translatedDependencies = translatedDependencies;
}
}
}