/**
   * 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();
    }
  }