Cindy

 view release on metacpan or  search on metacpan

lib/Cindy.pm  view on Meta::CPAN


our @EXPORT= qw(get_html_doc get_xml_doc 
                parse_html_string parse_xml_string 
                parse_cis parse_cis_string
                inject dump_xpath_profile);

use XML::LibXML;
use Cindy::Sheet;
use Cindy::Log;
 
sub get_html_doc($)
{
  my ($file)  = @_;
  my $parser = XML::LibXML->new();

  return $parser->parse_html_file($file);
}

sub get_xml_doc($)
{
  my ($file)  = @_;
  my $parser = XML::LibXML->new();

  return $parser->parse_file($file);
}

sub omit_nodes {
  my ($doc, $tag) = @_; 

  my $found = $doc->find( "///$tag" );
  foreach my $node ($found->get_nodelist()) {
    my $parent = $node->parentNode;

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

sub parse_html_string($;$)
{
  my ($string, $ropt)  = @_;
  $ropt ||= {};
  
  my $html_parse_noimplied = $ropt->{html_parse_noimplied}
                             || $ropt->{no_implied};

  my $dont_omit =  !$html_parse_noimplied 
               ||  ($string =~ /<html|<body/i);

  my $parser = XML::LibXML->new();

  my $doc = $parser->parse_html_string($string, $ropt);

  if (!$dont_omit) {
    # Until HTML_PARSE_NOIMPLIED is implemented by 
    # libxml2 (and passed by XML::LibXML) we need
    # to remove html/body tags that have been added to 
    # fragments.
    omit_nodes($doc, 'html');
    omit_nodes($doc, 'body');
  }
  return $doc;
}

sub parse_xml_string($)
{
  my $parser = XML::LibXML->new();

  return $parser->parse_string($_[0]);
}

sub parse_cis($)
{
  return Cindy::Sheet::parse_cis($_[0]);
}

sub parse_cis_string($)
{
  return Cindy::Sheet::parse_cis_string($_[0]);
}

#
# Get a copied doc. root for modification.
#
sub get_root_copy($)
{
  my ($doc)   = @_;
  my $root  = $doc->documentElement();
  my $rtn = $root->cloneNode( 1 );
  return $rtn;
}

sub dump_xpath_profile()
{
  Cindy::Injection::dump_profile();
}

sub inject($$$)
{
  my ($data, $doc, $descriptions) = @_;
  my $docroot = get_root_copy($doc);
#  my $dataroot = get_root_copy($data);
  my $dataroot = $data->getDocumentElement();
  # Create a root description with action none 
  # to hold the description list 
  my $descroot = Cindy::Injection->new(
      '.', 'none', '.', 'xpath', 
      sublist => $descriptions);
   
  # Connect the copied docroot with the output document.
  # This has to be done before the tree is matched.
  my $out = XML::LibXML::Document->new($doc->getVersion, $doc->getEncoding);
  # Copy doctypes
  # This worked for 2.0001/2.8.0 (wheezy),
  # but does look somewhat clumsy. 
  if ($doc->externalSubset) {
    my $ext = $doc->externalSubset;
    $out->createExternalSubset($ext->getName(),

lib/Cindy.pm  view on Meta::CPAN


  <source>  content   <target> ;
  true()    omit-tag  <target> ;

If no source node matched, the target node will be left unchanged. 

=head3 copy

The source node with all its content replaces the target node 
and all its content. This means that the target tag including any 
content is replaced by the the source tag and its content. 

If no source node matched, the target node will be left unchanged. 

Be aware that this requires the source tag to be valid in the target 
document.

=head3 omit-tag

The source node is used as a condition. If it exists and if its text 
content evaluates to true the target node is replaced by its children.
This means that if the source tag exists and its content is not '' or
0 the target tag is removed while its content remains.

=head3 comment

The source nodes content is moved into a comment node. This comment node
is appended to the children of the target node. This can be useful for 
debugging and enables injection of SSI directives.

=head3 attribute

The syntax has an additional field atname

  <source>  attribute   <target> <atname> ;

that holds the name of the attribute. If the source node exists, its 
content replaces or sets the value of the atname attribute of the 
target node. If the source node does not exist the attribute atname
is removed from the target node.

=head3 condition

The source node is used as a condition. If it exists and if its text 
content evaluates to true nothing is done. Otherwise the target node 
and its children are removed. This means that the target tag is removed 
if the source tage does not exist or contains '', 0 or 0.0 while it is 
left untouched otherwise.

=head3 repeat

The repeat action is the CJS equivalent of a template engines loop. For 
each match of the source path the source node and the target node are 
used as root nodes for a sequence of actions. The syntax is

  <source>  repeat   <target>  [condition] {
    <actions>
  } ;

The optional condition is an xpath expression that is run in the context 
of the root node of a temporary document fragment. The fragment has 
two children, DOC and DATA which hold a subtree from a repeat doc respective data 
match. Only those combinations where the condition evaluates to true are 
used, all others are discarded. 

Note that the repeat condition is an EXPERIMENTAL feature, it may well 
change.

=head2 XPATH FUNCTIONS

A small number of additional XPath functions have been implemented. 

=head3 current()

This returns the context node. It behaves like the identically 
named XSLT function.

=head1 ERROR HANDLING

As a default Cindy dies on errors. Currently there are no warnings. 
Cindy detects log4perl and uses it for trace logging 
with levels DEBUG and INFO. If Cindy is used from Cindy-Apache2 the 
apache log is used instead.

=head1 AUTHOR

Joachim Zobel <jz-2008@heute-morgen.de> 

=head1 SEE ALSO

See Cindy/CJSGrammar.rdc for the RecDescent grammar for content injection sheets.

If you prefer a classic push template engine, that uses an API to fill the template
from within the application see  L<http://search.cpan.org/~tomita/Template-Semantic>.
This also uses xpath or css selectors to move data into unmodified templates.





( run in 0.741 second using v1.01-cache-2.11-cpan-364913b4093 )