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, V> WritableSlice<K, V> createCollectiveSlice() {
068            return new BasicWritableSlice<K, V>(ONLY_REWRITE_TO_EQUAL, true);
069        }
070    
071        public static <K> WritableSlice<K, Boolean> createSimpleSetSlice() {
072            return createRemovableSetSlice();
073        }
074    
075        public static <K> WritableSlice<K, Boolean> createCollectiveSetSlice() {
076            return new SetSlice<K>(RewritePolicy.DO_NOTHING, true);
077        }
078    
079        public static <K> RemovableSlice<K, Boolean> createRemovableSetSlice() {
080            return new SetSlice<K>(RewritePolicy.DO_NOTHING, false);
081        }
082    
083        public static class SliceBuilder<K, V> {
084            private V defaultValue = null;
085            private List<ReadOnlySlice<K, V>> furtherLookupSlices = null;
086            private WritableSlice<? super V, ? super K> opposite = null;
087            private KeyNormalizer<K> keyNormalizer = null;
088    
089            private final RewritePolicy rewritePolicy;
090    
091            private String debugName;
092    
093            private SliceBuilder(RewritePolicy rewritePolicy) {
094                this.rewritePolicy = rewritePolicy;
095            }
096    
097            public SliceBuilder<K, V> setDefaultValue(V defaultValue) {
098                this.defaultValue = defaultValue;
099                return this;
100            }
101    
102            public SliceBuilder<K, V> setFurtherLookupSlices(ReadOnlySlice<K, V>... furtherLookupSlices) {
103                this.furtherLookupSlices = Arrays.asList(furtherLookupSlices);
104                return this;
105            }
106    
107            public SliceBuilder<K, V> setOpposite(WritableSlice<? super V, ? super K> opposite) {
108                this.opposite = opposite;
109                return this;
110            }
111    
112            public SliceBuilder<K, V> setDebugName(@NotNull String debugName) {
113                this.debugName = debugName;
114                return this;
115            }
116    
117            public SliceBuilder<K, V> setKeyNormalizer(KeyNormalizer<K> keyNormalizer) {
118                this.keyNormalizer = keyNormalizer;
119                return this;
120            }
121    
122            public RemovableSlice<K, V>  build() {
123                SliceWithOpposite<K, V> result = doBuild();
124                if (debugName != null) {
125                    result.setDebugName(debugName);
126                }
127                return result;
128            }
129    
130            private SliceWithOpposite<K, V> doBuild() {
131                if (defaultValue != null) {
132                    return new SliceWithOpposite<K, V>(rewritePolicy, opposite, keyNormalizer) {
133                        @Override
134                        public V computeValue(SlicedMap map, K key, V value, boolean valueNotFound) {
135                            if (valueNotFound) return defaultValue;
136                            return super.computeValue(map, key, value, false);
137                        }
138                    };
139                }
140                if (furtherLookupSlices != null) {
141                    return new SliceWithOpposite<K, V>(rewritePolicy, opposite, keyNormalizer) {
142                        @Override
143                        public V computeValue(SlicedMap map, K key, V value, boolean valueNotFound) {
144                            if (valueNotFound) {
145                                for (ReadOnlySlice<K, V> slice : furtherLookupSlices) {
146                                    V v = map.get(slice, key);
147                                    if (v != null) {
148                                        return v;
149                                    }
150                                }
151                                return defaultValue;
152                            }
153                            return super.computeValue(map, key, value, false);
154                        }
155                    };
156                }
157                return new SliceWithOpposite<K, V>(rewritePolicy, opposite, keyNormalizer);
158            }
159        }
160    
161        public static class BasicRemovableSlice<K, V> extends BasicWritableSlice<K, V> implements RemovableSlice<K, V> {
162            protected BasicRemovableSlice(RewritePolicy rewritePolicy) {
163                super(rewritePolicy);
164            }
165    
166            protected BasicRemovableSlice(RewritePolicy rewritePolicy, boolean isCollective) {
167                super(rewritePolicy, isCollective);
168            }
169        }
170    
171        public static class SliceWithOpposite<K, V> extends BasicRemovableSlice<K, V> {
172            private final WritableSlice<? super V, ? super K> opposite;
173            private final KeyNormalizer<K> keyNormalizer;
174    
175            public SliceWithOpposite(RewritePolicy rewritePolicy, WritableSlice<? super V, ? super K> opposite, KeyNormalizer<K> keyNormalizer) {
176                super(rewritePolicy);
177                this.opposite = opposite;
178                this.keyNormalizer = keyNormalizer;
179            }
180    
181            @Override
182            public void afterPut(MutableSlicedMap map, K key, V value) {
183                if (opposite != null) {
184                    map.put(opposite, value, key);
185                }
186            }
187            @Override
188            public SlicedMapKey<K, V> makeKey(K key) {
189                if (keyNormalizer == null) {
190                    return super.makeKey(key);
191                }
192                return super.makeKey(keyNormalizer.normalize(key));
193            }
194    
195        }
196    
197        public static class SetSlice<K> extends BasicRemovableSlice<K, Boolean> {
198    
199            protected SetSlice(RewritePolicy rewritePolicy) {
200                this(rewritePolicy, false);
201            }
202    
203            protected SetSlice(RewritePolicy rewritePolicy, boolean collective) {
204                super(rewritePolicy, collective);
205            }
206    
207            @Override
208            public Boolean computeValue(SlicedMap map, K key, Boolean value, boolean valueNotFound) {
209                Boolean result = super.computeValue(map, key, value, valueNotFound);
210                return result != null ? result : false;
211            }
212        }
213    
214    }