/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.sdklib.repository.descriptors; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.annotations.VisibleForTesting; import com.android.annotations.VisibleForTesting.Visibility; import com.android.sdklib.AndroidTargetHash; import com.android.sdklib.AndroidVersion; import com.android.sdklib.SystemImage; import com.android.sdklib.io.FileOp; import com.android.sdklib.repository.FullRevision; import com.android.sdklib.repository.FullRevision.PreviewComparison; import com.android.sdklib.repository.License; import com.android.sdklib.repository.MajorRevision; import com.android.sdklib.repository.NoPreviewRevision; import com.android.sdklib.repository.PreciseRevision; import java.io.File; import java.util.Locale; /** * {@link PkgDesc} keeps information on individual SDK packages * (both local or remote packages definitions.) * <br/> * Packages have different attributes depending on their type. * <p/> * To create a new {@link PkgDesc}, use one of the package-specific constructors * provided here. * <p/> * To query packages capabilities, rely on {@link #getType()} and the {@code PkgDesc.hasXxx()} * methods provided in the base {@link PkgDesc}. */ public class PkgDesc implements IPkgDesc { public static final String PREVIEW_SUFFIX = "-preview"; private final PkgType mType; private final FullRevision mFullRevision; private final MajorRevision mMajorRevision; private final AndroidVersion mAndroidVersion; private final String mPath; private final IdDisplay mTag; private final IdDisplay mVendor; private final FullRevision mMinToolsRev; private final FullRevision mMinPlatformToolsRev; private final IIsUpdateFor mCustomIsUpdateFor; private final IGetPath mCustomPath; private final License mLicense; private final String mListDisplay; private final String mDescriptionShort; private final String mDescriptionUrl; private final boolean mIsObsolete; protected PkgDesc(@NonNull PkgType type, @Nullable License license, @Nullable String listDisplay, @Nullable String descriptionShort, @Nullable String descriptionUrl, boolean isObsolete, @Nullable FullRevision fullRevision, @Nullable MajorRevision majorRevision, @Nullable AndroidVersion androidVersion, @Nullable String path, @Nullable IdDisplay tag, @Nullable IdDisplay vendor, @Nullable FullRevision minToolsRev, @Nullable FullRevision minPlatformToolsRev, @Nullable IIsUpdateFor customIsUpdateFor, @Nullable IGetPath customPath) { mType = type; mIsObsolete = isObsolete; mLicense = license; mListDisplay = listDisplay; mDescriptionShort = descriptionShort; mDescriptionUrl = descriptionUrl; mFullRevision = fullRevision; mMajorRevision = majorRevision; mAndroidVersion = androidVersion; mPath = path; mTag = tag; mVendor = vendor; mMinToolsRev = minToolsRev; mMinPlatformToolsRev = minPlatformToolsRev; mCustomIsUpdateFor = customIsUpdateFor; mCustomPath = customPath; } @NonNull @Override public PkgType getType() { return mType; } @Override @Nullable public String getListDisplay() { return mListDisplay; } @Override @Nullable public String getDescriptionShort() { return mDescriptionShort; } @Override @Nullable public String getDescriptionUrl() { return mDescriptionUrl; } @Override @Nullable public License getLicense() { return mLicense; } @Override public boolean isObsolete() { return mIsObsolete; } @Override public final boolean hasFullRevision() { return getType().hasFullRevision(); } @Override public final boolean hasMajorRevision() { return getType().hasMajorRevision(); } @Override public final boolean hasAndroidVersion() { return getType().hasAndroidVersion(); } @Override public final boolean hasPath() { return getType().hasPath(); } @Override public final boolean hasTag() { return getType().hasTag(); } @Override public boolean hasVendor() { return getType().hasVendor(); } @Override public final boolean hasMinToolsRev() { return getType().hasMinToolsRev(); } @Override public final boolean hasMinPlatformToolsRev() { return getType().hasMinPlatformToolsRev(); } @Nullable @Override public FullRevision getFullRevision() { return mFullRevision; } @Nullable @Override public MajorRevision getMajorRevision() { return mMajorRevision; } @NonNull @Override public final PreciseRevision getPreciseRevision() { if (mMajorRevision == null) { return new PreciseRevision(mFullRevision.getMajor(), mFullRevision.getMinor(), mFullRevision.getMicro(), mFullRevision.getPreview()); } return new PreciseRevision(mMajorRevision.getMajor()); } @Nullable @Override public AndroidVersion getAndroidVersion() { return mAndroidVersion; } @Override public boolean isPreview() { return getPreciseRevision().isPreview(); } @Nullable @Override public String getPath() { if (mCustomPath != null) { return mCustomPath.getPath(this); } else { return mPath; } } @Nullable @Override public IdDisplay getTag() { return mTag; } @Nullable @Override public IdDisplay getVendor() { return mVendor; } @Nullable @Override public FullRevision getMinToolsRev() { return mMinToolsRev; } @Nullable @Override public FullRevision getMinPlatformToolsRev() { return mMinPlatformToolsRev; } @Override public String getInstallId() { String id = getBaseInstallId(); if (getPreciseRevision().isPreview()) { return id + PREVIEW_SUFFIX; } return id; } @Override public String getBaseInstallId() { StringBuilder sb = new StringBuilder(); /* iid patterns: tools, platform-tools => FOLDER build-tools => FOLDER-REV doc, sample, source => ENUM-API extra => ENUM-VENDOR.id-PATH platform => android-API add-on => addon-NAME.id-VENDOR.id-API platform sys-img => sys-img-ABI-TAG|android-API add-on sys-img => sys-img-ABI-addon-NAME.id-VENDOR.id-API */ switch (mType) { case PKG_TOOLS: case PKG_PLATFORM_TOOLS: sb.append(mType.getFolderName()); break; case PKG_BUILD_TOOLS: sb.append(mType.getFolderName()).append('-'); // Add version number without the preview revision number. This is to make preview // packages be updatable to the next revision. int[] version = getPreciseRevision().toIntArray(false); for (int i = 0; i < version.length; i++) { sb.append(version[i]); if (i != version.length - 1) { sb.append('.'); } } break; case PKG_DOC: sb.append("doc"); break; case PKG_SAMPLE: case PKG_SOURCE: sb.append(mType.toString().toLowerCase(Locale.US).replace("pkg_", "")); sb.append('-').append(getAndroidVersion().getApiString()); break; case PKG_EXTRA: sb.append("extra-") .append(getVendor().getId()) .append('-') .append(getPath()); break; case PKG_PLATFORM: sb.append(AndroidTargetHash.PLATFORM_HASH_PREFIX) .append(getAndroidVersion().getApiString()); break; case PKG_ADDON: sb.append("addon-") .append(((IPkgDescAddon)this).getName().getId()) .append('-') .append(getVendor().getId()) .append('-') .append(getAndroidVersion().getApiString()); break; case PKG_SYS_IMAGE: sb.append("sys-img-") .append(getPath()) // path==ABI for sys-img .append('-') .append(SystemImage.DEFAULT_TAG.equals(getTag()) ? "android" : getTag().getId()) .append('-') .append(getAndroidVersion().getApiString()); break; case PKG_ADDON_SYS_IMAGE: sb.append("sys-img-") .append(getPath()) // path==ABI for sys-img .append("-addon-") .append(SystemImage.DEFAULT_TAG.equals(getTag()) ? "android" : getTag().getId()) .append('-') .append(getVendor().getId()) .append('-') .append(getAndroidVersion().getApiString()); break; case PKG_NDK: sb.append("ndk"); break; default: throw new IllegalArgumentException("IID not defined for type " + mType.toString()); } return sanitize(sb.toString()); } @Override public File getCanonicalInstallFolder(@NonNull File sdkLocation) { File f = FileOp.append(sdkLocation, mType.getFolderName()); /* folder patterns: tools, platform-tools, doc => FOLDER build-tools, add-on => FOLDER/IID platform, sample, source => FOLDER/android-API platform sys-img => FOLDER/android-API/TAG/ABI add-on sys-img => FOLDER/addon-NAME.id-VENDOR.id-API/ABI extra => FOLDER/VENDOR.id/PATH */ switch (mType) { case PKG_TOOLS: case PKG_PLATFORM_TOOLS: case PKG_DOC: // no-op, top-folder is all what is needed here break; case PKG_BUILD_TOOLS: case PKG_ADDON: f = FileOp.append(f, getInstallId()); break; case PKG_PLATFORM: case PKG_SAMPLE: case PKG_SOURCE: f = FileOp.append(f, AndroidTargetHash.PLATFORM_HASH_PREFIX + sanitize( getAndroidVersion().getApiString())); break; case PKG_SYS_IMAGE: f = FileOp.append(f, AndroidTargetHash.PLATFORM_HASH_PREFIX + sanitize( getAndroidVersion().getApiString()), sanitize(SystemImage.DEFAULT_TAG.equals(getTag()) ? "android" : getTag().getId()), sanitize(getPath())); // path==abi break; case PKG_ADDON_SYS_IMAGE: String name = "addon-" + (SystemImage.DEFAULT_TAG.equals(getTag()) ? "android" : getTag().getId()) + '-' + getVendor().getId() + '-' + getAndroidVersion().getApiString(); f = FileOp.append(f, sanitize(name), sanitize(getPath())); // path==abi break; case PKG_EXTRA: f = FileOp.append(f, sanitize(getVendor().getId()), sanitize(getPath())); break; default: throw new IllegalArgumentException( "CanonicalFolder not defined for type " + mType.toString()); } return f; } //---- Updating ---- /** * Computes the most general case of {@link #isUpdateFor(IPkgDesc)}. * Individual package types use this and complement with their own specific cases * as needed. * * @param existingDesc A non-null package descriptor to compare with. * @return True if this package descriptor would generally update the given one. */ @Override public boolean isUpdateFor(@NonNull IPkgDesc existingDesc) { return isUpdateFor(existingDesc, PreviewComparison.COMPARE_NUMBER); } @Override public boolean isUpdateFor(@NonNull IPkgDesc existingDesc, @NonNull PreviewComparison previewComparison) { if (mCustomIsUpdateFor != null) { return mCustomIsUpdateFor.isUpdateFor(this, existingDesc); } else { return isGenericUpdateFor(existingDesc, previewComparison); } } /** * Computes the most general case of {@link #isUpdateFor(IPkgDesc)}. * Individual package types use this and complement with their own specific cases * as needed. * * @param existingDesc A non-null package descriptor to compare with. * @param previewComparison The type of preview comparison to do. * @return True if this package descriptor would generally update the given one. */ private boolean isGenericUpdateFor(@NonNull IPkgDesc existingDesc, PreviewComparison previewComparison) { if (existingDesc == null || !getType().equals(existingDesc.getType())) { return false; } // Packages that have an Android version can generally only be updated // for the same Android version (otherwise it's a new artifact.) if (hasAndroidVersion() && !getAndroidVersion().equals(existingDesc.getAndroidVersion())) { return false; } // Packages that have a vendor id need the same vendor id on both sides if (hasVendor() && !getVendor().equals(existingDesc.getVendor())) { return false; } // Packages that have a tag id need the same tag id on both sides if (hasTag() && !getTag().getId().equals(existingDesc.getTag().getId())) { return false; } // Packages that have a path can generally only be updated if both use the same path if (hasPath()) { if (this instanceof IPkgDescExtra) { // Extra package handle paths differently, they need to use the old_path // to allow upgrade compatibility. if (!PkgDescExtra.compatibleVendorAndPath((IPkgDescExtra) this, (IPkgDescExtra) existingDesc)) { return false; } } else if (!getPath().equals(existingDesc.getPath())) { return false; } } // Packages that have a major version are generally updates if it increases. if (hasMajorRevision() && getMajorRevision().compareTo(existingDesc.getMajorRevision()) > 0) { return true; } // Packages that have a full revision are generally updates if it increases // but keeps the same kind of preview (e.g. previews are only updates by previews.) if (hasFullRevision() && (previewComparison == PreviewComparison.IGNORE || existingDesc.isPreview() == isPreview())) { // If both packages match in their preview type (both previews or both not previews) // then is the RC/preview number an update? return getFullRevision().compareTo(existingDesc.getFullRevision(), PreviewComparison.COMPARE_NUMBER) > 0; } return false; } //---- Ordering ---- /** * Compares this descriptor to another one. * All fields must match for equality. * <p/> * This is must not be used an indication that a package is a suitable update for another one. * The comparison order is however suitable for sorting packages for display purposes. */ @Override public int compareTo(@NonNull IPkgDesc o) { int t1 = getType().getIntValue(); int t2 = o.getType().getIntValue(); if (t1 != t2) { return t1 - t2; } if (hasAndroidVersion() && o.hasAndroidVersion()) { t1 = getAndroidVersion().compareTo(o.getAndroidVersion()); if (t1 != 0) { return t1; } } if (hasVendor() && o.hasVendor()) { t1 = getVendor().compareTo(o.getVendor()); if (t1 != 0) { return t1; } } if (hasTag() && o.hasTag()) { t1 = getTag().compareTo(o.getTag()); if (t1 != 0) { return t1; } } if (hasPath() && o.hasPath()) { t1 = getPath().compareTo(o.getPath()); if (t1 != 0) { return t1; } } if (hasFullRevision() && o.hasFullRevision()) { t1 = getFullRevision().compareTo(o.getFullRevision()); if (t1 != 0) { return t1; } } if (hasMajorRevision() && o.hasMajorRevision()) { t1 = getMajorRevision().compareTo(o.getMajorRevision()); if (t1 != 0) { return t1; } } if (hasMinToolsRev() && o.hasMinToolsRev()) { t1 = getMinToolsRev().compareTo(o.getMinToolsRev()); if (t1 != 0) { return t1; } } if (hasMinPlatformToolsRev() && o.hasMinPlatformToolsRev()) { t1 = getMinPlatformToolsRev().compareTo(o.getMinPlatformToolsRev()); if (t1 != 0) { return t1; } } return 0; } // --- display description ---- @NonNull @Override public String getListDescription() { if (mListDisplay != null && !mListDisplay.isEmpty()) { return mListDisplay; } return patternReplaceImpl(getType().getListDisplayPattern()); } @VisibleForTesting(visibility=Visibility.PRIVATE) protected String patternReplaceImpl(String result) { // Flags for list description pattern string, used in PkgType: // $MAJ $FULL $API $PATH $TAG $VEND $NAME (for extras) result = result .replace("$MAJ", hasMajorRevision() ? getMajorRevision().toShortString() : ""); result = result .replace("$FULL", hasFullRevision() ? getFullRevision().toShortString() : ""); result = result .replace("$API", hasAndroidVersion() ? getAndroidVersion().getApiString() : ""); result = result.replace("$PATH", hasPath() ? getPath() : ""); result = result.replace("$TAG", hasTag() && !getTag().equals(SystemImage.DEFAULT_TAG) ? getTag().getDisplay() : ""); result = result.replace("$VEND", hasVendor() ? getVendor().getDisplay() : ""); String name = ""; if (this instanceof IPkgDescExtra) { name = ((IPkgDescExtra) this).getNameDisplay(); } else if (this instanceof IPkgDescAddon) { name = ((IPkgDescAddon) this).getName().getDisplay(); } result = result.replace("$NAME", name); // Evaluate replacements. // {|choice1|choice2|...|choiceN|} gets replaced by the first non-empty choice. for (int start = result.indexOf("{|"); start >= 0; start = result.indexOf("{|")) { int end = result.indexOf('}', start); int last = start + 1; for (int pipe = result.indexOf('|', last+1); pipe > start; last = pipe, pipe = result.indexOf('|', last+1)) { if (pipe - last > 1) { result = result.substring(0, start) + result.substring(last+1, pipe) + result.substring(end+1); break; } } } // Evaluate conditions. // {?value>1:text to use} -- uses the text if value is greater than 1. // Simplification: this is our only test right now so hard-code it instead of // using a generic expression evaluation. for (int start = result.indexOf("{?"); start >= 0; start = result.indexOf("{?")) { int end = result.indexOf('}', start); int op = result.indexOf(">1:"); if (op > start) { String value = ""; try { FullRevision i = FullRevision.parseRevision(result.substring(start+2, op)); if (i.compareTo(new FullRevision(1)) > 0) { value = result.substring(op+3, end); } } catch (NumberFormatException e) { value = "ERROR " + e.getMessage() + " in " + result.substring(start, end+1); } result = result.substring(0, start) + value + result.substring(end+1); } } return result; } /** String representation for debugging purposes. */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("<PkgDesc"); //NON-NLS-1$ builder.append(" Type="); //NON-NLS-1$ builder.append(getType().toString() .toLowerCase(Locale.US) .replace("pkg_", "")); //NON-NLS-1$ //NON-NLS-2$ if (hasAndroidVersion()) { builder.append(" Android=").append(getAndroidVersion()); //NON-NLS-1$ } if (hasVendor()) { builder.append(" Vendor=").append(getVendor().toString()); //NON-NLS-1$ } if (hasTag()) { builder.append(" Tag=").append(getTag()); //NON-NLS-1$ } if (hasPath()) { builder.append(" Path=").append(getPath()); //NON-NLS-1$ } if (hasFullRevision()) { builder.append(" FullRev=").append(getFullRevision()); //NON-NLS-1$ } if (hasMajorRevision()) { builder.append(" MajorRev=").append(getMajorRevision()); //NON-NLS-1$ } if (hasMinToolsRev()) { builder.append(" MinToolsRev=").append(getMinToolsRev()); //NON-NLS-1$ } if (hasMinPlatformToolsRev()) { builder.append(" MinPlatToolsRev=").append(getMinPlatformToolsRev()); //NON-NLS-1$ } if (mListDisplay != null) { builder.append(" ListDisp=").append(mListDisplay); //NON-NLS-1$ } if (mDescriptionShort != null) { builder.append(" DescShort=").append(mDescriptionShort); //NON-NLS-1$ } if (mDescriptionUrl != null) { builder.append(" DescUrl=").append(mDescriptionUrl); //NON-NLS-1$ } if (mLicense != null) { builder.append(" License['").append(mLicense.getLicenseRef()) //NON-NLS-1$ .append("]=") //NON-NLS-1$ .append(mLicense.getLicense().length()).append(" chars"); //NON-NLS-1$ } if (isObsolete()) { builder.append(" Obsolete=yes"); //NON-NLS-1$ } builder.append('>'); return builder.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (hasAndroidVersion() ? getAndroidVersion().hashCode() : 0); result = prime * result + (hasVendor() ? getVendor() .hashCode() : 0); result = prime * result + (hasTag() ? getTag() .hashCode() : 0); result = prime * result + (hasPath() ? getPath() .hashCode() : 0); result = prime * result + (hasFullRevision() ? getFullRevision() .hashCode() : 0); result = prime * result + (hasMajorRevision() ? getMajorRevision() .hashCode() : 0); result = prime * result + (hasMinToolsRev() ? getMinToolsRev() .hashCode() : 0); result = prime * result + (hasMinPlatformToolsRev() ? getMinPlatformToolsRev().hashCode() : 0); return result; } @Override public boolean equals(Object obj) { if (!(obj instanceof IPkgDesc)) return false; IPkgDesc rhs = (IPkgDesc) obj; if (hasAndroidVersion() != rhs.hasAndroidVersion()) { return false; } if (hasAndroidVersion() && !getAndroidVersion().equals(rhs.getAndroidVersion())) { return false; } if (hasTag() != rhs.hasTag()) { return false; } if (hasTag() && !getTag().equals(rhs.getTag())) { return false; } if (hasPath() != rhs.hasPath()) { return false; } if (hasPath() && !getPath().equals(rhs.getPath())) { return false; } if (hasFullRevision() != rhs.hasFullRevision()) { return false; } if (hasFullRevision() && !getFullRevision().equals(rhs.getFullRevision())) { return false; } if (hasMajorRevision() != rhs.hasMajorRevision()) { return false; } if (hasMajorRevision() && !getMajorRevision().equals(rhs.getMajorRevision())) { return false; } if (hasMinToolsRev() != rhs.hasMinToolsRev()) { return false; } if (hasMinToolsRev() && !getMinToolsRev().equals(rhs.getMinToolsRev())) { return false; } if (hasMinPlatformToolsRev() != rhs.hasMinPlatformToolsRev()) { return false; } if (hasMinPlatformToolsRev() && !getMinPlatformToolsRev().equals(rhs.getMinPlatformToolsRev())) { return false; } return true; } // ---- Constructors ----- public interface IIsUpdateFor { boolean isUpdateFor(@NonNull PkgDesc thisPkgDesc, @NonNull IPkgDesc existingDesc); } public interface IGetPath { String getPath(@NonNull PkgDesc thisPkgDesc); } public static class Builder { private final PkgType mType; private FullRevision mFullRevision; private MajorRevision mMajorRevision; private AndroidVersion mAndroidVersion; private String mPath; private IdDisplay mTag; private IdDisplay mVendor; private FullRevision mMinToolsRev; private FullRevision mMinPlatformToolsRev; private IIsUpdateFor mCustomIsUpdateFor; private IGetPath mCustomPath; private String[] mOldPaths; private String mNameDisplay; private IdDisplay mNameIdDisplay; private License mLicense; private String mListDisplay; private String mDescriptionShort; private String mDescriptionUrl; private boolean mIsObsolete; private Builder(PkgType type) { mType = type; } /** * Creates a new tool package descriptor. * * @param revision The revision of the tool package. * @param minPlatformToolsRev The {@code min-platform-tools-rev}. * Use {@link FullRevision#NOT_SPECIFIED} to indicate there is no requirement. * @return A {@link PkgDesc} describing this tool package. */ @NonNull public static Builder newTool(@NonNull FullRevision revision, @NonNull FullRevision minPlatformToolsRev) { Builder p = new Builder(PkgType.PKG_TOOLS); p.mFullRevision = revision; p.mMinPlatformToolsRev = minPlatformToolsRev; return p; } /** * Creates a new platform-tool package descriptor. * * @param revision The revision of the platform-tool package. * @return A {@link PkgDesc} describing this platform-tool package. */ @NonNull public static Builder newPlatformTool(@NonNull FullRevision revision) { Builder p = new Builder(PkgType.PKG_PLATFORM_TOOLS); p.mFullRevision = revision; return p; } /** * Creates a new build-tool package descriptor. * * @param revision The revision of the build-tool package. * @return A {@link PkgDesc} describing this build-tool package. */ @NonNull public static Builder newBuildTool(@NonNull FullRevision revision) { Builder p = new Builder(PkgType.PKG_BUILD_TOOLS); p.mFullRevision = revision; p.mCustomIsUpdateFor = new IIsUpdateFor() { @Override public boolean isUpdateFor(PkgDesc thisPkgDesc, IPkgDesc existingDesc) { // Generic test checks that the preview type is the same (both previews or not). // Build tool is different in that the full revision must be an exact match // and not an increase. return thisPkgDesc .isGenericUpdateFor(existingDesc, PreviewComparison.COMPARE_NUMBER) && thisPkgDesc.getFullRevision().compareTo( existingDesc.getFullRevision(), PreviewComparison.COMPARE_TYPE) == 0; } }; return p; } /** * Creates a new doc package descriptor. * * @param revision The revision of the doc package. * @return A {@link PkgDesc} describing this doc package. */ @NonNull public static Builder newDoc(@NonNull AndroidVersion version, @NonNull MajorRevision revision) { Builder p = new Builder(PkgType.PKG_DOC); p.mAndroidVersion = version; p.mMajorRevision = revision; p.mCustomIsUpdateFor = new IIsUpdateFor() { @Override public boolean isUpdateFor(PkgDesc thisPkgDesc, IPkgDesc existingDesc) { if (existingDesc == null || !thisPkgDesc.getType().equals(existingDesc.getType())) { return false; } // This package is unique in the SDK. It's an update if the API is newer // or the revision is newer for the same API. int diff = thisPkgDesc.getAndroidVersion().compareTo( existingDesc.getAndroidVersion()); return diff > 0 || (diff == 0 && thisPkgDesc.getMajorRevision().compareTo( existingDesc.getMajorRevision()) > 0); } }; return p; } /** * Creates a new extra package descriptor. * * @param vendor The vendor id string of the extra package. * @param path The path id string of the extra package. * @param displayName The display name. If missing, caller should build one using the path. * @param oldPaths An optional list of older paths for this extra package. * @param revision The revision of the extra package. * @return A {@link PkgDesc} describing this extra package. */ @NonNull public static Builder newExtra(@NonNull IdDisplay vendor, @NonNull String path, @Nullable String displayName, @Nullable String[] oldPaths, @NonNull NoPreviewRevision revision) { Builder p = new Builder(PkgType.PKG_EXTRA); p.mFullRevision = revision; p.mVendor = vendor; p.mPath = path; p.mNameDisplay = displayName; p.mOldPaths = oldPaths; return p; } /** * Creates a new platform package descriptor. * * @param version The android version of the platform package. * @param revision The revision of the extra package. * @param minToolsRev An optional {@code min-tools-rev}. * Use {@link FullRevision#NOT_SPECIFIED} to indicate * there is no requirement. * @return A {@link PkgDesc} describing this platform package. */ @NonNull public static Builder newPlatform(@NonNull AndroidVersion version, @NonNull MajorRevision revision, @NonNull FullRevision minToolsRev) { Builder p = new Builder(PkgType.PKG_PLATFORM); p.mAndroidVersion = version; p.mMajorRevision = revision; p.mMinToolsRev = minToolsRev; p.mCustomPath = new IGetPath() { @Override public String getPath(PkgDesc thisPkgDesc) { /** The "path" of a Platform is its Target Hash. */ return AndroidTargetHash.getPlatformHashString(thisPkgDesc.getAndroidVersion()); } }; return p; } /** * Create a new add-on package descriptor. * <p/> * The vendor id and the name id provided are used to compute the add-on's * target hash. * * @param version The android version of the add-on package. * @param revision The revision of the add-on package. * @param addonVendor The vendor id/display of the add-on package. * @param addonName The name id/display of the add-on package. * @return A {@link PkgDesc} describing this add-on package. */ @NonNull public static Builder newAddon(@NonNull AndroidVersion version, @NonNull MajorRevision revision, @NonNull IdDisplay addonVendor, @NonNull IdDisplay addonName) { Builder p = new Builder(PkgType.PKG_ADDON); p.mAndroidVersion = version; p.mMajorRevision = revision; p.mVendor = addonVendor; p.mNameIdDisplay = addonName; return p; } /** * Create a new platform system-image package descriptor. * <p/> * For system-images, {@link PkgDesc#getPath()} returns the ABI. * * @param version The android version of the system-image package. * @param tag The tag of the system-image package. * @param abi The ABI of the system-image package. * @param revision The revision of the system-image package. * @return A {@link PkgDesc} describing this system-image package. */ @NonNull public static Builder newSysImg(@NonNull AndroidVersion version, @NonNull IdDisplay tag, @NonNull String abi, @NonNull MajorRevision revision) { Builder p = new Builder(PkgType.PKG_SYS_IMAGE); p.mAndroidVersion = version; p.mMajorRevision = revision; p.mTag = tag; p.mPath = abi; p.mVendor = null; return p; } /** * Create a new add-on system-image package descriptor. * <p/> * For system-images, {@link PkgDesc#getPath()} returns the ABI. * * @param version The android version of the system-image package. * @param addonVendor The vendor id/display of an associated add-on. * @param addonName The tag of the system-image package is the add-on name. * @param abi The ABI of the system-image package. * @param revision The revision of the system-image package. * @return A {@link PkgDesc} describing this system-image package. */ @NonNull public static Builder newAddonSysImg(@NonNull AndroidVersion version, @NonNull IdDisplay addonVendor, @NonNull IdDisplay addonName, @NonNull String abi, @NonNull MajorRevision revision) { Builder p = new Builder(PkgType.PKG_ADDON_SYS_IMAGE); p.mAndroidVersion = version; p.mMajorRevision = revision; p.mTag = addonName; p.mPath = abi; p.mVendor = addonVendor; return p; } /** * Create a new source package descriptor. * * @param version The android version of the source package. * @param revision The revision of the source package. * @return A {@link PkgDesc} describing this source package. */ @NonNull public static Builder newSource(@NonNull AndroidVersion version, @NonNull MajorRevision revision) { Builder p = new Builder(PkgType.PKG_SOURCE); p.mAndroidVersion = version; p.mMajorRevision = revision; return p; } /** * Create a new sample package descriptor. * * @param version The android version of the sample package. * @param revision The revision of the sample package. * @param minToolsRev An optional {@code min-tools-rev}. * Use {@link FullRevision#NOT_SPECIFIED} to indicate * there is no requirement. * @return A {@link PkgDesc} describing this sample package. */ @NonNull public static Builder newSample(@NonNull AndroidVersion version, @NonNull MajorRevision revision, @NonNull FullRevision minToolsRev) { Builder p = new Builder(PkgType.PKG_SAMPLE); p.mAndroidVersion = version; p.mMajorRevision = revision; p.mMinToolsRev = minToolsRev; return p; } /** * Creates a new NDK package descriptor. * * @param revision The revision of the NDK package. * @return A {@link PkgDesc} describing this NDK package. */ @NonNull public static Builder newNdk(@NonNull FullRevision revision) { Builder p = new Builder(PkgType.PKG_NDK); p.mFullRevision = revision; return p; } public Builder setLicense(@Nullable License license) { mLicense = license; return this; } public Builder setListDisplay(@Nullable String text) { mListDisplay = text; return this; } public Builder setDescriptionShort(@Nullable String text) { mDescriptionShort = text; return this; } public Builder setDescriptionUrl(@Nullable String text) { mDescriptionUrl = text; return this; } public Builder setIsObsolete(boolean isObsolete) { mIsObsolete = isObsolete; return this; } public IPkgDesc create() { if (mType == PkgType.PKG_ADDON) { return new PkgDescAddon( mType, mLicense, mListDisplay, mDescriptionShort, mDescriptionUrl, mIsObsolete, mMajorRevision, mAndroidVersion, mVendor, mNameIdDisplay); } if (mType == PkgType.PKG_EXTRA) { return new PkgDescExtra( mType, mLicense, mListDisplay, mDescriptionShort, mDescriptionUrl, mIsObsolete, mFullRevision, mMajorRevision, mAndroidVersion, mPath, mTag, mVendor, mMinToolsRev, mMinPlatformToolsRev, mNameDisplay, mOldPaths); } return new PkgDesc( mType, mLicense, mListDisplay, mDescriptionShort, mDescriptionUrl, mIsObsolete, mFullRevision, mMajorRevision, mAndroidVersion, mPath, mTag, mVendor, mMinToolsRev, mMinPlatformToolsRev, mCustomIsUpdateFor, mCustomPath); } } // ---- Helpers ----- @NonNull private static String sanitize(@NonNull String str) { str = str.toLowerCase(Locale.US).replaceAll("[^a-z0-9_.-]+", "_").replaceAll("_+", "_"); return str; } }