OSDN Git Service

am 0947e82e: Merge change Ica46d149 into eclair
[android-x86/sdk.git] / sdkmanager / libs / sdkuilib / src / com / android / sdkuilib / internal / repository / UpdaterLogic.java
1 /*\r
2  * Copyright (C) 2009 The Android Open Source Project\r
3  *\r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *      http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 package com.android.sdkuilib.internal.repository;\r
18 \r
19 import com.android.sdklib.AndroidVersion;\r
20 import com.android.sdklib.internal.repository.AddonPackage;\r
21 import com.android.sdklib.internal.repository.Archive;\r
22 import com.android.sdklib.internal.repository.Package;\r
23 import com.android.sdklib.internal.repository.PlatformPackage;\r
24 import com.android.sdklib.internal.repository.RepoSource;\r
25 import com.android.sdklib.internal.repository.RepoSources;\r
26 import com.android.sdklib.internal.repository.ToolPackage;\r
27 import com.android.sdklib.internal.repository.Package.UpdateInfo;\r
28 \r
29 import java.util.ArrayList;\r
30 import java.util.Collection;\r
31 \r
32 class UpdaterLogic {\r
33 \r
34     private RepoSources mSources;\r
35 \r
36     public ArrayList<ArchiveInfo> computeUpdates(\r
37             Collection<Archive> selectedArchives,\r
38             RepoSources sources,\r
39             Package[] localPkgs) {\r
40 \r
41         mSources = sources;\r
42         ArrayList<ArchiveInfo> archives = new ArrayList<ArchiveInfo>();\r
43         ArrayList<Package> remotePkgs = new ArrayList<Package>();\r
44 \r
45         if (selectedArchives == null) {\r
46             selectedArchives = findUpdates(localPkgs, remotePkgs);\r
47         }\r
48 \r
49         for (Archive a : selectedArchives) {\r
50             insertArchive(a, archives, selectedArchives, remotePkgs, localPkgs, false);\r
51         }\r
52 \r
53         return archives;\r
54     }\r
55 \r
56 \r
57     /**\r
58      * Find suitable updates to all current local packages.\r
59      */\r
60     private Collection<Archive> findUpdates(Package[] localPkgs, ArrayList<Package> remotePkgs) {\r
61         ArrayList<Archive> updates = new ArrayList<Archive>();\r
62 \r
63         fetchRemotePackages(remotePkgs);\r
64 \r
65         for (Package localPkg : localPkgs) {\r
66             for (Package remotePkg : remotePkgs) {\r
67                 if (localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) {\r
68                     // Found a suitable update. Only accept the remote package\r
69                     // if it provides at least one compatible archive.\r
70 \r
71                     for (Archive a : remotePkg.getArchives()) {\r
72                         if (a.isCompatible()) {\r
73                             updates.add(a);\r
74                             break;\r
75                         }\r
76                     }\r
77                 }\r
78             }\r
79         }\r
80 \r
81         return updates;\r
82     }\r
83 \r
84     private ArchiveInfo insertArchive(Archive archive,\r
85             ArrayList<ArchiveInfo> outArchives,\r
86             Collection<Archive> selectedArchives,\r
87             ArrayList<Package> remotePkgs,\r
88             Package[] localPkgs,\r
89             boolean automated) {\r
90         Package p = archive.getParentPackage();\r
91 \r
92         // Is this an update?\r
93         Archive updatedArchive = null;\r
94         for (Package lp : localPkgs) {\r
95             assert lp.getArchives().length == 1;\r
96             if (lp.getArchives().length > 0 && lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) {\r
97                 updatedArchive = lp.getArchives()[0];\r
98             }\r
99         }\r
100 \r
101         // find dependencies\r
102         ArchiveInfo dep = findDependency(p, outArchives, selectedArchives, remotePkgs, localPkgs);\r
103 \r
104         ArchiveInfo ai = new ArchiveInfo(\r
105                 archive, //newArchive\r
106                 updatedArchive, //replaced\r
107                 dep //dependsOn\r
108                 );\r
109 \r
110         outArchives.add(ai);\r
111         if (dep != null) {\r
112             dep.addDependencyFor(ai);\r
113         }\r
114 \r
115         return ai;\r
116     }\r
117 \r
118     private ArchiveInfo findDependency(Package pkg,\r
119             ArrayList<ArchiveInfo> outArchives,\r
120             Collection<Archive> selectedArchives,\r
121             ArrayList<Package> remotePkgs,\r
122             Package[] localPkgs) {\r
123 \r
124         // Current dependencies can be:\r
125         // - addon: *always* depends on platform of same API level\r
126         // - platform: *might* depends on tools of rev >= min-tools-rev\r
127 \r
128         if (pkg instanceof AddonPackage) {\r
129             AddonPackage addon = (AddonPackage) pkg;\r
130 \r
131             return findAddonDependency(\r
132                     addon, outArchives, selectedArchives, remotePkgs, localPkgs);\r
133 \r
134         } else if (pkg instanceof PlatformPackage) {\r
135             PlatformPackage platform = (PlatformPackage) pkg;\r
136 \r
137             return findPlatformDependency(\r
138                     platform, outArchives, selectedArchives, remotePkgs, localPkgs);\r
139         }\r
140 \r
141         return null;\r
142     }\r
143 \r
144     /**\r
145      * A platform can have a min-tools-rev, in which case it depends on having a tools package\r
146      * of the requested revision.\r
147      * Finds the tools dependency. If found, add it to the list of things to install.\r
148      * Returns the archive info dependency, if any.\r
149      */\r
150     protected ArchiveInfo findPlatformDependency(PlatformPackage platform,\r
151             ArrayList<ArchiveInfo> outArchives,\r
152             Collection<Archive> selectedArchives,\r
153             ArrayList<Package> remotePkgs,\r
154             Package[] localPkgs) {\r
155         // This is the requirement to match.\r
156         int rev = platform.getMinToolsRevision();\r
157 \r
158         if (rev == PlatformPackage.MIN_TOOLS_REV_NOT_SPECIFIED) {\r
159             // Well actually there's no requirement.\r
160             return null;\r
161         }\r
162 \r
163         // First look in local packages.\r
164         for (Package p : localPkgs) {\r
165             if (p instanceof ToolPackage) {\r
166                 if (((ToolPackage) p).getRevision() >= rev) {\r
167                     // We found one already installed. We don't report this dependency\r
168                     // as the UI only cares about resolving "newly added dependencies".\r
169                     return null;\r
170                 }\r
171             }\r
172         }\r
173 \r
174         // Look in archives already scheduled for install\r
175         for (ArchiveInfo ai : outArchives) {\r
176             Package p = ai.getNewArchive().getParentPackage();\r
177             if (p instanceof PlatformPackage) {\r
178                 if (((ToolPackage) p).getRevision() >= rev) {\r
179                     // The dependency is already scheduled for install, nothing else to do.\r
180                     return ai;\r
181                 }\r
182             }\r
183         }\r
184 \r
185         // Otherwise look in the selected archives.\r
186         for (Archive a : selectedArchives) {\r
187             Package p = a.getParentPackage();\r
188             if (p instanceof ToolPackage) {\r
189                 if (((ToolPackage) p).getRevision() >= rev) {\r
190                     // It's not already in the list of things to install, so add it now\r
191                     return insertArchive(a, outArchives,\r
192                             selectedArchives, remotePkgs, localPkgs,\r
193                             true);\r
194                 }\r
195             }\r
196         }\r
197 \r
198         // Finally nothing matched, so let's look at all available remote packages\r
199         fetchRemotePackages(remotePkgs);\r
200         for (Package p : remotePkgs) {\r
201             if (p instanceof ToolPackage) {\r
202                 if (((ToolPackage) p).getRevision() >= rev) {\r
203                     // It's not already in the list of things to install, so add the\r
204                     // first compatible archive we can find.\r
205                     for (Archive a : p.getArchives()) {\r
206                         if (a.isCompatible()) {\r
207                             return insertArchive(a, outArchives,\r
208                                     selectedArchives, remotePkgs, localPkgs,\r
209                                     true);\r
210                         }\r
211                     }\r
212                 }\r
213             }\r
214         }\r
215 \r
216         // We end up here if nothing matches. We don't have a good tools to match.\r
217         // Seriously, that can't happens unless we totally screwed our repo manifest.\r
218         // We'll let this one go through anyway.\r
219         return null;\r
220     }\r
221 \r
222     /**\r
223      * An addon depends on having a platform with the same API version.\r
224      * Finds the platform dependency. If found, add it to the list of things to install.\r
225      * Returns the archive info dependency, if any.\r
226      */\r
227     protected ArchiveInfo findAddonDependency(AddonPackage addon,\r
228             ArrayList<ArchiveInfo> outArchives,\r
229             Collection<Archive> selectedArchives,\r
230             ArrayList<Package> remotePkgs,\r
231             Package[] localPkgs) {\r
232         // This is the requirement to match.\r
233         AndroidVersion v = addon.getVersion();\r
234 \r
235         // Find a platform that would satisfy the requirement.\r
236 \r
237         // First look in local packages.\r
238         for (Package p : localPkgs) {\r
239             if (p instanceof PlatformPackage) {\r
240                 if (v.equals(((PlatformPackage) p).getVersion())) {\r
241                     // We found one already installed. We don't report this dependency\r
242                     // as the UI only cares about resolving "newly added dependencies".\r
243                     return null;\r
244                 }\r
245             }\r
246         }\r
247 \r
248         // Look in archives already scheduled for install\r
249         for (ArchiveInfo ai : outArchives) {\r
250             Package p = ai.getNewArchive().getParentPackage();\r
251             if (p instanceof PlatformPackage) {\r
252                 if (v.equals(((PlatformPackage) p).getVersion())) {\r
253                     // The dependency is already scheduled for install, nothing else to do.\r
254                     return ai;\r
255                 }\r
256             }\r
257         }\r
258 \r
259         // Otherwise look in the selected archives.\r
260         for (Archive a : selectedArchives) {\r
261             Package p = a.getParentPackage();\r
262             if (p instanceof PlatformPackage) {\r
263                 if (v.equals(((PlatformPackage) p).getVersion())) {\r
264                     // It's not already in the list of things to install, so add it now\r
265                     return insertArchive(a, outArchives,\r
266                             selectedArchives, remotePkgs, localPkgs,\r
267                             true);\r
268                 }\r
269             }\r
270         }\r
271 \r
272         // Finally nothing matched, so let's look at all available remote packages\r
273         fetchRemotePackages(remotePkgs);\r
274         for (Package p : remotePkgs) {\r
275             if (p instanceof PlatformPackage) {\r
276                 if (v.equals(((PlatformPackage) p).getVersion())) {\r
277                     // It's not already in the list of things to install, so add the\r
278                     // first compatible archive we can find.\r
279                     for (Archive a : p.getArchives()) {\r
280                         if (a.isCompatible()) {\r
281                             return insertArchive(a, outArchives,\r
282                                     selectedArchives, remotePkgs, localPkgs,\r
283                                     true);\r
284                         }\r
285                     }\r
286                 }\r
287             }\r
288         }\r
289 \r
290         // We end up here if nothing matches. We don't have a good platform to match.\r
291         // Seriously, that can't happens unless the repository contains a bogus addon\r
292         // entry that does not match any existing platform API level.\r
293         // It's conceivable that a 3rd part addon repo might have error, in which case\r
294         // we'll let this one go through anyway.\r
295         return null;\r
296     }\r
297 \r
298     /** Fetch all remote packages only if really needed. */\r
299     protected void fetchRemotePackages(ArrayList<Package> remotePkgs) {\r
300         if (remotePkgs.size() > 0) {\r
301             return;\r
302         }\r
303 \r
304         // Get all the available packages from all loaded sources\r
305         RepoSource[] remoteSources = mSources.getSources();\r
306 \r
307         for (RepoSource remoteSrc : remoteSources) {\r
308             Package[] pkgs = remoteSrc.getPackages();\r
309             if (pkgs != null) {\r
310                 nextPackage: for (Package pkg : pkgs) {\r
311                     for (Archive a : pkg.getArchives()) {\r
312                         // Only add a package if it contains at least one compatible archive\r
313                         if (a.isCompatible()) {\r
314                             remotePkgs.add(pkg);\r
315                             continue nextPackage;\r
316                         }\r
317                     }\r
318                 }\r
319             }\r
320         }\r
321     }\r
322 \r
323 }\r