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.k2js.translate.declaration; 018 019import com.google.dart.compiler.backend.js.ast.*; 020import gnu.trove.THashMap; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; 023import org.jetbrains.jet.lang.psi.JetFile; 024import org.jetbrains.jet.lang.resolve.BindingContext; 025import org.jetbrains.jet.lang.resolve.DescriptorUtils; 026import org.jetbrains.k2js.translate.context.Namer; 027import org.jetbrains.k2js.translate.context.TranslationContext; 028import org.jetbrains.k2js.translate.general.AbstractTranslator; 029import org.jetbrains.k2js.translate.utils.JsAstUtils; 030 031import java.util.*; 032 033import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar; 034 035public 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, List<JsExpression>> descriptorToDefineInvocation = new THashMap<NamespaceDescriptor, List<JsExpression>>(); 054 JsObjectLiteral rootNamespaceDefinition = null; 055 056 ClassDeclarationTranslator classDeclarationTranslator = new ClassDeclarationTranslator(context()); 057 for (JetFile file : files) { 058 NamespaceDescriptor descriptor = context().bindingContext().get(BindingContext.FILE_TO_NAMESPACE, file); 059 assert descriptor != null; 060 NamespaceTranslator translator = descriptorToTranslator.get(descriptor); 061 if (translator == null) { 062 if (rootNamespaceDefinition == null) { 063 rootNamespaceDefinition = getRootPackage(descriptorToDefineInvocation, descriptor); 064 } 065 translator = new NamespaceTranslator(descriptor, classDeclarationTranslator, context()); 066 descriptorToTranslator.put(descriptor, translator); 067 } 068 069 translator.translate(file); 070 } 071 072 if (rootNamespaceDefinition == null) { 073 return Collections.emptyList(); 074 } 075 076 JsVars vars = new JsVars(true); 077 List<JsStatement> result; 078 if (context().isEcma5()) { 079 result = Collections.<JsStatement>singletonList(vars); 080 } 081 else { 082 result = new ArrayList<JsStatement>(); 083 result.add(vars); 084 } 085 086 classDeclarationTranslator.generateDeclarations(); 087 for (NamespaceTranslator translator : descriptorToTranslator.values()) { 088 translator.add(descriptorToDefineInvocation, result); 089 } 090 091 vars.addIfHasInitializer(context().literalFunctionTranslator().getDeclaration()); 092 vars.addIfHasInitializer(classDeclarationTranslator.getDeclaration()); 093 vars.addIfHasInitializer(getDeclaration(rootNamespaceDefinition)); 094 return result; 095 } 096 097 private JsObjectLiteral getRootPackage(Map<NamespaceDescriptor, List<JsExpression>> descriptorToDefineInvocation, 098 NamespaceDescriptor descriptor) { 099 NamespaceDescriptor rootNamespace = descriptor; 100 while (DescriptorUtils.isTopLevelDeclaration(rootNamespace)) { 101 rootNamespace = (NamespaceDescriptor) rootNamespace.getContainingDeclaration(); 102 } 103 104 List<JsExpression> args; 105 JsObjectLiteral rootNamespaceDefinition = new JsObjectLiteral(true); 106 if (context().isEcma5()) { 107 args = Arrays.<JsExpression>asList(JsLiteral.NULL, rootNamespaceDefinition); 108 } 109 else { 110 args = Collections.<JsExpression>singletonList(rootNamespaceDefinition); 111 } 112 113 descriptorToDefineInvocation.put(rootNamespace, args); 114 return rootNamespaceDefinition; 115 } 116 117 private JsVar getDeclaration(@NotNull JsObjectLiteral rootNamespaceDefinition) { 118 JsExpression packageMapValue; 119 if (context().isEcma5()) { 120 packageMapValue = new JsInvocation(JsAstUtils.CREATE_OBJECT, JsLiteral.NULL, rootNamespaceDefinition); 121 } 122 else { 123 packageMapValue = rootNamespaceDefinition; 124 } 125 return new JsVar(context().scope().declareName(Namer.getRootNamespaceName()), packageMapValue); 126 } 127}