/*
* Copyright 2012 the original author or authors.
*
* Licensed 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.gradle.api.tasks.diagnostics.internal.insight;
import org.gradle.api.artifacts.component.ComponentIdentifier;
import org.gradle.api.artifacts.component.ComponentSelector;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.artifacts.component.ModuleComponentSelector;
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
import org.gradle.api.artifacts.component.ProjectComponentSelector;
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.VersionInfo;
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionComparator;
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme;
import org.gradle.api.tasks.diagnostics.internal.graph.nodes.DependencyEdge;
import org.gradle.util.CollectionUtils;
import java.util.Collection;
import java.util.Comparator;
public class DependencyResultSorter {
/**
* sorts by group:name:version mostly.
* If requested matches selected then it will override the version comparison
* so that the dependency that was selected is more prominent.
*/
public static Collection<DependencyEdge> sort(Collection<DependencyEdge> input, VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator) {
return CollectionUtils.sort(input, new DependencyComparator(versionSelectorScheme, versionComparator));
}
private static class DependencyComparator implements Comparator<DependencyEdge> {
private final VersionSelectorScheme versionSelectorScheme;
private final VersionComparator versionComparator;
private DependencyComparator(VersionSelectorScheme versionSelectorScheme, VersionComparator versionComparator) {
this.versionSelectorScheme = versionSelectorScheme;
this.versionComparator = versionComparator;
}
@Override
public int compare(DependencyEdge left, DependencyEdge right) {
checkRequestedComponentSelectorType(left);
checkRequestedComponentSelectorType(right);
if(isLeftProjectButRightIsModuleComponentSelector(left, right)) {
return -1;
}
if(isLeftModuleButRightIsProjectComponentSelector(left, right)) {
return 1;
}
if(isLeftAndRightProjectComponentSelector(left, right)) {
return compareRequestedProjectComponentSelectors(left, right);
}
if(isLeftAndRightModuleComponentSelector(left, right)) {
return compareModuleComponentSelectors(left, right);
}
return 0;
}
private void checkRequestedComponentSelectorType(DependencyEdge dependencyEdge) {
if(dependencyEdge == null || dependencyEdge.getRequested() == null) {
throw new IllegalArgumentException("Dependency edge or the requested component selector may not be null");
}
ComponentSelector requested = dependencyEdge.getRequested();
if(!isExpectedComponentSelector(requested)) {
throw new IllegalArgumentException("Unexpected component selector type for dependency edge: " + requested.getClass());
}
}
private boolean isExpectedComponentSelector(ComponentSelector componentSelector) {
return componentSelector instanceof ProjectComponentSelector || componentSelector instanceof ModuleComponentSelector;
}
private boolean isLeftProjectButRightIsModuleComponentSelector(DependencyEdge left, DependencyEdge right) {
return left.getRequested() instanceof ProjectComponentSelector && right.getRequested() instanceof ModuleComponentSelector;
}
private boolean isLeftModuleButRightIsProjectComponentSelector(DependencyEdge left, DependencyEdge right) {
return left.getRequested() instanceof ModuleComponentSelector && right.getRequested() instanceof ProjectComponentSelector;
}
private boolean isLeftAndRightProjectComponentSelector(DependencyEdge left, DependencyEdge right) {
return left.getRequested() instanceof ProjectComponentSelector && right.getRequested() instanceof ProjectComponentSelector;
}
private boolean isLeftAndRightModuleComponentSelector(DependencyEdge left, DependencyEdge right) {
return left.getRequested() instanceof ModuleComponentSelector && right.getRequested() instanceof ModuleComponentSelector;
}
private int compareModuleComponentSelectors(DependencyEdge left, DependencyEdge right) {
ModuleComponentSelector leftRequested = (ModuleComponentSelector)left.getRequested();
ModuleComponentSelector rightRequested = (ModuleComponentSelector)right.getRequested();
int byGroup = leftRequested.getGroup().compareTo(rightRequested.getGroup());
if (byGroup != 0) {
return byGroup;
}
int byModule = leftRequested.getModule().compareTo(rightRequested.getModule());
if (byModule != 0) {
return byModule;
}
//if selected matches requested version comparison is overridden
boolean leftMatches = leftRequested.matchesStrictly(left.getActual());
boolean rightMatches = rightRequested.matchesStrictly(right.getActual());
if (leftMatches && !rightMatches) {
return -1;
} else if (!leftMatches && rightMatches) {
return 1;
}
//order dynamic selectors after static selectors
boolean leftDynamic = versionSelectorScheme.parseSelector(leftRequested.getVersion()).isDynamic();
boolean rightDynamic = versionSelectorScheme.parseSelector(rightRequested.getVersion()).isDynamic();
if (leftDynamic && !rightDynamic) {
return 1;
} else if (!leftDynamic && rightDynamic) {
return -1;
}
int byVersion;
if (leftDynamic && rightDynamic) {
// order dynamic selectors lexicographically
byVersion = leftRequested.getVersion().compareTo(rightRequested.getVersion());
} else {
// order static selectors semantically
byVersion = compareVersions(leftRequested.getVersion(), rightRequested.getVersion());
}
if (byVersion != 0) {
return byVersion;
}
return compareFromComponentIdentifiers(left.getFrom(), right.getFrom());
}
private int compareRequestedProjectComponentSelectors(DependencyEdge left, DependencyEdge right) {
ProjectComponentSelector leftRequested = (ProjectComponentSelector)left.getRequested();
ProjectComponentSelector rightRequested = (ProjectComponentSelector)right.getRequested();
return leftRequested.getProjectPath().compareTo(rightRequested.getProjectPath());
}
public int compareFromComponentIdentifiers(ComponentIdentifier left, ComponentIdentifier right) {
if(isLeftAndRightFromProjectComponentIdentifier(left, right)) {
return compareFromProjectComponentIdentifiers(left, right);
}
if(isLeftAndRightFromModuleComponentIdentifier(left, right)) {
return compareFromModuleComponentIdentifiers(left, right);
}
return isLeftFromProjectButRightIsModuleComponentIdentifier(left, right) ? -1 : 1;
}
private int compareFromProjectComponentIdentifiers(ComponentIdentifier left, ComponentIdentifier right) {
ProjectComponentIdentifier leftFrom = (ProjectComponentIdentifier)left;
ProjectComponentIdentifier rightFrom = (ProjectComponentIdentifier)right;
return leftFrom.getProjectPath().compareTo(rightFrom.getProjectPath());
}
private int compareFromModuleComponentIdentifiers(ComponentIdentifier left, ComponentIdentifier right) {
ModuleComponentIdentifier leftFrom = (ModuleComponentIdentifier)left;
ModuleComponentIdentifier rightFrom = (ModuleComponentIdentifier)right;
int byGroup = leftFrom.getGroup().compareTo(rightFrom.getGroup());
if (byGroup != 0) {
return byGroup;
}
int byModule = leftFrom.getModule().compareTo(rightFrom.getModule());
if (byModule != 0) {
return byModule;
}
return compareVersions(leftFrom.getVersion(), rightFrom.getVersion());
}
private int compareVersions(String left, String right) {
return versionComparator.compare(new VersionInfo(left), new VersionInfo(right));
}
private boolean isLeftAndRightFromProjectComponentIdentifier(ComponentIdentifier left, ComponentIdentifier right) {
return left instanceof ProjectComponentIdentifier && right instanceof ProjectComponentIdentifier;
}
private boolean isLeftAndRightFromModuleComponentIdentifier(ComponentIdentifier left, ComponentIdentifier right) {
return left instanceof ModuleComponentIdentifier && right instanceof ModuleComponentIdentifier;
}
private boolean isLeftFromProjectButRightIsModuleComponentIdentifier(ComponentIdentifier left, ComponentIdentifier right) {
return left instanceof ProjectComponentIdentifier && right instanceof ModuleComponentIdentifier;
}
}
}