From 48c6578bb61351801b8fb1daef6676cf2d59dfdd Mon Sep 17 00:00:00 2001 From: Raphael Date: Thu, 18 Jun 2009 16:07:55 -0700 Subject: [PATCH] SDK Updater: use separate license node in XML. Support extra packages. --- .../internal/repository/AddonPackage.java | 17 ++- .../sdklib/internal/repository/Archive.java | 11 +- .../internal/repository/DocPackage.java | 7 +- .../internal/repository/ExtraPackage.java | 144 ++++++++++++++++++ .../internal/repository/LocalSdkParser.java | 25 ++- .../sdklib/internal/repository/Package.java | 124 ++++----------- .../internal/repository/PlatformPackage.java | 9 +- .../internal/repository/RepoSource.java | 28 +++- .../internal/repository/ToolPackage.java | 5 +- .../internal/repository/XmlParserUtils.java | 108 +++++++++++++ .../sdklib/repository/SdkRepository.java | 17 ++- .../sdklib/repository/sdk-repository.xsd | 138 ++++++++++++++--- .../sdklib/repository/TestSdkRepository.java | 55 +++++++ .../sdklib/repository/repository_sample.xml | 46 ++++-- .../repository/icons/ImageFactory.java | 5 +- .../repository/icons/addon_icon16.png | Bin 485 -> 539 bytes .../repository/icons/extra_icon16.png | Bin 0 -> 428 bytes 17 files changed, 582 insertions(+), 157 deletions(-) create mode 100755 tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java create mode 100755 tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/XmlParserUtils.java create mode 100755 tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/extra_icon16.png diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java index c9372de7c..e273a5458 100755 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java @@ -28,6 +28,7 @@ import org.w3c.dom.Node; import java.io.File; import java.util.ArrayList; +import java.util.Map; /** * Represents an add-on XML node in an SDK repository. @@ -64,13 +65,13 @@ public class AddonPackage extends Package { *

* This constructor should throw an exception if the package cannot be created. */ - AddonPackage(RepoSource source, Node packageNode) { - super(source, packageNode); - mVendor = getXmlString(packageNode, SdkRepository.NODE_VENDOR); - mName = getXmlString(packageNode, SdkRepository.NODE_NAME); - mApiLevel = getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0); + AddonPackage(RepoSource source, Node packageNode, Map licenses) { + super(source, packageNode, licenses); + mVendor = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VENDOR); + mName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_NAME); + mApiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0); - mLibs = parseLibs(getFirstChild(packageNode, SdkRepository.NODE_LIBS)); + mLibs = parseLibs(XmlParserUtils.getFirstChild(packageNode, SdkRepository.NODE_LIBS)); } /** @@ -131,8 +132,8 @@ public class AddonPackage extends Package { * Parses a element from a container. */ private Lib parseLib(Node libNode) { - return new Lib(getXmlString(libNode, SdkRepository.NODE_NAME), - getXmlString(libNode, SdkRepository.NODE_DESCRIPTION)); + return new Lib(XmlParserUtils.getXmlString(libNode, SdkRepository.NODE_NAME), + XmlParserUtils.getXmlString(libNode, SdkRepository.NODE_DESCRIPTION)); } /** Returns the vendor, a string, for add-on packages. */ diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java index 826d629ef..191ef1986 100755 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Archive.java @@ -348,9 +348,18 @@ public class Archive implements IDescription { SdkManager sdkManager, ITaskMonitor monitor) { + Package pkg = getParentPackage(); + File archiveFile = null; try { - String name = getParentPackage().getShortDescription(); + String name = pkg.getShortDescription(); + + if (pkg instanceof ExtraPackage && !((ExtraPackage) pkg).isPathValid()) { + monitor.setResult("Skipping %1$s: %2$s is not a valid install path.", + name, + ((ExtraPackage) pkg).getPath()); + return false; + } if (isLocal()) { // This should never happen. diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java index 0e9471740..e7fa8936a 100755 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java @@ -25,6 +25,7 @@ import com.android.sdklib.repository.SdkRepository; import org.w3c.dom.Node; import java.io.File; +import java.util.Map; /** * Represents a doc XML node in an SDK repository. @@ -38,9 +39,9 @@ public class DocPackage extends Package { *

* This constructor should throw an exception if the package cannot be created. */ - DocPackage(RepoSource source, Node packageNode) { - super(source, packageNode); - mApiLevel = getXmlInt(packageNode, SdkRepository.NODE_API_LEVEL, 0); + DocPackage(RepoSource source, Node packageNode, Map licenses) { + super(source, packageNode, licenses); + mApiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_API_LEVEL, 0); } /** diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java new file mode 100755 index 000000000..efa693115 --- /dev/null +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.repository; + +import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.repository.Archive.Arch; +import com.android.sdklib.internal.repository.Archive.Os; +import com.android.sdklib.repository.SdkRepository; + +import org.w3c.dom.Node; + +import java.io.File; +import java.util.Map; + +/** + * Represents a extra XML node in an SDK repository. + */ +public class ExtraPackage extends Package { + + private final String mPath; + + /** + * Creates a new tool package from the attributes and elements of the given XML node. + *

+ * This constructor should throw an exception if the package cannot be created. + */ + ExtraPackage(RepoSource source, Node packageNode, Map licenses) { + super(source, packageNode, licenses); + mPath = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_PATH); + } + + /** + * Manually create a new package with one archive and the given attributes. + * This is used to create packages from local directories in which case there must be + * one archive which URL is the actual target location. + */ + ExtraPackage(RepoSource source, + String path, + int revision, + String license, + String description, + String descUrl, + Os archiveOs, + Arch archiveArch, + String archiveOsPath) { + super(source, + revision, + license, + description, + descUrl, + archiveOs, + archiveArch, + archiveOsPath); + mPath = path; + } + + /** + * Static helper to check if a given path is acceptable for an "extra" package. + */ + public boolean isPathValid() { + if (SdkConstants.FD_ADDONS.equals(mPath) || + SdkConstants.FD_PLATFORMS.equals(mPath) || + SdkConstants.FD_TOOLS.equals(mPath) || + SdkConstants.FD_DOCS.equals(mPath)) { + return false; + } + return mPath != null && mPath.indexOf('/') == -1 && mPath.indexOf('\\') == -1; + } + + /** + * 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. + * This limitation cannot be written in the XML Schema and must be enforced here by using + * the method {@link #isPathValid()} *before* installing the package. + */ + public String getPath() { + return mPath; + } + + /** Returns a short description for an {@link IDescription}. */ + @Override + public String getShortDescription() { + return String.format("Extra %1$s package, revision %2$d", getPath(), getRevision()); + } + + /** Returns a long description for an {@link IDescription}. */ + @Override + public String getLongDescription() { + return String.format("Extra %1$s package, revision %2$d.\n%3$s", + getPath(), + getRevision(), + super.getLongDescription()); + } + + /** + * Computes a potential installation folder if an archive of this package were + * to be installed right away in the given SDK root. + *

+ * A "tool" package should always be located in SDK/tools. + * + * @param osSdkRoot The OS path of the SDK root folder. + * @return A new {@link File} corresponding to the directory to use to install this package. + */ + @Override + public File getInstallFolder(String osSdkRoot) { + return new File(osSdkRoot, getPath()); + } + + /** + * Computes whether the given extra package is a suitable update for the current package. + * The base method checks the class type. + * The tools package also tests that the revision number is greater and the path is the + * same. + *

+ * An update is just that: a new package that supersedes the current one. If the new + * package has the same revision as the current one, it's not an update. + * + * @param replacementPackage The potential replacement package. + * @return True if the replacement package is a suitable update for this one. + */ + @Override + public boolean canBeUpdatedBy(Package replacementPackage) { + if (!super.canBeUpdatedBy(replacementPackage)) { + return false; + } + + ExtraPackage newPkg = (ExtraPackage) replacementPackage; + return newPkg.getRevision() > this.getRevision() && newPkg.getPath().equals(this.getPath()); + } +} diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java index 10f297e53..91e264caf 100755 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; @@ -250,6 +251,22 @@ public class LocalSdkParser { Node root = getFirstChild(doc, SdkRepository.NODE_SDK_REPOSITORY); if (root != null) { + // Parse license definitions + HashMap licenses = new HashMap(); + for (Node child = root.getFirstChild(); + child != null; + child = child.getNextSibling()) { + if (child.getNodeType() == Node.ELEMENT_NODE && + SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI()) && + child.getLocalName().equals(SdkRepository.NODE_LICENSE)) { + Node id = child.getAttributes().getNamedItem(SdkRepository.ATTR_ID); + if (id != null) { + licenses.put(id.getNodeValue(), child.getTextContent()); + } + } + } + + // Parse packages for (Node child = root.getFirstChild(); child != null; child = child.getNextSibling()) { @@ -260,16 +277,16 @@ public class LocalSdkParser { try { if (SdkRepository.NODE_ADD_ON.equals(name)) { - return new AddonPackage(null /*source*/, child); + return new AddonPackage(null /*source*/, child, licenses); } else if (SdkRepository.NODE_PLATFORM.equals(name)) { - return new PlatformPackage(null /*source*/, child); + return new PlatformPackage(null /*source*/, child, licenses); } else if (SdkRepository.NODE_DOC.equals(name)) { - return new DocPackage(null /*source*/, child); + return new DocPackage(null /*source*/, child, licenses); } else if (SdkRepository.NODE_TOOL.equals(name)) { - return new ToolPackage(null /*source*/, child); + return new ToolPackage(null /*source*/, child, licenses); } } catch (Exception e) { // Ignore invalid packages diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java index bd6ccc046..0e11ef501 100755 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java @@ -25,6 +25,7 @@ import org.w3c.dom.Node; import java.io.File; import java.util.ArrayList; +import java.util.Map; /** * A {@link Package} is the base class for "something" that can be downloaded from @@ -52,13 +53,15 @@ public abstract class Package implements IDescription { *

* This constructor should throw an exception if the package cannot be created. */ - Package(RepoSource source, Node packageNode) { + Package(RepoSource source, Node packageNode, Map licenses) { mSource = source; - mRevision = getXmlInt (packageNode, SdkRepository.NODE_REVISION, 0); - mDescription = getXmlString(packageNode, SdkRepository.NODE_DESCRIPTION); - mDescUrl = getXmlString(packageNode, SdkRepository.NODE_DESC_URL); - mLicense = getXmlString(packageNode, SdkRepository.NODE_LICENSE); - mArchives = parseArchives(getFirstChild(packageNode, SdkRepository.NODE_ARCHIVES)); + mRevision = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_REVISION, 0); + mDescription = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_DESCRIPTION); + mDescUrl = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_DESC_URL); + + mLicense = parseLicense(packageNode, licenses); + mArchives = parseArchives(XmlParserUtils.getFirstChild( + packageNode, SdkRepository.NODE_ARCHIVES)); } /** @@ -86,6 +89,24 @@ public abstract class Package implements IDescription { archiveOsPath); } + /** + * Parses the uses-licence node of this package, if any, and returns the license + * definition if there's one. Returns null if there's no uses-license element or no + * license of this name defined. + */ + private String parseLicense(Node packageNode, Map licenses) { + Node usesLicense = XmlParserUtils.getFirstChild( + packageNode, SdkRepository.NODE_USES_LICENSE); + if (usesLicense != null) { + Node ref = usesLicense.getAttributes().getNamedItem(SdkRepository.ATTR_REF); + if (ref != null) { + String licenseRef = ref.getNodeValue(); + return licenses.get(licenseRef); + } + } + return null; + } + /** * Parses an XML node to process the element. */ @@ -114,13 +135,13 @@ public abstract class Package implements IDescription { private Archive parseArchive(Node archiveNode) { Archive a = new Archive( this, - (Os) getEnumAttribute(archiveNode, SdkRepository.ATTR_OS, + (Os) XmlParserUtils.getEnumAttribute(archiveNode, SdkRepository.ATTR_OS, Os.values(), null), - (Arch) getEnumAttribute(archiveNode, SdkRepository.ATTR_ARCH, + (Arch) XmlParserUtils.getEnumAttribute(archiveNode, SdkRepository.ATTR_ARCH, Arch.values(), Arch.ANY), - getXmlString(archiveNode, SdkRepository.NODE_URL), - getXmlLong(archiveNode, SdkRepository.NODE_SIZE, 0), - getXmlString(archiveNode, SdkRepository.NODE_CHECKSUM) + XmlParserUtils.getXmlString(archiveNode, SdkRepository.NODE_URL), + XmlParserUtils.getXmlLong (archiveNode, SdkRepository.NODE_SIZE, 0), + XmlParserUtils.getXmlString(archiveNode, SdkRepository.NODE_CHECKSUM) ); return a; @@ -224,85 +245,4 @@ public abstract class Package implements IDescription { replacementPackage.getRevision() > this.getRevision(); } - //--- - - /** - * Returns the first child element with the given XML local name. - * If xmlLocalName is null, returns the very first child element. - */ - protected static Node getFirstChild(Node node, String xmlLocalName) { - - for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { - if (child.getNodeType() == Node.ELEMENT_NODE && - SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI())) { - if (xmlLocalName == null || xmlLocalName.equals(child.getLocalName())) { - return child; - } - } - } - - return null; - } - - /** - * Retrieves the value of that XML element as a string. - * Returns an empty string when the element is missing. - */ - protected static String getXmlString(Node node, String xmlLocalName) { - Node child = getFirstChild(node, xmlLocalName); - - return child == null ? "" : child.getTextContent(); //$NON-NLS-1$ - } - - /** - * Retrieves the value of that XML element as an integer. - * Returns the default value when the element is missing or is not an integer. - */ - protected static int getXmlInt(Node node, String xmlLocalName, int defaultValue) { - String s = getXmlString(node, xmlLocalName); - try { - return Integer.parseInt(s); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * Retrieves the value of that XML element as a long. - * Returns the default value when the element is missing or is not an integer. - */ - protected static long getXmlLong(Node node, String xmlLocalName, long defaultValue) { - String s = getXmlString(node, xmlLocalName); - try { - return Long.parseLong(s); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * Retrieve an attribute which value must match one of the given enums using a - * case-insensitive name match. - * - * Returns defaultValue if the attribute does not exist or its value does not match - * the given enum values. - */ - private Object getEnumAttribute( - Node archiveNode, - String attrName, - Object[] values, - Object defaultValue) { - - Node attr = archiveNode.getAttributes().getNamedItem(attrName); - if (attr != null) { - String found = attr.getNodeValue(); - for (Object value : values) { - if (value.toString().equalsIgnoreCase(found)) { - return value; - } - } - } - - return defaultValue; - } } diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java index 66dd529c2..622a6f276 100755 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java @@ -26,6 +26,7 @@ import com.android.sdklib.repository.SdkRepository; import org.w3c.dom.Node; import java.io.File; +import java.util.Map; /** * Represents a platform XML node in an SDK repository. @@ -40,10 +41,10 @@ public class PlatformPackage extends Package { *

* This constructor should throw an exception if the package cannot be created. */ - PlatformPackage(RepoSource source, Node packageNode) { - super(source, packageNode); - mVersion = getXmlString(packageNode, SdkRepository.NODE_VERSION); - mApiLevel = getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0); + PlatformPackage(RepoSource source, Node packageNode, Map licenses) { + super(source, packageNode, licenses); + mVersion = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VERSION); + mApiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0); } /** diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java index b1993eef7..b72e7a9d8 100755 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/RepoSource.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; +import java.util.HashMap; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; @@ -248,6 +249,22 @@ public class RepoSource implements IDescription { ArrayList packages = new ArrayList(); + // Parse license definitions + HashMap licenses = new HashMap(); + for (Node child = root.getFirstChild(); + child != null; + child = child.getNextSibling()) { + if (child.getNodeType() == Node.ELEMENT_NODE && + SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI()) && + child.getLocalName().equals(SdkRepository.NODE_LICENSE)) { + Node id = child.getAttributes().getNamedItem(SdkRepository.ATTR_ID); + if (id != null) { + licenses.put(id.getNodeValue(), child.getTextContent()); + } + } + } + + // Parse packages for (Node child = root.getFirstChild(); child != null; child = child.getNextSibling()) { @@ -258,15 +275,18 @@ public class RepoSource implements IDescription { try { if (SdkRepository.NODE_ADD_ON.equals(name)) { - p = new AddonPackage(this, child); + p = new AddonPackage(this, child, licenses); + + } else if (SdkRepository.NODE_EXTRA.equals(name)) { + p = new ExtraPackage(this, child, licenses); } else if (!mAddonOnly) { if (SdkRepository.NODE_PLATFORM.equals(name)) { - p = new PlatformPackage(this, child); + p = new PlatformPackage(this, child, licenses); } else if (SdkRepository.NODE_DOC.equals(name)) { - p = new DocPackage(this, child); + p = new DocPackage(this, child, licenses); } else if (SdkRepository.NODE_TOOL.equals(name)) { - p = new ToolPackage(this, child); + p = new ToolPackage(this, child, licenses); } } diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java index b6d830c1b..107604965 100755 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java @@ -24,6 +24,7 @@ import com.android.sdklib.internal.repository.Archive.Os; import org.w3c.dom.Node; import java.io.File; +import java.util.Map; /** * Represents a tool XML node in an SDK repository. @@ -35,8 +36,8 @@ public class ToolPackage extends Package { *

* This constructor should throw an exception if the package cannot be created. */ - ToolPackage(RepoSource source, Node packageNode) { - super(source, packageNode); + ToolPackage(RepoSource source, Node packageNode, Map licenses) { + super(source, packageNode, licenses); } /** diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/XmlParserUtils.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/XmlParserUtils.java new file mode 100755 index 000000000..7a8bc7dd3 --- /dev/null +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/XmlParserUtils.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.repository; + +import com.android.sdklib.repository.SdkRepository; + +import org.w3c.dom.Node; + +/** + * Misc utilities to help extracting elements and attributes out of an XML document. + */ +class XmlParserUtils { + + /** + * Returns the first child element with the given XML local name. + * If xmlLocalName is null, returns the very first child element. + */ + public static Node getFirstChild(Node node, String xmlLocalName) { + + for(Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child.getNodeType() == Node.ELEMENT_NODE && + SdkRepository.NS_SDK_REPOSITORY.equals(child.getNamespaceURI())) { + if (xmlLocalName == null || xmlLocalName.equals(child.getLocalName())) { + return child; + } + } + } + + return null; + } + + /** + * Retrieves the value of that XML element as a string. + * Returns an empty string when the element is missing. + */ + public static String getXmlString(Node node, String xmlLocalName) { + Node child = getFirstChild(node, xmlLocalName); + + return child == null ? "" : child.getTextContent(); //$NON-NLS-1$ + } + + /** + * Retrieves the value of that XML element as an integer. + * Returns the default value when the element is missing or is not an integer. + */ + public static int getXmlInt(Node node, String xmlLocalName, int defaultValue) { + String s = getXmlString(node, xmlLocalName); + try { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * Retrieves the value of that XML element as a long. + * Returns the default value when the element is missing or is not an integer. + */ + public static long getXmlLong(Node node, String xmlLocalName, long defaultValue) { + String s = getXmlString(node, xmlLocalName); + try { + return Long.parseLong(s); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * Retrieve an attribute which value must match one of the given enums using a + * case-insensitive name match. + * + * Returns defaultValue if the attribute does not exist or its value does not match + * the given enum values. + */ + public static Object getEnumAttribute( + Node archiveNode, + String attrName, + Object[] values, + Object defaultValue) { + + Node attr = archiveNode.getAttributes().getNamedItem(attrName); + if (attr != null) { + String found = attr.getNodeValue(); + for (Object value : values) { + if (value.toString().equalsIgnoreCase(found)) { + return value; + } + } + } + + return defaultValue; + } + +} diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java index cd6d45b80..a317640c6 100755 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/SdkRepository.java @@ -43,11 +43,15 @@ public class SdkRepository { public static final String NODE_TOOL = "tool"; //$NON-NLS-1$ /** A doc package. */ public static final String NODE_DOC = "doc"; //$NON-NLS-1$ + /** An extra package. */ + public static final String NODE_EXTRA = "extra"; //$NON-NLS-1$ + /** The license definition. */ + public static final String NODE_LICENSE = "license"; //$NON-NLS-1$ + /** The optional uses-license for all packages (platform, add-on, tool, doc) or for a lib. */ + public static final String NODE_USES_LICENSE = "uses-license"; //$NON-NLS-1$ /** The revision, an int > 0, for all packages (platform, add-on, tool, doc). */ public static final String NODE_REVISION = "revision"; //$NON-NLS-1$ - /** The optional license for all packages (platform, add-on, tool, doc) or for a lib. */ - public static final String NODE_LICENSE = "license"; //$NON-NLS-1$ /** The optional description for all packages (platform, add-on, tool, doc) or for a lib. */ public static final String NODE_DESCRIPTION = "description"; //$NON-NLS-1$ /** The optional description URL for all packages (platform, add-on, tool, doc). */ @@ -67,6 +71,9 @@ public class SdkRepository { /** A lib element in a libs container. */ public static final String NODE_LIB = "lib"; //$NON-NLS-1$ + /** The path, a string, for extra packages. */ + public static final String NODE_PATH = "path"; //$NON-NLS-1$ + /** The archives container, for all packages. */ public static final String NODE_ARCHIVES = "archives"; //$NON-NLS-1$ /** An archive element, for the archives container. */ @@ -86,6 +93,12 @@ public class SdkRepository { /** An optional archive Architecture attribute. */ public static final String ATTR_ARCH = "arch"; //$NON-NLS-1$ + /** A license definition ID. */ + public static final String ATTR_ID = "id"; //$NON-NLS-1$ + /** A license reference. */ + public static final String ATTR_REF = "ref"; //$NON-NLS-1$ + + public static InputStream getXsdStream() { return SdkRepository.class.getResourceAsStream("sdk-repository.xsd"); //$NON-NLS-1$ } diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd index 1862ae87e..c55e7d513 100755 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository.xsd @@ -40,7 +40,7 @@ - + @@ -55,21 +55,22 @@ - + - + - + - + - + - + + @@ -86,16 +87,16 @@ - + - + - + - + - + @@ -119,7 +120,8 @@ - + + @@ -129,21 +131,22 @@ - + - + - + - + - + - + + @@ -156,23 +159,108 @@ - + - + - + - + - + + + + + + + + + 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A license definition. Such a license must be used later as a reference + using a uses-license element in one of the package elements. + + + + + + + + + + + + + + + + + + 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. + + + + + + @@ -254,4 +343,5 @@ + diff --git a/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/TestSdkRepository.java b/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/TestSdkRepository.java index 879f3e6b2..207a5a222 100755 --- a/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/TestSdkRepository.java +++ b/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/TestSdkRepository.java @@ -16,6 +16,8 @@ package com.android.sdklib.repository; +import com.android.sdklib.SdkConstants; + import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; @@ -245,4 +247,57 @@ public class TestSdkRepository extends TestCase { // If we get here, the validator has not failed as we expected it to. fail(); } + + /** A document an unknown license id. */ + public void testLicenseIdNotFound() throws Exception { + // we define a license named "lic1" and then reference "lic2" instead + String document = "" + + "" + + " some license " + + " 1 " + + " 1 2822ae37115ebf13412bbef91339ee0d9454525e " + + "url " + + ""; + + Source source = new StreamSource(new StringReader(document)); + + // don't capture the validator errors, we want it to fail and catch the exception + Validator validator = getValidator(null); + try { + validator.validate(source); + } catch (SAXParseException e) { + // We expect a parse error referring to this grammar rule + assertRegex("cvc-id.1: There is no ID/IDREF binding for IDREF 'lic2'.*", + e.getMessage()); + return; + } + // If we get here, the validator has not failed as we expected it to. + fail(); + } + + /** A document a slash in an extra path. */ + public void testExtraPathWithSlash() throws Exception { + // we define a license named "lic1" and then reference "lic2" instead + String document = "" + + "" + + " 1 path/cannot\\contain\\segments " + + " 1 2822ae37115ebf13412bbef91339ee0d9454525e " + + "url " + + ""; + + Source source = new StreamSource(new StringReader(document)); + + // don't capture the validator errors, we want it to fail and catch the exception + Validator validator = getValidator(null); + try { + validator.validate(source); + } catch (SAXParseException e) { + // We expect a parse error referring to this grammar rule + assertRegex("cvc-pattern-valid: Value 'path/cannot\\\\contain\\\\segments' is not facet-valid with respect to pattern.*", + e.getMessage()); + return; + } + // If we get here, the validator has not failed as we expected it to. + fail(); + } } diff --git a/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml b/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml index a2d0cda20..d4eacf9e6 100755 --- a/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml +++ b/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/repository_sample.xml @@ -18,6 +18,17 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sdk="http://schemas.android.com/sdk/android/repository/1"> + + + + This is the license + for this platform. + + + + Licenses are only of type 'text' right now, so this is implied. + + @@ -25,9 +36,8 @@ 1.0 1 3 + Some optional description - This is the license - for this platform. http://www.example.com/platform1.html @@ -42,8 +52,8 @@ 1 1 - Some optional description + Some optional description http://www.example.com/docs.html @@ -59,8 +69,7 @@ 1 John Doe 1 - - + Some optional description http://www.example.com/myfirstaddon @@ -87,7 +96,7 @@ 1.1 2 12 - This is the license for this package. + @@ -145,14 +154,14 @@ com.android.mymaps - This is the license for this package. + 1 Some optional description http://www.example.com/tools.html - This is the license for this package. + 65536 @@ -165,7 +174,7 @@ 2 42 - This is the license for this package. + 65536 @@ -187,7 +196,7 @@ 42 - This is the license for this package. + 65536 @@ -208,7 +217,7 @@ - This is the license for this package. + This add-on has no libraries 4 Joe Bar @@ -224,4 +233,19 @@ + + usb_driver + + 43 + + + 65536 + 2822ae37115ebf13412bbef91339ee0d9454525e + distrib/extraduff.zip + + + An Extra package for the USB driver, it will install in $SDK/usb_driver + http://www.example.com/extra.html + + diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java index 8cfe53f49..d4c74d783 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java @@ -19,6 +19,7 @@ package com.android.sdkuilib.internal.repository.icons; import com.android.sdklib.internal.repository.AddonPackage; import com.android.sdklib.internal.repository.Archive; import com.android.sdklib.internal.repository.DocPackage; +import com.android.sdklib.internal.repository.ExtraPackage; import com.android.sdklib.internal.repository.Package; import com.android.sdklib.internal.repository.PlatformPackage; import com.android.sdklib.internal.repository.RepoSource; @@ -103,8 +104,8 @@ public class ImageFactory { } else if (object instanceof DocPackage) { return getImageByName("doc_icon16.png"); - } else if (object instanceof Package) { - return getImageByName("extra_pkg_icon16.png"); + } else if (object instanceof ExtraPackage) { + return getImageByName("extra_icon16.png"); } else if (object instanceof Archive) { if (((Archive) object).isCompatible()) { diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/addon_icon16.png b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/addon_icon16.png index 1a3e0f28c15a11153165a99795b1e72345d60787..ca6a231345580670fa00f01586c77f02250f12a8 100755 GIT binary patch delta 494 zcmVO0yKtKKk$2BI z_r3!pfaD&@?S-=<`3b9l{8ES*yxre(u8Gy2aVovp4tFI-XA-PoOV#BEgGgS+K0t_)@790K6>b;Hp?G9_RD< zceWSR0U&ZBO!hb6dOvZ0db1bzfpW+8UO$Q07*qoM6N<$f-3adoB#j- delta 440 zcmV;p0Z0Cu1my#eH-G;C{{a7>y{D4^00E6jL_t(I%cW95O9D|4{#*rHGmHp2)L{c5 zi0F`Eq~a|I@(*T!{)-pL1%~7NEPJfJPkBn*6R=Yu=XpXE% zTo}e?GC7BMyo&l~XWlRflRg(NuciTD6F~M)^g#86*Ut*Ag|ft$Mv^~Gl0Qw1X|xi& zrI(N5P!|FxQdKP<#8v;&Q%UWN|rE@WoB z0gQl{D7qD{fq#5*;6qBuCrSS;VJ{s67ot(f!h6P280v z002L2iztfj2LOust-16JX*-SP((~{3u9AbB)f5VyFEIhJttsf*S9fDf{vi4qn8DYY i3&nj;c`U;_;ITg)kp$APZ#G*10000QL70(Y)*K0-AbW|YuPggaHX(6V&WYtE&w-}sc)B=-Se#xO zY@5~MAo7n#Z)3!srm5y0GtFiQw|voL%Jeznwqc8qpv_UaME#B895+~*4vHimG}{N9%rtm1Sd0J=g`o|ZrLZ5 ze*4}tGc1{s(BsI#cud0LdB*K)6)SdaW=xP^v*=rV@@rM^_1onP(<^S;CjQ=O%JRr! z*&+=cU4<5dN%PA23$(=_`OUe^1)@6HW z>e@C<`J|A#RCn@kR>6l!dAlDMK67xX-!5?6@bC8t7SFFe+UOE!a(ZfN=jM;hQ7O}` UE^R-Q0t`w9Pgg&ebxsLQ0GlMNWB>pF literal 0 HcmV?d00001