@NotNull public final Map<Key, Object> exportUserData() { final Map<Key, Object> result = new HashMap<Key, Object>(); KeyFMap map = getUserMap(); Key[] keys = map.getKeys(); for (Key<?> k : keys) { final Object data = map.get(k); if (data != null) { result.put(k, data); } } return result; }
private static int computeHashCode(KeyFMap object) { if (object instanceof OneElementFMap) { return ((OneElementFMap)object).getKey().hashCode() * 31 + System.identityHashCode(((OneElementFMap)object).getValue()); } if (object instanceof PairElementsFMap) { PairElementsFMap map = (PairElementsFMap)object; return (map.getKey1().hashCode() * 31 + map.getKey2().hashCode()) * 31 + System.identityHashCode(map.getValue1()) + System.identityHashCode(map.getValue2()); } if (object instanceof ArrayBackedFMap) { int hc = Arrays.hashCode(((ArrayBackedFMap)object).getKeyIds()); for (Object o : ((ArrayBackedFMap)object).getValues()) { hc = hc * 31 + System.identityHashCode(o); } return hc; } return 0; }
static void checkLeaks(KeyFMap newMap) { for (Key key : newMap.getKeys()) { if (key != null && newMap.get(key) instanceof PsiCachedValue) { throw new AssertionError("Don't store CachedValue in VFS user data, since it leads to memory leaks"); } } }
KeyFMap getUserMap(VirtualFileSystemEntry file, int id) { Object o = myObjectArray.get(getOffset(id)); if (!(o instanceof KeyFMap)) { throw reportDeadFileAccess(file); } return (KeyFMap)o; }
static KeyFMap internUserData(@NotNull KeyFMap map) { if (shouldIntern(map)) { MapReference key = new MapReference(map); synchronized (ourCache) { KeyFMap cached = SoftReference.dereference(ourCache.get(key)); if (cached != null) return cached; ourCache.put(key, key); } return map; } return map; }
@Override public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) { while (true) { KeyFMap map = getUserMap(); KeyFMap newMap = value == null ? map.minus(key) : map.plus(key, value); if (newMap == map || changeUserMap(map, newMap)) { break; } } }
public <T> void putCopyableUserData(@NotNull Key<T> key, T value) { while (true) { KeyFMap map = getUserMap(); KeyFMap copyableMap = map.get(COPYABLE_USER_MAP_KEY); if (copyableMap == null) { copyableMap = KeyFMap.EMPTY_MAP; } KeyFMap newCopyableMap = value == null ? copyableMap.minus(key) : copyableMap.plus(key, value); KeyFMap newMap = newCopyableMap.isEmpty() ? map.minus(COPYABLE_USER_MAP_KEY) : map.plus(COPYABLE_USER_MAP_KEY, newCopyableMap); if (newMap == map || changeUserMap(map, newMap)) { return; } } }
@Override public <T> boolean replace(@NotNull Key<T> key, @Nullable T oldValue, @Nullable T newValue) { while (true) { KeyFMap map = getUserMap(); if (map.get(key) != oldValue) { return false; } KeyFMap newMap = newValue == null ? map.minus(key) : map.plus(key, newValue); if (newMap == map || changeUserMap(map, newMap)) { return true; } } }
@Override @NotNull public <T> T putUserDataIfAbsent(@NotNull final Key<T> key, @NotNull final T value) { while (true) { KeyFMap map = getUserMap(); T oldValue = map.get(key); if (oldValue != null) { return oldValue; } KeyFMap newMap = map.plus(key, value); if (newMap == map || changeUserMap(map, newMap)) { return value; } } }
@Override public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) { while (true) { KeyFMap map = myUserMap; KeyFMap newMap = value == null ? map.minus(key) : map.plus(key, value); if (newMap == map || updater.compareAndSet(this, map, newMap)) { break; } } }
public <T> void putCopyableUserData(Key<T> key, T value) { while (true) { KeyFMap map = myUserMap; KeyFMap copyableMap = map.get(COPYABLE_USER_MAP_KEY); if (copyableMap == null) { copyableMap = KeyFMap.EMPTY_MAP; } KeyFMap newCopyableMap = value == null ? copyableMap.minus(key) : copyableMap.plus(key, value); KeyFMap newMap = newCopyableMap.isEmpty() ? map.minus(COPYABLE_USER_MAP_KEY) : map.plus(COPYABLE_USER_MAP_KEY, newCopyableMap); if (newMap == map || updater.compareAndSet(this, map, newMap)) { return; } } }
@Override public <T> boolean replace(@NotNull Key<T> key, @Nullable T oldValue, @Nullable T newValue) { while (true) { KeyFMap map = myUserMap; if (map.get(key) != oldValue) { return false; } KeyFMap newMap = newValue == null ? map.minus(key) : map.plus(key, newValue); if (newMap == map || updater.compareAndSet(this, map, newMap)) { return true; } } }
@Override @NotNull public <T> T putUserDataIfAbsent(@NotNull final Key<T> key, @NotNull final T value) { while (true) { KeyFMap map = myUserMap; T oldValue = map.get(key); if (oldValue != null) { return oldValue; } KeyFMap newMap = map.plus(key, value); if (newMap == map || updater.compareAndSet(this, map, newMap)) { return value; } } }
static KeyFMap internUserData(@Nonnull KeyFMap map) { if (shouldIntern(map)) { MapReference key = new MapReference(map); synchronized (ourCache) { KeyFMap cached = SoftReference.dereference(ourCache.get(key)); if (cached != null) return cached; ourCache.put(key, key); } return map; } return map; }
@Override public <T> void putUserData(@Nonnull Key<T> key, @Nullable T value) { while (true) { KeyFMap map = getUserMap(); KeyFMap newMap = value == null ? map.minus(key) : map.plus(key, value); if (newMap == map || changeUserMap(map, newMap)) { break; } } }
public <T> void putCopyableUserData(Key<T> key, T value) { while (true) { KeyFMap map = getUserMap(); KeyFMap copyableMap = map.get(COPYABLE_USER_MAP_KEY); if (copyableMap == null) { copyableMap = KeyFMap.EMPTY_MAP; } KeyFMap newCopyableMap = value == null ? copyableMap.minus(key) : copyableMap.plus(key, value); KeyFMap newMap = newCopyableMap.isEmpty() ? map.minus(COPYABLE_USER_MAP_KEY) : map.plus(COPYABLE_USER_MAP_KEY, newCopyableMap); if (newMap == map || changeUserMap(map, newMap)) { return; } } }
@Override public <T> boolean replace(@Nonnull Key<T> key, @Nullable T oldValue, @Nullable T newValue) { while (true) { KeyFMap map = getUserMap(); if (map.get(key) != oldValue) { return false; } KeyFMap newMap = newValue == null ? map.minus(key) : map.plus(key, newValue); if (newMap == map || changeUserMap(map, newMap)) { return true; } } }
@Override @Nonnull public <T> T putUserDataIfAbsent(@Nonnull final Key<T> key, @Nonnull final T value) { while (true) { KeyFMap map = getUserMap(); T oldValue = map.get(key); if (oldValue != null) { return oldValue; } KeyFMap newMap = map.plus(key, value); if (newMap == map || changeUserMap(map, newMap)) { return value; } } }
@Override protected void setUserMap(@NotNull KeyFMap map) { myData.myUserMap = map; }
@NotNull @Override protected KeyFMap getUserMap() { return myData.myUserMap; }
@Override protected boolean changeUserMap(KeyFMap oldMap, KeyFMap newMap) { checkLeaks(newMap); return myData.changeUserMap(oldMap, UserDataInterner.internUserData(newMap)); }
void setUserMap(int fileId, @NotNull KeyFMap map) { myObjectArray.set(getOffset(fileId), map); }
boolean changeUserMap(int fileId, KeyFMap oldMap, KeyFMap newMap) { return myObjectArray.compareAndSet(getOffset(fileId), oldMap, newMap); }
boolean changeUserMap(KeyFMap oldMap, KeyFMap newMap) { return updater.compareAndSet(this, oldMap, newMap); }
private static boolean shouldIntern(@NotNull KeyFMap map) { return map instanceof OneElementFMap || map instanceof PairElementsFMap || map instanceof ArrayBackedFMap && ((ArrayBackedFMap)map).getKeyIds().length <= 5; }
MapReference(KeyFMap referent) { super(referent); myHash = computeHashCode(referent); }
@Override protected void setUserMap(@NotNull KeyFMap map) { mySegment.setUserMap(myId, map); }
@NotNull @Override protected KeyFMap getUserMap() { return mySegment.getUserMap(this, myId); }
@Override protected boolean changeUserMap(KeyFMap oldMap, KeyFMap newMap) { VirtualDirectoryImpl.checkLeaks(newMap); return mySegment.changeUserMap(myId, oldMap, UserDataInterner.internUserData(newMap)); }
@TestOnly public String getUserDataString() { final KeyFMap userMap = getUserMap(); final KeyFMap copyableMap = getUserData(COPYABLE_USER_MAP_KEY); return userMap + (copyableMap == null ? "" : copyableMap.toString()); }
@NotNull protected KeyFMap getUserMap() { return myUserMap; }
protected boolean changeUserMap(KeyFMap oldMap, KeyFMap newMap) { return updater.compareAndSet(this, oldMap, newMap); }
public <T> T getCopyableUserData(@NotNull Key<T> key) { KeyFMap map = getUserData(COPYABLE_USER_MAP_KEY); //noinspection unchecked,ConstantConditions return map == null ? null : map.get(key); }
protected void clearUserData() { setUserMap(KeyFMap.EMPTY_MAP); }
protected void setUserMap(@NotNull KeyFMap map) { myUserMap = map; }
@TestOnly public String getUserDataString() { final KeyFMap userMap = myUserMap; final KeyFMap copyableMap = getUserData(COPYABLE_USER_MAP_KEY); return userMap.toString() + (copyableMap == null ? "" : copyableMap.toString()); }
public <T> T getCopyableUserData(Key<T> key) { KeyFMap map = getUserData(COPYABLE_USER_MAP_KEY); //noinspection unchecked,ConstantConditions return map == null ? null : map.get(key); }