/** * Complement of HtmlTemplateCompiler#lexicalClimb(). This method pops off the stack of lexical * scopes when we're done processing a sitebricks widget. */ private void lexicalDescend(PageCompilingContext pc, Element element, boolean shouldPopScope) { // pop form if ("form".equals(element.tagName())) pc.form = null; // pop compiler if the scope ends if (shouldPopScope) { pc.lexicalScopes.pop(); } }
public Renderable compile(Class<?> page, Template template) { PageCompilingContext pc = new PageCompilingContext(); pc.page = page; pc.template = template; pc.lexicalScopes.push(new MvelEvaluatorCompiler(page)); WidgetChain widgetChain; widgetChain = walk(pc, HtmlParser.parse(template.getText())); // TODO - get the errors when !(isValid) if (!pc.errors.isEmpty() || !pc.warnings.isEmpty()) { // If there were any errors we must track them. metrics.logErrorsAndWarnings(page, pc.errors, pc.warnings); // Only explode if there are errors. if (!pc.errors.isEmpty()) throw new TemplateCompileException(page, template.getText(), pc.errors, pc.warnings); } return widgetChain; }
/** Walks the DOM recursively, and converts elements into corresponding sitebricks widgets. */ @NotNull private <N extends Node> WidgetChain walk(PageCompilingContext pc, N node) { WidgetChain widgetChain = Chains.proceeding(); for (Node n : node.childNodes()) { if (n instanceof Element) { final Element child = (Element) n; // push form if this is a form tag if (child.tagName().equals("form")) pc.form = (Element) n; // setup a lexical scope if we're going into a repeat widget (by reading the previous node) final boolean shouldPopScope = lexicalClimb(pc, child); // continue recursing down, perform a post-order, depth-first traversal of the DOM WidgetChain childsChildren; try { childsChildren = walk(pc, child); // process the widget itself into a Renderable with child tree widgetChain.addWidget(widgetize(pc, child, childsChildren)); } finally { lexicalDescend(pc, child, shouldPopScope); } } else if (n instanceof TextNode) { TextNode child = (TextNode) n; Renderable textWidget; // setup a lexical scope if we're going into a repeat widget (by reading the previous node) final boolean shouldPopScope = lexicalClimb(pc, child); // construct the text widget try { textWidget = registry.textWidget(cleanHtml(n), pc.lexicalScopes.peek()); // if there are no annotations, add the text widget to the chain if (!child.hasAttr(ANNOTATION_KEY)) { widgetChain.addWidget(textWidget); } else { // construct a new widget chain for this text node WidgetChain childsChildren = Chains.proceeding().addWidget(textWidget); // make a new widget for the annotation, making the text chain the child String widgetName = child.attr(ANNOTATION_KEY).toLowerCase(); Renderable annotationWidget = registry.newWidget( widgetName, child.attr(ANNOTATION_CONTENT), childsChildren, pc.lexicalScopes.peek()); widgetChain.addWidget(annotationWidget); } } catch (ExpressionCompileException e) { pc.errors.add(CompileError.in(node.outerHtml()).near(line(node)).causedBy(e)); } if (shouldPopScope) pc.lexicalScopes.pop(); } else if ((n instanceof Comment) || (n instanceof DataNode)) { // process as raw text widget try { widgetChain.addWidget(registry.textWidget(cleanHtml(n), pc.lexicalScopes.peek())); } catch (ExpressionCompileException e) { pc.errors.add(CompileError.in(node.outerHtml()).near(line(node)).causedBy(e)); } } else if (n instanceof XmlDeclaration) { try { widgetChain.addWidget( registry.xmlDirectiveWidget( ((XmlDeclaration) n).getWholeDeclaration(), pc.lexicalScopes.peek())); } catch (ExpressionCompileException e) { pc.errors.add(CompileError.in(node.outerHtml()).near(line(node)).causedBy(e)); } } } // return computed chain, or a terminal return widgetChain; }