001    /*
002     * Copyright 2010-2014 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.light.LightEmptyImplementsList;
025    import com.intellij.psi.impl.light.LightModifierList;
026    import com.intellij.psi.javadoc.PsiDocComment;
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.NonNls;
031    import org.jetbrains.annotations.NotNull;
032    import org.jetbrains.annotations.Nullable;
033    import org.jetbrains.jet.lang.psi.JetClassOrObject;
034    import org.jetbrains.jet.lang.psi.JetFile;
035    import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
036    import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetJavaMirrorMarker;
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 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<KotlinPackageLightClassData> lightClassDataCache;
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<KotlinPackageLightClassData> stubProvider =
071                    KotlinJavaFileStubProvider.createForPackageClass(getProject(), packageFqName, searchScope);
072            this.lightClassDataCache = 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 JetClassOrObject getOrigin() {
098            return null;
099        }
100    
101        @Nullable
102        @Override
103        public PsiModifierList getModifierList() {
104            return modifierList;
105        }
106    
107        @Override
108        public boolean hasModifierProperty(@NonNls @NotNull String name) {
109            return modifierList.hasModifierProperty(name);
110        }
111    
112        @Override
113        public boolean isDeprecated() {
114            return false;
115        }
116    
117        @Override
118        public boolean isInterface() {
119            return false;
120        }
121    
122        @Override
123        public boolean isAnnotationType() {
124            return false;
125        }
126    
127        @Override
128        public boolean isEnum() {
129            return false;
130        }
131    
132        @Nullable
133        @Override
134        public PsiClass getContainingClass() {
135            return null;
136        }
137    
138        @Override
139        public boolean hasTypeParameters() {
140            return false;
141        }
142    
143        @NotNull
144        @Override
145        public PsiTypeParameter[] getTypeParameters() {
146            return PsiTypeParameter.EMPTY_ARRAY;
147        }
148    
149        @Nullable
150        @Override
151        public PsiTypeParameterList getTypeParameterList() {
152            return null;
153        }
154    
155        @Nullable
156        @Override
157        public PsiDocComment getDocComment() {
158            return null;
159        }
160    
161        @Nullable
162        @Override
163        public PsiReferenceList getImplementsList() {
164            return implementsList;
165        }
166    
167        @NotNull
168        @Override
169        public PsiClassType[] getImplementsListTypes() {
170            return PsiClassType.EMPTY_ARRAY;
171        }
172    
173        @Nullable
174        @Override
175        public PsiReferenceList getExtendsList() {
176            // TODO: Find a way to return just Object
177            return super.getExtendsList();
178        }
179    
180        @NotNull
181        @Override
182        public PsiClassType[] getExtendsListTypes() {
183            // TODO see getExtendsList()
184            return super.getExtendsListTypes();
185        }
186    
187        @Nullable
188        @Override
189        public PsiClass getSuperClass() {
190            // TODO see getExtendsList()
191            return super.getSuperClass();
192        }
193    
194        @NotNull
195        @Override
196        public PsiClass[] getSupers() {
197            // TODO see getExtendsList()
198            return super.getSupers();
199        }
200    
201        @NotNull
202        @Override
203        public PsiClassType[] getSuperTypes() {
204            // TODO see getExtendsList()
205            return super.getSuperTypes();
206        }
207    
208        @Override
209        public PsiClass[] getInterfaces() {
210            return PsiClass.EMPTY_ARRAY;
211        }
212    
213        @NotNull
214        @Override
215        public PsiClass[] getInnerClasses() {
216            return PsiClass.EMPTY_ARRAY;
217        }
218    
219        @NotNull
220        @Override
221        public List<PsiClass> getOwnInnerClasses() {
222            return Collections.emptyList();
223        }
224    
225        @NotNull
226        @Override
227        public PsiClass[] getAllInnerClasses() {
228            return PsiClass.EMPTY_ARRAY;
229        }
230    
231        @NotNull
232        @Override
233        public PsiClassInitializer[] getInitializers() {
234            return PsiClassInitializer.EMPTY_ARRAY;
235        }
236    
237        @Nullable
238        @Override
239        public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) {
240            return null;
241        }
242    
243        @NotNull
244        @Override
245        public FqName getFqName() {
246            return packageClassFqName;
247        }
248    
249        @Nullable
250        @Override
251        public String getName() {
252            return packageClassFqName.shortName().asString();
253        }
254    
255        @Nullable
256        @Override
257        public String getQualifiedName() {
258            return packageClassFqName.asString();
259        }
260    
261        @Override
262        public boolean isValid() {
263            return allValid(files);
264        }
265    
266        @NotNull
267        @Override
268        public PsiElement copy() {
269            return new KotlinLightClassForPackage(getManager(), packageFqName, searchScope, files);
270        }
271    
272        @NotNull
273        @Override
274        public PsiClass getDelegate() {
275            PsiClass psiClass = LightClassUtil.findClass(packageClassFqName, lightClassDataCache.getValue().getJavaFileStub());
276            if (psiClass == null) {
277                throw new IllegalStateException("Package class was not found " + packageFqName);
278            }
279            return psiClass;
280        }
281    
282        @NotNull
283        @Override
284        public PsiElement getNavigationElement() {
285            return files.iterator().next();
286        }
287    
288        @Override
289        public boolean isEquivalentTo(PsiElement another) {
290            return another instanceof PsiClass && Comparing.equal(((PsiClass) another).getQualifiedName(), getQualifiedName());
291        }
292    
293        @Override
294        public ItemPresentation getPresentation() {
295            return ItemPresentationProviders.getItemPresentation(this);
296        }
297    
298        @Override
299        public Icon getElementIcon(int flags) {
300            throw new UnsupportedOperationException("This should be done byt JetIconProvider");
301        }
302    
303        @Override
304        public int hashCode() {
305            return hashCode;
306        }
307    
308        private int computeHashCode() {
309            int result = getManager().hashCode();
310            result = 31 * result + files.hashCode();
311            result = 31 * result + packageFqName.hashCode();
312            return result;
313        }
314    
315        @Override
316        public boolean equals(Object obj) {
317            if (this == obj) return true;
318            if (obj == null || getClass() != obj.getClass()) {
319                return false;
320            }
321    
322            KotlinLightClassForPackage lightClass = (KotlinLightClassForPackage) obj;
323    
324            if (this.hashCode != lightClass.hashCode) return false;
325            if (getManager() != lightClass.getManager()) return false;
326            if (!files.equals(lightClass.files)) return false;
327            if (!packageFqName.equals(lightClass.packageFqName)) return false;
328    
329            return true;
330        }
331    
332        @Override
333        public String toString() {
334            try {
335                return KotlinLightClassForPackage.class.getSimpleName() + ":" + getQualifiedName();
336            }
337            catch (Throwable e) {
338                return KotlinLightClassForPackage.class.getSimpleName() + ":" + e.toString();
339            }
340        }
341    }