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    }