package org.netbeans.gradle.project.model; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.gradle.tooling.ModelBuilder; import org.gradle.tooling.ProjectConnection; import org.gradle.tooling.UnknownModelException; import org.gradle.tooling.model.DomainObjectSet; import org.gradle.tooling.model.GradleProject; import org.gradle.tooling.model.GradleTask; import org.gradle.tooling.model.idea.IdeaModule; import org.gradle.tooling.model.idea.IdeaProject; import org.jtrim.utils.ExceptionHelper; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.gradle.model.GenericProjectProperties; import org.netbeans.gradle.model.GradleTaskID; import org.netbeans.gradle.model.OperationInitializer; import org.netbeans.gradle.model.util.CollectionUtils; import org.netbeans.gradle.project.NbGradleProject; import org.netbeans.gradle.project.NbStrings; import org.netbeans.gradle.project.api.entry.ModelLoadResult; import org.netbeans.gradle.project.api.entry.ParsedModel; import org.netbeans.gradle.project.api.modelquery.GradleModelDefQuery1; import org.netbeans.gradle.project.api.modelquery.GradleTarget; import org.netbeans.gradle.project.extensions.NbGradleExtensionRef; import org.netbeans.gradle.project.java.model.idea.IdeaJavaModelUtils; import org.netbeans.gradle.project.script.ScriptFileProvider; import org.openide.util.lookup.Lookups; public final class NbCompatibleModelLoader implements NbModelLoader { private static final Logger LOGGER = Logger.getLogger(NbCompatibleModelLoader.class.getName()); private final SettingsGradleDef settingsGradleDef; private final NbGradleModel baseModels; private final OperationInitializer setup; private final GradleTarget gradleTarget; public NbCompatibleModelLoader( SettingsGradleDef settingsGradleDef, NbGradleModel baseModels, OperationInitializer setup, GradleTarget gradleTarget) { ExceptionHelper.checkNotNullArgument(settingsGradleDef, "settingsGradleDef"); ExceptionHelper.checkNotNullArgument(setup, "setup"); ExceptionHelper.checkNotNullArgument(gradleTarget, "gradleTarget"); this.settingsGradleDef = settingsGradleDef; this.gradleTarget = gradleTarget; this.baseModels = baseModels; this.setup = setup; } @Override public Result loadModels( NbGradleProject project, ProjectConnection connection, ProgressHandle progress) throws IOException { ArrayList<NbGradleModel.Builder> otherModels = new ArrayList<>(); NbGradleModel.Builder mainModel; if (baseModels == null) { mainModel = loadMainModel(project, connection, otherModels); } else { mainModel = new NbGradleModel.Builder(baseModels); initBuilder(mainModel); } Map<File, NbGradleModel.Builder> otherModelsMap = CollectionUtils.newHashMap(otherModels.size()); for (NbGradleModel.Builder model: otherModels) { otherModelsMap.put(model.getProjectDir(), model); } otherModelsMap.remove(mainModel.getProjectDir()); getExtensionModels(project, connection, progress, mainModel, otherModelsMap); return new Result(mainModel.create(), NbGradleModel.createAll(otherModels)); } private <T> T getModelWithProgress( ProjectConnection projectConnection, Class<T> model) { ModelBuilder<T> builder = projectConnection.model(model); DefaultGradleModelLoader.setupLongRunningOP(setup, builder); return builder.get(); } private void getExtensionModels( NbGradleProject project, ProjectConnection projectConnection, ProgressHandle progress, NbGradleModel.Builder mainModel, Map<File, NbGradleModel.Builder> otherModels) { Map<Class<?>, Object> found = new HashMap<>(); NbGradleModel initialMainModel = mainModel.create(); for (NbGradleExtensionRef extensionRef: DefaultGradleModelLoader.getUnloadedExtensions(project, initialMainModel)) { GradleModelDefQuery1 query1 = extensionRef.getModelNeeds().getQuery1(); Collection<Class<?>> toolingModels = query1.getToolingModels(gradleTarget); List<Object> extensionModels = new ArrayList<>(toolingModels.size()); for (Class<?> modelClass: toolingModels) { try { Object model = found.get(modelClass); if (model == null) { progress.progress(NbStrings.getFetchingToolingModel(modelClass)); model = getModelWithProgress(projectConnection, modelClass); } found.put(modelClass, model); extensionModels.add(model); } catch (UnknownModelException ex) { Throwable loggedException = LOGGER.isLoggable(Level.FINE) ? ex : null; LOGGER.log(Level.INFO, "Cannot find model " + modelClass.getName(), loggedException); } } progress.progress(NbStrings.getParsingModel()); ModelLoadResult modelLoadResult = new ModelLoadResult( gradleTarget, project.getProjectDirectoryAsFile(), Lookups.fixed(extensionModels.toArray())); ParsedModel<?> parsedModel = extensionRef.parseModel(modelLoadResult); mainModel.setModelForExtension(extensionRef, parsedModel.getMainModel()); for (Map.Entry<File, ?> otherEntry: parsedModel.getOtherProjectsModel().entrySet()) { NbGradleModel.Builder otherBuilder = otherModels.get(otherEntry.getKey()); if (otherBuilder != null) { otherBuilder.setModelForExtension(extensionRef, otherEntry.getValue()); } } } } private static List<GradleTaskID> getTasksOfModule(IdeaModule module) { DomainObjectSet<? extends GradleTask> modelTasks = module.getGradleProject().getTasks(); List<GradleTaskID> result = new ArrayList<>(modelTasks.size()); for (GradleTask modelTask: modelTasks) { result.add(new GradleTaskID(modelTask.getName(), modelTask.getPath())); } return result; } private static NbGradleProjectTree tryCreateProjectTreeFromIdea(IdeaModule module, ScriptFileProvider scriptProvider) { File moduleDir = IdeaJavaModelUtils.tryGetModuleDir(module); if (moduleDir == null) { return null; } int expectedChildCount = module.getGradleProject().getChildren().size(); List<NbGradleProjectTree> children = new ArrayList<>(expectedChildCount); for (IdeaModule child: getChildModules(module)) { NbGradleProjectTree childInfo = tryCreateProjectTreeFromIdea(child, scriptProvider); if (childInfo != null) { children.add(childInfo); } } GradleProject gradleProject = module.getGradleProject(); String projectName = gradleProject.getName(); String projectFullName = gradleProject.getPath(); GenericProjectProperties properties = NbGenericModelInfo .createProjectProperties(projectName, projectFullName, moduleDir.toPath(), scriptProvider); return new NbGradleProjectTree(properties, getTasksOfModule(module), children); } private static List<IdeaModule> getChildModules(IdeaModule module) { Collection<? extends GradleProject> children = module.getGradleProject().getChildren(); Set<String> childrenPaths = CollectionUtils.newHashSet(children.size()); for (GradleProject child: children) { childrenPaths.add(child.getPath()); } List<IdeaModule> result = new ArrayList<>(); for (IdeaModule candidateChild: module.getProject().getModules()) { if (childrenPaths.contains(candidateChild.getGradleProject().getPath())) { result.add(candidateChild); } } return result; } private static NbGradleModel loadMainModelFromIdeaModule( NbGradleProjectTree rootProject, IdeaModule ideaModule, ScriptFileProvider scriptProvider) throws IOException { ExceptionHelper.checkNotNullArgument(rootProject, "rootProject"); ExceptionHelper.checkNotNullArgument(ideaModule, "ideaModule"); NbGradleProjectTree projectTree = tryCreateProjectTreeFromIdea(ideaModule, scriptProvider); if (projectTree == null) { throw new IOException("Failed to create project tree for project: " + ideaModule.getName()); } return new NbGradleModel(new NbGradleMultiProjectDef(rootProject, projectTree), scriptProvider); } private NbGradleModel.Builder loadMainModel( NbGradleProject project, ProjectConnection projectConnection, ArrayList<NbGradleModel.Builder> otherModels) throws IOException { IdeaProject ideaProject = getModelWithProgress(projectConnection, IdeaProject.class); return parseMainModel(project, ideaProject, otherModels); } private void initBuilder(NbGradleModel.Builder builder) { builder.setRootWithoutSettingsGradle(!settingsGradleDef.isMaySearchUpwards()); } private NbGradleModel.Builder toBuilder(NbGradleMultiProjectDef projectDef, ScriptFileProvider scriptProvider) { NbGradleModel.Builder result = new NbGradleModel.Builder(new NbGenericModelInfo(projectDef, scriptProvider)); initBuilder(result); return result; } private NbGradleModel.Builder toBuilder(NbGradleModel model) { NbGradleModel.Builder result = new NbGradleModel.Builder(model); initBuilder(result); return result; } private NbGradleModel.Builder parseMainModel( NbGradleProject project, IdeaProject ideaProject, ArrayList<NbGradleModel.Builder> otherModels) throws IOException { ExceptionHelper.checkNotNullArgument(project, "project"); ExceptionHelper.checkNotNullArgument(ideaProject, "ideaProject"); ExceptionHelper.checkNotNullArgument(otherModels, "otherModels"); File projectDir = project.getProjectDirectoryAsFile(); IdeaModule mainModule = IdeaJavaModelUtils.tryFindMainModule(projectDir, ideaProject); if (mainModule == null) { throw new IOException("Failed to find idea module for project: " + project.getDisplayName()); } IdeaModule rootModule = tryFindRootModule(ideaProject); if (rootModule == null) { throw new IOException("Failed to find root module for project: " + project.getDisplayName()); } ScriptFileProvider scriptProvider = project.getScriptFileProvider(); NbGradleProjectTree rootTree = tryCreateProjectTreeFromIdea(rootModule, scriptProvider); if (rootTree == null) { throw new IOException("Failed to find root tree for project: " + rootModule.getName()); } DomainObjectSet<? extends IdeaModule> ideaModules = ideaProject.getModules(); otherModels.ensureCapacity(ideaModules.size()); String rootPath = rootModule.getGradleProject().getPath(); for (IdeaModule otherModule: ideaModules) { // This comparison is not strictly necessary but there is no reason // to reparse the main project. if (otherModule != mainModule) { if (rootPath.equals(otherModule.getGradleProject().getPath())) { otherModels.add(toBuilder(new NbGradleMultiProjectDef(rootTree, rootTree), scriptProvider)); } else { otherModels.add(toBuilder(loadMainModelFromIdeaModule(rootTree, otherModule, scriptProvider))); } } } NbGradleProjectTree mainTree; if (rootPath.equals(mainModule.getGradleProject().getPath())) { mainTree = rootTree; } else { mainTree = tryCreateProjectTreeFromIdea(mainModule, scriptProvider); } if (mainTree == null) { throw new IOException("Failed to find tree for project: " + mainModule.getName()); } return toBuilder(new NbGradleMultiProjectDef(rootTree, mainTree), scriptProvider); } private static IdeaModule tryFindRootModule(IdeaProject ideaModel) { DomainObjectSet<? extends IdeaModule> modules = ideaModel.getModules(); if (modules.isEmpty()) { return null; } GradleProject rootProject = getRoot(modules.iterator().next().getGradleProject()); String rootName = rootProject.getPath(); for (IdeaModule module: ideaModel.getModules()) { if (rootName.equals(module.getGradleProject().getPath())) { return module; } } return null; } private static GradleProject getRoot(GradleProject project) { GradleProject prev = null; GradleProject current = project; do { prev = current; current = current.getParent(); } while (current != null); return prev; } }