org.jetbrains.jet.plugin.references.StandardLibraryReferenceResolver.java Source code

Java tutorial

Introduction

Here is the source code for org.jetbrains.jet.plugin.references.StandardLibraryReferenceResolver.java

Source

/*
 * Copyright 2010-2012 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.jet.plugin.references;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.plugin.JetStandardLibraryInitializer;
import org.jetbrains.jet.resolve.DescriptorRenderer;

import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class StandardLibraryReferenceResolver extends AbstractProjectComponent {
    @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
    private BindingContext bindingContext = null;

    private final Object lock = new Object();
    private final FqName TUPLE0_FQ_NAME = DescriptorUtils.getFQName(KotlinBuiltIns.getInstance().getTuple(0))
            .toSafe();

    public StandardLibraryReferenceResolver(Project project,
            // This parameter is needed to initialize built-ins before this component
            JetStandardLibraryInitializer makeSureStandardLibraryIsInitialized) {
        super(project);
    }

    @Override
    public void initComponent() {
        StartupManager.getInstance(myProject).registerPostStartupActivity(new Runnable() {
            @Override
            public void run() {
                ensureInitialized();
            }
        });
    }

    private void ensureInitialized() {
        synchronized (lock) {
            if (bindingContext != null) {
                return;
            }

            BindingTraceContext context = new BindingTraceContext();
            FakeJetNamespaceDescriptor jetNamespace = new FakeJetNamespaceDescriptor();
            context.record(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR,
                    KotlinBuiltIns.getInstance().getBuiltInsPackageFqName(), jetNamespace);

            WritableScopeImpl scope = new WritableScopeImpl(JetScope.EMPTY, jetNamespace,
                    RedeclarationHandler.THROW_EXCEPTION, "Builtin classes scope");
            scope.changeLockLevel(WritableScope.LockLevel.BOTH);
            jetNamespace.setMemberScope(scope);

            Predicate<JetFile> jetFilesIndependentOfUnit = new Predicate<JetFile>() {
                @Override
                public boolean apply(@Nullable JetFile file) {
                    return "Unit.jet".equals(file.getName());
                }
            };
            TopDownAnalyzer.processStandardLibraryNamespace(myProject, context, scope, jetNamespace,
                    getJetFiles("jet", jetFilesIndependentOfUnit));

            ClassDescriptor tuple0 = context.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, TUPLE0_FQ_NAME);
            assert tuple0 != null;
            scope = new WritableScopeImpl(scope, jetNamespace, RedeclarationHandler.THROW_EXCEPTION,
                    "Builtin classes scope: needed to analyze builtins which depend on Unit type alias");
            scope.changeLockLevel(WritableScope.LockLevel.BOTH);
            scope.addClassifierAlias(KotlinBuiltIns.UNIT_ALIAS, tuple0);
            jetNamespace.setMemberScope(scope);

            TopDownAnalyzer.processStandardLibraryNamespace(myProject, context, scope, jetNamespace,
                    getJetFiles("jet", Predicates.not(jetFilesIndependentOfUnit)));

            AnalyzingUtils.throwExceptionOnErrors(context.getBindingContext());

            bindingContext = context.getBindingContext();
        }
    }

    private List<JetFile> getJetFiles(String dir, final Predicate<JetFile> filter) {
        URL url = StandardLibraryReferenceResolver.class.getResource("/" + dir + "/");
        VirtualFile vf = VfsUtil.findFileByURL(url);
        assert vf != null;

        PsiDirectory psiDirectory = PsiManager.getInstance(myProject).findDirectory(vf);
        assert psiDirectory != null;
        return ContainerUtil.mapNotNull(psiDirectory.getFiles(), new Function<PsiFile, JetFile>() {
            @Override
            public JetFile fun(PsiFile file) {
                if (file instanceof JetFile) {
                    JetFile jetFile = (JetFile) file;
                    return filter.apply(jetFile) ? jetFile : null;
                }
                return null;
            }
        });
    }

    @Nullable
    private DeclarationDescriptor findCurrentDescriptorForClass(@NotNull ClassDescriptor originalDescriptor) {
        if (originalDescriptor.getKind().isObject()) {
            DeclarationDescriptor currentParent = findCurrentDescriptor(
                    originalDescriptor.getContainingDeclaration());
            if (currentParent == null)
                return null;
            return ((ClassDescriptor) currentParent).getClassObjectDescriptor();
        } else {
            return bindingContext.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR,
                    DescriptorUtils.getFQName(originalDescriptor).toSafe());
        }
    }

    @Nullable
    private DeclarationDescriptor findCurrentDescriptorForMember(@NotNull MemberDescriptor originalDescriptor) {
        DeclarationDescriptor containingDeclaration = findCurrentDescriptor(
                originalDescriptor.getContainingDeclaration());
        JetScope memberScope = getMemberScope(containingDeclaration);
        if (memberScope == null)
            return null;

        String renderedOriginal = DescriptorRenderer.TEXT.render(originalDescriptor);
        Collection<? extends DeclarationDescriptor> descriptors;
        if (originalDescriptor instanceof ConstructorDescriptor
                && containingDeclaration instanceof ClassDescriptor) {
            descriptors = ((ClassDescriptor) containingDeclaration).getConstructors();
        } else {
            descriptors = memberScope.getAllDescriptors();
        }
        for (DeclarationDescriptor member : descriptors) {
            if (renderedOriginal.equals(DescriptorRenderer.TEXT.render(member).replace(TUPLE0_FQ_NAME.getFqName(),
                    KotlinBuiltIns.UNIT_ALIAS.getName()))) {
                return member;
            }
        }
        return null;
    }

    @Nullable
    private DeclarationDescriptor findCurrentDescriptor(@NotNull DeclarationDescriptor originalDescriptor) {
        if (originalDescriptor instanceof ClassDescriptor) {
            return findCurrentDescriptorForClass((ClassDescriptor) originalDescriptor);
        } else if (originalDescriptor instanceof NamespaceDescriptor) {
            return bindingContext.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR,
                    DescriptorUtils.getFQName(originalDescriptor).toSafe());
        } else if (originalDescriptor instanceof MemberDescriptor) {
            return findCurrentDescriptorForMember((MemberDescriptor) originalDescriptor);
        } else {
            return null;
        }
    }

    @NotNull
    public Collection<PsiElement> resolveStandardLibrarySymbol(@NotNull BindingContext originalContext,
            @Nullable JetReferenceExpression referenceExpression) {
        ensureInitialized();
        DeclarationDescriptor declarationDescriptor = originalContext.get(BindingContext.REFERENCE_TARGET,
                referenceExpression);

        return declarationDescriptor != null ? resolveStandardLibrarySymbol(declarationDescriptor)
                : Collections.<PsiElement>emptyList();
    }

    @NotNull
    public Collection<PsiElement> resolveStandardLibrarySymbol(
            @NotNull DeclarationDescriptor declarationDescriptor) {
        ensureInitialized();
        DeclarationDescriptor descriptor = declarationDescriptor;

        descriptor = descriptor.getOriginal();
        descriptor = findCurrentDescriptor(descriptor);
        if (descriptor != null) {
            return BindingContextUtils.descriptorToDeclarations(bindingContext, descriptor);
        }
        return Collections.emptyList();
    }

    @Nullable
    private static JetScope getMemberScope(@Nullable DeclarationDescriptor parent) {
        if (parent instanceof ClassDescriptor) {
            return ((ClassDescriptor) parent).getDefaultType().getMemberScope();
        } else if (parent instanceof NamespaceDescriptor) {
            return ((NamespaceDescriptor) parent).getMemberScope();
        } else {
            return null;
        }
    }

    private static class FakeJetNamespaceDescriptor extends NamespaceDescriptorImpl {
        private WritableScope memberScope;

        private FakeJetNamespaceDescriptor() {
            super(new NamespaceDescriptorImpl(new ModuleDescriptor(Name.special("<fake_module>")),
                    Collections.<AnnotationDescriptor>emptyList(), Name.special("<root>")),
                    Collections.<AnnotationDescriptor>emptyList(),
                    KotlinBuiltIns.getInstance().getBuiltInsPackage().getName());
        }

        void setMemberScope(WritableScope memberScope) {
            this.memberScope = memberScope;
        }

        @NotNull
        @Override
        public WritableScope getMemberScope() {
            return memberScope;
        }
    }
}