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
017package org.jetbrains.jet.lang.resolve;
018
019import com.google.common.base.Predicate;
020import com.google.common.collect.Lists;
021import com.intellij.openapi.util.Pair;
022import org.jetbrains.annotations.NotNull;
023import org.jetbrains.annotations.Nullable;
024import org.jetbrains.jet.lang.PlatformToKotlinClassMap;
025import org.jetbrains.jet.lang.descriptors.*;
026import org.jetbrains.jet.lang.resolve.name.Name;
027import org.jetbrains.jet.lang.resolve.scopes.FilteringScope;
028import org.jetbrains.jet.lang.resolve.scopes.JetScope;
029import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
030
031import java.util.Collection;
032import java.util.List;
033
034public 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}