Make: heuristics for processing removed constant fields added. If precise analysis...
[fedora-idea.git] / java / compiler / impl / src / com / intellij / compiler / make / ChangedConstantsDependencyProcessor.java
blobce11da5d72bbc9ecd7f820277e12ca2fde8fc365
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /**
18 * created at Feb 24, 2002
19 * @author Jeka
21 package com.intellij.compiler.make;
23 import com.intellij.compiler.classParsing.FieldInfo;
24 import com.intellij.lang.StdLanguages;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.diagnostic.Logger;
27 import com.intellij.openapi.progress.ProcessCanceledException;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.vfs.VirtualFile;
30 import com.intellij.psi.*;
31 import com.intellij.psi.search.*;
32 import com.intellij.psi.util.PsiUtil;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
36 import java.util.Collection;
37 import java.util.HashSet;
38 import java.util.Set;
40 class ChangedConstantsDependencyProcessor {
41 private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.make.ChangedConstantsDependencyProcessor");
42 private final Project myProject;
43 private final CachingSearcher mySearcher;
44 private final DependencyCache myDependencyCache;
45 private final int myQName;
46 private final FieldChangeInfo[] myChangedFields;
47 private final FieldChangeInfo[] myRemovedFields;
48 private static final long ANALYSIS_DURATION_THRESHOLD_MILLIS = 30000L /*30 sec*/;
51 public ChangedConstantsDependencyProcessor(Project project, CachingSearcher searcher, DependencyCache dependencyCache, int qName, FieldChangeInfo[] changedFields, FieldChangeInfo[] removedFields) {
52 myProject = project;
53 mySearcher = searcher;
54 myDependencyCache = dependencyCache;
55 myQName = qName;
56 myChangedFields = changedFields;
57 myRemovedFields = removedFields;
60 public void run() throws CacheCorruptedException {
61 final CacheCorruptedException[] _ex = new CacheCorruptedException[] {null};
62 ApplicationManager.getApplication().runReadAction(new Runnable() {
63 public void run() {
64 try {
65 final String qName = myDependencyCache.resolve(myQName);
66 PsiClass[] classes = JavaPsiFacade.getInstance(myProject).findClasses(qName.replace('$', '.'), GlobalSearchScope.allScope(myProject));
67 for (PsiClass aClass : classes) {
68 PsiField[] psiFields = aClass.getFields();
69 for (PsiField psiField : psiFields) {
70 final FieldChangeInfo changeInfo = findChangeInfo(psiField);
71 if (changeInfo != null) { // this field has been changed
72 processFieldChanged(psiField, aClass, changeInfo.isAccessibilityChange);
75 for (FieldChangeInfo removedField : myRemovedFields) {
76 processFieldRemoved(removedField.fieldInfo, aClass);
80 catch (CacheCorruptedException e) {
81 _ex[0] = e;
83 catch (ProcessCanceledException e) {
84 // supressed deliberately
87 });
88 if (_ex[0] != null) {
89 throw _ex[0];
93 private void processFieldRemoved(FieldInfo info, PsiClass aClass) throws CacheCorruptedException {
94 if (info.isPrivate()) {
95 return; // optimization: don't need to search, cause may be used only in this class
97 SearchScope searchScope = GlobalSearchScope.projectScope(myProject);
98 if (info.isPackageLocal()) {
99 final PsiFile containingFile = aClass.getContainingFile();
100 if (containingFile instanceof PsiJavaFile) {
101 final String packageName = ((PsiJavaFile)containingFile).getPackageName();
102 final PsiPackage aPackage = JavaPsiFacade.getInstance(myProject).findPackage(packageName);
103 if (aPackage != null) {
104 searchScope = PackageScope.packageScope(aPackage, false);
105 searchScope = searchScope.intersectWith(aClass.getUseScope());
109 final PsiSearchHelper psiSearchHelper = PsiManager.getInstance(myProject).getSearchHelper();
111 final long analysisStart = System.currentTimeMillis();
112 boolean skipResolve = false;
114 PsiIdentifier[] identifiers = findIdentifiers(psiSearchHelper, myDependencyCache.resolve(info.getName()), searchScope, UsageSearchContext.IN_CODE);
115 for (PsiIdentifier identifier : identifiers) {
116 PsiElement parent = identifier.getParent();
117 if (parent instanceof PsiReferenceExpression) {
118 PsiReferenceExpression refExpr = (PsiReferenceExpression)parent;
119 PsiReference reference = refExpr.getReference();
120 skipResolve = skipResolve || (System.currentTimeMillis() - analysisStart) > ANALYSIS_DURATION_THRESHOLD_MILLIS;
121 if (skipResolve || reference == null || reference.resolve() == null) {
122 PsiClass ownerClass = getOwnerClass(refExpr);
123 if (ownerClass != null && !ownerClass.equals(aClass)) {
124 int qualifiedName = myDependencyCache.getSymbolTable().getId(ownerClass.getQualifiedName());
125 // should force marking of the class no matter was it compiled or not
126 // This will ensure the class was recompiled _after_ all the constants get their new values
127 if (myDependencyCache.markClass(qualifiedName, true)) {
128 if (LOG.isDebugEnabled()) {
129 LOG.debug("Mark dependent class " + myDependencyCache.resolve(qualifiedName) +
130 "; reason: some constants were removed from " + myDependencyCache.resolve(myQName));
139 @NotNull
140 private static PsiIdentifier[] findIdentifiers(PsiSearchHelper helper, @NotNull String identifier, @NotNull SearchScope searchScope, short searchContext) {
141 PsiElementProcessor.CollectElements<PsiIdentifier> processor = new PsiElementProcessor.CollectElements<PsiIdentifier>();
142 processIdentifiers(helper, processor, identifier, searchScope, searchContext);
143 return processor.toArray(PsiIdentifier.EMPTY_ARRAY);
146 private static boolean processIdentifiers(PsiSearchHelper helper,
147 @NotNull final PsiElementProcessor<PsiIdentifier> processor,
148 @NotNull final String identifier,
149 @NotNull SearchScope searchScope,
150 short searchContext) {
151 TextOccurenceProcessor processor1 = new TextOccurenceProcessor() {
152 public boolean execute(PsiElement element, int offsetInElement) {
153 return !(element instanceof PsiIdentifier) || processor.execute((PsiIdentifier)element);
156 return helper.processElementsWithWord(processor1, searchScope, identifier, searchContext, true);
159 private void processFieldChanged(PsiField field, PsiClass aClass, final boolean isAccessibilityChange) throws CacheCorruptedException {
160 if (!isAccessibilityChange && field.hasModifierProperty(PsiModifier.PRIVATE)) {
161 return; // optimization: don't need to search, cause may be used only in this class
163 Set<PsiElement> usages = new HashSet<PsiElement>();
164 addUsages(field, usages, isAccessibilityChange);
165 if (LOG.isDebugEnabled()) {
166 LOG.debug("++++++++++++++++++++++++++++++++++++++++++++++++");
167 LOG.debug("Processing changed field: " + aClass.getQualifiedName() + "." + field.getName());
169 for (final PsiElement usage : usages) {
170 PsiClass ownerClass = getOwnerClass(usage);
171 if (LOG.isDebugEnabled()) {
172 if (ownerClass != null) {
173 LOG.debug("Usage " + usage + " found in class: " + ownerClass.getQualifiedName());
175 else {
176 LOG.debug("Usage " + usage + " found in class: null");
179 if (ownerClass != null && !ownerClass.equals(aClass)) {
180 int qualifiedName = myDependencyCache.getSymbolTable().getId(ownerClass.getQualifiedName());
181 // should force marking of the class no matter was it compiled or not
182 // This will ensure the class was recompiled _after_ all the constants get their new values
183 if (LOG.isDebugEnabled()) {
184 LOG.debug("Marking class id = [" + qualifiedName + "], name=[" + myDependencyCache.resolve(qualifiedName) + "]");
186 if (myDependencyCache.markClass(qualifiedName, true)) {
187 if (LOG.isDebugEnabled()) {
188 LOG.debug("Marked dependent class " + myDependencyCache.resolve(qualifiedName) + "; reason: constants changed in " +
189 myDependencyCache.resolve(myQName));
193 else if (ownerClass == null) {
194 final PsiFile containingFile = usage.getContainingFile();
195 if (containingFile != null) {
196 final VirtualFile file = containingFile.getVirtualFile();
197 if (file != null) {
198 myDependencyCache.markFile(file);
203 if (LOG.isDebugEnabled()) {
204 LOG.debug("+++++++++++++++++++++++++++++++++++++++++++++++");
208 private void addUsages(PsiField psiField, Collection<PsiElement> usages, final boolean ignoreAccessScope) {
209 Collection<PsiReference> references = mySearcher.findReferences(psiField, ignoreAccessScope)/*doFindReferences(searchHelper, psiField)*/;
210 for (final PsiReference ref : references) {
211 if (!(ref instanceof PsiReferenceExpression)) {
212 continue;
214 PsiElement e = ref.getElement();
215 usages.add(e);
216 PsiField ownerField = getOwnerField(e);
217 if (ownerField != null) {
218 if (ownerField.hasModifierProperty(PsiModifier.FINAL)) {
219 PsiExpression initializer = ownerField.getInitializer();
220 if (initializer != null && PsiUtil.isConstantExpression(initializer)) {
221 // if the field depends on the compile-time-constant expression and is itself final
222 addUsages(ownerField, usages, ignoreAccessScope);
230 private PsiReference[] doFindReferences(final PsiSearchHelper searchHelper, final PsiField psiField) {
231 final ProgressManager progressManager = ProgressManager.getInstance();
232 final ProgressIndicator currentProgress = progressManager.getProgressIndicator();
233 final PsiReference[][] references = new PsiReference[][] {null};
234 progressManager.runProcess(new Runnable() {
235 public void run() {
236 references[0] = searchHelper.findReferences(psiField, GlobalSearchScope.projectScope(myProject), false);
237 if (ENABLE_TRACING) {
238 System.out.println("Finding referencers for " + psiField);
241 }, new NonCancellableProgressAdapter(currentProgress));
242 return references[0];
246 private PsiField getOwnerField(PsiElement element) {
247 while (!(element instanceof PsiFile)) {
248 if (element instanceof PsiClass) {
249 break;
251 if (element instanceof PsiField) { // top-level class
252 return (PsiField)element;
254 element = element.getParent();
256 return null;
259 private FieldChangeInfo findChangeInfo(PsiField field) throws CacheCorruptedException {
260 String name = field.getName();
261 for (final FieldChangeInfo changeInfo : myChangedFields) {
262 if (name.equals(myDependencyCache.resolve(changeInfo.fieldInfo.getName()))) {
263 return changeInfo;
266 return null;
269 @Nullable
270 private static PsiClass getOwnerClass(PsiElement element) {
271 while (!(element instanceof PsiFile)) {
272 if (element instanceof PsiClass && element.getParent() instanceof PsiJavaFile) { // top-level class
273 final PsiClass psiClass = (PsiClass)element;
274 if (JspPsiUtil.isInJspFile(psiClass)) {
275 return null;
277 final PsiFile containingFile = psiClass.getContainingFile();
278 if (containingFile == null) {
279 return null;
281 return StdLanguages.JAVA.equals(containingFile.getLanguage())? psiClass : null;
283 element = element.getParent();
285 return null;
288 public static class FieldChangeInfo {
289 final FieldInfo fieldInfo;
290 final boolean isAccessibilityChange;
292 public FieldChangeInfo(final FieldInfo fieldId) {
293 this(fieldId, false);
296 public FieldChangeInfo(final FieldInfo fieldInfo, final boolean accessibilityChange) {
297 this.fieldInfo = fieldInfo;
298 isAccessibilityChange = accessibilityChange;
301 public boolean equals(Object o) {
302 if (this == o) return true;
303 if (o == null || getClass() != o.getClass()) return false;
305 final FieldChangeInfo fieldChangeInfo = (FieldChangeInfo)o;
307 if (isAccessibilityChange != fieldChangeInfo.isAccessibilityChange) return false;
308 if (!fieldInfo.equals(fieldChangeInfo.fieldInfo)) return false;
310 return true;
313 public int hashCode() {
314 int result;
315 result = fieldInfo.hashCode();
316 result = 29 * result + (isAccessibilityChange ? 1 : 0);
317 return result;