Home | History | Annotate | Download | only in sdkman1
      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.sdkuilib.internal.repository.sdkman1;
     18 
     19 import com.android.sdklib.internal.repository.Archive;
     20 import com.android.sdklib.internal.repository.IDescription;
     21 import com.android.sdklib.internal.repository.ITask;
     22 import com.android.sdklib.internal.repository.ITaskMonitor;
     23 import com.android.sdklib.internal.repository.NullTaskMonitor;
     24 import com.android.sdklib.internal.repository.Package;
     25 import com.android.sdklib.internal.repository.SdkSource;
     26 import com.android.sdklib.internal.repository.SdkSourceCategory;
     27 import com.android.sdklib.internal.repository.Package.UpdateInfo;
     28 import com.android.sdkuilib.internal.repository.UpdaterData;
     29 import com.android.sdkuilib.internal.repository.icons.ImageFactory;
     30 
     31 import org.eclipse.jface.viewers.IContentProvider;
     32 import org.eclipse.jface.viewers.ILabelProvider;
     33 import org.eclipse.jface.viewers.ITreeContentProvider;
     34 import org.eclipse.jface.viewers.LabelProvider;
     35 import org.eclipse.jface.viewers.Viewer;
     36 import org.eclipse.swt.graphics.Image;
     37 
     38 import java.util.ArrayList;
     39 
     40 /**
     41  * A list of sdk-repository sources.
     42  *
     43  * This implementation is UI dependent.
     44  */
     45 public class RepoSourcesAdapter {
     46 
     47     private final UpdaterData mUpdaterData;
     48 
     49     /**
     50      * A dummy RepoSource entry returned for sources which had load errors.
     51      * It displays a summary of the error as its short description or
     52      * it displays the source's long description.
     53      */
     54     public static class RepoSourceError implements IDescription {
     55 
     56         private final SdkSource mSource;
     57 
     58         public RepoSourceError(SdkSource source) {
     59             mSource = source;
     60         }
     61 
     62         public String getLongDescription() {
     63             return mSource.getLongDescription();
     64         }
     65 
     66         public String getShortDescription() {
     67             return mSource.getFetchError();
     68         }
     69     }
     70 
     71     /**
     72      * A dummy RepoSource entry returned for sources with no packages.
     73      * We need that to force the SWT tree to display an open/close triangle
     74      * even for empty sources.
     75      */
     76     public static class RepoSourceEmpty implements IDescription {
     77 
     78         private final SdkSource mSource;
     79         private final boolean mEmptyBecauseOfUpdateOnly;
     80 
     81         public RepoSourceEmpty(SdkSource source, boolean emptyBecauseOfUpdateOnly) {
     82             mSource = source;
     83             mEmptyBecauseOfUpdateOnly = emptyBecauseOfUpdateOnly;
     84         }
     85 
     86         public String getLongDescription() {
     87             return mSource.getLongDescription();
     88         }
     89 
     90         public String getShortDescription() {
     91             if (mEmptyBecauseOfUpdateOnly) {
     92                 return "Some packages were found but are not compatible updates.";
     93             } else {
     94                 return "No packages found";
     95             }
     96         }
     97     }
     98 
     99     public RepoSourcesAdapter(UpdaterData updaterData) {
    100         mUpdaterData = updaterData;
    101     }
    102 
    103     public ILabelProvider getLabelProvider() {
    104         return new ViewerLabelProvider();
    105     }
    106 
    107 
    108     public IContentProvider getContentProvider() {
    109         return new TreeContentProvider();
    110     }
    111 
    112     // ------------
    113 
    114     private class ViewerLabelProvider extends LabelProvider {
    115 
    116         /** Returns an image appropriate for this element. */
    117         @Override
    118         public Image getImage(Object element) {
    119 
    120             ImageFactory imgFactory = mUpdaterData.getImageFactory();
    121 
    122             if (imgFactory != null) {
    123                 return imgFactory.getImageForObject(element);
    124             }
    125 
    126             return super.getImage(element);
    127         }
    128 
    129         /** Returns the toString of the element. */
    130         @Override
    131         public String getText(Object element) {
    132             if (element instanceof IDescription) {
    133                 return ((IDescription) element).getShortDescription();
    134             }
    135             return super.getText(element);
    136         }
    137     }
    138 
    139     // ------------
    140 
    141     private class TreeContentProvider implements ITreeContentProvider {
    142 
    143         // Called when the viewer is disposed
    144         public void dispose() {
    145             // pass
    146         }
    147 
    148         // Called when the input is set or changed on the provider
    149         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
    150             // pass
    151         }
    152 
    153         /**
    154          * Called to collect the root elements for the given input.
    155          * The input here is a {@link RepoSourcesAdapter} object, this returns an array
    156          * of {@link SdkSource}.
    157          */
    158         public Object[] getElements(Object inputElement) {
    159             return getChildren(inputElement);
    160         }
    161 
    162         /**
    163          * Get the children of the given parent. This is requested on-demand as
    164          * nodes are expanded.
    165          *
    166          * For a {@link RepoSourcesAdapter} object, returns an array of {@link SdkSource}s.
    167          * For a {@link SdkSource}, returns an array of {@link Package}s.
    168          * For a {@link Package}, returns an array of {@link Archive}s.
    169          */
    170         public Object[] getChildren(Object parentElement) {
    171             if (parentElement == RepoSourcesAdapter.this) {
    172                 return mUpdaterData.getSources().getCategories();
    173 
    174             } else if (parentElement instanceof SdkSourceCategory) {
    175                 SdkSourceCategory cat = (SdkSourceCategory) parentElement;
    176                 if (cat == SdkSourceCategory.ADDONS_3RD_PARTY) {
    177                     mUpdaterData.loadRemoteAddonsList(
    178                             new NullTaskMonitor(mUpdaterData.getSdkLog()));
    179                 }
    180 
    181                 SdkSource[] sources = mUpdaterData.getSources().getSources(cat);
    182 
    183                 if (sources.length == 1 && sources[0] != null) {
    184                     // If a source has a single element and this element has the same
    185                     // uiName as the category, collapse both.
    186                     // Basically this is a kludge so that we don't end up with
    187                     //  Android Repository > Android Repository
    188                     // at the top level.
    189                     if (cat.getUiName().equals(sources[0].getUiName())) {
    190                         return getRepoSourceChildren(sources[0]);
    191                     }
    192                 }
    193 
    194                 return sources;
    195 
    196             } else if (parentElement instanceof SdkSource) {
    197                 return getRepoSourceChildren((SdkSource) parentElement);
    198 
    199             } else if (parentElement instanceof Package) {
    200                 return getPackageChildren((Package) parentElement);
    201             }
    202 
    203             return new Object[0];
    204         }
    205 
    206         /**
    207          * Returns the list of packages for this repo source, eventually filtered to display
    208          * only update packages. If the list is empty, returns a specific empty node. If there's
    209          * an error, returns a specific error node.
    210          */
    211         private Object[] getRepoSourceChildren(final SdkSource source) {
    212             Package[] packages = source.getPackages();
    213 
    214             if (packages == null && source.getFetchError() == null) {
    215                 final boolean forceHttp = mUpdaterData.getSettingsController().getForceHttp();
    216 
    217                 mUpdaterData.getTaskFactory().start("Loading Source", new ITask() {
    218                     public void run(ITaskMonitor monitor) {
    219                         source.load(monitor, forceHttp);
    220                     }
    221                 });
    222 
    223                 packages = source.getPackages();
    224             }
    225 
    226             boolean wasEmptyBeforeFilter = (packages == null || packages.length == 0);
    227 
    228             // filter out only the packages that are new/upgrade.
    229             if (packages != null && mUpdaterData.getSettingsController().getShowUpdateOnly()) {
    230                 packages = filterUpdateOnlyPackages(packages);
    231             }
    232             if (packages != null && packages.length == 0) {
    233                 packages = null;
    234             }
    235 
    236             ArrayList<Object> results = new ArrayList<Object>();
    237 
    238             if (source.getFetchError() != null) {
    239                 // Insert a dummy entry to display the fetch error
    240                 results.add(new RepoSourceError(source));
    241             }
    242 
    243             // Either return a non-null package list or create a new empty node
    244             if (packages != null) {
    245                 for (Package p : packages) {
    246                     results.add(p);
    247                 }
    248             } else {
    249                 results.add(new RepoSourceEmpty(source, !wasEmptyBeforeFilter));
    250             }
    251 
    252             return results.toArray();
    253         }
    254 
    255         /**
    256          * Returns the list of archives for the given package, eventually filtering it
    257          * to only show the compatible archives.
    258          */
    259         private Object[] getPackageChildren(Package pkg) {
    260             Archive[] archives = pkg.getArchives();
    261             if (mUpdaterData.getSettingsController().getShowUpdateOnly()) {
    262                 for (Archive archive : archives) {
    263 
    264                     // if we only want the compatible archives, then we just take the first
    265                     // one. it's unlikely there are 2 compatible archives for the same
    266                     // package
    267                     if (archive.isCompatible()) {
    268                         return new Object[] { archive };
    269                     }
    270                 }
    271             }
    272 
    273             return archives;
    274         }
    275 
    276         /**
    277          * Returns the parent of a given element.
    278          * The input {@link RepoSourcesAdapter} is the parent of all {@link SdkSource} elements.
    279          */
    280         public Object getParent(Object element) {
    281 
    282             if (element instanceof SdkSourceCategory) {
    283                 return RepoSourcesAdapter.this;
    284 
    285             } else if (element instanceof SdkSource) {
    286                 return mUpdaterData.getSources().getCategory((SdkSource) element);
    287 
    288             } else if (element instanceof Package) {
    289                 return ((Package) element).getParentSource();
    290 
    291             } else if (element instanceof Archive) {
    292                 return ((Archive) element).getParentPackage();
    293             }
    294             return null;
    295         }
    296 
    297         /**
    298          * Returns true if a given element has children, which is used to display a
    299          * "+/expand" box next to the tree node.
    300          * All non-terminal elements are expandable, whether they actually have any children
    301          * or not. This is necessary on windows in order for the tree to display the "triangle"
    302          * icon so that users can actually expand it, which will fill the node at runtime.
    303          */
    304         public boolean hasChildren(Object element) {
    305             return element instanceof SdkSourceCategory ||
    306                    element instanceof SdkSource ||
    307                    element instanceof Package;
    308         }
    309     }
    310 
    311     /**
    312      * Filters out a list of remote packages to only keep the ones that are either new or
    313      * updates of existing package. This also removes obsolete packages.
    314      *
    315      * @param remotePackages the list of packages to filter.
    316      * @return a non null (but maybe empty) list of new or update packages.
    317      */
    318     private Package[] filterUpdateOnlyPackages(Package[] remotePackages) {
    319         // get the installed packages
    320         Package[] installedPackages = mUpdaterData.getInstalledPackages(
    321                 new NullTaskMonitor(mUpdaterData.getSdkLog()));
    322 
    323         // we'll populate this package list with either upgrades or new packages.
    324         ArrayList<Package> filteredList = new ArrayList<Package>();
    325 
    326         // for each remote packages, we look for an existing version.
    327         // If no existing version -> add to the list
    328         // if existing version but with older revision -> add it to the list
    329         for (Package remotePkg : remotePackages) {
    330 
    331             // Obsolete packages are not offered as updates.
    332             if (remotePkg.isObsolete()) {
    333                 continue;
    334             }
    335 
    336             // For all potential packages, we also make sure that there's an archive for
    337             // the current platform, or we simply skip them.
    338             if (remotePkg.hasCompatibleArchive()) {
    339                 boolean keepPkg = true;
    340 
    341                 nextPkg: for (Package installedPkg : installedPackages) {
    342                     UpdateInfo info = installedPkg.canBeUpdatedBy(remotePkg);
    343                     switch(info) {
    344                     case UPDATE:
    345                         // The remote package is an update to an existing one.
    346                         // We're done looking.
    347                         keepPkg = true;
    348                         break nextPkg;
    349                     case NOT_UPDATE:
    350                         // The remote package is the same as one that is already installed.
    351                         keepPkg = false;
    352                         break;
    353                     case INCOMPATIBLE:
    354                         // We can't compare and decide on incompatible things.
    355                         break;
    356                     }
    357                 }
    358 
    359                 if (keepPkg) {
    360                     filteredList.add(remotePkg);
    361                 }
    362             }
    363         }
    364 
    365         return filteredList.toArray(new Package[filteredList.size()]);
    366     }
    367 }
    368