OSDN Git Service

SDK Manager: support extras/vendor/path
authorRaphael Moll <ralf@android.com>
Thu, 3 Feb 2011 23:14:14 +0000 (15:14 -0800)
committerRaphael Moll <ralf@android.com>
Thu, 3 Feb 2011 23:23:59 +0000 (15:23 -0800)
This change makes the SDK manager install new extra
packages into SDK/extras/vendor/path, creating the
folders as needed.

Existing extra packages are not moved when they are updated,
provided that they keep the same 'vendor' and 'path' attributes
in their XML definition.

There is also some support for the form SDK/vendor-path/ that
was introduced by Tools r8 and r9.

SDK Bug: 14493

Change-Id: I28b301a768ea2c8c03573f865520b1b3e85f3868

13 files changed:
sdkmanager/libs/sdklib/.settings/org.eclipse.core.resources.prefs [new file with mode: 0755]
sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java
sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockEmptySdkManager.java [new file with mode: 0755]
sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkAddonSourceTest.java
sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkRepoSourceTest.java
sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addon_sample_1.xml

diff --git a/sdkmanager/libs/sdklib/.settings/org.eclipse.core.resources.prefs b/sdkmanager/libs/sdklib/.settings/org.eclipse.core.resources.prefs
new file mode 100755 (executable)
index 0000000..683b395
--- /dev/null
@@ -0,0 +1,3 @@
+#Wed Feb 02 16:41:18 PST 2011\r
+eclipse.preferences.version=1\r
+encoding//tests/com/android/sdklib/testdata/addon_sample_1.xml=UTF-8\r
index f8f7f19..37265b1 100644 (file)
@@ -227,6 +227,8 @@ public final class SdkConstants {
     public final static String FD_SKINS = "skins";
     /** Name of the SDK samples folder. */
     public final static String FD_SAMPLES = "samples";
+    /** Name of the SDK extras folder. */
+    public final static String FD_EXTRAS = "extras";
     /** Name of the SDK templates folder, i.e. "templates" */
     public final static String FD_TEMPLATES = "templates";
     /** Name of the SDK Ant folder, i.e. "ant" */
index 767e582..3e6e23e 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.android.sdklib;
 
+import com.android.annotations.VisibleForTesting;
+import com.android.annotations.VisibleForTesting.Visibility;
 import com.android.prefs.AndroidLocation;
 import com.android.prefs.AndroidLocation.AndroidLocationException;
 import com.android.sdklib.AndroidVersion.AndroidVersionException;
@@ -42,7 +44,7 @@ import java.util.regex.Pattern;
  * @see PlatformTarget
  * @see AddOnTarget
  */
-public final class SdkManager {
+public class SdkManager {
 
     public final static String PROP_VERSION_SDK = "ro.build.version.sdk";              //$NON-NLS-1$
     public final static String PROP_VERSION_CODENAME = "ro.build.version.codename";    //$NON-NLS-1$
@@ -92,7 +94,8 @@ public final class SdkManager {
      *
      * @param osSdkPath the location of the SDK.
      */
-    private SdkManager(String osSdkPath) {
+    @VisibleForTesting(visibility=Visibility.PRIVATE)
+    protected SdkManager(String osSdkPath) {
         mOsSdkPath = osSdkPath;
     }
 
@@ -146,7 +149,8 @@ public final class SdkManager {
      * <p/>
      * The array can be empty but not null.
      */
-    private void setTargets(IAndroidTarget[] targets) {
+    @VisibleForTesting(visibility=Visibility.PRIVATE)
+    protected void setTargets(IAndroidTarget[] targets) {
         assert targets != null;
         mTargets = targets;
     }
index 9a90b2c..bed9174 100755 (executable)
@@ -329,22 +329,6 @@ public class AddonPackage extends Package
         return null;\r
     }\r
 \r
-    /**\r
-     * Makes sure the base /add-ons folder exists before installing.\r
-     */\r
-    @Override\r
-    public boolean preInstallHook(Archive archive,\r
-            ITaskMonitor monitor,\r
-            String osSdkRoot,\r
-            File installFolder) {\r
-        File addonsRoot = new File(osSdkRoot, SdkConstants.FD_ADDONS);\r
-        if (!addonsRoot.isDirectory()) {\r
-            addonsRoot.mkdir();\r
-        }\r
-\r
-        return super.preInstallHook(archive, monitor, osSdkRoot, installFolder);\r
-    }\r
-\r
     @Override\r
     public boolean sameItemAs(Package pkg) {\r
         if (pkg instanceof AddonPackage) {\r
index 9c94800..49236dc 100755 (executable)
@@ -16,6 +16,7 @@
 \r
 package com.android.sdklib.internal.repository;\r
 \r
+import com.android.sdklib.NullSdkLog;\r
 import com.android.sdklib.SdkConstants;\r
 import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
@@ -50,7 +51,8 @@ public class ExtraPackage extends MinToolsPackage
     private final String mVendor;\r
 \r
     /**\r
-     * The sub-folder name. It must be a non-empty single-segment path.\r
+     * The sub-folder name. It must be a non-empty single-segment path and has the same\r
+     * rules as {@link #mVendor}.\r
      */\r
     private final String mPath;\r
 \r
@@ -164,7 +166,7 @@ public class ExtraPackage extends MinToolsPackage
 \r
         props.setProperty(PROP_PATH, mPath);\r
         if (mVendor != null) {\r
-            props.setProperty(PROP_PATH, mVendor);\r
+            props.setProperty(PROP_VENDOR, mVendor);\r
         }\r
 \r
         if (getMinApiLevel() != MIN_API_LEVEL_NOT_SPECIFIED) {\r
@@ -181,74 +183,126 @@ public class ExtraPackage extends MinToolsPackage
     }\r
 \r
     /**\r
-     * Static helper to check if a given path is acceptable for an "extra" package.\r
+     * Static helper to check if a given vendor and path is acceptable for an "extra" package.\r
      */\r
     public boolean isPathValid() {\r
-        if (SdkConstants.FD_ADDONS.equals(mPath) ||\r
-                SdkConstants.FD_PLATFORMS.equals(mPath) ||\r
-                SdkConstants.FD_PLATFORM_TOOLS.equals(mPath) ||\r
-                SdkConstants.FD_TOOLS.equals(mPath) ||\r
-                SdkConstants.FD_DOCS.equals(mPath) ||\r
-                RepoConstants.FD_TEMP.equals(mPath)) {\r
+        return isSegmentValid(mVendor) && isSegmentValid(mPath);\r
+    }\r
+\r
+    private boolean isSegmentValid(String segment) {\r
+        if (SdkConstants.FD_ADDONS.equals(segment) ||\r
+                SdkConstants.FD_PLATFORMS.equals(segment) ||\r
+                SdkConstants.FD_PLATFORM_TOOLS.equals(segment) ||\r
+                SdkConstants.FD_TOOLS.equals(segment) ||\r
+                SdkConstants.FD_DOCS.equals(segment) ||\r
+                RepoConstants.FD_TEMP.equals(segment)) {\r
             return false;\r
         }\r
-        return mPath != null && mPath.indexOf('/') == -1 && mPath.indexOf('\\') == -1;\r
+        return segment != null && segment.indexOf('/') == -1 && segment.indexOf('\\') == -1;\r
     }\r
 \r
     /**\r
-     * The install folder name. It is a single-segment path.\r
+     * Returns the sanitized path folder name. It is a single-segment path.\r
+     * <p/>\r
+     * The package is installed in SDK/extras/vendor_name/path_name.\r
      * <p/>\r
      * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used.\r
      * This limitation cannot be written in the XML Schema and must be enforced here by using\r
      * the method {@link #isPathValid()} *before* installing the package.\r
      */\r
     public String getPath() {\r
-        String path = mPath;\r
+        // The XSD specifies the XML vendor and path should only contain [a-zA-Z0-9]+\r
+        // and cannot be empty. Let's be defensive and enforce that anyway since things\r
+        // like "____" are still valid values that we don't want to allow.\r
 \r
-        if (mVendor != null && mVendor.length() > 0) {\r
-            path = mVendor + "-" + mPath;    //$NON-NLS-1$\r
+        // Sanitize the path\r
+        String path = mPath.replaceAll("[^a-zA-Z0-9-]+", "_");      //$NON-NLS-1$\r
+        if (path.length() == 0 || path.equals("_")) {               //$NON-NLS-1$\r
+            int h = path.hashCode();\r
+            path = String.format("extra%08x", h);                   //$NON-NLS-1$\r
         }\r
 \r
-        int h = path.hashCode();\r
+        return path;\r
+    }\r
 \r
-        // Sanitize the path\r
-        path = path.replaceAll("[^a-zA-Z0-9-]+", "_");       //$NON-NLS-1$\r
-        if (path.length() == 0) {\r
-            path = String.format("unknown_extra%08x", h);  //$NON-NLS-1$\r
+    /**\r
+     * Returns the sanitized vendor folder name. It is a single-segment path.\r
+     * <p/>\r
+     * The package is installed in SDK/extras/vendor_name/path_name.\r
+     * <p/>\r
+     * An empty string is returned in case of error.\r
+     */\r
+    public String getVendor() {\r
+\r
+        // The XSD specifies the XML vendor and path should only contain [a-zA-Z0-9]+\r
+        // and cannot be empty. Let's be defensive and enforce that anyway since things\r
+        // like "____" are still valid values that we don't want to allow.\r
+\r
+        if (mVendor != null && mVendor.length() > 0) {\r
+            String vendor = mVendor;\r
+            // Sanitize the vendor\r
+            vendor = vendor.replaceAll("[^a-zA-Z0-9-]+", "_");      //$NON-NLS-1$\r
+            if (vendor.equals("_")) {                               //$NON-NLS-1$\r
+                int h = vendor.hashCode();\r
+                vendor = String.format("vendor%08x", h);            //$NON-NLS-1$\r
+            }\r
+\r
+            return vendor;\r
         }\r
 \r
-        return path;\r
+        return ""; //$NON-NLS-1$\r
     }\r
 \r
     /** Returns a short description for an {@link IDescription}. */\r
     @Override\r
     public String getShortDescription() {\r
-        String name = getPath();\r
-        if (name != null) {\r
-            // Uniformize all spaces in the name and upper case words.\r
-\r
-            name = name.replaceAll("[ _\t\f-]+", " ");     //$NON-NLS-1$ //$NON-NLS-2$\r
-\r
-            // Look at all lower case characters in range [1..n-1] and replace them by an upper\r
-            // case if they are preceded by a space. Also upper cases the first character of the\r
-            // string.\r
-            boolean changed = false;\r
-            char[] chars = name.toCharArray();\r
-            for (int n = chars.length - 1, i = 0; i < n; i++) {\r
-                if (Character.isLowerCase(chars[i]) && (i == 0 || chars[i - 1] == ' ')) {\r
-                    chars[i] = Character.toUpperCase(chars[i]);\r
-                    changed = true;\r
-                }\r
+        String name = mPath;\r
+\r
+        // In the past, we used to save the extras in a folder vendor-path,\r
+        // and that "vendor" would end up in the path when we reload the extra from\r
+        // disk. Detect this and compensate.\r
+        if (mVendor != null && mVendor.length() > 0) {\r
+            if (name.startsWith(mVendor + "-")) {  //$NON-NLS-1$\r
+                name = name.substring(mVendor.length() + 1);\r
             }\r
-            if (changed) {\r
-                name = new String(chars);\r
+        }\r
+\r
+        // Uniformize all spaces in the name\r
+        if (name != null) {\r
+            name = name.replaceAll("[ _\t\f-]+", " ").trim();   //$NON-NLS-1$ //$NON-NLS-2$\r
+        }\r
+        if (name == null || name.length() == 0) {   //$NON-NLS-1$\r
+            name = "Unkown Extra";\r
+        }\r
+\r
+        if (mVendor != null && mVendor.length() > 0) {\r
+            name = mVendor + " " + name;  //$NON-NLS-1$\r
+            name = name.replaceAll("[ _\t\f-]+", " ").trim();   //$NON-NLS-1$ //$NON-NLS-2$\r
+        }\r
+\r
+        // Look at all lower case characters in range [1..n-1] and replace them by an upper\r
+        // case if they are preceded by a space. Also upper cases the first character of the\r
+        // string.\r
+        boolean changed = false;\r
+        char[] chars = name.toCharArray();\r
+        for (int n = chars.length - 1, i = 0; i < n; i++) {\r
+            if (Character.isLowerCase(chars[i]) && (i == 0 || chars[i - 1] == ' ')) {\r
+                chars[i] = Character.toUpperCase(chars[i]);\r
+                changed = true;\r
             }\r
         }\r
+        if (changed) {\r
+            name = new String(chars);\r
+        }\r
+\r
+        // Special case: reformat a few typical acronyms.\r
+        name = name.replaceAll(" Usb ", " USB ");   //$NON-NLS-1$\r
+        name = name.replaceAll(" Api ", " API ");   //$NON-NLS-1$\r
 \r
         String s = String.format("%1$s package, revision %2$d%3$s",\r
                 name,\r
                 getRevision(),\r
-                isObsolete() ? " (Obsolete)" : "");\r
+                isObsolete() ? " (Obsolete)" : "");  //$NON-NLS-2$\r
 \r
         return s;\r
     }\r
@@ -263,13 +317,13 @@ public class ExtraPackage extends MinToolsPackage
     public String getLongDescription() {\r
         String s = getDescription();\r
         if (s == null || s.length() == 0) {\r
-            s = String.format("Extra %1$s package", getPath());\r
+            s = String.format("Extra %1$s package by %2$s", getPath(), getVendor());\r
         }\r
 \r
         if (s.indexOf("revision") == -1) {\r
             s += String.format("\nRevision %1$d%2$s",\r
                     getRevision(),\r
-                    isObsolete() ? " (Obsolete)" : "");\r
+                    isObsolete() ? " (Obsolete)" : "");  //$NON-NLS-2$\r
         }\r
 \r
         if (getMinToolsRevision() != MIN_TOOLS_REV_NOT_SPECIFIED) {\r
@@ -280,6 +334,13 @@ public class ExtraPackage extends MinToolsPackage
             s += String.format("\nRequires SDK Platform Android API %1$s", getMinApiLevel());\r
         }\r
 \r
+        // For a local archive, also put the install path in the long description.\r
+        // This should help users locate the extra on their drive.\r
+        File localPath = getLocalArchivePath();\r
+        if (localPath != null) {\r
+            s += String.format("\nLocation: %1$s", localPath.getAbsolutePath());\r
+        }\r
+\r
         return s;\r
     }\r
 \r
@@ -291,16 +352,82 @@ public class ExtraPackage extends MinToolsPackage
      *\r
      * @param osSdkRoot The OS path of the SDK root folder.\r
      * @param sdkManager An existing SDK manager to list current platforms and addons.\r
+     *                   Not used in this implementation.\r
      * @return A new {@link File} corresponding to the directory to use to install this package.\r
      */\r
     @Override\r
     public File getInstallFolder(String osSdkRoot, SdkManager sdkManager) {\r
-        return new File(osSdkRoot, getPath());\r
+\r
+        // First find if this extra is already installed. If so, reuse the same directory.\r
+        LocalSdkParser localParser = new LocalSdkParser();\r
+        Package[] pkgs = localParser.parseSdk(osSdkRoot, sdkManager, new NullSdkLog());\r
+\r
+        for (Package pkg : pkgs) {\r
+            if (sameItemAs(pkg) && pkg instanceof ExtraPackage) {\r
+                File localPath = ((ExtraPackage) pkg).getLocalArchivePath();\r
+                if (localPath != null) {\r
+                    return localPath;\r
+                }\r
+            }\r
+        }\r
+\r
+        // The /extras dir at the root of the SDK\r
+        File path = new File(osSdkRoot, SdkConstants.FD_EXTRAS);\r
+\r
+        String vendor = getVendor();\r
+        if (vendor != null && vendor.length() > 0) {\r
+            path = new File(path, vendor);\r
+        }\r
+\r
+        String name = getPath();\r
+        if (name != null && name.length() > 0) {\r
+            path = new File(path, name);\r
+        }\r
+\r
+        return path;\r
     }\r
 \r
     @Override\r
     public boolean sameItemAs(Package pkg) {\r
-        // Extra packages are similar if they have the same path.\r
-        return pkg instanceof ExtraPackage && ((ExtraPackage)pkg).mPath.equals(mPath);\r
+        // Extra packages are similar if they have the same path and vendor\r
+        if (pkg instanceof ExtraPackage) {\r
+            ExtraPackage ep = (ExtraPackage) pkg;\r
+\r
+            // To be backward compatible, we need to support the old vendor-path form\r
+            if (ep.mPath != null && (ep.mVendor == null || ep.mVendor.length() == 0) &&\r
+                    mPath != null && mVendor != null) {\r
+                if (ep.mPath.equals(mVendor + "-" + mPath)) {  //$NON-NLS-1$\r
+                    return true;\r
+                }\r
+            }\r
+\r
+            if (!mPath.equals(ep.mPath)) {\r
+                return false;\r
+            }\r
+            if ((mVendor == null && ep.mVendor == null) ||\r
+                (mVendor != null && !mVendor.equals(ep.mVendor))) {\r
+                return false;\r
+            }\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    // ---\r
+\r
+    /**\r
+     * If this package is installed, returns the install path of the archive if valid.\r
+     * Returns null if not installed or if the path does not exist.\r
+     */\r
+    private File getLocalArchivePath() {\r
+        Archive[] archives = getArchives();\r
+        if (archives.length == 1 && archives[0].isLocal()) {\r
+            File path = new File(archives[0].getLocalOsPath());\r
+            if (path.isDirectory()) {\r
+                return path;\r
+            }\r
+        }\r
+\r
+        return null;\r
     }\r
 }\r
index 9a2bf46..eb00562 100755 (executable)
@@ -142,6 +142,7 @@ public class LocalSdkParser {
         scanMissingAddons(sdkManager, visited, packages, log);\r
         scanMissingSamples(osSdkRoot, visited, packages, log);\r
         scanExtras(osSdkRoot, visited, packages, log);\r
+        scanExtrasDirectory(osSdkRoot, visited, packages, log);\r
 \r
         Collections.sort(packages);\r
 \r
@@ -150,14 +151,36 @@ public class LocalSdkParser {
     }\r
 \r
     /**\r
-     * Find any other directory <em>at the top level</em> that hasn't been visited yet\r
-     * and assume they contain extra packages. This is <em>not</em> a recursive search.\r
+     * Find any directory in the /extras/vendors/path folders for extra packages.\r
+     * This isn't a recursive search.\r
      */\r
     private void scanExtras(String osSdkRoot,\r
             HashSet<File> visited,\r
             ArrayList<Package> packages,\r
             ISdkLog log) {\r
-        File root = new File(osSdkRoot);\r
+        File root = new File(osSdkRoot, SdkConstants.FD_EXTRAS);\r
+\r
+        if (!root.isDirectory()) {\r
+            // This should not happen. It makes listFiles() return null so let's avoid it.\r
+            return;\r
+        }\r
+\r
+        for (File vendor : root.listFiles()) {\r
+            if (vendor.isDirectory()) {\r
+                scanExtrasDirectory(vendor.getAbsolutePath(), visited, packages, log);\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Find any other directory in the given "root" directory that hasn't been visited yet\r
+     * and assume they contain extra packages. This is <em>not</em> a recursive search.\r
+     */\r
+    private void scanExtrasDirectory(String extrasRoot,\r
+            HashSet<File> visited,\r
+            ArrayList<Package> packages,\r
+            ISdkLog log) {\r
+        File root = new File(extrasRoot);\r
 \r
         if (!root.isDirectory()) {\r
             // This should not happen. It makes listFiles() return null so let's avoid it.\r
index 12ca0e9..3e9f9d6 100755 (executable)
@@ -409,10 +409,13 @@ public abstract class Package implements IDescription, Comparable<Package> {
      * <p/>\r
      * Some types of packages install in a fix location, for example docs and tools.\r
      * In this case the returned folder may already exist with a different archive installed\r
-     * at the desired location.\r
+     * at the desired location. <br/>\r
      * For other packages types, such as add-on or platform, the folder name is only partially\r
      * relevant to determine the content and thus a real check will be done to provide an\r
      * existing or new folder depending on the current content of the SDK.\r
+     * <p/>\r
+     * Note that the installer *will* create all directories returned here just before\r
+     * installation so this method must not attempt to create them.\r
      *\r
      * @param osSdkRoot The OS path of the SDK root folder.\r
      * @param sdkManager An existing SDK manager to list current platforms and addons.\r
@@ -429,6 +432,11 @@ public abstract class Package implements IDescription, Comparable<Package> {
      * be continue. The installer will still install the remaining packages if possible.\r
      * <p/>\r
      * The base implementation always return true.\r
+     * <p/>\r
+     * Note that the installer *will* create all directories specified by\r
+     * {@link #getInstallFolder} just before installation, so they must not be\r
+     * created here. This is also called before the previous install dir is removed\r
+     * so the previous content is still there during upgrade.\r
      *\r
      * @param archive The archive that will be installed\r
      * @param monitor The {@link ITaskMonitor} to display errors.\r
@@ -513,6 +521,12 @@ public abstract class Package implements IDescription, Comparable<Package> {
      * - Add-on based on n <br/>\r
      * - Add-on based on n-1 <br/>\r
      * - Extra packages <br/>\r
+     * <p/>\r
+     * Important: this must NOT be used to compare if two packages are the same thing.\r
+     * This is achieved by {@link #sameItemAs(Package)} or {@link #canBeUpdatedBy(Package)}.\r
+     * <p/>\r
+     * This {@link #compareTo(Package)} method is purely an implementation detail to\r
+     * perform the right ordering of the packages in the list of available or installed packages.\r
      */\r
     public int compareTo(Package other) {\r
         int s1 = this.sortingScore();\r
index 8dee635..c303e2f 100755 (executable)
@@ -193,22 +193,6 @@ public class PlatformPackage extends MinToolsPackage implements IPackageVersion
         return folder;\r
     }\r
 \r
-    /**\r
-     * Makes sure the base /platforms folder exists before installing.\r
-     */\r
-    @Override\r
-    public boolean preInstallHook(Archive archive,\r
-            ITaskMonitor monitor,\r
-            String osSdkRoot,\r
-            File installFolder) {\r
-        File platformsRoot = new File(osSdkRoot, SdkConstants.FD_PLATFORMS);\r
-        if (!platformsRoot.isDirectory()) {\r
-            platformsRoot.mkdir();\r
-        }\r
-\r
-        return super.preInstallHook(archive, monitor, osSdkRoot, installFolder);\r
-    }\r
-\r
     @Override\r
     public boolean sameItemAs(Package pkg) {\r
         if (pkg instanceof PlatformPackage) {\r
index b6976e9..035677b 100755 (executable)
@@ -226,7 +226,7 @@ public class SamplePackage extends MinToolsPackage
         // The /samples dir at the root of the SDK\r
         File samplesRoot = new File(osSdkRoot, SdkConstants.FD_SAMPLES);\r
 \r
-        // First find if this platform is already installed. If so, reuse the same directory.\r
+        // First find if this sample is already installed. If so, reuse the same directory.\r
         for (IAndroidTarget target : sdkManager.getTargets()) {\r
             if (target.isPlatform() &&\r
                     target.getVersion().equals(mVersion)) {\r
@@ -278,10 +278,6 @@ public class SamplePackage extends MinToolsPackage
             ITaskMonitor monitor,\r
             String osSdkRoot,\r
             File installFolder) {\r
-        File samplesRoot = new File(osSdkRoot, SdkConstants.FD_SAMPLES);\r
-        if (!samplesRoot.isDirectory()) {\r
-            samplesRoot.mkdir();\r
-        }\r
 \r
         if (installFolder != null && installFolder.isDirectory()) {\r
             // Get the hash computed during the last installation\r
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockEmptySdkManager.java b/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockEmptySdkManager.java
new file mode 100755 (executable)
index 0000000..455841a
--- /dev/null
@@ -0,0 +1,30 @@
+/*\r
+ * Copyright (C) 2011 The Android Open Source Project\r
+ *\r
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.eclipse.org/org/documents/epl-v10.php\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package com.android.sdklib.internal.repository;\r
+\r
+import com.android.sdklib.IAndroidTarget;\r
+import com.android.sdklib.SdkManager;\r
+\r
+/**\r
+ * An invalid SDK Manager, just good enough to avoid passing a null reference.\r
+ */\r
+class MockEmptySdkManager extends SdkManager {\r
+    public MockEmptySdkManager(String osSdkPath) {\r
+        super(osSdkPath);\r
+        setTargets(new IAndroidTarget[0]);\r
+    }\r
+}\r
index 9e152d5..ea6c4f6 100755 (executable)
 \r
 package com.android.sdklib.internal.repository;\r
 \r
+import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.repository.SdkAddonConstants;\r
 \r
 import org.w3c.dom.Document;\r
 \r
 import java.io.ByteArrayInputStream;\r
+import java.io.File;\r
 import java.io.FileInputStream;\r
 import java.io.IOException;\r
 import java.io.InputStream;\r
@@ -171,29 +173,46 @@ public class SdkAddonSourceTest extends TestCase {
         assertEquals("Found My First add-on by John Doe, Android API 1, revision 1\n" +\r
                      "Found My Second add-on by John Deer, Android API 2, revision 42\n" +\r
                      "Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +\r
-                     "Found A Usb Driver package, revision 43 (Obsolete)\n" +\r
-                     "Found Android Vendor Extra Api Dep package, revision 2 (Obsolete)\n",\r
+                     "Found G USB Driver package, revision 43 (Obsolete)\n" +\r
+                     "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +\r
+                     "Found Unkown Extra package, revision 2 (Obsolete)\n",\r
                 monitor.getCapturedDescriptions());\r
         assertEquals("", monitor.getCapturedResults());\r
 \r
         // check the packages we found... we expected to find 11 packages with each at least\r
         // one archive.\r
         Package[] pkgs = mSource.getPackages();\r
-        assertEquals(5, pkgs.length);\r
+        assertEquals(6, pkgs.length);\r
         for (Package p : pkgs) {\r
             assertTrue(p.getArchives().length >= 1);\r
         }\r
 \r
-        // Check the extra packages path\r
-        ArrayList<String> extraPaths = new ArrayList<String>();\r
+        // Check the extra packages path, vendor, install folder\r
+\r
+        final String osSdkPath = "SDK";\r
+        final SdkManager sdkManager = new MockEmptySdkManager(osSdkPath);\r
+\r
+        ArrayList<String> extraPaths   = new ArrayList<String>();\r
+        ArrayList<String> extraVendors = new ArrayList<String>();\r
+        ArrayList<File>   extraInstall = new ArrayList<File>();\r
         for (Package p : pkgs) {\r
             if (p instanceof ExtraPackage) {\r
                 extraPaths.add(((ExtraPackage) p).getPath());\r
+                extraVendors.add(((ExtraPackage) p).getVendor());\r
+                extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));\r
             }\r
         }\r
         assertEquals(\r
-                "[a-usb_driver, android_vendor-extra_api_dep]",\r
+                "[usb_driver, extra_api_dep, extra0000005f]",\r
                 Arrays.toString(extraPaths.toArray()));\r
+        assertEquals(\r
+                "[g, android_vendor, vendor0000005f]",\r
+                Arrays.toString(extraVendors.toArray()));\r
+        assertEquals(\r
+                ("[SDK/extras/g/usb_driver, " +\r
+                  "SDK/extras/android_vendor/extra_api_dep, " +\r
+                  "SDK/extras/vendor0000005f/extra0000005f]").replace('/', File.separatorChar),\r
+                Arrays.toString(extraInstall.toArray()));\r
     }\r
 \r
     /**\r
index c92acde..aee811f 100755 (executable)
 \r
 package com.android.sdklib.internal.repository;\r
 \r
+import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.repository.SdkRepoConstants;\r
 \r
 import org.w3c.dom.Document;\r
 \r
 import java.io.ByteArrayInputStream;\r
+import java.io.File;\r
 import java.io.FileInputStream;\r
 import java.io.IOException;\r
 import java.io.InputStream;\r
@@ -208,16 +210,30 @@ public class SdkRepoSourceTest extends TestCase {
             assertTrue(p.getArchives().length >= 1);\r
         }\r
 \r
-        // Check the extra packages path\r
-        ArrayList<String> extraPaths = new ArrayList<String>();\r
+        // Check the extra packages path, vendor, install folder\r
+\r
+        final String osSdkPath = "SDK";\r
+        final SdkManager sdkManager = new MockEmptySdkManager(osSdkPath);\r
+\r
+        ArrayList<String> extraPaths   = new ArrayList<String>();\r
+        ArrayList<String> extraVendors = new ArrayList<String>();\r
+        ArrayList<File>   extraInstall = new ArrayList<File>();\r
         for (Package p : pkgs) {\r
             if (p instanceof ExtraPackage) {\r
                 extraPaths.add(((ExtraPackage) p).getPath());\r
+                extraVendors.add(((ExtraPackage) p).getVendor());\r
+                extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));\r
             }\r
         }\r
         assertEquals(\r
                 "[usb_driver]",\r
                 Arrays.toString(extraPaths.toArray()));\r
+        assertEquals(\r
+                "[]",\r
+                Arrays.toString(extraVendors.toArray()));\r
+        assertEquals(\r
+                "[SDK/extras/usb_driver]".replace('/', File.separatorChar),\r
+                Arrays.toString(extraInstall.toArray()));\r
     }\r
 \r
     /**\r
@@ -259,7 +275,7 @@ public class SdkRepoSourceTest extends TestCase {
                      "Found Android SDK Tools, revision 42\n" +\r
                      "Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +\r
                      "Found Usb Driver package, revision 43 (Obsolete)\n" +\r
-                     "Found Extra Api Dep package, revision 2 (Obsolete)\n" +\r
+                     "Found Extra API Dep package, revision 2 (Obsolete)\n" +\r
                      "Found Samples for SDK API 14, revision 24 (Obsolete)\n",\r
                 monitor.getCapturedDescriptions());\r
         assertEquals("", monitor.getCapturedResults());\r
@@ -272,16 +288,30 @@ public class SdkRepoSourceTest extends TestCase {
             assertTrue(p.getArchives().length >= 1);\r
         }\r
 \r
-        // Check the extra packages path\r
-        ArrayList<String> extraPaths = new ArrayList<String>();\r
+        // Check the extra packages path, vendor, install folder\r
+\r
+        final String osSdkPath = "SDK";\r
+        final SdkManager sdkManager = new MockEmptySdkManager(osSdkPath);\r
+\r
+        ArrayList<String> extraPaths   = new ArrayList<String>();\r
+        ArrayList<String> extraVendors = new ArrayList<String>();\r
+        ArrayList<File>   extraInstall = new ArrayList<File>();\r
         for (Package p : pkgs) {\r
             if (p instanceof ExtraPackage) {\r
                 extraPaths.add(((ExtraPackage) p).getPath());\r
+                extraVendors.add(((ExtraPackage) p).getVendor());\r
+                extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));\r
             }\r
         }\r
         assertEquals(\r
                 "[usb_driver, extra_api_dep]",\r
                 Arrays.toString(extraPaths.toArray()));\r
+        assertEquals(\r
+                "[, ]",\r
+                Arrays.toString(extraVendors.toArray()));\r
+        assertEquals(\r
+                "[SDK/extras/usb_driver, SDK/extras/extra_api_dep]".replace('/', File.separatorChar),\r
+                Arrays.toString(extraInstall.toArray()));\r
     }\r
 \r
     /**\r
@@ -320,8 +350,8 @@ public class SdkRepoSourceTest extends TestCase {
                     "Found Documentation for Android SDK, API 2, revision 42\n" +\r
                     "Found Android SDK Tools, revision 42\n" +\r
                     "Found Android SDK Platform-tools, revision 3\n" +\r
-                    "Found A Usb Driver package, revision 43 (Obsolete)\n" +\r
-                    "Found Android Vendor Extra Api Dep package, revision 2 (Obsolete)\n" +\r
+                    "Found A USB Driver package, revision 43 (Obsolete)\n" +\r
+                    "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +\r
                     "Found Samples for SDK API 14, revision 24 (Obsolete)\n",\r
                 monitor.getCapturedDescriptions());\r
         assertEquals("", monitor.getCapturedResults());\r
@@ -334,16 +364,31 @@ public class SdkRepoSourceTest extends TestCase {
             assertTrue(p.getArchives().length >= 1);\r
         }\r
 \r
-        // Check the extra packages path\r
-        ArrayList<String> extraPaths = new ArrayList<String>();\r
+        // Check the extra packages path, vendor, install folder\r
+\r
+        final String osSdkPath = "SDK";\r
+        final SdkManager sdkManager = new MockEmptySdkManager(osSdkPath);\r
+\r
+        ArrayList<String> extraPaths   = new ArrayList<String>();\r
+        ArrayList<String> extraVendors = new ArrayList<String>();\r
+        ArrayList<File>   extraInstall = new ArrayList<File>();\r
         for (Package p : pkgs) {\r
             if (p instanceof ExtraPackage) {\r
                 extraPaths.add(((ExtraPackage) p).getPath());\r
+                extraVendors.add(((ExtraPackage) p).getVendor());\r
+                extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));\r
             }\r
         }\r
         assertEquals(\r
-                "[a-usb_driver, android_vendor-extra_api_dep]",\r
+                "[usb_driver, extra_api_dep]",\r
                 Arrays.toString(extraPaths.toArray()));\r
+        assertEquals(\r
+                "[a, android_vendor]",\r
+                Arrays.toString(extraVendors.toArray()));\r
+        assertEquals(\r
+                "[SDK/extras/a/usb_driver, SDK/extras/android_vendor/extra_api_dep]"\r
+                 .replace('/', File.separatorChar),\r
+                Arrays.toString(extraInstall.toArray()));\r
     }\r
 \r
     /**\r
index 4e3f750..d761d73 100755 (executable)
     </sdk:add-on>
 
     <sdk:extra>
-        <sdk:vendor>a</sdk:vendor>
+        <sdk:vendor>g</sdk:vendor>
         <sdk:path>usb_driver</sdk:path>
         <sdk:uses-license ref="license2" />
         <sdk:revision>43</sdk:revision>
         <sdk:obsolete></sdk:obsolete>
     </sdk:extra>
 
+    <sdk:extra>
+        <sdk:vendor>____</sdk:vendor>
+        <sdk:path>____</sdk:path>
+        <sdk:uses-license ref="license2" />
+        <sdk:revision>2</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="any" arch="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/extra_mega_duff.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:description>Some extra package that has a min-api-level of 42</sdk:description>
+        <sdk:desc-url>http://www.example.com/extra.html</sdk:desc-url>
+        <sdk:min-tools-rev>3</sdk:min-tools-rev>
+        <sdk:min-api-level>42</sdk:min-api-level>
+        <sdk:obsolete></sdk:obsolete>
+    </sdk:extra>
+
 </sdk:sdk-addon>