| package com.intellij.codeInsight.dataflow.map; |
| |
| import com.intellij.codeInsight.dataflow.SetUtil; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| /** |
| * @author yole |
| */ |
| public class DFAMap<V> { |
| |
| // invariant: |
| // if myAll != null, the map contains more than one value, and all of them are in myAll |
| // if myAll == null && myK != null, the map contains only one value (myK, myV) |
| // if myAll == null && myK == null, the map is empty |
| private String myK; |
| private V myV; |
| private HashMap<String, V> myAll; |
| |
| private static final DFAMap ourEmptyMap = new DFAMap() { |
| @Override |
| public void put(String key, Object value) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public void remove(String name) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public DFAMap asWritable() { |
| return new DFAMap(); |
| } |
| }; |
| |
| public DFAMap() { |
| } |
| |
| private DFAMap(DFAMap<V> initialMap) { |
| myK = initialMap.myK; |
| myV = initialMap.myV; |
| myAll = initialMap.myAll == null ? null : new HashMap<String,V>(initialMap.myAll); |
| } |
| |
| public static <V> DFAMap<V> empty() { |
| //noinspection unchecked |
| return (DFAMap<V>) ourEmptyMap; |
| } |
| |
| public void addKeys(HashSet<String> allNames) { |
| if (myAll != null) { |
| allNames.addAll(myAll.keySet()); |
| } |
| else if (myK != null) { |
| allNames.add(myK); |
| } |
| } |
| |
| public void put(String key, V value) { |
| if ((myK == null || myK.equals(key)) && myAll == null) { |
| myK = key; |
| myV = value; |
| } |
| else { |
| if (myAll == null) { |
| myAll = new HashMap<String,V>(); |
| myAll.put(myK, myV); |
| } |
| myAll.put(key, value); |
| } |
| } |
| |
| @Nullable |
| public V get(String key) { |
| if (myAll != null) { |
| return myAll.get(key); |
| } |
| if (key.equals(myK)) { |
| return myV; |
| } |
| return null; |
| } |
| |
| public void remove(String name) { |
| if (myAll != null) { |
| myAll.remove(name); |
| if (myAll.size() == 1) { |
| final Map.Entry<String, V> e = myAll.entrySet().iterator().next(); |
| myK = e.getKey(); |
| myV = e.getValue(); |
| myAll = null; |
| } |
| } |
| else if (name.equals(myK)) { |
| myK = null; |
| myV = null; |
| } |
| } |
| |
| public boolean containsKey(String name) { |
| if (myAll != null) { |
| return myAll.containsKey(name); |
| } |
| return name.equals(myK); |
| } |
| |
| public Set<String> intersectKeys(@Nullable Set<String> names2Include) { |
| if (myAll != null) { |
| if (names2Include == null) return myAll.keySet(); |
| return SetUtil.intersect(names2Include, myAll.keySet()); |
| } |
| if (myK != null && (names2Include == null || names2Include.contains(myK))) { |
| if (names2Include != null && names2Include.size() == 1) return names2Include; |
| final HashSet<String> result = new HashSet<String>(); |
| result.add(myK); |
| return result; |
| } |
| return Collections.emptySet(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (!(obj instanceof DFAMap)) return false; |
| @SuppressWarnings({"unchecked"}) DFAMap<V> rhs = (DFAMap<V>) obj; |
| if (myAll == null) { |
| if (rhs.myAll != null) return false; |
| if (myK == null) return rhs.myK == null; |
| return myK.equals(rhs.myK) && myV.equals(rhs.myV); |
| } |
| else { |
| if (rhs.myAll == null) return false; |
| return myAll.equals(rhs.myAll); |
| } |
| } |
| |
| public Collection<V> values() { |
| if (myAll != null) { |
| return myAll.values(); |
| } |
| if (myV != null) { |
| return Collections.singletonList(myV); |
| } |
| return Collections.emptyList(); |
| } |
| |
| public Collection<? extends Map.Entry<String, V>> entrySet() { |
| if (myAll != null) { |
| return myAll.entrySet(); |
| } |
| if (myK != null) { |
| return Collections.singleton(new Map.Entry<String, V>() { |
| @Override |
| public String getKey() { |
| return myK; |
| } |
| |
| @Override |
| public V getValue() { |
| return myV; |
| } |
| |
| @Override |
| public V setValue(V value) { |
| throw new UnsupportedOperationException(); |
| } |
| }); |
| } |
| return Collections.emptyList(); |
| } |
| |
| public DFAMap<V> asWritable() { |
| return new DFAMap<V>(this); |
| } |
| |
| @Override |
| public String toString() { |
| if (this == ourEmptyMap){ |
| return "Empty Map"; |
| } |
| if (myAll != null){ |
| return myAll.toString(); |
| } |
| if (myK != null){ |
| return "{" + myK + "=" + myV + "}"; |
| } |
| return "Empty"; |
| } |
| |
| public Set<String> keySet() { |
| if (myAll != null){ |
| return myAll.keySet(); |
| } |
| return myK != null ? Collections.singleton(myK) : Collections.<String>emptySet(); |
| } |
| } |