001    /*
002     * Copyright 2010-2014 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    
017    package org.jetbrains.jet.lang.resolve.kotlin;
018    
019    import com.intellij.ide.highlighter.JavaClassFileType;
020    import com.intellij.openapi.Disposable;
021    import com.intellij.openapi.application.ApplicationManager;
022    import com.intellij.openapi.components.ServiceManager;
023    import com.intellij.openapi.util.Computable;
024    import com.intellij.openapi.vfs.VirtualFile;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    
028    public final class KotlinBinaryClassCache implements Disposable {
029        private static class RequestCache {
030            VirtualFile virtualFile;
031            long modificationStamp;
032            VirtualFileKotlinClass virtualFileKotlinClass;
033    
034            public VirtualFileKotlinClass cache(VirtualFile file, VirtualFileKotlinClass aClass) {
035                virtualFile = file;
036                virtualFileKotlinClass = aClass;
037                modificationStamp = file.getModificationStamp();
038    
039                return aClass;
040            }
041        }
042    
043        private final ThreadLocal<RequestCache> cache =
044                new ThreadLocal<RequestCache>() {
045                    @Override
046                    protected RequestCache initialValue() {
047                        return new RequestCache();
048                    }
049                };
050    
051        @Nullable
052        public static KotlinJvmBinaryClass getKotlinBinaryClass(@NotNull final VirtualFile file) {
053            if (file.getFileType() != JavaClassFileType.INSTANCE) return null;
054    
055            KotlinBinaryClassCache service = ServiceManager.getService(KotlinBinaryClassCache.class);
056            RequestCache requestCache = service.cache.get();
057    
058            if (file.getModificationStamp() == requestCache.modificationStamp && file.equals(requestCache.virtualFile)) {
059                return requestCache.virtualFileKotlinClass;
060            }
061            else {
062                VirtualFileKotlinClass aClass = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFileKotlinClass>() {
063                    @Override
064                    public VirtualFileKotlinClass compute() {
065                        //noinspection deprecation
066                        return VirtualFileKotlinClass.OBJECT$.create(file);
067                    }
068                });
069    
070                return requestCache.cache(file, aClass);
071            }
072        }
073    
074        @Override
075        public void dispose() {
076            // This is only relevant for tests. We create a new instance of Application for each test, and so a new instance of this service is
077            // also created for each test. However all tests share the same event dispatch thread, which would collect all instances of this
078            // thread-local if they're not removed properly. Each instance would transitively retain VFS resulting in OutOfMemoryError
079            cache.remove();
080        }
081    }