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.facade;
018    
019    import com.google.dart.compiler.backend.js.ast.JsProgram;
020    import com.google.dart.compiler.util.TextOutputImpl;
021    import com.intellij.openapi.project.Project;
022    import com.intellij.openapi.util.text.StringUtil;
023    import com.intellij.openapi.vfs.VfsUtilCore;
024    import com.intellij.openapi.vfs.VirtualFile;
025    import com.intellij.util.Function;
026    import com.intellij.util.SmartList;
027    import com.intellij.util.containers.ContainerUtil;
028    import org.jetbrains.annotations.NotNull;
029    import org.jetbrains.annotations.Nullable;
030    import org.jetbrains.jet.OutputFileCollection;
031    import org.jetbrains.jet.SimpleOutputFile;
032    import org.jetbrains.jet.SimpleOutputFileCollection;
033    import org.jetbrains.jet.lang.psi.JetFile;
034    import org.jetbrains.jet.lang.resolve.BindingContext;
035    import org.jetbrains.jet.utils.fileUtils.FileUtilsPackage;
036    import org.jetbrains.js.compiler.JsSourceGenerationVisitor;
037    import org.jetbrains.js.compiler.sourcemap.SourceMap3Builder;
038    import org.jetbrains.js.compiler.sourcemap.SourceMapBuilder;
039    import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS;
040    import org.jetbrains.k2js.config.Config;
041    import org.jetbrains.k2js.facade.exceptions.TranslationException;
042    import org.jetbrains.k2js.translate.general.Translation;
043    import org.jetbrains.k2js.utils.JetFileUtils;
044    
045    import java.io.File;
046    import java.io.IOException;
047    import java.util.Collections;
048    import java.util.List;
049    
050    import static org.jetbrains.k2js.facade.FacadeUtils.parseString;
051    
052    /**
053     * An entry point of translator.
054     */
055    public final class K2JSTranslator {
056    
057        public static final String FLUSH_SYSTEM_OUT = "Kotlin.System.flush();\n";
058        public static final String GET_SYSTEM_OUT = "Kotlin.System.output();\n";
059    
060        public static OutputFileCollection translateWithMainCallParameters(
061                @NotNull MainCallParameters mainCall,
062                @NotNull List<JetFile> files,
063                @NotNull File outputFile,
064                @Nullable File outputPrefixFile,
065                @Nullable File outputPostfixFile,
066                @NotNull Config config
067        ) throws TranslationException, IOException {
068            K2JSTranslator translator = new K2JSTranslator(config);
069            TextOutputImpl output = new TextOutputImpl();
070            SourceMapBuilder sourceMapBuilder = config.isSourcemap() ? new SourceMap3Builder(outputFile, output, new SourceMapBuilderConsumer()) : null;
071            String programCode = translator.generateProgramCode(files, mainCall, output, sourceMapBuilder);
072    
073            String prefix = FileUtilsPackage.readTextOrEmpty(outputPrefixFile);
074            String postfix = FileUtilsPackage.readTextOrEmpty(outputPostfixFile);
075    
076            StringBuilder outBuilder = new StringBuilder(programCode.length() + prefix.length() + postfix.length());
077            outBuilder.append(prefix).append(programCode).append(postfix);
078    
079            List<File> sourceFiles = ContainerUtil.map(files, new Function<JetFile, File>() {
080                @Override
081                public File fun(JetFile file) {
082                    VirtualFile virtualFile = file.getOriginalFile().getVirtualFile();
083                    if (virtualFile == null) return new File(file.getName());
084                    return VfsUtilCore.virtualToIoFile(virtualFile);
085                }
086            });
087    
088            SimpleOutputFile jsFile = new SimpleOutputFile(sourceFiles, outputFile.getName(), outBuilder.toString());
089            List<SimpleOutputFile> outputFiles = new SmartList<SimpleOutputFile>(jsFile);
090    
091            if (sourceMapBuilder != null) {
092                sourceMapBuilder.skipLinesAtBeginning(StringUtil.getLineBreakCount(prefix));
093                SimpleOutputFile sourcemapFile = new SimpleOutputFile(sourceFiles, sourceMapBuilder.getOutFile().getName(), sourceMapBuilder.build());
094                outputFiles.add(sourcemapFile);
095            }
096    
097            return new SimpleOutputFileCollection(outputFiles);
098        }
099    
100        @NotNull
101        private final Config config;
102    
103        public K2JSTranslator(@NotNull Config config) {
104            this.config = config;
105        }
106    
107        //NOTE: web demo related method
108        @SuppressWarnings("UnusedDeclaration")
109        @NotNull
110        public String translateStringWithCallToMain(@NotNull String programText, @NotNull String argumentsString) throws TranslationException {
111            JetFile file = JetFileUtils.createJetFile("test", programText, getProject());
112            String programCode = generateProgramCode(file, MainCallParameters.mainWithArguments(parseString(argumentsString))) + "\n";
113            return FLUSH_SYSTEM_OUT + programCode + GET_SYSTEM_OUT;
114        }
115    
116        @NotNull
117        public String generateProgramCode(@NotNull JetFile file, @NotNull MainCallParameters mainCallParameters) throws TranslationException {
118            return generateProgramCode(Collections.singletonList(file), mainCallParameters);
119        }
120    
121        @NotNull
122        public String generateProgramCode(@NotNull List<JetFile> files, @NotNull MainCallParameters mainCallParameters)
123                throws TranslationException {
124            return generateProgramCode(files, mainCallParameters, new TextOutputImpl(), null);
125        }
126    
127        @NotNull
128        public String generateProgramCode(
129                @NotNull List<JetFile> files,
130                @NotNull MainCallParameters mainCallParameters,
131                @NotNull TextOutputImpl output,
132                @Nullable SourceMapBuilder sourceMapBuilder
133        ) throws TranslationException {
134            JsProgram program = generateProgram(files, mainCallParameters);
135            JsSourceGenerationVisitor sourceGenerator = new JsSourceGenerationVisitor(output, sourceMapBuilder);
136            program.accept(sourceGenerator);
137            return output.toString();
138        }
139    
140        @NotNull
141        public JsProgram generateProgram(@NotNull List<JetFile> filesToTranslate,
142                @NotNull MainCallParameters mainCallParameters)
143                throws TranslationException {
144            BindingContext bindingContext = AnalyzerFacadeForJS.analyzeFilesAndCheckErrors(filesToTranslate, config);
145            return Translation.generateAst(bindingContext, filesToTranslate, mainCallParameters, config);
146        }
147    
148        @NotNull
149        private Project getProject() {
150            return config.getProject();
151        }
152    }