001/*
002 * Copyright 2010-2013 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
017package org.jetbrains.jet.asJava;
018
019import com.google.common.collect.Sets;
020import com.intellij.navigation.ItemPresentation;
021import com.intellij.navigation.ItemPresentationProviders;
022import com.intellij.openapi.util.Comparing;
023import com.intellij.psi.PsiClass;
024import com.intellij.psi.PsiElement;
025import com.intellij.psi.PsiManager;
026import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
027import com.intellij.psi.search.GlobalSearchScope;
028import com.intellij.psi.util.CachedValue;
029import com.intellij.psi.util.CachedValuesManager;
030import org.jetbrains.annotations.NotNull;
031import org.jetbrains.annotations.Nullable;
032import org.jetbrains.jet.lang.psi.JetFile;
033import org.jetbrains.jet.lang.resolve.java.JetJavaMirrorMarker;
034import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
035import org.jetbrains.jet.lang.resolve.name.FqName;
036
037import javax.swing.*;
038import java.util.Collection;
039
040public class KotlinLightClassForPackage extends KotlinLightClassForPackageBase implements KotlinLightClass, JetJavaMirrorMarker {
041    @Nullable
042    public static KotlinLightClassForPackage create(
043            @NotNull PsiManager manager,
044            @NotNull FqName qualifiedName,
045            @NotNull GlobalSearchScope searchScope,
046            @NotNull Collection<JetFile> files // this is redundant, but computing it multiple times is costly
047    ) {
048        for (JetFile file : files) {
049            if (LightClassUtil.belongsToKotlinBuiltIns(file)) return null;
050        }
051        return new KotlinLightClassForPackage(manager, qualifiedName, searchScope, files);
052    }
053
054    private final FqName packageFqName;
055    private final FqName packageClassFqName; // derived from packageFqName
056    private final GlobalSearchScope searchScope;
057    private final Collection<JetFile> files;
058    private final int hashCode;
059    private final CachedValue<PsiJavaFileStub> javaFileStub;
060
061    private KotlinLightClassForPackage(
062            @NotNull PsiManager manager,
063            @NotNull FqName packageFqName,
064            @NotNull GlobalSearchScope searchScope,
065            @NotNull Collection<JetFile> files
066    ) {
067        super(manager);
068        this.packageFqName = packageFqName;
069        this.packageClassFqName = PackageClassUtils.getPackageClassFqName(packageFqName);
070        this.searchScope = searchScope;
071        assert !files.isEmpty() : "No files for package " + packageFqName;
072        this.files = Sets.newHashSet(files); // needed for hashCode
073        this.hashCode = computeHashCode();
074        KotlinJavaFileStubProvider stubProvider = KotlinJavaFileStubProvider.createForPackageClass(getProject(), packageFqName, searchScope);
075        this.javaFileStub = CachedValuesManager.getManager(getProject()).createCachedValue(stubProvider, /*trackValue = */false);
076    }
077
078    @NotNull
079    @Override
080    public FqName getFqName() {
081        return packageClassFqName;
082    }
083
084    @Nullable
085    @Override
086    public String getName() {
087        return packageClassFqName.shortName().asString();
088    }
089
090    @Nullable
091    @Override
092    public String getQualifiedName() {
093        return packageClassFqName.asString();
094    }
095
096    @Override
097    public boolean isValid() {
098        return allValid(files);
099    }
100
101    private static boolean allValid(Collection<JetFile> files) {
102        for (JetFile file : files) {
103            if (!file.isValid()) return false;
104        }
105        return true;
106    }
107
108    @NotNull
109    @Override
110    public PsiElement copy() {
111        return new KotlinLightClassForPackage(getManager(), packageFqName, searchScope, files);
112    }
113
114    @NotNull
115    @Override
116    public PsiClass getDelegate() {
117        PsiClass psiClass = LightClassUtil.findClass(packageClassFqName, javaFileStub.getValue());
118        if (psiClass == null) {
119            throw new IllegalStateException("Package class was not found " + packageFqName);
120        }
121        return psiClass;
122    }
123
124    @NotNull
125    @Override
126    public PsiElement getNavigationElement() {
127        return files.iterator().next();
128    }
129
130    @Override
131    public boolean isEquivalentTo(PsiElement another) {
132        return another instanceof PsiClass && Comparing.equal(((PsiClass) another).getQualifiedName(), getQualifiedName());
133    }
134
135    @Override
136    public ItemPresentation getPresentation() {
137        return ItemPresentationProviders.getItemPresentation(this);
138    }
139
140    @Override
141    public Icon getElementIcon(int flags) {
142        throw new UnsupportedOperationException("This should be done byt JetIconProvider");
143    }
144
145    @Override
146    public int hashCode() {
147        return hashCode;
148    }
149
150    private int computeHashCode() {
151        int result = getManager().hashCode();
152        result = 31 * result + files.hashCode();
153        result = 31 * result + packageFqName.hashCode();
154        return result;
155    }
156
157    @Override
158    public boolean equals(Object obj) {
159        if (this == obj) return true;
160        if (obj == null || getClass() != obj.getClass()) {
161            return false;
162        }
163
164        KotlinLightClassForPackage lightClass = (KotlinLightClassForPackage) obj;
165
166        if (this.hashCode != lightClass.hashCode) return false;
167        if (getManager() != lightClass.getManager()) return false;
168        if (!files.equals(lightClass.files)) return false;
169        if (!packageFqName.equals(lightClass.packageFqName)) return false;
170
171        return true;
172    }
173
174    @Override
175    public String toString() {
176        try {
177            return KotlinLightClassForPackage.class.getSimpleName() + ":" + getQualifiedName();
178        }
179        catch (Throwable e) {
180            return KotlinLightClassForPackage.class.getSimpleName() + ":" + e.toString();
181        }
182    }
183
184}