package org.netbeans.gradle.project.java.model;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.gradle.model.GenericProjectProperties;
import org.netbeans.gradle.model.java.JacocoModel;
import org.netbeans.gradle.model.java.JarOutput;
import org.netbeans.gradle.model.java.JarOutputsModel;
import org.netbeans.gradle.model.java.JavaClassPaths;
import org.netbeans.gradle.model.java.JavaCompatibilityModel;
import org.netbeans.gradle.model.java.JavaSourceGroup;
import org.netbeans.gradle.model.java.JavaSourceSet;
import org.netbeans.gradle.model.java.JavaSourcesModel;
import org.netbeans.gradle.model.java.JavaTestModel;
import org.netbeans.gradle.model.java.WarFoldersModel;
import org.netbeans.gradle.model.util.CollectionUtils;
import org.netbeans.gradle.project.NbGradleProject;
import org.netbeans.gradle.project.NbGradleProjectFactory;
import org.netbeans.gradle.project.NbStrings;
import org.netbeans.gradle.project.api.entry.ModelLoadResult;
import org.netbeans.gradle.project.api.modelquery.GradleTarget;
import org.netbeans.gradle.project.model.NbGenericModelInfo;
import org.netbeans.gradle.project.others.OtherPlugins;
import org.netbeans.gradle.project.properties.standard.SourceLevelProperty;
import org.netbeans.gradle.project.script.ScriptFileProvider;
import org.netbeans.gradle.project.util.GradleVersions;
import org.netbeans.gradle.project.util.NbFileUtils;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Lookup;
public final class JavaParsingUtils {
private static final Logger LOGGER = Logger.getLogger(JavaParsingUtils.class.getName());
private static Collection<JavaSourceSet> getSourceSetsForJar(JarOutput jar, JavaSourcesModel sources) {
Set<String> sourceSetNames = jar.tryGetSourceSetNames();
if (sourceSetNames != null) {
int numberOfSourceSets = sourceSetNames.size();
Set<JavaSourceSet> result = CollectionUtils.newHashSet(numberOfSourceSets);
for (JavaSourceSet sourceSet: sources.getSourceSets()) {
if (sourceSetNames.contains(sourceSet.getName())) {
result.add(sourceSet);
// There is no reason to look for other source sets,
// we've already found all of them.
if (numberOfSourceSets == result.size()) {
break;
}
}
}
return result;
}
JavaSourceSet heuristicSourceSet = tryGetSourceSetForJarWithHeuristic(jar, sources);
return heuristicSourceSet != null
? Collections.singleton(heuristicSourceSet)
: Collections.<JavaSourceSet>emptySet();
}
private static JavaSourceSet tryGetSourceSetForJarWithHeuristic(JarOutput jar, JavaSourcesModel sources) {
String taskName = jar.getTaskName().toLowerCase(Locale.ROOT);
if (NbJarOutput.DEFAULT_JAR_TASK_NAME.equals(taskName)) {
for (JavaSourceSet sourceSet: sources.getSourceSets()) {
if (JavaSourceSet.NAME_MAIN.equals(sourceSet.getName())) {
return sourceSet;
}
}
return null;
}
else {
JavaSourceSet bestMatch = null;
for (JavaSourceSet sourceSet: sources.getSourceSets()) {
String sourceSetName = sourceSet.getName().toLowerCase(Locale.ROOT);
if (taskName.contains(sourceSetName)) {
if (bestMatch == null || bestMatch.getName().length() < sourceSetName.length()) {
bestMatch = sourceSet;
}
}
}
return bestMatch;
}
}
private static Set<File> tryGetBuildDirSourceForJar(JarOutput jar, JavaSourcesModel sources) {
Collection<JavaSourceSet> sourceSets = getSourceSetsForJar(jar, sources);
if (sourceSets.isEmpty()) {
return null;
}
Set<File> result = CollectionUtils.newHashSet(sourceSets.size());
for (JavaSourceSet sourceSet: sourceSets) {
result.add(sourceSet.getOutputDirs().getClassesDir());
}
return result;
}
private static Map<File, Set<File>> getJarsToBuildDirs(ModelLoadResult buildInfo) {
Map<File, Lookup> allProjects = buildInfo.getEvaluatedProjectsModel();
Map<File, Set<File>> result = CollectionUtils.newHashMap(allProjects.size());
for (Lookup projectInfo: allProjects.values()) {
JarOutputsModel jarsModel = projectInfo.lookup(JarOutputsModel.class);
if (jarsModel != null) {
JavaSourcesModel sources = projectInfo.lookup(JavaSourcesModel.class);
if (sources == null) {
GenericProjectProperties properties = projectInfo.lookup(GenericProjectProperties.class);
String projectName = properties != null
? properties.getProjectFullName()
: "???";
LOGGER.log(Level.WARNING, "No sources for Java project: {0}", projectName);
continue;
}
for (JarOutput jar: jarsModel.getJars()) {
Set<File> buildDirs = tryGetBuildDirSourceForJar(jar, sources);
if (buildDirs != null) {
result.put(jar.getJar(), buildDirs);
}
}
}
}
return result;
}
private static Collection<File> adjustedClassPaths(
Collection<File> files,
Map<File, ? extends Collection<File>> dependencyMap) {
List<File> result = new ArrayList<>(files.size());
for (File file: files) {
Collection<File> adjusted = dependencyMap.get(file);
if (adjusted != null) {
result.addAll(adjusted);
}
else {
result.add(file);
}
}
return result;
}
private static JavaSourceSet adjustedSources(
JavaSourceSet sourceSet,
Map<File, ? extends Collection<File>> dependencyMap) {
JavaClassPaths origClassPaths = sourceSet.getClasspaths();
Collection<File> compile = adjustedClassPaths(origClassPaths.getCompileClasspaths(), dependencyMap);
Collection<File> runtime = adjustedClassPaths(origClassPaths.getRuntimeClasspaths(), dependencyMap);
runtime.remove(sourceSet.getOutputDirs().getClassesDir());
runtime.remove(sourceSet.getOutputDirs().getResourcesDir());
JavaClassPaths classPaths = new JavaClassPaths(compile, runtime);
JavaSourceSet.Builder result = new JavaSourceSet.Builder(sourceSet.getName(), sourceSet.getOutputDirs());
result.setClasspaths(classPaths);
result.setCompileClassPathProblem(sourceSet.getCompileClassPathProblem());
result.setRuntimeClassPathProblem(sourceSet.getRuntimeClassPathProblem());
for (JavaSourceGroup group: sourceSet.getSourceGroups()) {
result.addSourceGroup(group);
}
return result.create();
}
private static Collection<JavaSourceSet> adjustedSources(
JavaSourcesModel sourcesModel,
Map<File, ? extends Collection<File>> dependencyMap) {
List<JavaSourceSet> result = new ArrayList<>();
for (JavaSourceSet sourceSet: sourcesModel.getSourceSets()) {
result.add(adjustedSources(sourceSet, dependencyMap));
}
return result;
}
private static NbGradleProject getProject(File projectDir) {
return NbGradleProjectFactory.tryLoadSafeGradleProject(projectDir);
}
private static List<NbListedDir> getListedDirs(ModelLoadResult retrievedModels, Lookup projectInfo) {
List<NbListedDir> listedDirs = new ArrayList<>();
NbGradleProject project = getProject(retrievedModels.getMainProjectDir());
if (project == null || !OtherPlugins.hasJavaEEExtension(project)) {
WarFoldersModel warFolders = projectInfo.lookup(WarFoldersModel.class);
if (warFolders != null) {
listedDirs.add(new NbListedDir(NbStrings.getWebPages(), warFolders.getWebAppDir()));
}
}
return listedDirs;
}
public static Collection<NbJavaModule> parseModules(ModelLoadResult retrievedModels) {
Map<File, Set<File>> jarsToBuildDirs = getJarsToBuildDirs(retrievedModels);
Map<File, Lookup> allProjects = retrievedModels.getEvaluatedProjectsModel();
List<NbJavaModule> result = new ArrayList<>(allProjects.size());
for (Lookup projectInfo: allProjects.values()) {
JavaCompatibilityModel versions = projectInfo.lookup(JavaCompatibilityModel.class);
JavaSourcesModel sourcesModel = projectInfo.lookup(JavaSourcesModel.class);
if (versions == null || sourcesModel == null) {
continue;
}
if (sourcesModel.getSourceSets().isEmpty()) {
LOGGER.log(Level.INFO,
"Disabling the Java extension because there are no sources: {0}",
retrievedModels.getMainProjectDir());
continue;
}
GenericProjectProperties properties = projectInfo.lookup(GenericProjectProperties.class);
if (properties == null) {
LOGGER.log(Level.WARNING,
"Missing GenericProjectProperties for project {0}",
retrievedModels.getMainProjectDir());
continue;
}
Collection<JavaSourceSet> sourceSets = adjustedSources(sourcesModel, jarsToBuildDirs);
List<NbListedDir> listedDirs = getListedDirs(retrievedModels, projectInfo);
JavaTestModel testModel = projectInfo.lookup(JavaTestModel.class);
if (testModel == null) {
LOGGER.log(Level.WARNING,
"Missing JavaTestModel for project {0}",
retrievedModels.getMainProjectDir());
testModel = JavaTestModel.getDefaulTestModel(retrievedModels.getMainProjectDir());
}
NbJavaModule module = new NbJavaModule(
properties,
versions,
sourceSets,
listedDirs,
getJarOutputs(projectInfo, jarsToBuildDirs),
testModel,
getCodeCoverage(projectInfo));
result.add(module);
}
return result;
}
private static List<NbJarOutput> getJarOutputs(Lookup projectInfo, Map<File, Set<File>> jarsToBuildDirs) {
JarOutputsModel model = projectInfo.lookup(JarOutputsModel.class);
if (model == null) {
return Collections.emptyList();
}
Collection<JarOutput> jars = model.getJars();
List<NbJarOutput> result = new ArrayList<>(jars.size());
for (JarOutput output: jars) {
Set<File> buildDirs = jarsToBuildDirs.get(output.getJar());
if (buildDirs == null) {
buildDirs = Collections.emptySet();
}
result.add(new NbJarOutput(output.getTaskName(), output.getJar(), buildDirs));
}
return result;
}
private static NbCodeCoverage getCodeCoverage(Lookup projectInfo) {
JacocoModel jacocoModel = projectInfo.lookup(JacocoModel.class);
return new NbCodeCoverage(jacocoModel);
}
public static NbJavaModel createEmptyModel(FileObject projectDir, ScriptFileProvider scriptProvider) {
File projectDirAsFile = FileUtil.toFile(projectDir);
if (projectDirAsFile == null) {
throw new IllegalStateException("Project directory does not exist.");
}
return createEmptyModel(projectDirAsFile.toPath(), scriptProvider);
}
private static NbJavaModel createUnreliableModel(GradleTarget evaluationEnvironment, NbJavaModule mainModule) {
return NbJavaModel.createModel(evaluationEnvironment, JavaModelSource.COMPATIBLE_API, mainModule);
}
private static NbJavaModel createEmptyModel(Path projectDir, ScriptFileProvider scriptProvider) {
String name = NbFileUtils.getFileNameStr(projectDir);
String level = SourceLevelProperty.getSourceLevelFromPlatform(JavaPlatform.getDefault());
GenericProjectProperties properties = NbGenericModelInfo.createProjectProperties(name, name, projectDir, scriptProvider);
JavaCompatibilityModel compatibilityModel = new JavaCompatibilityModel(level, level);
NbJavaModule result = new NbJavaModule(
properties,
compatibilityModel,
Collections.<JavaSourceSet>emptyList(),
Collections.<NbListedDir>emptyList(),
Collections.<NbJarOutput>emptyList(),
JavaTestModel.getDefaulTestModel(properties.getProjectDir()),
NbCodeCoverage.NO_CODE_COVERAGE
);
return createUnreliableModel(GradleVersions.DEFAULT_TARGET, result);
}
private JavaParsingUtils() {
throw new AssertionError();
}
}