blob: 2d84c928db22ebf5ea21566297c4a6b29c023dd9 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.stubs;
import com.intellij.openapi.util.Key;
import gnu.trove.THashMap;
import gnu.trove.TObjectObjectProcedure;
import gnu.trove.TObjectProcedure;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Dmitry Avdeev
* Date: 8/3/12
*/
public class ObjectStubTree<T extends Stub> {
private static final Key<ObjectStubTree> STUB_TO_TREE_REFERENCE = Key.create("stub to tree reference");
protected final ObjectStubBase myRoot;
private String myDebugInfo;
protected final List<T> myPlainList = new ArrayList<T>();
public ObjectStubTree(@NotNull final ObjectStubBase root, final boolean withBackReference) {
myRoot = root;
enumerateStubs(root, (List<Stub>)myPlainList);
if (withBackReference) {
myRoot.putUserData(STUB_TO_TREE_REFERENCE, this); // This will prevent soft references to stub tree to be collected before all of the stubs are collected.
}
}
@NotNull
public Stub getRoot() {
return myRoot;
}
@NotNull
public List<T> getPlainList() {
return myPlainList;
}
@NotNull
public Map<StubIndexKey, Map<Object, int[]>> indexStubTree() {
StubIndexSink sink = new StubIndexSink();
for (int i = 0, plainListSize = myPlainList.size(); i < plainListSize; i++) {
final Stub stub = myPlainList.get(i);
sink.myStubIdx = i;
StubSerializationUtil.getSerializer(stub).indexStub(stub, sink);
}
return sink.getResult();
}
protected static void enumerateStubs(@NotNull Stub root, @NotNull List<Stub> result) {
((ObjectStubBase)root).id = result.size();
result.add(root);
for (Stub child : root.getChildrenStubs()) {
enumerateStubs(child, result);
}
}
public void setDebugInfo(String info) {
ObjectStubTree ref = myRoot.getUserData(STUB_TO_TREE_REFERENCE);
if (ref != null) {
assert ref == this;
info += "; with backReference";
}
myDebugInfo = info;
}
public String getDebugInfo() {
return myDebugInfo;
}
private static class StubIndexSink implements IndexSink, TObjectProcedure<Map<Object, int[]>>,TObjectObjectProcedure<Object,int[]> {
private final THashMap<StubIndexKey, Map<Object, int[]>> myResult = new THashMap<StubIndexKey, Map<Object, int[]>>();
private int myStubIdx;
private Map<Object, int[]> myProcessingMap;
@Override
public void occurrence(@NotNull final StubIndexKey indexKey, @NotNull final Object value) {
Map<Object, int[]> map = myResult.get(indexKey);
if (map == null) {
map = new THashMap<Object, int[]>();
myResult.put(indexKey, map);
}
int[] list = map.get(value);
if (list == null) {
map.put(value, new int[] {myStubIdx});
} else {
int lastZero;
for(lastZero = list.length - 1; lastZero >=0 && list[lastZero] == 0; --lastZero);
++lastZero;
if (lastZero == list.length) {
int[] newlist = new int[Math.max(4, list.length << 1)];
System.arraycopy(list, 0, newlist, 0, list.length);
lastZero = list.length;
map.put(value, list = newlist);
}
list[lastZero] = myStubIdx;
}
}
public Map<StubIndexKey, Map<Object, int[]>> getResult() {
myResult.forEachValue(this);
return myResult;
}
@Override
public boolean execute(Map<Object, int[]> object) {
myProcessingMap = object;
((THashMap<Object, int[]>)object).forEachEntry(this);
return true;
}
@Override
public boolean execute(Object a, int[] b) {
if (b.length == 1) return true;
int firstZero;
for(firstZero = 0; firstZero < b.length && b[firstZero] != 0; ++firstZero);
if (firstZero != b.length) {
int[] shorterList = new int[firstZero];
System.arraycopy(b, 0, shorterList, 0, shorterList.length);
myProcessingMap.put(a, shorterList);
}
return true;
}
}
}