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.google.common.collect.ImmutableMap;
020    import com.google.common.collect.Maps;
021    import kotlin.jvm.functions.Function3;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.TestOnly;
024    import org.jetbrains.kotlin.utils.Printer;
025    
026    import java.util.Collection;
027    import java.util.Map;
028    
029    public class TrackingSlicedMap extends SlicedMapImpl {
030        private final Map<ReadOnlySlice<?, ?>, SliceWithStackTrace<?, ?>> sliceTranslationMap = Maps.newHashMap();
031        private final boolean trackWithStackTraces;
032    
033        public TrackingSlicedMap(boolean trackWithStackTraces) {
034            this.trackWithStackTraces = trackWithStackTraces;
035        }
036    
037        private <K, V> SliceWithStackTrace<K, V> wrapSlice(ReadOnlySlice<K, V> slice) {
038            SliceWithStackTrace<?, ?> translated = sliceTranslationMap.get(slice);
039            if (translated == null) {
040                translated = new SliceWithStackTrace<K, V>(slice);
041                sliceTranslationMap.put(slice, translated);
042            }
043            //noinspection unchecked
044            return (SliceWithStackTrace) translated;
045        }
046    
047        @Override
048        public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
049            return super.get(wrapSlice(slice), key).value;
050        }
051    
052        @Override
053        public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
054            return super.getKeys(wrapSlice(slice));
055        }
056    
057        @Override
058        public void forEach(@NotNull final Function3<WritableSlice, Object, Object, Void> f) {
059            super.forEach(new Function3<WritableSlice, Object, Object, Void>() {
060                @Override
061                public Void invoke(WritableSlice slice, Object key, Object value) {
062                    f.invoke(slice, key, ((TrackableValue<?>) value).value);
063                    return null;
064                }
065            });
066        }
067    
068        @Override
069        public <K, V> void put(WritableSlice<K, V> slice, K key, V value) {
070            super.put(wrapSlice(slice), key, new TrackableValue<V>(value, trackWithStackTraces));
071        }
072    
073        @Override
074        public <K, V> V remove(RemovableSlice<K, V> slice, K key) {
075            return super.remove(wrapSlice(slice), key).value;
076        }
077    
078        @Override
079        public void clear() {
080            super.clear();
081        }
082    
083        @Override
084        @NotNull
085        @TestOnly
086        public <K, V> ImmutableMap<K, V> getSliceContents(@NotNull ReadOnlySlice<K, V> slice) {
087            return super.getSliceContents(slice);
088        }
089    
090        private static class TrackableValue<V> {
091            private final static StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
092    
093            private final V value;
094            private final StackTraceElement[] stackTrace;
095            private final String threadName;
096    
097            private TrackableValue(V value, boolean storeStack) {
098                this.value = value;
099                this.stackTrace = storeStack ? Thread.currentThread().getStackTrace() : EMPTY_STACK_TRACE;
100                this.threadName = Thread.currentThread().getName();
101            }
102    
103            private Appendable printStackTrace(Appendable appendable) {
104                Printer s = new Printer(appendable);
105                s.println(value);
106                s.println("Thread: " + threadName);
107                s.println("Written at ");
108                StackTraceElement[] trace = stackTrace;
109                for (StackTraceElement aTrace : trace) {
110                    s.println("\tat " + aTrace);
111                }
112                s.println("---------");
113                return appendable;
114            }
115    
116            @Override
117            public String toString() {
118                return printStackTrace(new StringBuilder()).toString();
119            }
120    
121            @Override
122            public boolean equals(Object o) {
123                if (this == o) return true;
124                if (o == null || getClass() != o.getClass()) return false;
125    
126                TrackableValue other = (TrackableValue) o;
127    
128                if (value != null ? !value.equals(other.value) : other.value != null) return false;
129    
130                return true;
131            }
132    
133            @Override
134            public int hashCode() {
135                return value != null ? value.hashCode() : 0;
136            }
137        }
138    
139        private class SliceWithStackTrace<K, V> extends AbstractWritableSlice<K, TrackableValue<V>> implements RemovableSlice<K, TrackableValue<V>> {
140    
141            private final ReadOnlySlice<K, V> delegate;
142    
143            private SliceWithStackTrace(@NotNull ReadOnlySlice<K, V> delegate) {
144                super(delegate.toString());
145                this.delegate = delegate;
146            }
147    
148            // Methods of ReadOnlySlice
149    
150            @Override
151            public TrackableValue<V> computeValue(SlicedMap map, K key, TrackableValue<V> value, boolean valueNotFound) {
152                return new TrackableValue<V>(delegate.computeValue(map, key, value == null ? null : value.value, valueNotFound), trackWithStackTraces);
153            }
154    
155            @Override
156            public ReadOnlySlice<K, TrackableValue<V>> makeRawValueVersion() {
157                return wrapSlice(delegate.makeRawValueVersion());
158            }
159    
160            // Methods of WritableSlice
161    
162            private WritableSlice<K, V> getWritableDelegate() {
163                return (WritableSlice<K, V>) delegate;
164            }
165    
166            @Override
167            public boolean isCollective() {
168                return getWritableDelegate().isCollective();
169            }
170    
171            @Override
172            public RewritePolicy getRewritePolicy() {
173                return getWritableDelegate().getRewritePolicy();
174            }
175    
176            @Override
177            public void afterPut(MutableSlicedMap map, K key, TrackableValue<V> value) {
178                getWritableDelegate().afterPut(map, key, value.value);
179            }
180    
181            @Override
182            public boolean check(K key, TrackableValue<V> value) {
183                return getWritableDelegate().check(key, value.value);
184            }
185        }
186    }