return;
}
Packages pkgs = resourcePkgMap.get(resource);
ResolutionException rethrow = null;
Candidates permutation = null;
Set<Requirement> mutated = null;
List<Candidates> importPermutations = session.getImportPermutations();
List<Candidates> usesPermutations = session.getUsesPermutations();
// Check for conflicting imports from fragments.
// TODO: Is this only needed for imports or are generic and bundle requirements also needed?
// I think this is only a special case for fragment imports because they can overlap
// host imports, which is not allowed in normal metadata.
for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
{
if (entry.getValue().size() > 1)
{
Blame sourceBlame = null;
for (Blame blame : entry.getValue())
{
if (sourceBlame == null)
{
sourceBlame = blame;
}
else if (!sourceBlame.m_cap.getResource().equals(blame.m_cap.getResource()))
{
// Try to permutate the conflicting requirement.
permutate(allCandidates, blame.m_reqs.get(0), importPermutations);
// Try to permutate the source requirement.
permutate(allCandidates, sourceBlame.m_reqs.get(0), importPermutations);
// Report conflict.
ResolutionException ex = new ResolutionException(
"Uses constraint violation. Unable to resolve resource "
+ Util.getSymbolicName(resource)
+ " [" + resource
+ "] because it is exposed to package '"
+ entry.getKey()
+ "' from resources "
+ Util.getSymbolicName(sourceBlame.m_cap.getResource())
+ " [" + sourceBlame.m_cap.getResource()
+ "] and "
+ Util.getSymbolicName(blame.m_cap.getResource())
+ " [" + blame.m_cap.getResource()
+ "] via two dependency chains.\n\nChain 1:\n"
+ toStringBlame(session.getContext(), allCandidates, sourceBlame)
+ "\n\nChain 2:\n"
+ toStringBlame(session.getContext(), allCandidates, blame),
null,
Collections.singleton(blame.m_reqs.get(0)));
m_logger.log(
Logger.LOG_DEBUG,
"Candidate permutation failed due to a conflict with a "
+ "fragment import; will try another if possible.",
ex);
throw ex;
}
}
}
}
// Check if there are any uses conflicts with exported packages.
for (Entry<String, Blame> entry : pkgs.m_exportedPkgs.entrySet())
{
String pkgName = entry.getKey();
Blame exportBlame = entry.getValue();
if (!pkgs.m_usedPkgs.containsKey(pkgName))
{
continue;
}
for (UsedBlames usedBlames : pkgs.m_usedPkgs.get(pkgName))
{
if (!isCompatible(session, Collections.singletonList(exportBlame), usedBlames.m_cap, resourcePkgMap))
{
for (Blame usedBlame : usedBlames.m_blames)
{
if (checkMultiple(session, usedBlames, usedBlame, allCandidates))
{
// Continue to the next usedBlame, if possible we
// removed the conflicting candidates.
continue;
}
// Create a candidate permutation that eliminates all candidates
// that conflict with existing selected candidates.
permutation = (permutation != null)
? permutation
: allCandidates.copy();
rethrow = (rethrow != null)
? rethrow
: new ResolutionException(
"Uses constraint violation. Unable to resolve resource "
+ Util.getSymbolicName(resource)
+ " [" + resource
+ "] because it exports package '"
+ pkgName
+ "' and is also exposed to it from resource "
+ Util.getSymbolicName(usedBlame.m_cap.getResource())
+ " [" + usedBlame.m_cap.getResource()
+ "] via the following dependency chain:\n\n"
+ toStringBlame(session.getContext(), allCandidates, usedBlame),
null,
null);
mutated = (mutated != null)
? mutated
: new HashSet<Requirement>();
for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
{
Requirement req = usedBlame.m_reqs.get(reqIdx);
// Sanity check for multiple.
if (Util.isMultiple(req))
{
continue;
}
// If we've already permutated this requirement in another
// uses constraint, don't permutate it again just continue
// with the next uses constraint.
if (mutated.contains(req))
{
break;
}
// See if we can permutate the candidates for blamed
// requirement; there may be no candidates if the resource
// associated with the requirement is already resolved.
List<Capability> candidates = permutation.getCandidates(req);
if ((candidates != null) && (candidates.size() > 1 || Util.isOptional(req)))
{
mutated.add(req);
// Remove the conflicting candidate.
candidates.remove(0);
if (candidates.isEmpty())
{
permutation.clearCandidates(req);
}
// Continue with the next uses constraint.
break;
}
}
}
}
}
if (rethrow != null)
{
if (!mutated.isEmpty())
{
usesPermutations.add(permutation);
}
m_logger.log(
Logger.LOG_DEBUG,
"Candidate permutation failed due to a conflict between "
+ "an export and import; will try another if possible.",
rethrow);
throw rethrow;
}
}
// Check if there are any uses conflicts with imported and required packages.
// We combine the imported and required packages here into one map.
// Imported packages are added after required packages because they shadow or override
// the packages from required bundles.
Map<String, List<Blame>> allImportRequirePkgs =
new HashMap<String, List<Blame>>(pkgs.m_requiredPkgs);
allImportRequirePkgs.putAll(pkgs.m_importedPkgs);
for (Entry<String, List<Blame>> requirementBlames : allImportRequirePkgs.entrySet())
{
String pkgName = requirementBlames.getKey();
if (!pkgs.m_usedPkgs.containsKey(pkgName))
{
continue;
}
for (UsedBlames usedBlames : pkgs.m_usedPkgs.get(pkgName))
{
if (!isCompatible(session, requirementBlames.getValue(), usedBlames.m_cap, resourcePkgMap))
{
// Split packages, need to think how to get a good message for split packages (sigh)
// For now we just use the first requirement that brings in the package that conflicts
Blame requirementBlame = requirementBlames.getValue().get(0);
for (Blame usedBlame : usedBlames.m_blames)
{
if (checkMultiple(session, usedBlames, usedBlame, allCandidates))
{
// Continue to the next usedBlame, if possible we
// removed the conflicting candidates.
continue;
}
// Create a candidate permutation that eliminates all candidates
// that conflict with existing selected candidates.
permutation = (permutation != null)
? permutation
: allCandidates.copy();
rethrow = (rethrow != null)
? rethrow
: new ResolutionException(
"Uses constraint violation. Unable to resolve resource "
+ Util.getSymbolicName(resource)
+ " [" + resource
+ "] because it is exposed to package '"
+ pkgName