| package org.jetbrains.jps.android; |
| |
| import com.android.sdklib.SdkManager; |
| import com.android.sdklib.repository.local.LocalSdk; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.util.containers.HashMap; |
| import com.intellij.util.containers.HashSet; |
| import org.jetbrains.android.util.ResourceEntry; |
| import org.jetbrains.android.util.ValueResourcesFileParser; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.jps.android.model.JpsAndroidModuleExtension; |
| import org.jetbrains.jps.incremental.java.FormsParsing; |
| import org.jetbrains.jps.model.java.JpsJavaClasspathKind; |
| import org.jetbrains.jps.model.java.JpsJavaExtensionService; |
| import org.jetbrains.jps.model.module.JpsDependencyElement; |
| import org.jetbrains.jps.model.module.JpsModule; |
| import org.jetbrains.jps.model.module.JpsModuleDependency; |
| |
| import java.io.*; |
| import java.util.*; |
| |
| /** |
| * @author Eugene.Kudelevsky |
| */ |
| public class AndroidBuildDataCache { |
| private static AndroidBuildDataCache ourInstance; |
| |
| private final List<LocalSdk> myLocalSdks = Lists.newArrayList(); |
| private final Map<JpsModule, MyAndroidDeps> myModule2AndroidDeps = new HashMap<JpsModule, MyAndroidDeps>(); |
| private final Map<String, List<ResourceEntry>> myParsedValueResourceFiles = new HashMap<String, List<ResourceEntry>>(); |
| |
| @NotNull |
| public static AndroidBuildDataCache getInstance() { |
| if (ourInstance == null) { |
| ourInstance = new AndroidBuildDataCache(); |
| } |
| return ourInstance; |
| } |
| |
| public static void clean() { |
| ourInstance = null; |
| } |
| |
| // If parsing throws IOException, the result it is not cached, so invoker should catch it and stop the build |
| public List<ResourceEntry> getParsedValueResourceFile(@NotNull File file) throws IOException { |
| final String path = FileUtil.toCanonicalPath(file.getPath()); |
| List<ResourceEntry> entries = myParsedValueResourceFiles.get(path); |
| |
| if (entries == null) { |
| entries = parseValueResourceFile(file); |
| myParsedValueResourceFiles.put(path, entries); |
| } |
| return entries; |
| } |
| |
| @NotNull |
| private static List<ResourceEntry> parseValueResourceFile(@NotNull File valueResXmlFile) |
| throws IOException { |
| final ArrayList<ResourceEntry> result = new ArrayList<ResourceEntry>(); |
| |
| final InputStream inputStream = new BufferedInputStream(new FileInputStream(valueResXmlFile)); |
| try { |
| FormsParsing.parse(inputStream, new ValueResourcesFileParser() { |
| @Override |
| protected void stop() { |
| throw new FormsParsing.ParserStoppedException(); |
| } |
| |
| @Override |
| protected void process(@NotNull ResourceEntry resourceEntry) { |
| result.add(resourceEntry); |
| } |
| }); |
| } |
| finally { |
| inputStream.close(); |
| } |
| return result; |
| } |
| |
| @NotNull |
| public List<JpsAndroidModuleExtension> getAllAndroidDependencies(@NotNull JpsModule module, boolean librariesOnly) { |
| MyAndroidDeps deps = myModule2AndroidDeps.get(module); |
| |
| if (deps == null) { |
| deps = computeAndroidDependencies(module); |
| myModule2AndroidDeps.put(module, deps); |
| } |
| return librariesOnly ? deps.myLibAndroidDeps : deps.myAndroidDeps; |
| } |
| |
| @NotNull |
| private static MyAndroidDeps computeAndroidDependencies(@NotNull JpsModule module) { |
| final MyAndroidDeps result = new MyAndroidDeps(); |
| final boolean recursively = AndroidJpsUtil.shouldProcessDependenciesRecursively(module); |
| collectAndroidDependencies(module, result, new HashSet<String>(), true, recursively); |
| Collections.reverse(result.myAndroidDeps); |
| Collections.reverse(result.myLibAndroidDeps); |
| return result; |
| } |
| |
| private static void collectAndroidDependencies(@NotNull JpsModule module, |
| @NotNull MyAndroidDeps result, |
| @NotNull Set<String> visitedSet, |
| boolean fillLibs, |
| boolean recursively) { |
| final List<JpsDependencyElement> dependencies = |
| new ArrayList<JpsDependencyElement>(JpsJavaExtensionService.getInstance().getDependencies( |
| module, JpsJavaClasspathKind.PRODUCTION_RUNTIME, false)); |
| |
| for (int i = dependencies.size() - 1; i >= 0; i--) { |
| final JpsDependencyElement item = dependencies.get(i); |
| |
| if (item instanceof JpsModuleDependency) { |
| final JpsModule depModule = ((JpsModuleDependency)item).getModule(); |
| if (depModule != null) { |
| final JpsAndroidModuleExtension depExtension = AndroidJpsUtil.getExtension(depModule); |
| if (depExtension != null && visitedSet.add(depModule.getName())) { |
| |
| if (recursively) { |
| final boolean newRecursively = AndroidJpsUtil.shouldProcessDependenciesRecursively(depModule); |
| collectAndroidDependencies(depModule, result, visitedSet, fillLibs && depExtension.isLibrary(), newRecursively); |
| } |
| result.myAndroidDeps.add(depExtension); |
| |
| if (fillLibs && depExtension.isLibrary()) { |
| result.myLibAndroidDeps.add(depExtension); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| @NotNull |
| public LocalSdk getSdk(@NotNull File androidSdkHomePath) { |
| for (LocalSdk sdk : myLocalSdks) { |
| if (FileUtil.filesEqual(sdk.getLocation(), androidSdkHomePath)) { |
| return sdk; |
| } |
| } |
| |
| LocalSdk newSdk = new LocalSdk(androidSdkHomePath); |
| myLocalSdks.add(0, newSdk); |
| return newSdk; |
| } |
| |
| private abstract static class MyComputedValue<T> { |
| @NotNull |
| abstract T getValue() throws ComputationException; |
| } |
| |
| private static class SuccessComputedValue<T> extends MyComputedValue<T> { |
| @NotNull |
| private final T myValue; |
| |
| private SuccessComputedValue(@NotNull T value) { |
| myValue = value; |
| } |
| |
| @NotNull |
| @Override |
| T getValue() throws ComputationException { |
| return myValue; |
| } |
| } |
| |
| private static class ErrorComputedValue<T> extends MyComputedValue<T> { |
| @NotNull |
| private final String myMessage; |
| |
| private ErrorComputedValue(@NotNull String message) { |
| myMessage = message; |
| } |
| |
| @NotNull |
| @Override |
| T getValue() throws ComputationException { |
| throw new ComputationException(myMessage); |
| } |
| } |
| |
| public static class ComputationException extends Exception { |
| public ComputationException(@NotNull String message) { |
| super(message); |
| } |
| } |
| |
| private static class MyAndroidDeps { |
| final List<JpsAndroidModuleExtension> myAndroidDeps = new ArrayList<JpsAndroidModuleExtension>(); |
| final List<JpsAndroidModuleExtension> myLibAndroidDeps = new ArrayList<JpsAndroidModuleExtension>(); |
| } |
| } |