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.sdklib.internal.repository;
     18 
     19 import com.android.prefs.AndroidLocation;
     20 import com.android.prefs.AndroidLocation.AndroidLocationException;
     21 import com.android.sdklib.ISdkLog;
     22 
     23 import java.io.File;
     24 import java.io.FileInputStream;
     25 import java.io.FileOutputStream;
     26 import java.io.IOException;
     27 import java.util.ArrayList;
     28 import java.util.EnumMap;
     29 import java.util.Iterator;
     30 import java.util.Properties;
     31 import java.util.Map.Entry;
     32 
     33 /**
     34  * A list of sdk-repository and sdk-addon sources, sorted by {@link SdkSourceCategory}.
     35  */
     36 public class SdkSources {
     37 
     38     private static final String KEY_COUNT = "count";
     39 
     40     private static final String KEY_SRC = "src";
     41 
     42     private static final String SRC_FILENAME = "repositories.cfg"; //$NON-NLS-1$
     43 
     44     private final EnumMap<SdkSourceCategory, ArrayList<SdkSource>> mSources =
     45         new EnumMap<SdkSourceCategory, ArrayList<SdkSource>>(SdkSourceCategory.class);
     46 
     47     public SdkSources() {
     48     }
     49 
     50     /**
     51      * Adds a new source to the Sources list.
     52      */
     53     public void add(SdkSourceCategory category, SdkSource source) {
     54         synchronized (mSources) {
     55             ArrayList<SdkSource> list = mSources.get(category);
     56             if (list == null) {
     57                 list = new ArrayList<SdkSource>();
     58                 mSources.put(category, list);
     59             }
     60 
     61             list.add(source);
     62         }
     63     }
     64 
     65     /**
     66      * Removes a source from the Sources list.
     67      */
     68     public void remove(SdkSource source) {
     69         synchronized (mSources) {
     70             Iterator<Entry<SdkSourceCategory, ArrayList<SdkSource>>> it =
     71                 mSources.entrySet().iterator();
     72             while (it.hasNext()) {
     73                 Entry<SdkSourceCategory, ArrayList<SdkSource>> entry = it.next();
     74                 ArrayList<SdkSource> list = entry.getValue();
     75 
     76                 if (list.remove(source)) {
     77                     if (list.isEmpty()) {
     78                         // remove the entry since the source list became empty
     79                         it.remove();
     80                     }
     81                 }
     82             }
     83         }
     84     }
     85 
     86     /**
     87      * Removes all the sources in the given category.
     88      */
     89     public void removeAll(SdkSourceCategory category) {
     90         synchronized (mSources) {
     91             mSources.remove(category);
     92         }
     93     }
     94 
     95     /**
     96      * Returns a set of all categories that must be displayed. This includes all
     97      * categories that are to be always displayed as well as all categories which
     98      * have at least one source.
     99      * Might return a empty array, but never returns null.
    100      */
    101     public SdkSourceCategory[] getCategories() {
    102         ArrayList<SdkSourceCategory> cats = new ArrayList<SdkSourceCategory>();
    103 
    104         for (SdkSourceCategory cat : SdkSourceCategory.values()) {
    105             if (cat.getAlwaysDisplay()) {
    106                 cats.add(cat);
    107             } else {
    108                 synchronized (mSources) {
    109                     ArrayList<SdkSource> list = mSources.get(cat);
    110                     if (list != null && !list.isEmpty()) {
    111                         cats.add(cat);
    112                     }
    113                 }
    114             }
    115         }
    116 
    117         return cats.toArray(new SdkSourceCategory[cats.size()]);
    118     }
    119 
    120     /**
    121      * Returns a new array of sources attached to the given category.
    122      * Might return an empty array, but never returns null.
    123      */
    124     public SdkSource[] getSources(SdkSourceCategory category) {
    125         synchronized (mSources) {
    126             ArrayList<SdkSource> list = mSources.get(category);
    127             if (list == null) {
    128                 return new SdkSource[0];
    129             } else {
    130                 return list.toArray(new SdkSource[list.size()]);
    131             }
    132         }
    133     }
    134 
    135     /**
    136      * Returns an array of the sources across all categories. This is never null.
    137      */
    138     public SdkSource[] getAllSources() {
    139         synchronized (mSources) {
    140             int n = 0;
    141 
    142             for (ArrayList<SdkSource> list : mSources.values()) {
    143                 n += list.size();
    144             }
    145 
    146             SdkSource[] sources = new SdkSource[n];
    147 
    148             int i = 0;
    149             for (ArrayList<SdkSource> list : mSources.values()) {
    150                 for (SdkSource source : list) {
    151                     sources[i++] = source;
    152                 }
    153             }
    154 
    155             return sources;
    156         }
    157     }
    158 
    159     /**
    160      * Each source keeps a local cache of whatever it loaded recently.
    161      * This calls {@link SdkSource#clearPackages()} on all the available sources,
    162      * and the next call to {@link SdkSource#getPackages()} will actually reload
    163      * the remote package list.
    164      */
    165     public void clearAllPackages() {
    166         synchronized (mSources) {
    167             for (ArrayList<SdkSource> list : mSources.values()) {
    168                 for (SdkSource source : list) {
    169                     source.clearPackages();
    170                 }
    171             }
    172         }
    173     }
    174 
    175     /**
    176      * Returns the category of a given source, or null if the source is unknown.
    177      * <p/>
    178      * Note that this method uses object identity to find a given source, and does
    179      * not identify sources by their URL like {@link #hasSourceUrl(SdkSource)} does.
    180      * <p/>
    181      * The search is O(N), which should be acceptable on the expectedly small source list.
    182      */
    183     public SdkSourceCategory getCategory(SdkSource source) {
    184         if (source != null) {
    185             synchronized (mSources) {
    186                 for (Entry<SdkSourceCategory, ArrayList<SdkSource>> entry : mSources.entrySet()) {
    187                     if (entry.getValue().contains(source)) {
    188                         return entry.getKey();
    189                     }
    190                 }
    191             }
    192         }
    193         return null;
    194     }
    195 
    196     /**
    197      * Returns true if there's already a similar source in the sources list
    198      * under any category.
    199      * <p/>
    200      * Important: The match is NOT done on object identity.
    201      * Instead, this searches for a <em>similar</em> source, based on
    202      * {@link SdkSource#equals(Object)} which compares the source URLs.
    203      * <p/>
    204      * The search is O(N), which should be acceptable on the expectedly small source list.
    205      */
    206     public boolean hasSourceUrl(SdkSource source) {
    207         synchronized (mSources) {
    208             for (ArrayList<SdkSource> list : mSources.values()) {
    209                 for (SdkSource s : list) {
    210                     if (s.equals(source)) {
    211                         return true;
    212                     }
    213                 }
    214             }
    215             return false;
    216         }
    217     }
    218 
    219     /**
    220      * Returns true if there's already a similar source in the sources list
    221      * under the specified category.
    222      * <p/>
    223      * Important: The match is NOT done on object identity.
    224      * Instead, this searches for a <em>similar</em> source, based on
    225      * {@link SdkSource#equals(Object)} which compares the source URLs.
    226      * <p/>
    227      * The search is O(N), which should be acceptable on the expectedly small source list.
    228      */
    229     public boolean hasSourceUrl(SdkSourceCategory category, SdkSource source) {
    230         synchronized (mSources) {
    231             ArrayList<SdkSource> list = mSources.get(category);
    232             if (list != null) {
    233                 for (SdkSource s : list) {
    234                     if (s.equals(source)) {
    235                         return true;
    236                     }
    237                 }
    238             }
    239             return false;
    240         }
    241     }
    242 
    243     /**
    244      * Loads all user sources. This <em>replaces</em> all existing user sources
    245      * by the ones from the property file.
    246      */
    247     public void loadUserAddons(ISdkLog log) {
    248 
    249         // Remove all existing user sources
    250         removeAll(SdkSourceCategory.USER_ADDONS);
    251 
    252         // Load new user sources from property file
    253         FileInputStream fis = null;
    254         try {
    255             String folder = AndroidLocation.getFolder();
    256             File f = new File(folder, SRC_FILENAME);
    257             if (f.exists()) {
    258                 fis = new FileInputStream(f);
    259 
    260                 Properties props = new Properties();
    261                 props.load(fis);
    262 
    263                 int count = Integer.parseInt(props.getProperty(KEY_COUNT, "0"));
    264 
    265                 for (int i = 0; i < count; i++) {
    266                     String url = props.getProperty(String.format("%s%02d", KEY_SRC, i));  //$NON-NLS-1$
    267                     if (url != null) {
    268                         SdkSource s = new SdkAddonSource(url, null/*uiName*/);
    269                         if (!hasSourceUrl(s)) {
    270                             add(SdkSourceCategory.USER_ADDONS, s);
    271                         }
    272                     }
    273                 }
    274             }
    275 
    276         } catch (NumberFormatException e) {
    277             log.error(e, null);
    278 
    279         } catch (AndroidLocationException e) {
    280             log.error(e, null);
    281 
    282         } catch (IOException e) {
    283             log.error(e, null);
    284 
    285         } finally {
    286             if (fis != null) {
    287                 try {
    288                     fis.close();
    289                 } catch (IOException e) {
    290                 }
    291             }
    292         }
    293     }
    294 
    295     /**
    296      * Saves all the user sources.
    297      * @param log Logger. Cannot be null.
    298      */
    299     public void saveUserAddons(ISdkLog log) {
    300         FileOutputStream fos = null;
    301         try {
    302             String folder = AndroidLocation.getFolder();
    303             File f = new File(folder, SRC_FILENAME);
    304 
    305             fos = new FileOutputStream(f);
    306 
    307             Properties props = new Properties();
    308 
    309             int count = 0;
    310             for (SdkSource s : getSources(SdkSourceCategory.USER_ADDONS)) {
    311                 props.setProperty(String.format("%s%02d", KEY_SRC, count), s.getUrl());  //$NON-NLS-1$
    312                 count++;
    313             }
    314             props.setProperty(KEY_COUNT, Integer.toString(count));
    315 
    316             props.store( fos, "## User Sources for Android tool");  //$NON-NLS-1$
    317 
    318         } catch (AndroidLocationException e) {
    319             log.error(e, null);
    320 
    321         } catch (IOException e) {
    322             log.error(e, null);
    323 
    324         } finally {
    325             if (fos != null) {
    326                 try {
    327                     fos.close();
    328                 } catch (IOException e) {
    329                 }
    330             }
    331         }
    332 
    333     }
    334 }
    335