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
;
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();
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
)));
71 myData
.setIsLocked(result
.isLockValue());
74 myData
.setIsLocked(false);
79 protected Object
[] getDependencies(CachedValueProvider
.Result
<T
> result
) {
80 return result
== null ?
null : result
.getDependencyItems();
84 protected Object
[] getDependenciesPlusValue(CachedValueProvider
.Result
<T
> result
) {
89 Object
[] items
= result
.getDependencyItems();
90 return result
.getValue() == null ? items
: items
== null ?
new Object
[] {result
.getValue()}: ArrayUtil
.append(items
, result
.getValue());
98 public void setDataLocked(boolean value
) {
99 myData
.setIsLocked(value
);
102 public boolean hasUpToDateValue() {
106 return getUpToDateOrNull() != null;
114 protected T
getUpToDateOrNull() {
115 final Data
<T
> data
= myData
.getData();
118 T value
= data
.myValue
;
119 if (isUpToDate(data
)) {
122 if (value
instanceof Disposable
) {
123 Disposer
.dispose((Disposable
)value
);
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;
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
);
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();
178 LOG
.error("Wrong dependency type: " + dependency
.getClass());
183 public T
setValue(final CachedValueProvider
.Result
<T
> result
) {
187 T value
= result
.getValue();
188 setValue(value
, result
);
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
) {
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() {
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
));
237 public Data
<T
> getData() {
238 final SoftReference
<Data
<T
>> ref
= get();
239 return ref
!= null ? ref
.get() : null;