001    /*
002     * Copyright 2010-2015 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.kotlin.util.slicedMap;
018    
019    import com.intellij.openapi.diagnostic.Logger;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.kotlin.psi.KtElement;
022    import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
023    
024    import java.util.Arrays;
025    import java.util.List;
026    
027    public class Slices {
028        private static final Logger LOG = Logger.getInstance(Slices.class);
029    
030        public static final RewritePolicy ONLY_REWRITE_TO_EQUAL = new RewritePolicy() {
031            @Override
032            public <K> boolean rewriteProcessingNeeded(K key) {
033                return true;
034            }
035    
036            @Override
037            public <K, V> boolean processRewrite(WritableSlice<K, V> slice, K key, V oldValue, V newValue) {
038                if (!((oldValue == null && newValue == null) || (oldValue != null && oldValue.equals(newValue)))) {
039                    // NOTE: Use BindingTraceContext.TRACK_REWRITES to debug this exception
040                    LOG.error("Rewrite at slice " + slice +
041                            " key: " + key +
042                            " old value: " + oldValue + '@' + System.identityHashCode(oldValue) +
043                            " new value: " + newValue + '@' + System.identityHashCode(newValue) +
044                              (key instanceof KtElement ? "\n" + PsiUtilsKt.getElementTextWithContext((KtElement) key) : ""));
045                }
046                return true;
047            }
048        };
049    
050        private Slices() {
051        }
052    
053        public static <K, V> SliceBuilder<K, V> sliceBuilder() {
054            return new SliceBuilder<K, V>(ONLY_REWRITE_TO_EQUAL);
055        }
056    
057        public static <K, V> WritableSlice<K, V> createSimpleSlice() {
058            return new BasicWritableSlice<K, V>(ONLY_REWRITE_TO_EQUAL);
059        }
060    
061        public static <K, V> WritableSlice<K, V> createCollectiveSlice() {
062            return new BasicWritableSlice<K, V>(ONLY_REWRITE_TO_EQUAL, true);
063        }
064    
065        public static <K> WritableSlice<K, Boolean> createSimpleSetSlice() {
066            return new SetSlice<K>(RewritePolicy.DO_NOTHING);
067        }
068    
069        public static <K> WritableSlice<K, Boolean> createCollectiveSetSlice() {
070            return new SetSlice<K>(RewritePolicy.DO_NOTHING, true);
071        }
072    
073        public static class SliceBuilder<K, V> {
074            private List<ReadOnlySlice<K, V>> furtherLookupSlices;
075            private final RewritePolicy rewritePolicy;
076            private String debugName;
077    
078            private SliceBuilder(RewritePolicy rewritePolicy) {
079                this.rewritePolicy = rewritePolicy;
080            }
081    
082            public SliceBuilder<K, V> setFurtherLookupSlices(ReadOnlySlice<K, V>... furtherLookupSlices) {
083                this.furtherLookupSlices = Arrays.asList(furtherLookupSlices);
084                return this;
085            }
086    
087            public SliceBuilder<K, V> setDebugName(@NotNull String debugName) {
088                this.debugName = debugName;
089                return this;
090            }
091    
092            public WritableSlice<K, V> build() {
093                BasicWritableSlice<K, V> result = doBuild();
094                if (debugName != null) {
095                    result.setDebugName(debugName);
096                }
097                return result;
098            }
099    
100            private BasicWritableSlice<K, V> doBuild() {
101                if (furtherLookupSlices != null) {
102                    return new BasicWritableSlice<K, V>(rewritePolicy) {
103                        @Override
104                        public V computeValue(SlicedMap map, K key, V value, boolean valueNotFound) {
105                            if (valueNotFound) {
106                                for (ReadOnlySlice<K, V> slice : furtherLookupSlices) {
107                                    V v = map.get(slice, key);
108                                    if (v != null) {
109                                        return v;
110                                    }
111                                }
112                                return null;
113                            }
114                            return super.computeValue(map, key, value, false);
115                        }
116                    };
117                }
118                return new BasicWritableSlice<K, V>(rewritePolicy);
119            }
120        }
121    
122        public static class SetSlice<K> extends BasicWritableSlice<K, Boolean> {
123    
124            protected SetSlice(RewritePolicy rewritePolicy) {
125                this(rewritePolicy, false);
126            }
127    
128            protected SetSlice(RewritePolicy rewritePolicy, boolean collective) {
129                super(rewritePolicy, collective);
130            }
131    
132            @Override
133            public Boolean computeValue(SlicedMap map, K key, Boolean value, boolean valueNotFound) {
134                Boolean result = super.computeValue(map, key, value, valueNotFound);
135                return result != null ? result : false;
136            }
137        }
138    
139    }