make underlying map really immutable to simplify and avoid accidents

This commit is contained in:
Alexey Kudravtsev
2018-05-31 17:37:02 +03:00
parent 0c4cb96af7
commit 0979a4384a
2 changed files with 105 additions and 24 deletions
@@ -24,6 +24,7 @@ import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore; import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.UnmodifiableTHashMap;
import gnu.trove.THashMap; import gnu.trove.THashMap;
import gnu.trove.TObjectHashingStrategy; import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NonNls;
@@ -58,21 +59,15 @@ public class PsiSubstitutorImpl implements PsiSubstitutor {
private final SubstitutionVisitor mySimpleSubstitutionVisitor = new SubstitutionVisitor(); private final SubstitutionVisitor mySimpleSubstitutionVisitor = new SubstitutionVisitor();
private PsiSubstitutorImpl(@NotNull Map<PsiTypeParameter, PsiType> map) { private PsiSubstitutorImpl(@NotNull Map<PsiTypeParameter, PsiType> map) {
mySubstitutionMap = new THashMap<>(map, PSI_EQUIVALENCE); mySubstitutionMap = map;
}
private PsiSubstitutorImpl() {
mySubstitutionMap = new THashMap<>(2, PSI_EQUIVALENCE);
} }
PsiSubstitutorImpl(@NotNull PsiTypeParameter typeParameter, PsiType mapping) { PsiSubstitutorImpl(@NotNull PsiTypeParameter typeParameter, PsiType mapping) {
this(); this(Collections.singletonMap(typeParameter, mapping));
mySubstitutionMap.put(typeParameter, mapping);
} }
PsiSubstitutorImpl(@NotNull PsiClass parentClass, PsiType[] mappings) { PsiSubstitutorImpl(@NotNull PsiClass parentClass, PsiType[] mappings) {
this(); this(putAllInternal(Collections.emptyMap(), parentClass, mappings));
putAllInternal(parentClass, mappings);
} }
@Override @Override
@@ -304,45 +299,45 @@ public class PsiSubstitutorImpl implements PsiSubstitutor {
@Override @Override
protected PsiSubstitutorImpl clone() { protected PsiSubstitutorImpl clone() {
return new PsiSubstitutorImpl(mySubstitutionMap); throw new UnsupportedOperationException();
} }
@NotNull @NotNull
@Override @Override
public PsiSubstitutor put(@NotNull PsiTypeParameter typeParameter, PsiType mapping) { public PsiSubstitutor put(@NotNull PsiTypeParameter typeParameter, PsiType mapping) {
PsiSubstitutorImpl ret = clone();
if (mapping != null && !mapping.isValid()) { if (mapping != null && !mapping.isValid()) {
LOG.error("Invalid type in substitutor: " + mapping + "; " + mapping.getClass()); LOG.error("Invalid type in substitutor: " + mapping + "; " + mapping.getClass());
} }
ret.mySubstitutionMap.put(typeParameter, mapping); return new PsiSubstitutorImpl(new UnmodifiableTHashMap<>(PSI_EQUIVALENCE, mySubstitutionMap, typeParameter, mapping));
return ret;
} }
private void putAllInternal(@NotNull PsiClass parentClass, PsiType[] mappings) { private static Map<PsiTypeParameter, PsiType> putAllInternal(Map<PsiTypeParameter, PsiType> originalMap, @NotNull PsiClass parentClass,
PsiType[] mappings) {
final PsiTypeParameter[] params = parentClass.getTypeParameters(); final PsiTypeParameter[] params = parentClass.getTypeParameters();
if (params.length == 0) return originalMap;
Map<PsiTypeParameter, PsiType> newMap = new THashMap<>(originalMap);
for (int i = 0; i < params.length; i++) { for (int i = 0; i < params.length; i++) {
PsiTypeParameter param = params[i]; PsiTypeParameter param = params[i];
assert param != null; assert param != null;
if (mappings != null && mappings.length > i) { if (mappings != null && mappings.length > i) {
PsiType mapping = mappings[i]; PsiType mapping = mappings[i];
mySubstitutionMap.put(param, mapping); newMap.put(param, mapping);
if (mapping != null && !mapping.isValid()) { if (mapping != null && !mapping.isValid()) {
LOG.error("Invalid type in substitutor: " + mapping); LOG.error("Invalid type in substitutor: " + mapping);
} }
} }
else { else {
mySubstitutionMap.put(param, null); newMap.put(param, null);
} }
} }
return new UnmodifiableTHashMap<>(PSI_EQUIVALENCE, newMap);
} }
@NotNull @NotNull
@Override @Override
public PsiSubstitutor putAll(@NotNull PsiClass parentClass, PsiType[] mappings) { public PsiSubstitutor putAll(@NotNull PsiClass parentClass, PsiType[] mappings) {
PsiSubstitutorImpl substitutor = clone(); return new PsiSubstitutorImpl(putAllInternal(mySubstitutionMap, parentClass, mappings));
substitutor.putAllInternal(parentClass, mappings);
return substitutor;
} }
@NotNull @NotNull
@@ -350,9 +345,9 @@ public class PsiSubstitutorImpl implements PsiSubstitutor {
public PsiSubstitutor putAll(@NotNull PsiSubstitutor another) { public PsiSubstitutor putAll(@NotNull PsiSubstitutor another) {
if (another instanceof EmptySubstitutorImpl) return this; if (another instanceof EmptySubstitutorImpl) return this;
final PsiSubstitutorImpl anotherImpl = (PsiSubstitutorImpl)another; final PsiSubstitutorImpl anotherImpl = (PsiSubstitutorImpl)another;
PsiSubstitutorImpl substitutor = clone(); Map<PsiTypeParameter, PsiType> newMap = new THashMap<>(mySubstitutionMap, PSI_EQUIVALENCE);
substitutor.mySubstitutionMap.putAll(anotherImpl.mySubstitutionMap); newMap.putAll(anotherImpl.mySubstitutionMap);
return substitutor; return createSubstitutor(newMap);
} }
@Override @Override
@@ -389,7 +384,7 @@ public class PsiSubstitutorImpl implements PsiSubstitutor {
@NotNull @NotNull
public static PsiSubstitutor createSubstitutor(@Nullable Map<PsiTypeParameter, PsiType> map) { public static PsiSubstitutor createSubstitutor(@Nullable Map<PsiTypeParameter, PsiType> map) {
if (map == null || map.isEmpty()) return EMPTY; if (map == null || map.isEmpty()) return EMPTY;
return new PsiSubstitutorImpl(map); return new PsiSubstitutorImpl(new UnmodifiableTHashMap<>(map));
} }
@Override @Override
@@ -412,6 +407,6 @@ public class PsiSubstitutorImpl implements PsiSubstitutor {
@Override @Override
@NotNull @NotNull
public Map<PsiTypeParameter, PsiType> getSubstitutionMap() { public Map<PsiTypeParameter, PsiType> getSubstitutionMap() {
return Collections.unmodifiableMap(mySubstitutionMap); return mySubstitutionMap;
} }
} }
@@ -0,0 +1,86 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.containers;
import gnu.trove.THashMap;
import gnu.trove.TObjectFunction;
import gnu.trove.TObjectHashingStrategy;
import gnu.trove.TObjectObjectProcedure;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
public class UnmodifiableTHashMap<K,V> extends THashMap<K,V> {
public UnmodifiableTHashMap(@NotNull Map<? extends K, ? extends V> map) {
//noinspection unchecked
this(CANONICAL, map);
}
public UnmodifiableTHashMap(@NotNull TObjectHashingStrategy<K> strategy,
@NotNull Map<? extends K, ? extends V> map) {
super(map.size(), strategy);
for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
super.put(e.getKey(), e.getValue());
}
}
public UnmodifiableTHashMap(@NotNull TObjectHashingStrategy<K> strategy,
@NotNull Map<? extends K, ? extends V> map, K additionalKey, V additionalValue) {
this(strategy, map);
super.put(additionalKey, additionalValue);
}
@Override
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainEntries(TObjectObjectProcedure<K, V> procedure) {
throw new UnsupportedOperationException();
}
@Override
public void transformValues(TObjectFunction<V, V> function) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public V remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
protected void removeAt(int index) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Collection<V> values() {
return Collections.unmodifiableCollection(super.values());
}
@NotNull
@Override
public Set<K> keySet() {
return Collections.unmodifiableSet(super.keySet());
}
@NotNull
@Override
public Set<Map.Entry<K, V>> entrySet() {
return Collections.unmodifiableSet(super.entrySet());
}
}