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    
017    package org.jetbrains.jet.asJava;
018    
019    import com.google.common.collect.Sets;
020    import com.intellij.navigation.ItemPresentation;
021    import com.intellij.navigation.ItemPresentationProviders;
022    import com.intellij.openapi.util.Comparing;
023    import com.intellij.psi.PsiClass;
024    import com.intellij.psi.PsiElement;
025    import com.intellij.psi.PsiManager;
026    import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
027    import com.intellij.psi.search.GlobalSearchScope;
028    import com.intellij.psi.util.CachedValue;
029    import com.intellij.psi.util.CachedValuesManager;
030    import org.jetbrains.annotations.NotNull;
031    import org.jetbrains.annotations.Nullable;
032    import org.jetbrains.jet.lang.psi.JetFile;
033    import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetJavaMirrorMarker;
034    import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
035    import org.jetbrains.jet.lang.resolve.name.FqName;
036    
037    import javax.swing.*;
038    import java.util.Collection;
039    
040    public 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    }