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
017package org.jetbrains.jet.util.slicedmap;
018
019import org.jetbrains.annotations.NotNull;
020
021import java.util.Arrays;
022import java.util.List;
023
024public 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}