# $Id: Action.pm 87 2010-05-24 14:53:45Z jo $
# Cindy::Action - Action (content, replace,...) implementation
#
# Copyright (c) 2008 Joachim Zobel <jz-2008@heute-morgen.de>. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#

#
# The funtions  in this package manipulate the 
# given node using the given data.  
# 

package Cindy::Action;

use strict;
use warnings;

use XML::LibXML;

#
# Helpers/Wrappers
#

#
# Evaluate data node as boolean
#
sub is_true($)
{
  my ($data) = @_;

  return 0 if (!$data); 
  return $data->textContent if ($data->can('textContent'));
  return $data->value if ($data->can('value')); 
}

#
# Evaluate data node text
#
sub text($)
{
  my ($data) = @_;

  return $data->textContent if ($data->can('textContent'));
  return $data->value if ($data->can('value')); 
  # This may be text by some perl magic
  return $data;
}


#
# Get list of child nodes
#
sub copy_children($$)
{
  my ($data, $node) =@_;

  if (defined($data)) {
    if ($data->isa('XML::LibXML::Attr') ) {
      # Replace an attribute node with a text node
      return ($node->ownerDocument->createTextNode(
                      $data->textContent));
    } else {
      return map {$_->cloneNode(1);} $data->childNodes() ;
    }
  } else {
    return ();
  }
}

#
# The node only survives if data exists and its content
# evalutes to true. 
#
sub condition($$) 
{
  my ($node, $data) = @_;  

	#	remove node 
  if  (!is_true($data)) {
    my $parent = $node->parentNode;
    $parent->removeChild( $node );
  }

  return 0;
}

#
# The node gets a copy of the data children to replace
# the existing ones. This copies the text held by data
# as well as possible element nodes (e.g. <b>). If data
# is not a node its treated as text.
#
sub content($$) 
{
  my ($node, $data) = @_;  

  # An a node without children will remove all
  # target children. If however no node matched,
  # the target node will be left unchanged. 
  if (defined($data)) {
    $node->removeChildNodes();	
    if ( $data->can('childNodes')
      || $data->isa('XML::LibXML::Attr')) {
      foreach my $child (copy_children($data, $node)) {
        $node->appendChild($child);
      }
    } else {
      # No child nodes, not an attr. so its hopefully text
      $node->appendChild(
          $node->ownerDocument->createTextNode(text($data)));
    }
  }  

  return 0;
}

#
# Appends a comment as a child of the node. Data is
# interpreted as the text for the comment.
#
sub comment($$) 
{
  my ($node, $data) = @_;  

  if (defined($data)) {
    $node->appendChild(
      $node->ownerDocument->createComment(text($data)));
  }  

  return 0;
}

#
# The node is removed and the parent node gets 
# the data children instead. 
#
sub replace($$) 
{
  my ($node, $data) = @_;  

  my $parent = $node->parentNode;
  
  foreach my $child (copy_children($data, $node)) {
    $parent->insertBefore($child, $node);
  }

  # An a node without children will remove all
  # target children. If however no node matched,
  # the target node will be left unchanged. 
  if (defined($data)) {
    $parent->removeChild($node);
  }

  return 0;
}

#
# If data and its text content evaluate to true the node is 
# removed and the parent node gets the children instead.
#
sub omit_tag($$) 
{
  my ($node, $data) = @_;  

  if (is_true($data)) {
    my $parent = $node->parentNode;

    foreach my $child ($node->childNodes()) {
      $parent->insertBefore($child->cloneNode(1), $node);
    }
  
    $parent->removeChild($node);
  }
  return 0;
}

#
# Sets or removes an attribute from an element node.
# If data is undefined the element is removed, otherwise
# the data text content is used as the attribute value. 
# Note the additional parameter name which passes the
# attribute name. 
#
sub attribute($$$) 
{
  my ($node, $data, $name) = @_;  

  if ($data) {
    $node->setAttribute($name, text($data));    
  } else {
    $node->removeAttribute($name);
  }

  return 0;
}

#
# Copies the doc node and inserts the copy before
# the original. 
# The actual repetion is done by the data xpath.
#
# return The cloned node
#
sub repeat($$) 
{
  my ($node, $data) = @_;  

  if (defined($data)) {
    my $parent = $node->parentNode;
    # Note that we do a deep copy here.
    my $new = $node->cloneNode(1);
  
    $parent->insertBefore($new, $node);
    return $new;
  } else {
    return undef;
  }
}

#
# Special actions for internal use
#

#
# Removes the given node. Data is ignored. 
#
sub remove($$) 
{
  my ($node, $data) = @_;  
    
  my $parent = $node->parentNode;
  $parent->removeChild($node);

  return 0;
}

#
# Does nothing. Used for subsheet holders.
#
sub none($$) 
{
}


1;

