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