]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.project/src/org/simantics/project/management/ProvisioningUtil.java
Merge commit '728147df5d63a3333daff3d8c0e9bfd4f5597e3a'
[simantics/platform.git] / bundles / org.simantics.project / src / org / simantics / project / management / ProvisioningUtil.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.project.management;\r
13 \r
14 import java.io.ByteArrayInputStream;\r
15 import java.io.ByteArrayOutputStream;\r
16 import java.io.File;\r
17 import java.io.IOException;\r
18 import java.io.InputStream;\r
19 import java.util.Collection;\r
20 import java.util.Comparator;\r
21 import java.util.Enumeration;\r
22 import java.util.Iterator;\r
23 import java.util.Set;\r
24 import java.util.TreeSet;\r
25 import java.util.jar.JarEntry;\r
26 import java.util.jar.JarFile;\r
27 import java.util.jar.JarInputStream;\r
28 import java.util.jar.Manifest;\r
29 \r
30 import org.eclipse.core.runtime.Path;\r
31 import org.eclipse.equinox.internal.p2.metadata.RequiredCapability;\r
32 import org.eclipse.equinox.internal.p2.touchpoint.eclipse.Util;\r
33 import org.eclipse.equinox.internal.p2.ui.query.RequiredIUsQuery;\r
34 import org.eclipse.equinox.p2.core.IProvisioningAgent;\r
35 import org.eclipse.equinox.p2.core.ProvisionException;\r
36 import org.eclipse.equinox.p2.metadata.IArtifactKey;\r
37 import org.eclipse.equinox.p2.metadata.IInstallableUnit;\r
38 import org.eclipse.equinox.p2.metadata.IInstallableUnitFragment;\r
39 import org.eclipse.equinox.p2.metadata.IRequirement;\r
40 import org.eclipse.equinox.p2.metadata.Version;\r
41 import org.eclipse.equinox.p2.publisher.AdviceFileAdvice;\r
42 import org.eclipse.equinox.p2.publisher.PublisherInfo;\r
43 import org.eclipse.equinox.p2.publisher.eclipse.BundleShapeAdvice;\r
44 import org.eclipse.equinox.p2.publisher.eclipse.BundlesAction;\r
45 import org.eclipse.equinox.p2.publisher.eclipse.IBundleShapeAdvice;\r
46 import org.eclipse.equinox.p2.query.IQuery;\r
47 import org.eclipse.equinox.p2.query.IQueryResult;\r
48 import org.eclipse.equinox.p2.query.IQueryable;\r
49 import org.eclipse.equinox.p2.query.QueryUtil;\r
50 import org.eclipse.equinox.p2.repository.artifact.ArtifactKeyQuery;\r
51 import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor;\r
52 import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;\r
53 import org.eclipse.equinox.p2.repository.artifact.IFileArtifactRepository;\r
54 import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;\r
55 import org.eclipse.osgi.service.resolver.BundleDescription;\r
56 import org.osgi.framework.BundleException;\r
57 import org.simantics.databoard.Bindings;\r
58 import org.simantics.databoard.Files;\r
59 import org.simantics.databoard.binding.Binding;\r
60 import org.simantics.databoard.util.StreamUtil;\r
61 import org.simantics.graph.db.TransferableGraphException;\r
62 import org.simantics.graph.representation.TransferableGraph1;\r
63 \r
64 /**\r
65  * This class contains provisioning utilities.\r
66  *\r
67  * @see QueryUtil\r
68  * @see Util\r
69  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
70  */\r
71 @SuppressWarnings("restriction")\r
72 public class ProvisioningUtil {\r
73 \r
74         private static IRequirement IS_FEATURE_REQUIREMENT = new RequiredCapability("org.eclipse.equinox.p2.eclipse.type", "feature", null, null, false, false, false);\r
75 \r
76         public static void getAllRequirements(IInstallableUnit iu, IQueryable<IInstallableUnit> repo, Set<IInstallableUnit> result) \r
77         {\r
78                 if (result.contains(iu)) return;\r
79                 result.add(iu);\r
80                 IQuery<IInstallableUnit> reqsQuery = new RequiredIUsQuery(iu);\r
81                 for (IInstallableUnit _iu : repo.query( reqsQuery, null ).toSet()) {\r
82                         if (result.contains(_iu)) continue;\r
83                         getAllRequirements(_iu, repo, result);\r
84                 }               \r
85         }\r
86         \r
87         \r
88         /**\r
89          * Get IUs that are required to install this bundle. \r
90          * Assumed architecture is x86 and os win32. \r
91          * Throws an exception if not all requirements can be satisfied.\r
92          * \r
93          * @param iu\r
94          * @param availableIUs\r
95          * @return\r
96          */\r
97         /*\r
98         public static Collection<IRequirement> getAllRequirements(IInstallableUnit iu, IQueryable<IInstallableUnit> repo)\r
99         {               \r
100                 // Installable Units analysed\r
101                 Set<IInstallableUnit> ius = new HashSet<IInstallableUnit>();\r
102                 \r
103                 // Requirements\r
104                 Set<IRequirement> reqs = new HashSet<IRequirement>();\r
105                 \r
106                 // Satisfied requirements\r
107                 Set<IRequirement> sats = new HashSet<IRequirement>();\r
108                 ius.add(iu);\r
109                 reqs.addAll( iu.getRequirements() );\r
110                 \r
111                 // Find satisfactions\r
112                 return result;\r
113         }*/\r
114         /*\r
115         static void _addIU(IInstallableUnit iu, Set<IInstallableUnit> ius, Set<IRequirement> reqs, Set<IRequirement> sats, Set<IRequirement> unsats, IQueryable<IInstallableUnit> repo) \r
116         {\r
117                 if (ius.contains(iu)) return;\r
118                 ius.add( iu );\r
119                 \r
120                 for (IRequirement req : iu.getRequirements()) {\r
121                         if (sats.contains(req)) continue;\r
122                         if (unsats.contains(req)) continue;\r
123                         \r
124                         // req is unsatisfied\r
125                         IQuery\r
126                         IQueryResult<IInstallableUnit> result = repo.query(req, null);\r
127                 }\r
128                 \r
129                 for (IInstallableUnit _iu : availableIUs) {\r
130                         if (sats.contains(_iu)) continue;\r
131                         \r
132                         boolean satisfies = _iu.satisfies(  ) \r
133                 }\r
134         }*/\r
135         \r
136         /**\r
137          * Get all installable units from a metadata repository\r
138          * \r
139          * @param repo\r
140          * @return\r
141          */\r
142         public static Collection<IInstallableUnit> getInstallableUnits(IMetadataRepository repo) {\r
143                 \r
144 //              IQuery<IInstallableUnit> query = QueryUtil.createIUQuery(null, VersionRange.emptyRange);\r
145                 IQuery<IInstallableUnit> query = QueryUtil.createIUAnyQuery();\r
146             IQueryResult<IInstallableUnit> matches = repo.query(query, null);\r
147             return matches.toUnmodifiableSet();\r
148         }\r
149         \r
150 \r
151         /**\r
152          * Test if the {@link IInstallableUnit} is a category. \r
153          * @param iu the element being tested.\r
154          * @return <tt>true</tt> if the parameter is a category.\r
155          */\r
156         public static boolean isCategory(IInstallableUnit iu) {\r
157                 String PROP_TYPE_CATEGORY = "org.eclipse.equinox.p2.type.category"; //$NON-NLS-1$\r
158                 String value = iu.getProperty(PROP_TYPE_CATEGORY);\r
159                 if (value != null && (value.equals(Boolean.TRUE.toString())))\r
160                         return true;\r
161                 return false;\r
162         }\r
163 \r
164         /**\r
165          * Test if the {@link IInstallableUnit} is a fragment. \r
166          * @param iu the element being tested.\r
167          * @return <tt>true</tt> if the parameter is a fragment.\r
168          */\r
169         public static boolean isFragment(IInstallableUnit iu) {\r
170                 return iu instanceof IInstallableUnitFragment;\r
171         }\r
172 \r
173         /**\r
174          * Test if the {@link IInstallableUnit} is a group. \r
175          * @param iu the element being tested.\r
176          * @return <tt>true</tt> if the parameter is a group.\r
177          */\r
178         public static boolean isGroup(IInstallableUnit iu) {\r
179                 String PROP_TYPE_GROUP = "org.eclipse.equinox.p2.type.group"; //$NON-NLS-1$\r
180                 String value = iu.getProperty(PROP_TYPE_GROUP);\r
181                 if (value != null && (value.equals(Boolean.TRUE.toString())))\r
182                         return true;\r
183                 return false;\r
184         }\r
185 \r
186         /**\r
187          * Test if the {@link IInstallableUnit} is a patch. \r
188          * @param iu the element being tested.\r
189          * @return <tt>true</tt> if the parameter is a patch.\r
190          */\r
191         public static boolean isPatch(IInstallableUnit iu) {\r
192                 String PROP_TYPE_PATCH = "org.eclipse.equinox.p2.type.patch"; //$NON-NLS-1$\r
193                 String value = iu.getProperty(PROP_TYPE_PATCH);\r
194                 if (value != null && (value.equals(Boolean.TRUE.toString())))\r
195                         return true;\r
196                 return false;\r
197         }\r
198         \r
199         /**\r
200          * Checks whether a installable unit is a feature\r
201          * \r
202          * @param iu\r
203          * @return <code>true</code> if is feature\r
204          */\r
205         public static boolean isFeature(IInstallableUnit iu) {\r
206 //              String value = iu.getProperty("org.eclipse.equinox.p2.eclipse.type");\r
207 //              return value != null && value.equals("feature");\r
208         return iu.satisfies(IS_FEATURE_REQUIREMENT);\r
209         }\r
210 \r
211         public static boolean isGraph(IInstallableUnit iu, IMetadataRepository metadata, IArtifactRepository arts) throws IOException {\r
212         Collection<IArtifactKey> artifacts = iu.getArtifacts();\r
213         for (IArtifactKey key : artifacts) {\r
214                         if (isGraphArtifact(arts, key)) return true;                    \r
215         }\r
216                 return false;\r
217         }\r
218 \r
219         /**\r
220          * Checks whether artifact is a graph bundle. \r
221          * \r
222          * @param repo artifact repo\r
223          * @param key key to artifact\r
224          * @return <code>true</code> if is a graph bundle\r
225          * @throws IOException \r
226          */\r
227         public static boolean isGraphArtifact(IArtifactRepository repo, IArtifactKey key) throws IOException {\r
228                 return hasFile(repo, key, "graph.tg");\r
229         }\r
230         \r
231         public static boolean hasFile(IArtifactRepository repo, IArtifactKey key, String filename) throws IOException {\r
232                 boolean isBundle = key.getClassifier().equals("osgi.bundle");\r
233                 if (!isBundle) return false;\r
234                 \r
235                 for (IArtifactDescriptor desc : repo.getArtifactDescriptors(key)) {             \r
236                         if (repo instanceof IFileArtifactRepository) {                          \r
237                                 IFileArtifactRepository filerepo = (IFileArtifactRepository) repo;\r
238                                 File f = filerepo.getArtifactFile(key);\r
239                                 if (!f.exists()) return false;\r
240                                 \r
241                                 boolean isJar = f.getName().toLowerCase().endsWith(".jar");\r
242                                 if (!isJar) return false;\r
243                                 \r
244                                 JarFile jf = new JarFile(f);\r
245                                 try {\r
246                                         Enumeration<JarEntry> enm = jf.entries();\r
247                                         while (enm.hasMoreElements()) {\r
248                                                 JarEntry entry = enm.nextElement();\r
249                                                 String entryName = entry.getName();\r
250                                                 if ( entryName.equals(filename) ) return true;\r
251                                         }\r
252                                 } finally {\r
253                                         jf.close();\r
254                                 }\r
255                                 \r
256                         } else {\r
257                                 int size = Integer.valueOf( desc.getProperties().get("download.size") );\r
258                                 ByteArrayOutputStream bos = new ByteArrayOutputStream(size);\r
259                                 repo.getArtifact(desc, bos, null);\r
260                                 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());\r
261                                 JarInputStream jis = new JarInputStream( bis );\r
262                                 for (JarEntry entry = jis.getNextJarEntry(); entry!=null; entry = jis.getNextJarEntry()) {\r
263                                         String entryName = entry.getName();\r
264                                         if ( entryName.equals(filename) ) return true;\r
265                                 }                               \r
266                                 \r
267                         }\r
268                 }\r
269                 return false;\r
270         }\r
271 \r
272         /**\r
273          * Get a file from a repository\r
274          * \r
275          * @param repo artifact repo\r
276          * @param key key to artifact\r
277          * @return input stream (must be closed) or null\r
278          * @throws IOException \r
279          */\r
280         public static InputStream getFile(IArtifactRepository repo, IArtifactKey key, String filename) throws IOException {\r
281                 boolean isBundle = key.getClassifier().equals("osgi.bundle");\r
282                 if (!isBundle) return null;\r
283                 \r
284                 for (IArtifactDescriptor desc : repo.getArtifactDescriptors(key)) {             \r
285                         if (repo instanceof IFileArtifactRepository) {                          \r
286                                 IFileArtifactRepository filerepo = (IFileArtifactRepository) repo;\r
287                                 File f = filerepo.getArtifactFile(key);\r
288                                 if (!f.exists()) return null;\r
289                                 \r
290                                 boolean isJar = f.getName().toLowerCase().endsWith(".jar");\r
291                                 if (!isJar) return null;\r
292                                 \r
293                                 JarFile jf = new JarFile(f);\r
294                                 try {\r
295                                         Enumeration<JarEntry> enm = jf.entries();\r
296                                         while (enm.hasMoreElements()) {\r
297                                                 JarEntry entry = enm.nextElement();\r
298                                                 String entryName = entry.getName();\r
299                                                 if (! entryName.equals(filename) ) continue;\r
300                                                 InputStream is = jf.getInputStream(entry);\r
301                                                 byte[] data = StreamUtil.readFully(is);                                         \r
302                                                 is.close();\r
303                                                 return new ByteArrayInputStream(data);\r
304                                         }\r
305                                 } finally {\r
306                                         jf.close();\r
307                                 }\r
308                                 \r
309                         } else {\r
310                                 int size = Integer.valueOf( desc.getProperties().get("download.size") );\r
311                                 ByteArrayOutputStream bos = new ByteArrayOutputStream(size);\r
312                                 repo.getArtifact(desc, bos, null);\r
313                                 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());\r
314                                 JarInputStream jis = new JarInputStream( bis );\r
315                                 for (JarEntry entry = jis.getNextJarEntry(); entry!=null; entry = jis.getNextJarEntry()) {\r
316                                         String entryName = entry.getName();\r
317                                         if (! entryName.equals(filename) ) continue;                                    \r
318                                         return jis;\r
319                                 }                               \r
320                                 \r
321                         }\r
322                 }\r
323                 return null;\r
324         }       \r
325         /**\r
326          * Checks whether artifact is a graph bundle. \r
327          * \r
328          * @param repo artifact repo\r
329          * @param key key to artifact\r
330          * @return manifest or null\r
331          * @throws IOException \r
332          */\r
333         public static Manifest getManifest(IArtifactRepository repo, IArtifactKey key) throws IOException {\r
334                 InputStream is = getFile(repo, key, "META-INF/MANIFEST.MF");\r
335                 if (is == null) return null;\r
336                 try {\r
337                         return new Manifest(is);\r
338                 } finally {\r
339                         is.close();\r
340                 }\r
341         }\r
342         \r
343         /**\r
344          * Checks whether artifact is a graph bundle. \r
345          * \r
346          * @param repo artifact repo\r
347          * @param key key to artifact\r
348          * @return manifest or null\r
349          * @throws IOException \r
350          */\r
351         public static Manifest getSimanticsManifest(IArtifactRepository repo, IArtifactKey key) throws IOException {\r
352                 InputStream is = getFile(repo, key, "META-INF/SIMANTICS.MF");\r
353                 if (is == null) return null;\r
354                 try {\r
355                         return new Manifest(is);\r
356                 } finally {\r
357                         is.close();\r
358                 }\r
359         }\r
360         \r
361         public static IArtifactKey[] getAllArtifactKeys(IArtifactRepository repo) {\r
362                 return repo.query( ArtifactKeyQuery.ALL_KEYS, null ).toArray( IArtifactKey.class );\r
363         }\r
364         \r
365         public static void getUserInstallables(IArtifactRepository repo, Collection<String> result) throws IOException {\r
366                 IArtifactKey[] keys = repo.query( ArtifactKeyQuery.ALL_KEYS, null ).toArray( IArtifactKey.class );\r
367                 for (IArtifactKey key : keys) {\r
368                         Manifest mf = getSimanticsManifest(repo, key);\r
369                         if (mf==null) continue;\r
370                         String bundleId = mf.getMainAttributes().getValue("Simantics-Feature-Bundle");\r
371                         result.add( bundleId );\r
372                 }\r
373         }\r
374         \r
375         public static GraphBundle getGraphBundle(IArtifactRepository repo, IArtifactKey key, IInstallableUnit iu) \r
376         throws TransferableGraphException\r
377         {\r
378                 String name = ProvisioningUtil.getName(iu);\r
379                 TransferableGraph1 tg = getTransferableGraph(repo, key);                \r
380                 return new GraphBundleEx(name, tg, key.getId(), key.getVersion());              \r
381         }\r
382         \r
383         /**\r
384          * Get the .tg file from a Graph bundle\r
385          * \r
386          * @param repo\r
387          * @param key\r
388          * @return byte[] transferable fraph\r
389          * @throws TGException\r
390          */\r
391         public static TransferableGraph1 getTransferableGraph(IArtifactRepository repo, IArtifactKey key) \r
392         throws TransferableGraphException\r
393         {\r
394                 boolean isBundle = key.getClassifier().equals("osgi.bundle");\r
395                 if (!isBundle) throw new TransferableGraphException("Artifact Key is not osgi.bundle");\r
396                 \r
397                 for (IArtifactDescriptor desc : repo.getArtifactDescriptors(key)) {             \r
398                         if (repo instanceof IFileArtifactRepository) {\r
399                                 IFileArtifactRepository filerepo = (IFileArtifactRepository) repo;\r
400                                 File f = filerepo.getArtifactFile(key);\r
401                                 if (!f.exists()) {\r
402                                         throw new TransferableGraphException(f+" not found");\r
403                                 }\r
404                                 \r
405                                 boolean isJar = f.getName().toLowerCase().endsWith(".jar");\r
406                                 if (!isJar) throw new TransferableGraphException(f+" is not jar as expected");\r
407 \r
408                                 JarFile jf = null;\r
409                                 try {\r
410                                         jf = new JarFile(f);\r
411                                         Enumeration<JarEntry> enm = jf.entries();\r
412                                         while (enm.hasMoreElements()) {\r
413                                                 JarEntry entry = enm.nextElement();\r
414                                                 String entryName = entry.getName().toLowerCase();\r
415                                                 boolean isTG = entryName.equalsIgnoreCase("graph.tg");\r
416                                                 if (!isTG) continue;\r
417                                                 try {\r
418                                                         Binding binding = Bindings.getBindingUnchecked( TransferableGraph1.class );\r
419                                                         long size = entry.getSize();\r
420                                                         InputStream is = jf.getInputStream(entry);\r
421                                                         return (TransferableGraph1) Files.readFile(is, size, binding);\r
422                                                 } catch (IOException e) {\r
423                                                         throw new TransferableGraphException(e);\r
424                                                 }\r
425                                         }\r
426                                 } catch (IOException e) {\r
427                                         throw new TransferableGraphException(e);\r
428                                 } finally {\r
429                                         try {\r
430                                                 if (jf!=null) jf.close();\r
431                                         } catch (IOException e) {\r
432                                         }\r
433                                 }\r
434                                 throw new TransferableGraphException(".tg file was not found in "+key);\r
435                                 \r
436                         } else {\r
437                                 try {\r
438                                         int size = Integer.valueOf( desc.getProperties().get("download.size") );\r
439                                         ByteArrayOutputStream bos = new ByteArrayOutputStream(size);\r
440                                         repo.getArtifact(desc, bos, null);\r
441                                         ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());\r
442                                         JarInputStream jis = new JarInputStream( bis );\r
443                                         for (JarEntry entry = jis.getNextJarEntry(); entry!=null; entry = jis.getNextJarEntry()) {\r
444                                                 String entryName = entry.getName().toLowerCase();\r
445                                                 boolean isTG = entryName.equalsIgnoreCase("graph.tg");\r
446                                                 if (!isTG) continue;\r
447                                                 //long fileSize = entry.getSize();\r
448                                                 Binding binding = Bindings.getBindingUnchecked(TransferableGraph1.class);\r
449                                                 return (TransferableGraph1) Files.readFile(jis, binding);\r
450                                                 \r
451                                         }\r
452                                 } catch (IOException e) {\r
453                                         throw new TransferableGraphException(e);\r
454                                 }\r
455                                 throw new TransferableGraphException(".tg file was not found in "+key);\r
456                         }\r
457                 }\r
458                 throw new TransferableGraphException(".tg file was not found in "+key);\r
459         }\r
460         \r
461         public static String getDescription(IInstallableUnit iu) {\r
462                 return iu.getProperty("org.eclipse.equinox.p2.description");\r
463         }\r
464         \r
465         public static String getName(IInstallableUnit iu) {\r
466                 return iu.getProperty("org.eclipse.equinox.p2.name");\r
467         }\r
468 \r
469         /**\r
470          * Returns an IU corresponding to the given artifact key and bundle, or <code>null</code>\r
471          * if an IU could not be created.\r
472          */\r
473     public static IInstallableUnit createBundleIU(IArtifactKey artifactKey, File bundleFile) {\r
474                 BundleDescription bundleDescription = null;\r
475                 try {\r
476                         bundleDescription = BundlesAction.createBundleDescription(bundleFile);\r
477                 } catch (IOException | BundleException e) {\r
478                         e.printStackTrace();\r
479                 }\r
480                 if (bundleDescription == null)\r
481                         return null;\r
482                 PublisherInfo info = new PublisherInfo();\r
483                 Version version = Version.create(bundleDescription.getVersion().toString());\r
484                 AdviceFileAdvice advice = new AdviceFileAdvice(bundleDescription.getSymbolicName(), version, new Path(bundleFile.getAbsolutePath()), AdviceFileAdvice.BUNDLE_ADVICE_FILE);\r
485                 if (advice.containsAdvice())\r
486                         info.addAdvice(advice);\r
487                 String shape = bundleFile.isDirectory() ? IBundleShapeAdvice.DIR : IBundleShapeAdvice.JAR;\r
488                 info.addAdvice(new BundleShapeAdvice(bundleDescription.getSymbolicName(), version, shape));\r
489                 return BundlesAction.createBundleIU(bundleDescription, artifactKey, info);\r
490         }\r
491 \r
492         public static IFileArtifactRepository getDownloadCacheRepo(IProvisioningAgent agent) throws ProvisionException {\r
493                 return org.eclipse.equinox.internal.p2.touchpoint.natives.Util.getDownloadCacheRepo(agent);\r
494         }\r
495 \r
496         public static Comparator<IInstallableUnit> getIUComparator() {\r
497                 return new Comparator<IInstallableUnit>() {\r
498                         @Override\r
499                         public int compare(IInstallableUnit o1, IInstallableUnit o2) {\r
500                                 return o1.getId().compareTo(o2.getId());\r
501                         }\r
502                         \r
503                 };\r
504         }\r
505 \r
506         public static TreeSet<IInstallableUnit> getSorted( Iterator<IInstallableUnit> iter ) {\r
507                 TreeSet<IInstallableUnit> result = new TreeSet<IInstallableUnit>( getIUComparator() );\r
508                 while (iter.hasNext()) {\r
509                         IInstallableUnit iu = iter.next();\r
510                         result.add( iu );\r
511                 }\r
512                 return result;\r
513         }\r
514 \r
515         public static TreeSet<IInstallableUnit> getSorted( IQueryResult<IInstallableUnit> result ) {\r
516                 return getSorted(result.iterator());\r
517         }\r
518         \r
519 }\r
520 \r