Bug 575561 Fix ConcurrentModificationException in SymbolsModel
Change-Id: Id2fbc612889432fecb06b031aa36e2e501d6f8ea
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsModel.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsModel.java
index f7a207e..569d128 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsModel.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/outline/SymbolsModel.java
@@ -21,7 +21,6 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.function.Function;
import org.eclipse.core.resources.IFile;
@@ -37,9 +36,10 @@
private static final SymbolInformation ROOT_SYMBOL_INFORMATION = new SymbolInformation();
private static final Object[] EMPTY = new Object[0];
- private Map<SymbolInformation, List<SymbolInformation>> childrenMap = new HashMap<>();
- private List<DocumentSymbol> rootSymbols = new ArrayList<>();
+ private volatile Map<SymbolInformation, List<SymbolInformation>> childrenMap = Collections.emptyMap();
+ private volatile List<DocumentSymbol> rootSymbols = Collections.emptyList();
private Map<DocumentSymbol, DocumentSymbol> parent = new HashMap<>();
+
private IFile file;
public static class DocumentSymbolWithFile {
@@ -66,12 +66,16 @@
}
}
- public boolean update(List<Either<SymbolInformation, DocumentSymbol>> response) {
+ public synchronized boolean update(List<Either<SymbolInformation, DocumentSymbol>> response) {
// TODO update model only on real change
- childrenMap.clear();
- rootSymbols.clear();
parent.clear();
- if (response != null && !response.isEmpty()) {
+ if (response == null || response.isEmpty()) {
+ childrenMap = Collections.emptyMap();
+ rootSymbols = Collections.emptyList();
+ } else {
+ final Map<SymbolInformation, List<SymbolInformation>> newChildrenMap = new HashMap<>();
+ final List<DocumentSymbol> newRootSymbols = new ArrayList<>();
+
Collections.sort(response, Comparator.comparing(
either -> either.isLeft() ? either.getLeft().getLocation().getRange().getStart()
: either.getRight().getRange().getStart(),
@@ -87,21 +91,24 @@
SymbolInformation symbol = either.getLeft();
if (isIncluded(previousSymbol, symbol)) {
parentStack.push(previousSymbol);
- addChild(parentStack.peek(), symbol);
+ addChild(newChildrenMap, parentStack.peek(), symbol);
} else if (isIncluded(parentStack.peek(), symbol)) {
- addChild(parentStack.peek(), symbol);
+ addChild(newChildrenMap, parentStack.peek(), symbol);
} else {
while (!isIncluded(parentStack.peek(), symbol)) {
parentStack.pop();
}
- addChild(parentStack.peek(), symbol);
+ addChild(newChildrenMap, parentStack.peek(), symbol);
parentStack.push(symbol);
}
previousSymbol = symbol;
} else if (either.isRight()) {
- rootSymbols.add(either.getRight());
+ newRootSymbols.add(either.getRight());
}
}
+
+ childrenMap = newChildrenMap;
+ rootSymbols = newRootSymbols;
}
return true;
}
@@ -128,8 +135,9 @@
|| (included.getLine() == reference.getLine() && included.getCharacter() >= reference.getCharacter());
}
- private void addChild(SymbolInformation parent, SymbolInformation child) {
- List<SymbolInformation> children = childrenMap.computeIfAbsent(parent, key -> new ArrayList<>());
+ private void addChild(Map<SymbolInformation, List<SymbolInformation>> newChildrenMap, SymbolInformation parent,
+ SymbolInformation child) {
+ List<SymbolInformation> children = newChildrenMap.computeIfAbsent(parent, key -> new ArrayList<>());
children.add(child);
}
@@ -183,11 +191,11 @@
public Object getParent(Object element) {
if (element instanceof SymbolInformation) {
- Optional<SymbolInformation> result = childrenMap.keySet().stream().filter(parent -> {
- List<SymbolInformation> children = childrenMap.get(parent);
- return children == null ? false : children.contains(element);
- }).findFirst();
- return result.isPresent() ? result.get() : null;
+ for(Map.Entry<SymbolInformation, List<SymbolInformation>> entry: childrenMap.entrySet()) {
+ if(entry.getValue().contains(element)) {
+ return entry.getKey();
+ }
+ }
} else if (element instanceof DocumentSymbol) {
return parent.get(element);
} else if (element instanceof DocumentSymbolWithFile) {