18daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll/*
28daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * Copyright (C) 2012 The Android Open Source Project
38daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll *
48daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * Licensed under the Apache License, Version 2.0 (the "License");
58daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * you may not use this file except in compliance with the License.
68daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * You may obtain a copy of the License at
78daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll *
88daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll *      http://www.apache.org/licenses/LICENSE-2.0
98daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll *
108daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * Unless required by applicable law or agreed to in writing, software
118daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * distributed under the License is distributed on an "AS IS" BASIS,
128daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * See the License for the specific language governing permissions and
148daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * limitations under the License.
158daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll */
168daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
178daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollpackage com.android.sdkuilib.internal.repository.ui;
188daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
198daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.SdkConstants;
208daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdklib.internal.repository.DownloadCache;
218daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdklib.internal.repository.DownloadCache.Strategy;
228daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdklib.internal.repository.IDescription;
238daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdklib.internal.repository.archives.Archive;
248daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdklib.internal.repository.packages.Package;
258daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdklib.internal.repository.sources.SdkSource;
268daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdkuilib.internal.repository.UpdaterData;
278daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdkuilib.internal.repository.core.PackageLoader;
288daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdkuilib.internal.repository.core.PackageLoader.ISourceLoadedCallback;
298daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdkuilib.internal.repository.core.PackagesDiffLogic;
308daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdkuilib.internal.repository.core.PkgCategory;
318daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdkuilib.internal.repository.core.PkgCategoryApi;
328daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdkuilib.internal.repository.core.PkgContentProvider;
338daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdkuilib.internal.repository.core.PkgItem;
348daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdkuilib.internal.repository.core.PkgItem.PkgState;
358daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport com.android.sdkuilib.internal.repository.icons.ImageFactory;
368daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
378daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport org.eclipse.jface.viewers.ColumnLabelProvider;
388daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport org.eclipse.jface.viewers.IInputProvider;
398daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport org.eclipse.jface.viewers.ITableFontProvider;
408daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport org.eclipse.swt.graphics.Font;
418daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport org.eclipse.swt.graphics.Image;
428daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport org.eclipse.swt.graphics.Point;
438daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
448daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport java.net.MalformedURLException;
458daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport java.net.URL;
468daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollimport java.util.List;
478daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
488daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll/**
498daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * Base class for {@link PackagesPage} that holds most of the logic to display
508daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * the tree/list of packages. This class holds most of the logic and {@link PackagesPage}
518daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * holds most of the UI (creating the UI, dealing with menus and buttons and tree
528daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * selection.) This makes it easier to test the functionality by mocking only a
538daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll * subset of the UI.
548daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll */
558daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Mollabstract class PackagesPageImpl {
568daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
578daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    final UpdaterData mUpdaterData;
588daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    final PackagesDiffLogic mDiffLogic;
598daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
608daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    private ICheckboxTreeViewer mITreeViewer;
618daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    private ITreeViewerColumn   mIColumnName;
628daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    private ITreeViewerColumn   mIColumnApi;
638daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    private ITreeViewerColumn   mIColumnRevision;
648daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    private ITreeViewerColumn   mIColumnStatus;
658daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
668daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    PackagesPageImpl(UpdaterData updaterData) {
678daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mUpdaterData = updaterData;
688daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mDiffLogic = new PackagesDiffLogic(updaterData);
698daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
708daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
718daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
728daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Utility method that derived classes can override to check whether the UI is disposed.
738daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * When the UI is disposed, most operations that affect the UI will be bypassed.
748daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * @return True if UI is not available and should not be touched.
758daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
768daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    abstract protected boolean isUiDisposed();
778daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
788daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
798daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Utility method to execute a runnable on the main UI thread.
808daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Will do nothing if {@link #isUiDisposed()} returns false.
818daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * @param runnable The runnable to execute on the main UI thread.
828daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
838daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    abstract protected void syncExec(Runnable runnable);
848daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
858daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    void performFirstLoad() {
868daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // First a package loader is created that only checks
878daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // the local cache xml files. It populates the package
888daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // list based on what the client got last, essentially.
898daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        loadPackages(true /*useLocalCache*/, false /*overrideExisting*/);
908daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
918daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // Next a regular package loader is created that will
928daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // respect the expiration and refresh parameters of the
938daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // download cache.
948daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        loadPackages(false /*useLocalCache*/, true /*overrideExisting*/);
958daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
968daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
978daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    public void setITreeViewer(ICheckboxTreeViewer iTreeViewer) {
988daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mITreeViewer = iTreeViewer;
998daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
1008daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1018daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    public void setIColumns(
1028daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            ITreeViewerColumn columnName,
1038daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            ITreeViewerColumn columnApi,
1048daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            ITreeViewerColumn columnRevision,
1058daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            ITreeViewerColumn columnStatus) {
1068daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mIColumnName = columnName;
1078daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mIColumnApi = columnApi;
1088daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mIColumnRevision = columnRevision;
1098daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mIColumnStatus = columnStatus;
1108daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
1118daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1128daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    void postCreate() {
1138daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // Caller needs to call setITreeViewer before this.
1148daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        assert mITreeViewer     != null;
1158daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // Caller needs to call setIColumns before this.
1168daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        assert mIColumnApi      != null;
1178daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        assert mIColumnName     != null;
1188daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        assert mIColumnStatus   != null;
1198daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        assert mIColumnRevision != null;
1208daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1218daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mITreeViewer.setContentProvider(new PkgContentProvider(mITreeViewer));
1228daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1238daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mIColumnApi.setLabelProvider(
1248daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                new PkgTreeColumnViewerLabelProvider(new PkgCellLabelProvider(mIColumnApi)));
1258daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mIColumnName.setLabelProvider(
1268daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                new PkgTreeColumnViewerLabelProvider(new PkgCellLabelProvider(mIColumnName)));
1278daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mIColumnStatus.setLabelProvider(
1288daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                new PkgTreeColumnViewerLabelProvider(new PkgCellLabelProvider(mIColumnStatus)));
1298daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mIColumnRevision.setLabelProvider(
1308daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                new PkgTreeColumnViewerLabelProvider(new PkgCellLabelProvider(mIColumnRevision)));
1318daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
1328daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1338daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
1348daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Performs a full reload by removing all cached packages data, including the platforms
1358daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * and addons from the sdkmanager instance. This will perform a full local parsing
1368daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * as well as a full reload of the remote data (by fetching all sources again.)
1378daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
1388daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    void fullReload() {
1398daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // Clear all source information, forcing them to be refreshed.
1408daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mUpdaterData.getSources().clearAllPackages();
1418daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // Clear and reload all local data too.
1428daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        localReload();
1438daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
1448daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1458daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
1468daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Performs a full reload of all the local package information, including the platforms
1478daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * and addons from the sdkmanager instance. This will perform a full local parsing.
1488daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * <p/>
1498daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * This method does NOT force a new fetch of the remote sources.
1508daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     *
1518daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * @see #fullReload()
1528daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
1538daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    void localReload() {
1548daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // Clear all source caches, otherwise loading will use the cached data
1558daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mUpdaterData.getLocalSdkParser().clearPackages();
1568daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mUpdaterData.getSdkManager().reloadSdk(mUpdaterData.getSdkLog());
1578daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        loadPackages();
1588daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
1598daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1608daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
1618daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Performs a "normal" reload of the package information, use the default download
1628daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * cache and refreshing strategy as needed.
1638daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
1648daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    void loadPackages() {
1658daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        loadPackages(false /*useLocalCache*/, false /*overrideExisting*/);
1668daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
1678daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1688daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
1698daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Performs a reload of the package information.
1708daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     *
1718daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * @param useLocalCache When true, the {@link PackageLoader} is switched to use
1728daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     *  a specific {@link DownloadCache} using the {@link Strategy#ONLY_CACHE}, meaning
1738daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     *  it will only use data from the local cache. It will not try to fetch or refresh
1748daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     *  manifests. This is used once the very first time the sdk manager window opens
1758daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     *  and is typically followed by a regular load with refresh.
1768daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
1778daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    abstract protected void loadPackages(boolean useLocalCache, boolean overrideExisting);
1788daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1798daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
1808daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Actual implementation of {@link #loadPackages(boolean, boolean)}.
1818daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Derived implementations must call this to do the actual work after setting up the UI.
1828daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
1838daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    void loadPackagesImpl(final boolean useLocalCache, final boolean overrideExisting) {
1848daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        if (mUpdaterData == null) {
1858daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return;
1868daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
1878daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1888daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        final boolean displaySortByApi = isSortByApi();
1898daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1908daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        PackageLoader packageLoader = getPackageLoader(useLocalCache);
1918daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        assert packageLoader != null;
1928daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
1938daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mDiffLogic.updateStart();
1948daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        packageLoader.loadPackages(overrideExisting, new ISourceLoadedCallback() {
1958daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            @Override
1968daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            public boolean onUpdateSource(SdkSource source, Package[] newPackages) {
1978daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                // This runs in a thread and must not access UI directly.
1988daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                final boolean changed = mDiffLogic.updateSourcePackages(
1998daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        displaySortByApi, source, newPackages);
2008daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
2018daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                syncExec(new Runnable() {
2028daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    @Override
2038daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    public void run() {
2048daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        if (changed ||
2058daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            mITreeViewer.getInput() != mDiffLogic.getCategories(isSortByApi())) {
2068daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                refreshViewerInput();
2078daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        }
2088daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    }
2098daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                });
2108daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
2118daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                // Return true to tell the loader to continue with the next source.
2128daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                // Return false to stop the loader if any UI has been disposed, which can
2138daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                // happen if the user is trying to close the window during the load operation.
2148daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                return !isUiDisposed();
2158daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            }
2168daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
2178daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            @Override
2188daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            public void onLoadCompleted() {
2198daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                // This runs in a thread and must not access UI directly.
2208daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                final boolean changed = mDiffLogic.updateEnd(displaySortByApi);
2218daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
2228daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                syncExec(new Runnable() {
2238daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    @Override
2248daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    public void run() {
2258daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        if (changed ||
2268daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            mITreeViewer.getInput() != mDiffLogic.getCategories(isSortByApi())) {
2278daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            try {
2288daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                refreshViewerInput();
2298daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            } catch (Exception ignore) {}
2308daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        }
2318daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
2328daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        if (!useLocalCache &&
2338daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                mDiffLogic.isFirstLoadComplete() &&
2348daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                !isUiDisposed()) {
2358daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            // At the end of the first load, if nothing is selected then
2368daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            // automatically select all new and update packages.
2378daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            Object[] checked = mITreeViewer.getCheckedElements();
2388daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            if (checked == null || checked.length == 0) {
2398daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                onSelectNewUpdates(
2408daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                        false, //selectNew
2418daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                        true,  //selectUpdates,
2428daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                        true); //selectTop
2438daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            }
2448daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        }
2458daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    }
2468daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                });
2478daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            }
2488daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        });
2498daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
2508daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
2518daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
2528daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Used by {@link #loadPackagesImpl(boolean, boolean)} to get the package
2538daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * loader for the first or second pass update. When starting the manager
2548daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * starts with a first pass that reads only from the local cache, with no
2558daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * extra network access. That's {@code useLocalCache} being true.
2568daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * <p/>
2578daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Leter it does a second pass with {@code useLocalCache} set to false
2588daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * and actually uses the download cache specified in {@link UpdaterData}.
2598daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     *
2608daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * This is extracted so that we can control this cache via unit tests.
2618daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
2628daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    protected PackageLoader getPackageLoader(boolean useLocalCache) {
2638daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        if (useLocalCache) {
2648daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return new PackageLoader(mUpdaterData, new DownloadCache(Strategy.ONLY_CACHE));
2658daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        } else {
2668daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return mUpdaterData.getPackageLoader();
2678daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
2688daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
2698daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
2708daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
2718daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Overridden by the UI to respond to a request to refresh the tree viewer
2728daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * when the input has changed.
2738daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * The implementation must call {@link #setViewerInput()} somehow and will
2748daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * also need to adjust the expand state of the tree items and/or update
2758daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * some buttons or other state.
2768daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
2778daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    abstract protected void refreshViewerInput();
2788daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
2798daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
2808daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Invoked from {@link #refreshViewerInput()} to actually either set the
2818daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * input of the tree viewer or refresh it if it's the <em>same</em> input
2828daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * object.
2838daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
2848daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    protected void setViewerInput() {
2858daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        List<PkgCategory> cats = mDiffLogic.getCategories(isSortByApi());
2868daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        if (mITreeViewer.getInput() != cats) {
2878daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            // set initial input
2888daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            mITreeViewer.setInput(cats);
2898daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        } else {
2908daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            // refresh existing, which preserves the expanded state, the selection
2918daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            // and the checked state.
2928daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            mITreeViewer.refresh();
2938daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
2948daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
2958daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
2968daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
2978daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Overridden by the UI to determine if the tree should display packages sorted
2988daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * by API (returns true) or by repository source (returns false.)
2998daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
3008daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    abstract protected boolean isSortByApi();
3018daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3028daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
3038daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Checks all PkgItems that are either new or have updates or select top platform
3048daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * for initial run.
3058daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
3068daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    void onSelectNewUpdates(boolean selectNew, boolean selectUpdates, boolean selectTop) {
3078daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // This does not update the tree itself, syncViewerSelection does it in the caller.
3088daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mDiffLogic.checkNewUpdateItems(
3098daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                selectNew,
3108daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                selectUpdates,
3118daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                selectTop,
3128daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                SdkConstants.CURRENT_PLATFORM);
3138daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
3148daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3158daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    /**
3168daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     * Deselect all checked PkgItems.
3178daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll     */
3188daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    void onDeselectAll() {
3198daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // This does not update the tree itself, syncViewerSelection does it in the caller.
3208daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        mDiffLogic.uncheckAllItems();
3218daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
3228daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3238daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    // ----------------------
3248daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3258daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    abstract protected Font getTreeFontItalic();
3268daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3278daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    class PkgCellLabelProvider extends ColumnLabelProvider implements ITableFontProvider {
3288daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3298daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        private final ITreeViewerColumn mColumn;
3308daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3318daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        public PkgCellLabelProvider(ITreeViewerColumn column) {
3328daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            super();
3338daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            mColumn = column;
3348daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
3358daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3368daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        @Override
3378daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        public String getText(Object element) {
3388daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3398daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            if (mColumn == mIColumnName) {
3408daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (element instanceof PkgCategory) {
3418daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return ((PkgCategory) element).getLabel();
3428daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                } else if (element instanceof PkgItem) {
3438daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return getPkgItemName((PkgItem) element);
3448daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                } else if (element instanceof IDescription) {
3458daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return ((IDescription) element).getShortDescription();
3468daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
3478daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3488daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            } else if (mColumn == mIColumnApi) {
3498daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                int api = -1;
3508daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (element instanceof PkgItem) {
3518daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    api = ((PkgItem) element).getApi();
3528daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
3538daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (api >= 1) {
3548daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return Integer.toString(api);
3558daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
3568daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3578daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            } else if (mColumn == mIColumnRevision) {
3588daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (element instanceof PkgItem) {
3598daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    PkgItem pkg = (PkgItem) element;
3608daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return pkg.getRevision().toShortString();
3618daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
3628daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3638daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            } else if (mColumn == mIColumnStatus) {
3648daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (element instanceof PkgItem) {
3658daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    PkgItem pkg = (PkgItem) element;
3668daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3678daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    switch(pkg.getState()) {
3688daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    case INSTALLED:
3698daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        Package update = pkg.getUpdatePkg();
3708daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        if (update != null) {
3718daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            return String.format(
3728daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                    "Update available: rev. %1$s",
3738daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                    update.getRevision().toShortString());
3748daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        }
3758daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        return "Installed";
3768daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3778daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    case NEW:
3788daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        Package p = pkg.getMainPackage();
3798daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        if (p != null && p.hasCompatibleArchive()) {
3808daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            return "Not installed";
3818daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        } else {
3828daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            return String.format("Not compatible with %1$s",
3838daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                                    SdkConstants.currentPlatformName());
3848daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        }
3858daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    }
3868daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return pkg.getState().toString();
3878daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3888daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                } else if (element instanceof Package) {
3898daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    // This is an update package.
3908daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return "New revision " + ((Package) element).getRevision().toShortString();
3918daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
3928daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            }
3938daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3948daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return ""; //$NON-NLS-1$
3958daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
3968daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
3978daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        private String getPkgItemName(PkgItem item) {
3988daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            String name = item.getName().trim();
3998daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4008daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            if (isSortByApi()) {
4018daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                // When sorting by API, the package name might contains the API number
4028daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                // or the platform name at the end. If we find it, cut it out since it's
4038daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                // redundant.
4048daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4058daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                PkgCategoryApi cat = (PkgCategoryApi) findCategoryForItem(item);
4068daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                String apiLabel = cat.getApiLabel();
4078daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                String platLabel = cat.getPlatformName();
4088daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4098daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (platLabel != null && name.endsWith(platLabel)) {
4108daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return name.substring(0, name.length() - platLabel.length());
4118daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4128daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                } else if (apiLabel != null && name.endsWith(apiLabel)) {
4138daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return name.substring(0, name.length() - apiLabel.length());
4148daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4158daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                } else if (platLabel != null && item.isObsolete() && name.indexOf(platLabel) > 0) {
4168daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    // For obsolete items, the format is "<base name> <platform name> (Obsolete)"
4178daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    // so in this case only accept removing a platform name that is not at
4188daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    // the end.
4198daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    name = name.replace(platLabel, ""); //$NON-NLS-1$
4208daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
4218daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            }
4228daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4238daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            // Collapse potential duplicated spacing
4248daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            name = name.replaceAll(" +", " "); //$NON-NLS-1$ //$NON-NLS-2$
4258daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4268daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return name;
4278daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
4288daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4298daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        private PkgCategory findCategoryForItem(PkgItem item) {
4308daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            List<PkgCategory> cats = mDiffLogic.getCategories(isSortByApi());
4318daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            for (PkgCategory cat : cats) {
4328daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                for (PkgItem i : cat.getItems()) {
4338daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    if (i == item) {
4348daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        return cat;
4358daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    }
4368daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
4378daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            }
4388daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4398daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return null;
4408daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
4418daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4428daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        @Override
4438daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        public Image getImage(Object element) {
4448daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            ImageFactory imgFactory = mUpdaterData.getImageFactory();
4458daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4468daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            if (imgFactory != null) {
4478daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (mColumn == mIColumnName) {
4488daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    if (element instanceof PkgCategory) {
4498daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        return imgFactory.getImageForObject(((PkgCategory) element).getIconRef());
4508daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    } else if (element instanceof PkgItem) {
4518daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        return imgFactory.getImageForObject(((PkgItem) element).getMainPackage());
4528daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    }
4538daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return imgFactory.getImageForObject(element);
4548daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4558daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                } else if (mColumn == mIColumnStatus && element instanceof PkgItem) {
4568daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    PkgItem pi = (PkgItem) element;
4578daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    switch(pi.getState()) {
4588daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    case INSTALLED:
4598daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        if (pi.hasUpdatePkg()) {
4608daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            return imgFactory.getImageByName(PackagesPageIcons.ICON_PKG_UPDATE);
4618daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        } else {
4628daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            return imgFactory.getImageByName(PackagesPageIcons.ICON_PKG_INSTALLED);
4638daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        }
4648daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    case NEW:
4658daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        Package p = pi.getMainPackage();
4668daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        if (p != null && p.hasCompatibleArchive()) {
4678daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            return imgFactory.getImageByName(PackagesPageIcons.ICON_PKG_NEW);
4688daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        } else {
4698daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            return imgFactory.getImageByName(PackagesPageIcons.ICON_PKG_INCOMPAT);
4708daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        }
4718daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    }
4728daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
4738daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            }
4748daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return super.getImage(element);
4758daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
4768daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4778daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // -- ITableFontProvider
4788daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4798daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        @Override
4808daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        public Font getFont(Object element, int columnIndex) {
4818daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            if (element instanceof PkgItem) {
4828daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (((PkgItem) element).getState() == PkgState.NEW) {
4838daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    return getTreeFontItalic();
4848daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
4858daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            } else if (element instanceof Package) {
4868daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                // update package
4878daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                return getTreeFontItalic();
4888daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            }
4898daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return super.getFont(element);
4908daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
4918daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4928daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        // -- Tooltip support
4938daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
4948daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        @Override
4958daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        public String getToolTipText(Object element) {
4968daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            PkgItem pi = element instanceof PkgItem ? (PkgItem) element : null;
4978daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            if (pi != null) {
4988daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                element = pi.getMainPackage();
4998daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            }
5008daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            if (element instanceof IDescription) {
5018daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                String s = getTooltipDescription((IDescription) element);
5028daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
5038daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (pi != null && pi.hasUpdatePkg()) {
5048daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    s += "\n-----------------" +        //$NON-NLS-1$
5058daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                         "\nUpdate Available:\n" +      //$NON-NLS-1$
5068daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                         getTooltipDescription(pi.getUpdatePkg());
5078daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
5088daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
5098daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                return s;
5108daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            }
5118daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return super.getToolTipText(element);
5128daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
5138daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
5148daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        private String getTooltipDescription(IDescription element) {
5158daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            String s = element.getLongDescription();
5168daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            if (element instanceof Package) {
5178daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                Package p = (Package) element;
5188daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
5198daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (!p.isLocal()) {
5208daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    // For non-installed item, try to find a download size
5218daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    for (Archive a : p.getArchives()) {
5228daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        if (!a.isLocal() && a.isCompatible()) {
5238daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            s += '\n' + a.getSizeDescription();
5248daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            break;
5258daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        }
5268daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    }
5278daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
5288daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
5298daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                // Display info about where this package comes/came from
5308daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                SdkSource src = p.getParentSource();
5318daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                if (src != null) {
5328daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    try {
5338daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        URL url = new URL(src.getUrl());
5348daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        String host = url.getHost();
5358daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        if (p.isLocal()) {
5368daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            s += String.format("\nInstalled from %1$s", host);
5378daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        } else {
5388daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                            s += String.format("\nProvided by %1$s", host);
5398daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                        }
5408daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    } catch (MalformedURLException ignore) {
5418daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                    }
5428daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll                }
5438daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            }
5448daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return s;
5458daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
5468daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
5478daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        @Override
5488daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        public Point getToolTipShift(Object object) {
5498daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return new Point(15, 5);
5508daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
5518daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
5528daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        @Override
5538daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        public int getToolTipDisplayDelayTime(Object object) {
5548daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll            return 500;
5558daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        }
5568daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
5578daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
5588daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    interface ICheckboxTreeViewer extends IInputProvider {
5598daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        void setContentProvider(PkgContentProvider pkgContentProvider);
5608daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        void refresh();
5618daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        void setInput(List<PkgCategory> cats);
5628daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        Object[] getCheckedElements();
5638daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
5648daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll
5658daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    interface ITreeViewerColumn {
5668daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll        void setLabelProvider(ColumnLabelProvider labelProvider);
5678daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll    }
5688daea84228bdda8d714f2ab4dfc19a3c2f10271bRaphael Moll}
569