sticky documentation popup [take 1]
[fedora-idea.git] / platform / platform-impl / src / com / intellij / util / CachedValueBase.java
blob80133bde1163e4bda1542cf2c7fc6407c39f2c54
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.util;
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.editor.Document;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.util.Disposer;
23 import com.intellij.openapi.util.ModificationTracker;
24 import com.intellij.openapi.util.Ref;
25 import com.intellij.psi.util.CachedValueProvider;
26 import com.intellij.reference.SoftReference;
27 import com.intellij.util.concurrency.JBLock;
28 import com.intellij.util.concurrency.JBReentrantReadWriteLock;
29 import com.intellij.util.concurrency.LockFactory;
30 import gnu.trove.TLongArrayList;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
34 import java.lang.ref.Reference;
35 import java.util.ArrayList;
36 import java.util.List;
38 /**
39 * @author Dmitry Avdeev
41 public abstract class CachedValueBase<T> {
42 protected static final Object NULL = new Object();
43 private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.CachedValueImpl");
45 private final MyTimedReference<T> myData = new MyTimedReference<T>();
46 protected final JBLock r;
47 protected final JBLock w;
49 public CachedValueBase() {
50 JBReentrantReadWriteLock rw = LockFactory.createReadWriteLock();
51 r = rw.readLock();
52 w = rw.writeLock();
56 protected Data<T> computeData(T value, Object[] dependencies) {
57 if (dependencies == null) {
58 return new Data<T>(value, null, null);
61 TLongArrayList timeStamps = new TLongArrayList();
62 List<Object> deps = new ArrayList<Object>();
63 collectDependencies(timeStamps, deps, dependencies);
65 return new Data<T>(value, ArrayUtil.toObjectArray(deps), timeStamps.toNativeArray());
68 protected void setValue(final T value, final CachedValueProvider.Result<T> result) {
69 myData.setData(computeData(value == null ? (T) NULL : value, getDependencies(result)));
70 if (result != null) {
71 myData.setIsLocked(result.isLockValue());
73 else {
74 myData.setIsLocked(false);
78 @Nullable
79 protected Object[] getDependencies(CachedValueProvider.Result<T> result) {
80 return result == null ? null : result.getDependencyItems();
83 @Nullable
84 protected Object[] getDependenciesPlusValue(CachedValueProvider.Result<T> result) {
85 if (result == null) {
86 return null;
88 else {
89 Object[] items = result.getDependencyItems();
90 return result.getValue() == null ? items : items == null ? new Object[] {result.getValue()}: ArrayUtil.append(items, result.getValue());
94 public void clear() {
95 myData.set(null);
98 public void setDataLocked(boolean value) {
99 myData.setIsLocked(value);
102 public boolean hasUpToDateValue() {
103 r.lock();
105 try {
106 return getUpToDateOrNull() != null;
108 finally {
109 r.unlock();
113 @Nullable
114 protected T getUpToDateOrNull() {
115 final Data<T> data = myData.getData();
117 if (data != null) {
118 T value = data.myValue;
119 if (isUpToDate(data)) {
120 return value;
122 if (value instanceof Disposable) {
123 Disposer.dispose((Disposable)value);
126 return null;
129 protected boolean isUpToDate(@NotNull Data data) {
130 if (data.myTimeStamps == null) return true;
132 for (int i = 0; i < data.myDependencies.length; i++) {
133 Object dependency = data.myDependencies[i];
134 if (dependency == null) continue;
135 if (isDependencyOutOfDate(dependency, data.myTimeStamps[i])) return false;
138 return true;
141 protected boolean isDependencyOutOfDate(Object dependency, long oldTimeStamp) {
142 final long timeStamp = getTimeStamp(dependency);
143 return timeStamp < 0 || timeStamp != oldTimeStamp;
146 protected void collectDependencies(TLongArrayList timeStamps, List<Object> resultingDeps, Object[] dependencies) {
147 for (Object dependency : dependencies) {
148 if (dependency == null || dependency == NULL) continue;
149 if (dependency instanceof Object[]) {
150 collectDependencies(timeStamps, resultingDeps, (Object[])dependency);
152 else {
153 resultingDeps.add(dependency);
154 timeStamps.add(getTimeStamp(dependency));
159 protected long getTimeStamp(Object dependency) {
161 if (dependency instanceof ModificationTracker) {
162 return ((ModificationTracker)dependency).getModificationCount();
164 else if (dependency instanceof Reference){
165 final Object original = ((Reference)dependency).get();
166 if(original == null) return -1;
167 return getTimeStamp(original);
169 else if (dependency instanceof Ref) {
170 final Object original = ((Ref)dependency).get();
171 if(original == null) return -1;
172 return getTimeStamp(original);
174 else if (dependency instanceof Document) {
175 return ((Document)dependency).getModificationStamp();
177 else {
178 LOG.error("Wrong dependency type: " + dependency.getClass());
179 return -1;
183 public T setValue(final CachedValueProvider.Result<T> result) {
184 w.lock();
186 try {
187 T value = result.getValue();
188 setValue(value, result);
189 return value;
191 finally {
192 w.unlock();
196 public abstract boolean isFromMyProject(Project project);
198 protected static class Data<T> implements Disposable {
199 private final T myValue;
200 private final Object[] myDependencies;
201 private final long[] myTimeStamps;
203 public Data(final T value, final Object[] dependencies, final long[] timeStamps) {
204 myValue = value;
205 myDependencies = dependencies;
206 myTimeStamps = timeStamps;
209 public void dispose() {
210 if (myValue instanceof Disposable) {
211 Disposer.dispose((Disposable)myValue);
216 private static class MyTimedReference<T> extends TimedReference<SoftReference<Data<T>>> {
217 private boolean myIsLocked;
220 public MyTimedReference() {
221 super(null);
224 public void setIsLocked(final boolean isLocked) {
225 myIsLocked = isLocked;
228 protected boolean isLocked() {
229 return super.isLocked() || myIsLocked;
232 public void setData(final Data<T> data) {
233 set(new SoftReference<Data<T>>(data));
236 @Nullable
237 public Data<T> getData() {
238 final SoftReference<Data<T>> ref = get();
239 return ref != null ? ref.get() : null;