org.jetbrains.kotlin.core.builder.KotlinPsiManager.java Source code

Java tutorial

Introduction

Here is the source code for org.jetbrains.kotlin.core.builder.KotlinPsiManager.java

Source

/*******************************************************************************
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *******************************************************************************/
package org.jetbrains.kotlin.core.builder;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.core.log.KotlinLogger;
import org.jetbrains.kotlin.core.model.KotlinEnvironment;
import org.jetbrains.kotlin.idea.JetFileType;
import org.jetbrains.kotlin.idea.JetLanguage;
import org.jetbrains.kotlin.psi.JetFile;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.impl.PsiFileFactoryImpl;
import com.intellij.testFramework.LightVirtualFile;

public class KotlinPsiManager {

    public static final KotlinPsiManager INSTANCE = new KotlinPsiManager();

    private final Map<IProject, Set<IFile>> projectFiles = new HashMap<>();
    private final Map<IFile, JetFile> cachedJetFiles = new HashMap<>();

    private final Object mapOperationLock = new Object();

    private KotlinPsiManager() {
    }

    public void updateProjectPsiSources(@NotNull IFile file, int flag) {
        switch (flag) {
        case IResourceDelta.ADDED:
            addFile(file);
            break;

        case IResourceDelta.REMOVED:
            removeFile(file);
            break;

        default:
            throw new IllegalArgumentException();
        }
    }

    public void updateProjectPsiSources(@NotNull IProject project, int flag) {
        switch (flag) {
        case IResourceDelta.REMOVED:
            removeProject(project);
            break;
        }
    }

    public void removeProject(@NotNull IProject project) {
        synchronized (mapOperationLock) {
            Set<IFile> files = getFilesByProject(project);
            projectFiles.remove(project);
            for (IFile file : files) {
                cachedJetFiles.remove(file);
            }
        }
    }

    public void addFile(@NotNull IFile file) {
        synchronized (mapOperationLock) {
            assert !exists(file) : "File(" + file.getName() + ") is already added";

            IProject project = file.getProject();
            if (!projectFiles.containsKey(project)) {
                projectFiles.put(project, new HashSet<IFile>());
            }

            projectFiles.get(project).add(file);
        }
    }

    public void removeFile(@NotNull IFile file) {
        synchronized (mapOperationLock) {
            assert exists(file) : "File(" + file.getName() + ") does not contain in the psiFiles";

            IProject project = file.getProject();

            cachedJetFiles.remove(file);
            projectFiles.get(project).remove(file);
        }
    }

    @NotNull
    public Set<IFile> getFilesByProject(@Nullable IProject project) {
        synchronized (mapOperationLock) {
            if (projectFiles.containsKey(project)) {
                return Collections.unmodifiableSet(projectFiles.get(project));
            }

            return Collections.emptySet();
        }
    }

    @NotNull
    public JetFile getParsedFile(@NotNull IFile file) {
        synchronized (mapOperationLock) {
            assert exists(file) : "File(" + file.getName() + ") does not contain in the psiFiles";

            if (!cachedJetFiles.containsKey(file)) {
                JetFile jetFile = parseFile(file);
                cachedJetFiles.put(file, jetFile);
            }

            return cachedJetFiles.get(file);
        }
    }

    public boolean exists(@NotNull IFile file) {
        synchronized (mapOperationLock) {
            IProject project = file.getProject();
            if (project == null)
                return false;

            Set<IFile> files = projectFiles.get(project);
            return files != null ? files.contains(file) : false;
        }
    }

    @NotNull
    public Set<IFile> getFilesByProject(@NotNull String projectName) {
        IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
        return getFilesByProject(project);
    }

    public boolean isProjectChangedState(@NotNull IResourceDelta delta) {
        return (delta.getFlags() & IResourceDelta.CONTENT) != 0 || (delta.getKind() == IResourceDelta.REMOVED)
                || (delta.getKind() == IResourceDelta.ADDED);
    }

    public boolean isKotlinSourceFile(@NotNull IResource resource) throws JavaModelException {
        return isKotlinSourceFile(resource, JavaCore.create(resource.getProject()));
    }

    public boolean isKotlinSourceFile(@NotNull IResource resource, @NotNull IJavaProject javaProject)
            throws JavaModelException {
        if (!(resource instanceof IFile)
                || !JetFileType.INSTANCE.getDefaultExtension().equals(resource.getFileExtension())) {
            return false;
        }

        if (!javaProject.exists()) {
            return false;
        }

        IClasspathEntry[] classpathEntries = javaProject.getRawClasspath();
        String resourceRoot = resource.getFullPath().segment(1);
        for (IClasspathEntry classpathEntry : classpathEntries) {
            if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                if (resourceRoot.equals(classpathEntry.getPath().segment(1))) {
                    return true;
                }
            }
        }

        return false;
    }

    @Nullable
    private JetFile parseFile(@NotNull IFile file) {
        synchronized (mapOperationLock) {
            try {
                File ioFile = new File(file.getRawLocation().toOSString());
                return parseText(FileUtil.loadFile(ioFile, null, true), file);
            } catch (IOException e) {
                KotlinLogger.logAndThrow(e);
            }
        }

        return null;
    }

    @NotNull
    private JetFile getParsedFile(@NotNull IFile file, @NotNull String expectedSourceCode) {
        synchronized (mapOperationLock) {
            updatePsiFile(file, expectedSourceCode);
            return getParsedFile(file);
        }
    }

    private void updatePsiFile(@NotNull IFile file, @NotNull String sourceCode) {
        String sourceCodeWithouCR = StringUtilRt.convertLineSeparators(sourceCode);
        synchronized (mapOperationLock) {
            assert exists(file) : "File(" + file.getName() + ") does not contain in the psiFiles";

            PsiFile currentParsedFile = getParsedFile(file);
            if (!currentParsedFile.getText().equals(sourceCodeWithouCR)) {
                JetFile jetFile = parseText(sourceCodeWithouCR, file);
                cachedJetFiles.put(file, jetFile);
            }
        }
    }

    @Nullable
    public JetFile parseText(@NotNull String text, @NotNull IFile file) {
        StringUtil.assertValidSeparators(text);

        IJavaProject javaProject = JavaCore.create(file.getProject());
        Project project = KotlinEnvironment.getEnvironment(javaProject).getProject();

        String path = file.getRawLocation().toOSString();
        LightVirtualFile virtualFile = new LightVirtualFile(path, JetLanguage.INSTANCE, text);
        virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);

        PsiFileFactoryImpl psiFileFactory = (PsiFileFactoryImpl) PsiFileFactory.getInstance(project);

        return (JetFile) psiFileFactory.trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false);
    }

    @Nullable
    public static JetFile getKotlinParsedFile(@NotNull IFile file) {
        return INSTANCE.exists(file) ? INSTANCE.getParsedFile(file) : null;
    }

    @Nullable
    public static JetFile getKotlinFileIfExist(@NotNull IFile file, @NotNull String sourceCode) {
        return INSTANCE.exists(file) ? INSTANCE.getParsedFile(file, sourceCode) : null;
    }
}