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.AndroidVersion; 20 import com.android.sdklib.SdkConstants; 21 import com.android.sdklib.SdkManager; 22 import com.android.sdklib.internal.repository.Archive.Arch; 23 import com.android.sdklib.internal.repository.Archive.Os; 24 import com.android.sdklib.repository.SdkRepoConstants; 25 26 import org.w3c.dom.Node; 27 28 import java.io.File; 29 import java.util.Map; 30 import java.util.Properties; 31 32 /** 33 * Represents a doc XML node in an SDK repository. 34 * <p/> 35 * Note that a doc package has a version and thus implements {@link IPackageVersion}. 36 * However there is no mandatory dependency that limits installation so this does not 37 * implement {@link IPlatformDependency}. 38 */ 39 public class DocPackage extends Package implements IPackageVersion { 40 41 private final AndroidVersion mVersion; 42 43 /** 44 * Creates a new doc package from the attributes and elements of the given XML node. 45 * This constructor should throw an exception if the package cannot be created. 46 * 47 * @param source The {@link SdkSource} where this is loaded from. 48 * @param packageNode The XML element being parsed. 49 * @param nsUri The namespace URI of the originating XML document, to be able to deal with 50 * parameters that vary according to the originating XML schema. 51 * @param licenses The licenses loaded from the XML originating document. 52 */ 53 DocPackage(SdkSource source, Node packageNode, String nsUri, Map<String,String> licenses) { 54 super(source, packageNode, nsUri, licenses); 55 56 int apiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0); 57 String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME); 58 if (codeName.length() == 0) { 59 codeName = null; 60 } 61 mVersion = new AndroidVersion(apiLevel, codeName); 62 } 63 64 /** 65 * Manually create a new package with one archive and the given attributes. 66 * This is used to create packages from local directories in which case there must be 67 * one archive which URL is the actual target location. 68 * <p/> 69 * By design, this creates a package with one and only one archive. 70 */ 71 static Package create(SdkSource source, 72 Properties props, 73 int apiLevel, 74 String codename, 75 int revision, 76 String license, 77 String description, 78 String descUrl, 79 Os archiveOs, 80 Arch archiveArch, 81 String archiveOsPath) { 82 return new DocPackage(source, props, apiLevel, codename, revision, license, description, 83 descUrl, archiveOs, archiveArch, archiveOsPath); 84 } 85 86 private DocPackage(SdkSource source, 87 Properties props, 88 int apiLevel, 89 String codename, 90 int revision, 91 String license, 92 String description, 93 String descUrl, 94 Os archiveOs, 95 Arch archiveArch, 96 String archiveOsPath) { 97 super(source, 98 props, 99 revision, 100 license, 101 description, 102 descUrl, 103 archiveOs, 104 archiveArch, 105 archiveOsPath); 106 mVersion = new AndroidVersion(props, apiLevel, codename); 107 } 108 109 /** 110 * Save the properties of the current packages in the given {@link Properties} object. 111 * These properties will later be give the constructor that takes a {@link Properties} object. 112 */ 113 @Override 114 void saveProperties(Properties props) { 115 super.saveProperties(props); 116 117 mVersion.saveProperties(props); 118 } 119 120 /** 121 * Returns the version, for platform, add-on and doc packages. 122 * Can be 0 if this is a local package of unknown api-level. 123 */ 124 public AndroidVersion getVersion() { 125 return mVersion; 126 } 127 128 /** 129 * Returns a string identifier to install this package from the command line. 130 * For docs, we use "doc-N" where N is the API or the preview codename. 131 * <p/> 132 * {@inheritDoc} 133 */ 134 @Override 135 public String installId() { 136 return "doc-" + mVersion.getApiString(); //$NON-NLS-1$ 137 } 138 139 /** 140 * Returns a description of this package that is suitable for a list display. 141 * <p/> 142 * {@inheritDoc} 143 */ 144 @Override 145 public String getListDescription() { 146 if (mVersion.isPreview()) { 147 return String.format("Documentation for Android '%1$s' Preview SDK%2$s", 148 mVersion.getCodename(), 149 isObsolete() ? " (Obsolete)" : ""); 150 } else { 151 return String.format("Documentation for Android SDK%2$s", 152 mVersion.getApiLevel(), 153 isObsolete() ? " (Obsolete)" : ""); 154 } 155 } 156 157 /** 158 * Returns a short description for an {@link IDescription}. 159 */ 160 @Override 161 public String getShortDescription() { 162 if (mVersion.isPreview()) { 163 return String.format("Documentation for Android '%1$s' Preview SDK, revision %2$s%3$s", 164 mVersion.getCodename(), 165 getRevision(), 166 isObsolete() ? " (Obsolete)" : ""); 167 } else { 168 return String.format("Documentation for Android SDK, API %1$d, revision %2$s%3$s", 169 mVersion.getApiLevel(), 170 getRevision(), 171 isObsolete() ? " (Obsolete)" : ""); 172 } 173 } 174 175 /** 176 * Returns a long description for an {@link IDescription}. 177 * 178 * The long description is whatever the XML contains for the <description> field, 179 * or the short description if the former is empty. 180 */ 181 @Override 182 public String getLongDescription() { 183 String s = getDescription(); 184 if (s == null || s.length() == 0) { 185 s = getShortDescription(); 186 } 187 188 if (s.indexOf("revision") == -1) { 189 s += String.format("\nRevision %1$d%2$s", 190 getRevision(), 191 isObsolete() ? " (Obsolete)" : ""); 192 } 193 194 return s; 195 } 196 197 /** 198 * Computes a potential installation folder if an archive of this package were 199 * to be installed right away in the given SDK root. 200 * <p/> 201 * A "doc" package should always be located in SDK/docs. 202 * 203 * @param osSdkRoot The OS path of the SDK root folder. 204 * @param sdkManager An existing SDK manager to list current platforms and addons. 205 * @return A new {@link File} corresponding to the directory to use to install this package. 206 */ 207 @Override 208 public File getInstallFolder(String osSdkRoot, SdkManager sdkManager) { 209 return new File(osSdkRoot, SdkConstants.FD_DOCS); 210 } 211 212 @Override 213 public boolean sameItemAs(Package pkg) { 214 // only one doc package so any doc package is the same item 215 // and we explicitly don't check whether the version is the same. 216 return pkg instanceof DocPackage; 217 } 218 219 /** 220 * {@inheritDoc} 221 * 222 * The comparison between doc packages is a bit more complex so we override the default 223 * implementation. 224 * <p/> 225 * Docs are upgrade if they have a higher api, or a similar api but a higher revision. 226 * <p/> 227 * What makes this more complex is handling codename. 228 */ 229 @Override 230 public UpdateInfo canBeUpdatedBy(Package replacementPackage) { 231 if (replacementPackage == null) { 232 return UpdateInfo.INCOMPATIBLE; 233 } 234 235 // check they are the same item. 236 if (sameItemAs(replacementPackage) == false) { 237 return UpdateInfo.INCOMPATIBLE; 238 } 239 240 DocPackage replacementDoc = (DocPackage)replacementPackage; 241 242 AndroidVersion replacementVersion = replacementDoc.getVersion(); 243 244 // the new doc is an update if the api level is higher (no matter the codename on either) 245 if (replacementVersion.getApiLevel() > mVersion.getApiLevel()) { 246 return UpdateInfo.UPDATE; 247 } 248 249 // Check if they're the same exact (api and codename) 250 if (replacementVersion.equals(mVersion)) { 251 // exact same version, so check the revision level 252 if (replacementPackage.getRevision() > this.getRevision()) { 253 return UpdateInfo.UPDATE; 254 } 255 } else { 256 // not the same version? we check if they have the same api level and the new one 257 // is a preview, in which case it's also an update (since preview have the api level 258 // of the _previous_ version.) 259 if (replacementVersion.getApiLevel() == mVersion.getApiLevel() && 260 replacementVersion.isPreview()) { 261 return UpdateInfo.UPDATE; 262 } 263 } 264 265 // not an upgrade but not incompatible either. 266 return UpdateInfo.NOT_UPDATE; 267 } 268 269 @Override 270 public int hashCode() { 271 final int prime = 31; 272 int result = super.hashCode(); 273 result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode()); 274 return result; 275 } 276 277 @Override 278 public boolean equals(Object obj) { 279 if (this == obj) { 280 return true; 281 } 282 if (!super.equals(obj)) { 283 return false; 284 } 285 if (!(obj instanceof DocPackage)) { 286 return false; 287 } 288 DocPackage other = (DocPackage) obj; 289 if (mVersion == null) { 290 if (other.mVersion != null) { 291 return false; 292 } 293 } else if (!mVersion.equals(other.mVersion)) { 294 return false; 295 } 296 return true; 297 } 298 } 299