package org.robolectric.internal;
import org.robolectric.annotation.Config;
import org.robolectric.manifest.AndroidManifest;
import org.robolectric.res.Fs;
import org.robolectric.res.FsFile;
import org.robolectric.util.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class MavenManifestFactory implements ManifestFactory {
@Override
public ManifestIdentifier identify(Config config) {
if (config.manifest().equals(Config.NONE)) {
return new ManifestIdentifier(null, null, null, null, null);
}
FsFile manifestFile = getBaseDir().join(config.manifest());
FsFile baseDir = manifestFile.getParent();
FsFile resDir = baseDir.join(config.resourceDir());
FsFile assetDir = baseDir.join(config.assetDir());
List<FsFile> libraryDirs = null;
if (config.libraries().length > 0) {
libraryDirs = new ArrayList<>();
for (String libraryDirName : config.libraries()) {
libraryDirs.add(baseDir.join(libraryDirName));
}
}
return new ManifestIdentifier(manifestFile, resDir, assetDir, config.packageName(), libraryDirs);
}
@Override
public AndroidManifest create(ManifestIdentifier manifestIdentifier) {
AndroidManifest appManifest;
FsFile manifestFile = manifestIdentifier.getManifestFile();
if (manifestFile == null) {
appManifest = createDummyManifest(manifestIdentifier.getPackageName());
} else if (!manifestFile.exists()) {
System.out.println("WARNING: No manifest file found at " + manifestFile.getPath() + ".");
System.out.println("Falling back to the Android OS resources only.");
System.out.println("To remove this warning, annotate your test class with @Config(manifest=Config.NONE).");
appManifest = createDummyManifest(manifestIdentifier.getPackageName());
} else {
FsFile resDir = manifestIdentifier.getResDir();
FsFile assetDir = manifestIdentifier.getAssetDir();
String packageName = manifestIdentifier.getPackageName();
Logger.debug("Robolectric assets directory: " + assetDir.getPath());
Logger.debug(" Robolectric res directory: " + resDir.getPath());
Logger.debug(" Robolectric manifest path: " + manifestFile.getPath());
Logger.debug(" Robolectric package name: " + packageName);
appManifest = new AndroidManifest(manifestFile, resDir, assetDir, packageName);
}
List<FsFile> libraryDirs = manifestIdentifier.getLibraryDirs();
appManifest.setLibraryManifests(createLibraryManifests(appManifest, libraryDirs));
return appManifest;
}
private AndroidManifest createDummyManifest(String packageName) {
if (packageName == null || packageName.equals("")) {
packageName = "org.robolectric.default";
}
return new AndroidManifest(null, null, null, packageName) {
@Override
public int getTargetSdkVersion() {
return SdkConfig.FALLBACK_SDK_VERSION;
}
};
}
FsFile getBaseDir() {
return Fs.currentDirectory();
}
/**
* Search through an AndroidManifest's library directories to load library AppManifest files.
* For testing, allow a parameter override of the library directories.
* @param androidManifest The main AppManifest which may have library resources.
* @param libraryDirectories If not null, override the libraries in androidManifest.
* @return A list of AndroidManifest objects, one for each library found.
*/
private static List<AndroidManifest> createLibraryManifests(
AndroidManifest androidManifest,
List<FsFile> libraryDirectories) {
List<AndroidManifest> libraryManifests = new ArrayList<>();
if (androidManifest != null) {
// If there is no library override, look through subdirectories.
if (libraryDirectories == null) {
libraryDirectories = findLibraries(androidManifest);
}
for (FsFile libraryBaseDir : libraryDirectories) {
AndroidManifest libraryManifest = createLibraryAndroidManifest(libraryBaseDir);
libraryManifest.setLibraryManifests(
createLibraryManifests(libraryManifest, null));
libraryManifests.add(libraryManifest);
}
}
return libraryManifests;
}
private static AndroidManifest createLibraryAndroidManifest(FsFile libraryBaseDir) {
return new AndroidManifest(libraryBaseDir.join(Config.DEFAULT_MANIFEST_NAME), libraryBaseDir.join(Config.DEFAULT_RES_FOLDER), libraryBaseDir.join(Config.DEFAULT_ASSET_FOLDER));
}
private static Properties getProperties(FsFile propertiesFile) {
Properties properties = new Properties();
// return an empty Properties object if the propertiesFile does not exist
if (!propertiesFile.exists()) return properties;
InputStream stream;
try {
stream = propertiesFile.getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
try {
properties.load(stream);
} finally {
stream.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return properties;
}
/**
* Find valid library AndroidManifest files referenced from an already loaded AndroidManifest's
* "project.properties" file.
* @param androidManifest
*/
private static List<FsFile> findLibraries(AndroidManifest androidManifest) {
List<FsFile> libraryBaseDirs = new ArrayList<>();
if (androidManifest.getResDirectory() != null) {
FsFile baseDir = androidManifest.getResDirectory().getParent();
final Properties properties = getProperties(baseDir.join("project.properties"));
Properties overrideProperties = getProperties(baseDir.join("test-project.properties"));
properties.putAll(overrideProperties);
int libRef = 1;
String lib;
while ((lib = properties.getProperty("android.library.reference." + libRef)) != null) {
FsFile libraryBaseDir = baseDir.join(lib);
if (libraryBaseDir.isDirectory()) {
// Ignore directories without any files
FsFile[] libraryBaseDirFiles = libraryBaseDir.listFiles();
if (libraryBaseDirFiles != null && libraryBaseDirFiles.length > 0) {
libraryBaseDirs.add(libraryBaseDir);
}
}
libRef++;
}
}
return libraryBaseDirs;
}
}