Home | History | Annotate | Download | only in repository
      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