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 org.jetbrains.annotations.NotNull;
020    
021    import java.util.Arrays;
022    import java.util.List;
023    
024    public class Slices {
025    
026        public static final RewritePolicy ONLY_REWRITE_TO_EQUAL = new RewritePolicy() {
027            @Override
028            public <K> boolean rewriteProcessingNeeded(K key) {
029                return true;
030            }
031    
032            @Override
033            public <K, V> boolean processRewrite(WritableSlice<K, V> slice, K key, V oldValue, V newValue) {
034                if (!((oldValue == null && newValue == null) || (oldValue != null && oldValue.equals(newValue)))) {
035                    // NOTE: Use BindingTraceContext.TRACK_REWRITES to debug this exception
036                    throw new IllegalStateException("Rewrite at slice " + slice +
037                            " key: " + key +
038                            " old value: " + oldValue + '@' + System.identityHashCode(oldValue) +
039                            " new value: " + newValue + '@' + System.identityHashCode(newValue));
040                }
041                return true;
042            }
043        };
044    
045        private Slices() {
046        }
047    
048        public interface KeyNormalizer<K> {
049            KeyNormalizer DO_NOTHING = new KeyNormalizer<Object>() {
050                @Override
051                public Object normalize(Object key) {
052                    return key;
053                }
054            };
055    
056            K normalize(K key);
057        }
058    
059        public static <K, V> SliceBuilder<K, V> sliceBuilder() {
060            return new SliceBuilder<K, V>(ONLY_REWRITE_TO_EQUAL);
061        }
062    
063        public static <K, V> WritableSlice<K, V> createSimpleSlice() {
064            return new BasicWritableSlice<K, V>(ONLY_REWRITE_TO_EQUAL);
065        }
066    
067        public static <K> WritableSlice<K, Boolean> createSimpleSetSlice() {
068            return createRemovableSetSlice();
069        }
070    
071        public static <K> WritableSlice<K, Boolean> createCollectiveSetSlice() {
072            return new SetSlice<K>(RewritePolicy.DO_NOTHING, true);
073        }
074    
075        public static <K> RemovableSlice<K, Boolean> createRemovableSetSlice() {
076            return new SetSlice<K>(RewritePolicy.DO_NOTHING, false);
077        }
078    
079        public static class SliceBuilder<K, V> {
080            private V defaultValue = null;
081            private List<ReadOnlySlice<K, V>> furtherLookupSlices = null;
082            private WritableSlice<? super V, ? super K> opposite = null;
083            private KeyNormalizer<K> keyNormalizer = null;
084    
085            private final RewritePolicy rewritePolicy;
086    
087            private String debugName;
088    
089            private SliceBuilder(RewritePolicy rewritePolicy) {
090                this.rewritePolicy = rewritePolicy;
091            }
092    
093            public SliceBuilder<K, V> setDefaultValue(V defaultValue) {
094                this.defaultValue = defaultValue;
095                return this;
096            }
097    
098            public SliceBuilder<K, V> setFurtherLookupSlices(ReadOnlySlice<K, V>... furtherLookupSlices) {
099                this.furtherLookupSlices = Arrays.asList(furtherLookupSlices);
100                return this;
101            }
102    
103            public SliceBuilder<K, V> setOpposite(WritableSlice<? super V, ? super K> opposite) {
104                this.opposite = opposite;
105                return this;
106            }
107    
108            public SliceBuilder<K, V> setDebugName(@NotNull String debugName) {
109                this.debugName = debugName;
110                return this;
111            }
112    
113            public SliceBuilder<K, V> setKeyNormalizer(KeyNormalizer<K> keyNormalizer) {
114                this.keyNormalizer = keyNormalizer;
115                return this;
116            }
117    
118            public RemovableSlice<K, V>  build() {
119                SliceWithOpposite<K, V> result = doBuild();
120                if (debugName != null) {
121                    result.setDebugName(debugName);
122                }
123                return result;
124            }
125    
126            private SliceWithOpposite<K, V> doBuild() {
127                if (defaultValue != null) {
128                    return new SliceWithOpposite<K, V>(rewritePolicy, opposite, keyNormalizer) {
129                        @Override
130                        public V computeValue(SlicedMap map, K key, V value, boolean valueNotFound) {
131                            if (valueNotFound) return defaultValue;
132                            return super.computeValue(map, key, value, false);
133                        }
134                    };
135                }
136                if (furtherLookupSlices != null) {
137                    return new SliceWithOpposite<K, V>(rewritePolicy, opposite, keyNormalizer) {
138                        @Override
139                        public V computeValue(SlicedMap map, K key, V value, boolean valueNotFound) {
140                            if (valueNotFound) {
141                                for (ReadOnlySlice<K, V> slice : furtherLookupSlices) {
142                                    V v = map.get(slice, key);
143                                    if (v != null) {
144                                        return v;
145                                    }
146                                }
147                                return defaultValue;
148                            }
149                            return super.computeValue(map, key, value, false);
150                        }
151                    };
152                }
153                return new SliceWithOpposite<K, V>(rewritePolicy, opposite, keyNormalizer);
154            }
155        }
156    
157        public static class BasicRemovableSlice<K, V> extends BasicWritableSlice<K, V> implements RemovableSlice<K, V> {
158            protected BasicRemovableSlice(RewritePolicy rewritePolicy) {
159                super(rewritePolicy);
160            }
161    
162            protected BasicRemovableSlice(RewritePolicy rewritePolicy, boolean isCollective) {
163                super(rewritePolicy, isCollective);
164            }
165        }
166    
167        public static class SliceWithOpposite<K, V> extends BasicRemovableSlice<K, V> {
168            private final WritableSlice<? super V, ? super K> opposite;
169            private final KeyNormalizer<K> keyNormalizer;
170    
171            public SliceWithOpposite(RewritePolicy rewritePolicy, WritableSlice<? super V, ? super K> opposite, KeyNormalizer<K> keyNormalizer) {
172                super(rewritePolicy);
173                this.opposite = opposite;
174                this.keyNormalizer = keyNormalizer;
175            }
176    
177            @Override
178            public void afterPut(MutableSlicedMap map, K key, V value) {
179                if (opposite != null) {
180                    map.put(opposite, value, key);
181                }
182            }
183            @Override
184            public SlicedMapKey<K, V> makeKey(K key) {
185                if (keyNormalizer == null) {
186                    return super.makeKey(key);
187                }
188                return super.makeKey(keyNormalizer.normalize(key));
189            }
190    
191        }
192    
193        public static class SetSlice<K> extends BasicRemovableSlice<K, Boolean> {
194    
195            protected SetSlice(RewritePolicy rewritePolicy) {
196                this(rewritePolicy, false);
197            }
198    
199            protected SetSlice(RewritePolicy rewritePolicy, boolean collective) {
200                super(rewritePolicy, collective);
201            }
202    
203            @Override
204            public Boolean computeValue(SlicedMap map, K key, Boolean value, boolean valueNotFound) {
205                Boolean result = super.computeValue(map, key, value, valueNotFound);
206                return result != null ? result : false;
207            }
208        }
209    
210    }