Added: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/SourceMain.java URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/SourceMain.java?rev=807795&view=auto ============================================================================== --- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/SourceMain.java (added) +++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/SourceMain.java Tue Aug 25 20:30:33 2009 @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.felix.resolver.manifestparser; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.jar.JarFile; + +public class SourceMain +{ + public static void main(String[] args) throws IOException, Exception + { + if (args.length != 1) + { + System.err.println("usage: <bundle-dir>"); + System.exit(0); + } + + // Look in the specified bundle directory to create a list + // of all JAR files to install. + File[] files = new File(args[0]).listFiles(); + List<File> jarList = new ArrayList(); + if (files != null) + { + Arrays.sort(files); + for (int i = 0; i < files.length; i++) + { + if (files[i].getName().endsWith(".jar")) + { + jarList.add(files[i]); + } + } + } + + generateHeaderSource(System.out); + + int count = 0; + for (int jarIdx = 0; jarIdx < jarList.size(); jarIdx++) + { + JarFile jarFile = new JarFile(jarList.get(jarIdx)); + try + { + Map headerMap = new StringMap(jarFile.getManifest().getMainAttributes(), false); + if (headerMap.containsKey("Bundle-SymbolicName")) + { + count++; + ManifestParser mp = new ManifestParser(headerMap); + generateModuleSource(System.out, mp); + } + } + finally + { + jarFile.close(); + } + } + + generateFooterSource(System.out); + + System.out.println("\nGenerated " + count + " modules."); + } + + private static void generateHeaderSource(PrintStream out) + { + out.println(" private static Module scenario(List<Module> moduleList)"); + out.println(" {"); + out.println(" Module target;"); + } + + private static void generateFooterSource(PrintStream out) + { + out.println(""); + out.println(" return target;"); + out.println(" }"); + } + + private static void generateModuleSource(PrintStream out, ManifestParser mp) + { + out.println(""); + out.println(" // Bundle " + mp.getSymbolicName()); + out.println(" moduleList.add("); + out.println(" new Module(\"" + mp.getSymbolicName() + "\")"); + + generateCapabilitiesSource(out, mp.getCapabilities()); + generateRequirementsSource(out, mp.getRequirements()); + + out.println(" );"); + } + + private static void generateCapabilitiesSource(PrintStream out, Capability[] caps) + { + for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++) + { + if (caps[capIdx].getNamespace().equals(Capability.MODULE_NAMESPACE) + || caps[capIdx].getNamespace().equals(Capability.HOST_NAMESPACE)) + { + continue; + } + else if (!caps[capIdx].getNamespace().equals(Capability.PACKAGE_NAMESPACE)) + { + System.out.println("Unsupported capability: " + caps[capIdx].getNamespace()); + System.exit(0); + } + out.print(" .exporting(new ExportedPackage(\"" + caps[capIdx].getPackageName() + "\")"); + + R4Directive[] dirs = caps[capIdx].getDirectives(); + for (int dirIdx = 0; (dirs != null) && (dirIdx < dirs.length); dirIdx++) + { + if (dirs[dirIdx].getName().equals(Constants.INCLUDE_DIRECTIVE) + || dirs[dirIdx].getName().equals(Constants.MANDATORY_DIRECTIVE)) + { + continue; + } + else if (!dirs[dirIdx].getName().equals(Constants.USES_DIRECTIVE)) + { + System.out.println("\nUnsupported capability directive: " + dirs[dirIdx].getName()); + System.exit(0); + } + out.print(".using(\"" + dirs[dirIdx].getValue() + "\")"); + } + + R4Attribute[] attrs = caps[capIdx].getAttributes(); + for (int attrIdx = 0; (attrs != null) && (attrIdx < attrs.length); attrIdx++) + { + if (attrs[attrIdx].getName().equals(Capability.PACKAGE_PROPERTY)) + { + continue; + } + if (attrs[attrIdx].isMandatory()) + { + out.print(".withMandatory(\"" + attrs[attrIdx].getName() + "=" + attrs[attrIdx].getValue() + "\")"); + } + else + { + out.print(".with(\"" + attrs[attrIdx].getName() + "=" + attrs[attrIdx].getValue() + "\")"); + } + } + + out.println(")"); + } + } + + private static void generateRequirementsSource(PrintStream out, Requirement[] reqs) + { + for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++) + { + if (!reqs[reqIdx].getNamespace().equals(Capability.PACKAGE_NAMESPACE)) + { + // Generate invalid source for now. + out.println("require-bundle"); + continue; + } + else if (!reqs[reqIdx].getNamespace().equals(Capability.PACKAGE_NAMESPACE)) + { + System.out.println("Unsupported requirement: " + reqs[reqIdx].getNamespace()); + System.exit(0); + } + out.print(" .importing(new ImportedPackage(\"" + reqs[reqIdx].getTargetName() + "\")"); + + R4Directive[] dirs = reqs[reqIdx].getDirectives(); + for (int dirIdx = 0; (dirs != null) && (dirIdx < dirs.length); dirIdx++) + { + if (!dirs[dirIdx].getName().equals(Constants.RESOLUTION_DIRECTIVE)) + { + System.out.println("\nUnsupported requirement directive: " + dirs[dirIdx].getName()); + System.exit(0); + } + if (dirs[dirIdx].getValue().equals(Constants.RESOLUTION_OPTIONAL)) + { + out.print(".optional()"); + } + } + + R4Attribute[] attrs = reqs[reqIdx].getAttributes(); + for (int attrIdx = 0; (attrs != null) && (attrIdx < attrs.length); attrIdx++) + { + if (attrs[attrIdx].getName().equals(Capability.PACKAGE_PROPERTY)) + { + continue; + } + else + { + out.print(".with(\"" + attrs[attrIdx].getName() + "=" + attrs[attrIdx].getValue() + "\")"); + } + } + + out.println(")"); + } + } +} \ No newline at end of file
Added: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/StringMap.java URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/StringMap.java?rev=807795&view=auto ============================================================================== --- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/StringMap.java (added) +++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/StringMap.java Tue Aug 25 20:30:33 2009 @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.felix.resolver.manifestparser; + +import java.util.*; + +/** + * Simple utility class that creates a map for string-based keys. + * This map can be set to use case-sensitive or case-insensitive + * comparison when searching for the key. Any keys put into this + * map will be converted to a <tt>String</tt> using the + * <tt>toString()</tt> method, since it is only intended to + * compare strings. +**/ +public class StringMap implements Map +{ + private TreeMap m_map; + + public StringMap() + { + this(true); + } + + public StringMap(boolean caseSensitive) + { + m_map = new TreeMap(new StringComparator(caseSensitive)); + } + + public StringMap(Map map, boolean caseSensitive) + { + this(caseSensitive); + putAll(map); + } + + public boolean isCaseSensitive() + { + return ((StringComparator) m_map.comparator()).isCaseSensitive(); + } + + public void setCaseSensitive(boolean b) + { + if (isCaseSensitive() != b) + { + TreeMap map = new TreeMap(new StringComparator(b)); + map.putAll(m_map); + m_map = map; + } + } + + public int size() + { + return m_map.size(); + } + + public boolean isEmpty() + { + return m_map.isEmpty(); + } + + public boolean containsKey(Object arg0) + { + return m_map.containsKey(arg0); + } + + public boolean containsValue(Object arg0) + { + return m_map.containsValue(arg0); + } + + public Object get(Object arg0) + { + return m_map.get(arg0); + } + + public Object put(Object key, Object value) + { + return m_map.put(key.toString(), value); + } + + public void putAll(Map map) + { + for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry entry = (Map.Entry) it.next(); + put(entry.getKey(), entry.getValue()); + } + } + + public Object remove(Object arg0) + { + return m_map.remove(arg0); + } + + public void clear() + { + m_map.clear(); + } + + public Set keySet() + { + return m_map.keySet(); + } + + public Collection values() + { + return m_map.values(); + } + + public Set entrySet() + { + return m_map.entrySet(); + } + + public String toString() + { + return m_map.toString(); + } + + private static class StringComparator implements Comparator + { + private final boolean m_isCaseSensitive; + + public StringComparator(boolean b) + { + m_isCaseSensitive = b; + } + + public int compare(Object o1, Object o2) + { + if (m_isCaseSensitive) + { + return o1.toString().compareTo(o2.toString()); + } + else + { + return o1.toString().compareToIgnoreCase(o2.toString()); + } + } + + public boolean isCaseSensitive() + { + return m_isCaseSensitive; + } + } +} \ No newline at end of file Added: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Util.java URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Util.java?rev=807795&view=auto ============================================================================== --- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Util.java (added) +++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/manifestparser/Util.java Tue Aug 25 20:30:33 2009 @@ -0,0 +1,207 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.felix.resolver.manifestparser; + + +import java.util.ArrayList; +import java.util.List; + +public class Util +{ + public static String getClassName(String className) + { + if (className == null) + { + className = ""; + } + return (className.lastIndexOf('.') < 0) + ? "" : className.substring(className.lastIndexOf('.') + 1); + } + + public static String getClassPackage(String className) + { + if (className == null) + { + className = ""; + } + return (className.lastIndexOf('.') < 0) + ? "" : className.substring(0, className.lastIndexOf('.')); + } + + public static String getResourcePackage(String resource) + { + if (resource == null) + { + resource = ""; + } + // NOTE: The package of a resource is tricky to determine since + // resources do not follow the same naming conventions as classes. + // This code is pessimistic and assumes that the package of a + // resource is everything up to the last '/' character. By making + // this choice, it will not be possible to load resources from + // imports using relative resource names. For example, if a + // bundle exports "foo" and an importer of "foo" tries to load + // "/foo/bar/myresource.txt", this will not be found in the exporter + // because the following algorithm assumes the package name is + // "foo.bar", not just "foo". This only affects imported resources, + // local resources will work as expected. + String pkgName = (resource.startsWith("/")) ? resource.substring(1) : resource; + pkgName = (pkgName.lastIndexOf('/') < 0) + ? "" : pkgName.substring(0, pkgName.lastIndexOf('/')); + pkgName = pkgName.replace('/', '.'); + return pkgName; + } + + // + // The following substring-related code was lifted and modified + // from the LDAP parser code. + // + + public static String[] parseSubstring(String target) + { + List pieces = new ArrayList(); + StringBuffer ss = new StringBuffer(); + // int kind = SIMPLE; // assume until proven otherwise + boolean wasStar = false; // indicates last piece was a star + boolean leftstar = false; // track if the initial piece is a star + boolean rightstar = false; // track if the final piece is a star + + int idx = 0; + + // We assume (sub)strings can contain leading and trailing blanks +loop: for (;;) + { + if (idx >= target.length()) + { + if (wasStar) + { + // insert last piece as "" to handle trailing star + rightstar = true; + } + else + { + pieces.add(ss.toString()); + // accumulate the last piece + // note that in the case of + // (cn=); this might be + // the string "" (!=null) + } + ss.setLength(0); + break loop; + } + + char c = target.charAt(idx++); + if (c == '*') + { + if (wasStar) + { + // encountered two successive stars; + // I assume this is illegal + throw new IllegalArgumentException("Invalid filter string: " + target); + } + if (ss.length() > 0) + { + pieces.add(ss.toString()); // accumulate the pieces + // between '*' occurrences + } + ss.setLength(0); + // if this is a leading star, then track it + if (pieces.size() == 0) + { + leftstar = true; + } + ss.setLength(0); + wasStar = true; + } + else + { + wasStar = false; + ss.append(c); + } + } + if (leftstar || rightstar || pieces.size() > 1) + { + // insert leading and/or trailing "" to anchor ends + if (rightstar) + { + pieces.add(""); + } + if (leftstar) + { + pieces.add(0, ""); + } + } + return (String[]) pieces.toArray(new String[pieces.size()]); + } + + public static boolean checkSubstring(String[] pieces, String s) + { + // Walk the pieces to match the string + // There are implicit stars between each piece, + // and the first and last pieces might be "" to anchor the match. + // assert (pieces.length > 1) + // minimal case is <string>*<string> + + boolean result = false; + int len = pieces.length; + +loop: for (int i = 0; i < len; i++) + { + String piece = pieces[i]; + int index = 0; + if (i == len - 1) + { + // this is the last piece + if (s.endsWith(piece)) + { + result = true; + } + else + { + result = false; + } + break loop; + } + // initial non-star; assert index == 0 + else if (i == 0) + { + if (!s.startsWith(piece)) + { + result = false; + break loop; + } + } + // assert i > 0 && i < len-1 + else + { + // Sure wish stringbuffer supported e.g. indexOf + index = s.indexOf(piece, index); + if (index < 0) + { + result = false; + break loop; + } + } + // start beyond the matching piece + index += piece.length(); + } + + return result; + } +} \ No newline at end of file Added: felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/prototype/ProtoResolver.java URL: http://svn.apache.org/viewvc/felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/prototype/ProtoResolver.java?rev=807795&view=auto ============================================================================== --- felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/prototype/ProtoResolver.java (added) +++ felix/sandbox/rickhall/resolver/src/main/java/org/apache/felix/resolver/prototype/ProtoResolver.java Tue Aug 25 20:30:33 2009 @@ -0,0 +1,578 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.felix.resolver.prototype; + +import org.apache.felix.resolver.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class ProtoResolver implements Resolver +{ + private final List<Module> m_moduleList; + + public ProtoResolver(List<Module> moduleList) + { + m_moduleList = moduleList; + } + + public Module getModule(String name) + { + for (int modIdx = 0; modIdx < m_moduleList.size(); modIdx++) + { + if (m_moduleList.get(modIdx).getName().equals(name)) + { + return m_moduleList.get(modIdx); + } + } + return null; + } + + private Module m_rootModule = null; + private List<Map<ImportedPackage, List<Module>>> m_candidatePermutations + = new ArrayList<Map<ImportedPackage, List<Module>>>(); + + public Map<Module, List<Wire>> resolve(Module module) + { +System.out.println("+++ PROTO RESOLVER"); + Map<ImportedPackage, List<Module>> candidateMap = resolve( + module, + null, + module, + new HashMap<ImportedPackage, List<Module>>(), + new HashMap<String, Blame>(), + new HashMap<Module, Boolean>()); + + return populateWireMap(module, candidateMap, new HashMap<Module, List<Wire>>()); + } + + private Map<ImportedPackage, List<Module>> resolve( + Module module, ImportedPackage ipGoal, Module blameModule, + Map<ImportedPackage, List<Module>> candidateMap, + Map<String, Blame> existingConstraints, + Map<Module, Boolean> cycleMap) + { + try + { + // If module is already resolved, then we are done. + if (!module.isResolved()) + { + try + { + // If the cycle map already contains the module, then verify + // consistency again and then just return. + if (cycleMap.containsKey(module)) + { + // Make sure any merged constraints do not conflict with passed in constraints. + if (cycleMap.get(module).booleanValue()) + { + Map<String, Blame> currentConstraints = + calculatePackageConstraints(module, blameModule); + checkConsistency(module, ipGoal, candidateMap, + existingConstraints, currentConstraints); + } + return candidateMap; + } + cycleMap.put(module, Boolean.FALSE); + + // Keep track of the starting module. + if (m_rootModule == null) + { + m_rootModule = module; + m_candidatePermutations.clear(); + } + + // Find candidates for all imports for the target module. + List<ImportedPackage> imports = module.getImports(); + for (int impIdx = 0; impIdx < imports.size(); impIdx++) + { + // If we are using a permutated candidate map, then the target + // module's candidates may have already been calculated, so use + // those instead, otherwise find the matching providers. + if (candidateMap.get(imports.get(impIdx)) == null) + { + List<Module> exporters = findExporters(imports.get(impIdx)); + if (exporters.size() == 0) + { + throw new RuntimeException("Unable to resolve " + module + + ": missing requirement " + imports.get(impIdx).getName()); + } + candidateMap.put(imports.get(impIdx), exporters); + } + } + + // Calculate current package constraints for the target module. + Map<String, Blame> currentConstraints = + calculatePackageConstraints(module, blameModule); + // Make copy of current package constraints, since we may need + // to revert if a candidate resolve fails. + Map <String, Blame> currentConstraintsOrig = + new HashMap<String, Blame>(currentConstraints); + + // Verify current candidates are resolvable and consistent. + boolean repeat; + do + { + do + { + repeat = false; + + // Loop through all of the target module's imports and v + for (int impIdx = 0; impIdx < imports.size(); impIdx++) + { + // Get the current candidate for the import. + List<Module> exporters = candidateMap.get(imports.get(impIdx)); + // If the current candidate is already resolved, then try + // to merge packages while verifying constraints. + if (exporters.get(0).isResolved()) + { + // HERE WE WILL NEED TO TRY TO MERGE THE CANDIDATE. + } + // If the current candidate is not resolved, then try to resolve + // it, which will also merge packages while verify constraints. + else if (!exporters.get(0).isResolved()) + { + try + { + resolve( + exporters.get(0), + imports.get(impIdx), + module, + candidateMap, + currentConstraints, + cycleMap); + } + // If we have a constraint conflict, then the current candidate + // should be removed, since it conflicts with an existing choice. + // If we are at the root, we should try the next permutated + // candidate map if possible. + catch (ResolverConflictException ex) + { +//System.out.println("RCE " + ex); +//System.out.println("Current candidate map : " + candidateMap); + // Remove offending candidate. + exporters.remove(0); +//System.out.println("Updated candidate map : " + candidateMap); + if (exporters.size() == 0) + { + // TODO: PROTO RESOLVER - Maybe this should be moved. + if ((module == m_rootModule) && (m_candidatePermutations.size() > 0)) + { + currentConstraints.clear(); + currentConstraints.putAll(currentConstraintsOrig); + candidateMap = m_candidatePermutations.remove(0); +System.out.println("+++ TRYING ALTERNATIVE: " + candidateMap); + repeat = true; + } + else + { + candidateMap.remove(imports.get(impIdx)); + throw new ResolveException("Unresolved constraint " + + imports.get(impIdx) + + " in " + module); + } + } + else + { + repeat = true; + } + break; + } + // If we cannot resolve the candidate, then the current candidate + // should be removed. If we are at the root, we should try the + // next permutated candidate map if possible. + catch (ResolveException ex) + { +System.out.println("RE " + ex); +//System.out.println("Current candidate map : " + candidateMap); + // Remove offending candidate. + exporters.remove(0); +//System.out.println("Updated candidate map : " + candidateMap); + if (exporters.size() == 0) + { + // TODO: PROTO RESOLVER - Maybe this should be moved. + if ((module == m_rootModule) && (m_candidatePermutations.size() > 0)) + { + currentConstraints.clear(); + currentConstraints.putAll(currentConstraintsOrig); + candidateMap = m_candidatePermutations.remove(0); +System.out.println("+++ TRYING ALTERNATIVE: " + candidateMap); + repeat = true; + } + else + { + candidateMap.remove(imports.get(impIdx)); + throw new ResolveException("Unresolved constraint " + + imports.get(impIdx) + + " in " + module); + } + } + else + { + repeat = true; + } + break; + } + } + } + } + while (repeat); + } + while (repeat); + + // Make sure any candidate constraints merged with the current constraints + // do not conflict with existing in constraints. + checkConsistency(module, ipGoal, candidateMap, existingConstraints, currentConstraints); + + // Merge current constraints with existing constraints. + for (Iterator<Entry<String, Blame>> it = currentConstraints.entrySet().iterator(); + it.hasNext(); ) + { + Entry<String, Blame> entry = it.next(); + if ((ipGoal == null) || ipGoal.isSatistfiedBy(entry.getValue().m_exportedPackage)) + { + Blame blame = existingConstraints.get(entry.getKey()); + if ((blame != null) + && !blame.m_blameModules.contains(entry.getValue().m_blameModules.get(0))) + { + blame.m_blameModules.add(entry.getValue().m_blameModules.get(0)); + } + else if (blame == null) + { + existingConstraints.put(entry.getKey(), entry.getValue()); + } + mergeUses(entry.getValue().m_exportedPackage, + existingConstraints, currentConstraints, + new HashMap<ExportedPackage, ExportedPackage>()); + } + } + } + catch (RuntimeException ex) + { + // Any exception should remove the target module from the cycle map. + cycleMap.remove(module); + throw ex; + } + } + + cycleMap.put(module, Boolean.TRUE); + + return candidateMap; + } + finally + { + // If we are done with the root target module, then forget about it. + if (module == m_rootModule) + { + m_rootModule = null; + } + } + } + + private List<Module> findExporters(ImportedPackage ip) + { + List<Module> exporters = new ArrayList<Module>(); + for (int modIdx = 0; modIdx < m_moduleList.size(); modIdx++) + { + List<ExportedPackage> exports = m_moduleList.get(modIdx).getExports(); + for (int expIdx = 0; expIdx < exports.size(); expIdx++) + { + if (ip.isSatistfiedBy(exports.get(expIdx))) + { + exporters.add(m_moduleList.get(modIdx)); + break; + } + } + } + return exporters; + } + + private static Map<String, Blame> calculatePackageConstraints( + Module module, Module blameModule) + { + Map<String, Blame> pkgMap = new HashMap<String, Blame>(); + List<ExportedPackage> exports = module.getExports(); + for (int i = 0; i < exports.size(); i++) + { + // TODO: PROTO RESOLVER - Assume if a module imports the same package it + // exports that the import will overlap the export. + if (!hasOverlappingImport(module, exports.get(i))) + { + pkgMap.put(exports.get(i).getName(), new Blame(module, exports.get(i), blameModule)); + } + } + return pkgMap; + } + + private static boolean hasOverlappingImport(Module module, ExportedPackage ep) + { + List<ImportedPackage> imports = module.getImports(); + for (int i = 0; i < imports.size(); i++) + { + if (imports.get(i).getName().equals(ep.getName())) + { + return true; + } + } + return false; + } + + private void checkConsistency( + Module targetModule, ImportedPackage ipGoal, Map<ImportedPackage, List<Module>> candidateMap, + Map<String, Blame> existingConstraints, Map<String, Blame> currentConstraints) + + throws ResolverConflictException + { +//System.out.println("Goal: " + ipGoal); +//System.out.println("Candidate map: " + candidateMap); +//System.out.println(targetModule + " current : " + currentConstraints); +//System.out.println(spaces(targetModule.toString()) + " existing : " + existingConstraints); + // Find matching providing for requirement. + for (Iterator<Entry<String, Blame>> it = currentConstraints.entrySet().iterator(); it.hasNext(); ) + { + Entry<String, Blame> entry = it.next(); + Blame current = entry.getValue(); + if ((ipGoal == null) || ipGoal.isSatistfiedBy(current.m_exportedPackage)) + { + verifyUses(targetModule, entry.getKey(), + candidateMap, existingConstraints, currentConstraints, + new HashMap<Module, Module>()); + } + } + +//System.out.println(spaces(targetModule.toString()) + " -CONSISTENT-"); + } + + private void verifyUses(Module targetModule, String pkgName, + Map<ImportedPackage, List<Module>> candidateMap, + Map<String, Blame> existingConstraints, + Map<String, Blame> currentConstraints, Map<Module, Module> cycleMap) + { +// TODO: PROTO RESOLVER - Without this we get into infinite loop with GF modules. +// With it, we do not resolve scenario5 correctly. +// if (cycleMap.containsKey(targetModule)) +// { +// return; +// } +// cycleMap.put(targetModule, targetModule); + + // Check that the uses constraints implied by this package are + // consistent with the existing constraints. + Blame current = currentConstraints.get(pkgName); + Blame existing = existingConstraints.get(pkgName); + + // If there is no current constraint, then just return + // since it cannot impact anything. + if (current == null) + { + return; + } + + if ((existing != null) + && !existing.m_provider.equals(current.m_provider)) + { + Map<ImportedPackage, List<Module>> candidateMapCopy = copyCandidateMap(candidateMap); + boolean modified = false; + boolean invalid = false; + for (int blameIdx = 0; blameIdx < existing.m_blameModules.size(); blameIdx++) + { + List<ImportedPackage> blameImports = existing.m_blameModules.get(blameIdx).getImports(); + for (int impIdx = 0; impIdx < blameImports.size(); impIdx++) + { + // TODO: PROTO RESOLVER - Not efficient at all. + // TODO: PROTO RESOLVER - This comment out part is too narrow, since it could + // also check for other candidates that imply the conficting package. + // Works for scenario 9, but it fails for scenario 1. +// if (blameImports.get(impIdx).getName().equals(pkgName) +// && candidateMapCopy.get(blameImports.get(impIdx)).contains(existing.m_provider)) + // TODO: PROTO RESOLVER - This comment out part is too broad, since it removes + // the conflicting module from all candidate lists. + // Works for scenario 1, but fails for scenario 9. + if (candidateMapCopy.get(blameImports.get(impIdx)).contains(existing.m_provider)) + { + candidateMapCopy.get(blameImports.get(impIdx)).remove(existing.m_provider); + modified = true; + if (candidateMapCopy.get(blameImports.get(impIdx)).size() == 0) + { + invalid = true; + } + } + } + } + if (invalid) + { +//System.out.println("Invalid permutated candidate map: " + candidateMapCopy); + } + else if (modified) + { +//System.out.println("Permutated candidate map: " + candidateMapCopy); + m_candidatePermutations.add(candidateMapCopy); + } + else + { +//System.out.println("Permutated candidate map: Unchanged, so ignoring."); + } +// TODO: PROTO RESOLVER - We could perhaps check to see if the candidate permutation +// is even viable, e.g., the conflicting candidate may be the only candidate +// so the resulting permutation may not be resolveable. + throw new ResolverConflictException("Unable to resolve " + + targetModule + ": constraint conflict with '" + + pkgName + "' between " + current + " and " + + existing); + } + + if (current != null) + { + List<String> uses = current.m_exportedPackage.getUses(); + for (int usesIdx = 0; usesIdx < uses.size(); usesIdx++) + { + verifyUses(targetModule, uses.get(usesIdx), candidateMap, + existingConstraints, currentConstraints, cycleMap); + } + } + } + + private static Map<ImportedPackage, List<Module>> copyCandidateMap( + Map<ImportedPackage, List<Module>> candidateMap) + { + Map<ImportedPackage, List<Module>> copy = new HashMap<ImportedPackage, List<Module>>(); + for (Iterator<Entry<ImportedPackage, List<Module>>> it = candidateMap.entrySet().iterator(); + it.hasNext(); ) + { + Entry<ImportedPackage, List<Module>> entry = it.next(); + copy.put(entry.getKey(), new ArrayList(entry.getValue())); + } + return copy; + } + + private static void mergeUses(ExportedPackage ep, Map<String, Blame> existingConstraints, + Map<String, Blame> currentConstraints, Map<ExportedPackage, ExportedPackage> cycleMap) + { + if (cycleMap.containsKey(ep)) + { + return; + } + cycleMap.put(ep, ep); + + if (ep.getUses().size() > 0) + { + for (Iterator<Entry<String, Blame>> it = currentConstraints.entrySet().iterator(); + it.hasNext(); ) + { + Entry<String, Blame> entry = it.next(); + for (int usesIdx = 0; usesIdx < ep.getUses().size(); usesIdx++) + { + if (entry.getKey().equals(ep.getUses().get(usesIdx))) + { + Blame blame = existingConstraints.get(entry.getKey()); + if ((blame != null) + && !blame.m_blameModules.contains(entry.getValue().m_blameModules.get(0))) + { + blame.m_blameModules.add(entry.getValue().m_blameModules.get(0)); + } + else if (blame == null) + { + existingConstraints.put(entry.getKey(), entry.getValue()); + } + mergeUses(entry.getValue().m_exportedPackage, + existingConstraints, currentConstraints, + cycleMap); + } + } + } + } + } + + private static Map<Module, List<Wire>> populateWireMap( + Module module, Map<ImportedPackage, List<Module>> candidateMap, + Map<Module, List<Wire>> wireMap) + { + if (wireMap.get(module) == null) + { + List<Wire> moduleWires = new ArrayList<Wire>(); + wireMap.put(module, moduleWires); + + List<ImportedPackage> imports = module.getImports(); + for (int i = 0; i < imports.size(); i++) + { + Module provider = candidateMap.get(imports.get(i)).get(0); + if (!provider.isResolved()) + { + populateWireMap(candidateMap.get(imports.get(i)).get(0), candidateMap, wireMap); + } + + // Ignore modules that import themselves. + if (!module.equals(provider)) + { + moduleWires.add( + new Wire(module, + imports.get(i), + provider, + getMatchingExport(provider, imports.get(i)))); + } + } + } + return wireMap; + } + + private static ExportedPackage getMatchingExport(Module exporter, ImportedPackage ip) + { + List<ExportedPackage> exports = exporter.getExports(); + for (int i = 0; i < exports.size(); i++) + { + if (ip.isSatistfiedBy(exports.get(i))) + { + return exports.get(i); + } + } + return null; + } + + public static class Blame + { + public final Module m_provider; + public final ExportedPackage m_exportedPackage; + public final List<Module> m_blameModules; + public Blame(Module provider, ExportedPackage exportedPackage, Module blameModule) + { + m_provider = provider; + m_exportedPackage = exportedPackage; + m_blameModules = new ArrayList<Module>(); + m_blameModules.add(blameModule); + } + + public String toString() + { + return m_provider + " {Blamed on " + m_blameModules + "}"; + } + } + + private static String spaces(String s) + { + StringBuffer sb = new StringBuffer(s.length()); + for (int i = 0; i < s.length(); i++) + { + sb.append(' '); + } + return sb.toString(); + } +} \ No newline at end of file
