Home | History | Annotate | Download | only in archives
      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.archives;
     18 
     19 import com.android.annotations.VisibleForTesting;
     20 import com.android.annotations.VisibleForTesting.Visibility;
     21 import com.android.sdklib.internal.repository.IDescription;
     22 import com.android.sdklib.internal.repository.packages.Package;
     23 import com.android.sdklib.internal.repository.sources.SdkSource;
     24 import com.android.sdklib.io.FileOp;
     25 
     26 import java.io.File;
     27 import java.security.MessageDigest;
     28 import java.security.NoSuchAlgorithmException;
     29 import java.util.Locale;
     30 import java.util.Properties;
     31 
     32 
     33 /**
     34  * A {@link Archive} is the base class for "something" that can be downloaded from
     35  * the SDK repository.
     36  * <p/>
     37  * A package has some attributes (revision, description) and a list of archives
     38  * which represent the downloadable bits.
     39  * <p/>
     40  * Packages are offered by a {@link SdkSource} (a download site).
     41  * The {@link ArchiveInstaller} takes care of downloading, unpacking and installing an archive.
     42  */
     43 public class Archive implements IDescription, Comparable<Archive> {
     44 
     45     private static final String PROP_OS   = "Archive.Os";       //$NON-NLS-1$
     46     private static final String PROP_ARCH = "Archive.Arch";     //$NON-NLS-1$
     47 
     48     /** The checksum type. */
     49     public enum ChecksumType {
     50         /** A SHA1 checksum, represented as a 40-hex string. */
     51         SHA1("SHA-1");  //$NON-NLS-1$
     52 
     53         private final String mAlgorithmName;
     54 
     55         /**
     56          * Constructs a {@link ChecksumType} with the algorigth name
     57          * suitable for {@link MessageDigest#getInstance(String)}.
     58          * <p/>
     59          * These names are officially documented at
     60          * http://java.sun.com/javase/6/docs/technotes/guides/security/StandardNames.html#MessageDigest
     61          */
     62         private ChecksumType(String algorithmName) {
     63             mAlgorithmName = algorithmName;
     64         }
     65 
     66         /**
     67          * Returns a new {@link MessageDigest} instance for this checksum type.
     68          * @throws NoSuchAlgorithmException if this algorithm is not available.
     69          */
     70         public MessageDigest getMessageDigest() throws NoSuchAlgorithmException {
     71             return MessageDigest.getInstance(mAlgorithmName);
     72         }
     73     }
     74 
     75     /** The OS that this archive can be downloaded on. */
     76     public enum Os {
     77         ANY("Any"),
     78         LINUX("Linux"),
     79         MACOSX("MacOS X"),
     80         WINDOWS("Windows");
     81 
     82         private final String mUiName;
     83 
     84         private Os(String uiName) {
     85             mUiName = uiName;
     86         }
     87 
     88         /** Returns the UI name of the OS. */
     89         public String getUiName() {
     90             return mUiName;
     91         }
     92 
     93         /** Returns the XML name of the OS. */
     94         public String getXmlName() {
     95             return toString().toLowerCase(Locale.US);
     96         }
     97 
     98         /**
     99          * Returns the current OS as one of the {@link Os} enum values or null.
    100          */
    101         public static Os getCurrentOs() {
    102             String os = System.getProperty("os.name");          //$NON-NLS-1$
    103             if (os.startsWith("Mac")) {                         //$NON-NLS-1$
    104                 return Os.MACOSX;
    105 
    106             } else if (os.startsWith("Windows")) {              //$NON-NLS-1$
    107                 return Os.WINDOWS;
    108 
    109             } else if (os.startsWith("Linux")) {                //$NON-NLS-1$
    110                 return Os.LINUX;
    111             }
    112 
    113             return null;
    114         }
    115 
    116         /** Returns true if this OS is compatible with the current one. */
    117         public boolean isCompatible() {
    118             if (this == ANY) {
    119                 return true;
    120             }
    121 
    122             Os os = getCurrentOs();
    123             return this == os;
    124         }
    125     }
    126 
    127     /** The Architecture that this archive can be downloaded on. */
    128     public enum Arch {
    129         ANY("Any"),
    130         PPC("PowerPC"),
    131         X86("x86"),
    132         X86_64("x86_64");
    133 
    134         private final String mUiName;
    135 
    136         private Arch(String uiName) {
    137             mUiName = uiName;
    138         }
    139 
    140         /** Returns the UI name of the architecture. */
    141         public String getUiName() {
    142             return mUiName;
    143         }
    144 
    145         /** Returns the XML name of the architecture. */
    146         public String getXmlName() {
    147             return toString().toLowerCase(Locale.US);
    148         }
    149 
    150         /**
    151          * Returns the current architecture as one of the {@link Arch} enum values or null.
    152          */
    153         public static Arch getCurrentArch() {
    154             // Values listed from http://lopica.sourceforge.net/os.html
    155             String arch = System.getProperty("os.arch");
    156 
    157             if (arch.equalsIgnoreCase("x86_64") || arch.equalsIgnoreCase("amd64")) {
    158                 return Arch.X86_64;
    159 
    160             } else if (arch.equalsIgnoreCase("x86")
    161                     || arch.equalsIgnoreCase("i386")
    162                     || arch.equalsIgnoreCase("i686")) {
    163                 return Arch.X86;
    164 
    165             } else if (arch.equalsIgnoreCase("ppc") || arch.equalsIgnoreCase("PowerPC")) {
    166                 return Arch.PPC;
    167             }
    168 
    169             return null;
    170         }
    171 
    172         /** Returns true if this architecture is compatible with the current one. */
    173         public boolean isCompatible() {
    174             if (this == ANY) {
    175                 return true;
    176             }
    177 
    178             Arch arch = getCurrentArch();
    179             return this == arch;
    180         }
    181     }
    182 
    183     private final Os     mOs;
    184     private final Arch   mArch;
    185     private final String mUrl;
    186     private final long   mSize;
    187     private final String mChecksum;
    188     private final ChecksumType mChecksumType = ChecksumType.SHA1;
    189     private final Package mPackage;
    190     private final String mLocalOsPath;
    191     private final boolean mIsLocal;
    192 
    193     /**
    194      * Creates a new remote archive.
    195      */
    196     public Archive(Package pkg, Os os, Arch arch, String url, long size, String checksum) {
    197         mPackage = pkg;
    198         mOs = os;
    199         mArch = arch;
    200         mUrl = url == null ? null : url.trim();
    201         mLocalOsPath = null;
    202         mSize = size;
    203         mChecksum = checksum;
    204         mIsLocal = false;
    205     }
    206 
    207     /**
    208      * Creates a new local archive.
    209      * Uses the properties from props first, if possible. Props can be null.
    210      */
    211     @VisibleForTesting(visibility=Visibility.PACKAGE)
    212     public Archive(Package pkg, Properties props, Os os, Arch arch, String localOsPath) {
    213         mPackage = pkg;
    214 
    215         mOs   = props == null ? os   : Os.valueOf(  props.getProperty(PROP_OS,   os.toString()));
    216         mArch = props == null ? arch : Arch.valueOf(props.getProperty(PROP_ARCH, arch.toString()));
    217 
    218         mUrl = null;
    219         mLocalOsPath = localOsPath;
    220         mSize = 0;
    221         mChecksum = "";
    222         mIsLocal = localOsPath != null;
    223     }
    224 
    225     /**
    226      * Save the properties of the current archive in the give {@link Properties} object.
    227      * These properties will later be give the constructor that takes a {@link Properties} object.
    228      */
    229     void saveProperties(Properties props) {
    230         props.setProperty(PROP_OS,   mOs.toString());
    231         props.setProperty(PROP_ARCH, mArch.toString());
    232     }
    233 
    234     /**
    235      * Returns true if this is a locally installed archive.
    236      * Returns false if this is a remote archive that needs to be downloaded.
    237      */
    238     public boolean isLocal() {
    239         return mIsLocal;
    240     }
    241 
    242     /**
    243      * Returns the package that created and owns this archive.
    244      * It should generally not be null.
    245      */
    246     public Package getParentPackage() {
    247         return mPackage;
    248     }
    249 
    250     /**
    251      * Returns the archive size, an int > 0.
    252      * Size will be 0 if this a local installed folder of unknown size.
    253      */
    254     public long getSize() {
    255         return mSize;
    256     }
    257 
    258     /**
    259      * Returns the SHA1 archive checksum, as a 40-char hex.
    260      * Can be empty but not null for local installed folders.
    261      */
    262     public String getChecksum() {
    263         return mChecksum;
    264     }
    265 
    266     /**
    267      * Returns the checksum type, always {@link ChecksumType#SHA1} right now.
    268      */
    269     public ChecksumType getChecksumType() {
    270         return mChecksumType;
    271     }
    272 
    273     /**
    274      * Returns the download archive URL, either absolute or relative to the repository xml.
    275      * Always return null for a local installed folder.
    276      * @see #getLocalOsPath()
    277      */
    278     public String getUrl() {
    279         return mUrl;
    280     }
    281 
    282     /**
    283      * Returns the local OS folder where a local archive is installed.
    284      * Always return null for remote archives.
    285      * @see #getUrl()
    286      */
    287     public String getLocalOsPath() {
    288         return mLocalOsPath;
    289     }
    290 
    291     /**
    292      * Returns the archive {@link Os} enum.
    293      * Can be null for a local installed folder on an unknown OS.
    294      */
    295     public Os getOs() {
    296         return mOs;
    297     }
    298 
    299     /**
    300      * Returns the archive {@link Arch} enum.
    301      * Can be null for a local installed folder on an unknown architecture.
    302      */
    303     public Arch getArch() {
    304         return mArch;
    305     }
    306 
    307     /**
    308      * Generates a description for this archive of the OS/Arch supported by this archive.
    309      */
    310     public String getOsDescription() {
    311         String os;
    312         if (mOs == null) {
    313             os = "unknown OS";
    314         } else if (mOs == Os.ANY) {
    315             os = "any OS";
    316         } else {
    317             os = mOs.getUiName();
    318         }
    319 
    320         String arch = "";                               //$NON-NLS-1$
    321         if (mArch != null && mArch != Arch.ANY) {
    322             arch = mArch.getUiName();
    323         }
    324 
    325         return String.format("%1$s%2$s%3$s",
    326                 os,
    327                 arch.length() > 0 ? " " : "",           //$NON-NLS-2$
    328                 arch);
    329     }
    330 
    331     /**
    332      * Returns the short description of the source, if not null.
    333      * Otherwise returns the default Object toString result.
    334      * <p/>
    335      * This is mostly helpful for debugging.
    336      * For UI display, use the {@link IDescription} interface.
    337      */
    338     @Override
    339     public String toString() {
    340         String s = getShortDescription();
    341         if (s != null) {
    342             return s;
    343         }
    344         return super.toString();
    345     }
    346 
    347     /**
    348      * Generates a short description for this archive.
    349      */
    350     @Override
    351     public String getShortDescription() {
    352         return String.format("Archive for %1$s", getOsDescription());
    353     }
    354 
    355     /**
    356      * Generates a longer description for this archive.
    357      */
    358     @Override
    359     public String getLongDescription() {
    360         return String.format("%1$s\n%2$s\n%3$s",
    361                 getShortDescription(),
    362                 getSizeDescription(),
    363                 getSha1Description());
    364     }
    365 
    366     public String getSizeDescription() {
    367         long size = getSize();
    368         String sizeStr;
    369         if (size < 1024) {
    370             sizeStr = String.format("%d Bytes", size);
    371         } else if (size < 1024 * 1024) {
    372             sizeStr = String.format("%d KiB", Math.round(size / 1024.0));
    373         } else if (size < 1024 * 1024 * 1024) {
    374             sizeStr = String.format("%.1f MiB",
    375                     Math.round(10.0 * size / (1024 * 1024.0))/ 10.0);
    376         } else {
    377             sizeStr = String.format("%.1f GiB",
    378                     Math.round(10.0 * size / (1024 * 1024 * 1024.0))/ 10.0);
    379         }
    380 
    381         return String.format("Size: %1$s", sizeStr);
    382     }
    383 
    384     public String getSha1Description() {
    385         return String.format("SHA1: %1$s", getChecksum());
    386     }
    387 
    388     /**
    389      * Returns true if this archive can be installed on the current platform.
    390      */
    391     public boolean isCompatible() {
    392         return getOs().isCompatible() && getArch().isCompatible();
    393     }
    394 
    395     /**
    396      * Delete the archive folder if this is a local archive.
    397      */
    398     public void deleteLocal() {
    399         if (isLocal()) {
    400             new FileOp().deleteFileOrFolder(new File(getLocalOsPath()));
    401         }
    402     }
    403 
    404     /**
    405      * Archives are compared using their {@link Package} ordering.
    406      *
    407      * @see Package#compareTo(Package)
    408      */
    409     @Override
    410     public int compareTo(Archive rhs) {
    411         if (mPackage != null && rhs != null) {
    412             return mPackage.compareTo(rhs.getParentPackage());
    413         }
    414         return 0;
    415     }
    416 
    417     /**
    418      * Note: An {@link Archive}'s hash code does NOT depend on the parent {@link Package} hash code.
    419      * <p/>
    420      * {@inheritDoc}
    421      */
    422     @Override
    423     public int hashCode() {
    424         final int prime = 31;
    425         int result = 1;
    426         result = prime * result + ((mArch == null) ? 0 : mArch.hashCode());
    427         result = prime * result + ((mChecksum == null) ? 0 : mChecksum.hashCode());
    428         result = prime * result + ((mChecksumType == null) ? 0 : mChecksumType.hashCode());
    429         result = prime * result + (mIsLocal ? 1231 : 1237);
    430         result = prime * result + ((mLocalOsPath == null) ? 0 : mLocalOsPath.hashCode());
    431         result = prime * result + ((mOs == null) ? 0 : mOs.hashCode());
    432         result = prime * result + (int) (mSize ^ (mSize >>> 32));
    433         result = prime * result + ((mUrl == null) ? 0 : mUrl.hashCode());
    434         return result;
    435     }
    436 
    437     /**
    438      * Note: An {@link Archive}'s equality does NOT depend on the parent {@link Package} equality.
    439      * <p/>
    440      * {@inheritDoc}
    441      */
    442     @Override
    443     public boolean equals(Object obj) {
    444         if (this == obj) {
    445             return true;
    446         }
    447         if (obj == null) {
    448             return false;
    449         }
    450         if (!(obj instanceof Archive)) {
    451             return false;
    452         }
    453         Archive other = (Archive) obj;
    454         if (mArch == null) {
    455             if (other.mArch != null) {
    456                 return false;
    457             }
    458         } else if (!mArch.equals(other.mArch)) {
    459             return false;
    460         }
    461         if (mChecksum == null) {
    462             if (other.mChecksum != null) {
    463                 return false;
    464             }
    465         } else if (!mChecksum.equals(other.mChecksum)) {
    466             return false;
    467         }
    468         if (mChecksumType == null) {
    469             if (other.mChecksumType != null) {
    470                 return false;
    471             }
    472         } else if (!mChecksumType.equals(other.mChecksumType)) {
    473             return false;
    474         }
    475         if (mIsLocal != other.mIsLocal) {
    476             return false;
    477         }
    478         if (mLocalOsPath == null) {
    479             if (other.mLocalOsPath != null) {
    480                 return false;
    481             }
    482         } else if (!mLocalOsPath.equals(other.mLocalOsPath)) {
    483             return false;
    484         }
    485         if (mOs == null) {
    486             if (other.mOs != null) {
    487                 return false;
    488             }
    489         } else if (!mOs.equals(other.mOs)) {
    490             return false;
    491         }
    492         if (mSize != other.mSize) {
    493             return false;
    494         }
    495         if (mUrl == null) {
    496             if (other.mUrl != null) {
    497                 return false;
    498             }
    499         } else if (!mUrl.equals(other.mUrl)) {
    500             return false;
    501         }
    502         return true;
    503     }
    504 }
    505