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) return v;
145                                }
146                                return defaultValue;
147                            }
148                            return super.computeValue(map, key, value, valueNotFound);
149                        }
150                    };
151                }
152                return new SliceWithOpposite<K, V>(rewritePolicy, opposite, keyNormalizer);
153            }
154        }
155    
156        public static class BasicRemovableSlice<K, V> extends BasicWritableSlice<K, V> implements RemovableSlice<K, V> {
157            protected BasicRemovableSlice(RewritePolicy rewritePolicy) {
158                super(rewritePolicy);
159            }
160    
161            protected BasicRemovableSlice(RewritePolicy rewritePolicy, boolean isCollective) {
162                super(rewritePolicy, isCollective);
163            }
164        }
165    
166        public static class SliceWithOpposite<K, V> extends BasicRemovableSlice<K, V> {
167            private final WritableSlice<? super V, ? super K> opposite;
168    
169    
170            private final KeyNormalizer<K> keyNormalizer;
171    
172            public SliceWithOpposite(String debugName, RewritePolicy rewritePolicy) {
173                this(debugName, rewritePolicy, KeyNormalizer.DO_NOTHING);
174            }
175    
176            public SliceWithOpposite(String debugName, RewritePolicy rewritePolicy, KeyNormalizer<K> keyNormalizer) {
177                this(rewritePolicy, null, keyNormalizer);
178            }
179    
180            public SliceWithOpposite(RewritePolicy rewritePolicy, WritableSlice<? super V, ? super K> opposite, KeyNormalizer<K> keyNormalizer) {
181                super(rewritePolicy);
182                this.opposite = opposite;
183                this.keyNormalizer = keyNormalizer;
184            }
185    
186            @Override
187            public void afterPut(MutableSlicedMap map, K key, V value) {
188                if (opposite != null) {
189                    map.put(opposite, value, key);
190                }
191            }
192            @Override
193            public SlicedMapKey<K, V> makeKey(K key) {
194                if (keyNormalizer == null) {
195                    return super.makeKey(key);
196                }
197                return super.makeKey(keyNormalizer.normalize(key));
198            }
199    
200        }
201    
202        public static class SetSlice<K> extends BasicRemovableSlice<K, Boolean> {
203    
204            protected SetSlice(RewritePolicy rewritePolicy) {
205                this(rewritePolicy, false);
206            }
207    
208            protected SetSlice(RewritePolicy rewritePolicy, boolean collective) {
209                super(rewritePolicy, collective);
210            }
211    
212            @Override
213            public Boolean computeValue(SlicedMap map, K key, Boolean value, boolean valueNotFound) {
214                Boolean result = super.computeValue(map, key, value, valueNotFound);
215                return result != null ? result : false;
216            }
217        }
218    
219    }