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.lang.resolve;
018    
019    import com.google.common.base.Predicate;
020    import com.google.common.collect.Lists;
021    import com.intellij.openapi.util.Pair;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.PlatformToKotlinClassMap;
025    import org.jetbrains.jet.lang.descriptors.*;
026    import org.jetbrains.jet.lang.resolve.name.Name;
027    import org.jetbrains.jet.lang.resolve.scopes.FilteringScope;
028    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
029    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
030    
031    import java.util.Collection;
032    import java.util.List;
033    
034    public interface Importer {
035        void addAllUnderImport(@NotNull DeclarationDescriptor descriptor, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap);
036    
037        void addAliasImport(@NotNull DeclarationDescriptor descriptor, @NotNull Name aliasName);
038    
039        Importer DO_NOTHING = new Importer() {
040            @Override
041            public void addAllUnderImport(@NotNull DeclarationDescriptor descriptor, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap) {
042            }
043    
044            @Override
045            public void addAliasImport(@NotNull DeclarationDescriptor descriptor, @NotNull Name aliasName) {
046            }
047        };
048    
049        class StandardImporter implements Importer {
050            private final WritableScope namespaceScope;
051    
052            public StandardImporter(WritableScope namespaceScope) {
053                this.namespaceScope = namespaceScope;
054            }
055    
056            @Override
057            public void addAllUnderImport(@NotNull DeclarationDescriptor descriptor, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap) {
058                importAllUnderDeclaration(descriptor, platformToKotlinClassMap);
059            }
060    
061            @Override
062            public void addAliasImport(@NotNull DeclarationDescriptor descriptor, @NotNull Name aliasName) {
063                importDeclarationAlias(descriptor, aliasName);
064            }
065    
066            @NotNull
067            private static JetScope createFilteringScope(
068                    @NotNull JetScope scope,
069                    @NotNull DeclarationDescriptor descriptor,
070                    @NotNull PlatformToKotlinClassMap platformToKotlinClassMap
071            ) {
072                final Collection<ClassDescriptor> kotlinAnalogsForClassesInside = platformToKotlinClassMap.mapPlatformClassesInside(
073                        descriptor);
074                if (kotlinAnalogsForClassesInside.isEmpty()) return scope;
075                return new FilteringScope(scope, new Predicate<DeclarationDescriptor>() {
076                    @Override
077                    public boolean apply(DeclarationDescriptor descriptor) {
078                        for (ClassDescriptor kotlinAnalog : kotlinAnalogsForClassesInside) {
079                            if (kotlinAnalog.getName().equals(descriptor.getName())) {
080                                return false;
081                            }
082                        }
083                        return true;
084                    }
085                });
086            }
087    
088            protected void importAllUnderDeclaration(@NotNull DeclarationDescriptor descriptor, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap) {
089                JetScope scopeToImport = null;
090                if (descriptor instanceof NamespaceDescriptor) {
091                    scopeToImport = ((NamespaceDescriptor) descriptor).getMemberScope();
092                }
093                if (descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() != ClassKind.OBJECT) {
094                    ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
095                    scopeToImport = classDescriptor.getUnsubstitutedInnerClassesScope();
096                    ClassDescriptor classObjectDescriptor = classDescriptor.getClassObjectDescriptor();
097                    if (classObjectDescriptor != null) {
098                        scopeToImport = classObjectDescriptor.getUnsubstitutedInnerClassesScope();
099                    }
100                }
101                if (scopeToImport != null) {
102                    namespaceScope.importScope(createFilteringScope(scopeToImport, descriptor, platformToKotlinClassMap));
103                }
104            }
105    
106            protected void importDeclarationAlias(@NotNull DeclarationDescriptor descriptor, @NotNull Name aliasName) {
107                if (descriptor instanceof ClassifierDescriptor) {
108                    namespaceScope.importClassifierAlias(aliasName, (ClassifierDescriptor) descriptor);
109                }
110                else if (descriptor instanceof NamespaceDescriptor) {
111                    namespaceScope.importNamespaceAlias(aliasName, (NamespaceDescriptor) descriptor);
112                }
113                else if (descriptor instanceof FunctionDescriptor) {
114                    namespaceScope.importFunctionAlias(aliasName, (FunctionDescriptor) descriptor);
115                }
116                else if (descriptor instanceof VariableDescriptor) {
117                    namespaceScope.importVariableAlias(aliasName, (VariableDescriptor) descriptor);
118                }
119            }
120    
121        }
122    
123        class DelayedImporter extends StandardImporter {
124            private interface DelayedImportEntry {}
125            private static class AllUnderImportEntry extends Pair<DeclarationDescriptor, PlatformToKotlinClassMap> implements DelayedImportEntry {
126                public AllUnderImportEntry(@NotNull DeclarationDescriptor first, @Nullable PlatformToKotlinClassMap second) {
127                    super(first, second);
128                }
129            }
130            private static class AliasImportEntry extends Pair<DeclarationDescriptor, Name> implements DelayedImportEntry {
131                public AliasImportEntry(DeclarationDescriptor first, Name second) {
132                    super(first, second);
133                }
134            }
135    
136            private final List<DelayedImportEntry> imports = Lists.newArrayList();
137    
138            public DelayedImporter(@NotNull WritableScope namespaceScope) {
139                super(namespaceScope);
140            }
141    
142            @Override
143            public void addAllUnderImport(@NotNull DeclarationDescriptor descriptor, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap) {
144                imports.add(new AllUnderImportEntry(descriptor, platformToKotlinClassMap));
145            }
146    
147            @Override
148            public void addAliasImport(@NotNull DeclarationDescriptor descriptor, @NotNull Name aliasName) {
149                imports.add(new AliasImportEntry(descriptor, aliasName));
150            }
151    
152            public void processImports() {
153                for (DelayedImportEntry anImport : imports) {
154                    if (anImport instanceof AllUnderImportEntry) {
155                        AllUnderImportEntry allUnderImportEntry = (AllUnderImportEntry) anImport;
156                        importAllUnderDeclaration(allUnderImportEntry.getFirst(), allUnderImportEntry.getSecond());
157                    }
158                    else {
159                        AliasImportEntry aliasImportEntry = (AliasImportEntry) anImport;
160                        importDeclarationAlias(aliasImportEntry.getFirst(), aliasImportEntry.getSecond());
161                    }
162                }
163            }
164        }
165    }