// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. class CGBlock { int _blockType; // Code type of this block int _indent; // Number of spaces to prefix for each statement bool _inEach; // This block or any currently active blocks is a // #each. If so then any element marked with a // var attribute is repeated therefore the var // is a List type instead of an Element type. String _localName; // optional local name for #each or #with List _stmts; int localIndex; // Local variable index (e.g., e0, e1, etc.) // Block Types: static const int CONSTRUCTOR = 0; static const int EACH = 1; static const int WITH = 2; CGBlock([this._indent = 4, this._blockType = CGBlock.CONSTRUCTOR, this._inEach = false, this._localName = null]) : _stmts = new List(), localIndex = 0 { assert(_blockType >= CGBlock.CONSTRUCTOR && _blockType <= CGBlock.WITH); } bool get anyStatements => !_stmts.isEmpty; bool get isConstructor => _blockType == CGBlock.CONSTRUCTOR; bool get isEach => _blockType == CGBlock.EACH; bool get isWith => _blockType == CGBlock.WITH; bool get hasLocalName => _localName != null; String get localName => _localName; CGStatement push(var elem, var parentName, [bool exact = false]) { var varName; if (elem is TemplateElement && elem.hasVar) { varName = elem.varName; } else { varName = localIndex++; } CGStatement stmt = new CGStatement(elem, _indent, parentName, varName, exact, _inEach); _stmts.add(stmt); return stmt; } void pop() { _stmts.removeLast(); } void add(String value) { if (_stmts.last != null) { _stmts.last.add(value); } } CGStatement get last => _stmts.length > 0 ? _stmts.last : null; /** * Returns mixed list of elements marked with the var attribute. If the * element is inside of a #each the name exposed is: * * List varName; * * otherwise it's: * * var varName; * * TODO(terry): For scalars var varName should be Element tag type e.g., * * DivElement varName; */ String get globalDeclarations { StringBuffer buff = new StringBuffer(); for (final CGStatement stmt in _stmts) { buff.write(stmt.globalDeclaration()); } return buff.toString(); } /** * List of statement constructors for each var inside a #each. * * ${#each products} *
...
* ${/each} * * returns: * * myVar = []; */ String get globalInitializers { StringBuffer buff = new StringBuffer(); for (final CGStatement stmt in _stmts) { buff.write(stmt.globalInitializers()); } return buff.toString(); } String get codeBody { StringBuffer buff = new StringBuffer(); for (final CGStatement stmt in _stmts) { buff.write(stmt.emitDartStatement()); } return buff.toString(); } } class CGStatement { bool _exact; // If True not HTML construct instead exact stmt bool _repeating; // Stmt in a #each this block or nested block. StringBuffer _buff; var _elem; int _indent; var parentName; String varName; bool _globalVariable; bool _closed; CGStatement(this._elem, this._indent, this.parentName, var varNameOrIndex, [this._exact = false, this._repeating = false]) : _buff = new StringBuffer(), _closed = false { if (varNameOrIndex is String) { // We have the global variable name varName = varNameOrIndex; _globalVariable = true; } else { // local index generate local variable name. varName = "e${varNameOrIndex}"; _globalVariable = false; } } bool get hasGlobalVariable => _globalVariable; String get variableName => varName; String globalDeclaration() { if (hasGlobalVariable) { String spaces = Codegen.spaces(_indent); return (_repeating) ? " List ${varName}; // Repeated elements.\n" : " var ${varName};\n"; } return ""; } String globalInitializers() { if (hasGlobalVariable && _repeating) { return " ${varName} = [];\n"; } return ""; } void add(String value) { _buff.write(value); } bool get isClosed => _closed; void close() { if (_elem is TemplateElement && _elem.scoped) { add(""); } _closed = true; } String emitDartStatement() { StringBuffer statement = new StringBuffer(); String spaces = Codegen.spaces(_indent); if (_exact) { statement.add("${spaces}${_buff.toString()};\n"); } else { String localVar = ""; String tmpRepeat; if (hasGlobalVariable) { if (_repeating) { tmpRepeat = "tmp_${varName}"; localVar = "var "; } } else { localVar = "var "; } /* Emiting the following code fragment where varName is the attribute value for var= varName = new Element.html('HTML GOES HERE'); parent.elements.add(varName); for repeating elements in a #each: var tmp_nnn = new Element.html('HTML GOES HERE'); varName.add(tmp_nnn); parent.elements.add(tmp_nnn); for elements w/o var attribute set: var eNNN = new Element.html('HTML GOES HERE'); parent.elements.add(eNNN); */ if (_elem is TemplateCall) { // Call template NameEntry2 String cls = _elem.toCall; String params = _elem.params; statement.add("\n${spaces}// Call template ${cls}.\n"); statement.add( "${spaces}${localVar}${varName} = new ${cls}${params};\n"); statement.add( "${spaces}${parentName}.elements.add(${varName}.root);\n"); } else { bool isTextNode = _elem is TemplateText; String createType = isTextNode ? "Text" : "Element.html"; if (tmpRepeat == null) { statement.add("${spaces}${localVar}${varName} = new ${createType}('"); } else { statement.add( "${spaces}${localVar}${tmpRepeat} = new ${createType}('"); } statement.add(isTextNode ? _buff.toString().trim() : _buff.toString()); if (tmpRepeat == null) { statement.add( "');\n${spaces}${parentName}.elements.add(${varName});\n"); } else { statement.add( "');\n${spaces}${parentName}.elements.add(${tmpRepeat});\n"); statement.add("${spaces}${varName}.add(${tmpRepeat});\n"); } } } return statement.toString(); } } class Codegen { static const String SPACES = " "; static String spaces(int numSpaces) { return SPACES.substring(0, numSpaces); } // TODO(terry): Before generating Dart class need a validate phase that // checks mangles all class names to be prefix with the // template name to avoid any class name collisions. Also, // investigate possible runtime check mode to insure that only // valid CSS class names are used (in case someone uses strings // and not the generated getters to the CSS class selector. This // mode would be slower would require that every class name set // (maybe jQuery too) is for a particular view (requires walking // the HTML tree looking for a parent template prefix that // matches the CSS prefix. (more thinking needed). static String generate(List