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.k2js.translate.declaration;
018    
019    import com.google.dart.compiler.backend.js.ast.*;
020    import gnu.trove.THashMap;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
023    import org.jetbrains.jet.lang.psi.JetFile;
024    import org.jetbrains.jet.lang.resolve.BindingContext;
025    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
026    import org.jetbrains.k2js.translate.context.Namer;
027    import org.jetbrains.k2js.translate.context.TranslationContext;
028    import org.jetbrains.k2js.translate.general.AbstractTranslator;
029    
030    import java.util.*;
031    
032    import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar;
033    import static org.jetbrains.k2js.translate.declaration.DefineInvocation.createDefineInvocation;
034    
035    public final class NamespaceDeclarationTranslator extends AbstractTranslator {
036        private final Iterable<JetFile> files;
037        private final Map<NamespaceDescriptor,NamespaceTranslator> descriptorToTranslator =
038                new LinkedHashMap<NamespaceDescriptor, NamespaceTranslator>();
039    
040        public static List<JsStatement> translateFiles(@NotNull Collection<JetFile> files, @NotNull TranslationContext context) {
041            return new NamespaceDeclarationTranslator(files, context).translate();
042        }
043    
044        private NamespaceDeclarationTranslator(@NotNull Iterable<JetFile> files, @NotNull TranslationContext context) {
045            super(context);
046    
047            this.files = files;
048        }
049    
050        @NotNull
051        private List<JsStatement> translate() {
052            // predictable order
053            Map<NamespaceDescriptor, DefineInvocation> descriptorToDefineInvocation = new THashMap<NamespaceDescriptor, DefineInvocation>();
054            NamespaceDescriptor rootNamespaceDescriptor = null;
055    
056            for (JetFile file : files) {
057                NamespaceDescriptor descriptor = context().bindingContext().get(BindingContext.FILE_TO_NAMESPACE, file);
058                assert descriptor != null;
059                NamespaceTranslator translator = descriptorToTranslator.get(descriptor);
060                if (translator == null) {
061                    if (rootNamespaceDescriptor == null) {
062                        rootNamespaceDescriptor = getRootPackageDescriptor(descriptorToDefineInvocation, descriptor);
063                    }
064                    translator = new NamespaceTranslator(descriptor, descriptorToDefineInvocation, context());
065                    descriptorToTranslator.put(descriptor, translator);
066                }
067    
068                translator.translate(file);
069            }
070    
071            if (rootNamespaceDescriptor == null) {
072                return Collections.emptyList();
073            }
074    
075            context().classDeclarationTranslator().generateDeclarations();
076            for (NamespaceTranslator translator : descriptorToTranslator.values()) {
077                translator.add(descriptorToDefineInvocation);
078            }
079    
080            JsVars vars = new JsVars(true);
081            vars.addIfHasInitializer(context().classDeclarationTranslator().getDeclaration());
082            vars.addIfHasInitializer(getRootPackageDeclaration(descriptorToDefineInvocation.get(rootNamespaceDescriptor)));
083    
084            return Collections.<JsStatement>singletonList(vars);
085        }
086    
087        @NotNull
088        private NamespaceDescriptor getRootPackageDescriptor(
089                @NotNull Map<NamespaceDescriptor, DefineInvocation> descriptorToDefineInvocation,
090                @NotNull NamespaceDescriptor descriptor
091        ) {
092            NamespaceDescriptor rootNamespace = descriptor;
093            while (DescriptorUtils.isTopLevelDeclaration(rootNamespace)) {
094                rootNamespace = (NamespaceDescriptor) rootNamespace.getContainingDeclaration();
095            }
096    
097            descriptorToDefineInvocation.put(rootNamespace, createDefineInvocation(rootNamespace, null, new JsObjectLiteral(true), context()));
098            return rootNamespace;
099        }
100    
101        private JsVar getRootPackageDeclaration(@NotNull DefineInvocation defineInvocation) {
102            JsExpression rootPackageVar = new JsInvocation(context().namer().rootPackageDefinitionMethodReference(), defineInvocation.asList());
103            return new JsVar(context().scope().declareName(Namer.getRootNamespaceName()), rootPackageVar);
104        }
105    }