2d56e3425b7cadd5f904457777d16453e7d7aec3
[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.SystemProperties;
19 import com.sun.tools.javac.code.Symbol;
20 import gnu.trove.TIntHashSet;
21 import gnu.trove.TIntProcedure;
22 import org.jetbrains.annotations.NotNull;
23 import org.jetbrains.jps.builders.java.JavaBuilderUtil;
24 import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
25 import org.jetbrains.jps.incremental.CompileContext;
26 import org.jetbrains.jps.incremental.java.JavaBuilder;
27 import org.jetbrains.jps.incremental.storage.BuildDataManager;
28 import org.jetbrains.jps.javac.ast.api.JavacRefSymbol;
29 import org.jetbrains.jps.model.java.compiler.JavaCompilers;
30
31 import javax.tools.*;
32 import java.io.File;
33 import java.io.IOException;
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.Set;
37
38 public class BackwardReferenceIndexWriter {
39   public static final String PROP_KEY = "ref.index.builder";
40
41   private static volatile BackwardReferenceIndexWriter ourInstance;
42
43   private final CompilerBackwardReferenceIndex myIndex;
44   private final boolean myRebuild;
45
46   private BackwardReferenceIndexWriter(CompilerBackwardReferenceIndex index, boolean rebuild) {
47     myIndex = index;
48     myRebuild = rebuild;
49   }
50
51   static BackwardReferenceIndexWriter getInstance() {
52     return ourInstance;
53   }
54
55   static void initialize(@NotNull CompileContext context) {
56     if (isEnabled()) {
57       final BuildDataManager dataManager = context.getProjectDescriptor().dataManager;
58       final File buildDir = dataManager.getDataPaths().getDataStorageRoot();
59       boolean isRebuild = JavaBuilderUtil.isForcedRecompilationAllJavaModules(context);
60
61       if (!JavaCompilers.JAVAC_ID.equals(JavaBuilder.getUsedCompilerId(context))) {
62         CompilerBackwardReferenceIndex.removeIndexFiles(buildDir);
63         return;
64       }
65       if (isRebuild) {
66         CompilerBackwardReferenceIndex.removeIndexFiles(buildDir);
67       }
68       else if (CompilerBackwardReferenceIndex.versionDiffers(buildDir)) {
69         throw new BuildDataCorruptedException(new IOException("backward reference index should be updated to actual version"));
70       }
71
72       if (CompilerBackwardReferenceIndex.exist(buildDir) || isRebuild) {
73         ourInstance = new BackwardReferenceIndexWriter(new CompilerBackwardReferenceIndex(buildDir), isRebuild);
74       }
75     }
76   }
77
78   static boolean isEnabled() {
79     return SystemProperties.getBooleanProperty(PROP_KEY, false);
80   }
81
82   void closeIfNeed() {
83     myIndex.close();
84   }
85
86   synchronized void writeReferences(JavaFileObject file, Set<JavacRefSymbol> refs) {
87     final LightUsage[] usages = new LightUsage[refs.size()];
88     int idx = 0;
89     final ByteArrayEnumerator byteSeqEum = myIndex.getByteSeqEum();
90     for (JavacRefSymbol ref : refs) {
91       usages[idx++] = LightUsage.fromSymbol(ref.getSymbol(), byteSeqEum);
92     }
93
94     final int fileId = enumerateFile(file);
95     if (myRebuild) {
96       for (LightUsage usage : usages) {
97         myIndex.getBackwardReferenceMap().put(usage, fileId);
98         myIndex.getReferenceMap().put(fileId, usage);
99       }
100     }
101     else {
102       updateReferenceIndicesIncrementally(fileId, usages);
103     }
104   }
105
106   private void updateReferenceIndicesIncrementally(int fileId, LightUsage[] usages) {
107     final Collection<LightUsage> rawOldUsages = myIndex.getReferenceMap().get(fileId);
108     Collection<LightUsage> oldUsages = rawOldUsages == null ? null : new ArrayList<LightUsage>(rawOldUsages);
109     for (LightUsage usage : usages) {
110       if (oldUsages == null || !oldUsages.remove(usage)) {
111         myIndex.getBackwardReferenceMap().put(usage, fileId);
112         myIndex.getReferenceMap().put(fileId, usage);
113       }
114     }
115     if (oldUsages != null && !oldUsages.isEmpty()) {
116       myIndex.getReferenceMap().removeAll(fileId, oldUsages);
117       for (LightUsage usage : oldUsages) {
118         myIndex.getBackwardReferenceMap().removeFrom(usage, fileId);
119       }
120     }
121   }
122
123   synchronized void writeHierarchy(Symbol name, Symbol[] supers) {
124     if (supers.length == 0) {
125       return;
126     }
127     final int classId = classId(name);
128     final int[] superIds = new int[supers.length];
129     for (int i = 0; i < supers.length; i++) {
130       superIds[i] = classId(supers[i]);
131     }
132
133     if (myRebuild) {
134       directlyWriteHierarchyIndices(classId, superIds);
135     }
136     else {
137       updateHierarchyIndicesIncrementally(classId, superIds);
138     }
139   }
140
141   private void directlyWriteHierarchyIndices(int classId, int[] superIds) {
142     for (int superId : superIds) {
143       myIndex.getBackwardHierarchyMap().put(superId, classId);
144       myIndex.getHierarchyMap().put(classId, superId);
145     }
146   }
147
148   private void updateHierarchyIndicesIncrementally(final int classId, int[] superIds) {
149     final TIntHashSet rawOldSupers = myIndex.getHierarchyMap().get(classId);
150     TIntHashSet oldSuperClasses = rawOldSupers == null ? null : new TIntHashSet(rawOldSupers);
151     for (int superId: superIds) {
152       if (oldSuperClasses == null || !oldSuperClasses.remove(superId)) {
153         myIndex.getBackwardHierarchyMap().put(superId, classId);
154         myIndex.getHierarchyMap().put(classId, superId);
155       }
156     }
157     if (oldSuperClasses != null && !oldSuperClasses.isEmpty()) {
158       myIndex.getHierarchyMap().removeAll(classId, oldSuperClasses);
159       oldSuperClasses.forEach(new TIntProcedure() {
160         @Override
161         public boolean execute(int oldSuperId) {
162           myIndex.getBackwardHierarchyMap().put(oldSuperId, classId);
163           return true;
164         }
165       });
166     }
167   }
168
169   private int classId(Symbol name) {
170     return myIndex.getByteSeqEum().enumerate(LightUsage.bytes(name));
171   }
172
173   private int enumerateFile(JavaFileObject file) {
174     try {
175       return myIndex.getFilePathEnumerator().enumerate(file.getName());
176     }
177     catch (IOException e) {
178       throw new BuildDataCorruptedException(e);
179     }
180   }
181 }
182
183