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.expression; 018 019import com.google.dart.compiler.backend.js.ast.JsExpression; 020import com.google.dart.compiler.backend.js.ast.JsInvocation; 021import com.google.dart.compiler.backend.js.ast.JsNameRef; 022import com.google.dart.compiler.backend.js.ast.JsNumberLiteral; 023import org.jetbrains.annotations.NotNull; 024import org.jetbrains.annotations.Nullable; 025import org.jetbrains.jet.lang.psi.*; 026import org.jetbrains.jet.lang.resolve.name.Name; 027import org.jetbrains.k2js.translate.context.TranslationContext; 028import org.jetbrains.k2js.translate.general.AbstractTranslator; 029import org.jetbrains.k2js.translate.general.Translation; 030 031import static org.jetbrains.k2js.translate.utils.JsAstUtils.sum; 032import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getNameIfStandardType; 033 034 035public final class StringTemplateTranslator extends AbstractTranslator { 036 037 @NotNull 038 public static JsExpression translate(@NotNull JetStringTemplateExpression expression, 039 @NotNull TranslationContext context) { 040 return (new StringTemplateTranslator(expression, context).translate()); 041 } 042 043 @NotNull 044 private final JetStringTemplateExpression expression; 045 046 private StringTemplateTranslator(@NotNull JetStringTemplateExpression expression, 047 @NotNull TranslationContext context) { 048 super(context); 049 this.expression = expression; 050 } 051 052 @NotNull 053 private JsExpression translate() { 054 assert expression.getEntries().length != 0 : "String template must have one or more entries."; 055 EntryVisitor entryVisitor = new EntryVisitor(); 056 for (JetStringTemplateEntry entry : expression.getEntries()) { 057 entry.accept(entryVisitor); 058 } 059 return entryVisitor.getResultingExpression(); 060 } 061 062 private final class EntryVisitor extends JetVisitorVoid { 063 064 @Nullable 065 private JsExpression resultingExpression = null; 066 067 void append(@NotNull JsExpression expression) { 068 if (resultingExpression == null) { 069 resultingExpression = expression; 070 } 071 else { 072 resultingExpression = sum(resultingExpression, expression); 073 } 074 } 075 076 @Override 077 public void visitStringTemplateEntryWithExpression(@NotNull JetStringTemplateEntryWithExpression entry) { 078 JetExpression entryExpression = entry.getExpression(); 079 assert entryExpression != null : 080 "JetStringTemplateEntryWithExpression must have not null entry expression."; 081 JsExpression translatedExpression = Translation.translateAsExpression(entryExpression, context()); 082 if (translatedExpression instanceof JsNumberLiteral) { 083 append(context().program().getStringLiteral(translatedExpression.toString())); 084 return; 085 } 086 if (mustCallToString(entryExpression)) { 087 append(new JsInvocation(new JsNameRef("toString", translatedExpression))); 088 } else { 089 append(translatedExpression); 090 } 091 } 092 093 private boolean mustCallToString(@NotNull JetExpression entryExpression) { 094 Name typeName = getNameIfStandardType(entryExpression, context()); 095 if (typeName == null) { 096 return true; 097 } 098 //TODO: this is a hacky optimization, should use some generic approach 099 if (typeName.asString().equals("String")) { 100 return false; 101 } 102 if (typeName.asString().equals("Int") && resultingExpression != null) { 103 return false; 104 } 105 return true; 106 } 107 108 @Override 109 public void visitLiteralStringTemplateEntry(@NotNull JetLiteralStringTemplateEntry entry) { 110 appendText(entry.getText()); 111 } 112 113 @Override 114 public void visitEscapeStringTemplateEntry(@NotNull JetEscapeStringTemplateEntry entry) { 115 appendText(entry.getUnescapedValue()); 116 } 117 118 private void appendText(@NotNull String text) { 119 append(program().getStringLiteral(text)); 120 } 121 122 @NotNull 123 public JsExpression getResultingExpression() { 124 assert resultingExpression != null; 125 return resultingExpression; 126 } 127 } 128}