\r
import java.io.File;\r
import java.util.ArrayList;\r
+import java.util.Map;\r
\r
/**\r
* Represents an add-on XML node in an SDK repository.\r
* <p/>\r
* This constructor should throw an exception if the package cannot be created.\r
*/\r
- AddonPackage(RepoSource source, Node packageNode) {\r
- super(source, packageNode);\r
- mVendor = getXmlString(packageNode, SdkRepository.NODE_VENDOR);\r
- mName = getXmlString(packageNode, SdkRepository.NODE_NAME);\r
- mApiLevel = getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);\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
+ mApiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
\r
- mLibs = parseLibs(getFirstChild(packageNode, SdkRepository.NODE_LIBS));\r
+ mLibs = parseLibs(XmlParserUtils.getFirstChild(packageNode, SdkRepository.NODE_LIBS));\r
}\r
\r
/**\r
* Parses a <lib> element from a <libs> container.\r
*/\r
private Lib parseLib(Node libNode) {\r
- return new Lib(getXmlString(libNode, SdkRepository.NODE_NAME),\r
- getXmlString(libNode, SdkRepository.NODE_DESCRIPTION));\r
+ return new Lib(XmlParserUtils.getXmlString(libNode, SdkRepository.NODE_NAME),\r
+ XmlParserUtils.getXmlString(libNode, SdkRepository.NODE_DESCRIPTION));\r
}\r
\r
/** Returns the vendor, a string, for add-on packages. */\r
SdkManager sdkManager,\r
ITaskMonitor monitor) {\r
\r
+ Package pkg = getParentPackage();\r
+\r
File archiveFile = null;\r
try {\r
- String name = getParentPackage().getShortDescription();\r
+ String name = pkg.getShortDescription();\r
+\r
+ if (pkg instanceof ExtraPackage && !((ExtraPackage) pkg).isPathValid()) {\r
+ monitor.setResult("Skipping %1$s: %2$s is not a valid install path.",\r
+ name,\r
+ ((ExtraPackage) pkg).getPath());\r
+ return false;\r
+ }\r
\r
if (isLocal()) {\r
// This should never happen.\r
import org.w3c.dom.Node;\r
\r
import java.io.File;\r
+import java.util.Map;\r
\r
/**\r
* Represents a doc XML node in an SDK repository.\r
* <p/>\r
* This constructor should throw an exception if the package cannot be created.\r
*/\r
- DocPackage(RepoSource source, Node packageNode) {\r
- super(source, packageNode);\r
- mApiLevel = getXmlInt(packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
+ DocPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
+ super(source, packageNode, licenses);\r
+ mApiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
}\r
\r
/**\r
--- /dev/null
+/*\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.SdkConstants;\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
+\r
+import org.w3c.dom.Node;\r
+\r
+import java.io.File;\r
+import java.util.Map;\r
+\r
+/**\r
+ * Represents a extra XML node in an SDK repository.\r
+ */\r
+public class ExtraPackage extends Package {\r
+\r
+ private final String mPath;\r
+\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
+ ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
+ super(source, packageNode, licenses);\r
+ mPath = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_PATH);\r
+ }\r
+\r
+ /**\r
+ * Manually create a new package with one archive and the given attributes.\r
+ * This is used to create packages from local directories in which case there must be\r
+ * one archive which URL is the actual target location.\r
+ */\r
+ ExtraPackage(RepoSource source,\r
+ String path,\r
+ int revision,\r
+ String license,\r
+ String description,\r
+ String descUrl,\r
+ Os archiveOs,\r
+ Arch archiveArch,\r
+ String archiveOsPath) {\r
+ super(source,\r
+ revision,\r
+ license,\r
+ description,\r
+ descUrl,\r
+ archiveOs,\r
+ archiveArch,\r
+ archiveOsPath);\r
+ mPath = path;\r
+ }\r
+\r
+ /**\r
+ * Static helper to check if a given path is acceptable for an "extra" package.\r
+ */\r
+ public boolean isPathValid() {\r
+ if (SdkConstants.FD_ADDONS.equals(mPath) ||\r
+ SdkConstants.FD_PLATFORMS.equals(mPath) ||\r
+ SdkConstants.FD_TOOLS.equals(mPath) ||\r
+ SdkConstants.FD_DOCS.equals(mPath)) {\r
+ return false;\r
+ }\r
+ return mPath != null && mPath.indexOf('/') == -1 && mPath.indexOf('\\') == -1;\r
+ }\r
+\r
+ /**\r
+ * The install folder name. It must be a single-segment path.\r
+ * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used.\r
+ * This limitation cannot be written in the XML Schema and must be enforced here by using\r
+ * the method {@link #isPathValid()} *before* installing the package.\r
+ */\r
+ public String getPath() {\r
+ return mPath;\r
+ }\r
+\r
+ /** Returns a short description for an {@link IDescription}. */\r
+ @Override\r
+ public String getShortDescription() {\r
+ return String.format("Extra %1$s package, revision %2$d", getPath(), getRevision());\r
+ }\r
+\r
+ /** Returns a long description for an {@link IDescription}. */\r
+ @Override\r
+ public String getLongDescription() {\r
+ return String.format("Extra %1$s package, revision %2$d.\n%3$s",\r
+ getPath(),\r
+ getRevision(),\r
+ super.getLongDescription());\r
+ }\r
+\r
+ /**\r
+ * Computes a potential installation folder if an archive of this package were\r
+ * to be installed right away in the given SDK root.\r
+ * <p/>\r
+ * A "tool" package should always be located in SDK/tools.\r
+ *\r
+ * @param osSdkRoot The OS path of the SDK root folder.\r
+ * @return A new {@link File} corresponding to the directory to use to install this package.\r
+ */\r
+ @Override\r
+ public File getInstallFolder(String osSdkRoot) {\r
+ return new File(osSdkRoot, getPath());\r
+ }\r
+\r
+ /**\r
+ * Computes whether the given extra package is a suitable update for the current package.\r
+ * The base method checks the class type.\r
+ * The tools package also tests that the revision number is greater and the path is the\r
+ * same.\r
+ * <p/>\r
+ * An update is just that: a new package that supersedes the current one. If the new\r
+ * package has the same revision as the current one, it's not an update.\r
+ *\r
+ * @param replacementPackage The potential replacement package.\r
+ * @return True if the replacement package is a suitable update for this one.\r
+ */\r
+ @Override\r
+ public boolean canBeUpdatedBy(Package replacementPackage) {\r
+ if (!super.canBeUpdatedBy(replacementPackage)) {\r
+ return false;\r
+ }\r
+\r
+ ExtraPackage newPkg = (ExtraPackage) replacementPackage;\r
+ return newPkg.getRevision() > this.getRevision() && newPkg.getPath().equals(this.getPath());\r
+ }\r
+}\r
import java.io.InputStream;\r
import java.io.StringReader;\r
import java.util.ArrayList;\r
+import java.util.HashMap;\r
import java.util.HashSet;\r
import java.util.Set;\r
import java.util.regex.Matcher;\r
Node root = getFirstChild(doc, SdkRepository.NODE_SDK_REPOSITORY);\r
if (root != null) {\r
\r
+ // Parse license definitions\r
+ HashMap<String, String> licenses = new HashMap<String, String>();\r
+ for (Node child = root.getFirstChild();\r
+ child != null;\r
+ child = child.getNextSibling()) {\r
+ if (child.getNodeType() == Node.ELEMENT_NODE &&\r
+ SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI()) &&\r
+ child.getLocalName().equals(SdkRepository.NODE_LICENSE)) {\r
+ Node id = child.getAttributes().getNamedItem(SdkRepository.ATTR_ID);\r
+ if (id != null) {\r
+ licenses.put(id.getNodeValue(), child.getTextContent());\r
+ }\r
+ }\r
+ }\r
+\r
+ // Parse packages\r
for (Node child = root.getFirstChild();\r
child != null;\r
child = child.getNextSibling()) {\r
\r
try {\r
if (SdkRepository.NODE_ADD_ON.equals(name)) {\r
- return new AddonPackage(null /*source*/, child);\r
+ return new AddonPackage(null /*source*/, child, licenses);\r
\r
} else if (SdkRepository.NODE_PLATFORM.equals(name)) {\r
- return new PlatformPackage(null /*source*/, child);\r
+ return new PlatformPackage(null /*source*/, child, licenses);\r
\r
} else if (SdkRepository.NODE_DOC.equals(name)) {\r
- return new DocPackage(null /*source*/, child);\r
+ return new DocPackage(null /*source*/, child, licenses);\r
\r
} else if (SdkRepository.NODE_TOOL.equals(name)) {\r
- return new ToolPackage(null /*source*/, child);\r
+ return new ToolPackage(null /*source*/, child, licenses);\r
}\r
} catch (Exception e) {\r
// Ignore invalid packages\r
\r
import java.io.File;\r
import java.util.ArrayList;\r
+import java.util.Map;\r
\r
/**\r
* A {@link Package} is the base class for "something" that can be downloaded from\r
* <p/>\r
* This constructor should throw an exception if the package cannot be created.\r
*/\r
- Package(RepoSource source, Node packageNode) {\r
+ Package(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
mSource = source;\r
- mRevision = getXmlInt (packageNode, SdkRepository.NODE_REVISION, 0);\r
- mDescription = getXmlString(packageNode, SdkRepository.NODE_DESCRIPTION);\r
- mDescUrl = getXmlString(packageNode, SdkRepository.NODE_DESC_URL);\r
- mLicense = getXmlString(packageNode, SdkRepository.NODE_LICENSE);\r
- mArchives = parseArchives(getFirstChild(packageNode, SdkRepository.NODE_ARCHIVES));\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
+\r
+ mLicense = parseLicense(packageNode, licenses);\r
+ mArchives = parseArchives(XmlParserUtils.getFirstChild(\r
+ packageNode, SdkRepository.NODE_ARCHIVES));\r
}\r
\r
/**\r
}\r
\r
/**\r
+ * Parses the uses-licence node of this package, if any, and returns the license\r
+ * definition if there's one. Returns null if there's no uses-license element or no\r
+ * license of this name defined.\r
+ */\r
+ private String parseLicense(Node packageNode, Map<String, String> licenses) {\r
+ Node usesLicense = XmlParserUtils.getFirstChild(\r
+ packageNode, SdkRepository.NODE_USES_LICENSE);\r
+ if (usesLicense != null) {\r
+ Node ref = usesLicense.getAttributes().getNamedItem(SdkRepository.ATTR_REF);\r
+ if (ref != null) {\r
+ String licenseRef = ref.getNodeValue();\r
+ return licenses.get(licenseRef);\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
* Parses an XML node to process the <archives> element.\r
*/\r
private Archive[] parseArchives(Node archivesNode) {\r
private Archive parseArchive(Node archiveNode) {\r
Archive a = new Archive(\r
this,\r
- (Os) getEnumAttribute(archiveNode, SdkRepository.ATTR_OS,\r
+ (Os) XmlParserUtils.getEnumAttribute(archiveNode, SdkRepository.ATTR_OS,\r
Os.values(), null),\r
- (Arch) getEnumAttribute(archiveNode, SdkRepository.ATTR_ARCH,\r
+ (Arch) XmlParserUtils.getEnumAttribute(archiveNode, SdkRepository.ATTR_ARCH,\r
Arch.values(), Arch.ANY),\r
- getXmlString(archiveNode, SdkRepository.NODE_URL),\r
- getXmlLong(archiveNode, SdkRepository.NODE_SIZE, 0),\r
- getXmlString(archiveNode, SdkRepository.NODE_CHECKSUM)\r
+ XmlParserUtils.getXmlString(archiveNode, SdkRepository.NODE_URL),\r
+ XmlParserUtils.getXmlLong (archiveNode, SdkRepository.NODE_SIZE, 0),\r
+ XmlParserUtils.getXmlString(archiveNode, SdkRepository.NODE_CHECKSUM)\r
);\r
\r
return a;\r
replacementPackage.getRevision() > this.getRevision();\r
}\r
\r
- //---\r
-\r
- /**\r
- * Returns the first child element with the given XML local name.\r
- * If xmlLocalName is null, returns the very first child element.\r
- */\r
- protected static Node getFirstChild(Node node, String xmlLocalName) {\r
-\r
- for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {\r
- if (child.getNodeType() == Node.ELEMENT_NODE &&\r
- SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI())) {\r
- if (xmlLocalName == null || xmlLocalName.equals(child.getLocalName())) {\r
- return child;\r
- }\r
- }\r
- }\r
-\r
- return null;\r
- }\r
-\r
- /**\r
- * Retrieves the value of that XML element as a string.\r
- * Returns an empty string when the element is missing.\r
- */\r
- protected static String getXmlString(Node node, String xmlLocalName) {\r
- Node child = getFirstChild(node, xmlLocalName);\r
-\r
- return child == null ? "" : child.getTextContent(); //$NON-NLS-1$\r
- }\r
-\r
- /**\r
- * Retrieves the value of that XML element as an integer.\r
- * Returns the default value when the element is missing or is not an integer.\r
- */\r
- protected static int getXmlInt(Node node, String xmlLocalName, int defaultValue) {\r
- String s = getXmlString(node, xmlLocalName);\r
- try {\r
- return Integer.parseInt(s);\r
- } catch (NumberFormatException e) {\r
- return defaultValue;\r
- }\r
- }\r
-\r
- /**\r
- * Retrieves the value of that XML element as a long.\r
- * Returns the default value when the element is missing or is not an integer.\r
- */\r
- protected static long getXmlLong(Node node, String xmlLocalName, long defaultValue) {\r
- String s = getXmlString(node, xmlLocalName);\r
- try {\r
- return Long.parseLong(s);\r
- } catch (NumberFormatException e) {\r
- return defaultValue;\r
- }\r
- }\r
-\r
- /**\r
- * Retrieve an attribute which value must match one of the given enums using a\r
- * case-insensitive name match.\r
- *\r
- * Returns defaultValue if the attribute does not exist or its value does not match\r
- * the given enum values.\r
- */\r
- private Object getEnumAttribute(\r
- Node archiveNode,\r
- String attrName,\r
- Object[] values,\r
- Object defaultValue) {\r
-\r
- Node attr = archiveNode.getAttributes().getNamedItem(attrName);\r
- if (attr != null) {\r
- String found = attr.getNodeValue();\r
- for (Object value : values) {\r
- if (value.toString().equalsIgnoreCase(found)) {\r
- return value;\r
- }\r
- }\r
- }\r
-\r
- return defaultValue;\r
- }\r
}\r
import org.w3c.dom.Node;\r
\r
import java.io.File;\r
+import java.util.Map;\r
\r
/**\r
* Represents a platform XML node in an SDK repository.\r
* <p/>\r
* This constructor should throw an exception if the package cannot be created.\r
*/\r
- PlatformPackage(RepoSource source, Node packageNode) {\r
- super(source, packageNode);\r
- mVersion = getXmlString(packageNode, SdkRepository.NODE_VERSION);\r
- mApiLevel = getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
+ PlatformPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
+ super(source, packageNode, licenses);\r
+ mVersion = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VERSION);\r
+ mApiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);\r
}\r
\r
/**\r
import java.io.InputStream;\r
import java.net.URL;\r
import java.util.ArrayList;\r
+import java.util.HashMap;\r
\r
import javax.xml.XMLConstants;\r
import javax.xml.parsers.DocumentBuilder;\r
\r
ArrayList<Package> packages = new ArrayList<Package>();\r
\r
+ // Parse license definitions\r
+ HashMap<String, String> licenses = new HashMap<String, String>();\r
+ for (Node child = root.getFirstChild();\r
+ child != null;\r
+ child = child.getNextSibling()) {\r
+ if (child.getNodeType() == Node.ELEMENT_NODE &&\r
+ SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI()) &&\r
+ child.getLocalName().equals(SdkRepository.NODE_LICENSE)) {\r
+ Node id = child.getAttributes().getNamedItem(SdkRepository.ATTR_ID);\r
+ if (id != null) {\r
+ licenses.put(id.getNodeValue(), child.getTextContent());\r
+ }\r
+ }\r
+ }\r
+\r
+ // Parse packages\r
for (Node child = root.getFirstChild();\r
child != null;\r
child = child.getNextSibling()) {\r
\r
try {\r
if (SdkRepository.NODE_ADD_ON.equals(name)) {\r
- p = new AddonPackage(this, child);\r
+ p = new AddonPackage(this, child, licenses);\r
+\r
+ } else if (SdkRepository.NODE_EXTRA.equals(name)) {\r
+ p = new ExtraPackage(this, child, licenses);\r
\r
} else if (!mAddonOnly) {\r
if (SdkRepository.NODE_PLATFORM.equals(name)) {\r
- p = new PlatformPackage(this, child);\r
+ p = new PlatformPackage(this, child, licenses);\r
} else if (SdkRepository.NODE_DOC.equals(name)) {\r
- p = new DocPackage(this, child);\r
+ p = new DocPackage(this, child, licenses);\r
} else if (SdkRepository.NODE_TOOL.equals(name)) {\r
- p = new ToolPackage(this, child);\r
+ p = new ToolPackage(this, child, licenses);\r
}\r
}\r
\r
import org.w3c.dom.Node;\r
\r
import java.io.File;\r
+import java.util.Map;\r
\r
/**\r
* Represents a tool XML node in an SDK repository.\r
* <p/>\r
* This constructor should throw an exception if the package cannot be created.\r
*/\r
- ToolPackage(RepoSource source, Node packageNode) {\r
- super(source, packageNode);\r
+ ToolPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {\r
+ super(source, packageNode, licenses);\r
}\r
\r
/**\r
--- /dev/null
+/*\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.repository.SdkRepository;\r
+\r
+import org.w3c.dom.Node;\r
+\r
+/**\r
+ * Misc utilities to help extracting elements and attributes out of an XML document.\r
+ */\r
+class XmlParserUtils {\r
+\r
+ /**\r
+ * Returns the first child element with the given XML local name.\r
+ * If xmlLocalName is null, returns the very first child element.\r
+ */\r
+ public static Node getFirstChild(Node node, String xmlLocalName) {\r
+\r
+ for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {\r
+ if (child.getNodeType() == Node.ELEMENT_NODE &&\r
+ SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI())) {\r
+ if (xmlLocalName == null || xmlLocalName.equals(child.getLocalName())) {\r
+ return child;\r
+ }\r
+ }\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Retrieves the value of that XML element as a string.\r
+ * Returns an empty string when the element is missing.\r
+ */\r
+ public static String getXmlString(Node node, String xmlLocalName) {\r
+ Node child = getFirstChild(node, xmlLocalName);\r
+\r
+ return child == null ? "" : child.getTextContent(); //$NON-NLS-1$\r
+ }\r
+\r
+ /**\r
+ * Retrieves the value of that XML element as an integer.\r
+ * Returns the default value when the element is missing or is not an integer.\r
+ */\r
+ public static int getXmlInt(Node node, String xmlLocalName, int defaultValue) {\r
+ String s = getXmlString(node, xmlLocalName);\r
+ try {\r
+ return Integer.parseInt(s);\r
+ } catch (NumberFormatException e) {\r
+ return defaultValue;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Retrieves the value of that XML element as a long.\r
+ * Returns the default value when the element is missing or is not an integer.\r
+ */\r
+ public static long getXmlLong(Node node, String xmlLocalName, long defaultValue) {\r
+ String s = getXmlString(node, xmlLocalName);\r
+ try {\r
+ return Long.parseLong(s);\r
+ } catch (NumberFormatException e) {\r
+ return defaultValue;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Retrieve an attribute which value must match one of the given enums using a\r
+ * case-insensitive name match.\r
+ *\r
+ * Returns defaultValue if the attribute does not exist or its value does not match\r
+ * the given enum values.\r
+ */\r
+ public static Object getEnumAttribute(\r
+ Node archiveNode,\r
+ String attrName,\r
+ Object[] values,\r
+ Object defaultValue) {\r
+\r
+ Node attr = archiveNode.getAttributes().getNamedItem(attrName);\r
+ if (attr != null) {\r
+ String found = attr.getNodeValue();\r
+ for (Object value : values) {\r
+ if (value.toString().equalsIgnoreCase(found)) {\r
+ return value;\r
+ }\r
+ }\r
+ }\r
+\r
+ return defaultValue;\r
+ }\r
+\r
+}\r
public static final String NODE_TOOL = "tool"; //$NON-NLS-1$\r
/** A doc package. */\r
public static final String NODE_DOC = "doc"; //$NON-NLS-1$\r
+ /** An extra package. */\r
+ public static final String NODE_EXTRA = "extra"; //$NON-NLS-1$\r
\r
+ /** The license definition. */\r
+ public static final String NODE_LICENSE = "license"; //$NON-NLS-1$\r
+ /** The optional uses-license for all packages (platform, add-on, tool, doc) or for a lib. */\r
+ public static final String NODE_USES_LICENSE = "uses-license"; //$NON-NLS-1$\r
/** The revision, an int > 0, for all packages (platform, add-on, tool, doc). */\r
public static final String NODE_REVISION = "revision"; //$NON-NLS-1$\r
- /** The optional license for all packages (platform, add-on, tool, doc) or for a lib. */\r
- public static final String NODE_LICENSE = "license"; //$NON-NLS-1$\r
/** The optional description for all packages (platform, add-on, tool, doc) or for a lib. */\r
public static final String NODE_DESCRIPTION = "description"; //$NON-NLS-1$\r
/** The optional description URL for all packages (platform, add-on, tool, doc). */\r
/** A lib element in a libs container. */\r
public static final String NODE_LIB = "lib"; //$NON-NLS-1$\r
\r
+ /** The path, a string, for extra packages. */\r
+ public static final String NODE_PATH = "path"; //$NON-NLS-1$\r
+\r
/** The archives container, for all packages. */\r
public static final String NODE_ARCHIVES = "archives"; //$NON-NLS-1$\r
/** An archive element, for the archives container. */\r
/** An optional archive Architecture attribute. */\r
public static final String ATTR_ARCH = "arch"; //$NON-NLS-1$\r
\r
+ /** A license definition ID. */\r
+ public static final String ATTR_ID = "id"; //$NON-NLS-1$\r
+ /** A license reference. */\r
+ public static final String ATTR_REF = "ref"; //$NON-NLS-1$\r
+\r
+\r
public static InputStream getXsdStream() {\r
return SdkRepository.class.getResourceAsStream("sdk-repository.xsd"); //$NON-NLS-1$\r
}\r
<xsd:complexType>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
- <!-- The definition of an SDK platform package -->
+ <!-- The definition of an SDK platform package. -->
<xsd:element name="platform">
<xsd:annotation>
<!-- The revision, an int > 0, incremented each time a new
package is generated. -->
- <xsd:element name="revision" type="xsd:positiveInteger" />
+ <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="license" type="xsd:string" minOccurs="0" />
+ <xsd:element name="uses-license" type="sdk:licenseType" minOccurs="0" />
<!-- The optional description of this package. -->
- <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <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" />
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
<!-- A list of file archives for this package. -->
- <xsd:element name="archives" type="sdk:archivesType" />
+ <xsd:element name="archives" type="sdk:archivesType" />
</xsd:all>
</xsd:complexType>
</xsd:element>
- <!-- The definition of an SDK Add-on package -->
+
+ <!-- The definition of an SDK Add-on package. -->
<xsd:element name="add-on">
<xsd:annotation>
<!-- The revision, an int > 0, incremented each time a new
package is generated. -->
- <xsd:element name="revision" type="xsd:positiveInteger" />
+ <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="license" type="xsd:string" minOccurs="0" />
+ <xsd:element name="uses-license" type="sdk:licenseType" minOccurs="0" />
<!-- The optional description of this package. -->
- <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <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" />
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
<!-- A list of file archives for this package. -->
- <xsd:element name="archives" type="sdk:archivesType" />
+ <xsd:element name="archives" type="sdk:archivesType" />
<!-- An add-on can declare 0 or more libraries. -->
</xsd:complexType>
</xsd:element>
- <!-- The definition of an SDK tool package -->
+
+ <!-- The definition of an SDK tool package. -->
<xsd:element name="tool">
<xsd:annotation>
<xsd:all>
<!-- The revision, an int > 0, incremented each time a new
package is generated. -->
- <xsd:element name="revision" type="xsd:positiveInteger" />
+ <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="license" type="xsd:string" minOccurs="0" />
+ <xsd:element name="uses-license" type="sdk:licenseType" minOccurs="0" />
<!-- The optional description of this package. -->
- <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <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" />
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
<!-- A list of file archives for this package. -->
- <xsd:element name="archives" type="sdk:archivesType" />
+ <xsd:element name="archives" type="sdk:archivesType" />
</xsd:all>
</xsd:complexType>
</xsd:element>
- <!-- The definition of an SDK doc package -->
+
+ <!-- The definition of an SDK doc package. -->
<xsd:element name="doc">
<xsd:annotation>
<!-- The revision, an int > 0, incremented each time a new
package is generated. -->
- <xsd:element name="revision" type="xsd:positiveInteger" />
+ <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="sdk:licenseType" 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" />
+ <!-- A list of file archives for this package. -->
+ <xsd:element name="archives" type="sdk:archivesType" />
+ </xsd:all>
+ </xsd:complexType>
+ </xsd:element>
+
+
+ <!-- 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:element name="extra">
+ <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:complexType>
+ <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="license" type="xsd:string" minOccurs="0" />
+ <xsd:element name="uses-license" type="sdk:licenseType" minOccurs="0" />
<!-- The optional description of this package. -->
- <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <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" />
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
<!-- A list of file archives for this package. -->
- <xsd:element name="archives" type="sdk:archivesType" />
+ <xsd:element name="archives" type="sdk:archivesType" />
</xsd:all>
</xsd:complexType>
</xsd:element>
+
+
+
+ <!-- The definition of a license to be referenced by the uses-license element. -->
+
+ <xsd:element name="license">
+ <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:complexType>
+ <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>
+ </xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
+
+ <!-- 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="licenseType">
+ <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.
</xsd:sequence>
</xsd:complexType>
+
<!-- The definition of a file checksum -->
<xsd:simpleType name="sha1Number">
</xsd:simpleContent>
</xsd:complexType>
+
</xsd:schema>
\r
package com.android.sdklib.repository;\r
\r
+import com.android.sdklib.SdkConstants;\r
+\r
import org.xml.sax.ErrorHandler;\r
import org.xml.sax.SAXException;\r
import org.xml.sax.SAXParseException;\r
// If we get here, the validator has not failed as we expected it to.\r
fail();\r
}\r
+\r
+ /** A document an unknown license id. */\r
+ public void testLicenseIdNotFound() throws Exception {\r
+ // we define a license named "lic1" and then reference "lic2" instead\r
+ String document = "<?xml version=\"1.0\"?>" +\r
+ "<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" >" +\r
+ "<r:license id=\"lic1\"> some license </r:license> " +\r
+ "<r:tool> <r:uses-license ref=\"lic2\" /> <r:revision>1</r:revision> " +\r
+ "<r:archives> <r:archive os=\"any\"> <r:size>1</r:size> <r:checksum>2822ae37115ebf13412bbef91339ee0d9454525e</r:checksum> " +\r
+ "<r:url>url</r:url> </r:archive> </r:archives> </r:tool>" +\r
+ "</r:sdk-repository>";\r
+\r
+ 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(null);\r
+ try {\r
+ validator.validate(source);\r
+ } catch (SAXParseException e) {\r
+ // We expect a parse error referring to this grammar rule\r
+ assertRegex("cvc-id.1: There is no ID/IDREF binding for IDREF 'lic2'.*",\r
+ e.getMessage());\r
+ return;\r
+ }\r
+ // If we get here, the validator has not failed as we expected it to.\r
+ fail();\r
+ }\r
+\r
+ /** A document a slash in an extra path. */\r
+ public void testExtraPathWithSlash() throws Exception {\r
+ // we define a license named "lic1" and then reference "lic2" instead\r
+ String document = "<?xml version=\"1.0\"?>" +\r
+ "<r:sdk-repository xmlns:r=\"http://schemas.android.com/sdk/android/repository/1\" >" +\r
+ "<r:extra> <r:revision>1</r:revision> <r:path>path/cannot\\contain\\segments</r:path> " +\r
+ "<r:archives> <r:archive os=\"any\"> <r:size>1</r:size> <r:checksum>2822ae37115ebf13412bbef91339ee0d9454525e</r:checksum> " +\r
+ "<r:url>url</r:url> </r:archive> </r:archives> </r:extra>" +\r
+ "</r:sdk-repository>";\r
+\r
+ 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(null);\r
+ try {\r
+ validator.validate(source);\r
+ } catch (SAXParseException e) {\r
+ // We expect a parse error referring to this grammar rule\r
+ assertRegex("cvc-pattern-valid: Value 'path/cannot\\\\contain\\\\segments' is not facet-valid with respect to pattern.*",\r
+ e.getMessage());\r
+ return;\r
+ }\r
+ // If we get here, the validator has not failed as we expected it to.\r
+ fail();\r
+ }\r
}\r
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
xmlns:sdk="http://schemas.android.com/sdk/android/repository/1">\r
\r
+ <!-- Define a couple of licenses. These will be referenced by uses-license later. -->\r
+\r
+ <sdk:license type="text" id="license1">\r
+ This is the license\r
+ for this platform.\r
+ </sdk:license>\r
+\r
+ <sdk:license id="license2">\r
+ Licenses are only of type 'text' right now, so this is implied.\r
+ </sdk:license>\r
+\r
<!-- Inner elements must be either platform, add-on, doc or tool.\r
There can be 0 or more of each, in any order. -->\r
\r
<sdk:version>1.0</sdk:version>\r
<sdk:api-level>1</sdk:api-level>\r
<sdk:revision>3</sdk:revision>\r
+ <sdk:uses-license ref="license1" />\r
<sdk:description>Some optional description</sdk:description>\r
- <sdk:license>This is the license\r
- for this platform.</sdk:license>\r
<sdk:desc-url>http://www.example.com/platform1.html</sdk:desc-url>\r
<!-- The archives node is mandatory and it cannot be empty. -->\r
<sdk:archives>\r
<sdk:doc>\r
<sdk:api-level>1</sdk:api-level>\r
<sdk:revision>1</sdk:revision>\r
- <sdk:description>Some optional description</sdk:description>\r
<!-- the license element is not mandatory. -->\r
+ <sdk:description>Some optional description</sdk:description>\r
<sdk:desc-url>http://www.example.com/docs.html</sdk:desc-url>\r
<sdk:archives>\r
<sdk:archive os="any">\r
<sdk:api-level>1</sdk:api-level>\r
<sdk:vendor>John Doe</sdk:vendor>\r
<sdk:revision>1</sdk:revision>\r
- <!-- license can be empty. -->\r
- <sdk:license></sdk:license>\r
+ <sdk:uses-license ref="license2" />\r
<sdk:description>Some optional description</sdk:description>\r
<sdk:desc-url>http://www.example.com/myfirstaddon</sdk:desc-url>\r
<sdk:archives>\r
<sdk:version>1.1</sdk:version>\r
<sdk:api-level>2</sdk:api-level>\r
<sdk:revision>12</sdk:revision>\r
- <sdk:license>This is the license for this package.</sdk:license>\r
+ <sdk:uses-license ref="license1" />\r
<!-- sdk:description and sdk:desc-url are optional -->\r
<sdk:archives>\r
<sdk:archive os="windows">\r
<sdk:name>com.android.mymaps</sdk:name>\r
</sdk:lib>\r
</sdk:libs>\r
- <sdk:license>This is the license for this package.</sdk:license>\r
+ <sdk:uses-license ref="license2" />\r
</sdk:add-on>\r
\r
<sdk:tool>\r
<sdk:revision>1</sdk:revision>\r
<sdk:description>Some optional description</sdk:description>\r
<sdk:desc-url>http://www.example.com/tools.html</sdk:desc-url>\r
- <sdk:license>This is the license for this package.</sdk:license>\r
+ <sdk:uses-license ref="license1" />\r
<sdk:archives>\r
<sdk:archive os="any">\r
<sdk:size>65536</sdk:size>\r
<sdk:doc>\r
<sdk:api-level>2</sdk:api-level>\r
<sdk:revision>42</sdk:revision>\r
- <sdk:license>This is the license for this package.</sdk:license>\r
+ <sdk:uses-license ref="license2" />\r
<sdk:archives>\r
<sdk:archive os="windows">\r
<sdk:size>65536</sdk:size>\r
\r
<sdk:tool>\r
<sdk:revision>42</sdk:revision>\r
- <sdk:license>This is the license for this package.</sdk:license>\r
+ <sdk:uses-license ref="license1" />\r
<sdk:archives>\r
<sdk:archive os="windows">\r
<sdk:size>65536</sdk:size>\r
</sdk:tool>\r
\r
<sdk:add-on>\r
- <sdk:license>This is the license for this package.</sdk:license>\r
+ <sdk:uses-license ref="license2" />\r
<sdk:name>This add-on has no libraries</sdk:name>\r
<sdk:api-level>4</sdk:api-level>\r
<sdk:vendor>Joe Bar</sdk:vendor>\r
<sdk:libs />\r
</sdk:add-on>\r
\r
+ <sdk:extra>\r
+ <sdk:path>usb_driver</sdk:path>\r
+ <sdk:uses-license ref="license2" />\r
+ <sdk:revision>43</sdk:revision>\r
+ <sdk:archives>\r
+ <sdk:archive os="any" arch="any">\r
+ <sdk:size>65536</sdk:size>\r
+ <sdk:checksum type="sha1">2822ae37115ebf13412bbef91339ee0d9454525e</sdk:checksum>\r
+ <sdk:url>distrib/extraduff.zip</sdk:url>\r
+ </sdk:archive>\r
+ </sdk:archives>\r
+ <sdk:description>An Extra package for the USB driver, it will install in $SDK/usb_driver</sdk:description>\r
+ <sdk:desc-url>http://www.example.com/extra.html</sdk:desc-url>\r
+ </sdk:extra>\r
+\r
</sdk:sdk-repository>\r
import com.android.sdklib.internal.repository.AddonPackage;\r
import com.android.sdklib.internal.repository.Archive;\r
import com.android.sdklib.internal.repository.DocPackage;\r
+import com.android.sdklib.internal.repository.ExtraPackage;\r
import com.android.sdklib.internal.repository.Package;\r
import com.android.sdklib.internal.repository.PlatformPackage;\r
import com.android.sdklib.internal.repository.RepoSource;\r
} else if (object instanceof DocPackage) {\r
return getImageByName("doc_icon16.png");\r
\r
- } else if (object instanceof Package) {\r
- return getImageByName("extra_pkg_icon16.png");\r
+ } else if (object instanceof ExtraPackage) {\r
+ return getImageByName("extra_icon16.png");\r
\r
} else if (object instanceof Archive) {\r
if (((Archive) object).isCompatible()) {\r