diff options
Diffstat (limited to 'sdkmanager')
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(); } |