blob: 63d3106cd737bdaf3c311f5fb271b54b36e4ada3 [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* 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.jetbrains.idea.maven.dom.inspections;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.Processor;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.hash.HashSet;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.highlighting.DomElementAnnotationHolder;
import com.intellij.util.xml.highlighting.DomElementsInspection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.maven.dom.DependencyConflictId;
import org.jetbrains.idea.maven.dom.MavenDomBundle;
import org.jetbrains.idea.maven.dom.MavenDomProjectProcessorUtils;
import org.jetbrains.idea.maven.dom.MavenDomUtil;
import org.jetbrains.idea.maven.dom.model.MavenDomDependencies;
import org.jetbrains.idea.maven.dom.model.MavenDomDependency;
import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel;
import org.jetbrains.idea.maven.project.MavenProject;
import java.util.*;
public class MavenDuplicateDependenciesInspection extends DomElementsInspection<MavenDomProjectModel> {
public MavenDuplicateDependenciesInspection() {
super(MavenDomProjectModel.class);
}
@Override
public void checkFileElement(DomFileElement<MavenDomProjectModel> domFileElement,
DomElementAnnotationHolder holder) {
MavenDomProjectModel projectModel = domFileElement.getRootElement();
checkManagedDependencies(projectModel, holder);
checkDependencies(projectModel, holder);
}
private static void checkDependencies(@NotNull MavenDomProjectModel projectModel,
@NotNull DomElementAnnotationHolder holder) {
MultiMap<DependencyConflictId, MavenDomDependency> allDuplicates = getDuplicateDependenciesMap(projectModel);
for (MavenDomDependency dependency : projectModel.getDependencies().getDependencies()) {
DependencyConflictId id = DependencyConflictId.create(dependency);
if (id != null) {
Collection<MavenDomDependency> dependencies = allDuplicates.get(id);
if (dependencies.size() > 1) {
List<MavenDomDependency> duplicatedDependencies = new ArrayList<MavenDomDependency>();
for (MavenDomDependency d : dependencies) {
if (d == dependency) continue;
if (d.getParent() == dependency.getParent()) {
duplicatedDependencies.add(d); // Dependencies in the same file must be unique by groupId:artifactId:type:classifier
}
else {
if (scope(d).equals(scope(dependency))
&& Comparing.equal(d.getVersion().getStringValue(), dependency.getVersion().getStringValue())) {
duplicatedDependencies.add(d); // Dependencies in different files must not have same groupId:artifactId:VERSION:type:classifier:SCOPE
}
}
}
if (duplicatedDependencies.size() > 0) {
addProblem(dependency, duplicatedDependencies, holder);
}
}
}
}
}
private static String scope(MavenDomDependency dependency) {
String res = dependency.getScope().getRawText();
if (StringUtil.isEmpty(res)) return "compile";
return res;
}
private static void addProblem(@NotNull MavenDomDependency dependency,
@NotNull Collection<MavenDomDependency> dependencies,
@NotNull DomElementAnnotationHolder holder) {
StringBuilder sb = new StringBuilder();
Set<MavenDomProjectModel> processed = new HashSet<MavenDomProjectModel>();
for (MavenDomDependency domDependency : dependencies) {
if (dependency.equals(domDependency)) continue;
MavenDomProjectModel model = domDependency.getParentOfType(MavenDomProjectModel.class, false);
if (model != null && !processed.contains(model)) {
if (processed.size() > 0) sb.append(", ");
sb.append(createLinkText(model, domDependency));
processed.add(model);
}
}
holder.createProblem(dependency, HighlightSeverity.WARNING,
MavenDomBundle.message("MavenDuplicateDependenciesInspection.has.duplicates", sb.toString()));
}
private static String createLinkText(@NotNull MavenDomProjectModel model, @NotNull MavenDomDependency dependency) {
XmlTag tag = dependency.getXmlTag();
if (tag == null) return getProjectName(model);
VirtualFile file = tag.getContainingFile().getVirtualFile();
if (file == null) return getProjectName(model);
return "<a href ='#navigation/" + file.getPath() + ":" + tag.getTextRange().getStartOffset() + "'>" + getProjectName(model) + "</a>";
}
@NotNull
private static String getProjectName(MavenDomProjectModel model) {
MavenProject mavenProject = MavenDomUtil.findProject(model);
if (mavenProject != null) {
return mavenProject.getDisplayName();
}
else {
String name = model.getName().getStringValue();
if (!StringUtil.isEmptyOrSpaces(name)) {
return name;
}
else {
return "pom.xml"; // ?
}
}
}
@NotNull
private static MultiMap<DependencyConflictId, MavenDomDependency> getDuplicateDependenciesMap(MavenDomProjectModel projectModel) {
final MultiMap<DependencyConflictId, MavenDomDependency> allDependencies = MultiMap.createSet();
Processor<MavenDomProjectModel> collectProcessor = new Processor<MavenDomProjectModel>() {
public boolean process(MavenDomProjectModel model) {
collect(allDependencies, model.getDependencies());
return false;
}
};
MavenDomProjectProcessorUtils.processChildrenRecursively(projectModel, collectProcessor, true);
MavenDomProjectProcessorUtils.processParentProjects(projectModel, collectProcessor);
return allDependencies;
}
private static void collect(MultiMap<DependencyConflictId, MavenDomDependency> duplicates, @NotNull MavenDomDependencies dependencies) {
for (MavenDomDependency dependency : dependencies.getDependencies()) {
DependencyConflictId mavenId = DependencyConflictId.create(dependency);
if (mavenId == null) continue;
duplicates.putValue(mavenId, dependency);
}
}
private static void checkManagedDependencies(@NotNull MavenDomProjectModel projectModel,
@NotNull DomElementAnnotationHolder holder) {
MultiMap<DependencyConflictId, MavenDomDependency> duplicates = MultiMap.createSet();
collect(duplicates, projectModel.getDependencyManagement().getDependencies());
for (Map.Entry<DependencyConflictId, Collection<MavenDomDependency>> entry : duplicates.entrySet()) {
Collection<MavenDomDependency> set = entry.getValue();
if (set.size() <= 1) continue;
for (MavenDomDependency dependency : set) {
holder.createProblem(dependency, HighlightSeverity.WARNING, "Duplicated dependency");
}
}
}
@NotNull
public String getGroupDisplayName() {
return MavenDomBundle.message("inspection.group");
}
@NotNull
public String getDisplayName() {
return MavenDomBundle.message("inspection.duplicate.dependencies.name");
}
@NotNull
public String getShortName() {
return "MavenDuplicateDependenciesInspection";
}
@NotNull
public HighlightDisplayLevel getDefaultLevel() {
return HighlightDisplayLevel.WARNING;
}
}