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