/******************************************************************************* * Copyright (c) 2008 Pierre-Antoine Grégoire. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Pierre-Antoine Grégoire - initial API and implementation *******************************************************************************/ package org.org.eclipse.dws.core.internal; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.org.eclipse.dws.core.internal.model.AbstractChosenArtifactVersion; import org.org.eclipse.dws.core.internal.model.DWSClasspathEntryDescriptor; import org.org.eclipse.dws.core.internal.model.ResolvedArtifact; import org.org.eclipse.dws.core.internal.model.UnresolvedArtifact; import org.org.eclipse.dws.core.internal.model.PomDependency.Scope; import org.org.model.IModelItem; import org.org.model.IModelItemVisitor; import org.org.repository.crawler.maven2.model.ArtifactVersion; /** * The Class DependenciesHelper. */ public class DependenciesHelper { /** * The Class SearchContext. */ public static class SearchContext { /** The deal with transitive. */ private Boolean dealWithTransitive; /** The deal with optional. */ private Boolean dealWithOptional; /** The deal with unknown or restrictive scope. */ private Boolean dealWithUnknownOrRestrictiveScope; /** * Gets the deal with optional. * * @return the deal with optional */ public Boolean getDealWithOptional() { return dealWithOptional; } /** * Sets the deal with optional. * * @param dealWithOptional the new deal with optional */ public void setDealWithOptional(Boolean dealWithOptional) { this.dealWithOptional = dealWithOptional; } /** * Gets the deal with transitive. * * @return the deal with transitive */ public Boolean getDealWithTransitive() { return dealWithTransitive; } /** * Sets the deal with transitive. * * @param dealWithTransitive the new deal with transitive */ public void setDealWithTransitive(Boolean dealWithTransitive) { this.dealWithTransitive = dealWithTransitive; } /** * Gets the deal with unknown or restrictive scope. * * @return the deal with unknown or restrictive scope */ public Boolean getDealWithUnknownOrRestrictiveScope() { return dealWithUnknownOrRestrictiveScope; } /** * Sets the deal with unknown or restrictive scope. * * @param dealWithUnknownOrRestrictiveScope the new deal with unknown or restrictive scope */ public void setDealWithUnknownOrRestrictiveScope(Boolean dealWithUnknownOrRestrictiveScope) { this.dealWithUnknownOrRestrictiveScope = dealWithUnknownOrRestrictiveScope; } } /** * The Class LookForUnresolvedArtifactVisitor. */ private static final class LookForUnresolvedArtifactVisitor implements IModelItemVisitor { /** The has unresolved artifact. */ private Boolean hasUnresolvedArtifact=new Boolean(false); /** * Instantiates a new look for unresolved artifact visitor. */ private LookForUnresolvedArtifactVisitor() { } /* (non-Javadoc) * @see org.org.model.IModelItemVisitor#visit(org.org.model.IModelItem) */ /** * @see org.org.model.IModelItemVisitor#visit(org.org.model.IModelItem) */ @SuppressWarnings("rawtypes") public boolean visit(IModelItem modelItem) { if (modelItem instanceof UnresolvedArtifact) { hasUnresolvedArtifact = true; return false; } return true; } /** * Found unresolved artifact. * * @return true, if successful */ public boolean foundUnresolvedArtifact() { return hasUnresolvedArtifact; } } /** * The Class HarvestConflictingClasspathEntriesVisitor. */ private static class HarvestConflictingClasspathEntriesVisitor implements IModelItemVisitor { /** The result. */ private List<DWSClasspathEntryDescriptor> result; /** * Instantiates a new harvest conflicting classpath entries visitor. */ public HarvestConflictingClasspathEntriesVisitor() { this.result = new LinkedList<DWSClasspathEntryDescriptor>(); } /** * Gets the result. * * @return the result */ public List<DWSClasspathEntryDescriptor> getResult() { return result; } /* (non-Javadoc) * @see org.org.model.IModelItemVisitor#visit(org.org.model.IModelItem) */ /** * @see org.org.model.IModelItemVisitor#visit(org.org.model.IModelItem) */ @SuppressWarnings("rawtypes") public boolean visit(IModelItem modelItem) { if (modelItem instanceof ResolvedArtifact) { ResolvedArtifact resolvedArtifact = (ResolvedArtifact) modelItem; if (resolvedArtifact.hasConflictingClasspathEntries()) { result.addAll(resolvedArtifact.getConflictingClasspathEntries()); } } return true; } } /** * The Class RemoveOptionalAndSkippedVisitor. */ private static class RemoveOptionalAndSkippedVisitor implements IModelItemVisitor { /** The search context. */ private final SearchContext searchContext; /** * Instantiates a new removes the optional and skipped visitor. * * @param searchContext the search context */ public RemoveOptionalAndSkippedVisitor(SearchContext searchContext) { this.searchContext = searchContext; } /* (non-Javadoc) * @see org.org.model.IModelItemVisitor#visit(org.org.model.IModelItem) */ /** * @see org.org.model.IModelItemVisitor#visit(org.org.model.IModelItem) */ @SuppressWarnings("rawtypes") public boolean visit(IModelItem modelItem) { boolean result = true; if (modelItem instanceof AbstractChosenArtifactVersion && modelItem.getParent() != null) { AbstractChosenArtifactVersion chosenArtifactVersion = (AbstractChosenArtifactVersion) modelItem; final boolean optionalLibrary = chosenArtifactVersion.getOptional(); if (chosenArtifactVersion.isSkipped() || (optionalLibrary && !(optionalLibrary && searchContext.getDealWithOptional()))) { chosenArtifactVersion.getParent().removeChild(chosenArtifactVersion.getUID()); result = false; } } return result; } } /** * The Class LookForConflictingClasspathEntriesVisitor. */ private static class LookForConflictingClasspathEntriesVisitor implements IModelItemVisitor { /** The has at least one conflicting classpath entry. */ private Boolean hasAtLeastOneConflictingClasspathEntry; /** * Instantiates a new look for conflicting classpath entries visitor. */ public LookForConflictingClasspathEntriesVisitor() { this.hasAtLeastOneConflictingClasspathEntry = false; } /** * Found at least one conflicting classpath entry. * * @return the boolean */ public Boolean foundAtLeastOneConflictingClasspathEntry() { return hasAtLeastOneConflictingClasspathEntry; } /* (non-Javadoc) * @see org.org.model.IModelItemVisitor#visit(org.org.model.IModelItem) */ /** * @see org.org.model.IModelItemVisitor#visit(org.org.model.IModelItem) */ @SuppressWarnings("rawtypes") public boolean visit(IModelItem modelItem) { if (modelItem instanceof ResolvedArtifact) { if (((ResolvedArtifact) modelItem).hasConflictingClasspathEntries()) { hasAtLeastOneConflictingClasspathEntry = true; return false; } } return true; } } /** * The Class LookForDuplicateVisitor. */ private static class LookForDuplicateVisitor implements IModelItemVisitor { /** The comparison string. */ private final String comparisonString; /** The has at least one duplicate. */ private boolean hasAtLeastOneDuplicate; /** * Instantiates a new look for duplicate visitor. * * @param comparisonString the comparison string */ public LookForDuplicateVisitor(String comparisonString) { this.comparisonString = comparisonString; } /** * Found at least one duplicate. * * @return true, if successful */ public boolean foundAtLeastOneDuplicate() { return hasAtLeastOneDuplicate; } /* (non-Javadoc) * @see org.org.model.IModelItemVisitor#visit(org.org.model.IModelItem) */ /** * @see org.org.model.IModelItemVisitor#visit(org.org.model.IModelItem) */ @SuppressWarnings("rawtypes") public boolean visit(IModelItem modelItem) { if (modelItem instanceof ResolvedArtifact) { ArtifactVersion artifactVersion2 = ((ResolvedArtifact) modelItem).getArtifactVersion(); String comparisonString2 = buildComparisonString(artifactVersion2); if (comparisonString.equals(comparisonString2)) { hasAtLeastOneDuplicate = true; return false; } } else { return false; } return true; } } /** * Builds the comparison string. * * @param artifactVersion the artifact version * * @return the string */ private static String buildComparisonString(ArtifactVersion artifactVersion) { return artifactVersion.getParent().getParent().getName() + ":" + artifactVersion.getParent().getId(); //$NON-NLS-1$ } /** * Scan resolved artifact for conflicting classpath entries. * * @param resolvedArtifact the resolved artifact * * @return the boolean */ private static Boolean scanResolvedArtifactForConflictingClasspathEntries(ResolvedArtifact resolvedArtifact) { LookForConflictingClasspathEntriesVisitor visitor = new LookForConflictingClasspathEntriesVisitor(); resolvedArtifact.accept(visitor); return visitor.foundAtLeastOneConflictingClasspathEntry(); } /** * Extract conflicting classpath entries from resolved artifact. * * @param resolvedArtifact the resolved artifact * * @return the list< dws classpath entry descriptor> */ private static List<DWSClasspathEntryDescriptor> extractConflictingClasspathEntriesFromResolvedArtifact(ResolvedArtifact resolvedArtifact) { HarvestConflictingClasspathEntriesVisitor visitor = new HarvestConflictingClasspathEntriesVisitor(); resolvedArtifact.accept(visitor); return visitor.getResult(); } /** * This method checks a set of Chosen artifacts for conflicts.<br> * It returns as soon as it has found a conflict. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return true, if contains conflicting classpath entries */ public static Boolean containsConflictingClasspathEntries(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { boolean result = false; for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { if (artifact instanceof ResolvedArtifact) { boolean containsConflicts = scanResolvedArtifactForConflictingClasspathEntries((ResolvedArtifact) artifact); if (containsConflicts) { result = true; break; } } } } } return result; } /** * This method looks for any library that has a duplicate.<br> * It returns as soon as it's found a duplicate. * * @param libraries the libraries * @param transitiveDependencies the transitive dependencies * * @return true, if contains duplicate libraries */ public static Boolean containsDuplicateLibraries(List<?> libraries, List<?> transitiveDependencies) { for (Object library : libraries) { if (library instanceof ResolvedArtifact) { ArtifactVersion artifactVersion = ((ResolvedArtifact) library).getArtifactVersion(); final String comparisonString = buildComparisonString(artifactVersion); for (Object transitiveDependency : transitiveDependencies) { if (transitiveDependency instanceof ResolvedArtifact) { ResolvedArtifact resolvedArtifact = (ResolvedArtifact) transitiveDependency; ArtifactVersion artifactVersion2 = ((ResolvedArtifact) transitiveDependency).getArtifactVersion(); final String comparisonString2 = buildComparisonString(artifactVersion2); if (comparisonString.equals(comparisonString2)) { return true; } if (resolvedArtifact.hasChildren()) { LookForDuplicateVisitor visitor = new LookForDuplicateVisitor(comparisonString); resolvedArtifact.accept(visitor); if (visitor.foundAtLeastOneDuplicate()) { return true; } } } } } } return false; } /** * This method extracts the conflicting classpath entries from a list of Chosen artifacts. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return the list< dws classpath entry descriptor> */ public static List<DWSClasspathEntryDescriptor> extractConflictingClasspathEntries(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { List<DWSClasspathEntryDescriptor> result = new LinkedList<DWSClasspathEntryDescriptor>(); for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { if (artifact instanceof ResolvedArtifact) { result.addAll(extractConflictingClasspathEntriesFromResolvedArtifact((ResolvedArtifact) artifact)); } } } } return result; } /** * This method extracts the transitive dependencies for a list of Chosen artifacts. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return the set< abstract chosen artifact version> */ public static Set<AbstractChosenArtifactVersion> extractTransitiveDependencies(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { Set<AbstractChosenArtifactVersion> result = new LinkedHashSet<AbstractChosenArtifactVersion>(); for (AbstractChosenArtifactVersion chosenArtifactVersion : chosenArtifactVersions) { boolean optional = chosenArtifactVersion.getOptional(); boolean skipped = chosenArtifactVersion.isSkipped(); if (!(optional || skipped) || (optional && searchContext.getDealWithOptional())) { RemoveOptionalAndSkippedVisitor visitor = new RemoveOptionalAndSkippedVisitor(searchContext); chosenArtifactVersion.accept(visitor); result.add(chosenArtifactVersion); } } return result; } /** * This method removes the artifacts with scopes other than COMPILE, TEST or a null scope. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return the set< abstract chosen artifact version> */ public static Set<AbstractChosenArtifactVersion> filterResolved(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { Set<AbstractChosenArtifactVersion> result = new LinkedHashSet<AbstractChosenArtifactVersion>(); for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { if (artifact instanceof ResolvedArtifact) { final boolean nullScope = artifact.getScope() == null; final boolean compileScope = (!nullScope) && artifact.getScope() == Scope.COMPILE; final boolean testScope = (!nullScope) && artifact.getScope() == Scope.TEST; if (nullScope || compileScope || testScope) { result.add(artifact); } } } } } return result; } /** * This method returns the chosen artifacts which should be added to the Build path in a web application.<br> * This means all libraries except COMPILE Scope libraries or libraries with no scope defined (COMPILE is the default scope),<br> * and except libraries with risky scope. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return the list< abstract chosen artifact version> */ public static List<AbstractChosenArtifactVersion> filterResolvedForClasspathInWebProjects(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { List<AbstractChosenArtifactVersion> result = new LinkedList<AbstractChosenArtifactVersion>(); for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { if (artifact instanceof ResolvedArtifact) { if (artifact.getScope() != null && (artifact.getScope() != Scope.COMPILE) && !artifact.isNarrowScope()) { result.add(artifact); } } } } } return result; } /** * This method returns the chosen artifacts which should be added to the /WEB-INF/lib folder in a web application.<br> * This means COMPILE Scope libraries or libraries with no scope defined (COMPILE is the default scope). * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return the list< abstract chosen artifact version> */ public static List<AbstractChosenArtifactVersion> filterResolvedForWebInfInWebProjects(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { List<AbstractChosenArtifactVersion> result = new LinkedList<AbstractChosenArtifactVersion>(); for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { if (artifact instanceof ResolvedArtifact) { if (artifact.getScope() == null || artifact.getScope() == Scope.COMPILE) { result.add(artifact); } } } } } return result; } /** * This method returns the chosen artifacts which have a risky scope. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return the list< abstract chosen artifact version> */ public static List<AbstractChosenArtifactVersion> filterResolvedWithRiskyScope(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { List<AbstractChosenArtifactVersion> result = new LinkedList<AbstractChosenArtifactVersion>(); for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { if (artifact instanceof ResolvedArtifact) { if (artifact.isNarrowScope()) { result.add(artifact); } } } } } return result; } /** * This method returns the chosen artifacts which are not yet resolved against the available repositories. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return the list< abstract chosen artifact version> */ public static List<AbstractChosenArtifactVersion> filterUnresolved(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { List<AbstractChosenArtifactVersion> result = new LinkedList<AbstractChosenArtifactVersion>(); for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { if (artifact instanceof UnresolvedArtifact) { result.add(artifact); } } } } return result; } /** * This method is called to determine is any of the chosen artifacts is UnResolved in the available repositories.<br> * It returns as soon as it has found one. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return true, if unresolved libraries */ public static Boolean unresolvedLibraries(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { boolean result = false; for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { if (artifact instanceof UnresolvedArtifact) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { result = true; break; } } } } return result; } /** * This method is called to determine if any of the chosen artifacts or one of its transitive dependencies is UnResolved in the available repositories.<br> * It returns as soon as it has found one. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return true, if unresolved transitive libraries */ public static Boolean unresolvedTransitiveLibraries(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { boolean result = false; if (searchContext.getDealWithTransitive()) { for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (artifact.hasChildren()) { LookForUnresolvedArtifactVisitor visitor = new LookForUnresolvedArtifactVisitor(); artifact.accept(visitor); if (visitor.foundUnresolvedArtifact()) { result = true; break; } } } } return result; } /** * This method is called to determine if any of the chosen artifacts is Resolved in the available repositories.<br> * It returns as soon as it has found one. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return true, if resolved artifacts */ public static Boolean resolvedArtifacts(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { Boolean result = false; for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { if (artifact instanceof ResolvedArtifact) { final boolean nullScope = artifact.getScope() == null; final boolean compileScope = (!nullScope) && artifact.getScope() == Scope.COMPILE; final boolean testScope = (!nullScope) && artifact.getScope() == Scope.TEST; if (nullScope || compileScope || testScope) { result = true; break; } } } } } return result; } /** * This method is called to determine if any of the chosen artifacts has transitive dependencies.<br> * It returns as soon as it has found one. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return true, if transitive dependencies */ public static Boolean transitiveDependencies(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { boolean result = false; if (searchContext.getDealWithTransitive()) { for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { if (artifact instanceof ResolvedArtifact) { ResolvedArtifact resolvedArtifact = (ResolvedArtifact) artifact; if (resolvedArtifact.hasTransitiveDependencies()) { result = true; break; } } } } } } return result; } /** * This method is called to determine if any of the chosen artifacts is of unknown or restrictive scope.<br> * It returns as soon as it has found one. * * @param searchContext the search context * @param chosenArtifactVersions the chosen artifact versions * * @return true, if unknown or restricted scope */ public static Boolean unknownOrRestrictedScope(SearchContext searchContext, Set<AbstractChosenArtifactVersion> chosenArtifactVersions) { boolean result = false; if (searchContext.getDealWithUnknownOrRestrictiveScope()) { for (AbstractChosenArtifactVersion artifact : chosenArtifactVersions) { if (!artifact.isSkipped()) { final boolean optionalLibrary = artifact.getOptional(); if ((optionalLibrary && searchContext.getDealWithOptional()) || (!optionalLibrary)) { if (artifact instanceof ResolvedArtifact) { if (artifact.isNarrowScope()) { result = true; break; } } } } } } return result; } }