• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SdkSources()47     public SdkSources() {
48     }
49 
50     /**
51      * Adds a new source to the Sources list.
52      */
add(SdkSourceCategory category, SdkSource source)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      */
remove(SdkSource source)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      */
removeAll(SdkSourceCategory category)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      */
getCategories()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      */
getSources(SdkSourceCategory category)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      */
getAllSources()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      */
clearAllPackages()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      */
getCategory(SdkSource source)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      */
hasSourceUrl(SdkSource source)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      */
hasSourceUrl(SdkSourceCategory category, SdkSource source)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      */
loadUserAddons(ISdkLog log)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      */
saveUserAddons(ISdkLog log)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