You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2009/08/25 22:30:34 UTC

svn commit: r807795 [5/5] - in /felix/sandbox/rickhall/resolver: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/felix/ src/main/java/org/apache/felix/resolver/ src/main/java/org/apache/felix/resol...

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