1 /* 2 * Copyright (C) 2011 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.annotations.VisibleForTesting; 20 import com.android.annotations.VisibleForTesting.Visibility; 21 import com.android.sdklib.AndroidVersion; 22 import com.android.sdklib.SdkConstants; 23 import com.android.sdklib.SdkManager; 24 import com.android.sdklib.AndroidVersion.AndroidVersionException; 25 import com.android.sdklib.internal.repository.Archive.Arch; 26 import com.android.sdklib.internal.repository.Archive.Os; 27 import com.android.sdklib.repository.SdkRepoConstants; 28 29 import org.w3c.dom.Node; 30 31 import java.io.File; 32 import java.util.Map; 33 import java.util.Properties; 34 35 /** 36 * Represents a source XML node in an SDK repository. 37 * <p/> 38 * Note that a source package has a version and thus implements {@link IPackageVersion}. 39 * However there is no mandatory dependency that limits installation so this does not 40 * implement {@link IPlatformDependency}. 41 */ 42 public class SourcePackage extends Package implements IPackageVersion { 43 44 /** The package version, for platform, add-on and doc packages. */ 45 private final AndroidVersion mVersion; 46 47 /** 48 * Creates a new source package from the attributes and elements of the given XML node. 49 * This constructor should throw an exception if the package cannot be created. 50 * 51 * @param source The {@link SdkSource} where this is loaded from. 52 * @param packageNode The XML element being parsed. 53 * @param nsUri The namespace URI of the originating XML document, to be able to deal with 54 * parameters that vary according to the originating XML schema. 55 * @param licenses The licenses loaded from the XML originating document. 56 */ 57 SourcePackage(SdkSource source, 58 Node packageNode, 59 String nsUri, 60 Map<String,String> licenses) { 61 super(source, packageNode, nsUri, licenses); 62 63 int apiLevel = XmlParserUtils.getXmlInt(packageNode, SdkRepoConstants.NODE_API_LEVEL, 0); 64 String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME); 65 if (codeName.length() == 0) { 66 codeName = null; 67 } 68 mVersion = new AndroidVersion(apiLevel, codeName); 69 } 70 71 @VisibleForTesting(visibility=Visibility.PRIVATE) 72 protected SourcePackage( 73 AndroidVersion platformVersion, 74 int revision, 75 Properties props, 76 String localOsPath) { 77 this(null /*source*/, platformVersion, revision, props, localOsPath); 78 } 79 80 @VisibleForTesting(visibility=Visibility.PRIVATE) 81 protected SourcePackage( 82 SdkSource source, 83 AndroidVersion platformVersion, 84 int revision, 85 Properties props, 86 String localOsPath) { 87 super( source, //source 88 props, //properties 89 revision, //revision 90 null, //license 91 null, //description 92 null, //descUrl 93 Os.getCurrentOs(), //archiveOs 94 Arch.getCurrentArch(), //archiveArch 95 localOsPath //archiveOsPath 96 ); 97 mVersion = platformVersion; 98 } 99 100 /** 101 * Creates either a valid {@link SourcePackage} or a {@link BrokenPackage}. 102 * <p/> 103 * If the source directory contains valid properties, this creates a new {@link SourcePackage} 104 * with the android version listed in the properties. 105 * Otherwise returns a new {@link BrokenPackage} with some explanation on what failed. 106 * 107 * @param srcDir The SDK/sources/android-N folder 108 * @param props The properties located in {@code srcDir} or null if not found. 109 * @return A new {@link SourcePackage} or a new {@link BrokenPackage}. 110 */ 111 public static Package create(File srcDir, Properties props) { 112 AndroidVersion version = null; 113 String error = null; 114 115 // Try to load the android version from the sources.props. 116 // If we don't find them, it would explain why this package is broken. 117 if (props == null) { 118 error = String.format("Missing file %1$s", SdkConstants.FN_SOURCE_PROP); 119 } else { 120 try { 121 version = new AndroidVersion(props); 122 // The constructor will extract the revision from the properties 123 // and it will not consider a missing revision as being fatal. 124 return new SourcePackage(version, 0 /*revision*/, props, srcDir.getAbsolutePath()); 125 } catch (AndroidVersionException e) { 126 error = String.format("Invalid file %1$s: %2$s", 127 SdkConstants.FN_SOURCE_PROP, 128 e.getMessage()); 129 } 130 } 131 132 if (version == null) { 133 try { 134 // Try to parse the first number out of the platform folder name. 135 // This is just a wild guess in case we can create a broken package using that info. 136 String platform = srcDir.getParentFile().getName(); 137 platform = platform.replaceAll("[^0-9]+", " ").trim(); //$NON-NLS-1$ //$NON-NLS-2$ 138 int pos = platform.indexOf(' '); 139 if (pos >= 0) { 140 platform = platform.substring(0, pos); 141 } 142 int apiLevel = Integer.parseInt(platform); 143 version = new AndroidVersion(apiLevel, null /*codename*/); 144 } catch (Exception ignore) { 145 } 146 } 147 148 StringBuilder sb = new StringBuilder("Broken Source Package"); 149 if (version != null) { 150 sb.append(String.format(", API %1$s", version.getApiString())); 151 } 152 153 String shortDesc = sb.toString(); 154 155 if (error != null) { 156 sb.append('\n').append(error); 157 } 158 159 String longDesc = sb.toString(); 160 161 return new BrokenPackage(props, shortDesc, longDesc, 162 IMinApiLevelDependency.MIN_API_LEVEL_NOT_SPECIFIED, 163 version==null ? IExactApiLevelDependency.API_LEVEL_INVALID : version.getApiLevel(), 164 srcDir.getAbsolutePath()); 165 } 166 167 /** 168 * Save the properties of the current packages in the given {@link Properties} object. 169 * These properties will later be given to a constructor that takes a {@link Properties} object. 170 */ 171 @Override 172 void saveProperties(Properties props) { 173 super.saveProperties(props); 174 mVersion.saveProperties(props); 175 } 176 177 /** 178 * Returns the android version of this package. 179 */ 180 public AndroidVersion getVersion() { 181 return mVersion; 182 } 183 184 /** 185 * Returns a string identifier to install this package from the command line. 186 * For sources, we use "source-N" where N is the API or the preview codename. 187 * <p/> 188 * {@inheritDoc} 189 */ 190 @Override 191 public String installId() { 192 return "source-" + mVersion.getApiString(); //$NON-NLS-1$ 193 } 194 195 /** 196 * Returns a description of this package that is suitable for a list display. 197 * <p/> 198 * {@inheritDoc} 199 */ 200 @Override 201 public String getListDescription() { 202 if (mVersion.isPreview()) { 203 return String.format("Sources for Android '%1$s' Preview SDK%2$s", 204 mVersion.getCodename(), 205 isObsolete() ? " (Obsolete)" : ""); 206 } else { 207 return String.format("Sources for Android SDK%2$s", 208 mVersion.getApiLevel(), 209 isObsolete() ? " (Obsolete)" : ""); 210 } 211 } 212 213 /** 214 * Returns a short description for an {@link IDescription}. 215 */ 216 @Override 217 public String getShortDescription() { 218 if (mVersion.isPreview()) { 219 return String.format("Sources for Android '%1$s' Preview SDK, revision %2$s%3$s", 220 mVersion.getCodename(), 221 getRevision(), 222 isObsolete() ? " (Obsolete)" : ""); 223 } else { 224 return String.format("Sources for Android SDK, API %1$d, revision %2$s%3$s", 225 mVersion.getApiLevel(), 226 getRevision(), 227 isObsolete() ? " (Obsolete)" : ""); 228 } 229 } 230 231 /** 232 * Returns a long description for an {@link IDescription}. 233 * 234 * The long description is whatever the XML contains for the {@code description} field, 235 * or the short description if the former is empty. 236 */ 237 @Override 238 public String getLongDescription() { 239 String s = getDescription(); 240 if (s == null || s.length() == 0) { 241 s = getShortDescription(); 242 } 243 244 if (s.indexOf("revision") == -1) { 245 s += String.format("\nRevision %1$d%2$s", 246 getRevision(), 247 isObsolete() ? " (Obsolete)" : ""); 248 } 249 250 return s; 251 } 252 253 /** 254 * Computes a potential installation folder if an archive of this package were 255 * to be installed right away in the given SDK root. 256 * <p/> 257 * A sources package is typically installed in SDK/sources/platform. 258 * 259 * @param osSdkRoot The OS path of the SDK root folder. 260 * @param sdkManager An existing SDK manager to list current platforms and addons. 261 * @return A new {@link File} corresponding to the directory to use to install this package. 262 */ 263 @Override 264 public File getInstallFolder(String osSdkRoot, SdkManager sdkManager) { 265 File folder = new File(osSdkRoot, SdkConstants.FD_PKG_SOURCES); 266 folder = new File(folder, "android-" + mVersion.getApiString()); //$NON-NLS-1$ 267 return folder; 268 } 269 270 @Override 271 public boolean sameItemAs(Package pkg) { 272 if (pkg instanceof SourcePackage) { 273 SourcePackage newPkg = (SourcePackage)pkg; 274 275 // check they are the same version. 276 return getVersion().equals(newPkg.getVersion()); 277 } 278 279 return false; 280 } 281 282 @Override 283 public int hashCode() { 284 final int prime = 31; 285 int result = super.hashCode(); 286 result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode()); 287 return result; 288 } 289 290 @Override 291 public boolean equals(Object obj) { 292 if (this == obj) { 293 return true; 294 } 295 if (!super.equals(obj)) { 296 return false; 297 } 298 if (!(obj instanceof SourcePackage)) { 299 return false; 300 } 301 SourcePackage other = (SourcePackage) obj; 302 if (mVersion == null) { 303 if (other.mVersion != null) { 304 return false; 305 } 306 } else if (!mVersion.equals(other.mVersion)) { 307 return false; 308 } 309 return true; 310 } 311 } 312