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.*;
024    import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
025    import com.intellij.psi.impl.light.AbstractLightClass;
026    import com.intellij.psi.impl.light.LightEmptyImplementsList;
027    import com.intellij.psi.impl.light.LightModifierList;
028    import com.intellij.psi.javadoc.PsiDocComment;
029    import com.intellij.psi.search.GlobalSearchScope;
030    import com.intellij.psi.util.CachedValue;
031    import com.intellij.psi.util.CachedValuesManager;
032    import org.jetbrains.annotations.NonNls;
033    import org.jetbrains.annotations.NotNull;
034    import org.jetbrains.annotations.Nullable;
035    import org.jetbrains.jet.lang.psi.JetFile;
036    import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetJavaMirrorMarker;
037    import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
038    import org.jetbrains.jet.lang.resolve.name.FqName;
039    import org.jetbrains.jet.plugin.JetLanguage;
040    
041    import javax.swing.*;
042    import java.util.Collection;
043    
044    public class KotlinLightClassForPackage extends AbstractLightClass implements KotlinLightClass, JetJavaMirrorMarker {
045        private final FqName packageFqName;
046        private final FqName packageClassFqName; // derived from packageFqName
047        private final GlobalSearchScope searchScope;
048        private final Collection<JetFile> files;
049        private final int hashCode;
050        private final CachedValue<PsiJavaFileStub> javaFileStub;
051        private final PsiModifierList modifierList;
052        private final LightEmptyImplementsList implementsList;
053    
054        private KotlinLightClassForPackage(
055                @NotNull PsiManager manager,
056                @NotNull FqName packageFqName,
057                @NotNull GlobalSearchScope searchScope,
058                @NotNull Collection<JetFile> files
059        ) {
060            super(manager, JetLanguage.INSTANCE);
061            this.modifierList = new LightModifierList(manager, JetLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL);
062            this.implementsList = new LightEmptyImplementsList(manager);
063            this.packageFqName = packageFqName;
064            this.packageClassFqName = PackageClassUtils.getPackageClassFqName(packageFqName);
065            this.searchScope = searchScope;
066            assert !files.isEmpty() : "No files for package " + packageFqName;
067            this.files = Sets.newHashSet(files); // needed for hashCode
068            this.hashCode = computeHashCode();
069            KotlinJavaFileStubProvider stubProvider =
070                    KotlinJavaFileStubProvider.createForPackageClass(getProject(), packageFqName, searchScope);
071            this.javaFileStub = CachedValuesManager.getManager(getProject()).createCachedValue(stubProvider, /*trackValue = */false);
072        }
073    
074        @Nullable
075        public static KotlinLightClassForPackage create(
076                @NotNull PsiManager manager,
077                @NotNull FqName qualifiedName,
078                @NotNull GlobalSearchScope searchScope,
079                @NotNull Collection<JetFile> files // this is redundant, but computing it multiple times is costly
080        ) {
081            for (JetFile file : files) {
082                if (LightClassUtil.belongsToKotlinBuiltIns(file)) return null;
083            }
084            return new KotlinLightClassForPackage(manager, qualifiedName, searchScope, files);
085        }
086    
087        private static boolean allValid(Collection<JetFile> files) {
088            for (JetFile file : files) {
089                if (!file.isValid()) return false;
090            }
091            return true;
092        }
093    
094        @Nullable
095        @Override
096        public PsiModifierList getModifierList() {
097            return modifierList;
098        }
099    
100        @Override
101        public boolean hasModifierProperty(@NonNls @NotNull String name) {
102            return modifierList.hasModifierProperty(name);
103        }
104    
105        @Override
106        public boolean isDeprecated() {
107            return false;
108        }
109    
110        @Override
111        public boolean isInterface() {
112            return false;
113        }
114    
115        @Override
116        public boolean isAnnotationType() {
117            return false;
118        }
119    
120        @Override
121        public boolean isEnum() {
122            return false;
123        }
124    
125        @Nullable
126        @Override
127        public PsiClass getContainingClass() {
128            return null;
129        }
130    
131        @Override
132        public boolean hasTypeParameters() {
133            return false;
134        }
135    
136        @NotNull
137        @Override
138        public PsiTypeParameter[] getTypeParameters() {
139            return PsiTypeParameter.EMPTY_ARRAY;
140        }
141    
142        @Nullable
143        @Override
144        public PsiTypeParameterList getTypeParameterList() {
145            return null;
146        }
147    
148        @Nullable
149        @Override
150        public PsiDocComment getDocComment() {
151            return null;
152        }
153    
154        @Nullable
155        @Override
156        public PsiReferenceList getImplementsList() {
157            return implementsList;
158        }
159    
160        @NotNull
161        @Override
162        public PsiClassType[] getImplementsListTypes() {
163            return PsiClassType.EMPTY_ARRAY;
164        }
165    
166        @Nullable
167        @Override
168        public PsiReferenceList getExtendsList() {
169            // TODO: Find a way to return just Object
170            return super.getExtendsList();
171        }
172    
173        @NotNull
174        @Override
175        public PsiClassType[] getExtendsListTypes() {
176            // TODO see getExtendsList()
177            return super.getExtendsListTypes();
178        }
179    
180        @Nullable
181        @Override
182        public PsiClass getSuperClass() {
183            // TODO see getExtendsList()
184            return super.getSuperClass();
185        }
186    
187        @NotNull
188        @Override
189        public PsiClass[] getSupers() {
190            // TODO see getExtendsList()
191            return super.getSupers();
192        }
193    
194        @NotNull
195        @Override
196        public PsiClassType[] getSuperTypes() {
197            // TODO see getExtendsList()
198            return super.getSuperTypes();
199        }
200    
201        @Override
202        public PsiClass[] getInterfaces() {
203            return PsiClass.EMPTY_ARRAY;
204        }
205    
206        @NotNull
207        @Override
208        public PsiClass[] getInnerClasses() {
209            return PsiClass.EMPTY_ARRAY;
210        }
211    
212        @NotNull
213        @Override
214        public PsiClassInitializer[] getInitializers() {
215            return PsiClassInitializer.EMPTY_ARRAY;
216        }
217    
218        @NotNull
219        @Override
220        public PsiClass[] getAllInnerClasses() {
221            return PsiClass.EMPTY_ARRAY;
222        }
223    
224        @Nullable
225        @Override
226        public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) {
227            return null;
228        }
229    
230        @NotNull
231        @Override
232        public FqName getFqName() {
233            return packageClassFqName;
234        }
235    
236        @Nullable
237        @Override
238        public String getName() {
239            return packageClassFqName.shortName().asString();
240        }
241    
242        @Nullable
243        @Override
244        public String getQualifiedName() {
245            return packageClassFqName.asString();
246        }
247    
248        @Override
249        public boolean isValid() {
250            return allValid(files);
251        }
252    
253        @NotNull
254        @Override
255        public PsiElement copy() {
256            return new KotlinLightClassForPackage(getManager(), packageFqName, searchScope, files);
257        }
258    
259        @NotNull
260        @Override
261        public PsiClass getDelegate() {
262            PsiClass psiClass = LightClassUtil.findClass(packageClassFqName, javaFileStub.getValue());
263            if (psiClass == null) {
264                throw new IllegalStateException("Package class was not found " + packageFqName);
265            }
266            return psiClass;
267        }
268    
269        @NotNull
270        @Override
271        public PsiElement getNavigationElement() {
272            return files.iterator().next();
273        }
274    
275        @Override
276        public boolean isEquivalentTo(PsiElement another) {
277            return another instanceof PsiClass && Comparing.equal(((PsiClass) another).getQualifiedName(), getQualifiedName());
278        }
279    
280        @Override
281        public ItemPresentation getPresentation() {
282            return ItemPresentationProviders.getItemPresentation(this);
283        }
284    
285        @Override
286        public Icon getElementIcon(int flags) {
287            throw new UnsupportedOperationException("This should be done byt JetIconProvider");
288        }
289    
290        @Override
291        public int hashCode() {
292            return hashCode;
293        }
294    
295        private int computeHashCode() {
296            int result = getManager().hashCode();
297            result = 31 * result + files.hashCode();
298            result = 31 * result + packageFqName.hashCode();
299            return result;
300        }
301    
302        @Override
303        public boolean equals(Object obj) {
304            if (this == obj) return true;
305            if (obj == null || getClass() != obj.getClass()) {
306                return false;
307            }
308    
309            KotlinLightClassForPackage lightClass = (KotlinLightClassForPackage) obj;
310    
311            if (this.hashCode != lightClass.hashCode) return false;
312            if (getManager() != lightClass.getManager()) return false;
313            if (!files.equals(lightClass.files)) return false;
314            if (!packageFqName.equals(lightClass.packageFqName)) return false;
315    
316            return true;
317        }
318    
319        @Override
320        public String toString() {
321            try {
322                return KotlinLightClassForPackage.class.getSimpleName() + ":" + getQualifiedName();
323            }
324            catch (Throwable e) {
325                return KotlinLightClassForPackage.class.getSimpleName() + ":" + e.toString();
326            }
327        }
328    }