diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java index d83b7b1ae9ce..0de7124af8e0 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java @@ -24,6 +24,7 @@ import com.intellij.psi.impl.source.PsiClassReferenceType; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.UnmodifiableTHashMap; import gnu.trove.THashMap; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NonNls; @@ -58,21 +59,15 @@ public class PsiSubstitutorImpl implements PsiSubstitutor { private final SubstitutionVisitor mySimpleSubstitutionVisitor = new SubstitutionVisitor(); private PsiSubstitutorImpl(@NotNull Map map) { - mySubstitutionMap = new THashMap<>(map, PSI_EQUIVALENCE); - } - - private PsiSubstitutorImpl() { - mySubstitutionMap = new THashMap<>(2, PSI_EQUIVALENCE); + mySubstitutionMap = map; } PsiSubstitutorImpl(@NotNull PsiTypeParameter typeParameter, PsiType mapping) { - this(); - mySubstitutionMap.put(typeParameter, mapping); + this(Collections.singletonMap(typeParameter, mapping)); } PsiSubstitutorImpl(@NotNull PsiClass parentClass, PsiType[] mappings) { - this(); - putAllInternal(parentClass, mappings); + this(putAllInternal(Collections.emptyMap(), parentClass, mappings)); } @Override @@ -304,45 +299,45 @@ public class PsiSubstitutorImpl implements PsiSubstitutor { @Override protected PsiSubstitutorImpl clone() { - return new PsiSubstitutorImpl(mySubstitutionMap); + throw new UnsupportedOperationException(); } @NotNull @Override public PsiSubstitutor put(@NotNull PsiTypeParameter typeParameter, PsiType mapping) { - PsiSubstitutorImpl ret = clone(); if (mapping != null && !mapping.isValid()) { LOG.error("Invalid type in substitutor: " + mapping + "; " + mapping.getClass()); } - ret.mySubstitutionMap.put(typeParameter, mapping); - return ret; + return new PsiSubstitutorImpl(new UnmodifiableTHashMap<>(PSI_EQUIVALENCE, mySubstitutionMap, typeParameter, mapping)); } - private void putAllInternal(@NotNull PsiClass parentClass, PsiType[] mappings) { + private static Map putAllInternal(Map originalMap, @NotNull PsiClass parentClass, + PsiType[] mappings) { final PsiTypeParameter[] params = parentClass.getTypeParameters(); + if (params.length == 0) return originalMap; + Map newMap = new THashMap<>(originalMap); for (int i = 0; i < params.length; i++) { PsiTypeParameter param = params[i]; assert param != null; if (mappings != null && mappings.length > i) { PsiType mapping = mappings[i]; - mySubstitutionMap.put(param, mapping); + newMap.put(param, mapping); if (mapping != null && !mapping.isValid()) { LOG.error("Invalid type in substitutor: " + mapping); } } else { - mySubstitutionMap.put(param, null); + newMap.put(param, null); } } + return new UnmodifiableTHashMap<>(PSI_EQUIVALENCE, newMap); } @NotNull @Override public PsiSubstitutor putAll(@NotNull PsiClass parentClass, PsiType[] mappings) { - PsiSubstitutorImpl substitutor = clone(); - substitutor.putAllInternal(parentClass, mappings); - return substitutor; + return new PsiSubstitutorImpl(putAllInternal(mySubstitutionMap, parentClass, mappings)); } @NotNull @@ -350,9 +345,9 @@ public class PsiSubstitutorImpl implements PsiSubstitutor { public PsiSubstitutor putAll(@NotNull PsiSubstitutor another) { if (another instanceof EmptySubstitutorImpl) return this; final PsiSubstitutorImpl anotherImpl = (PsiSubstitutorImpl)another; - PsiSubstitutorImpl substitutor = clone(); - substitutor.mySubstitutionMap.putAll(anotherImpl.mySubstitutionMap); - return substitutor; + Map newMap = new THashMap<>(mySubstitutionMap, PSI_EQUIVALENCE); + newMap.putAll(anotherImpl.mySubstitutionMap); + return createSubstitutor(newMap); } @Override @@ -389,7 +384,7 @@ public class PsiSubstitutorImpl implements PsiSubstitutor { @NotNull public static PsiSubstitutor createSubstitutor(@Nullable Map map) { if (map == null || map.isEmpty()) return EMPTY; - return new PsiSubstitutorImpl(map); + return new PsiSubstitutorImpl(new UnmodifiableTHashMap<>(map)); } @Override @@ -412,6 +407,6 @@ public class PsiSubstitutorImpl implements PsiSubstitutor { @Override @NotNull public Map getSubstitutionMap() { - return Collections.unmodifiableMap(mySubstitutionMap); + return mySubstitutionMap; } } diff --git a/platform/util/src/com/intellij/util/containers/UnmodifiableTHashMap.java b/platform/util/src/com/intellij/util/containers/UnmodifiableTHashMap.java new file mode 100644 index 000000000000..40f58c53884e --- /dev/null +++ b/platform/util/src/com/intellij/util/containers/UnmodifiableTHashMap.java @@ -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 extends THashMap { + public UnmodifiableTHashMap(@NotNull Map map) { + //noinspection unchecked + this(CANONICAL, map); + } + + public UnmodifiableTHashMap(@NotNull TObjectHashingStrategy strategy, + @NotNull Map map) { + super(map.size(), strategy); + for (Map.Entry e : map.entrySet()) { + super.put(e.getKey(), e.getValue()); + } + } + public UnmodifiableTHashMap(@NotNull TObjectHashingStrategy strategy, + @NotNull Map 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 procedure) { + throw new UnsupportedOperationException(); + } + + @Override + public void transformValues(TObjectFunction 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 map) { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Collection values() { + return Collections.unmodifiableCollection(super.values()); + } + + @NotNull + @Override + public Set keySet() { + return Collections.unmodifiableSet(super.keySet()); + } + + @NotNull + @Override + public Set> entrySet() { + return Collections.unmodifiableSet(super.entrySet()); + } +}