1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.sdklib.internal.repository; 18 19 import com.android.sdklib.SdkConstants; 20 import com.android.sdklib.SdkManager; 21 import com.android.sdklib.internal.repository.Archive.Arch; 22 import com.android.sdklib.internal.repository.Archive.Os; 23 import com.android.sdklib.repository.SdkRepository; 24 25 import org.w3c.dom.Node; 26 27 import java.io.File; 28 import java.util.Map; 29 import java.util.Properties; 30 31 /** 32 * Represents a extra XML node in an SDK repository. 33 */ 34 public class ExtraPackage extends MinToolsPackage 35 implements IMinApiLevelDependency { 36 37 private static final String PROP_PATH = "Extra.Path"; //$NON-NLS-1$ 38 private static final String PROP_MIN_API_LEVEL = "Extra.MinApiLevel"; //$NON-NLS-1$ 39 40 /** 41 * The install folder name. It must be a single-segment path. 42 * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used. 43 * This limitation cannot be written in the XML Schema and must be enforced here by using 44 * the method {@link #isPathValid()} *before* installing the package. 45 */ 46 private final String mPath; 47 48 /** 49 * The minimal API level required by this extra package, if > 0, 50 * or {@link #MIN_API_LEVEL_NOT_SPECIFIED} if there is no such requirement. 51 */ 52 private final int mMinApiLevel; 53 54 /** 55 * Creates a new tool package from the attributes and elements of the given XML node. 56 * <p/> 57 * This constructor should throw an exception if the package cannot be created. 58 */ ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses)59 ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses) { 60 super(source, packageNode, licenses); 61 62 mPath = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_PATH); 63 64 mMinApiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_API_LEVEL, 65 MIN_API_LEVEL_NOT_SPECIFIED); 66 } 67 68 /** 69 * Manually create a new package with one archive and the given attributes or properties. 70 * This is used to create packages from local directories in which case there must be 71 * one archive which URL is the actual target location. 72 * <p/> 73 * By design, this creates a package with one and only one archive. 74 */ ExtraPackage(RepoSource source, Properties props, String path, int revision, String license, String description, String descUrl, Os archiveOs, Arch archiveArch, String archiveOsPath)75 ExtraPackage(RepoSource source, 76 Properties props, 77 String path, 78 int revision, 79 String license, 80 String description, 81 String descUrl, 82 Os archiveOs, 83 Arch archiveArch, 84 String archiveOsPath) { 85 super(source, 86 props, 87 revision, 88 license, 89 description, 90 descUrl, 91 archiveOs, 92 archiveArch, 93 archiveOsPath); 94 95 // The path argument comes before whatever could be in the properties 96 mPath = path != null ? path : getProperty(props, PROP_PATH, path); 97 98 mMinApiLevel = Integer.parseInt( 99 getProperty(props, PROP_MIN_API_LEVEL, Integer.toString(MIN_API_LEVEL_NOT_SPECIFIED))); 100 } 101 102 /** 103 * Save the properties of the current packages in the given {@link Properties} object. 104 * These properties will later be give the constructor that takes a {@link Properties} object. 105 */ 106 @Override saveProperties(Properties props)107 void saveProperties(Properties props) { 108 super.saveProperties(props); 109 110 props.setProperty(PROP_PATH, mPath); 111 112 if (getMinApiLevel() != MIN_API_LEVEL_NOT_SPECIFIED) { 113 props.setProperty(PROP_MIN_API_LEVEL, Integer.toString(getMinApiLevel())); 114 } 115 } 116 117 /** 118 * Returns the minimal API level required by this extra package, if > 0, 119 * or {@link #MIN_API_LEVEL_NOT_SPECIFIED} if there is no such requirement. 120 */ getMinApiLevel()121 public int getMinApiLevel() { 122 return mMinApiLevel; 123 } 124 125 /** 126 * Static helper to check if a given path is acceptable for an "extra" package. 127 */ isPathValid()128 public boolean isPathValid() { 129 if (SdkConstants.FD_ADDONS.equals(mPath) || 130 SdkConstants.FD_PLATFORMS.equals(mPath) || 131 SdkConstants.FD_TOOLS.equals(mPath) || 132 SdkConstants.FD_DOCS.equals(mPath)) { 133 return false; 134 } 135 return mPath != null && mPath.indexOf('/') == -1 && mPath.indexOf('\\') == -1; 136 } 137 138 /** 139 * The install folder name. It must be a single-segment path. 140 * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used. 141 * This limitation cannot be written in the XML Schema and must be enforced here by using 142 * the method {@link #isPathValid()} *before* installing the package. 143 */ getPath()144 public String getPath() { 145 return mPath; 146 } 147 148 /** Returns a short description for an {@link IDescription}. */ 149 @Override getShortDescription()150 public String getShortDescription() { 151 String name = getPath(); 152 if (name != null) { 153 // Uniformize all spaces in the name and upper case words. 154 155 name = name.replaceAll("[ _\t\f-]+", " "); //$NON-NLS-1$ //$NON-NLS-2$ 156 157 // Look at all lower case characters in range [1..n-1] and replace them by an upper 158 // case if they are preceded by a space. Also upper cases the first character of the 159 // string. 160 boolean changed = false; 161 char[] chars = name.toCharArray(); 162 for (int n = chars.length - 1, i = 0; i < n; i++) { 163 if (Character.isLowerCase(chars[i]) && (i == 0 || chars[i - 1] == ' ')) { 164 chars[i] = Character.toUpperCase(chars[i]); 165 changed = true; 166 } 167 } 168 if (changed) { 169 name = new String(chars); 170 } 171 } 172 173 String s = String.format("%1$s package, revision %2$d%3$s", 174 name, 175 getRevision(), 176 isObsolete() ? " (Obsolete)" : ""); 177 178 return s; 179 } 180 181 /** 182 * Returns a long description for an {@link IDescription}. 183 * 184 * The long description is whatever the XML contains for the <description> field, 185 * or the short description if the former is empty. 186 */ 187 @Override getLongDescription()188 public String getLongDescription() { 189 String s = getDescription(); 190 if (s == null || s.length() == 0) { 191 s = String.format("Extra %1$s package", getPath()); 192 } 193 194 if (s.indexOf("revision") == -1) { 195 s += String.format("\nRevision %1$d%2$s", 196 getRevision(), 197 isObsolete() ? " (Obsolete)" : ""); 198 } 199 200 if (getMinToolsRevision() != MIN_TOOLS_REV_NOT_SPECIFIED) { 201 s += String.format("\nRequires tools revision %1$d", getMinToolsRevision()); 202 } 203 204 if (getMinApiLevel() != MIN_API_LEVEL_NOT_SPECIFIED) { 205 s += String.format("\nRequires SDK Platform Android API %1$s", getMinApiLevel()); 206 } 207 208 return s; 209 } 210 211 /** 212 * Computes a potential installation folder if an archive of this package were 213 * to be installed right away in the given SDK root. 214 * <p/> 215 * A "tool" package should always be located in SDK/tools. 216 * 217 * @param osSdkRoot The OS path of the SDK root folder. 218 * @param suggestedDir A suggestion for the installation folder name, based on the root 219 * folder used in the zip archive. 220 * @param sdkManager An existing SDK manager to list current platforms and addons. 221 * @return A new {@link File} corresponding to the directory to use to install this package. 222 */ 223 @Override getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager)224 public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) { 225 return new File(osSdkRoot, getPath()); 226 } 227 228 @Override sameItemAs(Package pkg)229 public boolean sameItemAs(Package pkg) { 230 // Extra packages are similar if they have the same path. 231 return pkg instanceof ExtraPackage && ((ExtraPackage)pkg).mPath.equals(mPath); 232 } 233 } 234