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