package org.netbeans.gradle.model; import java.io.File; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.gradle.tooling.BuildController; import org.netbeans.gradle.model.api.GradleInfoQuery; import org.netbeans.gradle.model.api.GradleProjectInfoQuery2; import org.netbeans.gradle.model.api.ModelClassPathDef; import org.netbeans.gradle.model.internal.CustomSerializedMap; import org.netbeans.gradle.model.internal.IssueTransformer; import org.netbeans.gradle.model.internal.SerializedEntries; import org.netbeans.gradle.model.util.ClassLoaderUtils; import org.netbeans.gradle.model.util.CollectionUtils; import org.netbeans.gradle.model.util.MultiMapUtils; import org.netbeans.gradle.model.util.SerializationCache; import org.netbeans.gradle.model.util.SerializationCaches; import org.netbeans.gradle.model.util.TransferableExceptionWrapper; final class GradleInfoQueryMap { private final CustomSerializedMap builderMap; private final Map<KeyWrapper, ModelClassPathDef> classpath; private final Map<KeyWrapper, Throwable> serializationIssues; private final SerializationCache serializationCache; private GradleInfoQueryMap( CustomSerializedMap builderMap, Map<KeyWrapper, ModelClassPathDef> classpath, Map<Object, Throwable> serializationIssues) { this.builderMap = builderMap; this.classpath = classpath; this.serializationCache = SerializationCaches.getDefault(); if (serializationIssues.isEmpty()) { this.serializationIssues = Collections.emptyMap(); } else { Map<KeyWrapper, Throwable> issues = CollectionUtils.newHashMap(serializationIssues.size()); for (Map.Entry<Object, Throwable> entry: serializationIssues.entrySet()) { issues.put((KeyWrapper)entry.getKey(), entry.getValue()); } this.serializationIssues = issues; } } private static <QueryType extends GradleInfoQuery> GradleInfoQueryMap fromQueries( Map<Object, List<QueryType>> map, BuilderRetriever<QueryType> builderRetriever) { Map<KeyWrapper, ModelClassPathDef> classpath = new HashMap<KeyWrapper, ModelClassPathDef>(32); CustomSerializedMap.Builder builders = new CustomSerializedMap.Builder(map.size()); for (Map.Entry<?, List<QueryType>> entry: map.entrySet()) { Object entryKey = entry.getKey(); int index = 0; for (QueryType query: entry.getValue()) { KeyWrapper key = new KeyWrapper(index, entryKey); builders.addValue(key, builderRetriever.getBuilder(query)); classpath.put(key, query.getInfoClassPath()); index++; } } Map<Object, Throwable> serializationIssues = new HashMap<Object, Throwable>(); CustomSerializedMap serializedBuilders = builders.create(serializationIssues); return new GradleInfoQueryMap( serializedBuilders, classpath, serializationIssues); } public static GradleInfoQueryMap fromBuildInfos(Map<Object, List<GradleBuildInfoQuery<?>>> map) { return fromQueries(map, new BuilderRetriever<GradleBuildInfoQuery<?>>() { public Object getBuilder(GradleBuildInfoQuery<?> infoQuery) { return infoQuery.getInfoBuilder(); } }); } public static GradleInfoQueryMap fromProjectInfos(Map<Object, List<GradleProjectInfoQuery2<?>>> map) { return fromQueries(map, new BuilderRetriever<GradleProjectInfoQuery2<?>>() { public Object getBuilder(GradleProjectInfoQuery2<?> infoQuery) { return infoQuery.getInfoBuilder(); } }); } private ClassLoader getClassLoaderForKey(KeyWrapper key) { ModelClassPathDef classpathDef = classpath.get(key); return classpathDef != null ? classpathDef.getClassLoader() : null; } public CustomSerializedMap getBuilderMap() { return builderMap; } public CustomSerializedMap.Deserializer getSerializableBuilderMap() { return new Deserializer(builderMap, classpath); } @SuppressWarnings("unchecked") private static Map<Object, List<?>> unsafeCast(Map<Object, List<Object>> map) { return (Map<Object, List<?>>)(Map<?, ?>)map; } public Map<Object, List<?>> deserializeResults( CustomSerializedMap map, IssueTransformer issueTransformer) { if (map == null) throw new NullPointerException("map"); if (issueTransformer == null) throw new NullPointerException("issueTransformer"); Map<Object, List<Object>> result = CollectionUtils.newHashMap(map.size()); for (Map.Entry<Object, SerializedEntries> entry: map.getMap().entrySet()) { KeyWrapper key = (KeyWrapper)entry.getKey(); List<?> values = entry.getValue().getUnserialized(serializationCache, getClassLoaderForKey(key)); MultiMapUtils.addAllToMultiMap(key.wrappedKey, values, result); } for (Map.Entry<Object, Throwable> entry: map.getSerializationProblems().entrySet()) { KeyWrapper key = (KeyWrapper)entry.getKey(); Object issue = issueTransformer.transformIssue(entry.getValue()); MultiMapUtils.addToMultiMap(key.wrappedKey, issue, result); } for (Map.Entry<KeyWrapper, Throwable> entry: serializationIssues.entrySet()) { Object key = entry.getKey().wrappedKey; Object issue = issueTransformer.transformIssue(entry.getValue()); MultiMapUtils.addToMultiMap(key, issue, result); } return unsafeCast(result); } public static IssueTransformer builderIssueTransformer() { return BuilderIssueTransformer.INSTANCE; } public static IssueTransformer buildInfoBuilderIssueTransformer() { return BuildInfoBuilderIssueTransformer.INSTANCE; } private static enum BuildInfoBuilderIssueTransformer implements IssueTransformer { INSTANCE; public Object transformIssue(Throwable issue) { return new FailingBuildInfoBuilder(issue); } } private static final class FailingBuildInfoBuilder implements BuildInfoBuilder<Void> { private static final long serialVersionUID = 1L; private final RuntimeException issue; public FailingBuildInfoBuilder(Throwable issue) { this.issue = TransferableExceptionWrapper.wrap(issue); } public Void getInfo(BuildController controller) { throw issue; } public String getName() { return ""; } } private static enum BuilderIssueTransformer implements IssueTransformer { INSTANCE; public Object transformIssue(Throwable issue) { return new BuilderResult(null, new BuilderIssue("", issue)); } } private static final class Deserializer implements CustomSerializedMap.Deserializer, Serializable { private static final long serialVersionUID = 1L; private final CustomSerializedMap builderMap; private final Map<KeyWrapper, Set<File>> paths; public Deserializer( CustomSerializedMap builderMap, Map<KeyWrapper, ModelClassPathDef> classpath) { if (builderMap == null) throw new NullPointerException("builderMap"); this.builderMap = builderMap; this.paths = CollectionUtils.newHashMap(classpath.size()); for (Map.Entry<KeyWrapper, ModelClassPathDef> entry: classpath.entrySet()) { KeyWrapper key = entry.getKey(); if (key == null) throw new NullPointerException("classpath[?].key"); this.paths.put(key, entry.getValue().getJarFiles()); } } private ClassLoader getClassLoaderForKey( KeyWrapper key, ClassLoader parent, Map<Set<File>, ClassLoader> cache) { Set<File> files = paths.get(key); if (files == null || files.isEmpty()) { return parent; } ClassLoader result = cache.get(files); if (result != null) { return result; } result = ClassLoaderUtils.classLoaderFromClassPath(files, parent); cache.put(files, result); return result; } public Map<Object, List<?>> deserialize( SerializationCache serializationCache, ClassLoader parent, IssueTransformer deserializationIssueTransformer) { Map<Set<File>, ClassLoader> cache = new HashMap<Set<File>, ClassLoader>(); Map<Object, List<?>> result = CollectionUtils.newHashMap(builderMap.size()); for (Map.Entry<Object, SerializedEntries> entry: builderMap.getMap().entrySet()) { KeyWrapper key = (KeyWrapper)entry.getKey(); ClassLoader classLoader = getClassLoaderForKey(key, parent, cache); List<?> deserializedValues; try { deserializedValues = entry.getValue().getUnserialized(serializationCache, classLoader); } catch (Throwable deserializeEx) { Object issue = deserializationIssueTransformer.transformIssue(deserializeEx); deserializedValues = Collections.singletonList(issue); } result.put(key, deserializedValues); } return result; } } private static interface BuilderRetriever<QueryType extends GradleInfoQuery> { public Object getBuilder(QueryType infoQuery); } private static final class KeyWrapper implements Serializable { private static final long serialVersionUID = 1L; private final int index; public final Object wrappedKey; public KeyWrapper(int index, Object key) { this.index = index; this.wrappedKey = key; } @Override public int hashCode() { int hash = 3; hash = 59 * hash + this.index; hash = 59 * hash + (this.wrappedKey != null ? this.wrappedKey.hashCode() : 0); return hash; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (getClass() != obj.getClass()) return false; final KeyWrapper other = (KeyWrapper)obj; if (this.index != other.index) return false; return this.wrappedKey == other.wrappedKey || (this.wrappedKey != null && this.wrappedKey.equals(other.wrappedKey)); } } }