1 /* 2 * Copyright (C) 2008 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; 18 19 import java.io.File; 20 import java.io.FileFilter; 21 import java.util.Arrays; 22 import java.util.HashSet; 23 import java.util.Map; 24 import java.util.Map.Entry; 25 26 /** 27 * Represents an add-on target in the SDK. 28 * An add-on extends a standard {@link PlatformTarget}. 29 */ 30 final class AddOnTarget implements IAndroidTarget { 31 /** 32 * String to compute hash for add-on targets. 33 * Format is vendor:name:apiVersion 34 * */ 35 private final static String ADD_ON_FORMAT = "%s:%s:%s"; //$NON-NLS-1$ 36 37 private final static class OptionalLibrary implements IOptionalLibrary { 38 private final String mJarName; 39 private final String mJarPath; 40 private final String mName; 41 private final String mDescription; 42 43 OptionalLibrary(String jarName, String jarPath, String name, String description) { 44 mJarName = jarName; 45 mJarPath = jarPath; 46 mName = name; 47 mDescription = description; 48 } 49 50 @Override 51 public String getJarName() { 52 return mJarName; 53 } 54 55 @Override 56 public String getJarPath() { 57 return mJarPath; 58 } 59 60 @Override 61 public String getName() { 62 return mName; 63 } 64 65 @Override 66 public String getDescription() { 67 return mDescription; 68 } 69 } 70 71 private final String mLocation; 72 private final PlatformTarget mBasePlatform; 73 private final String mName; 74 private final ISystemImage[] mSystemImages; 75 private final String mVendor; 76 private final int mRevision; 77 private final String mDescription; 78 private final boolean mHasRenderingLibrary; 79 private final boolean mHasRenderingResources; 80 81 private String[] mSkins; 82 private String mDefaultSkin; 83 private IOptionalLibrary[] mLibraries; 84 private int mVendorId = NO_USB_ID; 85 86 /** 87 * Creates a new add-on 88 * @param location the OS path location of the add-on 89 * @param name the name of the add-on 90 * @param vendor the vendor name of the add-on 91 * @param revision the revision of the add-on 92 * @param description the add-on description 93 * @param systemImages list of supported system images. Can be null or empty. 94 * @param libMap A map containing the optional libraries. The map key is the fully-qualified 95 * library name. The value is a 2 string array with the .jar filename, and the description. 96 * @param hasRenderingLibrary whether the addon has a custom layoutlib.jar 97 * @param hasRenderingResources whether the add has custom framework resources. 98 * @param basePlatform the platform the add-on is extending. 99 */ 100 AddOnTarget( 101 String location, 102 String name, 103 String vendor, 104 int revision, 105 String description, 106 ISystemImage[] systemImages, 107 Map<String, String[]> libMap, 108 boolean hasRenderingLibrary, 109 boolean hasRenderingResources, 110 PlatformTarget basePlatform) { 111 if (location.endsWith(File.separator) == false) { 112 location = location + File.separator; 113 } 114 115 mLocation = location; 116 mName = name; 117 mVendor = vendor; 118 mRevision = revision; 119 mDescription = description; 120 mHasRenderingLibrary = hasRenderingLibrary; 121 mHasRenderingResources = hasRenderingResources; 122 mBasePlatform = basePlatform; 123 124 // If the add-on does not have any system-image of its own, the list here 125 // is empty and it's up to the callers to query the parent platform. 126 mSystemImages = systemImages == null ? new ISystemImage[0] : systemImages; 127 Arrays.sort(mSystemImages); 128 129 // handle the optional libraries. 130 if (libMap != null) { 131 mLibraries = new IOptionalLibrary[libMap.size()]; 132 int index = 0; 133 for (Entry<String, String[]> entry : libMap.entrySet()) { 134 String jarFile = entry.getValue()[0]; 135 String desc = entry.getValue()[1]; 136 mLibraries[index++] = new OptionalLibrary(jarFile, 137 mLocation + SdkConstants.OS_ADDON_LIBS_FOLDER + jarFile, 138 entry.getKey(), desc); 139 } 140 } 141 } 142 143 @Override 144 public String getLocation() { 145 return mLocation; 146 } 147 148 @Override 149 public String getName() { 150 return mName; 151 } 152 153 @Override 154 public ISystemImage getSystemImage(String abiType) { 155 for (ISystemImage sysImg : mSystemImages) { 156 if (sysImg.getAbiType().equals(abiType)) { 157 return sysImg; 158 } 159 } 160 return null; 161 } 162 163 @Override 164 public ISystemImage[] getSystemImages() { 165 return mSystemImages; 166 } 167 168 @Override 169 public String getVendor() { 170 return mVendor; 171 } 172 173 @Override 174 public String getFullName() { 175 return String.format("%1$s (%2$s)", mName, mVendor); 176 } 177 178 @Override 179 public String getClasspathName() { 180 return String.format("%1$s [%2$s]", mName, mBasePlatform.getClasspathName()); 181 } 182 183 @Override 184 public String getShortClasspathName() { 185 return String.format("%1$s [%2$s]", mName, mBasePlatform.getVersionName()); 186 } 187 188 @Override 189 public String getDescription() { 190 return mDescription; 191 } 192 193 @Override 194 public AndroidVersion getVersion() { 195 // this is always defined by the base platform 196 return mBasePlatform.getVersion(); 197 } 198 199 @Override 200 public String getVersionName() { 201 return mBasePlatform.getVersionName(); 202 } 203 204 @Override 205 public int getRevision() { 206 return mRevision; 207 } 208 209 @Override 210 public boolean isPlatform() { 211 return false; 212 } 213 214 @Override 215 public IAndroidTarget getParent() { 216 return mBasePlatform; 217 } 218 219 @Override 220 public String getPath(int pathId) { 221 switch (pathId) { 222 case SKINS: 223 return mLocation + SdkConstants.OS_SKINS_FOLDER; 224 case DOCS: 225 return mLocation + SdkConstants.FD_DOCS + File.separator 226 + SdkConstants.FD_DOCS_REFERENCE; 227 228 case LAYOUT_LIB: 229 if (mHasRenderingLibrary) { 230 return mLocation + SdkConstants.FD_DATA + File.separator 231 + SdkConstants.FN_LAYOUTLIB_JAR; 232 } 233 return mBasePlatform.getPath(pathId); 234 235 case RESOURCES: 236 if (mHasRenderingResources) { 237 return mLocation + SdkConstants.FD_DATA + File.separator 238 + SdkConstants.FD_RES; 239 } 240 return mBasePlatform.getPath(pathId); 241 242 case FONTS: 243 if (mHasRenderingResources) { 244 return mLocation + SdkConstants.FD_DATA + File.separator 245 + SdkConstants.FD_FONTS; 246 } 247 return mBasePlatform.getPath(pathId); 248 249 case SAMPLES: 250 // only return the add-on samples folder if there is actually a sample (or more) 251 File sampleLoc = new File(mLocation, SdkConstants.FD_SAMPLES); 252 if (sampleLoc.isDirectory()) { 253 File[] files = sampleLoc.listFiles(new FileFilter() { 254 @Override 255 public boolean accept(File pathname) { 256 return pathname.isDirectory(); 257 } 258 259 }); 260 if (files != null && files.length > 0) { 261 return sampleLoc.getAbsolutePath(); 262 } 263 } 264 //$FALL-THROUGH$ 265 default : 266 return mBasePlatform.getPath(pathId); 267 } 268 } 269 270 @Override 271 public boolean hasRenderingLibrary() { 272 return mHasRenderingLibrary || mHasRenderingResources; 273 } 274 275 @Override 276 public String[] getSkins() { 277 return mSkins; 278 } 279 280 @Override 281 public String getDefaultSkin() { 282 return mDefaultSkin; 283 } 284 285 @Override 286 public IOptionalLibrary[] getOptionalLibraries() { 287 return mLibraries; 288 } 289 290 /** 291 * Returns the list of libraries of the underlying platform. 292 * 293 * {@inheritDoc} 294 */ 295 @Override 296 public String[] getPlatformLibraries() { 297 return mBasePlatform.getPlatformLibraries(); 298 } 299 300 @Override 301 public String getProperty(String name) { 302 return mBasePlatform.getProperty(name); 303 } 304 305 @Override 306 public Integer getProperty(String name, Integer defaultValue) { 307 return mBasePlatform.getProperty(name, defaultValue); 308 } 309 310 @Override 311 public Boolean getProperty(String name, Boolean defaultValue) { 312 return mBasePlatform.getProperty(name, defaultValue); 313 } 314 315 @Override 316 public Map<String, String> getProperties() { 317 return mBasePlatform.getProperties(); 318 } 319 320 @Override 321 public int getUsbVendorId() { 322 return mVendorId; 323 } 324 325 @Override 326 public boolean canRunOn(IAndroidTarget target) { 327 // basic test 328 if (target == this) { 329 return true; 330 } 331 332 /* 333 * The method javadoc indicates: 334 * Returns whether the given target is compatible with the receiver. 335 * <p/>A target is considered compatible if applications developed for the receiver can 336 * run on the given target. 337 */ 338 339 // The receiver is an add-on. There are 2 big use cases: The add-on has libraries 340 // or the add-on doesn't (in which case we consider it a platform). 341 if (mLibraries == null || mLibraries.length == 0) { 342 return mBasePlatform.canRunOn(target); 343 } else { 344 // the only targets that can run the receiver are the same add-on in the same or later 345 // versions. 346 // first check: vendor/name 347 if (mVendor.equals(target.getVendor()) == false || 348 mName.equals(target.getName()) == false) { 349 return false; 350 } 351 352 // now check the version. At this point since we checked the add-on part, 353 // we can revert to the basic check on version/codename which are done by the 354 // base platform already. 355 return mBasePlatform.canRunOn(target); 356 } 357 358 } 359 360 @Override 361 public String hashString() { 362 return String.format(ADD_ON_FORMAT, mVendor, mName, 363 mBasePlatform.getVersion().getApiString()); 364 } 365 366 @Override 367 public int hashCode() { 368 return hashString().hashCode(); 369 } 370 371 @Override 372 public boolean equals(Object obj) { 373 if (obj instanceof AddOnTarget) { 374 AddOnTarget addon = (AddOnTarget)obj; 375 376 return mVendor.equals(addon.mVendor) && mName.equals(addon.mName) && 377 mBasePlatform.getVersion().equals(addon.mBasePlatform.getVersion()); 378 } 379 380 return false; 381 } 382 383 /* 384 * Order by API level (preview/n count as between n and n+1). 385 * At the same API level, order as: Platform first, then add-on ordered by vendor and then name 386 * (non-Javadoc) 387 * @see java.lang.Comparable#compareTo(java.lang.Object) 388 */ 389 @Override 390 public int compareTo(IAndroidTarget target) { 391 // quick check. 392 if (this == target) { 393 return 0; 394 } 395 396 int versionDiff = getVersion().compareTo(target.getVersion()); 397 398 // only if the version are the same do we care about platform/add-ons. 399 if (versionDiff == 0) { 400 // platforms go before add-ons. 401 if (target.isPlatform()) { 402 return +1; 403 } else { 404 AddOnTarget targetAddOn = (AddOnTarget)target; 405 406 // both are add-ons of the same version. Compare per vendor then by name 407 int vendorDiff = mVendor.compareTo(targetAddOn.mVendor); 408 if (vendorDiff == 0) { 409 return mName.compareTo(targetAddOn.mName); 410 } else { 411 return vendorDiff; 412 } 413 } 414 415 } 416 417 return versionDiff; 418 } 419 420 /** 421 * Returns a string representation suitable for debugging. 422 * The representation is not intended for display to the user. 423 * 424 * The representation is also purposely compact. It does not describe _all_ the properties 425 * of the target, only a few key ones. 426 * 427 * @see #getDescription() 428 */ 429 @Override 430 public String toString() { 431 return String.format("AddonTarget %1$s rev %2$d (based on %3$s)", //$NON-NLS-1$ 432 getVersion(), 433 getRevision(), 434 getParent().toString()); 435 } 436 437 // ---- local methods. 438 439 void setSkins(String[] skins, String defaultSkin) { 440 mDefaultSkin = defaultSkin; 441 442 // we mix the add-on and base platform skins 443 HashSet<String> skinSet = new HashSet<String>(); 444 skinSet.addAll(Arrays.asList(skins)); 445 skinSet.addAll(Arrays.asList(mBasePlatform.getSkins())); 446 447 mSkins = skinSet.toArray(new String[skinSet.size()]); 448 } 449 450 /** 451 * Sets the USB vendor id in the add-on. 452 */ 453 void setUsbVendorId(int vendorId) { 454 if (vendorId == 0) { 455 throw new IllegalArgumentException( "VendorId must be > 0"); 456 } 457 458 mVendorId = vendorId; 459 } 460 } 461