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 package org.jetbrains.kotlin.util.userDataHolder; 017 018 019 import com.intellij.openapi.util.Key; 020 import com.intellij.openapi.util.UserDataHolderEx; 021 import com.intellij.util.concurrency.AtomicFieldUpdater; 022 import org.jetbrains.kotlin.util.userDataHolder.keyFMap.KeyFMap; 023 import org.jetbrains.annotations.NotNull; 024 import org.jetbrains.annotations.Nullable; 025 import org.jetbrains.annotations.TestOnly; 026 027 public class UserDataHolderBase implements UserDataHolderEx, Cloneable { 028 public static final Key<KeyFMap> COPYABLE_USER_MAP_KEY = Key.create("COPYABLE_USER_MAP_KEY"); 029 030 /** 031 * Concurrent writes to this field are via CASes only, using the {@link #updater} 032 */ 033 @NotNull private volatile KeyFMap myUserMap = KeyFMap.EMPTY_MAP; 034 035 @Override 036 protected Object clone() { 037 try { 038 UserDataHolderBase clone = (UserDataHolderBase)super.clone(); 039 clone.setUserMap(KeyFMap.EMPTY_MAP); 040 copyCopyableDataTo(clone); 041 return clone; 042 } 043 catch (CloneNotSupportedException e) { 044 throw new RuntimeException(e); 045 } 046 } 047 048 @TestOnly 049 public String getUserDataString() { 050 final KeyFMap userMap = getUserMap(); 051 final KeyFMap copyableMap = getUserData(COPYABLE_USER_MAP_KEY); 052 return userMap.toString() + (copyableMap == null ? "" : copyableMap.toString()); 053 } 054 055 public void copyUserDataTo(UserDataHolderBase other) { 056 other.setUserMap(getUserMap()); 057 } 058 059 @Override 060 public <T> T getUserData(@NotNull Key<T> key) { 061 //noinspection unchecked 062 return getUserMap().get(key); 063 } 064 065 @NotNull 066 protected KeyFMap getUserMap() { 067 return myUserMap; 068 } 069 070 @Override 071 public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) { 072 while (true) { 073 KeyFMap map = getUserMap(); 074 KeyFMap newMap = value == null ? map.minus(key) : map.plus(key, value); 075 if (newMap == map || changeUserMap(map, newMap)) { 076 break; 077 } 078 } 079 } 080 081 protected boolean changeUserMap(KeyFMap oldMap, KeyFMap newMap) { 082 return updater.compareAndSet(this, oldMap, newMap); 083 } 084 085 public <T> T getCopyableUserData(@NotNull Key<T> key) { 086 KeyFMap map = getUserData(COPYABLE_USER_MAP_KEY); 087 //noinspection unchecked,ConstantConditions 088 return map == null ? null : map.get(key); 089 } 090 091 public <T> void putCopyableUserData(@NotNull Key<T> key, T value) { 092 while (true) { 093 KeyFMap map = getUserMap(); 094 KeyFMap copyableMap = map.get(COPYABLE_USER_MAP_KEY); 095 if (copyableMap == null) { 096 copyableMap = KeyFMap.EMPTY_MAP; 097 } 098 KeyFMap newCopyableMap = value == null ? copyableMap.minus(key) : copyableMap.plus(key, value); 099 KeyFMap newMap = newCopyableMap.isEmpty() ? map.minus(COPYABLE_USER_MAP_KEY) : map.plus(COPYABLE_USER_MAP_KEY, newCopyableMap); 100 if (newMap == map || changeUserMap(map, newMap)) { 101 return; 102 } 103 } 104 } 105 106 @Override 107 public <T> boolean replace(@NotNull Key<T> key, @Nullable T oldValue, @Nullable T newValue) { 108 while (true) { 109 KeyFMap map = getUserMap(); 110 if (map.get(key) != oldValue) { 111 return false; 112 } 113 KeyFMap newMap = newValue == null ? map.minus(key) : map.plus(key, newValue); 114 if (newMap == map || changeUserMap(map, newMap)) { 115 return true; 116 } 117 } 118 } 119 120 @Override 121 @NotNull 122 public <T> T putUserDataIfAbsent(@NotNull final Key<T> key, @NotNull final T value) { 123 while (true) { 124 KeyFMap map = getUserMap(); 125 T oldValue = map.get(key); 126 if (oldValue != null) { 127 return oldValue; 128 } 129 KeyFMap newMap = map.plus(key, value); 130 if (newMap == map || changeUserMap(map, newMap)) { 131 return value; 132 } 133 } 134 } 135 136 public void copyCopyableDataTo(@NotNull UserDataHolderBase clone) { 137 clone.putUserData(COPYABLE_USER_MAP_KEY, getUserData(COPYABLE_USER_MAP_KEY)); 138 } 139 140 protected void clearUserData() { 141 setUserMap(KeyFMap.EMPTY_MAP); 142 } 143 144 protected void setUserMap(@NotNull KeyFMap map) { 145 myUserMap = map; 146 } 147 148 public boolean isUserDataEmpty() { 149 return getUserMap().isEmpty(); 150 } 151 152 private static final AtomicFieldUpdater<UserDataHolderBase, KeyFMap> updater = AtomicFieldUpdater.forFieldOfType(UserDataHolderBase.class, KeyFMap.class); 153 }