summaryrefslogtreecommitdiff
path: root/java/java-analysis-impl/src/com/intellij/codeInspection/unnecessaryModuleDependency/UnnecessaryModuleDependencyInspection.java
blob: f3884b30efeb11afa13f9ccb9934747d8feeb38a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package com.intellij.codeInspection.unnecessaryModuleDependency;

import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.reference.RefGraphAnnotator;
import com.intellij.codeInspection.reference.RefManager;
import com.intellij.codeInspection.reference.RefModule;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleOrderEntry;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.reference.SoftReference;
import com.intellij.util.ArrayUtil;
import com.intellij.util.graph.Graph;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * User: anna
 * Date: 09-Jan-2006
 */
public class UnnecessaryModuleDependencyInspection extends GlobalInspectionTool {

  private SoftReference<Graph<Module>> myGraph = new SoftReference<Graph<Module>>(null);

  @Override
  public RefGraphAnnotator getAnnotator(@NotNull final RefManager refManager) {
    return new UnnecessaryModuleDependencyAnnotator(refManager);
  }

  @Override
  public CommonProblemDescriptor[] checkElement(@NotNull RefEntity refEntity, @NotNull AnalysisScope scope, @NotNull InspectionManager manager, @NotNull final GlobalInspectionContext globalContext) {
    if (refEntity instanceof RefModule){
      final RefModule refModule = (RefModule)refEntity;
      final Module module = refModule.getModule();
      if (module.isDisposed()) return CommonProblemDescriptor.EMPTY_ARRAY;
      final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
      final OrderEntry[] declaredDependencies = moduleRootManager.getOrderEntries();
      final Module[] declaredModuleDependencies = moduleRootManager.getDependencies();

      List<CommonProblemDescriptor> descriptors = new ArrayList<CommonProblemDescriptor>();
      final Set<Module> modules = refModule.getUserData(UnnecessaryModuleDependencyAnnotator.DEPENDENCIES);
      Graph<Module> graph = myGraph.get();
      if (graph == null) {
        graph = ModuleManager.getInstance(globalContext.getProject()).moduleGraph();
        myGraph = new SoftReference<Graph<Module>>(graph);
      }

      final RefManager refManager = globalContext.getRefManager();
      for (final OrderEntry entry : declaredDependencies) {
        if (entry instanceof ModuleOrderEntry) {
          final Module dependency = ((ModuleOrderEntry)entry).getModule();
          if (dependency != null) {
            if (modules == null || !modules.contains(dependency)) {
              List<String> dependenciesThroughExported = null;
              if (((ModuleOrderEntry)entry).isExported()) {
                final Iterator<Module> iterator = graph.getOut(module);
                while (iterator.hasNext()) {
                  final Module dep = iterator.next();
                  final RefModule depRefModule = refManager.getRefModule(dep);
                  if (depRefModule != null) {
                    final Set<Module> neededModules = depRefModule.getUserData(UnnecessaryModuleDependencyAnnotator.DEPENDENCIES);
                    if (neededModules != null && neededModules.contains(dependency)) {
                      if (dependenciesThroughExported == null) {
                        dependenciesThroughExported = new ArrayList<String>();
                      }
                      dependenciesThroughExported.add(dep.getName());
                    }
                  }
                }
              }
              if (modules != null) {
                List<String> transitiveDependencies = new ArrayList<String>();
                final OrderEntry[] dependenciesOfDependencies = ModuleRootManager.getInstance(dependency).getOrderEntries();
                for (OrderEntry secondDependency : dependenciesOfDependencies) {
                  if (secondDependency instanceof ModuleOrderEntry && ((ModuleOrderEntry)secondDependency).isExported()) {
                    final Module mod = ((ModuleOrderEntry)secondDependency).getModule();
                    if (mod != null && modules.contains(mod) && ArrayUtil.find(declaredModuleDependencies, mod) < 0) {
                      transitiveDependencies.add(mod.getName());
                    }
                  }
                }
                if (!transitiveDependencies.isEmpty()) {
                  final String exported = StringUtil.join(transitiveDependencies, ", ");
                  descriptors.add(manager.createProblemDescriptor(InspectionsBundle.message("unnecessary.module.dependency.exported.problem.descriptor1", module.getName(), dependency.getName(), exported)));
                  continue;
                }
              }

              descriptors.add(createDescriptor(scope, manager, module, dependency, dependenciesThroughExported));
            }
          }
        }
      }
      return descriptors.isEmpty() ? null : descriptors.toArray(new CommonProblemDescriptor[descriptors.size()]);
    }
    return null;
  }

  private static CommonProblemDescriptor createDescriptor(AnalysisScope scope,
                                                          InspectionManager manager,
                                                          Module module,
                                                          Module dependency, 
                                                          List<String> exportedDependencies) {
    if (exportedDependencies != null) {
      final String exported = StringUtil.join(exportedDependencies, ", ");
      return manager.createProblemDescriptor(InspectionsBundle.message("unnecessary.module.dependency.exported.problem.descriptor", module.getName(), dependency.getName(), exported));
    }

    if (scope.containsModule(dependency)) { //external references are rejected -> annotator doesn't provide any information on them -> false positives
      final String allContainsMessage = InspectionsBundle.message("unnecessary.module.dependency.problem.descriptor", module.getName(), dependency.getName());
      return manager.createProblemDescriptor(allContainsMessage, new RemoveModuleDependencyFix(module, dependency));
    } else {
      String message = InspectionsBundle.message("suspected.module.dependency.problem.descriptor", module.getName(), dependency.getName(), scope.getDisplayName(), dependency.getName());
      return manager.createProblemDescriptor(message);
    }
  }

  @Override
  @NotNull
  public String getGroupDisplayName() {
    return GroupNames.DECLARATION_REDUNDANCY;
  }

  @Override
  @NotNull
  public String getDisplayName() {
    return InspectionsBundle.message("unnecessary.module.dependency.display.name");
  }

  @Override
  @NotNull
  @NonNls
  public String getShortName() {
    return "UnnecessaryModuleDependencyInspection";
  }

  public static class RemoveModuleDependencyFix implements QuickFix {
    private final Module myModule;
    private final Module myDependency;

    public RemoveModuleDependencyFix(Module module, Module dependency) {
      myModule = module;
      myDependency = dependency;
    }

    @Override
    @NotNull
    public String getName() {
      return "Remove dependency";
    }

    @Override
    @NotNull
    public String getFamilyName() {
      return getName();
    }

    @Override
    public void applyFix(@NotNull Project project, @NotNull CommonProblemDescriptor descriptor) {
      final ModifiableRootModel model = ModuleRootManager.getInstance(myModule).getModifiableModel();
      for (OrderEntry entry : model.getOrderEntries()) {
        if (entry instanceof ModuleOrderEntry) {
          final Module mDependency = ((ModuleOrderEntry)entry).getModule();
          if (Comparing.equal(mDependency, myDependency)) {
            model.removeOrderEntry(entry);
            break;
          }
        }
      }
      model.commit();
    }
  }
}