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.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.JetFile;
034    import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
035    import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetJavaMirrorMarker;
036    import org.jetbrains.jet.lang.resolve.name.FqName;
037    import org.jetbrains.jet.plugin.JetLanguage;
038    
039    import javax.swing.*;
040    import java.util.Collection;
041    import java.util.Collections;
042    import java.util.List;
043    
044    public class KotlinLightClassForPackage extends KotlinWrappingLightClass 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<LightClassStubWithData> 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 List<PsiClass> getOwnInnerClasses() {
215            return Collections.emptyList();
216        }
217    
218        @NotNull
219        @Override
220        public PsiClass[] getAllInnerClasses() {
221            return PsiClass.EMPTY_ARRAY;
222        }
223    
224        @NotNull
225        @Override
226        public PsiClassInitializer[] getInitializers() {
227            return PsiClassInitializer.EMPTY_ARRAY;
228        }
229    
230        @Nullable
231        @Override
232        public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) {
233            return null;
234        }
235    
236        @NotNull
237        @Override
238        public FqName getFqName() {
239            return packageClassFqName;
240        }
241    
242        @Nullable
243        @Override
244        public String getName() {
245            return packageClassFqName.shortName().asString();
246        }
247    
248        @Nullable
249        @Override
250        public String getQualifiedName() {
251            return packageClassFqName.asString();
252        }
253    
254        @Override
255        public boolean isValid() {
256            return allValid(files);
257        }
258    
259        @NotNull
260        @Override
261        public PsiElement copy() {
262            return new KotlinLightClassForPackage(getManager(), packageFqName, searchScope, files);
263        }
264    
265        @NotNull
266        @Override
267        public PsiClass getDelegate() {
268            PsiClass psiClass = LightClassUtil.findClass(packageClassFqName, javaFileStub.getValue().getJavaFileStub());
269            if (psiClass == null) {
270                throw new IllegalStateException("Package class was not found " + packageFqName);
271            }
272            return psiClass;
273        }
274    
275        @NotNull
276        @Override
277        public PsiElement getNavigationElement() {
278            return files.iterator().next();
279        }
280    
281        @Override
282        public boolean isEquivalentTo(PsiElement another) {
283            return another instanceof PsiClass && Comparing.equal(((PsiClass) another).getQualifiedName(), getQualifiedName());
284        }
285    
286        @Override
287        public ItemPresentation getPresentation() {
288            return ItemPresentationProviders.getItemPresentation(this);
289        }
290    
291        @Override
292        public Icon getElementIcon(int flags) {
293            throw new UnsupportedOperationException("This should be done byt JetIconProvider");
294        }
295    
296        @Override
297        public int hashCode() {
298            return hashCode;
299        }
300    
301        private int computeHashCode() {
302            int result = getManager().hashCode();
303            result = 31 * result + files.hashCode();
304            result = 31 * result + packageFqName.hashCode();
305            return result;
306        }
307    
308        @Override
309        public boolean equals(Object obj) {
310            if (this == obj) return true;
311            if (obj == null || getClass() != obj.getClass()) {
312                return false;
313            }
314    
315            KotlinLightClassForPackage lightClass = (KotlinLightClassForPackage) obj;
316    
317            if (this.hashCode != lightClass.hashCode) return false;
318            if (getManager() != lightClass.getManager()) return false;
319            if (!files.equals(lightClass.files)) return false;
320            if (!packageFqName.equals(lightClass.packageFqName)) return false;
321    
322            return true;
323        }
324    
325        @Override
326        public String toString() {
327            try {
328                return KotlinLightClassForPackage.class.getSimpleName() + ":" + getQualifiedName();
329            }
330            catch (Throwable e) {
331                return KotlinLightClassForPackage.class.getSimpleName() + ":" + e.toString();
332            }
333        }
334    }