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 }