Merge branch 'db/method-chains-with-casts'
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / backwardRefs / BackwardReferenceIndexUtil.java
1 /*
2  * Copyright 2000-2017 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.openapi.diagnostic.Logger;
19 import com.intellij.openapi.util.Factory;
20 import com.intellij.util.SmartList;
21 import com.intellij.util.containers.ContainerUtil;
22 import gnu.trove.THashMap;
23 import gnu.trove.TObjectIntHashMap;
24 import org.jetbrains.jps.backwardRefs.index.CompiledFileData;
25 import org.jetbrains.jps.javac.ast.api.JavacDef;
26 import org.jetbrains.jps.javac.ast.api.JavacRef;
27 import org.jetbrains.jps.javac.ast.api.JavacTypeCast;
28
29 import java.io.IOException;
30 import java.util.Collection;
31 import java.util.HashMap;
32 import java.util.Map;
33
34 public class BackwardReferenceIndexUtil {
35   private static final Logger LOG = Logger.getInstance(BackwardReferenceIndexUtil.class);
36
37   static void registerFile(String filePath,
38                            TObjectIntHashMap<? extends JavacRef> refs,
39                            Collection<JavacDef> defs,
40                            Collection<JavacTypeCast> casts,
41                            final BackwardReferenceIndexWriter writer) {
42
43     try {
44       final int fileId = writer.enumeratePath(filePath);
45       int funExprId = 0;
46
47       Map<LightRef, Void> definitions = new HashMap<>(defs.size());
48       Map<LightRef, Collection<LightRef>> backwardHierarchyMap = new HashMap<>();
49       Map<SignatureData, Collection<LightRef>> signatureData = new THashMap<>();
50       THashMap<LightRef, Collection<LightRef>> castMap = new THashMap<>();
51
52
53       final AnonymousClassEnumerator anonymousClassEnumerator = new AnonymousClassEnumerator();
54
55       for (JavacDef def : defs) {
56         if (def instanceof JavacDef.JavacClassDef) {
57           JavacRef.JavacClass sym = (JavacRef.JavacClass)def.getDefinedElement();
58
59           final LightRef.LightClassHierarchyElementDef aClass;
60           if (sym.isAnonymous()) {
61             final JavacRef[] classes = ((JavacDef.JavacClassDef)def).getSuperClasses();
62             if (classes.length == 0) {
63               LOG.info("Seems that compilation will finish with errors in anonymous class inside file " + filePath);
64               continue;
65             }
66             aClass = anonymousClassEnumerator.addAnonymous(sym.getName(), writer.asClassUsage(classes[0]));
67           } else {
68             aClass = writer.asClassUsage(sym);
69           }
70           definitions.put(aClass, null);
71
72           final JavacRef[] superClasses = ((JavacDef.JavacClassDef)def).getSuperClasses();
73           for (JavacRef superClass : superClasses) {
74             LightRef.JavaLightClassRef superClassRef = writer.asClassUsage(superClass);
75
76             backwardHierarchyMap.computeIfAbsent(superClassRef, k -> new SmartList<>()).add(aClass);
77           }
78         }
79         else if (def instanceof JavacDef.JavacFunExprDef) {
80           final LightRef.JavaLightClassRef functionalType = writer.asClassUsage(def.getDefinedElement());
81           int id = funExprId++;
82           LightRef.JavaLightFunExprDef result = new LightRef.JavaLightFunExprDef(id);
83           definitions.put(result, null);
84
85           ContainerUtil.getOrCreate(backwardHierarchyMap, functionalType,
86                                     (Factory<Collection<LightRef>>)() -> new SmartList<>()).add(result);
87         }
88         else if (def instanceof JavacDef.JavacMemberDef) {
89           final LightRef ref = writer.enumerateNames(def.getDefinedElement(), name -> anonymousClassEnumerator.getLightRefIfAnonymous(name));
90           final LightRef.JavaLightClassRef returnType = writer.asClassUsage(((JavacDef.JavacMemberDef)def).getReturnType());
91           if (ref != null && returnType != null) {
92             final SignatureData data = new SignatureData(returnType.getName(), ((JavacDef.JavacMemberDef)def).getIteratorKind(), ((JavacDef.JavacMemberDef)def).isStatic());
93             signatureData.computeIfAbsent(data, element -> new SmartList<>()).add(ref);
94           }
95         }
96       }
97
98       Map<LightRef, Integer> convertedRefs = new THashMap<>();
99       IOException[] exception = new IOException[]{null};
100       refs.forEachEntry((ref, count) -> {
101         final LightRef lightRef;
102         try {
103           lightRef = writer.enumerateNames(ref, name -> anonymousClassEnumerator.getLightRefIfAnonymous(name));
104           if (lightRef != null) {
105             Integer old = convertedRefs.get(lightRef);
106             convertedRefs.put(lightRef, old == null ? count : (old + count));
107           }
108         }
109         catch (IOException e) {
110           exception[0] = e;
111           return false;
112         }
113         return true;
114       });
115       if (exception[0] != null) {
116         throw exception[0];
117       }
118
119       for (JavacTypeCast cast : casts) {
120         LightRef enumeratedCastType = writer.enumerateNames(cast.getCastType(), name -> null);
121         if (enumeratedCastType == null) continue;
122         LightRef enumeratedOperandType = writer.enumerateNames(cast.getOperandType(), name -> null);
123         if (enumeratedOperandType == null) continue;
124         castMap.computeIfAbsent(enumeratedCastType, t -> new SmartList<>()).add(enumeratedOperandType);
125       }
126
127       writer.writeData(fileId, new CompiledFileData(backwardHierarchyMap, castMap, convertedRefs, definitions, signatureData));
128     }
129     catch (IOException e) {
130       writer.setRebuildCause(e);
131     }
132   }
133
134   private static class AnonymousClassEnumerator {
135     private THashMap<String, LightRef.LightClassHierarchyElementDef> myAnonymousName2Id = null;
136
137     private LightRef.JavaLightAnonymousClassRef addAnonymous(String internalName,
138                                                              LightRef.JavaLightClassRef base) {
139       if (myAnonymousName2Id == null) {
140         myAnonymousName2Id = new THashMap<>();
141       }
142       final int anonymousIdx = myAnonymousName2Id.size();
143       myAnonymousName2Id.put(internalName, base);
144       return new LightRef.JavaLightAnonymousClassRef(anonymousIdx);
145     }
146
147     private Integer getLightRefIfAnonymous(String className) {
148       if (myAnonymousName2Id == null) return null;
149       final LightRef.LightClassHierarchyElementDef ref = myAnonymousName2Id.get(className);
150       return ref == null ? null : ref.getName();
151     }
152   }
153 }