Python reaching defs fix leading to unbound local variable inspection false positives
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / dataflow / DFAMap.java
1 package com.intellij.codeInsight.dataflow;
2
3 import org.jetbrains.annotations.Nullable;
4
5 import java.util.*;
6
7 /**
8  * @author yole
9  */
10 public class DFAMap<V> {
11
12   // invariant:
13   // if myAll != null, the map contains more than one value, and all of them are in myAll
14   // if myAll == null && myK != null, the map contains only one value (myK, myV)
15   // if myAll == null && myK == null, the map is empty
16   private String myK;
17   private V myV;
18   private HashMap<String, V> myAll;
19
20   private static final DFAMap ourEmptyMap = new DFAMap() {
21     @Override
22     public void put(String key, Object value) {
23       throw new UnsupportedOperationException();
24     }
25
26     @Override
27     public void remove(String name) {
28       throw new UnsupportedOperationException();
29     }
30
31     @Override
32     public DFAMap asWritable() {
33       return new DFAMap();
34     }
35   };
36
37   public DFAMap() {
38   }
39
40   private DFAMap(DFAMap<V> initialMap) {
41     myK = initialMap.myK;
42     myV = initialMap.myV;
43     myAll = initialMap.myAll == null ? null : new HashMap<String,V>(initialMap.myAll);
44   }
45
46   public static <V> DFAMap<V> empty() {
47     //noinspection unchecked
48     return (DFAMap<V>) ourEmptyMap;
49   }
50
51   public void addKeys(HashSet<String> allNames) {
52     if (myAll != null) {
53       allNames.addAll(myAll.keySet());
54     }
55     else if (myK != null) {
56       allNames.add(myK);
57     }
58   }
59
60   public void put(String key, V value) {
61     if ((myK == null || myK.equals(key)) && myAll == null) {
62       myK = key;
63       myV = value;
64     }
65     else {
66       if (myAll == null) {
67         myAll = new HashMap<String,V>();
68         myAll.put(myK, myV);
69       }
70       myAll.put(key, value);
71     }
72   }
73
74   @Nullable
75   public V get(String key) {
76     if (myAll != null) {
77       return myAll.get(key);
78     }
79     if (key.equals(myK)) {
80       return myV;
81     }
82     return null;
83   }
84
85   public void remove(String name) {
86     if (myAll != null) {
87       myAll.remove(name);
88       if (myAll.size() == 1) {
89         final Map.Entry<String, V> e = myAll.entrySet().iterator().next();
90         myK = e.getKey();
91         myV = e.getValue();
92         myAll = null;
93       }
94     }
95     else if (name.equals(myK)) {
96       myK = null;
97       myV = null;
98     }
99   }
100
101   public boolean containsKey(String name) {
102     if (myAll != null) {
103       return myAll.containsKey(name);
104     }
105     return name.equals(myK);
106   }
107
108   public Set<String> intersectKeys(@Nullable Set<String> names2Include) {
109     if (myAll != null) {
110       if (names2Include == null) return myAll.keySet();
111       return SetUtil.intersect(names2Include, myAll.keySet());
112     }
113     if (myK != null && (names2Include == null || names2Include.contains(myK))) {
114       if (names2Include != null && names2Include.size() == 1) return names2Include;
115       final HashSet<String> result = new HashSet<String>();
116       result.add(myK);
117       return result;
118     }
119     return Collections.emptySet();
120   }
121
122   @Override
123   public boolean equals(Object obj) {
124     if (!(obj instanceof DFAMap)) return false;
125     @SuppressWarnings({"unchecked"}) DFAMap<V> rhs = (DFAMap<V>) obj;
126     if (myAll == null) {
127       if (rhs.myAll != null) return false;
128       if (myK == null) return rhs.myK == null;
129       return myK.equals(rhs.myK) && myV.equals(rhs.myV);
130     }
131     else {
132       if (rhs.myAll == null) return false;
133       return myAll.equals(rhs.myAll);
134     }
135   }
136
137   public Collection<V> values() {
138     if (myAll != null) {
139       return myAll.values();
140     }
141     if (myV != null) {
142       return Collections.singletonList(myV);
143     }
144     return Collections.emptyList();
145   }
146
147   public Collection<? extends Map.Entry<String, V>> entrySet() {
148     if (myAll != null) {
149       return myAll.entrySet();
150     }
151     if (myK != null) {
152       return Collections.singleton(new Map.Entry<String, V>() {
153         public String getKey() {
154           return myK;
155         }
156
157         public V getValue() {
158           return myV;
159         }
160
161         public V setValue(V value) {
162           throw new UnsupportedOperationException();
163         }
164       });
165     }
166     return Collections.emptyList();
167   }
168
169   public DFAMap<V> asWritable() {
170     return new DFAMap<V>(this);
171   }
172
173   @Override
174   public String toString() {
175     if (this == ourEmptyMap){
176       return "Empty Map";
177     }
178     if (myAll != null){
179       return myAll.toString();
180     }
181     if (myK != null){
182       return "{" + myK + "=" + myV + "}";
183     }
184     return "Empty";
185   }
186
187   public Set<String> keySet() {
188     if (myAll != null){
189       return myAll.keySet();
190     }
191     return myK != null ? Collections.singleton(myK) : Collections.<String>emptySet();
192   }
193 }