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