aboutsummaryrefslogtreecommitdiffstats
path: root/sdkmanager/libs/sdklib
diff options
context:
space:
mode:
Diffstat (limited to 'sdkmanager/libs/sdklib')
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java37
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java11
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java407
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java10
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java76
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java20
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java37
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java14
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java25
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java10
10 files changed, 593 insertions, 54 deletions
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
index 519e8fb..71fcee5 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
@@ -19,6 +19,9 @@ package com.android.sdklib;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.AndroidVersion.AndroidVersionException;
+import com.android.sdklib.io.FileWrapper;
+import com.android.sdklib.io.IAbstractFile;
+import com.android.sdklib.io.StreamException;
import java.io.BufferedReader;
import java.io.File;
@@ -613,13 +616,26 @@ public final class SdkManager {
* @param propFile the property file to parse
* @param log the ISdkLog object receiving warning/error from the parsing. Cannot be null.
* @return the map of (key,value) pairs, or null if the parsing failed.
+ * @deprecated Use {@link #parsePropertyFile(IAbstractFile, ISdkLog)}
*/
public static Map<String, String> parsePropertyFile(File propFile, ISdkLog log) {
- FileInputStream fis = null;
+ IAbstractFile wrapper = new FileWrapper(propFile);
+ return parsePropertyFile(wrapper, log);
+ }
+
+ /**
+ * Parses a property file (using UTF-8 encoding) and returns a map of the content.
+ * <p/>If the file is not present, null is returned with no error messages sent to the log.
+ *
+ * @param propFile the property file to parse
+ * @param log the ISdkLog object receiving warning/error from the parsing. Cannot be null.
+ * @return the map of (key,value) pairs, or null if the parsing failed.
+ */
+ public static Map<String, String> parsePropertyFile(IAbstractFile propFile, ISdkLog log) {
BufferedReader reader = null;
try {
- fis = new FileInputStream(propFile);
- reader = new BufferedReader(new InputStreamReader(fis, SdkConstants.INI_CHARSET));
+ reader = new BufferedReader(new InputStreamReader(propFile.getContents(),
+ SdkConstants.INI_CHARSET));
String line = null;
Map<String, String> map = new HashMap<String, String>();
@@ -631,7 +647,7 @@ public final class SdkManager {
map.put(m.group(1), m.group(2));
} else {
log.warning("Error parsing '%1$s': \"%2$s\" is not a valid syntax",
- propFile.getAbsolutePath(),
+ propFile.getOsLocation(),
line);
return null;
}
@@ -645,7 +661,11 @@ public final class SdkManager {
// Return null below.
} catch (IOException e) {
log.warning("Error parsing '%1$s': %2$s.",
- propFile.getAbsolutePath(),
+ propFile.getOsLocation(),
+ e.getMessage());
+ } catch (StreamException e) {
+ log.warning("Error parsing '%1$s': %2$s.",
+ propFile.getOsLocation(),
e.getMessage());
} finally {
if (reader != null) {
@@ -655,13 +675,6 @@ public final class SdkManager {
// pass
}
}
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
- // pass
- }
- }
}
return null;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java
index 50acebc..9c3a8c5 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java
@@ -19,6 +19,7 @@ package com.android.sdklib.internal.export;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.OutputStreamWriter;
/**
* Class representing one apk that needs to be generated. This contains
@@ -174,12 +175,12 @@ public class ApkData implements Comparable<ApkData> {
/**
* Writes the apk description in the given writer. a single line is used to write
* everything.
- * @param writer The {@link FileWriter} to write to.
+ * @param writer The {@link OutputStreamWriter} to write to.
* @throws IOException
*
* @see {@link #read(String)}
*/
- public void write(FileWriter writer) throws IOException {
+ public void write(OutputStreamWriter writer) throws IOException {
for (int i = 0 ; i < ApkData.INDEX_MAX ; i++) {
write(i, writer);
}
@@ -198,7 +199,7 @@ public class ApkData implements Comparable<ApkData> {
}
}
- private void write(int index, FileWriter writer) throws IOException {
+ private void write(int index, OutputStreamWriter writer) throws IOException {
switch (index) {
case INDEX_OUTPUTNAME:
writeValue(writer, mOutputName);
@@ -240,11 +241,11 @@ public class ApkData implements Comparable<ApkData> {
}
}
- private static void writeValue(FileWriter writer, String value) throws IOException {
+ private static void writeValue(OutputStreamWriter writer, String value) throws IOException {
writer.append(value).append(',');
}
- private static void writeValue(FileWriter writer, int value) throws IOException {
+ private static void writeValue(OutputStreamWriter writer, int value) throws IOException {
writeValue(writer, Integer.toString(value));
}
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
new file mode 100644
index 0000000..72014e4
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2010 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.sdklib.internal.export;
+
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.project.ApkSettings;
+import com.android.sdklib.internal.project.ProjectProperties;
+import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
+import com.android.sdklib.io.FileWrapper;
+import com.android.sdklib.io.IAbstractFile;
+import com.android.sdklib.io.IAbstractFolder;
+import com.android.sdklib.io.IAbstractResource;
+import com.android.sdklib.io.StreamException;
+import com.android.sdklib.io.IAbstractFolder.FilenameFilter;
+import com.android.sdklib.xml.AndroidManifest;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.xml.xpath.XPathExpressionException;
+
+public class MultiApkExportHelper {
+
+ private final String mAppPackage;
+ private final int mVersionCode;
+ private final Target mTarget;
+
+ public final static int MAX_MINOR = 100;
+ public final static int MAX_BUILDINFO = 100;
+ public final static int OFFSET_BUILD_INFO = MAX_MINOR;
+ public final static int OFFSET_VERSION_CODE = OFFSET_BUILD_INFO * MAX_BUILDINFO;
+
+ public static final class ExportException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public ExportException(String message) {
+ super(message);
+ }
+
+ public ExportException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ public static enum Target {
+ RELEASE("release"), CLEAN("clean");
+
+ private final String mName;
+
+ Target(String name) {
+ mName = name;
+ }
+
+ public String getTarget() {
+ return mName;
+ }
+
+ public static Target getTarget(String value) {
+ for (Target t : values()) {
+ if (t.mName.equals(value)) {
+ return t;
+ }
+
+ }
+
+ return null;
+ }
+ }
+
+ public MultiApkExportHelper(String appPackage, int versionCode, Target target) {
+ mAppPackage = appPackage;
+ mVersionCode = versionCode;
+ mTarget = target;
+ }
+
+ public ApkData[] getProjects(String projects, IAbstractFile buildLog) throws ExportException {
+ // get the list of apk to export and their configuration.
+ ApkData[] apks = getProjects(projects);
+
+ // look to see if there's an export log from a previous export
+ if (mTarget == Target.RELEASE && buildLog != null && buildLog.exists()) {
+ // load the log and compare to current export list.
+ // Any difference will force a new versionCode.
+ ApkData[] previousApks = getProjects(buildLog);
+
+ if (previousApks.length != apks.length) {
+ throw new ExportException(String.format(
+ "Project export is setup differently from previous export at versionCode %d.\n" +
+ "Any change in the multi-apk configuration requires an increment of the versionCode.",
+ mVersionCode));
+ }
+
+ for (int i = 0 ; i < previousApks.length ; i++) {
+ // update the minor value from what is in the log file.
+ apks[i].setMinor(previousApks[i].getMinor());
+ if (apks[i].compareTo(previousApks[i]) != 0) {
+ throw new ExportException(String.format(
+ "Project export is setup differently from previous export at versionCode %d.\n" +
+ "Any change in the multi-apk configuration requires an increment of the versionCode.",
+ mVersionCode));
+ }
+ }
+ }
+
+ return apks;
+
+ }
+
+ /**
+ * Writes the build log for a given list of {@link ApkData}.
+ * @param buildLog the build log file into which to write the log.
+ * @param apks the list of apks that were exported.
+ * @throws ExportException
+ */
+ public void makeBuildLog(IAbstractFile buildLog, ApkData[] apks) throws ExportException {
+ OutputStreamWriter writer = null;
+ try {
+ writer = new OutputStreamWriter(buildLog.getOutputStream());
+
+ writer.append("# Multi-APK BUILD log.\n");
+ writer.append("# Only edit manually to change minor versions.\n");
+
+ writeValue(writer, "package", mAppPackage);
+ writeValue(writer, "versionCode", mVersionCode);
+
+ writer.append("# what follows is one line per generated apk with its description.\n");
+ writer.append("# the format is CSV in the following order:\n");
+ writer.append("# apkname,project,minor, minsdkversion, abi filter,\n");
+
+ for (ApkData apk : apks) {
+ apk.write(writer);
+ writer.append('\n');
+ }
+
+ writer.flush();
+ } catch (Exception e) {
+ throw new ExportException("Failed to write build log", e);
+ } finally {
+ try {
+ if (writer != null) {
+ writer.close();
+ }
+ } catch (IOException e) {
+ throw new ExportException("Failed to write build log", e);
+ }
+ }
+ }
+
+ private void writeValue(OutputStreamWriter writer, String name, String value)
+ throws IOException {
+ writer.append(name).append('=').append(value).append('\n');
+ }
+
+ private void writeValue(OutputStreamWriter writer, String name, int value) throws IOException {
+ writeValue(writer, name, Integer.toString(value));
+ }
+
+ /**
+ * gets the projects to export from the property, checks they exist, validates them,
+ * loads their export info and return it.
+ * If a project does not exist or is not valid, this will throw a {@link BuildException}.
+ * @param projects the Ant project.
+ * @throws ExportException
+ */
+ private ApkData[] getProjects(String projects) throws ExportException {
+ String[] paths = projects.split("\\:");
+
+ ArrayList<ApkData> datalist = new ArrayList<ApkData>();
+
+ for (String path : paths) {
+ File projectFolder = new File(path);
+
+ // resolve the path (to remove potential ..)
+ try {
+ projectFolder = projectFolder.getCanonicalFile();
+
+ // project folder must exist and be a directory
+ if (projectFolder.isDirectory() == false) {
+ throw new ExportException(String.format(
+ "Project folder '%1$s' is not a valid directory.",
+ projectFolder.getAbsolutePath()));
+ }
+
+ // Check AndroidManifest.xml is present
+ FileWrapper androidManifest = new FileWrapper(projectFolder,
+ SdkConstants.FN_ANDROID_MANIFEST_XML);
+
+ if (androidManifest.isFile() == false) {
+ throw new ExportException(String.format(
+ "%1$s is not a valid project (%2$s not found).",
+ projectFolder.getAbsolutePath(),
+ SdkConstants.FN_ANDROID_MANIFEST_XML));
+ }
+
+ ArrayList<ApkData> datalist2 = checkManifest(androidManifest);
+
+ // if the method returns without throwing, this is a good project to
+ // export.
+ for (ApkData data : datalist2) {
+ data.setRelativePath(path);
+ data.setProject(projectFolder);
+ }
+
+ datalist.addAll(datalist2);
+
+ } catch (IOException e) {
+ throw new ExportException(String.format("Failed to resolve path %1$s", path), e);
+ }
+ }
+
+ // sort the projects and assign buildInfo
+ Collections.sort(datalist);
+ int buildInfo = 0;
+ for (ApkData data : datalist) {
+ data.setBuildInfo(buildInfo++);
+ }
+
+ return datalist.toArray(new ApkData[datalist.size()]);
+ }
+
+ /**
+ * Checks a manifest of the project for inclusion in the list of exported APK.
+ * If the manifest is correct, a list of apk to export is created and returned.
+ *
+ * @param androidManifest the manifest to check
+ * @return A new non-null {@link ArrayList} of {@link ApkData}.
+ * @throws ExportException in case of error.
+ */
+ private ArrayList<ApkData> checkManifest(IAbstractFile androidManifest) throws ExportException {
+ try {
+ String manifestPackage = AndroidManifest.getPackage(androidManifest);
+ if (mAppPackage.equals(manifestPackage) == false) {
+ throw new ExportException(String.format(
+ "%1$s package value is not valid. Found '%2$s', expected '%3$s'.",
+ androidManifest.getOsLocation(), manifestPackage, mAppPackage));
+ }
+
+ if (AndroidManifest.hasVersionCode(androidManifest)) {
+ throw new ExportException(String.format(
+ "%1$s is not valid: versionCode must not be set for multi-apk export.",
+ androidManifest.getOsLocation()));
+ }
+
+ int minSdkVersion = AndroidManifest.getMinSdkVersion(androidManifest);
+ if (minSdkVersion == -1) {
+ throw new ExportException(
+ "Codename in minSdkVersion is not supported by multi-apk export.");
+ }
+
+ ArrayList<ApkData> dataList = new ArrayList<ApkData>();
+ ApkData data = new ApkData();
+ dataList.add(data);
+ data.setMinSdkVersion(minSdkVersion);
+
+ // only look for more exports if the target is not clean.
+ if (mTarget != Target.CLEAN) {
+ // load the project properties
+ IAbstractFolder projectFolder = androidManifest.getParentFolder();
+ ProjectProperties projectProp = ProjectProperties.load(projectFolder,
+ PropertyType.DEFAULT);
+ if (projectProp == null) {
+ throw new ExportException(String.format(
+ "%1$s is missing.", PropertyType.DEFAULT.getFilename()));
+ }
+
+ ApkSettings apkSettings = new ApkSettings(projectProp);
+ if (apkSettings.isSplitByAbi()) {
+ // need to find the available ABIs.
+ List<String> abis = findAbis(projectFolder);
+ ApkData current = data;
+ for (String abi : abis) {
+ if (current == null) {
+ current = new ApkData(data);
+ dataList.add(current);
+ }
+
+ current.setAbi(abi);
+ current = null;
+ }
+ }
+ }
+
+ return dataList;
+ } catch (XPathExpressionException e) {
+ throw new ExportException(
+ String.format("Failed to validate %1$s", androidManifest.getOsLocation()), e);
+ } catch (StreamException e) {
+ throw new ExportException(
+ String.format("Failed to validate %1$s", androidManifest.getOsLocation()), e);
+ }
+ }
+
+ /**
+ * Loads and returns a list of {@link ApkData} from a build log.
+ * @param log
+ * @return A new non-null, possibly empty, array of {@link ApkData}.
+ * @throws ExportException
+ * @throws BuildException in case of error.
+ */
+ private ApkData[] getProjects(IAbstractFile buildLog) throws ExportException {
+ ArrayList<ApkData> datalist = new ArrayList<ApkData>();
+
+ InputStreamReader reader = null;
+ BufferedReader bufferedReader = null;
+ try {
+ reader = new InputStreamReader(buildLog.getContents());
+ bufferedReader = new BufferedReader(reader);
+ String line;
+ int lineIndex = 0;
+ int apkIndex = 0;
+ while ((line = bufferedReader.readLine()) != null) {
+ line = line.trim();
+ if (line.length() == 0 || line.startsWith("#")) {
+ continue;
+ }
+
+ switch (lineIndex) {
+ case 0:
+ // read package value
+ lineIndex++;
+ break;
+ case 1:
+ // read versionCode value
+ lineIndex++;
+ break;
+ default:
+ // read apk description
+ ApkData data = new ApkData();
+ data.setBuildInfo(apkIndex++);
+ datalist.add(data);
+ data.read(line);
+ if (data.getMinor() >= MAX_MINOR) {
+ throw new ExportException(
+ "Valid minor version code values are 0-" + (MAX_MINOR-1));
+ }
+ break;
+ }
+ }
+ } catch (IOException e) {
+ throw new ExportException("Failed to read existing build log", e);
+ } catch (StreamException e) {
+ throw new ExportException("Failed to read existing build log", e);
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ } catch (IOException e) {
+ throw new ExportException("Failed to read existing build log", e);
+ }
+ }
+
+ return datalist.toArray(new ApkData[datalist.size()]);
+ }
+
+ /**
+ * Finds ABIs in a project folder. This is based on the presence of libs/<abi>/ folder.
+ *
+ * @param projectPath The OS path of the project.
+ * @return A new non-null, possibly empty, list of ABI strings.
+ */
+ private List<String> findAbis(IAbstractFolder projectFolder) {
+ ArrayList<String> abiList = new ArrayList<String>();
+ IAbstractFolder libs = projectFolder.getFolder(SdkConstants.FD_NATIVE_LIBS);
+ if (libs.exists()) {
+ IAbstractResource[] abis = libs.listMembers();
+ for (IAbstractResource abi : abis) {
+ if (abi instanceof IAbstractFolder && abi.exists()) {
+ // only add the abi folder if there are .so files in it.
+ String[] content = ((IAbstractFolder)abi).list(new FilenameFilter() {
+ public boolean accept(IAbstractFolder dir, String name) {
+ return name.toLowerCase().endsWith(".so");
+ }
+ });
+
+ if (content.length > 0) {
+ abiList.add(abi.getName());
+ }
+ }
+ }
+ }
+
+ return abiList;
+ }
+
+
+}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
index 7b22e3d..ff32a6c 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java
@@ -391,9 +391,7 @@ public class ProjectCreator {
installTemplate("build.template",
new File(projectFolder, SdkConstants.FN_BUILD_XML),
keywords);
- } catch (ProjectCreateException e) {
- mLog.error(e, null);
- } catch (IOException e) {
+ } catch (Exception e) {
mLog.error(e, null);
}
}
@@ -517,7 +515,7 @@ public class ProjectCreator {
try {
props.save();
println("Updated %1$s", PropertyType.DEFAULT.getFilename());
- } catch (IOException e) {
+ } catch (Exception e) {
mLog.error(e, "Failed to write %1$s file in '%2$s'",
PropertyType.DEFAULT.getFilename(),
folderPath);
@@ -538,7 +536,7 @@ public class ProjectCreator {
try {
props.save();
println("Updated %1$s", PropertyType.LOCAL.getFilename());
- } catch (IOException e) {
+ } catch (Exception e) {
mLog.error(e, "Failed to write %1$s file in '%2$s'",
PropertyType.LOCAL.getFilename(),
folderPath);
@@ -718,7 +716,7 @@ public class ProjectCreator {
try {
buildProps.save();
println("Updated %1$s", PropertyType.BUILD.getFilename());
- } catch (IOException e) {
+ } catch (Exception e) {
mLog.error(e, "Failed to write %1$s file in '%2$s'",
PropertyType.BUILD.getFilename(),
folderPath);
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
index 734efda..d05c9f6 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java
@@ -18,9 +18,11 @@ package com.android.sdklib.internal.project;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
+import com.android.sdklib.io.FolderWrapper;
+import com.android.sdklib.io.IAbstractFile;
+import com.android.sdklib.io.IAbstractFolder;
+import com.android.sdklib.io.StreamException;
-import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
@@ -129,7 +131,7 @@ public final class ProjectProperties {
"# Used by the 'uninstall' rule.\n");
}
- private final String mProjectFolderOsPath;
+ private final IAbstractFolder mProjectFolder;
private final Map<String, String> mProperties;
private final PropertyType mType;
@@ -141,13 +143,24 @@ public final class ProjectProperties {
* @param type One the possible {@link PropertyType}s.
*/
public static ProjectProperties load(String projectFolderOsPath, PropertyType type) {
- File projectFolder = new File(projectFolderOsPath);
- if (projectFolder.isDirectory()) {
- File defaultFile = new File(projectFolder, type.mFilename);
- if (defaultFile.isFile()) {
- Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */);
+ IAbstractFolder wrapper = new FolderWrapper(projectFolderOsPath);
+ return load(wrapper, type);
+ }
+
+ /**
+ * Loads a project properties file and return a {@link ProjectProperties} object
+ * containing the properties
+ *
+ * @param projectFolder the project folder.
+ * @param type One the possible {@link PropertyType}s.
+ */
+ public static ProjectProperties load(IAbstractFolder projectFolder, PropertyType type) {
+ if (projectFolder.exists()) {
+ IAbstractFile propFile = projectFolder.getFile(type.mFilename);
+ if (propFile.exists()) {
+ Map<String, String> map = SdkManager.parsePropertyFile(propFile, null /* log */);
if (map != null) {
- return new ProjectProperties(projectFolderOsPath, map, type);
+ return new ProjectProperties(projectFolder, map, type);
}
}
}
@@ -172,11 +185,10 @@ public final class ProjectProperties {
* @return this object, for chaining.
*/
public synchronized ProjectProperties merge(PropertyType type) {
- File projectFolder = new File(mProjectFolderOsPath);
- if (projectFolder.isDirectory()) {
- File defaultFile = new File(projectFolder, type.mFilename);
- if (defaultFile.isFile()) {
- Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */);
+ if (mProjectFolder.exists()) {
+ IAbstractFile propFile = mProjectFolder.getFile(type.mFilename);
+ if (propFile.exists()) {
+ Map<String, String> map = SdkManager.parsePropertyFile(propFile, null /* log */);
if (map != null) {
for(Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
@@ -195,11 +207,23 @@ public final class ProjectProperties {
* Creates a new project properties object, with no properties.
* <p/>The file is not created until {@link #save()} is called.
* @param projectFolderOsPath the project folder.
- * @param type
+ * @param type the type of property file to create
*/
public static ProjectProperties create(String projectFolderOsPath, PropertyType type) {
// create and return a ProjectProperties with an empty map.
- return new ProjectProperties(projectFolderOsPath, new HashMap<String, String>(), type);
+ IAbstractFolder folder = new FolderWrapper(projectFolderOsPath);
+ return create(folder, type);
+ }
+
+ /**
+ * Creates a new project properties object, with no properties.
+ * <p/>The file is not created until {@link #save()} is called.
+ * @param projectFolder the project folder.
+ * @param type the type of property file to create
+ */
+ public static ProjectProperties create(IAbstractFolder projectFolder, PropertyType type) {
+ // create and return a ProjectProperties with an empty map.
+ return new ProjectProperties(projectFolder, new HashMap<String, String>(), type);
}
/**
@@ -249,11 +273,10 @@ public final class ProjectProperties {
* Reloads the properties from the underlying file.
*/
public synchronized void reload() {
- File projectFolder = new File(mProjectFolderOsPath);
- if (projectFolder.isDirectory()) {
- File defaultFile = new File(projectFolder, mType.mFilename);
- if (defaultFile.isFile()) {
- Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */);
+ if (mProjectFolder.exists()) {
+ IAbstractFile propFile = mProjectFolder.getFile(mType.mFilename);
+ if (propFile.exists()) {
+ Map<String, String> map = SdkManager.parsePropertyFile(propFile, null /* log */);
if (map != null) {
mProperties.clear();
mProperties.putAll(map);
@@ -265,11 +288,12 @@ public final class ProjectProperties {
/**
* Saves the property file, using UTF-8 encoding.
* @throws IOException
+ * @throws StreamException
*/
- public synchronized void save() throws IOException {
- File toSave = new File(mProjectFolderOsPath, mType.mFilename);
+ public synchronized void save() throws IOException, StreamException {
+ IAbstractFile toSave = mProjectFolder.getFile(mType.mFilename);
- OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(toSave),
+ OutputStreamWriter writer = new OutputStreamWriter(toSave.getOutputStream(),
SdkConstants.INI_CHARSET);
// write the header
@@ -298,9 +322,9 @@ public final class ProjectProperties {
* Use {@link #load(String, PropertyType)} or {@link #create(String, PropertyType)}
* to instantiate.
*/
- private ProjectProperties(String projectFolderOsPath, Map<String, String> map,
+ private ProjectProperties(IAbstractFolder projectFolder, Map<String, String> map,
PropertyType type) {
- mProjectFolderOsPath = projectFolderOsPath;
+ mProjectFolder = projectFolder;
mProperties = map;
mType = type;
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java
index 13dea12..9a0a4a6 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java
@@ -23,6 +23,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.net.URI;
/**
@@ -115,6 +116,17 @@ public class FileWrapper extends File implements IAbstractFile {
}
}
+ public OutputStream getOutputStream() throws StreamException {
+ try {
+ return new FileOutputStream(this);
+ } catch (FileNotFoundException e) {
+ throw new StreamException(e);
+ }
+ }
+
+ public PreferredWriteMode getPreferredWriteMode() {
+ return PreferredWriteMode.OUTPUTSTREAM;
+ }
public String getOsLocation() {
return getAbsolutePath();
@@ -124,4 +136,12 @@ public class FileWrapper extends File implements IAbstractFile {
public boolean exists() {
return isFile();
}
+
+ public IAbstractFolder getParentFolder() {
+ String p = this.getParent();
+ if (p == null) {
+ return null;
+ }
+ return new FolderWrapper(p);
+ }
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java
index 97cfad2..d009b7f 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java
@@ -18,8 +18,8 @@ package com.android.sdklib.io;
import java.io.File;
-import java.io.FilenameFilter;
import java.net.URI;
+import java.util.ArrayList;
/**
* An implementation of {@link IAbstractFolder} extending {@link File}.
@@ -100,7 +100,7 @@ public class FolderWrapper extends File implements IAbstractFolder {
public boolean hasFile(final String name) {
String[] match = list(new FilenameFilter() {
- public boolean accept(File dir, String filename) {
+ public boolean accept(IAbstractFolder dir, String filename) {
return name.equals(filename);
}
});
@@ -112,8 +112,41 @@ public class FolderWrapper extends File implements IAbstractFolder {
return new FileWrapper(this, name);
}
+ public IAbstractFolder getFolder(String name) {
+ return new FolderWrapper(this, name);
+ }
+
+ public IAbstractFolder getParentFolder() {
+ String p = this.getParent();
+ if (p == null) {
+ return null;
+ }
+ return new FolderWrapper(p);
+ }
+
+ public String getOsLocation() {
+ return getAbsolutePath();
+ }
+
@Override
public boolean exists() {
return isDirectory();
}
+
+ public String[] list(FilenameFilter filter) {
+ File[] files = listFiles();
+ if (files.length > 0) {
+ ArrayList<String> list = new ArrayList<String>();
+
+ for (File file : files) {
+ if (filter.accept(this, file.getName())) {
+ list.add(file.getName());
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+ return new String[0];
+ }
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java
index a1fe667..2ff1fc8 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java
@@ -17,11 +17,15 @@
package com.android.sdklib.io;
import java.io.InputStream;
+import java.io.OutputStream;
/**
* A file.
*/
public interface IAbstractFile extends IAbstractResource {
+ public static enum PreferredWriteMode {
+ INPUTSTREAM, OUTPUTSTREAM;
+ }
/**
* Returns an {@link InputStream} object on the file content.
@@ -37,7 +41,13 @@ public interface IAbstractFile extends IAbstractResource {
void setContents(InputStream source) throws StreamException;
/**
- * Returns the OS path of the file location.
+ * Returns an {@link OutputStream} to write into the file.
+ * @throws StreamException
+ */
+ OutputStream getOutputStream() throws StreamException;
+
+ /**
+ * Returns the preferred mode to write into the file.
*/
- String getOsLocation();
+ PreferredWriteMode getPreferredWriteMode();
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java
index 80a6f84..bfbc86d 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java
@@ -16,11 +16,25 @@
package com.android.sdklib.io;
-
/**
* A folder.
*/
public interface IAbstractFolder extends IAbstractResource {
+ /**
+ * Instances of classes that implement this interface are used to
+ * filter filenames.
+ */
+ public interface FilenameFilter {
+ /**
+ * Tests if a specified file should be included in a file list.
+ *
+ * @param dir the directory in which the file was found.
+ * @param name the name of the file.
+ * @return <code>true</code> if and only if the name should be
+ * included in the file list; <code>false</code> otherwise.
+ */
+ boolean accept(IAbstractFolder dir, String name);
+ }
/**
* Returns true if the receiver contains a file with a given name
@@ -37,7 +51,16 @@ public interface IAbstractFolder extends IAbstractResource {
IAbstractFile getFile(String name);
/**
+ * returns an {@link IAbstractFolder} representing a child of the current folder with the
+ * given name. The folder may not actually exist.
+ * @param name the name of the folder.
+ */
+ IAbstractFolder getFolder(String name);
+
+ /**
* returns a list of existing members in this folder.
*/
IAbstractResource[] listMembers();
+
+ String[] list(FilenameFilter filter);
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java
index ccc4988..0ccb107 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java
@@ -29,7 +29,17 @@ public interface IAbstractResource {
String getName();
/**
+ * Returns the OS path of the folder location.
+ */
+ String getOsLocation();
+
+ /**
* Returns whether the resource actually exists.
*/
boolean exists();
+
+ /**
+ * Returns the parent folder or null if there is no parent.
+ */
+ IAbstractFolder getParentFolder();
}