a66969609754f594de4ee58c14607186a1099572
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / backwardRefs / BackwardReferenceIndexWriter.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.jetbrains.jps.backwardRefs;
17
18 import com.intellij.util.Function;
19 import com.intellij.util.SystemProperties;
20 import com.intellij.util.containers.ContainerUtil;
21 import com.sun.tools.javac.code.Symbol;
22 import gnu.trove.THashSet;
23 import org.jetbrains.annotations.NotNull;
24 import org.jetbrains.jps.builders.java.JavaBuilderUtil;
25 import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
26 import org.jetbrains.jps.incremental.CompileContext;
27 import org.jetbrains.jps.incremental.java.JavaBuilder;
28 import org.jetbrains.jps.incremental.storage.BuildDataManager;
29 import org.jetbrains.jps.javac.ast.api.JavacRefSymbol;
30 import org.jetbrains.jps.model.java.compiler.JavaCompilers;
31
32 import javax.tools.*;
33 import java.io.File;
34 import java.io.IOException;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.List;
38 import java.util.Set;
39
40 public class BackwardReferenceIndexWriter {
41   public static final String PROP_KEY = "ref.index.builder";
42
43   private static volatile BackwardReferenceIndexWriter ourInstance;
44
45   private final CompilerBackwardReferenceIndex myIndex;
46   private final boolean myRebuild;
47
48   private BackwardReferenceIndexWriter(CompilerBackwardReferenceIndex index, boolean rebuild) {
49     myIndex = index;
50     myRebuild = rebuild;
51   }
52
53   static BackwardReferenceIndexWriter getInstance() {
54     return ourInstance;
55   }
56
57   static void initialize(@NotNull final CompileContext context) {
58     if (isEnabled()) {
59       final BuildDataManager dataManager = context.getProjectDescriptor().dataManager;
60       final File buildDir = dataManager.getDataPaths().getDataStorageRoot();
61       boolean isRebuild = JavaBuilderUtil.isForcedRecompilationAllJavaModules(context);
62
63       if (!JavaCompilers.JAVAC_ID.equals(JavaBuilder.getUsedCompilerId(context))) {
64         CompilerBackwardReferenceIndex.removeIndexFiles(buildDir);
65         return;
66       }
67       if (isRebuild) {
68         CompilerBackwardReferenceIndex.removeIndexFiles(buildDir);
69       }
70       else if (CompilerBackwardReferenceIndex.versionDiffers(buildDir)) {
71         throw new BuildDataCorruptedException(new IOException("backward reference index should be updated to actual version"));
72       }
73
74       if (CompilerBackwardReferenceIndex.exist(buildDir) || isRebuild) {
75         ourInstance = new BackwardReferenceIndexWriter(new CompilerBackwardReferenceIndex(buildDir), isRebuild);
76       }
77     }
78   }
79
80   static boolean isEnabled() {
81     return SystemProperties.getBooleanProperty(PROP_KEY, false);
82   }
83
84   void close() {
85     myIndex.close();
86   }
87
88   int enumerateFile(JavaFileObject file) {
89     return enumeratePath(file.getName());
90   }
91
92   synchronized LightUsage.LightClassUsage asClassUsage(Symbol name) {
93     return new LightUsage.LightClassUsage(myIndex.getByteSeqEum().enumerate(LightUsage.bytes(name)));
94   }
95
96   synchronized void processDeletedFiles(Collection<String> paths) {
97     for (String path : paths) {
98       final int deletedFileId = enumeratePath(path);
99
100       //remove from reference maps
101       final Collection<LightUsage> refs = myIndex.getReferenceMap().get(deletedFileId);
102       if (refs != null) {
103         for (LightUsage ref : refs) {
104           myIndex.getBackwardReferenceMap().removeFrom(ref, deletedFileId);
105         }
106       }
107       myIndex.getReferenceMap().remove(deletedFileId);
108
109       //remove from definition & hierarchy maps
110       final Collection<LightUsage> definedClasses = myIndex.getClassDefinitionMap().get(deletedFileId);
111       removeClassesFromHierarchy(deletedFileId, definedClasses);
112       myIndex.getClassDefinitionMap().remove(deletedFileId);
113     }
114   }
115
116   synchronized void writeClassDefinitions(int fileId, Collection<LightUsage> classes) {
117     if (myRebuild) {
118       directlyWriteClassDefinitions(fileId, classes);
119     } else {
120       updateClassDefinitions(fileId, classes);
121     }
122   }
123
124   private void updateClassDefinitions(int fileId, Collection<LightUsage> classes) {
125     final Collection<LightUsage> oldDefs = myIndex.getClassDefinitionMap().get(fileId);
126     final Collection<LightUsage> oldDefsCopy = oldDefs == null ? null : new THashSet<LightUsage>(oldDefs);
127
128     myIndex.getClassDefinitionMap().replace(fileId, classes);
129     for (LightUsage aClass : classes) {
130       if (oldDefsCopy == null || !oldDefsCopy.remove(aClass)) {
131         myIndex.getBackwardClassDefinitionMap().put(aClass, fileId);
132       }
133     }
134
135     removeClassesFromHierarchy(fileId, oldDefsCopy);
136   }
137
138   private void directlyWriteClassDefinitions(int fileId, Collection<LightUsage> classes) {
139     myIndex.getClassDefinitionMap().put(fileId, classes);
140     for (LightUsage aClass : classes) {
141       myIndex.getBackwardClassDefinitionMap().put(aClass, fileId);
142     }
143   }
144
145   synchronized void writeReferences(int fileId, Collection<JavacRefSymbol> refs) {
146     final ByteArrayEnumerator byteSeqEum = myIndex.getByteSeqEum();
147     final List<LightUsage> usages = ContainerUtil.mapNotNull(refs, new Function<JavacRefSymbol, LightUsage>() {
148       @Override
149       public LightUsage fun(JavacRefSymbol symbol) {
150         return LightUsage.fromSymbol(symbol, byteSeqEum);
151       }
152     });
153
154     if (myRebuild) {
155       for (LightUsage usage : usages) {
156         myIndex.getBackwardReferenceMap().put(usage, fileId);
157         myIndex.getReferenceMap().put(fileId, usage);
158       }
159     }
160     else {
161       updateReferenceIndicesIncrementally(fileId, usages);
162     }
163   }
164
165   private void updateReferenceIndicesIncrementally(int fileId, Collection<LightUsage> usages) {
166     final Collection<LightUsage> rawOldUsages = myIndex.getReferenceMap().get(fileId);
167     Collection<LightUsage> oldUsages = rawOldUsages == null ? null : new ArrayList<LightUsage>(rawOldUsages);
168     for (LightUsage usage : usages) {
169       if (oldUsages == null || !oldUsages.remove(usage)) {
170         myIndex.getBackwardReferenceMap().put(usage, fileId);
171         myIndex.getReferenceMap().put(fileId, usage);
172       }
173     }
174     if (oldUsages != null && !oldUsages.isEmpty()) {
175       myIndex.getReferenceMap().removeAll(fileId, oldUsages);
176       for (LightUsage usage : oldUsages) {
177         myIndex.getBackwardReferenceMap().removeFrom(usage, fileId);
178       }
179     }
180   }
181
182   synchronized void writeHierarchy(int fileId, LightUsage.LightClassUsage aClass, LightUsage.LightClassUsage[] supers) {
183     CompilerBackwardReferenceIndex.LightDefinition def = new CompilerBackwardReferenceIndex.LightDefinition(aClass, fileId);
184     if (myRebuild) {
185       directlyWriteHierarchyIndices(def, supers);
186     }
187     else {
188       updateHierarchyIndicesIncrementally(def, supers);
189     }
190   }
191
192   private synchronized int enumeratePath(String file) {
193     try {
194       return myIndex.getFilePathEnumerator().enumerate(file);
195     }
196     catch (IOException e) {
197       throw new BuildDataCorruptedException(e);
198     }
199   }
200
201   private void directlyWriteHierarchyIndices(CompilerBackwardReferenceIndex.LightDefinition classId, LightUsage.LightClassUsage[] superIds) {
202     for (LightUsage.LightClassUsage superId : superIds) {
203       myIndex.getBackwardHierarchyMap().put(superId, classId);
204       myIndex.getHierarchyMap().put(classId, superId);
205     }
206   }
207
208   private void updateHierarchyIndicesIncrementally(final CompilerBackwardReferenceIndex.LightDefinition classId, LightUsage.LightClassUsage[] superIds) {
209     final Collection<LightUsage> rawOldSupers = myIndex.getHierarchyMap().get(classId);
210     Set<LightUsage> oldSuperClasses = rawOldSupers == null ? null : new THashSet<LightUsage>(rawOldSupers);
211     for (LightUsage.LightClassUsage superId: superIds) {
212       if (oldSuperClasses == null || !oldSuperClasses.remove(superId)) {
213         myIndex.getBackwardHierarchyMap().put(superId, classId);
214         myIndex.getHierarchyMap().put(classId, superId);
215       }
216     }
217     if (oldSuperClasses != null && !oldSuperClasses.isEmpty()) {
218       myIndex.getHierarchyMap().removeAll(classId, oldSuperClasses);
219       for (LightUsage anOldClass : oldSuperClasses) {
220         myIndex.getBackwardHierarchyMap().put(anOldClass, classId);
221       }
222     }
223   }
224
225
226   private void removeClassesFromHierarchy(int deletedFileId, Collection<LightUsage> definedClasses) {
227     if (definedClasses != null) {
228       for (LightUsage aClass : definedClasses) {
229         myIndex.getBackwardClassDefinitionMap().removeFrom(aClass, deletedFileId);
230         final CompilerBackwardReferenceIndex.LightDefinition def =
231           new CompilerBackwardReferenceIndex.LightDefinition(aClass, deletedFileId);
232         final Collection<LightUsage> superClasses = myIndex.getHierarchyMap().get(def);
233         if (superClasses != null) {
234           for (LightUsage superClass : superClasses) {
235             myIndex.getBackwardHierarchyMap().removeFrom(superClass, def);
236           }
237         }
238         myIndex.getHierarchyMap().remove(def);
239       }
240     }
241   }
242 }
243
244