/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle.internal.model;
import static com.android.SdkConstants.DOT_JAR;
import static com.android.SdkConstants.FD_JARS;
import com.android.annotations.NonNull;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.internal.dependency.LibraryDependencyImpl;
import com.android.build.gradle.internal.dependency.VariantDependencies;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.builder.core.AndroidBuilder;
import com.android.builder.dependency.JarDependency;
import com.android.builder.dependency.LibraryDependency;
import com.android.builder.model.AndroidLibrary;
import com.android.builder.model.Dependencies;
import com.android.builder.model.JavaLibrary;
import com.android.ide.common.caching.CreatingCache;
import com.google.common.collect.Lists;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
*/
public class DependenciesImpl implements Dependencies, Serializable {
private static final long serialVersionUID = 1L;
private static final CreatingCache<LibraryDependency, AndroidLibrary> sCache
= new CreatingCache<LibraryDependency, AndroidLibrary>(
new CreatingCache.ValueFactory<LibraryDependency, AndroidLibrary>() {
@Override
@NonNull
public AndroidLibrary create(@NonNull LibraryDependency key) {
return convertAndroidLibrary(key);
}
});
@NonNull
private final List<AndroidLibrary> libraries;
@NonNull
private final List<JavaLibrary> javaLibraries;
@NonNull
private final List<String> projects;
public static void clearCaches() {
sCache.clear();
}
@NonNull
static DependenciesImpl cloneDependenciesForJavaArtifacts(@NonNull Dependencies dependencies) {
List<AndroidLibrary> libraries = Collections.emptyList();
List<JavaLibrary> javaLibraries = Lists.newArrayList(dependencies.getJavaLibraries());
List<String> projects = Collections.emptyList();
return new DependenciesImpl(libraries, javaLibraries, projects);
}
@NonNull
static DependenciesImpl cloneDependencies(
@NonNull BaseVariantData variantData,
@NonNull AndroidBuilder androidBuilder) {
VariantDependencies variantDependencies = variantData.getVariantDependency();
List<AndroidLibrary> libraries;
List<JavaLibrary> javaLibraries;
List<String> projects;
List<LibraryDependencyImpl> libs = variantDependencies.getLibraries();
libraries = Lists.newArrayListWithCapacity(libs.size());
for (LibraryDependencyImpl libImpl : libs) {
AndroidLibrary clonedLib = sCache.get(libImpl);
if (clonedLib != null) {
libraries.add(clonedLib);
}
}
List<JarDependency> jarDeps = variantDependencies.getJarDependencies();
List<JarDependency> localDeps = variantDependencies.getLocalDependencies();
javaLibraries = Lists.newArrayListWithExpectedSize(jarDeps.size() + localDeps.size());
projects = Lists.newArrayList();
for (JarDependency jarDep : jarDeps) {
// don't include package-only dependencies
if (jarDep.isCompiled()) {
boolean customArtifact = jarDep.getResolvedCoordinates() != null &&
jarDep.getResolvedCoordinates().getClassifier() != null;
File jarFile = jarDep.getJarFile();
if (!customArtifact && jarDep.getProjectPath() != null) {
projects.add(jarDep.getProjectPath());
} else {
javaLibraries.add(
new JavaLibraryImpl(jarFile, null, jarDep.getResolvedCoordinates()));
}
}
}
for (JarDependency jarDep : localDeps) {
// don't include package-only dependencies
if (jarDep.isCompiled()) {
javaLibraries.add(
new JavaLibraryImpl(
jarDep.getJarFile(),
null,
jarDep.getResolvedCoordinates()));
}
}
GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
if (variantConfig.getRenderscriptSupportModeEnabled()) {
File supportJar = androidBuilder.getRenderScriptSupportJar();
if (supportJar != null) {
javaLibraries.add(new JavaLibraryImpl(supportJar, null, null));
}
}
return new DependenciesImpl(libraries, javaLibraries, projects);
}
public DependenciesImpl(@NonNull Set<JavaLibrary> javaLibraries) {
this.javaLibraries = Lists.newArrayList(javaLibraries);
this.libraries = Collections.emptyList();
this.projects = Collections.emptyList();
}
private DependenciesImpl(@NonNull List<AndroidLibrary> libraries,
@NonNull List<JavaLibrary> javaLibraries,
@NonNull List<String> projects) {
this.libraries = libraries;
this.javaLibraries = javaLibraries;
this.projects = projects;
}
@NonNull
@Override
public Collection<AndroidLibrary> getLibraries() {
return libraries;
}
@NonNull
@Override
public Collection<JavaLibrary> getJavaLibraries() {
return javaLibraries;
}
@NonNull
@Override
public List<String> getProjects() {
return projects;
}
@NonNull
private static AndroidLibrary convertAndroidLibrary(
@NonNull LibraryDependency libraryDependency) {
List<LibraryDependency> deps = libraryDependency.getDependencies();
List<AndroidLibrary> clonedDeps = Lists.newArrayListWithCapacity(deps.size());
for (LibraryDependency child : deps) {
AndroidLibrary clonedLib = sCache.get(child);
if (clonedLib != null) {
clonedDeps.add(clonedLib);
}
}
// compute local jar even if the bundle isn't exploded.
Collection<File> localJarOverride = findLocalJar(libraryDependency);
return new AndroidLibraryImpl(
libraryDependency,
clonedDeps,
localJarOverride,
libraryDependency.getProject(),
libraryDependency.getProjectVariant(),
libraryDependency.getRequestedCoordinates(),
libraryDependency.getResolvedCoordinates());
}
/**
* Finds the local jar for an aar.
*
* Since the model can be queried before the aar are exploded, we attempt to get them
* from inside the aar.
*
* @param library the library.
* @return its local jars.
*/
@NonNull
private static Collection<File> findLocalJar(LibraryDependency library) {
// if the library is exploded, just use the normal method.
File explodedFolder = library.getFolder();
if (explodedFolder.isDirectory()) {
return library.getLocalJars();
}
// if the aar file is present, search inside it for jar files under libs/
File aarFile = library.getBundle();
if (aarFile.isFile()) {
List<File> jarList = Lists.newArrayList();
File jarsFolder = new File(explodedFolder, FD_JARS);
ZipFile zipFile = null;
try {
//noinspection IOResourceOpenedButNotSafelyClosed
zipFile = new ZipFile(aarFile);
for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements();) {
ZipEntry zipEntry = e.nextElement();
String name = zipEntry.getName();
if (name.startsWith("libs/") && name.endsWith(DOT_JAR)) {
jarList.add(new File(jarsFolder, name.replace('/', File.separatorChar)));
}
}
return jarList;
} catch (FileNotFoundException ignored) {
// should not happen since we check ahead of time
} catch (IOException e) {
// we'll return an empty list below
} finally {
if (zipFile != null) {
try {
zipFile.close();
} catch (IOException ignored) {
}
}
}
}
return Collections.emptyList();
}
}