OSDN Git Service

SDK Manager: support separate addon.xml schema
authorRaphael Moll <ralf@android.com>
Wed, 6 Oct 2010 00:09:38 +0000 (17:09 -0700)
committerRaphael Moll <ralf@android.com>
Wed, 6 Oct 2010 21:56:05 +0000 (14:56 -0700)
This splits sdk-repository-3.xsd into 2 XML schemas,
one for the sdk-repository and one for the sdk-addon.

The SDK Manager is then modified to only load <addon>
and <extra> from the sdk-addon whils the <sdk-repository>
supports everything (including <addon> if the old v1 or v2
schemas are being used).

Change-Id: I30b263f2ab48dd3ea7b70d3006fb6516291736d0

43 files changed:
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/annotations/Nullable.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/annotations/VisibleForTesting.java
sdkmanager/app/src/com/android/sdkmanager/Main.java
sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/annotations/Nullable.java [new file with mode: 0755]
sdkmanager/libs/sdklib/src/com/android/sdklib/annotations/VisibleForTesting.java [new file with mode: 0755]
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IMinApiLevelDependency.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IMinPlatformToolsDependency.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/IMinToolsDependency.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/MinToolsPackage.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/PlatformToolPackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkAddonSource.java [new file with mode: 0755]
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkRepoSource.java [new file with mode: 0755]
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java [moved from sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java with 61% similarity]
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java [moved from sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSources.java with 83% similarity]
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
sdkmanager/libs/sdklib/src/com/android/sdklib/repository/CommonConstants.java [moved from sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java with 61% similarity]
sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkAddonConstants.java [new file with mode: 0755]
sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepoConstants.java [new file with mode: 0755]
sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-addon-1.xsd [new file with mode: 0755]
sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository-3.xsd
sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkAddonSourceTest.java [moved from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/RepoSourceTest.java with 50% similarity]
sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkRepoSourceTest.java [new file with mode: 0755]
sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/SdkRepositoryTest.java
sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addon_sample_1.xml [new file with mode: 0755]
sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_3.xml
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalSdkAdapter.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java
sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java

index 6ea5b36..ca0f374 100755 (executable)
@@ -29,7 +29,10 @@ import java.lang.annotation.RetentionPolicy;
  * When decorating a method, this denotes the method might legitimately return null.
  * <p/>
  * This is a marker annotation and it has no specific attributes.
+ *
+ * @deprecated TODO use the version moved into sdklib instead
  */
+@Deprecated
 @Retention(RetentionPolicy.SOURCE)
 public @interface Nullable {
 }
index b495692..9e26bdf 100755 (executable)
@@ -26,7 +26,10 @@ import java.lang.annotation.RetentionPolicy;
  * The <code>visibility</code> argument can be used to specific what the original
  * visibility should have been if it had not been made public or package-private for testing.
  * The default is to consider the element private.
+ *
+ * @deprecated TODO use the version moved into sdklib instead
  */
+@Deprecated
 @Retention(RetentionPolicy.SOURCE)
 public @interface VisibleForTesting {
     /**
index 222cd40..0066b6c 100644 (file)
@@ -32,7 +32,7 @@ import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.sdklib.internal.project.ProjectCreator.OutputLevel;
 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
 import com.android.sdklib.io.FileWrapper;
-import com.android.sdklib.repository.SdkRepository;
+import com.android.sdklib.repository.SdkRepoConstants;
 import com.android.sdklib.xml.AndroidXPathFactory;
 import com.android.sdkmanager.internal.repository.AboutPage;
 import com.android.sdkmanager.internal.repository.SettingsPage;
@@ -332,7 +332,7 @@ public class Main {
                     t = t.trim();
                     if (t.length() > 0) {
                         boolean found = false;
-                        for (String t2 : SdkRepository.NODES) {
+                        for (String t2 : SdkRepoConstants.NODES) {
                             if (t2.equals(t)) {
                                 pkgFilter.add(t2);
                                 found = true;
@@ -343,7 +343,7 @@ public class Main {
                             errorAndExit(
                                 "Unknown package filter type '%1$s'.\nAccepted values are: %2$s",
                                 t,
-                                Arrays.toString(SdkRepository.NODES));
+                                Arrays.toString(SdkRepoConstants.NODES));
                             return;
                         }
                     }
index b0c0e50..2cc721d 100644 (file)
@@ -18,7 +18,7 @@ package com.android.sdkmanager;
 
 import com.android.sdklib.ISdkLog;
 import com.android.sdklib.SdkManager;
-import com.android.sdklib.repository.SdkRepository;
+import com.android.sdklib.repository.SdkRepoConstants;
 
 import java.util.Arrays;
 
@@ -207,7 +207,7 @@ class SdkCommandLine extends CommandLineProcessor {
         define(Mode.STRING, false,
                 VERB_UPDATE, OBJECT_SDK, "t", KEY_FILTER,
                 "A filter that limits the update to the specified types of packages in the form of\n" +
-                "                a comma-separated list of " + Arrays.toString(SdkRepository.NODES),
+                "                a comma-separated list of " + Arrays.toString(SdkRepoConstants.NODES),
                 null);
 
         define(Mode.BOOLEAN, false,
index fa05b29..114aa22 100755 (executable)
@@ -19,7 +19,7 @@ package com.android.sdkmanager.internal.repository;
 \r
 import com.android.sdklib.SdkConstants;\r
 import com.android.sdklib.internal.repository.Package;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 import com.android.sdkmanager.*;\r
 import org.eclipse.swt.SWT;\r
 import org.eclipse.swt.graphics.Image;\r
@@ -70,7 +70,7 @@ public class AboutPage extends Composite {
                 "Repository XML Schema #%2$d\n" +\r
                 "Copyright (C) 2009-2010 The Android Open Source Project.",\r
                 getRevision(),\r
-                SdkRepository.NS_LATEST_VERSION));\r
+                SdkRepoConstants.NS_LATEST_VERSION));\r
     }\r
 \r
     @Override\r
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/annotations/Nullable.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/annotations/Nullable.java
new file mode 100755 (executable)
index 0000000..3b5e955
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a parameter or field can be null.
+ * <p/>
+ * When decorating a method call parameter, this denotes the parameter can
+ * legitimately be null and the method will gracefully deal with it. Typically used
+ * on optional parameters.
+ * <p/>
+ * When decorating a method, this denotes the method might legitimately return null.
+ * <p/>
+ * This is a marker annotation and it has no specific attributes.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface Nullable {
+}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/annotations/VisibleForTesting.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/annotations/VisibleForTesting.java
new file mode 100755 (executable)
index 0000000..86d1c99
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes that the class, method or field has its visibility relaxed so
+ * that unit tests can access it.
+ * <p/>
+ * The <code>visibility</code> argument can be used to specific what the original
+ * visibility should have been if it had not been made public or package-private for testing.
+ * The default is to consider the element private.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface VisibleForTesting {
+    /**
+     * Intended visibility if the element had not been made public or package-private for
+     * testing.
+     */
+    enum Visibility {
+        /** The element should be considered protected. */
+        PROTECTED,
+        /** The element should be considered package-private. */
+        PACKAGE,
+        /** The element should be considered private. */
+        PRIVATE
+    }
+
+    /**
+     * Intended visibility if the element had not been made public or package-private for testing.
+     * If not specified, one should assume the element originally intended to be private.
+     */
+    Visibility visibility() default Visibility.PRIVATE;
+}
index ae630e3..39750d4 100755 (executable)
@@ -23,7 +23,7 @@ import com.android.sdklib.SdkManager;
 import com.android.sdklib.IAndroidTarget.IOptionalLibrary;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 \r
 import org.w3c.dom.Node;\r
 \r
@@ -68,21 +68,26 @@ public class AddonPackage extends Package
 \r
     /**\r
      * Creates a new add-on package from the attributes and elements of the given XML node.\r
-     * <p/>\r
      * This constructor should throw an exception if the package cannot be created.\r
+     *\r
+     * @param source The {@link SdkSource} where this is loaded from.\r
+     * @param packageNode The XML element being parsed.\r
+     * @param nsUri The namespace URI of the originating XML document, to be able to deal with\r
+     *          parameters that vary according to the originating XML schema.\r
+     * @param licenses The licenses loaded from the XML originating document.\r
      */\r
-    AddonPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
-        super(source, packageNode, licenses);\r
-        mVendor   = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VENDOR);\r
-        mName     = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_NAME);\r
-        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
-        String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_CODENAME);\r
+    AddonPackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) {\r
+        super(source, packageNode, nsUri, licenses);\r
+        mVendor   = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_VENDOR);\r
+        mName     = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_NAME);\r
+        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);\r
+        String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);\r
         if (codeName.length() == 0) {\r
             codeName = null;\r
         }\r
         mVersion = new AndroidVersion(apiLevel, codeName);\r
 \r
-        mLibs = parseLibs(XmlParserUtils.getFirstChild(packageNode, SdkRepository.NODE_LIBS));\r
+        mLibs = parseLibs(XmlParserUtils.getFirstChild(packageNode, SdkRepoConstants.NODE_LIBS));\r
     }\r
 \r
     /**\r
@@ -151,7 +156,7 @@ public class AddonPackage extends Package
 \r
                 if (child.getNodeType() == Node.ELEMENT_NODE &&\r
                         nsUri.equals(child.getNamespaceURI()) &&\r
-                        SdkRepository.NODE_LIB.equals(child.getLocalName())) {\r
+                        SdkRepoConstants.NODE_LIB.equals(child.getLocalName())) {\r
                     libs.add(parseLib(child));\r
                 }\r
             }\r
@@ -164,8 +169,8 @@ public class AddonPackage extends Package
      * Parses a <lib> element from a <libs> container.\r
      */\r
     private Lib parseLib(Node libNode) {\r
-        return new Lib(XmlParserUtils.getXmlString(libNode, SdkRepository.NODE_NAME),\r
-                       XmlParserUtils.getXmlString(libNode, SdkRepository.NODE_DESCRIPTION));\r
+        return new Lib(XmlParserUtils.getXmlString(libNode, SdkRepoConstants.NODE_NAME),\r
+                       XmlParserUtils.getXmlString(libNode, SdkRepoConstants.NODE_DESCRIPTION));\r
     }\r
 \r
     /** Returns the vendor, a string, for add-on packages. */\r
index 21cae7a..86f041c 100755 (executable)
@@ -42,7 +42,7 @@ import java.util.Properties;
  * A package has some attributes (revision, description) and a list of archives\r
  * which represent the downloadable bits.\r
  * <p/>\r
- * Packages are offered by a {@link RepoSource} (a download site).\r
+ * Packages are offered by a {@link SdkSource} (a download site).\r
  */\r
 public class Archive implements IDescription {\r
 \r
@@ -434,7 +434,7 @@ public class Archive implements IDescription {
                 && !link.startsWith("ftp://")) {                 //$NON-NLS-1$\r
             // Make the URL absolute by prepending the source\r
             Package pkg = getParentPackage();\r
-            RepoSource src = pkg.getParentSource();\r
+            SdkSource src = pkg.getParentSource();\r
             if (src == null) {\r
                 monitor.setResult("Internal error: no source for archive %1$s", name);\r
                 return null;\r
index 6980bac..3bfd639 100755 (executable)
@@ -21,7 +21,7 @@ import com.android.sdklib.SdkConstants;
 import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 \r
 import org.w3c.dom.Node;\r
 \r
@@ -42,14 +42,19 @@ public class DocPackage extends Package implements IPackageVersion {
 \r
     /**\r
      * Creates a new doc package from the attributes and elements of the given XML node.\r
-     * <p/>\r
      * This constructor should throw an exception if the package cannot be created.\r
+     *\r
+     * @param source The {@link SdkSource} where this is loaded from.\r
+     * @param packageNode The XML element being parsed.\r
+     * @param nsUri The namespace URI of the originating XML document, to be able to deal with\r
+     *          parameters that vary according to the originating XML schema.\r
+     * @param licenses The licenses loaded from the XML originating document.\r
      */\r
-    DocPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
-        super(source, packageNode, licenses);\r
+    DocPackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) {\r
+        super(source, packageNode, nsUri, licenses);\r
 \r
-        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
-        String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_CODENAME);\r
+        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);\r
+        String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);\r
         if (codeName.length() == 0) {\r
             codeName = null;\r
         }\r
@@ -63,7 +68,7 @@ public class DocPackage extends Package implements IPackageVersion {
      * <p/>\r
      * By design, this creates a package with one and only one archive.\r
      */\r
-    DocPackage(RepoSource source,\r
+    DocPackage(SdkSource source,\r
             Properties props,\r
             int apiLevel,\r
             String codename,\r
index 4b79508..622b8ba 100755 (executable)
@@ -20,7 +20,7 @@ import com.android.sdklib.SdkConstants;
 import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 \r
 import org.w3c.dom.Node;\r
 \r
@@ -53,15 +53,20 @@ public class ExtraPackage extends MinToolsPackage
 \r
     /**\r
      * Creates a new tool package from the attributes and elements of the given XML node.\r
-     * <p/>\r
      * This constructor should throw an exception if the package cannot be created.\r
+     *\r
+     * @param source The {@link SdkSource} where this is loaded from.\r
+     * @param packageNode The XML element being parsed.\r
+     * @param nsUri The namespace URI of the originating XML document, to be able to deal with\r
+     *          parameters that vary according to the originating XML schema.\r
+     * @param licenses The licenses loaded from the XML originating document.\r
      */\r
-    ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
-        super(source, packageNode, licenses);\r
+    ExtraPackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) {\r
+        super(source, packageNode, nsUri, licenses);\r
 \r
-        mPath = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_PATH);\r
+        mPath = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_PATH);\r
 \r
-        mMinApiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_API_LEVEL,\r
+        mMinApiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepoConstants.NODE_MIN_API_LEVEL,\r
                 MIN_API_LEVEL_NOT_SPECIFIED);\r
     }\r
 \r
@@ -72,7 +77,7 @@ public class ExtraPackage extends MinToolsPackage
      * <p/>\r
      * By design, this creates a package with one and only one archive.\r
      */\r
-    ExtraPackage(RepoSource source,\r
+    ExtraPackage(SdkSource source,\r
             Properties props,\r
             String path,\r
             int revision,\r
index 76c1955..14e6744 100755 (executable)
@@ -16,7 +16,7 @@
 
 package com.android.sdklib.internal.repository;
 
-import com.android.sdklib.repository.SdkRepository;
+import com.android.sdklib.repository.SdkRepoConstants;
 
 /**
  * Interface used to decorate a {@link Package} that has a dependency
@@ -28,7 +28,7 @@ import com.android.sdklib.repository.SdkRepository;
 public interface IMinApiLevelDependency {
 
     /**
-     * The value of {@link #getMinApiLevel()} when the {@link SdkRepository#NODE_MIN_API_LEVEL}
+     * The value of {@link #getMinApiLevel()} when the {@link SdkRepoConstants#NODE_MIN_API_LEVEL}
      * was not specified in the XML source.
      */
     public static final int MIN_API_LEVEL_NOT_SPECIFIED = 0;
index 7bdfba4..c536b3e 100755 (executable)
@@ -16,7 +16,7 @@
 
 package com.android.sdklib.internal.repository;
 
-import com.android.sdklib.repository.SdkRepository;
+import com.android.sdklib.repository.SdkRepoConstants;
 
 /**
  * Interface used to decorate a {@link Package} that has a dependency
@@ -30,7 +30,7 @@ public interface IMinPlatformToolsDependency {
 
     /**
      * The value of {@link #getMinPlatformToolsRevision()} when the
-     * {@link SdkRepository#NODE_MIN_PLATFORM_TOOLS_REV} was not specified in the XML source.
+     * {@link SdkRepoConstants#NODE_MIN_PLATFORM_TOOLS_REV} was not specified in the XML source.
      * Since this is a required attribute in the XML schema, it can only happen when dealing
      * with an invalid repository XML.
      */
index 669806b..2f0b8fa 100755 (executable)
@@ -16,7 +16,7 @@
 
 package com.android.sdklib.internal.repository;
 
-import com.android.sdklib.repository.SdkRepository;
+import com.android.sdklib.repository.SdkRepoConstants;
 
 /**
  * Interface used to decorate a {@link Package} that has a dependency
@@ -28,8 +28,8 @@ import com.android.sdklib.repository.SdkRepository;
 public interface IMinToolsDependency {
 
     /**
-     * The value of {@link #getMinToolsRevision()} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}
-     * was not specified in the XML source.
+     * The value of {@link #getMinToolsRevision()} when the
+     * {@link SdkRepoConstants#NODE_MIN_TOOLS_REV} was not specified in the XML source.
      */
     public static final int MIN_TOOLS_REV_NOT_SPECIFIED = 0;
 
index e4a96e5..51549f3 100755 (executable)
@@ -278,6 +278,13 @@ public class LocalSdkParser {
 \r
         // We're not going to check that all tools are present. At the very least\r
         // we should expect to find adb, aidl, aapt and dx (adapted to the current OS).\r
+\r
+        if (platformToolsFolder.listFiles() == null) {\r
+            // ListFiles is null if the directory doesn't even exist.\r
+            // Not going to find anything in there...\r
+            return null;\r
+        }\r
+\r
         Set<String> names = new HashSet<String>();\r
         for (File file : platformToolsFolder.listFiles()) {\r
             names.add(file.getName());\r
index 2201173..a95e69c 100755 (executable)
@@ -18,7 +18,7 @@ package com.android.sdklib.internal.repository;
 \r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 \r
 import org.w3c.dom.Node;\r
 \r
@@ -40,13 +40,19 @@ public abstract class MinToolsPackage extends Package implements IMinToolsDepend
 \r
     /**\r
      * Creates a new package from the attributes and elements of the given XML node.\r
-     * <p/>\r
      * This constructor should throw an exception if the package cannot be created.\r
+     *\r
+     * @param source The {@link SdkSource} where this is loaded from.\r
+     * @param packageNode The XML element being parsed.\r
+     * @param nsUri The namespace URI of the originating XML document, to be able to deal with\r
+     *          parameters that vary according to the originating XML schema.\r
+     * @param licenses The licenses loaded from the XML originating document.\r
      */\r
-    MinToolsPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
-        super(source, packageNode, licenses);\r
+    MinToolsPackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) {\r
+        super(source, packageNode, nsUri, licenses);\r
 \r
-        mMinToolsRevision = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_TOOLS_REV,\r
+        mMinToolsRevision = XmlParserUtils.getXmlInt(packageNode,\r
+                SdkRepoConstants.NODE_MIN_TOOLS_REV,\r
                 MIN_TOOLS_REV_NOT_SPECIFIED);\r
     }\r
 \r
@@ -60,7 +66,7 @@ public abstract class MinToolsPackage extends Package implements IMinToolsDepend
      * By design, this creates a package with one and only one archive.\r
      */\r
     public MinToolsPackage(\r
-            RepoSource source,\r
+            SdkSource source,\r
             Properties props,\r
             int revision,\r
             String license,\r
index 2046bf9..1bcac3f 100755 (executable)
@@ -20,7 +20,7 @@ import com.android.sdklib.AndroidVersion;
 import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 \r
 import org.w3c.dom.Node;\r
 \r
@@ -36,7 +36,7 @@ import java.util.Properties;
  * A package has some attributes (revision, description) and a list of archives\r
  * which represent the downloadable bits.\r
  * <p/>\r
- * Packages are contained by a {@link RepoSource} (a download site).\r
+ * Packages are contained by a {@link SdkSource} (a download site).\r
  * <p/>\r
  * Derived classes must implement the {@link IDescription} methods.\r
  */\r
@@ -60,7 +60,7 @@ public abstract class Package implements IDescription, Comparable<Package> {
     private final String mReleaseNote;\r
     private final String mReleaseUrl;\r
     private final Archive[] mArchives;\r
-    private final RepoSource mSource;\r
+    private final SdkSource mSource;\r
 \r
     /**\r
      * Enum for the result of {@link Package#canBeUpdatedBy(Package)}. This used so that we can\r
@@ -79,22 +79,27 @@ public abstract class Package implements IDescription, Comparable<Package> {
 \r
     /**\r
      * Creates a new package from the attributes and elements of the given XML node.\r
-     * <p/>\r
      * This constructor should throw an exception if the package cannot be created.\r
+     *\r
+     * @param source The {@link SdkSource} where this is loaded from.\r
+     * @param packageNode The XML element being parsed.\r
+     * @param nsUri The namespace URI of the originating XML document, to be able to deal with\r
+     *          parameters that vary according to the originating XML schema.\r
+     * @param licenses The licenses loaded from the XML originating document.\r
      */\r
-    Package(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
+    Package(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) {\r
         mSource = source;\r
-        mRevision    = XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_REVISION, 0);\r
-        mDescription = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_DESCRIPTION);\r
-        mDescUrl     = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_DESC_URL);\r
-        mReleaseNote = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_RELEASE_NOTE);\r
-        mReleaseUrl  = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_RELEASE_URL);\r
+        mRevision    = XmlParserUtils.getXmlInt   (packageNode, SdkRepoConstants.NODE_REVISION, 0);\r
+        mDescription = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_DESCRIPTION);\r
+        mDescUrl     = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_DESC_URL);\r
+        mReleaseNote = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_RELEASE_NOTE);\r
+        mReleaseUrl  = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_RELEASE_URL);\r
         mObsolete    = XmlParserUtils.getOptionalXmlString(\r
-                                                   packageNode, SdkRepository.NODE_OBSOLETE);\r
+                                                   packageNode, SdkRepoConstants.NODE_OBSOLETE);\r
 \r
         mLicense  = parseLicense(packageNode, licenses);\r
         mArchives = parseArchives(XmlParserUtils.getFirstChild(\r
-                                  packageNode, SdkRepository.NODE_ARCHIVES));\r
+                                  packageNode, SdkRepoConstants.NODE_ARCHIVES));\r
     }\r
 \r
     /**\r
@@ -107,7 +112,7 @@ public abstract class Package implements IDescription, Comparable<Package> {
      * By design, this creates a package with one and only one archive.\r
      */\r
     public Package(\r
-            RepoSource source,\r
+            SdkSource source,\r
             Properties props,\r
             int revision,\r
             String license,\r
@@ -139,7 +144,11 @@ public abstract class Package implements IDescription, Comparable<Package> {
         if (props != null && source == null && srcUrl != null) {\r
             boolean isUser = Boolean.parseBoolean(props.getProperty(PROP_USER_SOURCE,\r
                                                                     Boolean.TRUE.toString()));\r
-            source = new RepoSource(srcUrl, isUser);\r
+            if (isUser || (this instanceof AddonPackage)) {\r
+                source = new SdkAddonSource(srcUrl, isUser);\r
+            } else {\r
+                source = new SdkRepoSource(srcUrl);\r
+            }\r
         }\r
         mSource = source;\r
 \r
@@ -202,9 +211,9 @@ public abstract class Package implements IDescription, Comparable<Package> {
      */\r
     private String parseLicense(Node packageNode, Map<String, String> licenses) {\r
         Node usesLicense = XmlParserUtils.getFirstChild(\r
-                                            packageNode, SdkRepository.NODE_USES_LICENSE);\r
+                                            packageNode, SdkRepoConstants.NODE_USES_LICENSE);\r
         if (usesLicense != null) {\r
-            Node ref = usesLicense.getAttributes().getNamedItem(SdkRepository.ATTR_REF);\r
+            Node ref = usesLicense.getAttributes().getNamedItem(SdkRepoConstants.ATTR_REF);\r
             if (ref != null) {\r
                 String licenseRef = ref.getNodeValue();\r
                 return licenses.get(licenseRef);\r
@@ -227,7 +236,7 @@ public abstract class Package implements IDescription, Comparable<Package> {
 \r
                 if (child.getNodeType() == Node.ELEMENT_NODE &&\r
                         nsUri.equals(child.getNamespaceURI()) &&\r
-                        SdkRepository.NODE_ARCHIVE.equals(child.getLocalName())) {\r
+                        SdkRepoConstants.NODE_ARCHIVE.equals(child.getLocalName())) {\r
                     archives.add(parseArchive(child));\r
                 }\r
             }\r
@@ -242,13 +251,13 @@ public abstract class Package implements IDescription, Comparable<Package> {
     private Archive parseArchive(Node archiveNode) {\r
         Archive a = new Archive(\r
                     this,\r
-                    (Os)   XmlParserUtils.getEnumAttribute(archiveNode, SdkRepository.ATTR_OS,\r
+                    (Os)   XmlParserUtils.getEnumAttribute(archiveNode, SdkRepoConstants.ATTR_OS,\r
                             Os.values(), null),\r
-                    (Arch) XmlParserUtils.getEnumAttribute(archiveNode, SdkRepository.ATTR_ARCH,\r
+                    (Arch) XmlParserUtils.getEnumAttribute(archiveNode, SdkRepoConstants.ATTR_ARCH,\r
                             Arch.values(), Arch.ANY),\r
-                    XmlParserUtils.getXmlString(archiveNode, SdkRepository.NODE_URL),\r
-                    XmlParserUtils.getXmlLong  (archiveNode, SdkRepository.NODE_SIZE, 0),\r
-                    XmlParserUtils.getXmlString(archiveNode, SdkRepository.NODE_CHECKSUM)\r
+                    XmlParserUtils.getXmlString(archiveNode, SdkRepoConstants.NODE_URL),\r
+                    XmlParserUtils.getXmlLong  (archiveNode, SdkRepoConstants.NODE_SIZE, 0),\r
+                    XmlParserUtils.getXmlString(archiveNode, SdkRepoConstants.NODE_CHECKSUM)\r
                 );\r
 \r
         return a;\r
@@ -257,7 +266,7 @@ public abstract class Package implements IDescription, Comparable<Package> {
     /**\r
      * Returns the source that created (and owns) this package. Can be null.\r
      */\r
-    public RepoSource getParentSource() {\r
+    public SdkSource getParentSource() {\r
         return mSource;\r
     }\r
 \r
index 4ca5f3f..1032e03 100755 (executable)
@@ -22,7 +22,7 @@ import com.android.sdklib.SdkConstants;
 import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 \r
 import org.w3c.dom.Node;\r
 \r
@@ -45,15 +45,20 @@ public class PlatformPackage extends MinToolsPackage implements IPackageVersion
 \r
     /**\r
      * Creates a new platform package from the attributes and elements of the given XML node.\r
-     * <p/>\r
      * This constructor should throw an exception if the package cannot be created.\r
+     *\r
+     * @param source The {@link SdkSource} where this is loaded from.\r
+     * @param packageNode The XML element being parsed.\r
+     * @param nsUri The namespace URI of the originating XML document, to be able to deal with\r
+     *          parameters that vary according to the originating XML schema.\r
+     * @param licenses The licenses loaded from the XML originating document.\r
      */\r
-    PlatformPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
-        super(source, packageNode, licenses);\r
+    PlatformPackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) {\r
+        super(source, packageNode, nsUri, licenses);\r
 \r
-        mVersionName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VERSION);\r
-        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
-        String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_CODENAME);\r
+        mVersionName = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_VERSION);\r
+        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);\r
+        String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);\r
         if (codeName.length() == 0) {\r
             codeName = null;\r
         }\r
index 1929636..cfd88e9 100755 (executable)
@@ -34,11 +34,17 @@ public class PlatformToolPackage extends Package {
 
     /**
      * Creates a new platform-tool package from the attributes and elements of the given XML node.
-     * <p/>
      * This constructor should throw an exception if the package cannot be created.
+     *
+     * @param source The {@link SdkSource} where this is loaded from.
+     * @param packageNode The XML element being parsed.
+     * @param nsUri The namespace URI of the originating XML document, to be able to deal with
+     *          parameters that vary according to the originating XML schema.
+     * @param licenses The licenses loaded from the XML originating document.
      */
-    PlatformToolPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {
-        super(source, packageNode, licenses);
+    PlatformToolPackage(SdkSource source, Node packageNode,
+            String nsUri, Map<String,String> licenses) {
+        super(source, packageNode, nsUri, licenses);
     }
 
     /**
@@ -49,7 +55,7 @@ public class PlatformToolPackage extends Package {
      * By design, this creates a package with one and only one archive.
      */
     PlatformToolPackage(
-            RepoSource source,
+            SdkSource source,
             Properties props,
             int revision,
             String license,
index 0d215e8..d60cbaf 100755 (executable)
@@ -23,7 +23,7 @@ import com.android.sdklib.SdkManager;
 import com.android.sdklib.AndroidVersion.AndroidVersionException;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 \r
 import org.w3c.dom.Node;\r
 \r
@@ -56,20 +56,25 @@ public class SamplePackage extends MinToolsPackage
 \r
     /**\r
      * Creates a new sample package from the attributes and elements of the given XML node.\r
-     * <p/>\r
      * This constructor should throw an exception if the package cannot be created.\r
+     *\r
+     * @param source The {@link SdkSource} where this is loaded from.\r
+     * @param packageNode The XML element being parsed.\r
+     * @param nsUri The namespace URI of the originating XML document, to be able to deal with\r
+     *          parameters that vary according to the originating XML schema.\r
+     * @param licenses The licenses loaded from the XML originating document.\r
      */\r
-    SamplePackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
-        super(source, packageNode, licenses);\r
+    SamplePackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) {\r
+        super(source, packageNode, nsUri, licenses);\r
 \r
-        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
-        String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_CODENAME);\r
+        int apiLevel = XmlParserUtils.getXmlInt   (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);\r
+        String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);\r
         if (codeName.length() == 0) {\r
             codeName = null;\r
         }\r
         mVersion = new AndroidVersion(apiLevel, codeName);\r
 \r
-        mMinApiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_API_LEVEL,\r
+        mMinApiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepoConstants.NODE_MIN_API_LEVEL,\r
                 MIN_API_LEVEL_NOT_SPECIFIED);\r
     }\r
 \r
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkAddonSource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkAddonSource.java
new file mode 100755 (executable)
index 0000000..ab24587
--- /dev/null
@@ -0,0 +1,99 @@
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0\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.annotations.Nullable;\r
+import com.android.sdklib.repository.SdkAddonConstants;\r
+\r
+import org.w3c.dom.Document;\r
+\r
+import java.io.InputStream;\r
+\r
+\r
+/**\r
+ * An sdk-addon source, i.e. a download site for addons and extra packages.\r
+ * A repository describes one or more {@link Package}s available for download.\r
+ */\r
+public class SdkAddonSource extends SdkSource {\r
+\r
+    /**\r
+     * Constructs a new source for the given repository URL.\r
+     * @param url The source URL. Cannot be null. If the URL ends with a /, the default\r
+     *            repository.xml filename will be appended automatically.\r
+     * @param userSource True if this a user source (add-ons & packages only.)\r
+     */\r
+    public SdkAddonSource(String url, boolean userSource) {\r
+        super(url, false);\r
+    }\r
+\r
+    /**\r
+     * Returns true if this is an addon source.\r
+     * We only load addons and extras from these sources.\r
+     */\r
+    @Override\r
+    public boolean isAddonSource() {\r
+        return true;\r
+    }\r
+\r
+    @Override\r
+    protected String getUrlDefaultXmlFile() {\r
+        return SdkAddonConstants.URL_DEFAULT_XML_FILE;\r
+    }\r
+\r
+    @Override\r
+    protected int getNsLatestVersion() {\r
+        return SdkAddonConstants.NS_LATEST_VERSION;\r
+    }\r
+\r
+    @Override\r
+    protected String getNsUri() {\r
+        return SdkAddonConstants.NS_URI;\r
+    }\r
+\r
+    @Override\r
+    protected String getNsPattern() {\r
+        return SdkAddonConstants.NS_PATTERN;\r
+    }\r
+\r
+    @Override\r
+    protected String getSchemaUri(int version) {\r
+        return SdkAddonConstants.getSchemaUri(version);\r
+    }\r
+\r
+    @Override\r
+    protected String getRootElementName() {\r
+        return SdkAddonConstants.NODE_SDK_ADDON;\r
+    }\r
+\r
+    @Override\r
+    protected InputStream getXsdStream(int version) {\r
+        return SdkAddonConstants.getXsdStream(version);\r
+    }\r
+\r
+    /**\r
+     * There is no support forward evolution of the sdk-addon schema yet since we\r
+     * currently have only one version.\r
+     *\r
+     * @param xml The input XML stream. Can be null.\r
+     * @return Always null.\r
+     * @null This implementation always return null.\r
+     */\r
+    @Override\r
+    protected Document findAlternateToolsXml(@Nullable InputStream xml) {\r
+        return null;\r
+    }\r
+}\r
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkRepoSource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkRepoSource.java
new file mode 100755 (executable)
index 0000000..d28b2b3
--- /dev/null
@@ -0,0 +1,470 @@
+/*\r
+ * Copyright (C) 2009 The Android Open Source Project\r
+ *\r
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0\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.annotations.Nullable;\r
+import com.android.sdklib.internal.repository.Archive.Arch;\r
+import com.android.sdklib.internal.repository.Archive.Os;\r
+import com.android.sdklib.repository.CommonConstants;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
+\r
+import org.w3c.dom.Attr;\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+import org.w3c.dom.NamedNodeMap;\r
+import org.w3c.dom.Node;\r
+import org.w3c.dom.Text;\r
+\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.xml.parsers.DocumentBuilder;\r
+import javax.xml.parsers.DocumentBuilderFactory;\r
+\r
+\r
+/**\r
+ * An sdk-repository source, i.e. a download site.\r
+ * A repository describes one or more {@link Package}s available for download.\r
+ */\r
+public class SdkRepoSource extends SdkSource {\r
+\r
+    /**\r
+     * Constructs a new source for the given repository URL.\r
+     * @param url The source URL. Cannot be null. If the URL ends with a /, the default\r
+     *            repository.xml filename will be appended automatically.\r
+     */\r
+    public SdkRepoSource(String url) {\r
+        super(url, false);\r
+    }\r
+\r
+    /**\r
+     * Returns true if this is an addon source.\r
+     * We only load addons and extras from these sources.\r
+     */\r
+    @Override\r
+    public boolean isAddonSource() {\r
+        return false;\r
+    }\r
+\r
+    @Override\r
+    protected String getUrlDefaultXmlFile() {\r
+        return SdkRepoConstants.URL_DEFAULT_XML_FILE;\r
+    }\r
+\r
+    @Override\r
+    protected int getNsLatestVersion() {\r
+        return SdkRepoConstants.NS_LATEST_VERSION;\r
+    }\r
+\r
+    @Override\r
+    protected String getNsUri() {\r
+        return SdkRepoConstants.NS_URI;\r
+    }\r
+\r
+    @Override\r
+    protected String getNsPattern() {\r
+        return SdkRepoConstants.NS_PATTERN;\r
+    }\r
+\r
+    @Override\r
+    protected String getSchemaUri(int version) {\r
+        return SdkRepoConstants.getSchemaUri(version);\r
+    }\r
+\r
+    @Override\r
+    protected String getRootElementName() {\r
+        return SdkRepoConstants.NODE_SDK_REPOSITORY;\r
+    }\r
+\r
+    @Override\r
+    protected InputStream getXsdStream(int version) {\r
+        return SdkRepoConstants.getXsdStream(version);\r
+    }\r
+\r
+\r
+    /**\r
+     * The purpose of this method is to support forward evolution of our schema.\r
+     * <p/>\r
+     * At this point, we know that xml does not point to any schema that this version of\r
+     * the tool knows how to process, so it's not one of the possible 1..N versions of our\r
+     * XSD schema.\r
+     * <p/>\r
+     * We thus try to interpret the byte stream as a possible XML stream. It may not be\r
+     * one at all in the first place. If it looks anything line an XML schema, we try to\r
+     * find its &lt;tool&gt; and the &lt;platform-tools&gt; elements. If we find any,\r
+     * we recreate a suitable document that conforms to what we expect from our XSD schema\r
+     * with only those elements.\r
+     * <p/>\r
+     * To be valid, the &lt;tool&gt; and the &lt;platform-tools&gt; elements must have at\r
+     * least one &lt;archive&gt; compatible with this platform.\r
+     * <p/>\r
+     * Starting the sdk-repository schema v3, &lt;tools&gt; has a &lt;min-platform-tools-rev&gt;\r
+     * node, so technically the corresponding XML schema will be usable only if there's a\r
+     * &lt;platform-tools&gt; with the request revision number. We don't enforce that here, as\r
+     * this is done at install time.\r
+     * <p/>\r
+     * If we don't find anything suitable, we drop the whole thing.\r
+     *\r
+     * @param xml The input XML stream. Can be null.\r
+     * @return Either a new XML document conforming to our schema with at least one &lt;tool&gt;\r
+     *         and &lt;platform-tools&gt; element or null.\r
+     * @throws IOException if InputStream.reset() fails\r
+     * @null Can return null on failure.\r
+     */\r
+    @Override\r
+    protected Document findAlternateToolsXml(@Nullable InputStream xml) throws IOException {\r
+        if (xml == null) {\r
+            return null;\r
+        }\r
+\r
+        // Reset the stream if it supports that operation.\r
+        xml.reset();\r
+\r
+        // Get an XML document\r
+\r
+        Document oldDoc = null;\r
+        Document newDoc = null;\r
+        try {\r
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\r
+            factory.setIgnoringComments(false);\r
+            factory.setValidating(false);\r
+\r
+            // Parse the old document using a non namespace aware builder\r
+            factory.setNamespaceAware(false);\r
+            DocumentBuilder builder = factory.newDocumentBuilder();\r
+            oldDoc = builder.parse(xml);\r
+\r
+            // Prepare a new document using a namespace aware builder\r
+            factory.setNamespaceAware(true);\r
+            builder = factory.newDocumentBuilder();\r
+            newDoc = builder.newDocument();\r
+\r
+        } catch (Exception e) {\r
+            // Failed to get builder factor\r
+            // Failed to create XML document builder\r
+            // Failed to parse XML document\r
+            // Failed to read XML document\r
+        }\r
+\r
+        if (oldDoc == null || newDoc == null) {\r
+            return null;\r
+        }\r
+\r
+\r
+        // Check the root element is an XML with at least the following properties:\r
+        // <sdk:sdk-repository\r
+        //    xmlns:sdk="http://schemas.android.com/sdk/android/repository/$N">\r
+        //\r
+        // Note that we don't have namespace support enabled, we just do it manually.\r
+\r
+        Pattern nsPattern = Pattern.compile(getNsPattern());\r
+\r
+        Node oldRoot = null;\r
+        String prefix = null;\r
+        for (Node child = oldDoc.getFirstChild(); child != null; child = child.getNextSibling()) {\r
+            if (child.getNodeType() == Node.ELEMENT_NODE) {\r
+                prefix = null;\r
+                String name = child.getNodeName();\r
+                int pos = name.indexOf(':');\r
+                if (pos > 0 && pos < name.length() - 1) {\r
+                    prefix = name.substring(0, pos);\r
+                    name = name.substring(pos + 1);\r
+                }\r
+                if (SdkRepoConstants.NODE_SDK_REPOSITORY.equals(name)) {\r
+                    NamedNodeMap attrs = child.getAttributes();\r
+                    String xmlns = "xmlns";                                         //$NON-NLS-1$\r
+                    if (prefix != null) {\r
+                        xmlns += ":" + prefix;                                      //$NON-NLS-1$\r
+                    }\r
+                    Node attr = attrs.getNamedItem(xmlns);\r
+                    if (attr != null) {\r
+                        String uri = attr.getNodeValue();\r
+                        if (uri != null && nsPattern.matcher(uri).matches()) {\r
+                            oldRoot = child;\r
+                            break;\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        // we must have found the root node, and it must have an XML namespace prefix.\r
+        if (oldRoot == null || prefix == null || prefix.length() == 0) {\r
+            return null;\r
+        }\r
+\r
+        final String ns = getNsUri();\r
+        Element newRoot = newDoc.createElementNS(ns, getRootElementName());\r
+        newRoot.setPrefix(prefix);\r
+        newDoc.appendChild(newRoot);\r
+        int numTool = 0;\r
+\r
+        // Find any inner <tool> or <platform-tool> nodes and extract their required parameters\r
+\r
+        String[] elementNames = {\r
+                SdkRepoConstants.NODE_TOOL,\r
+                SdkRepoConstants.NODE_PLATFORM_TOOL,\r
+                SdkRepoConstants.NODE_LICENSE\r
+        };\r
+\r
+        Element element = null;\r
+        while ((element = findChild(oldRoot, element, prefix, elementNames)) != null) {\r
+            boolean isElementValid = false;\r
+\r
+            String name = element.getLocalName();\r
+            if (name == null) {\r
+                name = element.getNodeName();\r
+\r
+                int pos = name.indexOf(':');\r
+                if (pos > 0 && pos < name.length() - 1) {\r
+                    name = name.substring(pos + 1);\r
+                }\r
+            }\r
+\r
+            // To be valid, the tool or platform-tool element must have:\r
+            // - a <revision> element with a number\r
+            // - a <min-platform-tools-rev> element with a number for a <tool> element\r
+            // - an <archives> element with one or more <archive> elements inside\r
+            // - one of the <archive> elements must have an "os" and "arch" attributes\r
+            //   compatible with the current platform. Only keep the first such element found.\r
+            // - the <archive> element must contain a <size>, a <checksum> and a <url>.\r
+            // - none of the above for a license element\r
+\r
+            if (SdkRepoConstants.NODE_LICENSE.equals(name)) {\r
+                isElementValid = true;\r
+\r
+            } else {\r
+                try {\r
+                    Node revision = findChild(element, null, prefix, CommonConstants.NODE_REVISION);\r
+                    Node archives = findChild(element, null, prefix, CommonConstants.NODE_ARCHIVES);\r
+\r
+                    if (revision == null || archives == null) {\r
+                        continue;\r
+                    }\r
+\r
+                    // check revision contains a number\r
+                    try {\r
+                        String content = revision.getTextContent();\r
+                        content = content.trim();\r
+                        int rev = Integer.parseInt(content);\r
+                        if (rev < 1) {\r
+                            continue;\r
+                        }\r
+                    } catch (NumberFormatException ignore) {\r
+                        continue;\r
+                    }\r
+\r
+                    if (SdkRepoConstants.NODE_TOOL.equals(name)) {\r
+                        Node minPTRev = findChild(element, null, prefix,\r
+                                CommonConstants.NODE_MIN_PLATFORM_TOOLS_REV);\r
+\r
+                        if (minPTRev == null) {\r
+                            continue;\r
+                        }\r
+\r
+                        // check min-platform-tools-rev contains a number\r
+                        try {\r
+                            String content = minPTRev.getTextContent();\r
+                            content = content.trim();\r
+                            int rev = Integer.parseInt(content);\r
+                            if (rev < 1) {\r
+                                continue;\r
+                            }\r
+                        } catch (NumberFormatException ignore) {\r
+                            continue;\r
+                        }\r
+                    }\r
+\r
+                    Node archive = null;\r
+                    while ((archive = findChild(archives,\r
+                                                archive,\r
+                                                prefix,\r
+                                                CommonConstants.NODE_ARCHIVE)) != null) {\r
+                        try {\r
+                            Os os = (Os) XmlParserUtils.getEnumAttribute(archive,\r
+                                    CommonConstants.ATTR_OS,\r
+                                    Os.values(),\r
+                                    null /*default*/);\r
+                            Arch arch = (Arch) XmlParserUtils.getEnumAttribute(archive,\r
+                                    CommonConstants.ATTR_ARCH,\r
+                                    Arch.values(),\r
+                                    Arch.ANY);\r
+                            if (os == null || !os.isCompatible() ||\r
+                                    arch == null || !arch.isCompatible()) {\r
+                                continue;\r
+                            }\r
+\r
+                            Node node = findChild(archive, null, prefix, CommonConstants.NODE_URL);\r
+                            String url = node == null ? null : node.getTextContent().trim();\r
+                            if (url == null || url.length() == 0) {\r
+                                continue;\r
+                            }\r
+\r
+                            node = findChild(archive, null, prefix, CommonConstants.NODE_SIZE);\r
+                            long size = 0;\r
+                            try {\r
+                                size = Long.parseLong(node.getTextContent());\r
+                            } catch (Exception e) {\r
+                                // pass\r
+                            }\r
+                            if (size < 1) {\r
+                                continue;\r
+                            }\r
+\r
+                            node = findChild(archive, null, prefix, CommonConstants.NODE_CHECKSUM);\r
+                            // double check that the checksum element contains a type=sha1 attribute\r
+                            if (node == null) {\r
+                                continue;\r
+                            }\r
+                            NamedNodeMap attrs = node.getAttributes();\r
+                            Node typeNode = attrs.getNamedItem(CommonConstants.ATTR_TYPE);\r
+                            if (typeNode == null ||\r
+                                    !CommonConstants.ATTR_TYPE.equals(typeNode.getNodeName()) ||\r
+                                    !CommonConstants.SHA1_TYPE.equals(typeNode.getNodeValue())) {\r
+                                continue;\r
+                            }\r
+                            String sha1 = node == null ? null : node.getTextContent().trim();\r
+                            if (sha1 == null ||\r
+                                    sha1.length() != CommonConstants.SHA1_CHECKSUM_LEN) {\r
+                                continue;\r
+                            }\r
+\r
+                            isElementValid = true;\r
+\r
+                        } catch (Exception ignore1) {\r
+                            // pass\r
+                        }\r
+                    } // while <archive>\r
+                } catch (Exception ignore2) {\r
+                    // For debugging it is useful to re-throw the exception.\r
+                    // For end-users, not so much. It would be nice to make it\r
+                    // happen automatically during unit tests.\r
+                    if (System.getenv("TESTING") != null) {\r
+                        throw new RuntimeException(ignore2);\r
+                    }\r
+                }\r
+            }\r
+\r
+            if (isElementValid) {\r
+                duplicateNode(newRoot, element, SdkRepoConstants.NS_URI, prefix);\r
+                numTool++;\r
+            }\r
+        } // while <tool>\r
+\r
+        return numTool > 0 ? newDoc : null;\r
+    }\r
+\r
+    /**\r
+     * Helper method used by {@link #findAlternateToolsXml(InputStream)} to find a given\r
+     * element child in a root XML node.\r
+     */\r
+    private Element findChild(Node rootNode, Node after, String prefix, String[] nodeNames) {\r
+        for (int i = 0; i < nodeNames.length; i++) {\r
+            if (nodeNames[i].indexOf(':') < 0) {\r
+                nodeNames[i] = prefix + ":" + nodeNames[i];\r
+            }\r
+        }\r
+        Node child = after == null ? rootNode.getFirstChild() : after.getNextSibling();\r
+        for(; child != null; child = child.getNextSibling()) {\r
+            if (child.getNodeType() != Node.ELEMENT_NODE) {\r
+                continue;\r
+            }\r
+            for (String nodeName : nodeNames) {\r
+                if (nodeName.equals(child.getNodeName())) {\r
+                    return (Element) child;\r
+                }\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * Helper method used by {@link #findAlternateToolsXml(InputStream)} to find a given\r
+     * element child in a root XML node.\r
+     */\r
+    private Node findChild(Node rootNode, Node after, String prefix, String nodeName) {\r
+        return findChild(rootNode, after, prefix, new String[] { nodeName });\r
+    }\r
+\r
+    /**\r
+     * Helper method used by {@link #findAlternateToolsXml(InputStream)} to duplicate a node\r
+     * and attach it to the given root in the new document.\r
+     */\r
+    private Element duplicateNode(Element newRootNode, Element oldNode,\r
+            String namespaceUri, String prefix) {\r
+        // The implementation here is more or less equivalent to\r
+        //\r
+        //    newRoot.appendChild(newDoc.importNode(oldNode, deep=true))\r
+        //\r
+        // except we can't just use importNode() since we need to deal with the fact\r
+        // that the old document is not namespace-aware yet the new one is.\r
+\r
+        Document newDoc = newRootNode.getOwnerDocument();\r
+        Element newNode = null;\r
+\r
+        String nodeName = oldNode.getNodeName();\r
+        int pos = nodeName.indexOf(':');\r
+        if (pos > 0 && pos < nodeName.length() - 1) {\r
+            nodeName = nodeName.substring(pos + 1);\r
+            newNode = newDoc.createElementNS(namespaceUri, nodeName);\r
+            newNode.setPrefix(prefix);\r
+        } else {\r
+            newNode = newDoc.createElement(nodeName);\r
+        }\r
+\r
+        newRootNode.appendChild(newNode);\r
+\r
+        // Merge in all the attributes\r
+        NamedNodeMap attrs = oldNode.getAttributes();\r
+        for (int i = 0; i < attrs.getLength(); i++) {\r
+            Attr attr = (Attr) attrs.item(i);\r
+            Attr newAttr = null;\r
+\r
+            String attrName = oldNode.getNodeName();\r
+            pos = attrName.indexOf(':');\r
+            if (pos > 0 && pos < attrName.length() - 1) {\r
+                attrName = attrName.substring(pos + 1);\r
+                newAttr = newDoc.createAttributeNS(namespaceUri, attrName);\r
+                newAttr.setPrefix(prefix);\r
+            } else {\r
+                newAttr = newDoc.createAttribute(attrName);\r
+            }\r
+\r
+            newAttr.setNodeValue(attr.getNodeValue());\r
+\r
+            if (pos > 0) {\r
+                newNode.getAttributes().setNamedItemNS(newAttr);\r
+            } else {\r
+                newNode.getAttributes().setNamedItem(newAttr);\r
+            }\r
+        }\r
+\r
+        // Merge all child elements and texts\r
+        for (Node child = oldNode.getFirstChild(); child != null; child = child.getNextSibling()) {\r
+            if (child.getNodeType() == Node.ELEMENT_NODE) {\r
+                duplicateNode(newNode, (Element) child, namespaceUri, prefix);\r
+\r
+            } else if (child.getNodeType() == Node.TEXT_NODE) {\r
+                Text newText = newDoc.createTextNode(child.getNodeValue());\r
+                newNode.appendChild(newText);\r
+            }\r
+        }\r
+\r
+        return newNode;\r
+    }\r
+}\r
 \r
 package com.android.sdklib.internal.repository;\r
 \r
-import com.android.sdklib.internal.repository.Archive.Arch;\r
-import com.android.sdklib.internal.repository.Archive.Os;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.annotations.Nullable;\r
+import com.android.sdklib.annotations.VisibleForTesting;\r
+import com.android.sdklib.annotations.VisibleForTesting.Visibility;\r
+import com.android.sdklib.repository.CommonConstants;\r
+import com.android.sdklib.repository.SdkAddonConstants;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 \r
 import org.w3c.dom.Document;\r
-import org.w3c.dom.Element;\r
 import org.w3c.dom.NamedNodeMap;\r
 import org.w3c.dom.Node;\r
 import org.xml.sax.InputSource;\r
@@ -50,11 +52,11 @@ import javax.xml.validation.SchemaFactory;
 import javax.xml.validation.Validator;\r
 \r
 /**\r
- * An sdk-repository source, i.e. a download site.\r
+ * An sdk-addon or sdk-repository source, i.e. a download site.\r
  * It may be a full repository or an add-on only repository.\r
  * A repository describes one or {@link Package}s available for download.\r
  */\r
-public class RepoSource implements IDescription {\r
+public abstract class SdkSource implements IDescription {\r
 \r
     private String mUrl;\r
     private final boolean mUserSource;\r
@@ -69,14 +71,14 @@ public class RepoSource implements IDescription {
      *            repository.xml filename will be appended automatically.\r
      * @param userSource True if this a user source (add-ons & packages only.)\r
      */\r
-    public RepoSource(String url, boolean userSource) {\r
+    public SdkSource(String url, boolean userSource) {\r
 \r
         // if the URL ends with a /, it must be "directory" resource,\r
         // in which case we automatically add the default file that will\r
         // looked for. This way it will be obvious to the user which\r
         // resource we are actually trying to fetch.\r
         if (url.endsWith("/")) {  //$NON-NLS-1$\r
-            url += SdkRepository.URL_DEFAULT_XML_FILE;\r
+            url += getUrlDefaultXmlFile();\r
         }\r
 \r
         mUrl = url;\r
@@ -85,12 +87,52 @@ public class RepoSource implements IDescription {
     }\r
 \r
     /**\r
+     * Returns true if this is an addon source.\r
+     * We only load addons and extras from these sources.\r
+     */\r
+    public abstract boolean isAddonSource();\r
+\r
+    /** Returns SdkRepoConstants.URL_DEFAULT_XML_FILE or SdkAddonConstants.URL_DEFAULT_XML_FILE */\r
+    protected abstract String getUrlDefaultXmlFile();\r
+\r
+    /** Returns SdkRepoConstants.NS_LATEST_VERSION or SdkAddonConstants.NS_LATEST_VERSION. */\r
+    protected abstract int getNsLatestVersion();\r
+\r
+    /** Returns SdkRepoConstants.NS_URI or SdkAddonConstants.NS_URI. */\r
+    protected abstract String getNsUri();\r
+\r
+    /** Returns SdkRepoConstants.NS_PATTERN or SdkAddonConstants.NS_PATTERN. */\r
+    protected abstract String getNsPattern();\r
+\r
+    /** Returns SdkRepoConstants.getSchemaUri() or SdkAddonConstants.getSchemaUri(). */\r
+    protected abstract String getSchemaUri(int version);\r
+\r
+    /* Returns SdkRepoConstants.NODE_SDK_REPOSITORY or SdkAddonConstants.NODE_SDK_ADDON. */\r
+    protected abstract String getRootElementName();\r
+\r
+    /** Returns SdkRepoConstants.getXsdStream() or SdkAddonConstants.getXsdStream(). */\r
+    protected abstract InputStream getXsdStream(int version);\r
+\r
+    /**\r
+     * In case we fail to load an XML, examine the XML to see if it matches a <b>future</b>\r
+     * schema that as at least a <code>tools</code> node that we could load to update the\r
+     * SDK Manager.\r
+     *\r
+     * @param xml The input XML stream. Can be null.\r
+     * @return Null on failure, otherwise returns an XML DOM with just the tools we\r
+     *   need to update this SDK Manager.\r
+     * @null Can return null on failure.\r
+     */\r
+    protected abstract Document findAlternateToolsXml(@Nullable InputStream xml)\r
+        throws IOException;\r
+\r
+    /**\r
      * Two repo source are equal if they have the same userSource flag and the same URL.\r
      */\r
     @Override\r
     public boolean equals(Object obj) {\r
-        if (obj instanceof RepoSource) {\r
-            RepoSource rs = (RepoSource) obj;\r
+        if (obj instanceof SdkSource) {\r
+            SdkSource rs = (SdkSource) obj;\r
             return  rs.isUserSource() == this.isUserSource() && rs.getUrl().equals(this.getUrl());\r
         }\r
         return false;\r
@@ -101,13 +143,12 @@ public class RepoSource implements IDescription {
         return mUrl.hashCode() ^ Boolean.valueOf(mUserSource).hashCode();\r
     }\r
 \r
-    /** Returns true if this is a user source. We only load addon and extra packages\r
-     * from a user source and ignore the rest. */\r
+    /** Returns true if this is a user source. */\r
     public boolean isUserSource() {\r
         return mUserSource;\r
     }\r
 \r
-    /** Returns the URL of the repository.xml file for this source. */\r
+    /** Returns the URL of the XML file for this source. */\r
     public String getUrl() {\r
         return mUrl;\r
     }\r
@@ -173,11 +214,11 @@ public class RepoSource implements IDescription {
 \r
         // If the original URL can't be fetched and the URL doesn't explicitly end with\r
         // our filename, make another tentative after changing the URL.\r
-        if (xml == null && !url.endsWith(SdkRepository.URL_DEFAULT_XML_FILE)) {\r
+        if (xml == null && !url.endsWith(getUrlDefaultXmlFile())) {\r
             if (!url.endsWith("/")) {       //$NON-NLS-1$\r
                 url += "/";                 //$NON-NLS-1$\r
             }\r
-            url += SdkRepository.URL_DEFAULT_XML_FILE;\r
+            url += getUrlDefaultXmlFile();\r
 \r
             xml = fetchUrl(url, exception);\r
             usingAlternateUrl = true;\r
@@ -190,7 +231,7 @@ public class RepoSource implements IDescription {
                 // Explore the XML to find the potential XML schema version\r
                 int version = getXmlSchemaVersion(xml);\r
 \r
-                if (version >= 1 && version <= SdkRepository.NS_LATEST_VERSION) {\r
+                if (version >= 1 && version <= getNsLatestVersion()) {\r
                     // This should be a version we can handle. Try to validate it\r
                     // and report any error as invalid XML syntax,\r
 \r
@@ -216,15 +257,19 @@ public class RepoSource implements IDescription {
                         // what looks like a suitable root element with a suitable XMLNS\r
                         // so it must be a genuine error of an XML not conforming to the schema.\r
                     }\r
-                } else if (version > SdkRepository.NS_LATEST_VERSION) {\r
+                } else if (version > getNsLatestVersion()) {\r
                     // The schema used is more recent than what is supported by this tool.\r
                     // Tell the user to upgrade, pointing him to the right version of the tool\r
                     // package.\r
 \r
-                    validatedDoc = findAlternateToolsXml(xml);\r
+                    try {\r
+                        validatedDoc = findAlternateToolsXml(xml);\r
+                    } catch (IOException e) {\r
+                        // Failed, will be handled below.\r
+                    }\r
                     if (validatedDoc != null) {\r
                         validationError[0] = null;  // remove error from XML validation\r
-                        validatedUri = SdkRepository.NS_SDK_REPOSITORY;\r
+                        validatedUri = getNsUri();\r
                         usingAlternateXml = true;\r
                     }\r
 \r
@@ -237,11 +282,11 @@ public class RepoSource implements IDescription {
                     // If we haven't already tried the alternate URL, let's do it now.\r
                     // We don't capture any fetch exception that happen during the second\r
                     // fetch in order to avoid hidding any previous fetch errors.\r
-                    if (!url.endsWith(SdkRepository.URL_DEFAULT_XML_FILE)) {\r
+                    if (!url.endsWith(getUrlDefaultXmlFile())) {\r
                         if (!url.endsWith("/")) {       //$NON-NLS-1$\r
                             url += "/";                 //$NON-NLS-1$\r
                         }\r
-                        url += SdkRepository.URL_DEFAULT_XML_FILE;\r
+                        url += getUrlDefaultXmlFile();\r
 \r
                         xml = fetchUrl(url, null /*outException*/);\r
 \r
@@ -412,10 +457,11 @@ public class RepoSource implements IDescription {
     /**\r
      * Validates this XML against one of the requested SDK Repository schemas.\r
      * If the XML was correctly validated, returns the schema that worked.\r
-     * If it doesn't validate, returns null and store the error in outError[0].\r
+     * If it doesn't validate, returns null and stores the error in outError[0].\r
      * If we can't find a validator, returns null and set validatorFound[0] to false.\r
      */\r
-    private String validateXml(InputStream xml, String url, int version,\r
+    @VisibleForTesting(visibility=Visibility.PRIVATE)\r
+    protected String validateXml(InputStream xml, String url, int version,\r
             String[] outError, Boolean[] validatorFound) {\r
 \r
         if (xml == null) {\r
@@ -446,7 +492,7 @@ public class RepoSource implements IDescription {
 \r
             // Validation throws a bunch of possible Exceptions on failure.\r
             validator.validate(new StreamSource(xml));\r
-            return SdkRepository.getSchemaUri(version);\r
+            return getSchemaUri(version);\r
 \r
         } catch (SAXParseException e) {\r
             outError[0] = String.format(\r
@@ -458,7 +504,7 @@ public class RepoSource implements IDescription {
 \r
         } catch (Exception e) {\r
             outError[0] = String.format(\r
-                    "XML verification failed for %1$s.\nError: %4$s",\r
+                    "XML verification failed for %1$s.\nError: %2$s",\r
                     url,\r
                     e.toString());\r
         }\r
@@ -470,10 +516,11 @@ public class RepoSource implements IDescription {
      * at the end of the xmlns:sdk="http://schemas.android.com/sdk/android/repository/$N"\r
      * declaration.\r
      *\r
-     * @return 1..{@link SdkRepository#NS_LATEST_VERSION} for a valid schema version\r
+     * @return 1..{@link SdkRepoConstants#NS_LATEST_VERSION} for a valid schema version\r
      *         or 0 if no schema could be found.\r
      */\r
-    private int getXmlSchemaVersion(InputStream xml) {\r
+    @VisibleForTesting(visibility=Visibility.PRIVATE)\r
+    protected int getXmlSchemaVersion(InputStream xml) {\r
         if (xml == null) {\r
             return 0;\r
         }\r
@@ -520,7 +567,7 @@ public class RepoSource implements IDescription {
         //\r
         // Note that we don't have namespace support enabled, we just do it manually.\r
 \r
-        Pattern nsPattern = Pattern.compile(SdkRepository.NS_SDK_REPOSITORY_PATTERN);\r
+        Pattern nsPattern = Pattern.compile(getNsPattern());\r
 \r
         String prefix = null;\r
         for (Node child = doc.getFirstChild(); child != null; child = child.getNextSibling()) {\r
@@ -532,7 +579,7 @@ public class RepoSource implements IDescription {
                     prefix = name.substring(0, pos);\r
                     name = name.substring(pos + 1);\r
                 }\r
-                if (SdkRepository.NODE_SDK_REPOSITORY.equals(name)) {\r
+                if (getRootElementName().equals(name)) {\r
                     NamedNodeMap attrs = child.getAttributes();\r
                     String xmlns = "xmlns";                                         //$NON-NLS-1$\r
                     if (prefix != null) {\r
@@ -561,285 +608,14 @@ public class RepoSource implements IDescription {
     }\r
 \r
     /**\r
-     * The purpose of this method is to support forward evolution of our schema.\r
-     * <p/>\r
-     * At this point, we know that xml does not point to any schema that this version of\r
-     * the tool know how to process, so it's not one of the possible 1..N versions of our\r
-     * XSD schema.\r
-     * <p/>\r
-     * We thus try to interpret the byte stream as a possible XML stream. It may not be\r
-     * one at all in the first place. If it looks anything line an XML schema, we try to\r
-     * find its &lt;tool&gt; elements. If we find any, we recreate a suitable document\r
-     * that conforms to what we expect from our XSD schema with only those elements.\r
-     * To be valid, the &lt;tool&gt; element must have at least one &lt;archive&gt;\r
-     * compatible with this platform.\r
-     *\r
-     * If we don't find anything suitable, we drop the whole thing.\r
-     *\r
-     * @param xml The input XML stream. Can be null.\r
-     * @return Either a new XML document conforming to our schema with at least one &lt;tool&gt;\r
-     *         element or null.\r
-     */\r
-    protected Document findAlternateToolsXml(InputStream xml) {\r
-        // Note: protected for unit-test access\r
-\r
-        if (xml == null) {\r
-            return null;\r
-        }\r
-\r
-        // Reset the stream if it supports that operation.\r
-        // At runtime we use a ByteArrayInputStream which can be reset; however for unit tests\r
-        // we use a FileInputStream that doesn't support resetting and is read-once.\r
-        try {\r
-            xml.reset();\r
-        } catch (IOException e1) {\r
-            // ignore if not supported\r
-        }\r
-\r
-        // Get an XML document\r
-\r
-        Document oldDoc = null;\r
-        Document newDoc = null;\r
-        try {\r
-            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\r
-            factory.setIgnoringComments(false);\r
-            factory.setValidating(false);\r
-\r
-            // Parse the old document using a non namespace aware builder\r
-            factory.setNamespaceAware(false);\r
-            DocumentBuilder builder = factory.newDocumentBuilder();\r
-            oldDoc = builder.parse(xml);\r
-\r
-            // Prepare a new document using a namespace aware builder\r
-            factory.setNamespaceAware(true);\r
-            builder = factory.newDocumentBuilder();\r
-            newDoc = builder.newDocument();\r
-\r
-        } catch (Exception e) {\r
-            // Failed to get builder factor\r
-            // Failed to create XML document builder\r
-            // Failed to parse XML document\r
-            // Failed to read XML document\r
-        }\r
-\r
-        if (oldDoc == null || newDoc == null) {\r
-            return null;\r
-        }\r
-\r
-\r
-        // Check the root element is an XML with at least the following properties:\r
-        // <sdk:sdk-repository\r
-        //    xmlns:sdk="http://schemas.android.com/sdk/android/repository/$N">\r
-        //\r
-        // Note that we don't have namespace support enabled, we just do it manually.\r
-\r
-        Pattern nsPattern = Pattern.compile(SdkRepository.NS_SDK_REPOSITORY_PATTERN);\r
-\r
-        Node oldRoot = null;\r
-        String prefix = null;\r
-        for (Node child = oldDoc.getFirstChild(); child != null; child = child.getNextSibling()) {\r
-            if (child.getNodeType() == Node.ELEMENT_NODE) {\r
-                prefix = null;\r
-                String name = child.getNodeName();\r
-                int pos = name.indexOf(':');\r
-                if (pos > 0 && pos < name.length() - 1) {\r
-                    prefix = name.substring(0, pos);\r
-                    name = name.substring(pos + 1);\r
-                }\r
-                if (SdkRepository.NODE_SDK_REPOSITORY.equals(name)) {\r
-                    NamedNodeMap attrs = child.getAttributes();\r
-                    String xmlns = "xmlns";                                         //$NON-NLS-1$\r
-                    if (prefix != null) {\r
-                        xmlns += ":" + prefix;                                      //$NON-NLS-1$\r
-                    }\r
-                    Node attr = attrs.getNamedItem(xmlns);\r
-                    if (attr != null) {\r
-                        String uri = attr.getNodeValue();\r
-                        if (uri != null && nsPattern.matcher(uri).matches()) {\r
-                            oldRoot = child;\r
-                            break;\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-        // we must have found the root node, and it must have an XML namespace prefix.\r
-        if (oldRoot == null || prefix == null || prefix.length() == 0) {\r
-            return null;\r
-        }\r
-\r
-        final String ns = SdkRepository.NS_SDK_REPOSITORY;\r
-        Element newRoot = newDoc.createElementNS(ns, SdkRepository.NODE_SDK_REPOSITORY);\r
-        newRoot.setPrefix(prefix);\r
-        newDoc.appendChild(newRoot);\r
-        int numTool = 0;\r
-\r
-        // Find an inner <tool> node and extract its required parameters\r
-\r
-        Node tool = null;\r
-        while ((tool = findChild(oldRoot, tool, prefix, SdkRepository.NODE_TOOL)) != null) {\r
-            // To be valid, the tool element must have:\r
-            // - a <revision> element with a number\r
-            // - an optional <uses-license> node, which we'll skip right now.\r
-            //   (if we add it later, we must find the license declaration element too)\r
-            // - an <archives> element with one or more <archive> elements inside\r
-            // - one of the <archive> elements must have an "os" and "arch" attributes\r
-            //   compatible with the current platform. Only keep the first such element found.\r
-            // - the <archive> element must contain a <size>, a <checksum> and a <url>.\r
-\r
-            try {\r
-                Node revision = findChild(tool, null, prefix, SdkRepository.NODE_REVISION);\r
-                Node archives = findChild(tool, null, prefix, SdkRepository.NODE_ARCHIVES);\r
-\r
-                if (revision == null || archives == null) {\r
-                    continue;\r
-                }\r
-\r
-                int rev = 0;\r
-                try {\r
-                    String content = revision.getTextContent();\r
-                    content = content.trim();\r
-                    rev = Integer.parseInt(content);\r
-                    if (rev < 1) {\r
-                        continue;\r
-                    }\r
-                } catch (NumberFormatException ignore) {\r
-                    continue;\r
-                }\r
-\r
-                Element newTool = newDoc.createElementNS(ns, SdkRepository.NODE_TOOL);\r
-                newTool.setPrefix(prefix);\r
-                appendChild(newTool, ns, prefix,\r
-                        SdkRepository.NODE_REVISION, Integer.toString(rev));\r
-                Element newArchives = appendChild(newTool, ns, prefix,\r
-                                                  SdkRepository.NODE_ARCHIVES, null);\r
-                int numArchives = 0;\r
-\r
-                Node archive = null;\r
-                while ((archive = findChild(archives,\r
-                                            archive,\r
-                                            prefix,\r
-                                            SdkRepository.NODE_ARCHIVE)) != null) {\r
-                    try {\r
-                        Os os = (Os) XmlParserUtils.getEnumAttribute(archive,\r
-                                SdkRepository.ATTR_OS,\r
-                                Os.values(),\r
-                                null /*default*/);\r
-                        Arch arch = (Arch) XmlParserUtils.getEnumAttribute(archive,\r
-                                SdkRepository.ATTR_ARCH,\r
-                                Arch.values(),\r
-                                Arch.ANY);\r
-                        if (os == null || !os.isCompatible() ||\r
-                                arch == null || !arch.isCompatible()) {\r
-                            continue;\r
-                        }\r
-\r
-                        Node node = findChild(archive, null, prefix, SdkRepository.NODE_URL);\r
-                        String url = node == null ? null : node.getTextContent().trim();\r
-                        if (url == null || url.length() == 0) {\r
-                            continue;\r
-                        }\r
-\r
-                        node = findChild(archive, null, prefix, SdkRepository.NODE_SIZE);\r
-                        long size = 0;\r
-                        try {\r
-                            size = Long.parseLong(node.getTextContent());\r
-                        } catch (Exception e) {\r
-                            // pass\r
-                        }\r
-                        if (size < 1) {\r
-                            continue;\r
-                        }\r
-\r
-                        node = findChild(archive, null, prefix, SdkRepository.NODE_CHECKSUM);\r
-                        // double check that the checksum element contains a type=sha1 attribute\r
-                        if (node == null) {\r
-                            continue;\r
-                        }\r
-                        NamedNodeMap attrs = node.getAttributes();\r
-                        Node typeNode = attrs.getNamedItem(SdkRepository.ATTR_TYPE);\r
-                        if (typeNode == null ||\r
-                                !SdkRepository.ATTR_TYPE.equals(typeNode.getNodeName()) ||\r
-                                !SdkRepository.SHA1_TYPE.equals(typeNode.getNodeValue())) {\r
-                            continue;\r
-                        }\r
-                        String sha1 = node == null ? null : node.getTextContent().trim();\r
-                        if (sha1 == null || sha1.length() != SdkRepository.SHA1_CHECKSUM_LEN) {\r
-                            continue;\r
-                        }\r
-\r
-                        // Use that archive for the new tool element\r
-                        Element ar = appendChild(newArchives, ns, prefix,\r
-                                                 SdkRepository.NODE_ARCHIVE, null);\r
-                        ar.setAttributeNS(ns, SdkRepository.ATTR_OS, os.getXmlName());\r
-                        ar.setAttributeNS(ns, SdkRepository.ATTR_ARCH, arch.getXmlName());\r
-\r
-                        appendChild(ar, ns, prefix, SdkRepository.NODE_URL, url);\r
-                        appendChild(ar, ns, prefix, SdkRepository.NODE_SIZE, Long.toString(size));\r
-                        Element cs = appendChild(ar, ns, prefix, SdkRepository.NODE_CHECKSUM, sha1);\r
-                        cs.setAttributeNS(ns, SdkRepository.ATTR_TYPE, SdkRepository.SHA1_TYPE);\r
-\r
-                        numArchives++;\r
-\r
-                    } catch (Exception ignore1) {\r
-                        // pass\r
-                    }\r
-                } // while <archive>\r
-\r
-                if (numArchives > 0) {\r
-                    newRoot.appendChild(newTool);\r
-                    numTool++;\r
-                }\r
-            } catch (Exception ignore2) {\r
-                // pass\r
-            }\r
-        } // while <tool>\r
-\r
-        return numTool > 0 ? newDoc : null;\r
-    }\r
-\r
-    /**\r
-     * Helper method used by {@link #findAlternateToolsXml(InputStream)} to find a given\r
-     * element child in a root XML node.\r
-     */\r
-    private Node findChild(Node rootNode, Node after, String prefix, String nodeName) {\r
-        nodeName = prefix + ":" + nodeName;\r
-        Node child = after == null ? rootNode.getFirstChild() : after.getNextSibling();\r
-        for(; child != null; child = child.getNextSibling()) {\r
-            if (child.getNodeType() == Node.ELEMENT_NODE && nodeName.equals(child.getNodeName())) {\r
-                return child;\r
-            }\r
-        }\r
-        return null;\r
-    }\r
-\r
-    /**\r
-     * Helper method used by {@link #findAlternateToolsXml(InputStream)} to create a new\r
-     * XML element into a parent element.\r
-     */\r
-    private Element appendChild(Element rootNode, String namespaceUri,\r
-            String prefix, String nodeName,\r
-            String nodeValue) {\r
-        Element node = rootNode.getOwnerDocument().createElementNS(namespaceUri, nodeName);\r
-        node.setPrefix(prefix);\r
-        if (nodeValue != null) {\r
-            node.setTextContent(nodeValue);\r
-        }\r
-        rootNode.appendChild(node);\r
-        return node;\r
-    }\r
-\r
-\r
-    /**\r
      * Helper method that returns a validator for our XSD, or null if the current Java\r
      * implementation can't process XSD schemas.\r
      *\r
      * @param version The version of the XML Schema.\r
-     *        See {@link SdkRepository#getXsdStream(int)}\r
+     *        See {@link SdkRepoConstants#getXsdStream(int)}\r
      */\r
     private Validator getValidator(int version) throws SAXException {\r
-        InputStream xsdStream = SdkRepository.getXsdStream(version);\r
+        InputStream xsdStream = getXsdStream(version);\r
         SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);\r
 \r
         if (factory == null) {\r
@@ -864,7 +640,7 @@ public class RepoSource implements IDescription {
 \r
         assert doc != null;\r
 \r
-        Node root = getFirstChild(doc, nsUri, SdkRepository.NODE_SDK_REPOSITORY);\r
+        Node root = getFirstChild(doc, nsUri, getRootElementName());\r
         if (root != null) {\r
 \r
             ArrayList<Package> packages = new ArrayList<Package>();\r
@@ -876,8 +652,8 @@ public class RepoSource implements IDescription {
                  child = child.getNextSibling()) {\r
                 if (child.getNodeType() == Node.ELEMENT_NODE &&\r
                         nsUri.equals(child.getNamespaceURI()) &&\r
-                        child.getLocalName().equals(SdkRepository.NODE_LICENSE)) {\r
-                    Node id = child.getAttributes().getNamedItem(SdkRepository.ATTR_ID);\r
+                        child.getLocalName().equals(CommonConstants.NODE_LICENSE)) {\r
+                    Node id = child.getAttributes().getNamedItem(CommonConstants.ATTR_ID);\r
                     if (id != null) {\r
                         licenses.put(id.getNodeValue(), child.getTextContent());\r
                     }\r
@@ -896,25 +672,25 @@ public class RepoSource implements IDescription {
                     try {\r
                         // We can load addon and extra packages from all sources, either\r
                         // internal or user sources.\r
-                        if (SdkRepository.NODE_ADD_ON.equals(name)) {\r
-                            p = new AddonPackage(this, child, licenses);\r
+                        if (SdkAddonConstants.NODE_ADD_ON.equals(name)) {\r
+                            p = new AddonPackage(this, child, nsUri, licenses);\r
 \r
-                        } else if (SdkRepository.NODE_EXTRA.equals(name)) {\r
-                            p = new ExtraPackage(this, child, licenses);\r
+                        } else if (CommonConstants.NODE_EXTRA.equals(name)) {\r
+                            p = new ExtraPackage(this, child, nsUri, licenses);\r
 \r
                         } else if (!mUserSource) {\r
                             // We only load platform, doc and tool packages from internal\r
                             // sources, never from user sources.\r
-                            if (SdkRepository.NODE_PLATFORM.equals(name)) {\r
-                                p = new PlatformPackage(this, child, licenses);\r
-                            } else if (SdkRepository.NODE_DOC.equals(name)) {\r
-                                p = new DocPackage(this, child, licenses);\r
-                            } else if (SdkRepository.NODE_TOOL.equals(name)) {\r
-                                p = new ToolPackage(this, child, licenses);\r
-                            } else if (SdkRepository.NODE_PLATFORM_TOOL.equals(name)) {\r
-                                p = new PlatformToolPackage(this, child, licenses);\r
-                            } else if (SdkRepository.NODE_SAMPLE.equals(name)) {\r
-                                p = new SamplePackage(this, child, licenses);\r
+                            if (SdkRepoConstants.NODE_PLATFORM.equals(name)) {\r
+                                p = new PlatformPackage(this, child, nsUri, licenses);\r
+                            } else if (SdkRepoConstants.NODE_DOC.equals(name)) {\r
+                                p = new DocPackage(this, child, nsUri, licenses);\r
+                            } else if (SdkRepoConstants.NODE_TOOL.equals(name)) {\r
+                                p = new ToolPackage(this, child, nsUri, licenses);\r
+                            } else if (SdkRepoConstants.NODE_PLATFORM_TOOL.equals(name)) {\r
+                                p = new PlatformToolPackage(this, child, nsUri, licenses);\r
+                            } else if (SdkRepoConstants.NODE_SAMPLE.equals(name)) {\r
+                                p = new SamplePackage(this, child, nsUri, licenses);\r
                             }\r
                         }\r
 \r
@@ -924,6 +700,8 @@ public class RepoSource implements IDescription {
                         }\r
                     } catch (Exception e) {\r
                         // Ignore invalid packages\r
+                        monitor.setResult("Ignoring invalid %1$s element: %2$s",\r
+                                name, e.toString());\r
                     }\r
                 }\r
             }\r
@@ -962,7 +740,8 @@ public class RepoSource implements IDescription {
      *\r
      * On error, returns null and prints a (hopefully) useful message on the monitor.\r
      */\r
-    private Document getDocument(ByteArrayInputStream xml, ITaskMonitor monitor) {\r
+    @VisibleForTesting(visibility=Visibility.PRIVATE)\r
+    protected Document getDocument(InputStream xml, ITaskMonitor monitor) {\r
         try {\r
             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\r
             factory.setIgnoringComments(true);\r
@@ -29,9 +29,9 @@ import java.util.Iterator;
 import java.util.Properties;\r
 \r
 /**\r
- * A list of sdk-repository sources.\r
+ * A list of sdk-repository and sdk-addon sources.\r
  */\r
-public class RepoSources {\r
+public class SdkSources {\r
 \r
     private static final String KEY_COUNT = "count";\r
 \r
@@ -39,30 +39,30 @@ public class RepoSources {
 \r
     private static final String SRC_FILENAME = "repositories.cfg"; //$NON-NLS-1$\r
 \r
-    private ArrayList<RepoSource> mSources = new ArrayList<RepoSource>();\r
+    private ArrayList<SdkSource> mSources = new ArrayList<SdkSource>();\r
 \r
-    public RepoSources() {\r
+    public SdkSources() {\r
     }\r
 \r
     /**\r
      * Adds a new source to the Sources list.\r
      */\r
-    public void add(RepoSource source) {\r
+    public void add(SdkSource source) {\r
         mSources.add(source);\r
     }\r
 \r
     /**\r
      * Removes a source from the Sources list.\r
      */\r
-    public void remove(RepoSource source) {\r
+    public void remove(SdkSource source) {\r
         mSources.remove(source);\r
     }\r
 \r
     /**\r
      * Returns the sources list array. This is never null.\r
      */\r
-    public RepoSource[] getSources() {\r
-        return mSources.toArray(new RepoSource[mSources.size()]);\r
+    public SdkSource[] getSources() {\r
+        return mSources.toArray(new SdkSource[mSources.size()]);\r
     }\r
 \r
     /**\r
@@ -72,8 +72,8 @@ public class RepoSources {
     public void loadUserSources(ISdkLog log) {\r
 \r
         // Remove all existing user sources\r
-        for (Iterator<RepoSource> it = mSources.iterator(); it.hasNext(); ) {\r
-            RepoSource s = it.next();\r
+        for (Iterator<SdkSource> it = mSources.iterator(); it.hasNext(); ) {\r
+            SdkSource s = it.next();\r
             if (s.isUserSource()) {\r
                 it.remove();\r
             }\r
@@ -95,7 +95,7 @@ public class RepoSources {
                 for (int i = 0; i < count; i++) {\r
                     String url = props.getProperty(String.format("%s%02d", KEY_SRC, i));  //$NON-NLS-1$\r
                     if (url != null) {\r
-                        RepoSource s = new RepoSource(url, true /*userSource*/);\r
+                        SdkSource s = new SdkAddonSource(url, true /*userSource*/);\r
                         if (!hasSource(s)) {\r
                             mSources.add(s);\r
                         }\r
@@ -127,8 +127,8 @@ public class RepoSources {
      * <p/>\r
      * The search is O(N), which should be acceptable on the expectedly small source list.\r
      */\r
-    public boolean hasSource(RepoSource source) {\r
-        for (RepoSource s : mSources) {\r
+    public boolean hasSource(SdkSource source) {\r
+        for (SdkSource s : mSources) {\r
             if (s.equals(source)) {\r
                 return true;\r
             }\r
@@ -151,7 +151,7 @@ public class RepoSources {
             Properties props = new Properties();\r
 \r
             int count = 0;\r
-            for (RepoSource s : mSources) {\r
+            for (SdkSource s : mSources) {\r
                 if (s.isUserSource()) {\r
                     count++;\r
                     props.setProperty(String.format("%s%02d", KEY_SRC, count), s.getUrl());  //$NON-NLS-1$\r
index 0e5ed47..24316e4 100755 (executable)
@@ -20,7 +20,7 @@ import com.android.sdklib.SdkConstants;
 import com.android.sdklib.SdkManager;\r
 import com.android.sdklib.internal.repository.Archive.Arch;\r
 import com.android.sdklib.internal.repository.Archive.Os;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 \r
 import org.w3c.dom.Node;\r
 \r
@@ -30,6 +30,8 @@ import java.io.IOException;
 import java.io.InputStreamReader;\r
 import java.util.Map;\r
 import java.util.Properties;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
 \r
 /**\r
  * Represents a tool XML node in an SDK repository.\r
@@ -47,21 +49,44 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency
 \r
     /**\r
      * Creates a new tool package from the attributes and elements of the given XML node.\r
-     * <p/>\r
      * This constructor should throw an exception if the package cannot be created.\r
+     *\r
+     * @param source The {@link SdkSource} where this is loaded from.\r
+     * @param packageNode The XML element being parsed.\r
+     * @param nsUri The namespace URI of the originating XML document, to be able to deal with\r
+     *          parameters that vary according to the originating XML schema.\r
+     * @param licenses The licenses loaded from the XML originating document.\r
      */\r
-    ToolPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
-        super(source, packageNode, licenses);\r
+    ToolPackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) {\r
+        super(source, packageNode, nsUri, licenses);\r
 \r
         mMinPlatformToolsRevision = XmlParserUtils.getXmlInt(\r
                 packageNode,\r
-                SdkRepository.NODE_MIN_PLATFORM_TOOLS_REV,\r
+                SdkRepoConstants.NODE_MIN_PLATFORM_TOOLS_REV,\r
                 MIN_PLATFORM_TOOLS_REV_INVALID);\r
         if (mMinPlatformToolsRevision == MIN_PLATFORM_TOOLS_REV_INVALID) {\r
-            throw new IllegalArgumentException(\r
-                    String.format("Missing %1$s element in %2$s package",\r
-                            SdkRepository.NODE_MIN_PLATFORM_TOOLS_REV,\r
-                            SdkRepository.NODE_PLATFORM_TOOL));\r
+            // This revision number is mandatory starting with sdk-repository-3.xsd\r
+            // and did not exist before. Complain if the URI has level >= 3.\r
+\r
+            boolean needRevision = false;\r
+\r
+            Pattern nsPattern = Pattern.compile(SdkRepoConstants.NS_PATTERN);\r
+            Matcher m = nsPattern.matcher(nsUri);\r
+            if (m.matches()) {\r
+                String version = m.group(1);\r
+                try {\r
+                    needRevision = Integer.parseInt(version) >= 3;\r
+                } catch (NumberFormatException e) {\r
+                    // ignore. needRevision defaults to false\r
+                }\r
+            }\r
+\r
+            if (needRevision) {\r
+                throw new IllegalArgumentException(\r
+                        String.format("Missing %1$s element in %2$s package",\r
+                                SdkRepoConstants.NODE_MIN_PLATFORM_TOOLS_REV,\r
+                                SdkRepoConstants.NODE_PLATFORM_TOOL));\r
+            }\r
         }\r
     }\r
 \r
@@ -73,7 +98,7 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency
      * By design, this creates a package with one and only one archive.\r
      */\r
     ToolPackage(\r
-            RepoSource source,\r
+            SdkSource source,\r
             Properties props,\r
             int revision,\r
             String license,\r
@@ -1,5 +1,5 @@
 /*\r
- * Copyright (C) 2009 The Android Open Source Project\r
+ * Copyright (C) 2010 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
 package com.android.sdklib.repository;\r
 \r
 \r
-import java.io.InputStream;\r
 \r
 /**\r
- * Public constants for the sdk-repository XML Schema.\r
+ * Public constants common to the sdk-repository and sdk-addon XML Schemas.\r
  */\r
-public class SdkRepository {\r
-\r
-    /** The URL of the official Google sdk-repository site. */\r
-    public static final String URL_GOOGLE_SDK_REPO_SITE =\r
-        "https://dl-ssl.google.com/android/repository/";                        //$NON-NLS-1$\r
-\r
-    public static final String URL_DEFAULT_XML_FILE = "repository.xml";         //$NON-NLS-1$\r
-\r
-    /** The base of our XML namespace. */\r
-    private static final String NS_SDK_REPOSITORY_BASE =\r
-        "http://schemas.android.com/sdk/android/repository/";                   //$NON-NLS-1$\r
-\r
-    /** The pattern of our XML namespace. Matcher's group(1) is the schema version (integer). */\r
-    public static final String NS_SDK_REPOSITORY_PATTERN =\r
-        NS_SDK_REPOSITORY_BASE + "([1-9][0-9]*)";        //$NON-NLS-1$\r
-\r
-    /** The latest version of the sdk-repository XML Schema.\r
-     *  Valid version numbers are between 1 and this number, included. */\r
-    public static final int NS_LATEST_VERSION = 3;\r
-\r
-    /** The XML namespace of the latest sdk-repository XML. */\r
-    public static final String NS_SDK_REPOSITORY = getSchemaUri(NS_LATEST_VERSION);\r
-\r
-    /** The root sdk-repository element */\r
-    public static final String NODE_SDK_REPOSITORY = "sdk-repository";        //$NON-NLS-1$\r
-\r
-    /** A platform package. */\r
-    public static final String NODE_PLATFORM        = "platform";             //$NON-NLS-1$\r
-    /** An add-on package. */\r
-    public static final String NODE_ADD_ON          = "add-on";               //$NON-NLS-1$\r
-    /** A tool package. */\r
-    public static final String NODE_TOOL            = "tool";                 //$NON-NLS-1$\r
-    /** A platform-tool package. */\r
-    public static final String NODE_PLATFORM_TOOL   = "platform-tool";        //$NON-NLS-1$\r
-    /** A doc package. */\r
-    public static final String NODE_DOC             = "doc";                  //$NON-NLS-1$\r
-    /** A sample package. */\r
-    public static final String NODE_SAMPLE          = "sample";               //$NON-NLS-1$\r
+public class CommonConstants {\r
+\r
     /** An extra package. */\r
     public static final String NODE_EXTRA           = "extra";                //$NON-NLS-1$\r
 \r
-    /**\r
-     * List of possible nodes in a repository XML. Used to populate options automatically\r
-     * in the no-GUI mode.\r
-     */\r
-    public static final String[] NODES = {\r
-        NODE_PLATFORM,\r
-        NODE_ADD_ON,\r
-        NODE_TOOL,\r
-        NODE_PLATFORM_TOOL,\r
-        NODE_DOC,\r
-        NODE_SAMPLE,\r
-        NODE_EXTRA\r
-    };\r
-\r
     /** The license definition. */\r
     public static final String NODE_LICENSE       = "license";                  //$NON-NLS-1$\r
     /** The optional uses-license for all packages or for a lib. */\r
@@ -150,24 +99,4 @@ public class SdkRepository {
     /** Length of a string representing a SHA1 checksum; always 40 characters long. */\r
     public static final int SHA1_CHECKSUM_LEN = 40;\r
 \r
-\r
-    /**\r
-     * Returns a stream to the requested repository XML Schema.\r
-     *\r
-     * @param version Between 1 and {@link #NS_LATEST_VERSION}, included.\r
-     * @return An {@link InputStream} object for the local XSD file or\r
-     *         null if there is no schema for the requested version.\r
-     */\r
-    public static InputStream getXsdStream(int version) {\r
-        String filename = String.format("sdk-repository-%d.xsd", version);      //$NON-NLS-1$\r
-        return SdkRepository.class.getResourceAsStream(filename);\r
-    }\r
-\r
-    /**\r
-     * Returns the URI of the SDK Repository schema for the given version number.\r
-     * @param version Between 1 and {@link #NS_LATEST_VERSION} included.\r
-     */\r
-    public static String getSchemaUri(int version) {\r
-        return String.format(NS_SDK_REPOSITORY_BASE + "%d", version);           //$NON-NLS-1$\r
-    }\r
 }\r
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkAddonConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkAddonConstants.java
new file mode 100755 (executable)
index 0000000..4f228c6
--- /dev/null
@@ -0,0 +1,84 @@
+/*\r
+ * Copyright (C) 2010 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.repository;\r
+\r
+\r
+import java.io.InputStream;\r
+\r
+/**\r
+ * Public constants for the sdk-addon XML Schema.\r
+ */\r
+public class SdkAddonConstants extends CommonConstants {\r
+\r
+    /** The URL of the official Google sdk-addon site. */\r
+    public static final String URL_GOOGLE_SDK_SITE =\r
+        "https://dl-ssl.google.com/android/repository/";                        //$NON-NLS-1$\r
+\r
+    public static final String URL_DEFAULT_XML_FILE = "addon.xml";         //$NON-NLS-1$\r
+\r
+    /** The base of our sdk-addon XML namespace. */\r
+    private static final String NS_BASE =\r
+        "http://schemas.android.com/sdk/android/addon/";                   //$NON-NLS-1$\r
+\r
+    /**\r
+     * The pattern of our sdk-addon XML namespace.\r
+     * Matcher's group(1) is the schema version (integer).\r
+     */\r
+    public static final String NS_PATTERN = NS_BASE + "([1-9][0-9]*)";     //$NON-NLS-1$\r
+\r
+    /** The latest version of the sdk-addon XML Schema.\r
+     *  Valid version numbers are between 1 and this number, included. */\r
+    public static final int NS_LATEST_VERSION = 3;\r
+\r
+    /** The XML namespace of the latest sdk-addon XML. */\r
+    public static final String NS_URI = getSchemaUri(NS_LATEST_VERSION);\r
+\r
+    /** The root sdk-addon element */\r
+    public static final String NODE_SDK_ADDON       = "sdk-addon";         //$NON-NLS-1$\r
+\r
+    /** An add-on package. */\r
+    public static final String NODE_ADD_ON          = "add-on";            //$NON-NLS-1$\r
+\r
+    /**\r
+     * List of possible nodes in a repository XML. Used to populate options automatically\r
+     * in the no-GUI mode.\r
+     */\r
+    public static final String[] NODES = {\r
+        NODE_ADD_ON,\r
+        NODE_EXTRA\r
+    };\r
+\r
+    /**\r
+     * Returns a stream to the requested sdk-addon XML Schema.\r
+     *\r
+     * @param version Between 1 and {@link #NS_LATEST_VERSION}, included.\r
+     * @return An {@link InputStream} object for the local XSD file or\r
+     *         null if there is no schema for the requested version.\r
+     */\r
+    public static InputStream getXsdStream(int version) {\r
+        String filename = String.format("sdk-addon-%d.xsd", version);      //$NON-NLS-1$\r
+        return SdkAddonConstants.class.getResourceAsStream(filename);\r
+    }\r
+\r
+    /**\r
+     * Returns the URI of the sdk-addon schema for the given version number.\r
+     * @param version Between 1 and {@link #NS_LATEST_VERSION} included.\r
+     */\r
+    public static String getSchemaUri(int version) {\r
+        return String.format(NS_BASE + "%d", version);           //$NON-NLS-1$\r
+    }\r
+}\r
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepoConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepoConstants.java
new file mode 100755 (executable)
index 0000000..45d5f16
--- /dev/null
@@ -0,0 +1,96 @@
+/*\r
+ * Copyright (C) 2009 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.repository;\r
+\r
+\r
+import java.io.InputStream;\r
+\r
+/**\r
+ * Public constants for the sdk-repository XML Schema.\r
+ */\r
+public class SdkRepoConstants extends CommonConstants {\r
+\r
+    /** The URL of the official Google sdk-repository site. */\r
+    public static final String URL_GOOGLE_SDK_SITE =\r
+        "https://dl-ssl.google.com/android/repository/";                        //$NON-NLS-1$\r
+\r
+    public static final String URL_DEFAULT_XML_FILE = "repository.xml";         //$NON-NLS-1$\r
+\r
+    /** The base of our sdk-repository XML namespace. */\r
+    private static final String NS_BASE =\r
+        "http://schemas.android.com/sdk/android/repository/";                   //$NON-NLS-1$\r
+\r
+    /**\r
+     * The pattern of our sdk-repository XML namespace.\r
+     * Matcher's group(1) is the schema version (integer).\r
+     */\r
+    public static final String NS_PATTERN = NS_BASE + "([1-9][0-9]*)";          //$NON-NLS-1$\r
+\r
+    /** The latest version of the sdk-repository XML Schema.\r
+     *  Valid version numbers are between 1 and this number, included. */\r
+    public static final int NS_LATEST_VERSION = 3;\r
+\r
+    /** The XML namespace of the latest sdk-repository XML. */\r
+    public static final String NS_URI = getSchemaUri(NS_LATEST_VERSION);\r
+\r
+    /** The root sdk-repository element */\r
+    public static final String NODE_SDK_REPOSITORY = "sdk-repository";        //$NON-NLS-1$\r
+\r
+    /** A platform package. */\r
+    public static final String NODE_PLATFORM        = "platform";             //$NON-NLS-1$\r
+    /** A tool package. */\r
+    public static final String NODE_TOOL            = "tool";                 //$NON-NLS-1$\r
+    /** A platform-tool package. */\r
+    public static final String NODE_PLATFORM_TOOL   = "platform-tool";        //$NON-NLS-1$\r
+    /** A doc package. */\r
+    public static final String NODE_DOC             = "doc";                  //$NON-NLS-1$\r
+    /** A sample package. */\r
+    public static final String NODE_SAMPLE          = "sample";               //$NON-NLS-1$\r
+\r
+    /**\r
+     * List of possible nodes in a repository XML. Used to populate options automatically\r
+     * in the no-GUI mode.\r
+     */\r
+    public static final String[] NODES = {\r
+        NODE_PLATFORM,\r
+        NODE_TOOL,\r
+        NODE_PLATFORM_TOOL,\r
+        NODE_DOC,\r
+        NODE_SAMPLE,\r
+        NODE_EXTRA\r
+    };\r
+\r
+    /**\r
+     * Returns a stream to the requested repository XML Schema.\r
+     *\r
+     * @param version Between 1 and {@link #NS_LATEST_VERSION}, included.\r
+     * @return An {@link InputStream} object for the local XSD file or\r
+     *         null if there is no schema for the requested version.\r
+     */\r
+    public static InputStream getXsdStream(int version) {\r
+        String filename = String.format("sdk-repository-%d.xsd", version);      //$NON-NLS-1$\r
+        return SdkRepoConstants.class.getResourceAsStream(filename);\r
+    }\r
+\r
+    /**\r
+     * Returns the URI of the SDK Repository schema for the given version number.\r
+     * @param version Between 1 and {@link #NS_LATEST_VERSION} included.\r
+     */\r
+    public static String getSchemaUri(int version) {\r
+        return String.format(NS_BASE + "%d", version);           //$NON-NLS-1$\r
+    }\r
+}\r
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-addon-1.xsd b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-addon-1.xsd
new file mode 100755 (executable)
index 0000000..041a038
--- /dev/null
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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.
+-->
+<xsd:schema
+    targetNamespace="http://schemas.android.com/sdk/android/addon/1"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+    xmlns:sa1="http://schemas.android.com/sdk/android/addon/1"
+    elementFormDefault="qualified"
+    attributeFormDefault="unqualified"
+    version="1">
+
+    <!-- The repository contains a collection of downloadable items known as
+         "packages". Each package has a type and various attributes and contains
+         a list of file "archives" that can be downloaded for specific OSes.
+
+         An Android Addon repository is a web site that contains an "addon.xml"
+         file that conforms to this XML Schema.
+
+         History:
+         - v1 is used by the SDK Updater in Tools r8. It is split out of the
+           main SDK Repository XML Schema and can only contain <addon> and
+           <extra> packages.
+    -->
+
+    <xsd:element name="sdk-addon" type="sa1:repositoryType" />
+
+    <xsd:complexType name="repositoryType">
+        <xsd:annotation>
+            <xsd:documentation>
+                The repository contains a collection of downloadable packages.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:choice minOccurs="0" maxOccurs="unbounded">
+            <xsd:element name="add-on"          type="sa1:addonType"        />
+            <xsd:element name="extra"           type="sa1:extraType"        />
+            <xsd:element name="license"         type="sa1:licenseType"      />
+        </xsd:choice>
+    </xsd:complexType>
+
+    <!-- The definition of an SDK Add-on package. -->
+
+    <xsd:complexType name="addonType">
+        <xsd:annotation>
+            <xsd:documentation>An SDK add-on package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The name of the add-on. -->
+            <xsd:element name="name"      type="xsd:normalizedString" />
+            <!-- The vendor of the add-on. -->
+            <xsd:element name="vendor"    type="xsd:normalizedString" />
+            <!-- The Android API Level for the add-on. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this add-on, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sa1:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sa1:archivesType" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+
+            <!-- An add-on can declare 0 or more libraries. -->
+
+            <xsd:element name="libs">
+                <xsd:complexType>
+                    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+                        <xsd:element name="lib">
+                            <xsd:complexType>
+                                <xsd:all>
+                                    <!-- The name of the library. -->
+                                    <xsd:element name="name" type="xsd:normalizedString" />
+                                    <!-- The optional description of this add-on library. -->
+                                    <xsd:element name="description" type="xsd:string" minOccurs="0" />
+                                </xsd:all>
+                            </xsd:complexType>
+                        </xsd:element>
+                    </xsd:sequence>
+                </xsd:complexType>
+            </xsd:element>
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of an SDK extra package. This kind of package is for
+         "free" content and specifies in which fixed root directory it must be
+         installed.
+    -->
+
+    <xsd:complexType name="extraType" >
+        <xsd:annotation>
+            <xsd:documentation>
+                An SDK extra package. This kind of package is for "free"
+                content and specifies in which fixed root directory it must be
+                installed.
+                The paths "add-ons", "platforms", "tools" and "docs" are
+                reserved and cannot be used.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The install folder name. It must be a single-segment path.
+                 The paths "add-ons", "platforms", "tools" and "docs" are
+                 reserved and cannot be used.
+            -->
+            <xsd:element name="path">
+                <xsd:simpleType>
+                    <xsd:restriction base="xsd:token">
+                        <xsd:pattern value="[^/\\]+"/>
+                    </xsd:restriction>
+                </xsd:simpleType>
+            </xsd:element>
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sa1:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sa1:archivesType" />
+            <!-- The minimal revision of tools required by this package.
+                 Optional. If present, must be an int > 0. -->
+            <xsd:element name="min-tools-rev" type="xsd:positiveInteger" minOccurs="0" />
+            <!-- The minimal API level required by this package.
+                 Optional. If present, must be an int > 0. -->
+            <xsd:element name="min-api-level" type="xsd:positiveInteger" minOccurs="0" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of a license to be referenced by the uses-license element. -->
+
+    <xsd:complexType name="licenseType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A license definition. Such a license must be used later as a reference
+                using a uses-license element in one of the package elements.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:simpleContent>
+            <xsd:extension base="xsd:string">
+                <xsd:attribute name="id"   type="xsd:ID" />
+                <xsd:attribute name="type" type="xsd:token" fixed="text" />
+            </xsd:extension>
+        </xsd:simpleContent>
+    </xsd:complexType>
+
+
+    <!-- Type describing the license used by a package.
+         The license MUST be defined using a license node and referenced
+         using the ref attribute of the license element inside a package.
+     -->
+
+    <xsd:complexType name="usesLicenseType">
+        <xsd:annotation>
+            <xsd:documentation>
+                Describes the license used by a package. The license MUST be defined
+                using a license node and referenced using the ref attribute of the
+                license element inside a package.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:attribute name="ref" type="xsd:IDREF" />
+    </xsd:complexType>
+
+
+    <!-- A collection of files that can be downloaded for a given architecture.
+         The <archives> node is mandatory in the repository elements and the
+         collection must have at least one <archive> declared.
+         Each archive is a zip file that will be unzipped in a location that depends
+         on its package type.
+     -->
+
+    <xsd:complexType name="archivesType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A collection of files that can be downloaded for a given architecture.
+                The &lt;archives&gt; node is mandatory in the repository packages and the
+                collection must have at least one &lt;archive&gt; declared.
+                Each archive is a zip file that will be unzipped in a location that depends
+                on its package type.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:sequence minOccurs="1" maxOccurs="unbounded">
+            <!-- One archive file -->
+            <xsd:element name="archive">
+                <xsd:complexType>
+                    <!-- Properties of the archive file -->
+                    <xsd:all>
+                        <!-- The size in bytes of the archive to download. -->
+                        <xsd:element name="size"     type="xsd:positiveInteger" />
+                        <!-- The checksum of the archive file. -->
+                        <xsd:element name="checksum" type="sa1:checksumType" />
+                        <!-- The URL is an absolute URL if it starts with http://, https://
+                             or ftp://. Otherwise it is relative to the parent directory that
+                             contains this repository.xml -->
+                        <xsd:element name="url"      type="xsd:token" />
+                    </xsd:all>
+
+                    <!-- Attributes that identify the OS and architecture -->
+                    <xsd:attribute name="os" use="required">
+                        <xsd:simpleType>
+                            <xsd:restriction base="xsd:token">
+                                <xsd:enumeration value="any" />
+                                <xsd:enumeration value="linux" />
+                                <xsd:enumeration value="macosx" />
+                                <xsd:enumeration value="windows" />
+                            </xsd:restriction>
+                        </xsd:simpleType>
+                    </xsd:attribute>
+                    <xsd:attribute name="arch" use="optional">
+                        <xsd:simpleType>
+                            <xsd:restriction base="xsd:token">
+                                <xsd:enumeration value="any" />
+                                <xsd:enumeration value="ppc" />
+                                <xsd:enumeration value="x86" />
+                                <xsd:enumeration value="x86_64" />
+                            </xsd:restriction>
+                        </xsd:simpleType>
+                    </xsd:attribute>
+                </xsd:complexType>
+            </xsd:element>
+        </xsd:sequence>
+    </xsd:complexType>
+
+
+    <!-- The definition of a file checksum -->
+
+    <xsd:simpleType name="sha1Number">
+        <xsd:annotation>
+            <xsd:documentation>A SHA1 checksum.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:string">
+            <xsd:pattern value="([0-9a-fA-F]){40}"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:complexType name="checksumType">
+        <xsd:annotation>
+            <xsd:documentation>A file checksum, currently only SHA1.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:simpleContent>
+            <xsd:extension base="sa1:sha1Number">
+                <xsd:attribute name="type" type="xsd:token" fixed="sha1" />
+            </xsd:extension>
+        </xsd:simpleContent>
+    </xsd:complexType>
+
+</xsd:schema>
index 6638756..89c74d2 100755 (executable)
@@ -46,6 +46,7 @@
               and platform-specific tools are installed in $SDK/platform-tools
             - There's a new element <min-platform-tools-rev> in <tool>. The tool package now
               requires that at least some minimal version of <platform-tool> be installed.
+            - It removes the <addon> repository type, which is now in its own XML Schema.
     -->
 
     <xsd:element name="sdk-repository" type="sr3:repositoryType" />
@@ -58,7 +59,6 @@
         </xsd:annotation>
         <xsd:choice minOccurs="0" maxOccurs="unbounded">
             <xsd:element name="platform"        type="sr3:platformType"     />
-            <xsd:element name="add-on"          type="sr3:addonType"        />
             <xsd:element name="tool"            type="sr3:toolType"         />
             <xsd:element name="platform-tool"   type="sr3:platformToolType" />
             <xsd:element name="doc"             type="sr3:docType"          />
     </xsd:complexType>
 
 
-    <!-- The definition of an SDK Add-on package. -->
-
-    <xsd:complexType name="addonType">
-        <xsd:annotation>
-            <xsd:documentation>An SDK add-on package.</xsd:documentation>
-        </xsd:annotation>
-        <xsd:all>
-            <!-- The name of the add-on. -->
-            <xsd:element name="name"      type="xsd:normalizedString" />
-            <!-- The vendor of the add-on. -->
-            <xsd:element name="vendor"    type="xsd:normalizedString" />
-            <!-- The Android API Level for the add-on. An int > 0. -->
-            <xsd:element name="api-level" type="xsd:positiveInteger"  />
-            <!-- The optional codename for this add-on, if it's a preview. -->
-            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
-
-            <!-- The revision, an int > 0, incremented each time a new
-                 package is generated. -->
-            <xsd:element name="revision"     type="xsd:positiveInteger" />
-            <!-- The optional license of this package. If present, users will have
-                 to agree to it before downloading. -->
-            <xsd:element name="uses-license" type="sr3:usesLicenseType" minOccurs="0" />
-            <!-- The optional description of this package. -->
-            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
-            <!-- The optional description URL of this package -->
-            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
-            <!-- The optional release note for this package. -->
-            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
-            <!-- The optional release note URL of this package -->
-            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
-            <!-- A list of file archives for this package. -->
-            <xsd:element name="archives"     type="sr3:archivesType" />
-
-            <!-- An optional element indicating the package is obsolete.
-                 The string content is however currently not defined and ignored. -->
-            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
-
-            <!-- An add-on can declare 0 or more libraries. -->
-
-            <xsd:element name="libs">
-                <xsd:complexType>
-                    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
-                        <xsd:element name="lib">
-                            <xsd:complexType>
-                                <xsd:all>
-                                    <!-- The name of the library. -->
-                                    <xsd:element name="name" type="xsd:normalizedString" />
-                                    <!-- The optional description of this add-on library. -->
-                                    <xsd:element name="description" type="xsd:string" minOccurs="0" />
-                                </xsd:all>
-                            </xsd:complexType>
-                        </xsd:element>
-                    </xsd:sequence>
-                </xsd:complexType>
-            </xsd:element>
-        </xsd:all>
-    </xsd:complexType>
-
-
     <!-- The definition of an SDK tool package. -->
 
     <xsd:complexType name="toolType" >
                 An SDK extra package. This kind of package is for "free"
                 content and specifies in which fixed root directory it must be
                 installed.
-                The paths "add-ons", "platforms", "tools" and "docs" are
-                reserved and cannot be used.
+                Path names matching existing package types here (e.g. "add-ons",
+                "platforms", "tools", "docs", etc.) are reserved and cannot be used.
             </xsd:documentation>
         </xsd:annotation>
         <xsd:all>
 \r
 package com.android.sdklib.internal.repository;\r
 \r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkAddonConstants;\r
 \r
 import org.w3c.dom.Document;\r
 \r
 import java.io.ByteArrayInputStream;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
 import java.io.InputStream;\r
 \r
 import junit.framework.TestCase;\r
 \r
 /**\r
- * Tests for {@link RepoSource}\r
+ * Tests for {@link SdkAddonSource}\r
  */\r
-public class RepoSourceTest extends TestCase {\r
+public class SdkAddonSourceTest extends TestCase {\r
 \r
     private static class MockMonitor implements ITaskMonitor {\r
+\r
+        String mCapturedResults = "";\r
+        String mCapturedDescriptions = "";\r
+\r
+        public String getCapturedResults() {\r
+            return mCapturedResults;\r
+        }\r
+\r
+        public String getCapturedDescriptions() {\r
+            return mCapturedDescriptions;\r
+        }\r
+\r
         public void setResult(String resultFormat, Object... args) {\r
+            mCapturedResults += String.format(resultFormat, args) + "\n";\r
         }\r
 \r
         public void setProgressMax(int max) {\r
         }\r
 \r
         public void setDescription(String descriptionFormat, Object... args) {\r
+            mCapturedDescriptions += String.format(descriptionFormat, args) + "\n";\r
         }\r
 \r
         public boolean isCancelRequested() {\r
@@ -64,9 +80,9 @@ public class RepoSourceTest extends TestCase {
      * An internal helper class to give us visibility to the protected members we want\r
      * to test.\r
      */\r
-    private static class MockRepoSource extends RepoSource {\r
-        public MockRepoSource() {\r
-            super("fake-url", false /*userSource*/);\r
+    private static class MockSdkAddonSource extends SdkAddonSource {\r
+        public MockSdkAddonSource() {\r
+            super("fake-url", true /*userSource*/);\r
         }\r
 \r
         public Document _findAlternateToolsXml(InputStream xml) {\r
@@ -76,15 +92,29 @@ public class RepoSourceTest extends TestCase {
         public boolean _parsePackages(Document doc, String nsUri, ITaskMonitor monitor) {\r
             return super.parsePackages(doc, nsUri, monitor);\r
         }\r
+\r
+        public int _getXmlSchemaVersion(InputStream xml) {\r
+            return super.getXmlSchemaVersion(xml);\r
+        }\r
+\r
+        public String _validateXml(InputStream xml, String url, int version,\r
+                                   String[] outError, Boolean[] validatorFound) {\r
+            return super.validateXml(xml, url, version, outError, validatorFound);\r
+        }\r
+\r
+        public Document _getDocument(InputStream xml, ITaskMonitor monitor) {\r
+            return super.getDocument(xml, monitor);\r
+        }\r
+\r
     }\r
 \r
-    private MockRepoSource mSource;\r
+    private MockSdkAddonSource mSource;\r
 \r
     @Override\r
     protected void setUp() throws Exception {\r
         super.setUp();\r
 \r
-        mSource = new MockRepoSource();\r
+        mSource = new MockSdkAddonSource();\r
     }\r
 \r
     @Override\r
@@ -94,7 +124,7 @@ public class RepoSourceTest extends TestCase {
         mSource = null;\r
     }\r
 \r
-    public void testFindAlternateToolsXml_Errors() {\r
+    public void testFindAlternateToolsXml_Errors() throws Exception {\r
         // Support null as input\r
         Document result = mSource._findAlternateToolsXml(null);\r
         assertNull(result);\r
@@ -142,22 +172,94 @@ public class RepoSourceTest extends TestCase {
         assertNull(result);\r
     }\r
 \r
-    public void testFindAlternateToolsXml_1() {\r
-        InputStream xmlStream = this.getClass().getResourceAsStream(\r
-                    "/com/android/sdklib/testdata/repository_sample_1.xml");\r
+    /**\r
+     * Validate that findAlternateToolsXml doesn't work for addon even\r
+     * when trying to load a valid addon xml.\r
+     */\r
+    public void testFindAlternateToolsXml_1() throws Exception {\r
+        InputStream xmlStream = getTestResource("/com/android/sdklib/testdata/addon_sample_1.xml");\r
 \r
         Document result = mSource._findAlternateToolsXml(xmlStream);\r
-        assertNotNull(result);\r
-        assertTrue(mSource._parsePackages(result,\r
-                SdkRepository.NS_SDK_REPOSITORY, new MockMonitor()));\r
+        assertNull(result);\r
+    }\r
 \r
-        // check the packages we found... we expected to find 2 tool packages with 1 archive each.\r
+    /**\r
+     * Validate we can still load a valid addon schema version 1\r
+     */\r
+    public void testLoadOldXml_1() throws Exception {\r
+        InputStream xmlStream = getTestResource("/com/android/sdklib/testdata/addon_sample_1.xml");\r
+\r
+        // guess the version from the XML document\r
+        int version = mSource._getXmlSchemaVersion(xmlStream);\r
+        assertEquals(1, version);\r
+\r
+        Boolean[] validatorFound = new Boolean[] { Boolean.FALSE };\r
+        String[] validationError = new String[] { null };\r
+        String url = "not-a-valid-url://" + SdkAddonConstants.URL_DEFAULT_XML_FILE;\r
+\r
+        String uri = mSource._validateXml(xmlStream, url, version, validationError, validatorFound);\r
+        assertEquals(Boolean.TRUE, validatorFound[0]);\r
+        assertEquals(null, validationError[0]);\r
+        assertEquals(SdkAddonConstants.getSchemaUri(1), uri);\r
+\r
+        // Validation was successful, load the document\r
+        MockMonitor monitor = new MockMonitor();\r
+        Document doc = mSource._getDocument(xmlStream, monitor);\r
+        assertNotNull(doc);\r
+\r
+        // Get the packages\r
+        assertTrue(mSource._parsePackages(doc, uri, monitor));\r
+\r
+        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 Usb Driver package, revision 43 (Obsolete)\n" +\r
+                     "Found Extra Api Dep 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(2, pkgs.length);\r
+        assertEquals(5, pkgs.length);\r
         for (Package p : pkgs) {\r
-            assertEquals(ToolPackage.class, p.getClass());\r
-            assertEquals(1, p.getArchives().length);\r
+            assertTrue(p.getArchives().length >= 1);\r
         }\r
     }\r
 \r
+    /**\r
+     * Returns an SdkLib file resource as a {@link ByteArrayInputStream},\r
+     * which has the advantage that we can use {@link InputStream#reset()} on it\r
+     * at any time to read it multiple times.\r
+     * <p/>\r
+     * The default for getResourceAsStream() is to return a {@link FileInputStream} that\r
+     * does not support reset(), yet we need it in the tested code.\r
+     *\r
+     * @throws IOException if some I/O read fails\r
+     */\r
+    private ByteArrayInputStream getTestResource(String filename) throws IOException {\r
+        InputStream xmlStream = this.getClass().getResourceAsStream(filename);\r
+\r
+        try {\r
+            byte[] data = new byte[8192];\r
+            int offset = 0;\r
+            int n;\r
+\r
+            while ((n = xmlStream.read(data, offset, data.length - offset)) != -1) {\r
+                offset += n;\r
+\r
+                if (offset == data.length) {\r
+                    byte[] newData = new byte[offset + 8192];\r
+                    System.arraycopy(data, 0, newData, 0, offset);\r
+                    data = newData;\r
+                }\r
+            }\r
+\r
+            return new ByteArrayInputStream(data, 0, offset);\r
+        } finally {\r
+            if (xmlStream != null) {\r
+                xmlStream.close();\r
+            }\r
+        }\r
+    }\r
 }\r
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkRepoSourceTest.java b/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkRepoSourceTest.java
new file mode 100755 (executable)
index 0000000..dabcd5e
--- /dev/null
@@ -0,0 +1,342 @@
+/*\r
+ * Copyright (C) 2009 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.repository.SdkRepoConstants;\r
+\r
+import org.w3c.dom.Document;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+\r
+import junit.framework.TestCase;\r
+\r
+/**\r
+ * Tests for {@link SdkRepoSource}\r
+ */\r
+public class SdkRepoSourceTest extends TestCase {\r
+\r
+    private static class MockMonitor implements ITaskMonitor {\r
+\r
+        String mCapturedResults = "";\r
+        String mCapturedDescriptions = "";\r
+\r
+        public String getCapturedResults() {\r
+            return mCapturedResults;\r
+        }\r
+\r
+        public String getCapturedDescriptions() {\r
+            return mCapturedDescriptions;\r
+        }\r
+\r
+        public void setResult(String resultFormat, Object... args) {\r
+            mCapturedResults += String.format(resultFormat, args) + "\n";\r
+        }\r
+\r
+        public void setProgressMax(int max) {\r
+        }\r
+\r
+        public void setDescription(String descriptionFormat, Object... args) {\r
+            mCapturedDescriptions += String.format(descriptionFormat, args) + "\n";\r
+        }\r
+\r
+        public boolean isCancelRequested() {\r
+            return false;\r
+        }\r
+\r
+        public void incProgress(int delta) {\r
+        }\r
+\r
+        public int getProgress() {\r
+            return 0;\r
+        }\r
+\r
+        public boolean displayPrompt(String title, String message) {\r
+            return false;\r
+        }\r
+\r
+        public ITaskMonitor createSubMonitor(int tickCount) {\r
+            return null;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * An internal helper class to give us visibility to the protected members we want\r
+     * to test.\r
+     */\r
+    private static class MockSdkRepoSource extends SdkRepoSource {\r
+        public MockSdkRepoSource() {\r
+            super("fake-url");\r
+        }\r
+\r
+        public Document _findAlternateToolsXml(InputStream xml) throws IOException {\r
+            return super.findAlternateToolsXml(xml);\r
+        }\r
+\r
+        public boolean _parsePackages(Document doc, String nsUri, ITaskMonitor monitor) {\r
+            return super.parsePackages(doc, nsUri, monitor);\r
+        }\r
+\r
+        public int _getXmlSchemaVersion(InputStream xml) {\r
+            return super.getXmlSchemaVersion(xml);\r
+        }\r
+\r
+        public String _validateXml(InputStream xml, String url, int version,\r
+                                   String[] outError, Boolean[] validatorFound) {\r
+            return super.validateXml(xml, url, version, outError, validatorFound);\r
+        }\r
+\r
+        public Document _getDocument(InputStream xml, ITaskMonitor monitor) {\r
+            return super.getDocument(xml, monitor);\r
+        }\r
+\r
+    }\r
+\r
+    private MockSdkRepoSource mSource;\r
+\r
+    @Override\r
+    protected void setUp() throws Exception {\r
+        super.setUp();\r
+\r
+        mSource = new MockSdkRepoSource();\r
+    }\r
+\r
+    @Override\r
+    protected void tearDown() throws Exception {\r
+        super.tearDown();\r
+\r
+        mSource = null;\r
+    }\r
+\r
+    public void testFindAlternateToolsXml_Errors() throws Exception {\r
+        // Support null as input\r
+        Document result = mSource._findAlternateToolsXml(null);\r
+        assertNull(result);\r
+\r
+        // Support an empty input\r
+        String str = "";\r
+        ByteArrayInputStream input = new ByteArrayInputStream(str.getBytes());\r
+        result = mSource._findAlternateToolsXml(input);\r
+        assertNull(result);\r
+\r
+        // Support a random string as input\r
+        str = "Some random string, not even HTML nor XML";\r
+        input = new ByteArrayInputStream(str.getBytes());\r
+        result = mSource._findAlternateToolsXml(input);\r
+        assertNull(result);\r
+\r
+        // Support an HTML input, e.g. a typical 404 document as returned by DL\r
+        str = "<html><head> " +\r
+        "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"> " +\r
+        "<title>404 Not Found</title> " + "<style><!--" + "body {font-family: arial,sans-serif}" +\r
+        "div.nav { ... blah blah more css here ... color: green}" +\r
+        "//--></style> " + "<script><!--" + "var rc=404;" + "//-->" + "</script> " + "</head> " +\r
+        "<body text=#000000 bgcolor=#ffffff> " +\r
+        "<table border=0 cellpadding=2 cellspacing=0 width=100%><tr><td rowspan=3 width=1% nowrap> " +\r
+        "<b><font face=times color=#0039b6 size=10>G</font><font face=times color=#c41200 size=10>o</font><font face=times color=#f3c518 size=10>o</font><font face=times color=#0039b6 size=10>g</font><font face=times color=#30a72f size=10>l</font><font face=times color=#c41200 size=10>e</font>&nbsp;&nbsp;</b> " +\r
+        "<td>&nbsp;</td></tr> " +\r
+        "<tr><td bgcolor=\"#3366cc\"><font face=arial,sans-serif color=\"#ffffff\"><b>Error</b></td></tr> " +\r
+        "<tr><td>&nbsp;</td></tr></table> " + "<blockquote> " + "<H1>Not Found</H1> " +\r
+        "The requested URL <code>/404</code> was not found on this server." + " " + "<p> " +\r
+        "</blockquote> " +\r
+        "<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=\"#3366cc\"><img alt=\"\" width=1 height=4></td></tr></table> " +\r
+        "</body></html> ";\r
+        input = new ByteArrayInputStream(str.getBytes());\r
+        result = mSource._findAlternateToolsXml(input);\r
+        assertNull(result);\r
+\r
+        // Support some random XML document, totally unrelated to our sdk-repository schema\r
+        str = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +\r
+        "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"" +\r
+        "    package=\"some.cool.app\" android:versionName=\"1.6.04\" android:versionCode=\"1604\">" +\r
+        "    <application android:label=\"@string/app_name\" android:icon=\"@drawable/icon\"/>" +\r
+        "</manifest>";\r
+        input = new ByteArrayInputStream(str.getBytes());\r
+        result = mSource._findAlternateToolsXml(input);\r
+        assertNull(result);\r
+    }\r
+\r
+    /**\r
+     * Validate we can load a new schema version 3 using the "alternate future tool" mode.\r
+     */\r
+    public void testFindAlternateToolsXml_3() throws Exception {\r
+        InputStream xmlStream = getTestResource(\r
+                    "/com/android/sdklib/testdata/repository_sample_3.xml");\r
+\r
+        Document result = mSource._findAlternateToolsXml(xmlStream);\r
+        assertNotNull(result);\r
+        MockMonitor mon = new MockMonitor();\r
+        assertTrue(mSource._parsePackages(result, SdkRepoConstants.NS_URI, mon));\r
+\r
+        assertEquals("Found Android SDK Tools, revision 1\n" +\r
+                     "Found Android SDK Tools, revision 42\n" +\r
+                     "Found Android SDK Platform-tools, revision 3\n",\r
+                mon.getCapturedDescriptions());\r
+        assertEquals("", mon.getCapturedResults());\r
+\r
+        // check the packages we found... we expected to find 2 tool packages and 1\r
+        // platform-tools package, with at least 1 archive each.\r
+        Package[] pkgs = mSource.getPackages();\r
+        assertEquals(3, pkgs.length);\r
+        for (Package p : pkgs) {\r
+            assertTrue((p instanceof ToolPackage) || (p instanceof PlatformToolPackage));\r
+            assertTrue(p.getArchives().length >= 1);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Validate we can still load an old repository in schema version 1\r
+     */\r
+    public void testLoadOldXml_1() throws Exception {\r
+        InputStream xmlStream = getTestResource(\r
+                    "/com/android/sdklib/testdata/repository_sample_1.xml");\r
+\r
+        // guess the version from the XML document\r
+        int version = mSource._getXmlSchemaVersion(xmlStream);\r
+        assertEquals(1, version);\r
+\r
+        Boolean[] validatorFound = new Boolean[] { Boolean.FALSE };\r
+        String[] validationError = new String[] { null };\r
+        String url = "not-a-valid-url://" + SdkRepoConstants.URL_DEFAULT_XML_FILE;\r
+\r
+        String uri = mSource._validateXml(xmlStream, url, version, validationError, validatorFound);\r
+        assertEquals(Boolean.TRUE, validatorFound[0]);\r
+        assertEquals(null, validationError[0]);\r
+        assertEquals(SdkRepoConstants.getSchemaUri(1), uri);\r
+\r
+        // Validation was successful, load the document\r
+        MockMonitor monitor = new MockMonitor();\r
+        Document doc = mSource._getDocument(xmlStream, monitor);\r
+        assertNotNull(doc);\r
+\r
+        // Get the packages\r
+        assertTrue(mSource._parsePackages(doc, uri, monitor));\r
+\r
+        assertEquals("Found SDK Platform Android 1.0, API 1, revision 3\n" +\r
+                     "Found Documentation for Android SDK, API 1, revision 1\n" +\r
+                     "Found My First add-on by John Doe, Android API 1, revision 1\n" +\r
+                     "Found SDK Platform Android 1.1, API 2, revision 12\n" +\r
+                     "Found My Second add-on by John Deer, Android API 2, revision 42\n" +\r
+                     "Found SDK Platform Android Pastry Preview, revision 3\n" +\r
+                     "Found Android SDK Tools, revision 1\n" +\r
+                     "Found Documentation for Android SDK, API 2, revision 42\n" +\r
+                     "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\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(11, pkgs.length);\r
+        for (Package p : pkgs) {\r
+            assertTrue(p.getArchives().length >= 1);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Validate we can still load an old repository in schema version 2\r
+     */\r
+    public void testLoadOldXml_2() throws Exception {\r
+        InputStream xmlStream = getTestResource(\r
+                    "/com/android/sdklib/testdata/repository_sample_2.xml");\r
+\r
+        // guess the version from the XML document\r
+        int version = mSource._getXmlSchemaVersion(xmlStream);\r
+        assertEquals(2, version);\r
+\r
+        Boolean[] validatorFound = new Boolean[] { Boolean.FALSE };\r
+        String[] validationError = new String[] { null };\r
+        String url = "not-a-valid-url://" + SdkRepoConstants.URL_DEFAULT_XML_FILE;\r
+\r
+        String uri = mSource._validateXml(xmlStream, url, version, validationError, validatorFound);\r
+        assertEquals(Boolean.TRUE, validatorFound[0]);\r
+        assertEquals(null, validationError[0]);\r
+        assertEquals(SdkRepoConstants.getSchemaUri(2), uri);\r
+\r
+        // Validation was successful, load the document\r
+        MockMonitor monitor = new MockMonitor();\r
+        Document doc = mSource._getDocument(xmlStream, monitor);\r
+        assertNotNull(doc);\r
+\r
+        // Get the packages\r
+        assertTrue(mSource._parsePackages(doc, uri, monitor));\r
+\r
+        assertEquals("Found SDK Platform Android 1.0, API 1, revision 3\n" +\r
+                     "Found Documentation for Android SDK, API 1, revision 1\n" +\r
+                     "Found My First add-on by John Doe, Android API 1, revision 1\n" +\r
+                     "Found SDK Platform Android 1.1, API 2, revision 12\n" +\r
+                     "Found My Second add-on by John Deer, Android API 2, revision 42\n" +\r
+                     "Found SDK Platform Android Pastry Preview, revision 3\n" +\r
+                     "Found Android SDK Tools, revision 1\n" +\r
+                     "Found Documentation for Android SDK, API 2, revision 42\n" +\r
+                     "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 Samples for SDK API 14, revision 24 (Obsolete)\n",\r
+                monitor.getCapturedDescriptions());\r
+        assertEquals("", monitor.getCapturedResults());\r
+\r
+        // check the packages we found... we expected to find 13 packages with each at least\r
+        // one archive.\r
+        Package[] pkgs = mSource.getPackages();\r
+        assertEquals(13, pkgs.length);\r
+        for (Package p : pkgs) {\r
+            assertTrue(p.getArchives().length >= 1);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Returns an SdkLib file resource as a {@link ByteArrayInputStream},\r
+     * which has the advantage that we can use {@link InputStream#reset()} on it\r
+     * at any time to read it multiple times.\r
+     * <p/>\r
+     * The default for getResourceAsStream() is to return a {@link FileInputStream} that\r
+     * does not support reset(), yet we need it in the tested code.\r
+     *\r
+     * @throws IOException if some I/O read fails\r
+     */\r
+    private ByteArrayInputStream getTestResource(String filename) throws IOException {\r
+        InputStream xmlStream = this.getClass().getResourceAsStream(filename);\r
+\r
+        try {\r
+            byte[] data = new byte[8192];\r
+            int offset = 0;\r
+            int n;\r
+\r
+            while ((n = xmlStream.read(data, offset, data.length - offset)) != -1) {\r
+                offset += n;\r
+\r
+                if (offset == data.length) {\r
+                    byte[] newData = new byte[offset + 8192];\r
+                    System.arraycopy(data, 0, newData, 0, offset);\r
+                    data = newData;\r
+                }\r
+            }\r
+\r
+            return new ByteArrayInputStream(data, 0, offset);\r
+        } finally {\r
+            if (xmlStream != null) {\r
+                xmlStream.close();\r
+            }\r
+        }\r
+    }\r
+}\r
index 14b0b60..2bbc088 100755 (executable)
@@ -112,7 +112,7 @@ public class SdkRepositoryTest extends TestCase {
 \r
     /** Helper method that returns a validator for our XSD */\r
     private Validator getValidator(int version, CaptureErrorHandler handler) throws SAXException {\r
-        InputStream xsdStream = SdkRepository.getXsdStream(version);\r
+        InputStream xsdStream = SdkRepoConstants.getXsdStream(version);\r
         SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);\r
         Schema schema = factory.newSchema(new StreamSource(xsdStream));\r
         Validator validator = schema.newValidator();\r
@@ -177,7 +177,7 @@ public class SdkRepositoryTest extends TestCase {
         Source source = new StreamSource(new StringReader(document));\r
 \r
         CaptureErrorHandler handler = new CaptureErrorHandler();\r
-        Validator validator = getValidator(SdkRepository.NS_LATEST_VERSION, handler);\r
+        Validator validator = getValidator(SdkRepoConstants.NS_LATEST_VERSION, handler);\r
 \r
         try {\r
             validator.validate(source);\r
@@ -193,7 +193,7 @@ public class SdkRepositoryTest extends TestCase {
 \r
     private static String OPEN_TAG =\r
         "<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/" +\r
-        Integer.toString(SdkRepository.NS_LATEST_VERSION) +\r
+        Integer.toString(SdkRepoConstants.NS_LATEST_VERSION) +\r
         "\">";\r
 \r
     private static String CLOSE_TAG = "</r:sdk-repository>";\r
@@ -207,7 +207,7 @@ public class SdkRepositoryTest extends TestCase {
         Source source = new StreamSource(new StringReader(document));\r
 \r
         CaptureErrorHandler handler = new CaptureErrorHandler();\r
-        Validator validator = getValidator(SdkRepository.NS_LATEST_VERSION, handler);\r
+        Validator validator = getValidator(SdkRepoConstants.NS_LATEST_VERSION, handler);\r
         validator.validate(source);\r
         handler.verify();\r
     }\r
@@ -222,7 +222,7 @@ public class SdkRepositoryTest extends TestCase {
         Source source = new StreamSource(new StringReader(document));\r
 \r
         // don't capture the validator errors, we want it to fail and catch the exception\r
-        Validator validator = getValidator(SdkRepository.NS_LATEST_VERSION, null);\r
+        Validator validator = getValidator(SdkRepoConstants.NS_LATEST_VERSION, null);\r
         try {\r
             validator.validate(source);\r
         } catch (SAXParseException e) {\r
@@ -244,7 +244,7 @@ public class SdkRepositoryTest extends TestCase {
         Source source = new StreamSource(new StringReader(document));\r
 \r
         // don't capture the validator errors, we want it to fail and catch the exception\r
-        Validator validator = getValidator(SdkRepository.NS_LATEST_VERSION, null);\r
+        Validator validator = getValidator(SdkRepoConstants.NS_LATEST_VERSION, null);\r
         try {\r
             validator.validate(source);\r
         } catch (SAXParseException e) {\r
@@ -266,7 +266,7 @@ public class SdkRepositoryTest extends TestCase {
         Source source = new StreamSource(new StringReader(document));\r
 \r
         // don't capture the validator errors, we want it to fail and catch the exception\r
-        Validator validator = getValidator(SdkRepository.NS_LATEST_VERSION, null);\r
+        Validator validator = getValidator(SdkRepoConstants.NS_LATEST_VERSION, null);\r
         try {\r
             validator.validate(source);\r
         } catch (SAXParseException e) {\r
@@ -294,7 +294,7 @@ public class SdkRepositoryTest extends TestCase {
         Source source = new StreamSource(new StringReader(document));\r
 \r
         // don't capture the validator errors, we want it to fail and catch the exception\r
-        Validator validator = getValidator(SdkRepository.NS_LATEST_VERSION, null);\r
+        Validator validator = getValidator(SdkRepoConstants.NS_LATEST_VERSION, null);\r
         try {\r
             validator.validate(source);\r
         } catch (SAXParseException e) {\r
@@ -320,7 +320,7 @@ public class SdkRepositoryTest extends TestCase {
         Source source = new StreamSource(new StringReader(document));\r
 \r
         // don't capture the validator errors, we want it to fail and catch the exception\r
-        Validator validator = getValidator(SdkRepository.NS_LATEST_VERSION, null);\r
+        Validator validator = getValidator(SdkRepoConstants.NS_LATEST_VERSION, null);\r
         try {\r
             validator.validate(source);\r
         } catch (SAXParseException e) {\r
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addon_sample_1.xml b/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addon_sample_1.xml
new file mode 100755 (executable)
index 0000000..7e304b9
--- /dev/null
@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+<!--
+ * 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.
+-->
+<sdk:sdk-addon
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:sdk="http://schemas.android.com/sdk/android/addon/1">
+
+    <!-- Define a couple of licenses. These will be referenced by uses-license later. -->
+
+    <sdk:license type="text" id="license1">
+        This is the license
+        for this platform.
+    </sdk:license>
+
+    <sdk:license id="license2">
+        Licenses are only of type 'text' right now, so this is implied.
+    </sdk:license>
+
+    <!-- Inner elements must be either platform, add-on, doc or tool.
+         There can be 0 or more of each, in any order. -->
+
+    <sdk:add-on>
+        <sdk:name>My First add-on</sdk:name>
+        <sdk:api-level>1</sdk:api-level>
+        <sdk:vendor>John Doe</sdk:vendor>
+        <sdk:revision>1</sdk:revision>
+        <sdk:uses-license ref="license2" />
+        <sdk:description>Some optional description</sdk:description>
+        <sdk:desc-url>http://www.example.com/myfirstaddon</sdk:desc-url>
+        <sdk:archives>
+            <sdk:archive os="any">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>http://www.example.com/add-ons/first.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <!-- The libs node is mandatory, however it can be empty. -->
+        <sdk:libs>
+            <sdk:lib>
+                <sdk:name>android.blah.somelib</sdk:name>
+                <sdk:description>The description for this library.</sdk:description>
+            </sdk:lib>
+            <sdk:lib>
+                <!-- sdk:description is optional, name is not -->
+                <sdk:name>com.android.mymaps</sdk:name>
+            </sdk:lib>
+        </sdk:libs>
+    </sdk:add-on>
+
+    <sdk:add-on>
+        <sdk:name>My Second add-on</sdk:name>
+        <sdk:api-level>2</sdk:api-level>
+        <sdk:vendor>John Deer</sdk:vendor>
+        <sdk:revision>42</sdk:revision>
+        <sdk:archives>
+            <sdk:archive os="windows">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/second-42-win.zip</sdk:url>
+            </sdk:archive>
+            <sdk:archive os="linux">
+                <sdk:size>65536</sdk:size>
+                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
+                <sdk:url>distrib/second-42-linux.tar.bz2</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:libs>
+            <sdk:lib>
+                <sdk:name>android.blah.somelib</sdk:name>
+                <sdk:description>The description for this library.</sdk:description>
+            </sdk:lib>
+            <sdk:lib>
+                <sdk:name>com.android.mymaps</sdk:name>
+            </sdk:lib>
+        </sdk:libs>
+        <sdk:uses-license ref="license2" />
+    </sdk:add-on>
+
+    <sdk:add-on>
+        <sdk:uses-license ref="license2" />
+        <sdk:name>This add-on has no libraries</sdk:name>
+        <sdk:api-level>4</sdk:api-level>
+        <sdk:vendor>Joe Bar</sdk:vendor>
+        <sdk:revision>3</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/imnotanarchiveimadoctorjim.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <!-- The libs node is mandatory, however it can be empty. -->
+        <sdk:libs />
+    </sdk:add-on>
+
+    <sdk:extra>
+        <sdk:path>usb_driver</sdk:path>
+        <sdk:uses-license ref="license2" />
+        <sdk:revision>43</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/extraduff.zip</sdk:url>
+            </sdk:archive>
+        </sdk:archives>
+        <sdk:description>An Extra package for the USB driver, it will install in $SDK/usb_driver</sdk:description>
+        <sdk:desc-url>http://www.example.com/extra.html</sdk:desc-url>
+        <sdk:min-tools-rev>3</sdk:min-tools-rev>
+        <sdk:obsolete/>
+    </sdk:extra>
+
+    <sdk:extra>
+        <sdk:path>extra_api_dep</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>
index 4b2779e..feaad6a 100755 (executable)
         </sdk:archives>
     </sdk:doc>
 
-    <sdk:add-on>
-        <sdk:name>My First add-on</sdk:name>
-        <sdk:api-level>1</sdk:api-level>
-        <sdk:vendor>John Doe</sdk:vendor>
-        <sdk:revision>1</sdk:revision>
-        <sdk:uses-license ref="license2" />
-        <sdk:description>Some optional description</sdk:description>
-        <sdk:desc-url>http://www.example.com/myfirstaddon</sdk:desc-url>
-        <sdk:archives>
-            <sdk:archive os="any">
-                <sdk:size>65536</sdk:size>
-                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
-                <sdk:url>http://www.example.com/add-ons/first.zip</sdk:url>
-            </sdk:archive>
-        </sdk:archives>
-        <!-- The libs node is mandatory, however it can be empty. -->
-        <sdk:libs>
-            <sdk:lib>
-                <sdk:name>android.blah.somelib</sdk:name>
-                <sdk:description>The description for this library.</sdk:description>
-            </sdk:lib>
-            <sdk:lib>
-                <!-- sdk:description is optional, name is not -->
-                <sdk:name>com.android.mymaps</sdk:name>
-            </sdk:lib>
-        </sdk:libs>
-    </sdk:add-on>
-
     <sdk:platform>
         <sdk:version>1.1</sdk:version>
         <sdk:api-level>2</sdk:api-level>
         </sdk:archives>
     </sdk:platform>
 
-    <sdk:add-on>
-        <sdk:name>My Second add-on</sdk:name>
-        <sdk:api-level>2</sdk:api-level>
-        <sdk:vendor>John Deer</sdk:vendor>
-        <sdk:revision>42</sdk:revision>
-        <sdk:archives>
-            <sdk:archive os="windows">
-                <sdk:size>65536</sdk:size>
-                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
-                <sdk:url>distrib/second-42-win.zip</sdk:url>
-            </sdk:archive>
-            <sdk:archive os="linux">
-                <sdk:size>65536</sdk:size>
-                <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>
-                <sdk:url>distrib/second-42-linux.tar.bz2</sdk:url>
-            </sdk:archive>
-        </sdk:archives>
-        <sdk:libs>
-            <sdk:lib>
-                <sdk:name>android.blah.somelib</sdk:name>
-                <sdk:description>The description for this library.</sdk:description>
-            </sdk:lib>
-            <sdk:lib>
-                <sdk:name>com.android.mymaps</sdk:name>
-            </sdk:lib>
-        </sdk:libs>
-        <sdk:uses-license ref="license2" />
-    </sdk:add-on>
-
    <sdk:platform>
         <sdk:version>Pastry</sdk:version>
         <sdk:api-level>5</sdk:api-level>
         </sdk:archives>
     </sdk:platform-tool>
 
-    <sdk:add-on>
-        <sdk:uses-license ref="license2" />
-        <sdk:name>This add-on has no libraries</sdk:name>
-        <sdk:api-level>4</sdk:api-level>
-        <sdk:vendor>Joe Bar</sdk:vendor>
-        <sdk:revision>3</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/imnotanarchiveimadoctorjim.zip</sdk:url>
-            </sdk:archive>
-        </sdk:archives>
-        <!-- The libs node is mandatory, however it can be empty. -->
-        <sdk:libs />
-    </sdk:add-on>
-
     <sdk:extra>
         <sdk:path>usb_driver</sdk:path>
         <sdk:uses-license ref="license2" />
index c124b47..444e998 100755 (executable)
@@ -18,7 +18,7 @@ package com.android.sdkuilib.internal.repository;
 \r
 import com.android.sdklib.internal.repository.IDescription;\r
 import com.android.sdklib.internal.repository.Package;\r
-import com.android.sdklib.internal.repository.RepoSource;\r
+import com.android.sdklib.internal.repository.SdkSource;\r
 import com.android.sdkuilib.internal.repository.icons.ImageFactory;\r
 \r
 import org.eclipse.jface.viewers.IContentProvider;\r
@@ -90,7 +90,7 @@ class LocalSdkAdapter  {
         /**\r
          * Called to collect the root elements for the given input.\r
          * The input here is a {@link LocalSdkAdapter} object, this returns an array\r
-         * of {@link RepoSource}.\r
+         * of {@link SdkSource}.\r
          */\r
         public Object[] getElements(Object inputElement) {\r
             if (inputElement == LocalSdkAdapter.this) {\r
index fe2734d..ac6c0c8 100755 (executable)
@@ -20,7 +20,8 @@ package com.android.sdkuilib.internal.repository;
 import com.android.sdklib.internal.repository.Archive;\r
 import com.android.sdklib.internal.repository.IDescription;\r
 import com.android.sdklib.internal.repository.Package;\r
-import com.android.sdklib.internal.repository.RepoSource;\r
+import com.android.sdklib.internal.repository.SdkAddonSource;\r
+import com.android.sdklib.internal.repository.SdkSource;\r
 import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;\r
 \r
 import org.eclipse.jface.dialogs.IInputValidator;\r
@@ -260,7 +261,7 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
             (ITreeContentProvider) mTreeViewerSources.getContentProvider();\r
 \r
         // When selecting, we want to only select compatible archives.\r
-        if (elem instanceof RepoSource) {\r
+        if (elem instanceof SdkSource) {\r
             mTreeViewerSources.setExpandedState(elem, true);\r
             for (Object pkg : provider.getChildren(elem)) {\r
                 mTreeViewerSources.setChecked(pkg, true);\r
@@ -328,7 +329,7 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
 \r
     private void onAddSiteSelected() {\r
 \r
-        final RepoSource[] knowSources = mUpdaterData.getSources().getSources();\r
+        final SdkSource[] knowSources = mUpdaterData.getSources().getSources();\r
         String title = "Add Add-on Site URL";\r
 \r
         String msg =\r
@@ -358,7 +359,7 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
                 // Reject URLs that are already in the source list.\r
                 // URLs are generally case-insensitive (except for file:// where it all depends\r
                 // on the current OS so we'll ignore this case.)\r
-                for (RepoSource s : knowSources) {\r
+                for (SdkSource s : knowSources) {\r
                     if (newText.equalsIgnoreCase(s.getUrl())) {\r
                         return "Error : This site is already listed.";\r
                     }\r
@@ -370,7 +371,7 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
 \r
         if (dlg.open() == Window.OK) {\r
             String url = dlg.getValue();\r
-            mUpdaterData.getSources().add(new RepoSource(url, true /*userSource*/));\r
+            mUpdaterData.getSources().add(new SdkAddonSource(url, true /*userSource*/));\r
             onRefreshSelected();\r
         }\r
     }\r
@@ -381,8 +382,8 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
         ISelection sel = mTreeViewerSources.getSelection();\r
         if (mUpdaterData != null && sel instanceof ITreeSelection) {\r
             for (Object c : ((ITreeSelection) sel).toList()) {\r
-                if (c instanceof RepoSource && ((RepoSource) c).isUserSource()) {\r
-                    RepoSource source = (RepoSource) c;\r
+                if (c instanceof SdkSource && ((SdkSource) c).isUserSource()) {\r
+                    SdkSource source = (SdkSource) c;\r
 \r
                     String title = "Delete Add-on Site?";\r
 \r
@@ -437,8 +438,8 @@ public class RemotePackagesPage extends Composite implements ISdkListener {
         ISelection sel = mTreeViewerSources.getSelection();\r
         if (sel instanceof ITreeSelection) {\r
             for (Object c : ((ITreeSelection) sel).toList()) {\r
-                if (c instanceof RepoSource &&\r
-                        ((RepoSource) c).isUserSource()) {\r
+                if (c instanceof SdkSource &&\r
+                        ((SdkSource) c).isUserSource()) {\r
                     hasSelectedUserSource = true;\r
                     break;\r
                 }\r
index 625cf3a..3a44bb4 100755 (executable)
@@ -21,7 +21,7 @@ import com.android.sdklib.internal.repository.IDescription;
 import com.android.sdklib.internal.repository.ITask;\r
 import com.android.sdklib.internal.repository.ITaskMonitor;\r
 import com.android.sdklib.internal.repository.Package;\r
-import com.android.sdklib.internal.repository.RepoSource;\r
+import com.android.sdklib.internal.repository.SdkSource;\r
 import com.android.sdklib.internal.repository.Package.UpdateInfo;\r
 import com.android.sdkuilib.internal.repository.icons.ImageFactory;\r
 \r
@@ -50,9 +50,9 @@ public class RepoSourcesAdapter {
      */\r
     public static class RepoSourceError implements IDescription {\r
 \r
-        private final RepoSource mSource;\r
+        private final SdkSource mSource;\r
 \r
-        public RepoSourceError(RepoSource source) {\r
+        public RepoSourceError(SdkSource source) {\r
             mSource = source;\r
         }\r
 \r
@@ -72,10 +72,10 @@ public class RepoSourcesAdapter {
      */\r
     public static class RepoSourceEmpty implements IDescription {\r
 \r
-        private final RepoSource mSource;\r
+        private final SdkSource mSource;\r
         private final boolean mEmptyBecauseOfUpdateOnly;\r
 \r
-        public RepoSourceEmpty(RepoSource source, boolean emptyBecauseOfUpdateOnly) {\r
+        public RepoSourceEmpty(SdkSource source, boolean emptyBecauseOfUpdateOnly) {\r
             mSource = source;\r
             mEmptyBecauseOfUpdateOnly = emptyBecauseOfUpdateOnly;\r
         }\r
@@ -150,7 +150,7 @@ public class RepoSourcesAdapter {
         /**\r
          * Called to collect the root elements for the given input.\r
          * The input here is a {@link RepoSourcesAdapter} object, this returns an array\r
-         * of {@link RepoSource}.\r
+         * of {@link SdkSource}.\r
          */\r
         public Object[] getElements(Object inputElement) {\r
             return getChildren(inputElement);\r
@@ -160,16 +160,16 @@ public class RepoSourcesAdapter {
          * Get the children of the given parent. This is requested on-demand as\r
          * nodes are expanded.\r
          *\r
-         * For a {@link RepoSourcesAdapter} object, returns an array of {@link RepoSource}s.\r
-         * For a {@link RepoSource}, returns an array of {@link Package}s.\r
+         * For a {@link RepoSourcesAdapter} object, returns an array of {@link SdkSource}s.\r
+         * For a {@link SdkSource}, returns an array of {@link Package}s.\r
          * For a {@link Package}, returns an array of {@link Archive}s.\r
          */\r
         public Object[] getChildren(Object parentElement) {\r
             if (parentElement == RepoSourcesAdapter.this) {\r
                 return mUpdaterData.getSources().getSources();\r
 \r
-            } else if (parentElement instanceof RepoSource) {\r
-                return getRepoSourceChildren((RepoSource) parentElement);\r
+            } else if (parentElement instanceof SdkSource) {\r
+                return getRepoSourceChildren((SdkSource) parentElement);\r
 \r
             } else if (parentElement instanceof Package) {\r
                 return getPackageChildren((Package) parentElement);\r
@@ -183,7 +183,7 @@ public class RepoSourcesAdapter {
          * only update packages. If the list is empty, returns a specific empty node. If there's\r
          * an error, returns a specific error node.\r
          */\r
-        private Object[] getRepoSourceChildren(final RepoSource source) {\r
+        private Object[] getRepoSourceChildren(final SdkSource source) {\r
             Package[] packages = source.getPackages();\r
 \r
             if (packages == null && source.getFetchError() == null) {\r
@@ -250,11 +250,11 @@ public class RepoSourcesAdapter {
 \r
         /**\r
          * Returns the parent of a given element.\r
-         * The input {@link RepoSourcesAdapter} is the parent of all {@link RepoSource} elements.\r
+         * The input {@link RepoSourcesAdapter} is the parent of all {@link SdkSource} elements.\r
          */\r
         public Object getParent(Object element) {\r
 \r
-            if (element instanceof RepoSource) {\r
+            if (element instanceof SdkSource) {\r
                 return RepoSourcesAdapter.this;\r
 \r
             } else if (element instanceof Package) {\r
@@ -269,11 +269,11 @@ public class RepoSourcesAdapter {
         /**\r
          * Returns true if a given element has children, which is used to display a\r
          * "+/expand" box next to the tree node.\r
-         * All {@link RepoSource} and {@link Package} are expandable, whether they actually\r
+         * All {@link SdkSource} and {@link Package} are expandable, whether they actually\r
          * have any children or not.\r
          */\r
         public boolean hasChildren(Object element) {\r
-            return element instanceof RepoSource || element instanceof Package;\r
+            return element instanceof SdkSource || element instanceof Package;\r
         }\r
     }\r
 \r
index be8f841..53ddd1e 100755 (executable)
@@ -21,7 +21,7 @@ import com.android.sdklib.SdkManager;
 import com.android.sdklib.internal.repository.ITask;
 import com.android.sdklib.internal.repository.ITaskFactory;
 import com.android.sdklib.internal.repository.ITaskMonitor;
-import com.android.sdklib.repository.SdkRepository;
+import com.android.sdklib.repository.SdkRepoConstants;
 
 import java.util.ArrayList;
 
@@ -87,7 +87,7 @@ public class UpdateNoWindow {
     /**
      * Performs the actual update.
      *
-     * @param pkgFilter A list of {@link SdkRepository#NODES} to limit the type of packages
+     * @param pkgFilter A list of {@link SdkRepoConstants#NODES} to limit the type of packages
      *   we can update. A null or empty list means to update everything possible.
      * @param includeObsoletes True to also list and install obsolete packages.
      * @param dryMode True to check what would be updated/installed but do not actually
index dd98b00..81f567a 100755 (executable)
@@ -28,10 +28,13 @@ import com.android.sdklib.internal.repository.ITaskFactory;
 import com.android.sdklib.internal.repository.ITaskMonitor;\r
 import com.android.sdklib.internal.repository.LocalSdkParser;\r
 import com.android.sdklib.internal.repository.Package;\r
-import com.android.sdklib.internal.repository.RepoSource;\r
-import com.android.sdklib.internal.repository.RepoSources;\r
+import com.android.sdklib.internal.repository.SdkAddonSource;\r
+import com.android.sdklib.internal.repository.SdkRepoSource;\r
+import com.android.sdklib.internal.repository.SdkSource;\r
+import com.android.sdklib.internal.repository.SdkSources;\r
 import com.android.sdklib.internal.repository.ToolPackage;\r
-import com.android.sdklib.repository.SdkRepository;\r
+import com.android.sdklib.repository.SdkAddonConstants;\r
+import com.android.sdklib.repository.SdkRepoConstants;\r
 import com.android.sdkuilib.internal.repository.icons.ImageFactory;\r
 import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;\r
 \r
@@ -61,7 +64,7 @@ class UpdaterData {
     private AvdManager mAvdManager;\r
 \r
     private final LocalSdkParser mLocalSdkParser = new LocalSdkParser();\r
-    private final RepoSources mSources = new RepoSources();\r
+    private final SdkSources mSources = new SdkSources();\r
 \r
     private final LocalSdkAdapter mLocalSdkAdapter = new LocalSdkAdapter(this);\r
     private final RepoSourcesAdapter mSourcesAdapter = new RepoSourcesAdapter(this);\r
@@ -113,7 +116,7 @@ class UpdaterData {
         return mUserCanChangeSdkRoot;\r
     }\r
 \r
-    public RepoSources getSources() {\r
+    public SdkSources getSources() {\r
         return mSources;\r
     }\r
 \r
@@ -288,8 +291,9 @@ class UpdaterData {
      * - and finally the extra user repo URLs from the environment.\r
      */\r
     public void setupDefaultSources() {\r
-        RepoSources sources = getSources();\r
-        sources.add(new RepoSource(SdkRepository.URL_GOOGLE_SDK_REPO_SITE, false /*userSource*/));\r
+        SdkSources sources = getSources();\r
+        sources.add(new SdkRepoSource(SdkRepoConstants.URL_GOOGLE_SDK_SITE));\r
+        sources.add(new SdkAddonSource(SdkAddonConstants.URL_GOOGLE_SDK_SITE, false /*userSource*/));\r
 \r
         // SDK_UPDATER_URLS is a semicolon-separated list of URLs that can be used to\r
         // seed the SDK Updater list for full repositories.\r
@@ -298,7 +302,11 @@ class UpdaterData {
             String[] urls = str.split(";");\r
             for (String url : urls) {\r
                 if (url != null && url.length() > 0) {\r
-                    RepoSource s = new RepoSource(url, false /*userSource*/);\r
+                    SdkSource s = new SdkRepoSource(url);\r
+                    if (!sources.hasSource(s)) {\r
+                        sources.add(s);\r
+                    }\r
+                    s = new SdkAddonSource(url, false /*userSource*/);\r
                     if (!sources.hasSource(s)) {\r
                         sources.add(s);\r
                     }\r
@@ -317,7 +325,7 @@ class UpdaterData {
             String[] urls = str.split(";");\r
             for (String url : urls) {\r
                 if (url != null && url.length() > 0) {\r
-                    RepoSource s = new RepoSource(url, true /*userSource*/);\r
+                    SdkSource s = new SdkAddonSource(url, true /*userSource*/);\r
                     if (!sources.hasSource(s)) {\r
                         sources.add(s);\r
                     }\r
@@ -628,7 +636,7 @@ class UpdaterData {
      * This version is intended to run without a GUI and\r
      * only outputs to the current {@link ISdkLog}.\r
      *\r
-     * @param pkgFilter A list of {@link SdkRepository#NODES} to limit the type of packages\r
+     * @param pkgFilter A list of {@link SdkRepoConstants#NODES} to limit the type of packages\r
      *   we can update. A null or empty list means to update everything possible.\r
      * @param includeObsoletes True to also list and install obsolete packages.\r
      * @param dryMode True to check what would be updated/installed but do not actually\r
@@ -664,7 +672,7 @@ class UpdaterData {
             // Automatically find the classes matching the node names\r
             ClassLoader classLoader = getClass().getClassLoader();\r
             String basePackage = Package.class.getPackage().getName();\r
-            for (String node : SdkRepository.NODES) {\r
+            for (String node : SdkRepoConstants.NODES) {\r
                 // Capitalize the name\r
                 String name = node.substring(0, 1).toUpperCase() + node.substring(1);\r
 \r
@@ -700,7 +708,7 @@ class UpdaterData {
                 }\r
             }\r
 \r
-            if (SdkRepository.NODES.length != pkgMap.size()) {\r
+            if (SdkRepoConstants.NODES.length != pkgMap.size()) {\r
                 // Sanity check in case we forget to update this node array.\r
                 // We don't cancel the operation though.\r
                 mSdkLog.printf(\r
@@ -783,9 +791,9 @@ class UpdaterData {
 \r
         mTaskFactory.start("Refresh Sources", new ITask() {\r
             public void run(ITaskMonitor monitor) {\r
-                RepoSource[] sources = mSources.getSources();\r
+                SdkSource[] sources = mSources.getSources();\r
                 monitor.setProgressMax(sources.length);\r
-                for (RepoSource source : sources) {\r
+                for (SdkSource source : sources) {\r
                     if (forceFetching ||\r
                             source.getPackages() != null ||\r
                             source.getFetchError() != null) {\r
index 81a11a2..9c26388 100755 (executable)
@@ -30,8 +30,8 @@ import com.android.sdklib.internal.repository.MinToolsPackage;
 import com.android.sdklib.internal.repository.Package;\r
 import com.android.sdklib.internal.repository.PlatformPackage;\r
 import com.android.sdklib.internal.repository.PlatformToolPackage;\r
-import com.android.sdklib.internal.repository.RepoSource;\r
-import com.android.sdklib.internal.repository.RepoSources;\r
+import com.android.sdklib.internal.repository.SdkSource;\r
+import com.android.sdklib.internal.repository.SdkSources;\r
 import com.android.sdklib.internal.repository.SamplePackage;\r
 import com.android.sdklib.internal.repository.ToolPackage;\r
 import com.android.sdklib.internal.repository.Package.UpdateInfo;\r
@@ -58,13 +58,13 @@ class UpdaterLogic {
      */\r
     public ArrayList<ArchiveInfo> computeUpdates(\r
             Collection<Archive> selectedArchives,\r
-            RepoSources sources,\r
+            SdkSources sources,\r
             Package[] localPkgs,\r
             boolean includeObsoletes) {\r
 \r
         ArrayList<ArchiveInfo> archives = new ArrayList<ArchiveInfo>();\r
         ArrayList<Package> remotePkgs = new ArrayList<Package>();\r
-        RepoSource[] remoteSources = sources.getSources();\r
+        SdkSource[] remoteSources = sources.getSources();\r
 \r
         // Create ArchiveInfos out of local (installed) packages.\r
         ArchiveInfo[] localArchives = createLocalArchives(localPkgs);\r
@@ -96,7 +96,7 @@ class UpdaterLogic {
      */\r
     public void addNewPlatforms(\r
             ArrayList<ArchiveInfo> archives,\r
-            RepoSources sources,\r
+            SdkSources sources,\r
             Package[] localPkgs,\r
             boolean includeObsoletes) {\r
 \r
@@ -137,7 +137,7 @@ class UpdaterLogic {
             }\r
         }\r
 \r
-        RepoSource[] remoteSources = sources.getSources();\r
+        SdkSource[] remoteSources = sources.getSources();\r
         ArrayList<Package> remotePkgs = new ArrayList<Package>();\r
         fetchRemotePackages(remotePkgs, remoteSources);\r
 \r
@@ -248,7 +248,7 @@ class UpdaterLogic {
     private Collection<Archive> findUpdates(\r
             ArchiveInfo[] localArchives,\r
             ArrayList<Package> remotePkgs,\r
-            RepoSource[] remoteSources,\r
+            SdkSource[] remoteSources,\r
             boolean includeObsoletes) {\r
         ArrayList<Archive> updates = new ArrayList<Archive>();\r
 \r
@@ -285,7 +285,7 @@ class UpdaterLogic {
             ArrayList<ArchiveInfo> outArchives,\r
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
-            RepoSource[] remoteSources,\r
+            SdkSource[] remoteSources,\r
             ArchiveInfo[] localArchives,\r
             boolean automated) {\r
         Package p = archive.getParentPackage();\r
@@ -351,7 +351,7 @@ class UpdaterLogic {
             ArrayList<ArchiveInfo> outArchives,\r
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
-            RepoSource[] remoteSources,\r
+            SdkSource[] remoteSources,\r
             ArchiveInfo[] localArchives) {\r
 \r
         // Current dependencies can be:\r
@@ -440,7 +440,7 @@ class UpdaterLogic {
             ArrayList<ArchiveInfo> outArchives,\r
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
-            RepoSource[] remoteSources,\r
+            SdkSource[] remoteSources,\r
             ArchiveInfo[] localArchives) {\r
         // This is the requirement to match.\r
         int rev = pkg.getMinToolsRevision();\r
@@ -538,7 +538,7 @@ class UpdaterLogic {
             ArrayList<ArchiveInfo> outArchives,\r
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
-            RepoSource[] remoteSources,\r
+            SdkSource[] remoteSources,\r
             ArchiveInfo[] localArchives) {\r
         // This is the requirement to match.\r
         int rev = pkg.getMinPlatformToolsRevision();\r
@@ -637,7 +637,7 @@ class UpdaterLogic {
             ArrayList<ArchiveInfo> outArchives,\r
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
-            RepoSource[] remoteSources,\r
+            SdkSource[] remoteSources,\r
             ArchiveInfo[] localArchives) {\r
         // This is the requirement to match.\r
         AndroidVersion v = pkg.getVersion();\r
@@ -736,7 +736,7 @@ class UpdaterLogic {
             ArrayList<ArchiveInfo> outArchives,\r
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
-            RepoSource[] remoteSources,\r
+            SdkSource[] remoteSources,\r
             ArchiveInfo[] localArchives) {\r
 \r
         int api = pkg.getMinApiLevel();\r
@@ -840,12 +840,12 @@ class UpdaterLogic {
     }\r
 \r
     /** Fetch all remote packages only if really needed. */\r
-    protected void fetchRemotePackages(ArrayList<Package> remotePkgs, RepoSource[] remoteSources) {\r
+    protected void fetchRemotePackages(ArrayList<Package> remotePkgs, SdkSource[] remoteSources) {\r
         if (remotePkgs.size() > 0) {\r
             return;\r
         }\r
 \r
-        for (RepoSource remoteSrc : remoteSources) {\r
+        for (SdkSource remoteSrc : remoteSources) {\r
             Package[] pkgs = remoteSrc.getPackages();\r
             if (pkgs != null) {\r
                 nextPackage: for (Package pkg : pkgs) {\r
index 5111f49..b4ccaf2 100755 (executable)
@@ -18,7 +18,7 @@ package com.android.sdkuilib.internal.repository.icons;
 \r
 import com.android.sdklib.internal.repository.Archive;\r
 import com.android.sdklib.internal.repository.Package;\r
-import com.android.sdklib.internal.repository.RepoSource;\r
+import com.android.sdklib.internal.repository.SdkSource;\r
 import com.android.sdkuilib.internal.repository.RepoSourcesAdapter;\r
 \r
 import org.eclipse.swt.SWTException;\r
@@ -79,7 +79,7 @@ public class ImageFactory {
     /**\r
      * Loads and returns the appropriate image for a given package, archive or source object.\r
      *\r
-     * @param object A {@link RepoSource} or {@link Package} or {@link Archive}.\r
+     * @param object A {@link SdkSource} or {@link Package} or {@link Archive}.\r
      * @return A new or existing {@link Image}. The caller must NOT dispose the image (the\r
      *  image will disposed by {@link #dispose()}). The returned image can be null if the\r
      *  expected file is missing.\r
@@ -97,7 +97,7 @@ public class ImageFactory {
             return getImageByName(name);\r
         }\r
 \r
-        if (object instanceof RepoSource) {\r
+        if (object instanceof SdkSource) {\r
             return getImageByName("source_icon16.png");                         //$NON-NLS-1$\r
 \r
         } else if (object instanceof RepoSourcesAdapter.RepoSourceError) {\r
index 5e5259a..485619a 100755 (executable)
@@ -22,7 +22,7 @@ import com.android.sdklib.internal.repository.MockPlatformPackage;
 import com.android.sdklib.internal.repository.MockPlatformToolPackage;\r
 import com.android.sdklib.internal.repository.MockToolPackage;\r
 import com.android.sdklib.internal.repository.Package;\r
-import com.android.sdklib.internal.repository.RepoSource;\r
+import com.android.sdklib.internal.repository.SdkSource;\r
 \r
 import java.util.ArrayList;\r
 import java.util.Arrays;\r
@@ -40,7 +40,7 @@ public class UpdaterLogicTest extends TestCase {
 \r
         @Override\r
         protected void fetchRemotePackages(ArrayList<Package> remotePkgs,\r
-                RepoSource[] remoteSources) {\r
+                SdkSource[] remoteSources) {\r
             // Ignore remoteSources and instead uses the remotePackages list given to the\r
             // constructor.\r
             if (mRemotePackages != null) {\r
@@ -71,7 +71,7 @@ public class UpdaterLogicTest extends TestCase {
         Package[] localPkgs = { p1, a1 };\r
         ArchiveInfo[] locals = mul.createLocalArchives(localPkgs);\r
 \r
-        RepoSource[] sources = null;\r
+        SdkSource[] sources = null;\r
 \r
         // a2 now depends on a "fake" archive info with no newArchive that wraps the missing\r
         // underlying platform.\r
@@ -114,7 +114,7 @@ public class UpdaterLogicTest extends TestCase {
         Package[] localPkgs = { t1, pt1 };\r
         ArchiveInfo[] locals = mul.createLocalArchives(localPkgs);\r
 \r
-        RepoSource[] sources = null;\r
+        SdkSource[] sources = null;\r
 \r
         // p2 now depends on a "fake" archive info with no newArchive that wraps the missing\r
         // underlying tool\r
@@ -155,7 +155,7 @@ public class UpdaterLogicTest extends TestCase {
         Package[] localPkgs = { t1 };\r
         ArchiveInfo[] locals = mul.createLocalArchives(localPkgs);\r
 \r
-        RepoSource[] sources = null;\r
+        SdkSource[] sources = null;\r
 \r
         // p2 now depends on a "fake" archive info with no newArchive that wraps the missing\r
         // underlying tool\r