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 }