/**
   * Assigns any existing properties to the node.<br>
   * It will call attributeDelegates before passing control to the factory that built the node.
   *
   * @param node the object returned by tne node factory
   * @param attributes the attributes for the node
   */
  protected void handleNodeAttributes(Object node, Map attributes) {
    // first, short circuit
    if (node == null) {
      return;
    }

    for (Closure attrDelegate : getProxyBuilder().getAttributeDelegates()) {
      FactoryBuilderSupport builder = this;
      if (attrDelegate.getOwner() instanceof FactoryBuilderSupport) {
        builder = (FactoryBuilderSupport) attrDelegate.getOwner();
      } else if (attrDelegate.getDelegate() instanceof FactoryBuilderSupport) {
        builder = (FactoryBuilderSupport) attrDelegate.getDelegate();
      }

      attrDelegate.call(new Object[] {builder, node, attributes});
    }

    if (getProxyBuilder()
        .getCurrentFactory()
        .onHandleNodeAttributes(getProxyBuilder().getChildBuilder(), node, attributes)) {
      getProxyBuilder().setNodeAttributes(node, attributes);
    }
  }
  protected Object dispatchNodeCall(Object name, Object args) {
    Object node;
    Closure closure = null;
    List list = InvokerHelper.asList(args);

    final boolean needToPopContext;
    if (getProxyBuilder().getContexts().isEmpty()) {
      // should be called on first build method only
      getProxyBuilder().newContext();
      needToPopContext = true;
    } else {
      needToPopContext = false;
    }

    try {
      Map namedArgs = Collections.EMPTY_MAP;

      // the arguments come in like [named_args?, args..., closure?]
      // so peel off a hashmap from the front, and a closure from the
      // end and presume that is what they meant, since there is
      // no way to distinguish node(a:b,c,d) {..} from
      // node([a:b],[c,d], {..}), i.e. the user can deliberately confuse
      // the builder and there is nothing we can really do to prevent
      // that

      if ((list.size() > 0) && (list.get(0) instanceof LinkedHashMap)) {
        namedArgs = (Map) list.get(0);
        list = list.subList(1, list.size());
      }
      if ((list.size() > 0) && (list.get(list.size() - 1) instanceof Closure)) {
        closure = (Closure) list.get(list.size() - 1);
        list = list.subList(0, list.size() - 1);
      }
      Object arg;
      if (list.size() == 0) {
        arg = null;
      } else if (list.size() == 1) {
        arg = list.get(0);
      } else {
        arg = list;
      }
      node = getProxyBuilder().createNode(name, namedArgs, arg);

      Object current = getProxyBuilder().getCurrent();
      if (current != null) {
        getProxyBuilder().setParent(current, node);
      }

      if (closure != null) {
        Factory parentFactory = getProxyBuilder().getCurrentFactory();
        if (parentFactory.isLeaf()) {
          throw new RuntimeException("'" + name + "' doesn't support nesting.");
        }
        boolean processContent = true;
        if (parentFactory.isHandlesNodeChildren()) {
          processContent = parentFactory.onNodeChildren(this, node, closure);
        }
        if (processContent) {
          // push new node on stack
          String parentName = getProxyBuilder().getCurrentName();
          Map parentContext = getProxyBuilder().getContext();
          getProxyBuilder().newContext();
          try {
            getProxyBuilder().getContext().put(OWNER, closure.getOwner());
            getProxyBuilder().getContext().put(CURRENT_NODE, node);
            getProxyBuilder().getContext().put(PARENT_FACTORY, parentFactory);
            getProxyBuilder().getContext().put(PARENT_NODE, current);
            getProxyBuilder().getContext().put(PARENT_CONTEXT, parentContext);
            getProxyBuilder().getContext().put(PARENT_NAME, parentName);
            getProxyBuilder().getContext().put(PARENT_BUILDER, parentContext.get(CURRENT_BUILDER));
            getProxyBuilder().getContext().put(CURRENT_BUILDER, parentContext.get(CHILD_BUILDER));
            // lets register the builder as the delegate
            getProxyBuilder().setClosureDelegate(closure, node);
            closure.call();
          } finally {
            getProxyBuilder().popContext();
          }
        }
      }

      getProxyBuilder().nodeCompleted(current, node);
      node = getProxyBuilder().postNodeCompletion(current, node);
    } finally {
      if (needToPopContext) {
        // pop the first context
        getProxyBuilder().popContext();
      }
    }
    return node;
  }