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 36 private static final String PROP_PATH = "Extra.Path"; //$NON-NLS-1$ 37 38 /** 39 * The install folder name. It must be a single-segment path. 40 * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used. 41 * This limitation cannot be written in the XML Schema and must be enforced here by using 42 * the method {@link #isPathValid()} *before* installing the package. 43 */ 44 private final String mPath; 45 46 /** 47 * Creates a new tool package from the attributes and elements of the given XML node. 48 * <p/> 49 * This constructor should throw an exception if the package cannot be created. 50 */ ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses)51 ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses) { 52 super(source, packageNode, licenses); 53 54 mPath = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_PATH); 55 } 56 57 /** 58 * Manually create a new package with one archive and the given attributes or properties. 59 * This is used to create packages from local directories in which case there must be 60 * one archive which URL is the actual target location. 61 * <p/> 62 * By design, this creates a package with one and only one archive. 63 */ ExtraPackage(RepoSource source, Properties props, String path, int revision, String license, String description, String descUrl, Os archiveOs, Arch archiveArch, String archiveOsPath)64 ExtraPackage(RepoSource source, 65 Properties props, 66 String path, 67 int revision, 68 String license, 69 String description, 70 String descUrl, 71 Os archiveOs, 72 Arch archiveArch, 73 String archiveOsPath) { 74 super(source, 75 props, 76 revision, 77 license, 78 description, 79 descUrl, 80 archiveOs, 81 archiveArch, 82 archiveOsPath); 83 84 // The path argument comes before whatever could be in the properties 85 mPath = path != null ? path : getProperty(props, PROP_PATH, path); 86 } 87 88 /** 89 * Save the properties of the current packages in the given {@link Properties} object. 90 * These properties will later be give the constructor that takes a {@link Properties} object. 91 */ 92 @Override saveProperties(Properties props)93 void saveProperties(Properties props) { 94 super.saveProperties(props); 95 96 props.setProperty(PROP_PATH, mPath); 97 98 if (getMinToolsRevision() != MIN_TOOLS_REV_NOT_SPECIFIED) { 99 props.setProperty(PROP_MIN_TOOLS_REV, Integer.toString(getMinToolsRevision())); 100 } 101 } 102 103 /** 104 * Static helper to check if a given path is acceptable for an "extra" package. 105 */ isPathValid()106 public boolean isPathValid() { 107 if (SdkConstants.FD_ADDONS.equals(mPath) || 108 SdkConstants.FD_PLATFORMS.equals(mPath) || 109 SdkConstants.FD_TOOLS.equals(mPath) || 110 SdkConstants.FD_DOCS.equals(mPath)) { 111 return false; 112 } 113 return mPath != null && mPath.indexOf('/') == -1 && mPath.indexOf('\\') == -1; 114 } 115 116 /** 117 * The install folder name. It must be a single-segment path. 118 * The paths "add-ons", "platforms", "tools" and "docs" are reserved and cannot be used. 119 * This limitation cannot be written in the XML Schema and must be enforced here by using 120 * the method {@link #isPathValid()} *before* installing the package. 121 */ getPath()122 public String getPath() { 123 return mPath; 124 } 125 126 /** Returns a short description for an {@link IDescription}. */ 127 @Override getShortDescription()128 public String getShortDescription() { 129 String name = getPath(); 130 if (name != null) { 131 // Uniformize all spaces in the name and upper case words. 132 133 name = name.replaceAll("[ _\t\f-]+", " "); //$NON-NLS-1$ //$NON-NLS-2$ 134 135 // Look at all lower case characters in range [1..n-1] and replace them by an upper 136 // case if they are preceded by a space. Also upper cases the first character of the 137 // string. 138 boolean changed = false; 139 char[] chars = name.toCharArray(); 140 for (int n = chars.length - 1, i = 0; i < n; i++) { 141 if (Character.isLowerCase(chars[i]) && (i == 0 || chars[i - 1] == ' ')) { 142 chars[i] = Character.toUpperCase(chars[i]); 143 changed = true; 144 } 145 } 146 if (changed) { 147 name = new String(chars); 148 } 149 } 150 151 String s = String.format("%1$s package, revision %2$d", 152 name, 153 getRevision()); 154 155 return s; 156 } 157 158 /** 159 * Returns a long description for an {@link IDescription}. 160 * 161 * The long description is whatever the XML contains for the <description> field, 162 * or the short description if the former is empty. 163 */ 164 @Override getLongDescription()165 public String getLongDescription() { 166 String s = getDescription(); 167 if (s == null || s.length() == 0) { 168 s = String.format("Extra %1$s package", getPath()); 169 } 170 171 if (s.indexOf("revision") == -1) { 172 s += String.format("\nRevision %1$d", getRevision()); 173 } 174 175 if (getMinToolsRevision() != MIN_TOOLS_REV_NOT_SPECIFIED) { 176 s += String.format("\nRequires tools revision %1$d", getMinToolsRevision()); 177 } 178 179 return s; 180 } 181 182 /** 183 * Computes a potential installation folder if an archive of this package were 184 * to be installed right away in the given SDK root. 185 * <p/> 186 * A "tool" package should always be located in SDK/tools. 187 * 188 * @param osSdkRoot The OS path of the SDK root folder. 189 * @param suggestedDir A suggestion for the installation folder name, based on the root 190 * folder used in the zip archive. 191 * @param sdkManager An existing SDK manager to list current platforms and addons. 192 * @return A new {@link File} corresponding to the directory to use to install this package. 193 */ 194 @Override getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager)195 public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) { 196 return new File(osSdkRoot, getPath()); 197 } 198 199 @Override sameItemAs(Package pkg)200 public boolean sameItemAs(Package pkg) { 201 // Extra packages are similar if they have the same path. 202 return pkg instanceof ExtraPackage && ((ExtraPackage)pkg).mPath.equals(mPath); 203 } 204 } 205