001    /*
002     * Copyright 2010-2013 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.util.slicedmap;
018    
019    import com.intellij.openapi.diagnostic.Logger;
020    import org.jetbrains.annotations.NotNull;
021    
022    import java.util.Arrays;
023    import java.util.List;
024    
025    public class Slices {
026        private static final Logger LOG = Logger.getInstance(Slices.class);
027    
028        public static final RewritePolicy ONLY_REWRITE_TO_EQUAL = new RewritePolicy() {
029            @Override
030            public <K> boolean rewriteProcessingNeeded(K key) {
031                return true;
032            }
033    
034            @Override
035            public <K, V> boolean processRewrite(WritableSlice<K, V> slice, K key, V oldValue, V newValue) {
036                if (!((oldValue == null && newValue == null) || (oldValue != null && oldValue.equals(newValue)))) {
037                    // NOTE: Use BindingTraceContext.TRACK_REWRITES to debug this exception
038                    LOG.error("Rewrite at slice " + slice +
039                            " key: " + key +
040                            " old value: " + oldValue + '@' + System.identityHashCode(oldValue) +
041                            " new value: " + newValue + '@' + System.identityHashCode(newValue));
042                }
043                return true;
044            }
045        };
046    
047        private Slices() {
048        }
049    
050        public interface KeyNormalizer<K> {
051            KeyNormalizer DO_NOTHING = new KeyNormalizer<Object>() {
052                @Override
053                public Object normalize(Object key) {
054                    return key;
055                }
056            };
057    
058            K normalize(K key);
059        }
060    
061        public static <K, V> SliceBuilder<K, V> sliceBuilder() {
062            return new SliceBuilder<K, V>(ONLY_REWRITE_TO_EQUAL);
063        }
064    
065        public static <K, V> WritableSlice<K, V> createSimpleSlice() {
066            return new BasicWritableSlice<K, V>(ONLY_REWRITE_TO_EQUAL);
067        }
068    
069        public static <K, V> WritableSlice<K, V> createCollectiveSlice() {
070            return new BasicWritableSlice<K, V>(ONLY_REWRITE_TO_EQUAL, true);
071        }
072    
073        public static <K> WritableSlice<K, Boolean> createSimpleSetSlice() {
074            return createRemovableSetSlice();
075        }
076    
077        public static <K> WritableSlice<K, Boolean> createCollectiveSetSlice() {
078            return new SetSlice<K>(RewritePolicy.DO_NOTHING, true);
079        }
080    
081        public static <K> RemovableSlice<K, Boolean> createRemovableSetSlice() {
082            return new SetSlice<K>(RewritePolicy.DO_NOTHING, false);
083        }
084    
085        public static class SliceBuilder<K, V> {
086            private V defaultValue = null;
087            private List<ReadOnlySlice<K, V>> furtherLookupSlices = null;
088            private WritableSlice<? super V, ? super K> opposite = null;
089            private KeyNormalizer<K> keyNormalizer = null;
090    
091            private final RewritePolicy rewritePolicy;
092    
093            private String debugName;
094    
095            private SliceBuilder(RewritePolicy rewritePolicy) {
096                this.rewritePolicy = rewritePolicy;
097            }
098    
099            public SliceBuilder<K, V> setDefaultValue(V defaultValue) {
100                this.defaultValue = defaultValue;
101                return this;
102            }
103    
104            public SliceBuilder<K, V> setFurtherLookupSlices(ReadOnlySlice<K, V>... furtherLookupSlices) {
105                this.furtherLookupSlices = Arrays.asList(furtherLookupSlices);
106                return this;
107            }
108    
109            public SliceBuilder<K, V> setOpposite(WritableSlice<? super V, ? super K> opposite) {
110                this.opposite = opposite;
111                return this;
112            }
113    
114            public SliceBuilder<K, V> setDebugName(@NotNull String debugName) {
115                this.debugName = debugName;
116                return this;
117            }
118    
119            public SliceBuilder<K, V> setKeyNormalizer(KeyNormalizer<K> keyNormalizer) {
120                this.keyNormalizer = keyNormalizer;
121                return this;
122            }
123    
124            public RemovableSlice<K, V>  build() {
125                SliceWithOpposite<K, V> result = doBuild();
126                if (debugName != null) {
127                    result.setDebugName(debugName);
128                }
129                return result;
130            }
131    
132            private SliceWithOpposite<K, V> doBuild() {
133                if (defaultValue != null) {
134                    return new SliceWithOpposite<K, V>(rewritePolicy, opposite, keyNormalizer) {
135                        @Override
136                        public V computeValue(SlicedMap map, K key, V value, boolean valueNotFound) {
137                            if (valueNotFound) return defaultValue;
138                            return super.computeValue(map, key, value, false);
139                        }
140                    };
141                }
142                if (furtherLookupSlices != null) {
143                    return new SliceWithOpposite<K, V>(rewritePolicy, opposite, keyNormalizer) {
144                        @Override
145                        public V computeValue(SlicedMap map, K key, V value, boolean valueNotFound) {
146                            if (valueNotFound) {
147                                for (ReadOnlySlice<K, V> slice : furtherLookupSlices) {
148                                    V v = map.get(slice, key);
149                                    if (v != null) {
150                                        return v;
151                                    }
152                                }
153                                return defaultValue;
154                            }
155                            return super.computeValue(map, key, value, false);
156                        }
157                    };
158                }
159                return new SliceWithOpposite<K, V>(rewritePolicy, opposite, keyNormalizer);
160            }
161        }
162    
163        public static class BasicRemovableSlice<K, V> extends BasicWritableSlice<K, V> implements RemovableSlice<K, V> {
164            protected BasicRemovableSlice(RewritePolicy rewritePolicy) {
165                super(rewritePolicy);
166            }
167    
168            protected BasicRemovableSlice(RewritePolicy rewritePolicy, boolean isCollective) {
169                super(rewritePolicy, isCollective);
170            }
171        }
172    
173        public static class SliceWithOpposite<K, V> extends BasicRemovableSlice<K, V> {
174            private final WritableSlice<? super V, ? super K> opposite;
175            private final KeyNormalizer<K> keyNormalizer;
176    
177            public SliceWithOpposite(RewritePolicy rewritePolicy, WritableSlice<? super V, ? super K> opposite, KeyNormalizer<K> keyNormalizer) {
178                super(rewritePolicy);
179                this.opposite = opposite;
180                this.keyNormalizer = keyNormalizer;
181            }
182    
183            @Override
184            public void afterPut(MutableSlicedMap map, K key, V value) {
185                if (opposite != null) {
186                    map.put(opposite, value, key);
187                }
188            }
189            @Override
190            public SlicedMapKey<K, V> makeKey(K key) {
191                if (keyNormalizer == null) {
192                    return super.makeKey(key);
193                }
194                return super.makeKey(keyNormalizer.normalize(key));
195            }
196    
197        }
198    
199        public static class SetSlice<K> extends BasicRemovableSlice<K, Boolean> {
200    
201            protected SetSlice(RewritePolicy rewritePolicy) {
202                this(rewritePolicy, false);
203            }
204    
205            protected SetSlice(RewritePolicy rewritePolicy, boolean collective) {
206                super(rewritePolicy, collective);
207            }
208    
209            @Override
210            public Boolean computeValue(SlicedMap map, K key, Boolean value, boolean valueNotFound) {
211                Boolean result = super.computeValue(map, key, value, valueNotFound);
212                return result != null ? result : false;
213            }
214        }
215    
216    }