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