package org.netbeans.gradle.model.internal;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.tooling.provider.model.ToolingModelBuilder;
import org.netbeans.gradle.model.BuilderResult;
import org.netbeans.gradle.model.GradleTaskID;
import org.netbeans.gradle.model.ProjectId;
import org.netbeans.gradle.model.api.ProjectInfoBuilder2;
import org.netbeans.gradle.model.util.BasicFileUtils;
import org.netbeans.gradle.model.util.BuilderUtils;
import org.netbeans.gradle.model.util.Exceptions;
import org.netbeans.gradle.model.util.SerializationCache;
import org.netbeans.gradle.model.util.SerializationCaches;
import org.netbeans.gradle.model.util.SerializationUtils;
public final class DynamicModelLoader implements ToolingModelBuilder {
private final ModelQueryInput input;
private final ClassLoader classLoader;
public DynamicModelLoader(ModelQueryInput input, ClassLoader classLoader) {
if (input == null) throw new NullPointerException("input");
this.input = input;
this.classLoader = classLoader;
}
public boolean canBuild(String modelName) {
return modelName.equals(ModelQueryOutputRef.class.getName());
}
private CustomSerializedMap fetchProjectInfos(Project project) {
SerializationCache serializationCache = SerializationCaches.getDefault();
Map<Object, List<?>> projectInfoRequests = input.getProjectInfoRequests(serializationCache, classLoader);
int requestCount = projectInfoRequests.size();
CustomSerializedMap.Builder projectInfosBuilder = new CustomSerializedMap.Builder(requestCount);
for (Map.Entry<?, List<?>> entry: projectInfoRequests.entrySet()) {
Object key = entry.getKey();
for (Object projectInfoBuilder: entry.getValue()) {
Object info = null;
Throwable issue = null;
ProjectInfoBuilder2<?> builder = null;
try {
builder = (ProjectInfoBuilder2<?>)projectInfoBuilder;
info = builder.getProjectInfo(project);
} catch (Throwable ex) {
issue = ex;
}
if (info != null || issue != null) {
BuilderResult builderResult = new BuilderResult(
info,
BuilderUtils.createIssue(builder, issue));
projectInfosBuilder.addValue(key, builderResult);
}
}
}
return projectInfosBuilder.create();
}
private Collection<GradleTaskID> findTasks(Project project) {
TaskContainer tasks = project.getTasks();
// Note: This might cause failures in Gradle 2.4-rc-1
// due to GRADLE-3293. (in practice however, we do not request
// custom models and GradleProject together).
List<GradleTaskID> result = new ArrayList<GradleTaskID>(tasks.size());
for (String taskName: tasks.getNames()) {
Task task = tasks.findByName(taskName);
if (task != null) {
String name = task.getName();
String fullName = task.getPath();
result.add(new GradleTaskID(name, fullName));
}
}
return result;
}
private static String toSafeString(Object obj) {
String result = obj != null ? obj.toString() : null;
return result != null ? result : "";
}
private static ProjectId getProjectId(Project project) {
String group = toSafeString(project.getGroup());
String name = toSafeString(project.getName());
String version = toSafeString(project.getVersion());
return new ProjectId(group, name, version);
}
private BasicInfoWithError getBasicInfo(Project project) {
File buildDir = project.getBuildDir();
String projectFullName = project.getPath();
ProjectId projectId = getProjectId(project);
File buildFile = null;
Collection<GradleTaskID> tasks = Collections.emptyList();
Throwable error = null;
try {
buildFile = BasicFileUtils.toCanonicalFile(project.getBuildFile());
tasks = findTasks(project);
} catch (Throwable ex) {
error = ex;
}
ModelQueryOutput.BasicInfo result = new ModelQueryOutput.BasicInfo(projectId, projectFullName, buildFile, buildDir, tasks);
return new BasicInfoWithError(result, error);
}
public Object buildAll(String modelName, Project project) {
if (!canBuild(modelName)) {
throw new IllegalArgumentException("Unsupported model: " + modelName);
}
BasicInfoWithError basicInfo = getBasicInfo(project);
ModelQueryOutput output;
try {
CustomSerializedMap projectInfos = fetchProjectInfos(project);
output = new ModelQueryOutput(basicInfo.info, projectInfos, basicInfo.error);
} catch (Throwable ex) {
if (basicInfo.error != null) {
Exceptions.tryAddSuppressedException(ex, basicInfo.error);
}
output = new ModelQueryOutput(basicInfo.info, CustomSerializedMap.EMPTY, ex);
}
return new DefaultModelQueryOutputRef(output);
}
private static final class BasicInfoWithError {
public final ModelQueryOutput.BasicInfo info;
public final Throwable error;
public BasicInfoWithError(ModelQueryOutput.BasicInfo info, Throwable error) {
this.info = info;
this.error = error;
}
}
private static final class DefaultModelQueryOutputRef implements ModelQueryOutputRef, Serializable {
private static final long serialVersionUID = 1L;
private final ModelQueryOutput modelQueryOutput;
public DefaultModelQueryOutputRef(ModelQueryOutput modelQueryOutput) {
this.modelQueryOutput = modelQueryOutput;
}
public byte[] getSerializedModelQueryOutput() {
return SerializationUtils.serializeObject(modelQueryOutput);
}
}
}