private WidgetChain walk(PageCompilingContext pc, List<Node> nodes) { WidgetChain chain = Chains.proceeding(); for (Node n : nodes) chain.addWidget(widgetize(pc, n, walk(pc, n))); return chain; }
/** * This method converts an XML element into a specific kind of widget. Special cases are the XML * widget, Header, @Require widget. Otherwise a standard widget is created. */ @SuppressWarnings({"JavaDoc"}) @NotNull private <N extends Node> Renderable widgetize( PageCompilingContext pc, N node, WidgetChain childsChildren) { if (node instanceof XmlDeclaration) { try { XmlDeclaration decl = (XmlDeclaration) node; return registry.xmlDirectiveWidget(decl.getWholeDeclaration(), pc.lexicalScopes.peek()); } catch (ExpressionCompileException e) { pc.errors.add(CompileError.in(node.outerHtml()).near(line(node)).causedBy(e)); } } // Header widget is a special case, where we match by the name of the tag =( if ("head".equals(node.nodeName())) { try { return registry.headWidget( childsChildren, parseAttribs(node.attributes()), pc.lexicalScopes.peek()); } catch (ExpressionCompileException e) { pc.errors.add(CompileError.in(node.outerHtml()).near(line(node)).causedBy(e)); } } String annotation = node.attr(ANNOTATION); // if there is no annotation, treat as a raw xml-widget (i.e. tag) if ((null == annotation) || 0 == annotation.trim().length()) try { checkUriConsistency(pc, node); checkFormFields(pc, node); return registry.xmlWidget( childsChildren, node.nodeName(), parseAttribs(node.attributes()), pc.lexicalScopes.peek()); } catch (ExpressionCompileException e) { pc.errors.add(CompileError.in(node.outerHtml()).near(line(node)).causedBy(e)); return Chains.terminal(); } // Special case: is this annotated with @Require // if so, tags in head need to be promoted to head of enclosing page. if (REQUIRE_WIDGET.equalsIgnoreCase(annotation.trim())) try { return registry.requireWidget( registry.xmlWidget( childsChildren, node.nodeName(), parseAttribs(node.attributes()), pc.lexicalScopes.peek())); } catch (ExpressionCompileException e) { pc.errors.add(CompileError.in(node.outerHtml()).near(line(node)).causedBy(e)); return Chains.terminal(); } // If this is NOT a self-rendering widget, give it a child. // final String widgetName = node.attr(ANNOTATION_KEY).trim().toLowerCase()); final String widgetName = node.attr(ANNOTATION_KEY).toLowerCase(); if (!registry.isSelfRendering(widgetName)) try { childsChildren = Chains.singleton( registry.xmlWidget( childsChildren, node.nodeName(), parseAttribs(node.attributes()), pc.lexicalScopes.peek())); } catch (ExpressionCompileException e) { pc.errors.add(CompileError.in(node.outerHtml()).near(line(node)).causedBy(e)); } // Recursively build widget from [Key, expression, child widgets]. try { return registry.newWidget( widgetName, node.attr(ANNOTATION_CONTENT), childsChildren, pc.lexicalScopes.peek()); } catch (ExpressionCompileException e) { pc.errors.add(CompileError.in(node.outerHtml()).near(line(node)).causedBy(e)); // This should never be used. return Chains.terminal(); } }
/** 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; }