Stuart McCulloch | 26e7a5a | 2011-10-17 10:31:43 +0000 | [diff] [blame^] | 1 | package aQute.bnd.resolver; |
| 2 | |
| 3 | import java.io.*; |
| 4 | import java.util.*; |
| 5 | import java.util.jar.*; |
| 6 | |
| 7 | import aQute.bnd.build.*; |
| 8 | import aQute.bnd.resolver.Resource.Requirement; |
| 9 | import aQute.lib.collections.*; |
| 10 | import aQute.lib.osgi.*; |
| 11 | import aQute.libg.generics.*; |
| 12 | |
| 13 | public class Resolver extends Processor { |
| 14 | |
| 15 | final Set<Resource> resources = new HashSet<Resource>(); |
| 16 | private Map<Resource.Requirement, Set<Resource>> cache = new IdentityHashMap<Resource.Requirement, Set<Resource>>(); |
| 17 | |
| 18 | public void add(Container c) throws Exception { |
| 19 | List<File> files = new ArrayList<File>(); |
| 20 | c.contributeFiles(files, this); |
| 21 | for (File f : files) { |
| 22 | add(f); |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | public void addAll(Collection<Container> containers) throws Exception { |
| 27 | for (Container c : containers) { |
| 28 | add(c); |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | public Resolution resolve() throws Exception { |
| 33 | // Split fragments and bundles |
| 34 | Set<Resource> active = new HashSet<Resource>(); |
| 35 | Set<Resource> fragments = new HashSet<Resource>(); |
| 36 | Set<Resource> singletons = new HashSet<Resource>(); |
| 37 | |
| 38 | for (Resource r : resources) { |
| 39 | if (r.fragments != null) { |
| 40 | active.add(r); |
| 41 | if (r.singleton) |
| 42 | singletons.add(r); |
| 43 | } else |
| 44 | fragments.add(r); |
| 45 | } |
| 46 | |
| 47 | // Attach fragments |
| 48 | for (Resource r : fragments) { |
| 49 | Collection<Resource> hosts = find(active, r.requirements, new HashSet<Resource>()); |
| 50 | for (Resource host : hosts) { |
| 51 | host.fragments.add(host); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | // Create a list of all the requirements |
| 56 | Set<Resource.Requirement> reqs = new HashSet<Resource.Requirement>(); |
| 57 | for (Resource r : active) { |
| 58 | reqs.addAll(r.requirements); |
| 59 | // And its attached fragments |
| 60 | for (Resource f : r.fragments) { |
| 61 | reqs.addAll(f.requirements); |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | Set<Resource.Requirement> optional = Create.set(); |
| 66 | Set<Resource.Requirement> unresolved = Create.set(); |
| 67 | Map<Resource.Requirement, Resource> unique = Create.map(); |
| 68 | MultiMap<Requirement, Resource> multiple = new MultiMap<Requirement, Resource>(); |
| 69 | |
| 70 | for (Resource.Requirement req : reqs) { |
| 71 | Collection<Resource> solutions = find(active, req, new HashSet<Resource>()); |
| 72 | if (solutions.isEmpty()) { |
| 73 | if (req.optional) |
| 74 | optional.add(req); |
| 75 | else |
| 76 | unresolved.add(req); |
| 77 | } else if (solutions.size() == 1) |
| 78 | unique.put(req, solutions.iterator().next()); |
| 79 | else { |
| 80 | multiple.addAll(req, solutions); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | // If we have unresolveds, tough shit |
| 85 | |
| 86 | if (!unresolved.isEmpty()) { |
| 87 | for (Requirement r : unresolved) { |
| 88 | error("Unresolved %s", r); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | // Calculate our singleton candidates |
| 93 | MultiMap<String, Resource> picked = new MultiMap<String, Resource>(); |
| 94 | for (Resource r : singletons) { |
| 95 | picked.add(r.bsn, r); |
| 96 | } |
| 97 | |
| 98 | // Remove any singletons that are alone |
| 99 | // and verify that if there are multiple they are not |
| 100 | // both in the unique solutions |
| 101 | for (Iterator<Map.Entry<String, Set<Resource>>> i = picked.entrySet().iterator(); i |
| 102 | .hasNext();) { |
| 103 | Map.Entry<String, Set<Resource>> entry = i.next(); |
| 104 | if (entry.getValue().size() == 1) |
| 105 | i.remove(); |
| 106 | else { |
| 107 | Set<Resource> x = new HashSet<Resource>(entry.getValue()); |
| 108 | boolean changed = x.retainAll(unique.values()); |
| 109 | if (x.size() > 1) { |
| 110 | // We need multiple singleton bundles with the same bsn |
| 111 | error("Singleton conflict: %s", x); |
| 112 | } else if (changed) { |
| 113 | Set<Resource> delta = new HashSet<Resource>(entry.getValue()); |
| 114 | delta.removeAll(x); |
| 115 | |
| 116 | // We've removed bundles from the possible solutions |
| 117 | for (Iterator<Resource> it = multiple.all(); i.hasNext();) { |
| 118 | Resource r = it.next(); |
| 119 | if (delta.contains(r)) { |
| 120 | it.remove(); |
| 121 | } |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | Resolution res = new Resolution(); |
| 128 | res.multiple = multiple; |
| 129 | res.unique = unique; |
| 130 | res.unresolved = unresolved; |
| 131 | return res; |
| 132 | } |
| 133 | |
| 134 | private Collection<Resource> find(Set<Resource> active, Set<Resource.Requirement> requirements, |
| 135 | Set<Resource> result) { |
| 136 | for (Resource.Requirement req : requirements) { |
| 137 | Set<Resource> resources = cache.get(req); |
| 138 | if (resources != null) { |
| 139 | result.addAll(resources); |
| 140 | } else { |
| 141 | resources = find(active, req, new HashSet<Resource>()); |
| 142 | cache.put(req, resources); |
| 143 | result.addAll(resources); |
| 144 | } |
| 145 | } |
| 146 | return result; |
| 147 | } |
| 148 | |
| 149 | private Set<Resource> find(Set<Resource> active, Requirement req, Set<Resource> result) { |
| 150 | for (Resource r : active) { |
| 151 | for (Resource.Capability cap : r.capabilities) { |
| 152 | if ( cap.name.equals(req.name)) |
| 153 | System.out.println("Yes"); |
| 154 | if (req.matches(cap)) |
| 155 | result.add(r); |
| 156 | } |
| 157 | } |
| 158 | return result; |
| 159 | } |
| 160 | |
| 161 | public void add(File file) throws IOException { |
| 162 | JarFile jf = new JarFile(file); |
| 163 | try { |
| 164 | Manifest m = jf.getManifest(); |
| 165 | Resource r = new Resource(this, m); |
| 166 | resources.add(r); |
| 167 | } finally { |
| 168 | jf.close(); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | } |