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