home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / perl5 / XML / Twig.pm < prev    next >
Encoding:
Text File  |  2010-02-13  |  432.2 KB  |  13,183 lines

  1. use strict;
  2. use warnings; # > perl 5.5
  3.  
  4. # This is created in the caller's space
  5. # I realize (now!) that it's not clean, but it's been there for 10 years...
  6. BEGIN
  7. { sub ::PCDATA { '#PCDATA' }  ## no critic (Subroutines::ProhibitNestedSubs);
  8.   sub ::CDATA  { '#CDATA'  }  ## no critic (Subroutines::ProhibitNestedSubs);
  9. }
  10.  
  11. use UNIVERSAL();
  12.  
  13. ## if a sub returns a scalar, it better not bloody disappear in list context
  14. ## no critic (Subroutines::ProhibitExplicitReturnUndef);
  15.  
  16. ######################################################################
  17. package XML::Twig;
  18. ######################################################################
  19.  
  20. require 5.004;
  21.  
  22. use utf8; # > perl 5.5
  23.  
  24. use vars qw($VERSION @ISA %valid_option);
  25.  
  26. use Carp;
  27. use File::Spec;
  28. use File::Basename;
  29.  
  30. *isa= *UNIVERSAL::isa;
  31.  
  32. # used to store the gi's
  33. my %gi2index;   # gi => index
  34. my @index2gi;   # list of gi's
  35. my $SPECIAL_GI; # first non-special gi;
  36. #my %base_ent;   # base entity character => replacement
  37.  
  38. # flag, set to true if the weaken sub is available
  39. use vars qw( $weakrefs);
  40.  
  41. # flag set to true if the version of expat seems to be 1.95.2, which has annoying bugs
  42. # wrt doctype handling. This is global for performance reasons. 
  43. my $expat_1_95_2=0;
  44.  
  45. # xml name (leading # allowed)
  46. # first line is for perl 5.005, second line for modern perl, that accept character classes
  47. my $REG_NAME       = q{(?:(?:[^\W\d]|[:#])(?:[\w.-]*:)?[\w.-]*)};     # does not work for leading non-ascii letters
  48.    $REG_NAME       = q{(?:(?:[[:alpha:]:#])(?:[\w.-]*:)?[\w.-]*)};    # > perl 5.5
  49.  
  50. # name or wildcard (* or '') (leading # allowed)
  51. my $REG_NAME_W     = q{(?:(?:[^\W\d]|[:#])(?:[\w.-]*:)?[\w.-]*|\*)}; # does not work for leading non-ascii letters
  52.    $REG_NAME_W     = q{(?:(?:[[:alpha:]:#])(?:[\w.-]*:)?[\w.-]*|\*)}; # > perl 5.5
  53.  
  54. # name or wildcard (* or '') (leading # allowed) with optional class
  55. my $REG_NAME_WC    = q{(?(?:(?:[^\W\d]|[:#])(?:[\w.-]*:)?[\w.-]*|\*)(?:\.[\w-]+)?|(?:\.[\w-]+))}; # does not work for leading non-ascii letters
  56.    $REG_NAME_WC    = q{(?:(?:(?:[[:alpha:]:#])(?:[\w.-]*:)?[\w.-]*|\*)(?:\.[\w-]+)?|(?:\.[\w-]+))}; # > perl 5.5
  57.  
  58.  
  59. my $REG_REGEXP     = q{(?:/(?:[^\\/]|\\.)*/[eimsox]*)};               # regexp
  60. my $REG_REGEXP_EXP = q{(?:(?:[^\\/]|\\.)*)};                          # content of a regexp
  61. my $REG_REGEXP_MOD = q{(?:[eimso]*)};                                 # regexp modifiers
  62. my $REG_MATCH      = q{[!=]~};                                        # match (or not)
  63. my $REG_STRING     = q{(?:"(?:[^\\"]|\\.)*"|'(?:[^\\']|\\.)*')};      # string (simple or double quoted)
  64. my $REG_NUMBER     = q{(?:\d+(?:\.\d*)?|\.\d+)};                      # number
  65. my $REG_VALUE      = qq{(?:$REG_STRING|$REG_NUMBER)};                 # value
  66. my $REG_OP         = q{==|!=|>|<|>=|<=|eq|ne|lt|gt|le|ge|=};          # op
  67. my $REG_FUNCTION   = q{(?:string|text)\(\s*\)};
  68. my $REG_STRING_ARG = qq{(?:string|text)\\(\\s*$REG_NAME_W\\s*\\)};
  69. my $REG_COMP       = q{(?:>=|<=|!=|<|>|=)};
  70.  
  71.  
  72. # used in the handler trigger code
  73. my $REG_NAKED_PREDICATE= qq{((?:"[^"]*"|'[^']*'|$REG_STRING_ARG|$REG_FUNCTION|\@$REG_NAME_W|$REG_MATCH\\s*$REG_REGEXP|[\\s\\d><=!()+.-]|(?i:and)|(?i:or))*)};
  74. my $REG_PREDICATE= qq{\\[$REG_NAKED_PREDICATE\\]};
  75.  
  76. # not all axis, only supported ones (in get_xpath)
  77. my @supported_axis= ( 'ancestor', 'ancestor-or-self', 'child', 'descendant', 'descendant-or-self', 
  78.                       'following', 'following-sibling', 'parent', 'preceding', 'preceding-sibling', 'self'
  79.                     );
  80. my $REG_AXIS       = "(?:" . join( '|', @supported_axis) .")";
  81.  
  82. # only used in the "xpath"engine (for get_xpath/findnodes) for now
  83. my $REG_PREDICATE_ALT  = qr{\[(?:(?:string\(\s*\)|\@$REG_NAME)\s*$REG_MATCH\s*$REG_REGEXP\s*|[^\]]*)\]};
  84.  
  85. # used to convert XPath tests on strings to the perl equivalent 
  86. my %PERL_ALPHA_TEST= ( '=' => ' eq ', '!=' => ' ne ', '>' => ' gt ', '>=' => ' ge ', '<' => ' lt ', '<=' => ' le ');
  87.  
  88. my $parser_version;
  89. my( $FB_HTMLCREF, $FB_XMLCREF);
  90.  
  91. my $NO_WARNINGS= $] >= 5.006 ? 'no warnings' : 'local $^W=0';
  92.  
  93. # constants
  94. my( $PCDATA, $CDATA, $PI, $COMMENT, $ENT, $ELT, $TEXT, $ASIS, $EMPTY, $BUFSIZE);
  95.  
  96. BEGIN
  97. $VERSION = '3.34';
  98.  
  99. use XML::Parser;
  100. my $needVersion = '2.23';
  101. $parser_version= $XML::Parser::VERSION;
  102. croak "need at least XML::Parser version $needVersion" unless $parser_version >= $needVersion;
  103.  
  104. if( $] >= 5.008) 
  105.   { eval "use Encode qw( :all)";
  106.     $FB_XMLCREF  = 0x0400; # Encode::FB_XMLCREF;
  107.     $FB_HTMLCREF = 0x0200; # Encode::FB_HTMLCREF;
  108.   }
  109.  
  110. # test whether we can use weak references
  111. # set local empty signal handler to trap error messages
  112. { local $SIG{__DIE__};
  113.   if( eval( 'require Scalar::Util') && defined( \&Scalar::Util::weaken)) 
  114.     { import Scalar::Util( 'weaken'); $weakrefs= 1; }
  115.   elsif( eval( 'require WeakRef')) 
  116.     { import WeakRef; $weakrefs= 1;                 }
  117.   else  
  118.     { $weakrefs= 0;                                 } 
  119. }
  120.  
  121. import XML::Twig::Elt;
  122. import XML::Twig::Entity;
  123. import XML::Twig::Entity_list;
  124.  
  125. # used to store the gi's
  126. # should be set for each twig really, at least when there are several
  127. # the init ensures that special gi's are always the same
  128.  
  129. # constants: element types
  130. $PCDATA  = '#PCDATA';
  131. $CDATA   = '#CDATA';
  132. $PI      = '#PI';
  133. $COMMENT = '#COMMENT';
  134. $ENT     = '#ENT';
  135.  
  136. # element classes
  137. $ELT     = '#ELT';
  138. $TEXT    = '#TEXT';
  139.  
  140. # element properties
  141. $ASIS    = '#ASIS';
  142. $EMPTY   = '#EMPTY';
  143.  
  144. # used in parseurl to set the buffer size to the same size as in XML::Parser::Expat
  145. $BUFSIZE = 32768;
  146.  
  147.  
  148. # gi => index
  149. %XML::Twig::gi2index=( '', 0, $PCDATA => 1, $CDATA => 2, $PI => 3, $COMMENT => 4, $ENT => 5); 
  150. # list of gi's
  151. @XML::Twig::index2gi=( '', $PCDATA, $CDATA, $PI, $COMMENT, $ENT);
  152.  
  153. # gi's under this value are special 
  154. $XML::Twig::SPECIAL_GI= @XML::Twig::index2gi;
  155.  
  156. %XML::Twig::base_ent= ( '>' => '>', '<' => '<', '&' => '&', "'" => ''', '"' => '"',);
  157.  
  158. # now set some aliases
  159. *find_nodes           = *get_xpath;               # same as XML::XPath
  160. *findnodes            = *get_xpath;               # same as XML::LibXML
  161. *getElementsByTagName = *descendants;
  162. *descendants_or_self  = *descendants;             # valid in XML::Twig, not in XML::Twig::Elt
  163. *find_by_tag_name     = *descendants;
  164. *getElementById       = *elt_id;
  165. *getEltById           = *elt_id;
  166. *toString             = *sprint;
  167. }
  168.  
  169. @ISA = qw(XML::Parser);
  170.  
  171. # fake gi's used in twig_handlers and start_tag_handlers
  172. my $ALL    = '_all_';     # the associated function is always called
  173. my $DEFAULT= '_default_'; # the function is called if no other handler has been
  174.  
  175. # some defaults
  176. my $COMMENTS_DEFAULT= 'keep';
  177. my $PI_DEFAULT      = 'keep';
  178.  
  179.  
  180. # handlers used in regular mode
  181. my %twig_handlers=( Start      => \&_twig_start, 
  182.                     End        => \&_twig_end, 
  183.                     Char       => \&_twig_char, 
  184.                     Entity     => \&_twig_entity, 
  185.                     XMLDecl    => \&_twig_xmldecl, 
  186.                     Doctype    => \&_twig_doctype, 
  187.                     Element    => \&_twig_element, 
  188.                     Attlist    => \&_twig_attlist, 
  189.                     CdataStart => \&_twig_cdatastart, 
  190.                     CdataEnd   => \&_twig_cdataend, 
  191.                     Proc       => \&_twig_pi,
  192.                     Comment    => \&_twig_comment,
  193.                     Default    => \&_twig_default,
  194.                     ExternEnt  => \&_twig_extern_ent,
  195.       );
  196.  
  197. # handlers used when twig_roots is used and we are outside of the roots
  198. my %twig_handlers_roots=
  199.   ( Start      => \&_twig_start_check_roots, 
  200.     End        => \&_twig_end_check_roots, 
  201.     Doctype    => \&_twig_doctype, 
  202.     Char       => undef, Entity     => undef, XMLDecl    => \&_twig_xmldecl, 
  203.     Element    => undef, Attlist    => undef, CdataStart => undef, 
  204.     CdataEnd   => undef, Proc       => undef, Comment    => undef, 
  205.     Proc       => \&_twig_pi_check_roots,
  206.     Default    =>  sub {}, # hack needed for XML::Parser 2.27
  207.     ExternEnt  => \&_twig_extern_ent,
  208.   );
  209.  
  210. # handlers used when twig_roots and print_outside_roots are used and we are
  211. # outside of the roots
  212. my %twig_handlers_roots_print_2_30=
  213.   ( Start      => \&_twig_start_check_roots, 
  214.     End        => \&_twig_end_check_roots, 
  215.     Char       => \&_twig_print, 
  216.     Entity     => \&_twig_print_entity, 
  217.     ExternEnt  => \&_twig_print_entity,
  218.     DoctypeFin => \&_twig_doctype_fin_print,
  219.     XMLDecl    => sub { _twig_xmldecl( @_); _twig_print( @_); },
  220.     Doctype   =>  \&_twig_print_doctype, # because recognized_string is broken here
  221.     # Element    => \&_twig_print, Attlist    => \&_twig_print, 
  222.     CdataStart => \&_twig_print, CdataEnd   => \&_twig_print, 
  223.     Proc       => \&_twig_pi_check_roots, Comment    => \&_twig_print, 
  224.     Default    => \&_twig_print_check_doctype,
  225.     ExternEnt  => \&_twig_extern_ent,
  226.   );
  227.  
  228. # handlers used when twig_roots, print_outside_roots and keep_encoding are used
  229. # and we are outside of the roots
  230. my %twig_handlers_roots_print_original_2_30=
  231.   ( Start      => \&_twig_start_check_roots, 
  232.     End        => \&_twig_end_check_roots, 
  233.     Char       => \&_twig_print_original, 
  234.     # I have no idea why I should not be using this handler!
  235.     Entity     => \&_twig_print_entity, 
  236.     ExternEnt  => \&_twig_print_entity,
  237.     DoctypeFin => \&_twig_doctype_fin_print,
  238.     XMLDecl    => sub { _twig_xmldecl( @_); _twig_print_original( @_) }, 
  239.     Doctype    => \&_twig_print_original_doctype,  # because original_string is broken here
  240.     Element    => \&_twig_print_original, Attlist   => \&_twig_print_original,
  241.     CdataStart => \&_twig_print_original, CdataEnd  => \&_twig_print_original,
  242.     Proc       => \&_twig_pi_check_roots, Comment   => \&_twig_print_original,
  243.     Default    => \&_twig_print_original_check_doctype, 
  244.   );
  245.  
  246. # handlers used when twig_roots and print_outside_roots are used and we are
  247. # outside of the roots
  248. my %twig_handlers_roots_print_2_27=
  249.   ( Start      => \&_twig_start_check_roots, 
  250.     End        => \&_twig_end_check_roots, 
  251.     Char       => \&_twig_print, 
  252.     # if the Entity handler is set then it prints the entity declaration
  253.     # before the entire internal subset (including the declaration!) is output
  254.     Entity     => sub {},
  255.     XMLDecl    => \&_twig_print, Doctype    => \&_twig_print, 
  256.     CdataStart => \&_twig_print, CdataEnd   => \&_twig_print, 
  257.     Proc       => \&_twig_pi_check_roots, Comment    => \&_twig_print, 
  258.     Default    => \&_twig_print, 
  259.     ExternEnt  => \&_twig_extern_ent,
  260.   );
  261.  
  262. # handlers used when twig_roots, print_outside_roots and keep_encoding are used
  263. # and we are outside of the roots
  264. my %twig_handlers_roots_print_original_2_27=
  265.   ( Start      => \&_twig_start_check_roots, 
  266.     End        => \&_twig_end_check_roots, 
  267.     Char       => \&_twig_print_original, 
  268.     # for some reason original_string is wrong here 
  269.     # this can be a problem if the doctype includes non ascii characters
  270.     XMLDecl    => \&_twig_print, Doctype    => \&_twig_print,
  271.     # if the Entity handler is set then it prints the entity declaration
  272.     # before the entire internal subset (including the declaration!) is output
  273.     Entity     => sub {}, 
  274.     #Element    => undef, Attlist   => undef,
  275.     CdataStart => \&_twig_print_original, CdataEnd  => \&_twig_print_original,
  276.     Proc       => \&_twig_pi_check_roots, Comment   => \&_twig_print_original,
  277.     Default    => \&_twig_print, #  _twig_print_original does not work
  278.     ExternEnt  => \&_twig_extern_ent,
  279.   );
  280.  
  281.  
  282. my %twig_handlers_roots_print= $parser_version > 2.27 
  283.                                ? %twig_handlers_roots_print_2_30 
  284.                                : %twig_handlers_roots_print_2_27; 
  285. my %twig_handlers_roots_print_original= $parser_version > 2.27 
  286.                                ? %twig_handlers_roots_print_original_2_30 
  287.                                : %twig_handlers_roots_print_original_2_27; 
  288.  
  289.  
  290. # handlers used when the finish_print method has been called
  291. my %twig_handlers_finish_print=
  292.   ( Start      => \&_twig_print, 
  293.     End        => \&_twig_print, Char       => \&_twig_print, 
  294.     Entity     => \&_twig_print, XMLDecl    => \&_twig_print, 
  295.     Doctype    => \&_twig_print, Element    => \&_twig_print, 
  296.     Attlist    => \&_twig_print, CdataStart => \&_twig_print, 
  297.     CdataEnd   => \&_twig_print, Proc       => \&_twig_print, 
  298.     Comment    => \&_twig_print, Default    => \&_twig_print, 
  299.     ExternEnt  => \&_twig_extern_ent,
  300.   );
  301.  
  302. # handlers used when the finish_print method has been called and the keep_encoding
  303. # option is used
  304. my %twig_handlers_finish_print_original=
  305.   ( Start      => \&_twig_print_original, End      => \&_twig_print_end_original, 
  306.     Char       => \&_twig_print_original, Entity   => \&_twig_print_original, 
  307.     XMLDecl    => \&_twig_print_original, Doctype  => \&_twig_print_original, 
  308.     Element    => \&_twig_print_original, Attlist  => \&_twig_print_original, 
  309.     CdataStart => \&_twig_print_original, CdataEnd => \&_twig_print_original, 
  310.     Proc       => \&_twig_print_original, Comment  => \&_twig_print_original, 
  311.     Default    => \&_twig_print_original, 
  312.   );
  313.  
  314. # handlers used within ignored elements
  315. my %twig_handlers_ignore=
  316.   ( Start      => \&_twig_ignore_start, 
  317.     End        => \&_twig_ignore_end, 
  318.     Char       => undef, Entity     => undef, XMLDecl    => undef, 
  319.     Doctype    => undef, Element    => undef, Attlist    => undef, 
  320.     CdataStart => undef, CdataEnd   => undef, Proc       => undef, 
  321.     Comment    => undef, Default    => undef,
  322.     ExternEnt  => undef,
  323.   );
  324.  
  325.  
  326. # those handlers are only used if the entities are NOT to be expanded
  327. my %twig_noexpand_handlers= ( ExternEnt => undef, Default => \&_twig_default );
  328.  
  329. my @saved_default_handler;
  330.  
  331. my $ID= 'id';  # default value, set by the Id argument
  332. my $css_sel=0; # set through the css_sel option to allow .class selectors in triggers 
  333.  
  334. # all allowed options
  335. %valid_option=
  336.     ( # XML::Twig options
  337.       TwigHandlers          => 1, Id                    => 1,
  338.       TwigRoots             => 1, TwigPrintOutsideRoots => 1,
  339.       StartTagHandlers      => 1, EndTagHandlers        => 1,
  340.       ForceEndTagHandlersUsage => 1,
  341.       DoNotChainHandlers    => 1,
  342.       IgnoreElts            => 1,
  343.       Index                 => 1,
  344.       CharHandler           => 1, 
  345.       TopDownHandlers       => 1,
  346.       KeepEncoding          => 1, DoNotEscapeAmpInAtts  => 1,
  347.       ParseStartTag         => 1, KeepAttsOrder         => 1,
  348.       LoadDTD               => 1, DTDHandler            => 1,
  349.       DoNotOutputDTD        => 1, NoProlog              => 1,
  350.       ExpandExternalEnts    => 1,
  351.       DiscardSpaces         => 1, KeepSpaces            => 1, 
  352.       DiscardSpacesIn       => 1, KeepSpacesIn          => 1, 
  353.       PrettyPrint           => 1, EmptyTags             => 1, 
  354.       EscapeGt              => 1,
  355.       Quote                 => 1,
  356.       Comments              => 1, Pi                    => 1, 
  357.       OutputFilter          => 1, InputFilter           => 1,
  358.       OutputTextFilter      => 1, 
  359.       OutputEncoding        => 1, 
  360.       RemoveCdata           => 1,
  361.       EltClass              => 1,
  362.       MapXmlns              => 1, KeepOriginalPrefix    => 1,
  363.       SkipMissingEnts       => 1,
  364.       # XML::Parser options
  365.       ErrorContext          => 1, ProtocolEncoding      => 1,
  366.       Namespaces            => 1, NoExpand              => 1,
  367.       Stream_Delimiter      => 1, ParseParamEnt         => 1,
  368.       NoLWP                 => 1, Non_Expat_Options     => 1,
  369.       Xmlns                 => 1, CssSel                => 1,
  370.     );
  371.  
  372. # predefined input and output filters
  373. use vars qw( %filter);
  374. %filter= ( html       => \&html_encode,
  375.            safe       => \&safe_encode,
  376.            safe_hex   => \&safe_encode_hex,
  377.          );
  378.  
  379.  
  380. # trigger types (used to sort them)
  381. my ($LEVEL_TRIGGER, $REGEXP_TRIGGER, $XPATH_TRIGGER)=(1..3);
  382.  
  383. sub new
  384.   { my ($class, %args) = @_;
  385.     my $handlers;
  386.  
  387.     # change all nice_perlish_names into nicePerlishNames
  388.     %args= _normalize_args( %args);
  389.  
  390.     # check options
  391.     unless( $args{MoreOptions})
  392.       { foreach my $arg (keys %args)
  393.         { carp "invalid option $arg" unless $valid_option{$arg}; }
  394.       }
  395.      
  396.     # a twig is really an XML::Parser
  397.     # my $self= XML::Parser->new(%args);
  398.     my $self;
  399.     $self= XML::Parser->new(%args);   
  400.  
  401.     bless $self, $class;
  402.  
  403.     $self->{_twig_context_stack}= [];
  404.  
  405.     # allow tag.class selectors in handler triggers
  406.     $css_sel= $args{CssSel} || 0; 
  407.  
  408.  
  409.     if( exists $args{TwigHandlers})
  410.       { $handlers= $args{TwigHandlers};
  411.         $self->setTwigHandlers( $handlers);
  412.         delete $args{TwigHandlers};
  413.       }
  414.  
  415.     # take care of twig-specific arguments
  416.     if( exists $args{StartTagHandlers})
  417.       { $self->setStartTagHandlers( $args{StartTagHandlers});
  418.         delete $args{StartTagHandlers};
  419.       }
  420.  
  421.     if( exists $args{DoNotChainHandlers})
  422.       { $self->{twig_do_not_chain_handlers}=  $args{DoNotChainHandlers}; }
  423.  
  424.     if( exists $args{IgnoreElts})
  425.       { # change array to hash so you can write ignore_elts => [ qw(foo bar baz)]
  426.         if( isa( $args{IgnoreElts}, 'ARRAY')) { $args{IgnoreElts}= { map { $_ => 1 } @{$args{IgnoreElts}} }; }
  427.         $self->setIgnoreEltsHandlers( $args{IgnoreElts});
  428.         delete $args{IgnoreElts};
  429.       }
  430.  
  431.     if( exists $args{Index})
  432.       { my $index= $args{Index};
  433.         # we really want a hash name => path, we turn an array into a hash if necessary
  434.         if( ref( $index) eq 'ARRAY')
  435.           { my %index= map { $_ => $_ } @$index;
  436.             $index= \%index;
  437.           }
  438.         while( my( $name, $exp)= each %$index)
  439.           { $self->setTwigHandler( $exp, sub { push @{$_[0]->{_twig_index}->{$name}}, $_; 1; }); }
  440.       }
  441.  
  442.     $self->{twig_elt_class}= $args{EltClass} || 'XML::Twig::Elt';
  443.     if( exists( $args{EltClass})) { delete $args{EltClass}; }
  444.  
  445.     if( exists( $args{MapXmlns}))
  446.       { $self->{twig_map_xmlns}=  $args{MapXmlns};
  447.         $self->{Namespaces}=1;
  448.         delete $args{MapXmlns};
  449.       }
  450.  
  451.     if( exists( $args{KeepOriginalPrefix}))
  452.       { $self->{twig_keep_original_prefix}= $args{KeepOriginalPrefix};
  453.         delete $args{KeepOriginalPrefix};
  454.       }
  455.  
  456.     $self->{twig_dtd_handler}= $args{DTDHandler};
  457.     delete $args{DTDHandler};
  458.  
  459.     if( $args{ExpandExternalEnts})
  460.       { $self->set_expand_external_entities( 1);
  461.         $self->{twig_expand_external_ents}= $args{ExpandExternalEnts}; 
  462.         $self->{twig_read_external_dtd}= 1; # implied by ExpandExternalEnts
  463.         if( $args{ExpandExternalEnts} == -1) 
  464.           { $self->{twig_extern_ent_nofail}= 1;
  465.             $self->setHandlers( ExternEnt => \&_twig_extern_ent_nofail);
  466.           }
  467.         delete $args{LoadDTD};
  468.         delete $args{ExpandExternalEnts};
  469.       }
  470.     else
  471.       { $self->set_expand_external_entities( 0); }
  472.  
  473.     if( !$args{NoLWP} && ! _use( 'URI') && ! _use( 'URI::File') && ! _use( 'LWP'))
  474.       { $self->{twig_ext_ent_handler}= \&XML::Parser::initial_ext_ent_handler }
  475.     else
  476.       { $self->{twig_ext_ent_handler}= \&XML::Parser::file_ext_ent_handler }
  477.  
  478.     if( $args{DoNotEscapeAmpInAtts})
  479.       { $self->set_do_not_escape_amp_in_atts( 1); 
  480.         $self->{twig_do_not_escape_amp_in_atts}=1;
  481.       }
  482.     else
  483.       { $self->set_do_not_escape_amp_in_atts( 0); 
  484.         $self->{twig_do_not_escape_amp_in_atts}=0;
  485.       }
  486.  
  487.     # deal with TwigRoots argument, a hash of elements for which
  488.     # subtrees will be built (and associated handlers)
  489.      
  490.     if( $args{TwigRoots})
  491.       { $self->setTwigRoots( $args{TwigRoots});
  492.         delete $args{TwigRoots}; 
  493.       }
  494.     
  495.     if( $args{EndTagHandlers})
  496.       { unless ($self->{twig_roots} || $args{ForceEndTagHandlersUsage})
  497.           { croak "you should not use EndTagHandlers without TwigRoots\n",
  498.                   "if you want to use it anyway, normally because you have ",
  499.                   "a start_tag_handlers that calls 'ignore' and you want to ",
  500.                   "call an ent_tag_handlers at the end of the element, then ",
  501.                   "pass 'force_end_tag_handlers_usage => 1' as an argument ",
  502.                   "to new";
  503.           }
  504.                   
  505.         $self->setEndTagHandlers( $args{EndTagHandlers});
  506.         delete $args{EndTagHandlers};
  507.       }
  508.       
  509.     if( $args{TwigPrintOutsideRoots})
  510.       { croak "cannot use twig_print_outside_roots without twig_roots"
  511.           unless( $self->{twig_roots});
  512.         # if the arg is a filehandle then store it
  513.         if( _is_fh( $args{TwigPrintOutsideRoots}) )
  514.           { $self->{twig_output_fh}= $args{TwigPrintOutsideRoots}; }
  515.         $self->{twig_default_print}= $args{TwigPrintOutsideRoots};
  516.       }
  517.  
  518.     # space policy
  519.     if( $args{KeepSpaces})
  520.       { croak "cannot use both keep_spaces and discard_spaces" if( $args{DiscardSpaces});
  521.         croak "cannot use both keep_spaces and keep_spaces_in" if( $args{KeepSpacesIn});
  522.         $self->{twig_keep_spaces}=1;
  523.         delete $args{KeepSpaces}; 
  524.       }
  525.     if( $args{DiscardSpaces})
  526.       { croak "cannot use both discard_spaces and keep_spaces_in" if( $args{KeepSpacesIn});
  527.         $self->{twig_discard_spaces}=1; 
  528.         delete $args{DiscardSpaces}; 
  529.       }
  530.     if( $args{KeepSpacesIn})
  531.       { croak "cannot use both keep_spaces_in and discard_spaces_in" if( $args{DiscardSpacesIn});
  532.         $self->{twig_discard_spaces}=1; 
  533.         $self->{twig_keep_spaces_in}={}; 
  534.         my @tags= @{$args{KeepSpacesIn}}; 
  535.         foreach my $tag (@tags) { $self->{twig_keep_spaces_in}->{$tag}=1; } 
  536.         delete $args{KeepSpacesIn}; 
  537.       }
  538.     if( $args{DiscardSpacesIn})
  539.       { $self->{twig_keep_spaces}=1; 
  540.         $self->{twig_discard_spaces_in}={}; 
  541.         my @tags= @{$args{DiscardSpacesIn}};
  542.         foreach my $tag (@tags) { $self->{twig_discard_spaces_in}->{$tag}=1; } 
  543.         delete $args{DiscardSpacesIn}; 
  544.       }
  545.     # discard spaces by default 
  546.     $self->{twig_discard_spaces}= 1 unless(  $self->{twig_keep_spaces});
  547.  
  548.     $args{Comments}||= $COMMENTS_DEFAULT;
  549.     if( $args{Comments} eq 'drop')       { $self->{twig_keep_comments}= 0;    }
  550.     elsif( $args{Comments} eq 'keep')    { $self->{twig_keep_comments}= 1;    }
  551.     elsif( $args{Comments} eq 'process') { $self->{twig_process_comments}= 1; }
  552.     else { croak "wrong value for comments argument: '$args{Comments}' (should be 'drop', 'keep' or 'process')"; }
  553.     delete $args{Comments};
  554.  
  555.     $args{Pi}||= $PI_DEFAULT;
  556.     if( $args{Pi} eq 'drop')       { $self->{twig_keep_pi}= 0;    }
  557.     elsif( $args{Pi} eq 'keep')    { $self->{twig_keep_pi}= 1;    }
  558.     elsif( $args{Pi} eq 'process') { $self->{twig_process_pi}= 1; }
  559.     else { croak "wrong value for pi argument: '$args{Pi}' (should be 'drop', 'keep' or 'process')"; }
  560.     delete $args{Pi};
  561.  
  562.     if( $args{KeepEncoding})
  563.       { 
  564.         # set it in XML::Twig::Elt so print functions know what to do
  565.         $self->set_keep_encoding( 1); 
  566.         $self->{parse_start_tag}= $args{ParseStartTag} || \&_parse_start_tag; 
  567.         delete $args{ParseStartTag} if defined( $args{ParseStartTag}) ;
  568.         delete $args{KeepEncoding};
  569.       }
  570.     else
  571.       { $self->set_keep_encoding( 0);  
  572.         if( $args{ParseStartTag}) 
  573.           { $self->{parse_start_tag}= $args{ParseStartTag}; }
  574.         else
  575.           { delete $self->{parse_start_tag}; }
  576.         delete $args{ParseStartTag};
  577.       }
  578.  
  579.     if( $args{OutputFilter})
  580.       { $self->set_output_filter( $args{OutputFilter}); 
  581.         delete $args{OutputFilter};
  582.       }
  583.     else
  584.       { $self->set_output_filter( 0); }
  585.  
  586.     if( $args{RemoveCdata})
  587.       { $self->set_remove_cdata( $args{RemoveCdata}); 
  588.         delete $args{RemoveCdata}; 
  589.       }
  590.     else
  591.       { $self->set_remove_cdata( 0); }
  592.  
  593.     if( $args{OutputTextFilter})
  594.       { $self->set_output_text_filter( $args{OutputTextFilter}); 
  595.         delete $args{OutputTextFilter};
  596.       }
  597.     else
  598.       { $self->set_output_text_filter( 0); }
  599.  
  600.     if( exists $args{KeepAttsOrder})
  601.       { $self->{keep_atts_order}= $args{KeepAttsOrder};
  602.         if( _use( 'Tie::IxHash'))
  603.           { $self->set_keep_atts_order(  $self->{keep_atts_order}); }
  604.         else 
  605.           { croak "Tie::IxHash not available, option keep_atts_order not allowed"; }
  606.       }
  607.     else
  608.       { $self->set_keep_atts_order( 0); }
  609.  
  610.  
  611.     if( $args{PrettyPrint})    { $self->set_pretty_print( $args{PrettyPrint}); }
  612.     if( $args{EscapeGt})       { $self->escape_gt( $args{EscapeGt});           }
  613.     if( $args{EmptyTags})      { $self->set_empty_tag_style( $args{EmptyTags}) }
  614.  
  615.     if( exists $args{Id})      { $ID= $args{Id};                     delete $args{ID};             }
  616.     if( $args{NoProlog})       { $self->{no_prolog}= 1;              delete $args{NoProlog};       }
  617.     if( $args{DoNotOutputDTD}) { $self->{no_dtd_output}= 1;          delete $args{DoNotOutputDTD}; }
  618.     if( $args{LoadDTD})        { $self->{twig_read_external_dtd}= 1; delete $args{LoadDTD};        }
  619.     if( $args{CharHandler})    { $self->setCharHandler( $args{CharHandler}); delete $args{CharHandler}; }
  620.  
  621.     if( $args{InputFilter})    { $self->set_input_filter(  $args{InputFilter}); delete  $args{InputFilter}; }
  622.     if( $args{NoExpand})       { $self->setHandlers( %twig_noexpand_handlers); $self->{twig_no_expand}=1; }
  623.     if( my $output_encoding= $args{OutputEncoding}) { $self->set_output_encoding( $output_encoding); delete $args{OutputFilter}; }
  624.  
  625.     if( my $tdh= $args{TopDownHandlers}) { $self->{twig_tdh}=1; delete $args{TopDownHandlers}; }
  626.  
  627.     $self->set_quote( $args{Quote} || 'double');
  628.  
  629.     # set handlers
  630.     if( $self->{twig_roots})
  631.       { if( $self->{twig_default_print})
  632.           { if( $self->{twig_keep_encoding})
  633.               { $self->setHandlers( %twig_handlers_roots_print_original); }
  634.             else
  635.               { $self->setHandlers( %twig_handlers_roots_print);  }
  636.           }
  637.         else
  638.           { $self->setHandlers( %twig_handlers_roots); }
  639.       }
  640.     else
  641.       { $self->setHandlers( %twig_handlers); }
  642.  
  643.     # XML::Parser::Expat does not like these handler to be set. So in order to 
  644.     # use the various sets of handlers on XML::Parser or XML::Parser::Expat
  645.     # objects when needed, these ones have to be set only once, here, at 
  646.     # XML::Parser level
  647.     $self->setHandlers( Init => \&_twig_init, Final => \&_twig_final);
  648.  
  649.     $self->{twig_entity_list}= XML::Twig::Entity_list->new; 
  650.  
  651.     $self->{twig_id}= $ID; 
  652.     $self->{twig_stored_spaces}='';
  653.  
  654.     $self->{twig_autoflush}= 1; # auto flush by default
  655.  
  656.     $self->{twig}= $self;
  657.     if( $weakrefs) { weaken( $self->{twig}); }
  658.  
  659.     return $self;
  660.   }
  661.  
  662. sub parse
  663.   {
  664.     my $t= shift;
  665.     # if called as a class method, calls nparse, which creates the twig then parses it
  666.     if( !ref( $t) || !isa( $t, 'XML::Twig')) { return $t->nparse( @_); }
  667.  
  668.     # requires 5.006 at least (or the ${^UNICODE} causes a problem)                                       # > perl 5.5
  669.     # trap underlying bug in IO::Handle (see RT #17500)                                                   # > perl 5.5
  670.     # croak if perl 5.8+, -CD (or PERL_UNICODE set to D) and parsing a pipe                               # > perl 5.5
  671.     if( $]>=5.008 && ${^UNICODE} && (${^UNICODE} & 24) && isa( $_[0], 'GLOB') && -p $_[0] )               # > perl 5.5
  672.       { croak   "cannot parse the output of a pipe when perl is set to use the UTF8 perlIO layer\n"       # > perl 5.5
  673.               . "set the environment variable PERL_UNICODE or use the -C option (see perldoc perlrun)\n"  # > perl 5.5
  674.               . "not to include 'D'";                                                                     # > perl 5.5
  675.       }                                                                                                   # > perl 5.5
  676.     $t= eval { $t->SUPER::parse( @_); }; 
  677.     return _checked_parse_result( $t, $@);
  678.   }
  679.  
  680. sub parsefile
  681.   { my $t= shift;
  682.     if( -f $_[0] && ! -s $_[0]) { return _checked_parse_result( undef, "empty file '$_[0]'"); }
  683.     $t= eval { $t->SUPER::parsefile( @_); };
  684.     return _checked_parse_result( $t, $@);
  685.   }
  686.  
  687. sub _checked_parse_result
  688.   { my( $t, $returned)= @_;
  689.     if( !$t)
  690.       { if( isa( $returned, 'XML::Twig') && $returned->{twig_finish_now})
  691.           { $t= $returned;
  692.             delete $t->{twig_finish_now};
  693.             return $t->_twig_final;
  694.           }
  695.         else
  696.           { _croak( $returned, 0); }
  697.       }
  698.     return $t;
  699.   }
  700.  
  701. sub finish_now
  702.   { my $t= shift;
  703.     $t->{twig_finish_now}=1;
  704.     die $t;    
  705.   }
  706.  
  707.  
  708. sub parsefile_inplace      { shift->_parse_inplace( parsefile      => @_); }
  709. sub parsefile_html_inplace { shift->_parse_inplace( parsefile_html => @_); }
  710.  
  711. sub _parse_inplace
  712.   { my( $t, $method, $file, $suffix)= @_;
  713.     _use( 'File::Temp') || croak "need File::Temp to use inplace methods\n";
  714.     _use( 'File::Basename');
  715.  
  716.  
  717.     my $tmpdir= dirname( $file);
  718.     my( $tmpfh, $tmpfile)= File::Temp::tempfile( DIR => $tmpdir);
  719.     my $original_fh= select $tmpfh;
  720.  
  721.     unless( $t->{twig_keep_encoding} || $] < 5.006) 
  722.       { if( grep /useperlio=define/, `$^X -V`) # we can only use binmode :utf8 if perl was compiled with useperlio
  723.           { binmode( $tmpfh, ":utf8" ); }
  724.       }
  725.  
  726.     $t->$method( $file);
  727.  
  728.     select $original_fh;
  729.     close $tmpfh;
  730.     my $mode= (stat( $file))[2] & oct(7777);
  731.     chmod $mode, $tmpfile or croak "cannot change temp file mode to $mode: $!";
  732.  
  733.     if( $suffix) 
  734.       { my $backup;
  735.         if( $suffix=~ m{\*}) { ($backup = $suffix) =~ s/\*/$file/g; }
  736.         else                 { $backup= $file . $suffix; }
  737.           
  738.         rename( $file, $backup) or croak "cannot backup initial file ($file) to $backup: $!"; 
  739.       }
  740.     rename( $tmpfile, $file) or croak "cannot rename temp file ($tmpfile) to initial file ($file): $!";
  741.  
  742.     return $t;
  743.   }
  744.     
  745.  
  746. sub parseurl
  747.   { my $t= shift;
  748.     $t->_parseurl( 0, @_);
  749.   }
  750.  
  751. sub safe_parseurl
  752.   { my $t= shift;
  753.     $t->_parseurl( 1, @_);
  754.   }
  755.  
  756. sub safe_parsefile_html
  757.   { my $t= shift;
  758.     eval { $t->parsefile_html( @_); };
  759.     return $@ ? $t->_reset_twig &&  0 : $t;
  760.   }
  761.  
  762. sub safe_parseurl_html
  763.   { my $t= shift;
  764.     _use( 'LWP::Simple') or croak "missing LWP::Simple"; 
  765.     eval { $t->parse_html( LWP::Simple::get( shift()), @_); } ;
  766.     return $@ ? $t->_reset_twig &&  0 : $t;
  767.   }
  768.  
  769. # uses eval to catch the parser's death
  770. sub safe_parse_html
  771.   { my $t= shift;
  772.     eval { $t->parse_html( @_); } ;
  773.     return $@ ? $t->_reset_twig &&  0 : $t;
  774.   }
  775.  
  776. sub parsefile_html
  777.   { my $t= shift;
  778.     my $file= shift;
  779.     my $indent= $t->{ErrorContext} ? 1 : 0;
  780.     $t->set_empty_tag_style( 'html');
  781.     $t->parse( _html2xml( _slurp( $file), { indent => $indent }), @_);
  782.     return $t;
  783.   }
  784.  
  785. sub parse_html
  786.   { my $t= shift;
  787.     my $content= shift;
  788.     my $indent= $t->{ErrorContext} ? 1 : 0;
  789.     $t->set_empty_tag_style( 'html');
  790.     $t->parse( _html2xml( isa( $content, 'GLOB') ? _slurp_fh( $content) : $content, { indent => $indent }), @_);
  791.     return $t;
  792.   }
  793.  
  794. sub xparse
  795.   { my $t= shift;
  796.     my $to_parse= $_[0];
  797.     if( isa( $to_parse, 'GLOB'))             { $t->parse( @_);                 }
  798.     elsif( $to_parse=~ m{^\s*<})             { $to_parse=~ m{<html}i ? $t->_parse_as_xml_or_html( @_)
  799.                                                                      : $t->parse( @_);                 
  800.                                              }
  801.     elsif( $to_parse=~ m{^\w+://.*\.html?$}) { _use( 'LWP::Simple') or croak "missing LWP::Simple"; 
  802.                                                $t->_parse_as_xml_or_html( LWP::Simple::get( shift()), @_);
  803.                                              }
  804.     elsif( $to_parse=~ m{^\w+://})           { _use( 'LWP::Simple') or croak "missing LWP::Simple";
  805.                                                my $doc= LWP::Simple::get( shift);
  806.                                                my $xml_parse_ok= $t->safe_parse( $doc, @_);
  807.                                                if( $xml_parse_ok)
  808.                                                  { return $xml_parse_ok; }
  809.                                                else
  810.                                                  { my $diag= $@;
  811.                                                    if( $doc=~ m{<html}i)
  812.                                                      { $t->parse_html( $doc, @_); }
  813.                                                     else
  814.                                                       { croak $diag; }
  815.                                                  }
  816.                                              }
  817.     elsif( $to_parse=~ m{\.html?$})          { my $content= _slurp( shift);
  818.                                                $t->_parse_as_xml_or_html( $content, @_); 
  819.                                              }
  820.     else                                     { $t->parsefile( @_);             }
  821.   }
  822.  
  823. sub _parse_as_xml_or_html
  824.   { my $t= shift; 
  825.     if( _is_well_formed_xml( $_[0]))
  826.       { $t->parse( @_) }
  827.     else
  828.       { my $html= _html2xml( $_[0]);
  829.         if( _is_well_formed_xml( $html))
  830.           { $t->parse( $html); }
  831.         else
  832.           { croak $@; }
  833.       }
  834.   }  
  835.     
  836. { my $parser;
  837.   sub _is_well_formed_xml
  838.     { $parser ||= XML::Parser->new;
  839.       eval { $parser->parse( $_[0]); };
  840.       return $@ ? 0 : 1;
  841.     }
  842. }
  843.  
  844. sub nparse
  845.   { my $class= shift;
  846.     my $to_parse= pop;
  847.     $class->new( @_)->xparse( $to_parse);
  848.   }
  849.  
  850. sub nparse_pp   { shift()->nparse( pretty_print => 'indented', @_); }
  851. sub nparse_e    { shift()->nparse( error_context => 1,         @_); }
  852. sub nparse_ppe  { shift()->nparse( pretty_print => 'indented', error_context => 1, @_); }
  853.  
  854.  
  855. sub _html2xml
  856.   { my( $html, $options)= @_;
  857.     _use( 'HTML::TreeBuilder', '3.13') or croak "cannot parse HTML: missing HTML::TreeBuilder v >= 3.13\n"; 
  858.     my $tree= HTML::TreeBuilder->new;
  859.     $tree->ignore_ignorable_whitespace( 0); 
  860.     $tree->no_space_compacting( 1);
  861.     $tree->store_comments( 1);
  862.     $tree->store_pis(1); 
  863.     $tree->parse( $html);
  864.     $tree->eof;
  865.  
  866.     my $xml= $tree->as_XML;
  867.     _fix_xml( $tree, \$xml);
  868.     $tree->delete;
  869.  
  870.     if( $options->{indent}) { _indent_xhtml( \$xml); }
  871.     $tree->delete;
  872.     $xml=~ s{\s+$}{}s; # trim end
  873.     return $xml;
  874.   }
  875.  
  876. { my %xml_parser_encoding;
  877.   sub _fix_xml
  878.     { my( $tree, $xml)= @_; # $xml is a ref to the xml string
  879.  
  880.       my $max_tries=5;
  881.       my $add_decl;
  882.  
  883.       while( ! _check_xml( $xml) && $max_tries--)
  884.         { 
  885.           # a couple of fixes for weird HTML::TreeBuilder errors
  886.           if( $@=~ m{^\s*xml declaration not at start of external entity})
  887.             { $$xml=~ s{<\?xml version.*\?>}{}; 
  888.               #warn " fixed xml declaration in the wrong place\n";
  889.             }
  890.           elsif( $@=~ m{^\s*not well-formed \(invalid token\)})
  891.             { my $encoding= _encoding_from_meta( $tree);
  892.               unless( keys %xml_parser_encoding) { %xml_parser_encoding= _xml_parser_encodings(); }
  893.  
  894.               if( ! $add_decl)
  895.                 { if( $xml_parser_encoding{$encoding})
  896.                     { $add_decl=1; }
  897.                   elsif( $encoding eq 'euc-jp' && $xml_parser_encoding{'x-euc-jp-jisx0221'})
  898.                     { $encoding="x-euc-jp-jisx0221"; $add_decl=1;}
  899.                   elsif( $encoding eq 'shift-jis' && $xml_parser_encoding{'x-sjis-jisx0221'})
  900.                     { $encoding="x-sjis-jisx0221";   $add_decl=1;}
  901.  
  902.                   if( $add_decl) 
  903.                     { $$xml=~ s{^(<\?xml.*?\?>)?}{<?xml version="1.0" encoding="$encoding"?>}s;
  904.                       #warn "  added decl (encoding $encoding)\n";
  905.                     }
  906.                   else
  907.                     { $$xml=~ s{^(<\?xml.*?\?>)?}{}s;
  908.                       #warn "  converting to utf8 from $encoding\n";
  909.                       $$xml= _to_utf8( $encoding, $$xml);
  910.                     }
  911.                 }
  912.               else
  913.                 { $$xml=~ s{^(<\?xml.*?\?>)?}{}s;
  914.                   #warn "  converting to utf8 from $encoding\n";
  915.                   $$xml= _to_utf8( $encoding, $$xml);
  916.                 }
  917.             }
  918.       }
  919.   }
  920.  
  921.   sub _xml_parser_encodings
  922.     { my @encodings=( 'iso-8859-1'); # this one is included by default, there is no map for it in @INC
  923.       foreach my $inc (@INC)
  924.         { push @encodings, map { basename( $_, '.enc') } glob( File::Spec->catdir( $inc => XML => Parser => Encodings => '*.enc')); }
  925.       return map { $_ => 1 } @encodings;
  926.     }
  927. }
  928.  
  929. sub _check_xml
  930.   { my( $xml)= @_; # $xml is a ref to the xml string
  931.     my $ok= eval { XML::Parser->new->parse( $$xml); };
  932.     #if( $ok) { warn "  parse OK\n"; }
  933.     return $ok;
  934.   }
  935.  
  936. sub _encoding_from_meta
  937.   { my( $tree)= @_; 
  938.     my $enc="iso-8859-1";
  939.     my @meta= $tree->find( 'meta');
  940.     foreach my $meta (@meta)
  941.       { if(    $meta->{'http-equiv'} && ($meta->{'http-equiv'} =~ m{^\s*content-type\s*}i)
  942.             && $meta->{content}      && ($meta->{content}      =~ m{^\s*text/html\s*;\s*charset\s*=\s*(\S*)\s*}i)
  943.           )
  944.           { $enc= lc $1;
  945.             #warn "  encoding from meta tag is '$enc'\n";
  946.             last;
  947.           }
  948.       }
  949.     return $enc;
  950.   }
  951.  
  952. { sub _to_utf8 
  953.     { my( $encoding, $string)= @_;
  954.       local $SIG{__DIE__};
  955.       if( _use(  'Encode')) 
  956.         { Encode::from_to( $string, $encoding => 'utf8', 0x0400); } # 0x0400 is Encode::FB_XMLCREF
  957.       elsif( _use( 'Text::Iconv'))
  958.         { my $converter =  eval { Text::Iconv->new( $encoding => "utf8") };
  959.           if( $converter) {  $string= $converter->convert( $string); }
  960.         }
  961.       elsif( _use( 'Unicode::Map8') && _use( 'Unicode::String'))
  962.         { my $map= Unicode::Map8->new( $encoding); 
  963.           $string= $map->tou( $string)->utf8;
  964.         }
  965.       $string=~ s{[\x00-\x08\x0B\x0C\x0E-\x1F]}{}g; # get rid of control chars, portable in 5.6
  966.     return $string;
  967.   }
  968. }
  969.  
  970.  
  971. sub _indent_xhtml
  972.   { my( $xhtml)= @_; # $xhtml is a ref
  973.     my %block_tag= map { $_ => 1 } qw( html 
  974.                                          head 
  975.                                            meta title link script base
  976.                                          body 
  977.                                            h1 h2 h3 h4 h5 h6 
  978.                                            p br address  blockquote pre 
  979.                                            ol ul li  dd dl dt 
  980.                                            table tr td th tbody tfoot thead  col colgroup caption 
  981.                                            div frame frameset hr
  982.                                      ); 
  983.  
  984.     my $level=0;
  985.     $$xhtml=~ s{( (?:<!(?:--.*?-->|[CDATA[.*]]>)) # ignore comments and CDATA sections
  986.                   | <(\w+)                        # start tag
  987.                   |(</\(\w+)                      # end tag 
  988.                 )
  989.                }
  990.                {
  991.                  if(    $2 && $block_tag{$2})  { my $indent= "  " x $level; 
  992.                                                  $level++ unless( $2=~ m{/>});
  993.                                                  "\n$indent<$2"; 
  994.                                                }
  995.                  elsif( $3  && $block_tag{$3}) { $level--; "</$3"; }
  996.                  else                          { $1; }
  997.                }xesg;
  998.   }
  999.  
  1000.  
  1001. sub add_stylesheet
  1002.   { my( $t, $type, $href)= @_;
  1003.     my %text_type= map { $_ => 1 } qw( xsl css);
  1004.     my $ss= $t->{twig_elt_class}->new( $PI);
  1005.     if( $text_type{$type}) 
  1006.       { $ss->_set_pi( 'xml-stylesheet', qq{type="text/$type" href="$href"}); }
  1007.     else
  1008.       { croak "unsupported style sheet type '$type'"; }
  1009.       
  1010.     $t->_add_cpi_outside_of_root( leading_cpi => $ss);
  1011.     return $t;
  1012.   }
  1013.  
  1014. { my %used;       # module => 1 if require ok, 0 otherwise
  1015.   my %disallowed; # for testing, refuses to _use modules in this hash
  1016.  
  1017.   sub _disallow_use ## no critic (Subroutines::ProhibitNestedSubs);
  1018.     { my( @modules)= @_;
  1019.       $disallowed{$_}= 1 foreach (@modules);
  1020.     }
  1021.  
  1022.   sub _allow_use  ## no critic (Subroutines::ProhibitNestedSubs);
  1023.     { my( @modules)= @_;
  1024.       $disallowed{$_}= 0 foreach (@modules);
  1025.     }
  1026.  
  1027.   sub _use  ## no critic (Subroutines::ProhibitNestedSubs);
  1028.     { my( $module, $version)= @_;
  1029.       $version ||= 0;
  1030.       if( $disallowed{$module})   { return 0; }
  1031.       if( $used{$module})         { return 1; }
  1032.       if( eval "require $module") { import $module; $used{$module}= 1; 
  1033.                                     if( $version)
  1034.                                       { 
  1035.                                         ## no critic (TestingAndDebugging::ProhibitNoStrict);
  1036.                                         no strict 'refs';
  1037.                                         if( ${"${module}::VERSION"} >= $version ) { return 1; }
  1038.                                         else                                      { return 0; }
  1039.                                       }
  1040.                                     else
  1041.                                       { return 1; }
  1042.                                   }
  1043.       else                        {                          $used{$module}= 0; return 0; }
  1044.     }
  1045. }
  1046.  
  1047. # used to solve the [n] predicates while avoiding getting the entire list
  1048. # needs a prototype to accept passing bare blocks
  1049. sub _first_n(&$@)       ## nocritic (Subroutines::ProhibitSubroutinePrototypes);
  1050.   { my $coderef= shift;
  1051.     my $n= shift;         
  1052.     my $i=0;
  1053.     if( $n > 0)
  1054.       { foreach (@_)         { if( &$coderef) { $i++; return $_ if( $i == $n); } } }
  1055.     elsif( $n < 0)
  1056.       { foreach (reverse @_) { if( &$coderef) { $i--; return $_ if( $i == $n); } } }
  1057.     else
  1058.       { croak "illegal position number 0"; }
  1059.     return undef;
  1060.   }
  1061.  
  1062. sub _slurp_uri
  1063.   { my( $uri, $base)= @_;
  1064.     if( $uri=~ m{^\w+://}) { _use( 'LWP::Simple'); return LWP::Simple::get( $uri); }
  1065.     else                   { return _slurp( _based_filename( $uri, $base));        }
  1066.   }
  1067.  
  1068. sub _based_filename
  1069.   { my( $filename, $base)= @_;
  1070.     # cf. XML/Parser.pm's file_ext_ent_handler
  1071.     if (defined($base) and not ($filename =~ m{^(?:[\\/]|\w+:)})) 
  1072.           { my $newpath = $base;
  1073.             $newpath =~ s{[^\\/:]*$}{$filename};
  1074.             $filename = $newpath;
  1075.           }
  1076.     return $filename;
  1077.   }
  1078.  
  1079. sub _slurp
  1080.   { my( $filename)= @_;
  1081.     # use bareword filehandle to stay compatible with real old perl
  1082.     open( TWIG_TO_SLURP, "<$filename") or croak "cannot open '$filename': $!"; 
  1083.     local $/= undef;
  1084.     my $content= <TWIG_TO_SLURP>;
  1085.     close TWIG_TO_SLURP;
  1086.     return $content;
  1087.   }
  1088.   
  1089. sub _slurp_fh
  1090.   { my( $fh)= @_;
  1091.     local $/= undef;
  1092.     my $content= <$fh>;
  1093.     return $content;
  1094.   }    
  1095.  
  1096. # I should really add extra options to allow better configuration of the 
  1097. # LWP::UserAgent object
  1098. # this method forks (except on VMS!)
  1099. #   - the child gets the data and copies it to the pipe,
  1100. #   - the parent reads the stream and sends it to XML::Parser
  1101. # the data is cut it chunks the size of the XML::Parser::Expat buffer
  1102. # the method returns the twig and the status
  1103. sub _parseurl
  1104.   { my( $t, $safe, $url, $agent)= @_;
  1105.     _use( 'LWP') || croak "LWP not available, needed to use parseurl methods";
  1106.     if( $^O ne 'VMS')
  1107.       { pipe( README, WRITEME) or croak  "cannot create connected pipes: $!";
  1108.         if( my $pid= fork)
  1109.           { # parent code: parse the incoming file
  1110.             close WRITEME; # no need to write
  1111.             my $result= $safe ? $t->safe_parse( \*README) : $t->parse( \*README);
  1112.             close README;
  1113.             return $@ ? 0 : $t;
  1114.           }
  1115.         else
  1116.          { # child
  1117.             close README; # no need to read
  1118.             local $|=1;
  1119.             $agent    ||= LWP::UserAgent->new;
  1120.             my $request  = HTTP::Request->new( GET => $url);
  1121.             # _pass_url_content is called with chunks of data the same size as
  1122.             # the XML::Parser buffer 
  1123.             my $response = $agent->request( $request, 
  1124.                              sub { _pass_url_content( \*WRITEME, @_); }, $BUFSIZE);
  1125.             $response->is_success or croak "$url ", $response->message;
  1126.             close WRITEME;
  1127.             CORE::exit(); # CORE is there for mod_perl (which redefines exit)
  1128.           }
  1129.       } 
  1130.     else 
  1131.       { local $|=1;
  1132.         $agent    ||= LWP::UserAgent->new;
  1133.         my $request  = HTTP::Request->new( GET => $url);
  1134.         my $response = $agent->request( $request);
  1135.         $response->is_success or croak "$url ", $response->message;
  1136.         my $result= $safe ? $t->safe_parse($response->content) : $t->parse($response->content);
  1137.         return $@ ? 0 : $t;
  1138.      }
  1139.  
  1140.   }
  1141.  
  1142. # get the (hopefully!) XML data from the URL and 
  1143. sub _pass_url_content
  1144.   { my( $fh, $data, $response, $protocol)= @_;
  1145.     print {$fh} $data;
  1146.   }
  1147.  
  1148. sub add_options
  1149.   { my %args= map { $_, 1 } @_;
  1150.     %args= _normalize_args( %args);
  1151.     foreach (keys %args) { $valid_option{$_}++; } 
  1152.   }
  1153.  
  1154. sub _pretty_print_styles { return XML::Twig::Elt::_pretty_print_styles(); }
  1155.  
  1156. sub _twig_store_internal_dtd
  1157.  { 
  1158.    # warn " in _twig_store_internal_dtd...\n"; # DEBUG handler
  1159.     my( $p, $string)= @_;
  1160.     my $t= $p->{twig};
  1161.     if( $t->{twig_keep_encoding}) { $string= $p->original_string(); }
  1162.     $t->{twig_doctype}->{internal} .= $string;
  1163.     return;
  1164.   }
  1165.  
  1166. sub _twig_stop_storing_internal_dtd
  1167.    { # warn " in _twig_stop_storing_internal_dtd...\n"; # DEBUG handler
  1168.     my $p= shift;
  1169.     if( @saved_default_handler && defined $saved_default_handler[1])
  1170.       { $p->setHandlers( @saved_default_handler); }
  1171.     else
  1172.       { my $t= $p->{twig};
  1173.         $p->setHandlers( Default => undef);
  1174.       }
  1175.     $p->{twig}->{twig_doctype}->{internal}=~ s{^\s*\[}{};
  1176.     $p->{twig}->{twig_doctype}->{internal}=~ s{\]\s*$}{};
  1177.     return;
  1178.   }
  1179.  
  1180. sub _twig_doctype_fin_print
  1181.   { # warn " in _twig_doctype_fin_print...\n"; # DEBUG handler
  1182.     my( $p)= shift;
  1183.     if( $p->{twig}->{twig_doctype}->{has_internal} && !$expat_1_95_2) { print ' ]>'; }
  1184.     return;
  1185.   }
  1186.     
  1187.  
  1188. sub _normalize_args
  1189.   { my %normalized_args;
  1190.     while( my $key= shift )
  1191.       { $key= join '', map { ucfirst } split /_/, $key;
  1192.         #$key= "Twig".$key unless( substr( $key, 0, 4) eq 'Twig');
  1193.         $normalized_args{$key}= shift ;
  1194.       }
  1195.     return %normalized_args;
  1196.   }    
  1197.  
  1198. sub _is_fh { return unless $_[0]; return $_[0] if( isa( $_[0], 'GLOB') || isa( $_[0], 'IO::Scalar')); }
  1199.  
  1200. sub _set_handler
  1201.   { my( $handlers, $path, $handler)= @_;
  1202.  
  1203.     my $prev_handler= $handlers->{handlers}->{string}->{$path} || undef;
  1204.  
  1205.        _set_special_handler         ( $handlers, $path, $handler, $prev_handler)
  1206.     || _set_pi_handler              ( $handlers, $path, $handler, $prev_handler)
  1207.     || _set_level_handler           ( $handlers, $path, $handler, $prev_handler)
  1208.     || _set_regexp_handler          ( $handlers, $path, $handler, $prev_handler)
  1209.     || _set_xpath_handler           ( $handlers, $path, $handler, $prev_handler)
  1210.     || croak "unrecognized expression in handler: '$path'";
  1211.  
  1212.  
  1213.     # this both takes care of the simple (gi) handlers and store
  1214.     # the handler code reference for other handlers
  1215.     $handlers->{handlers}->{string}->{$path}= $handler;
  1216.  
  1217.     return $prev_handler;
  1218.   }
  1219.  
  1220.  
  1221. sub _set_special_handler
  1222.   { my( $handlers, $path, $handler, $prev_handler)= @_;
  1223.     if( $path =~ m{^\s*($ALL|$DEFAULT|$COMMENT)\s*$}io )
  1224.       { $handlers->{handlers}->{$1}= $handler; 
  1225.         return 1;
  1226.       }
  1227.     else 
  1228.       { return 0; }
  1229.   }
  1230.  
  1231. sub _set_xpath_handler
  1232.   { my( $handlers, $path, $handler, $prev_handler)= @_;
  1233.     if( my $handler_data= _parse_xpath_handler( $path, $handler))
  1234.       { _add_handler( $handlers, $handler_data, $path, $prev_handler);
  1235.         return 1;
  1236.       }
  1237.     else 
  1238.       { return 0; }
  1239.   }
  1240.  
  1241. sub _add_handler
  1242.   { my( $handlers, $handler_data, $path, $prev_handler)= @_;
  1243.  
  1244.     my $tag= $handler_data->{tag};
  1245.     my @handlers= $handlers->{xpath_handler}->{$tag} ? @{$handlers->{xpath_handler}->{$tag}} : ();
  1246.  
  1247.     if( $prev_handler) { @handlers= grep { $_->{path} ne $path } @handlers; }
  1248.  
  1249.     push @handlers, $handler_data if( $handler_data->{handler});
  1250.     
  1251.     if( @handlers > 1)
  1252.       { @handlers= sort {    (($b->{score}->{type}        || 0)  <=>  ($a->{score}->{type}        || 0))
  1253.                           || (($b->{score}->{anchored}    || 0)  <=>  ($a->{score}->{anchored}    || 0))
  1254.                           || (($b->{score}->{steps}       || 0)  <=>  ($a->{score}->{steps}       || 0))
  1255.                           || (($b->{score}->{predicates}  || 0)  <=>  ($a->{score}->{predicates}  || 0))
  1256.                           || (($b->{score}->{tests}       || 0)  <=>  ($a->{score}->{tests}       || 0))
  1257.                           || ($a->{path} cmp $b->{path})
  1258.                         } @handlers;
  1259.       }
  1260.  
  1261.     $handlers->{xpath_handler}->{$tag}= \@handlers;
  1262.   }
  1263.  
  1264. sub _set_pi_handler
  1265.   { my( $handlers, $path, $handler, $prev_handler)= @_;
  1266.     # PI conditions ( '?target' => \&handler or '?' => \&handler
  1267.     #             or '#PItarget' => \&handler or '#PI' => \&handler)
  1268.     if( $path=~ /^\s*(?:\?|$PI)\s*(?:([^\s]*)\s*)$/)
  1269.       { my $target= $1 || '';
  1270.         # update the path_handlers count, knowing that
  1271.         # either the previous or the new handler can be undef
  1272.         $handlers->{pi_handlers}->{$1}= $handler;
  1273.         return 1;
  1274.       }
  1275.     else 
  1276.       { return 0; 
  1277.       }
  1278.   }
  1279.  
  1280. sub _set_level_handler
  1281.   { my( $handlers, $path, $handler, $prev_handler)= @_;
  1282.     if( $path =~ m{^ \s* level \s* \( \s* ([0-9]+) \s* \) \s* $}ox )
  1283.       { my $level= $1;
  1284.         my $sub= sub { my( $stack)= @_; return( ($stack->[-1]->{_tag} !~ m{^#}) && (scalar @$stack == $level + 1) ) }; 
  1285.         my $handler_data=  { tag=> '*', score => { type => $LEVEL_TRIGGER}, trigger => $sub, 
  1286.                              path => $path, handler => $handler, test_on_text => 0
  1287.                            };
  1288.         _add_handler( $handlers, $handler_data, $path, $prev_handler);
  1289.         return 1;
  1290.       }
  1291.     else 
  1292.       { return 0; }
  1293.   }
  1294.  
  1295. sub _set_regexp_handler
  1296.   { my( $handlers, $path, $handler, $prev_handler)= @_; 
  1297.     # if the expression was a regexp it is now a string (it was stringified when it became a hash key)
  1298.     if( $path=~ m{^\(\?([xism]*)(?:-[xism]*)?:(.*)\)$}) 
  1299.       { my $regexp= qr/(?$1:$2)/; # convert it back into a regexp
  1300.         my $sub= sub { my( $stack)= @_; return( $stack->[-1]->{_tag} =~ $regexp ) }; 
  1301.         my $handler_data=  { tag=> '*', score => { type => $REGEXP_TRIGGER} , trigger => $sub, 
  1302.                              path => $path, handler => $handler, test_on_text => 0 
  1303.                            };
  1304.         _add_handler( $handlers, $handler_data, $path, $prev_handler);
  1305.         return 1;
  1306.       }
  1307.     else 
  1308.       { return 0; }
  1309.   }
  1310.  
  1311. sub _parse_xpath_handler
  1312.   { my( $xpath, $handler)= @_;
  1313.     my $xpath_original= $xpath;
  1314.  
  1315.     my $DEBUG_HANDLER= 0; # 0 or 1 (output the handler checking code) or 2 (super verbose)
  1316.  
  1317.     if( $DEBUG_HANDLER >=1) { warn "\n\nparsing path '$xpath'\n"; }
  1318.  
  1319.     my $path_to_check= $xpath;
  1320.     $path_to_check=~ s{/?/?$REG_NAME_WC?\s*(?:$REG_PREDICATE\s*)?}{}g;
  1321.     if( $DEBUG_HANDLER && $path_to_check=~ /\S/) { warn "left: $path_to_check\n"; }
  1322.     return if( $path_to_check=~ /\S/);
  1323.  
  1324.     (my $xpath_to_display= $xpath)=~ s{(["{}'\[\]\@\$])}{\\$1}g;
  1325.  
  1326.     my @xpath_steps;
  1327.     my $last_token_is_sep;
  1328.  
  1329.     while( $xpath=~ s{^\s*
  1330.                        ( (//?)                                      # separator
  1331.                         | (?:$REG_NAME_WC\s*(?:$REG_PREDICATE\s*)?) # tag name and optional predicate
  1332.                         | (?:$REG_PREDICATE)                       # just a predicate
  1333.                        )
  1334.                      }
  1335.                      {}x
  1336.          )
  1337.       { # check that we have alternating separators and steps
  1338.         if( $2) # found a separator
  1339.           { if(  $last_token_is_sep) { return 0; } # 2 seps in a row
  1340.             $last_token_is_sep= 1;
  1341.           }
  1342.         else
  1343.           { if( defined( $last_token_is_sep) && !$last_token_is_sep) { return 0; } # 2 steps in a row
  1344.             $last_token_is_sep= 0;
  1345.           }
  1346.  
  1347.         push @xpath_steps, $1;
  1348.       }
  1349.     if( $last_token_is_sep) { return 0; } # expression cannot end with a separator 
  1350.  
  1351.     my $i=-1;
  1352.  
  1353.     my $perlfunc= _join_n( $NO_WARNINGS . ';',
  1354.                            q|my( $stack)= @_;                    |,
  1355.                            q|my @current_elts= (scalar @$stack); |,
  1356.                            q|my @new_current_elts;               |,
  1357.                            q|my $elt;                            |,
  1358.                            ($DEBUG_HANDLER >= 1) && (qq#warn q{checking path '$xpath_to_display'\n};#),
  1359.                          );
  1360.  
  1361.  
  1362.     my $last_tag='';
  1363.     my $anchored= $xpath_original=~ m{^\s*/(?!/)} ? 1 : 0; 
  1364.     my $score={ type => $XPATH_TRIGGER, anchored => $anchored };
  1365.     my $flag= { test_on_text => 0 };
  1366.     my $sep='/';  # '/' or '//'
  1367.     while( my $xpath_step= pop @xpath_steps)
  1368.       { my( $tag, $predicate)= $xpath_step =~ m{^($REG_NAME_WC)?(?:\[(.*)\])?\s*$};
  1369.         $score->{steps}++;
  1370.         $tag||='*';
  1371.  
  1372.         my $warn_empty_stack= $DEBUG_HANDLER >= 2 ? qq{warn "return with empty stack\\n";} : '';
  1373.  
  1374.         if( $predicate)
  1375.           { if( $DEBUG_HANDLER >= 2)  { warn "predicate is: '$predicate'\n"; }
  1376.             # changes $predicate (from an XPath expression to a Perl one)
  1377.             if( $predicate=~ m{^\s*$REG_NUMBER\s*$}) { croak "position selector [$predicate] not supported on twig_handlers"; }
  1378.             _parse_predicate_in_handler( $predicate, $flag, $score);
  1379.             if( $DEBUG_HANDLER >= 2) { warn "predicate becomes: '$predicate'\n"; }
  1380.           }
  1381.  
  1382.        my $tag_cond=  _tag_cond( $tag);
  1383.        my $cond= join( " && ", grep { $_ } $tag_cond, $predicate) || 1;
  1384.  
  1385.        if( $css_sel && $tag=~ m{\.}) { $tag=~s{\.[^.]*$}{}; $tag ||='*'; }
  1386.        $last_tag ||= $tag;
  1387.  
  1388.  
  1389.        if( $sep eq '/')
  1390.          { 
  1391.            $perlfunc .= sprintf( _join_n(  q#foreach my $current_elt (@current_elts)              #,
  1392.                                            q#  { next if( !$current_elt);                         #,
  1393.                                            q#    $current_elt--;                                  #,
  1394.                                            q#    $elt= $stack->[$current_elt];                    #,
  1395.                                            q#    if( %s) { push @new_current_elts, $current_elt;} #,
  1396.                                            q#  }                                                  #,
  1397.                                         ),
  1398.                                  $cond
  1399.                                );
  1400.          }
  1401.        elsif( $sep eq '//')
  1402.          { 
  1403.            $perlfunc .= sprintf( _join_n(  q#foreach my $current_elt (@current_elts)                #,
  1404.                                            q#  { next if( !$current_elt);                           #,
  1405.                                            q#    $current_elt--;                                    #,
  1406.                                            q#    my $candidate= $current_elt;                       #,
  1407.                                            q#    while( $candidate >=0)                             #,
  1408.                                            q#      { $elt= $stack->[$candidate];                    #,
  1409.                                            q#        if( %s) { push @new_current_elts, $candidate;} #,
  1410.                                            q#        $candidate--;                                  #,
  1411.                                            q#      }                                                #,
  1412.                                            q#  }                                                    #,
  1413.                                         ),
  1414.                                  $cond
  1415.                                );
  1416.          }
  1417.        my $warn= $DEBUG_HANDLER >= 2 ? _join_n( qq#warn qq%fail at cond '$cond'%;#) : '';
  1418.        $perlfunc .= sprintf( _join_n( q#unless( @new_current_elts) { %s return 0; } #,
  1419.                                       q#@current_elts= @new_current_elts;           #,
  1420.                                       q#@new_current_elts=();                       #,
  1421.                                     ),
  1422.                              $warn
  1423.                            );
  1424.  
  1425.         $sep= pop @xpath_steps;
  1426.      }
  1427.  
  1428.     if( $anchored) # there should be a better way, but this works
  1429.       {  
  1430.        my $warn= $DEBUG_HANDLER >= 2 ? _join_n( qq#warn qq{fail, stack not empty};#) : '';
  1431.        $perlfunc .= sprintf( _join_n( q#if( ! grep { $_ == 0 } @current_elts) { %s return 0;}#), $warn);
  1432.       }
  1433.  
  1434.     $perlfunc.= qq{warn "handler for '$xpath_to_display' triggered\\n";\n} if( $DEBUG_HANDLER >=2);
  1435.     $perlfunc.= qq{return q{$xpath_original};\n};
  1436.     warn "\nperlfunc:\n$perlfunc\n" if( $DEBUG_HANDLER>=1);
  1437.     my $s= eval "sub { $perlfunc }";
  1438.       if( $@) 
  1439.         { croak "wrong handler condition '$xpath' ($@);" }
  1440.  
  1441.       warn "last tag: '$last_tag', test_on_text: '$flag->{test_on_text}'\n" if( $DEBUG_HANDLER >=1);
  1442.       warn "score: ", join( ' ', map { "$_: $score->{$_}" } sort keys %$score), "\n" if( $DEBUG_HANDLER >=1);
  1443.       return { tag=> $last_tag, score => $score, trigger => $s, path => $xpath_original, handler => $handler, test_on_text => $flag->{test_on_text} };
  1444.     }
  1445.  
  1446. sub _join_n { return join( "\n", @_, ''); }
  1447.  
  1448. sub _tag_cond
  1449.   { my( $full_tag)= @_;
  1450.  
  1451.     my( $tag, $class)= $css_sel ? $full_tag=~ m{^(.*?)(?:\.([^.]*))?$} : ($full_tag, undef);
  1452.     my $tag_cond= $tag && $tag ne '*' ? qq#(\$elt->{_tag} eq "$tag")# : '';
  1453.     my $class_cond= defined $class ? qq#(\$elt->{_elt} && \$elt->{_elt}->{att}->{class}=~ m{\\b$class\\b})# : '';
  1454.     my $full_cond= join( ' && ', grep { $_ } ( $tag_cond, $class_cond));
  1455.     
  1456.     return $full_cond;
  1457.   }
  1458.  
  1459. # input: the predicate ($_[0]) which will be changed in place
  1460. #        flags, a hashref with various flags (like test_on_text)
  1461. #        the score 
  1462. sub _parse_predicate_in_handler
  1463.   { my( $flag, $score)= @_[1..2];
  1464.     $_[0]=~ s{(   ($REG_STRING)                        # strings
  1465.                  |\@($REG_NAME)(?=\s*(?:[><=!]|!~|=~)) # @att (followed by a comparison operator)
  1466.                  |\@($REG_NAME)                        # @att (not followed by a comparison operator)
  1467.                  |=~|!~                                # matching operators
  1468.                  |([><]=?|=|!=)(?=\s*[\d+-])           # test before a number
  1469.                  |([><]=?|=|!=)                        # test, other cases
  1470.                  |($REG_FUNCTION)                      # no arg functions
  1471.                  # this bit is a mess, but it is the only solution with this half-baked parser
  1472.                  |(string\(\s*$REG_NAME\s*\)\s*$REG_MATCH\s*$REG_REGEXP)  # string( child)=~ /regexp/
  1473.                  |(string\(\s*$REG_NAME\s*\)\s*$REG_COMP\s*$REG_STRING) # string( child) = "value" (or other test)
  1474.                  |(string\(\s*$REG_NAME\s*\)\s*$REG_COMP\s*$REG_NUMBER) # string( child) = nb (or other test)
  1475.                  |(and|or)
  1476.               )}
  1477.              { my( $token, $string, $att, $bare_att, $num_test, $alpha_test, $func, $string_regexp, $string_test_alpha, $string_test_num, $and_or) 
  1478.                = ( $1,     $2,      $3,   $4,        $5,        $6,          $7,    $8,             $9,                 $10,              $11); 
  1479.     
  1480.                $score->{predicates}++;
  1481.               
  1482.                # store tests on text (they are not always allowed)
  1483.                if( $func || $string_regexp || $string_test_num || $string_test_alpha ) { $flag->{test_on_text}= 1;   }
  1484.  
  1485.                if( defined $string)   { $token }
  1486.                elsif( $att)           { $att=~ m{^#} ? qq{ (\$elt->{_elt} && \$elt->{_elt}->{att}->{'$att'})}
  1487.                                                      : qq{\$elt->{'$att'}}
  1488.                                       }
  1489.                elsif( $bare_att)      { $bare_att=~ m{^#} ? qq{(\$elt->{_elt} && defined(\$elt->{_elt}->{att}->{'$bare_att'}))}
  1490.                                                           : qq{defined( \$elt->{'$bare_att'})}
  1491.                                       }
  1492.                elsif( $num_test && ($num_test eq '=') ) { "==" } # others tests are unchanged
  1493.                elsif( $alpha_test)    { $PERL_ALPHA_TEST{$alpha_test} }
  1494.                elsif( $func && $func=~ m{^string})
  1495.                                       { "\$elt->{_elt}->text"; }
  1496.                elsif( $string_regexp && $string_regexp =~ m{string\(\s*($REG_NAME)\s*\)\s*($REG_MATCH)\s*($REG_REGEXP)})
  1497.                                       { "defined( _first_n {  \$_->text $2 $3 } 1, \$elt->{_elt}->_children( '$1'))"; }
  1498.                elsif( $string_test_alpha && $string_test_alpha     =~ m{string\(\s*($REG_NAME)\s*\)\s*($REG_COMP)\s*($REG_STRING)})
  1499.                                       { my( $tag, $op, $str)= ($1, $2, $3);
  1500.                                         $str=~ s{(?<=.)'(?=.)}{\\'}g; # escape a quote within the string 
  1501.                                         $str=~ s{^"}{'};
  1502.                                         $str=~ s{"$}{'};
  1503.                                         "defined( _first_n { \$_->text $PERL_ALPHA_TEST{$op} $str } 1, \$elt->{_elt}->children( '$tag'))"; }
  1504.                elsif( $string_test_num && $string_test_num   =~ m{string\(\s*($REG_NAME)\s*\)\s*($REG_COMP)\s*($REG_NUMBER)})
  1505.                                       { my $test= ($2 eq '=') ? '==' : $2;
  1506.                                         "defined( _first_n { \$_->text $test $3 } 1, \$elt->{_elt}->children( '$1'))"; 
  1507.                                       }
  1508.                elsif( $and_or)        { $score->{tests}++; $and_or eq 'and' ? '&&' : '||' ; }
  1509.                else                   { $token; }
  1510.              }gexs;
  1511.   }
  1512.     
  1513.  
  1514. sub setCharHandler
  1515.   { my( $t, $handler)= @_;
  1516.     $t->{twig_char_handler}= $handler;
  1517.   }
  1518.  
  1519.  
  1520. sub _reset_handlers
  1521.   { my $handlers= shift;
  1522.     delete $handlers->{handlers};
  1523.     delete $handlers->{path_handlers};
  1524.     delete $handlers->{subpath_handlers};
  1525.     $handlers->{attcond_handlers_exp}=[] if( $handlers->{attcond_handlers});
  1526.     delete $handlers->{attcond_handlers};
  1527.   }
  1528.   
  1529. sub _set_handlers
  1530.   { my $handlers= shift || return;
  1531.     my $set_handlers= {};
  1532.     foreach my $path (keys %{$handlers})
  1533.       { _set_handler( $set_handlers, $path, $handlers->{$path}); }
  1534.     
  1535.     return $set_handlers;
  1536.   }
  1537.     
  1538.  
  1539. sub setTwigHandler
  1540.   { my( $t, $path, $handler)= @_;
  1541.     $t->{twig_handlers} ||={};
  1542.     return _set_handler( $t->{twig_handlers}, $path, $handler);
  1543.   }
  1544.  
  1545. sub setTwigHandlers
  1546.   { my( $t, $handlers)= @_;
  1547.     my $previous_handlers= $t->{twig_handlers} || undef;
  1548.     _reset_handlers( $t->{twig_handlers});
  1549.     $t->{twig_handlers}= _set_handlers( $handlers);
  1550.     return $previous_handlers;
  1551.   }
  1552.  
  1553. sub setStartTagHandler
  1554.   { my( $t, $path, $handler)= @_;
  1555.     $t->{twig_starttag_handlers}||={};
  1556.     return _set_handler( $t->{twig_starttag_handlers}, $path, $handler);
  1557.   }
  1558.  
  1559. sub setStartTagHandlers
  1560.   { my( $t, $handlers)= @_;
  1561.     my $previous_handlers= $t->{twig_starttag_handlers} || undef;
  1562.     _reset_handlers( $t->{twig_starttag_handlers});
  1563.     $t->{twig_starttag_handlers}= _set_handlers( $handlers);
  1564.     return $previous_handlers;
  1565.    }
  1566.  
  1567. sub setIgnoreEltsHandler
  1568.   { my( $t, $path, $action)= @_;
  1569.     $t->{twig_ignore_elts_handlers}||={};
  1570.     return _set_handler( $t->{twig_ignore_elts_handlers}, $path, $action );
  1571.   }
  1572.  
  1573. sub setIgnoreEltsHandlers
  1574.   { my( $t, $handlers)= @_;
  1575.     my $previous_handlers= $t->{twig_ignore_elts_handlers};
  1576.     _reset_handlers( $t->{twig_ignore_elts_handlers});
  1577.     $t->{twig_ignore_elts_handlers}= _set_handlers( $handlers);
  1578.     return $previous_handlers;
  1579.    }
  1580.  
  1581. sub setEndTagHandler
  1582.   { my( $t, $path, $handler)= @_;
  1583.     $t->{twig_endtag_handlers}||={};
  1584.     return _set_handler( $t->{twig_endtag_handlers}, $path,$handler);
  1585.   }
  1586.  
  1587. sub setEndTagHandlers
  1588.   { my( $t, $handlers)= @_;
  1589.     my $previous_handlers= $t->{twig_endtag_handlers};
  1590.     _reset_handlers( $t->{twig_endtag_handlers});
  1591.     $t->{twig_endtag_handlers}= _set_handlers( $handlers);
  1592.     return $previous_handlers;
  1593.    }
  1594.  
  1595. # a little more complex: set the twig_handlers only if a code ref is given
  1596. sub setTwigRoots
  1597.   { my( $t, $handlers)= @_;
  1598.     my $previous_roots= $t->{twig_roots};
  1599.     _reset_handlers($t->{twig_roots});
  1600.     $t->{twig_roots}= _set_handlers( $handlers);
  1601.  
  1602.     _check_illegal_twig_roots_handlers( $t->{twig_roots});
  1603.     
  1604.     foreach my $path (keys %{$handlers})
  1605.       { $t->{twig_handlers}||= {};
  1606.         _set_handler( $t->{twig_handlers}, $path, $handlers->{$path})
  1607.           if( ref($handlers->{$path}) && isa( $handlers->{$path}, 'CODE')); 
  1608.       }
  1609.     return $previous_roots;
  1610.   }
  1611.  
  1612. sub _check_illegal_twig_roots_handlers
  1613.   { my( $handlers)= @_;
  1614.     foreach my $tag_handlers (values %{$handlers->{xpath_handler}})
  1615.       { foreach my $handler_data (@$tag_handlers)
  1616.           { if( my $type= $handler_data->{test_on_text})
  1617.               { croak "string() condition not supported on twig_roots option"; }
  1618.           }
  1619.       }
  1620.     return;
  1621.   }
  1622.     
  1623.  
  1624. # just store the reference to the expat object in the twig
  1625. sub _twig_init
  1626.    { # warn " in _twig_init...\n"; # DEBUG handler
  1627.     
  1628.     my $p= shift;
  1629.     my $t=$p->{twig};
  1630.  
  1631.     if( $t->{twig_parsing} ) { croak "cannot reuse a twig that is already parsing"; }
  1632.     $t->{twig_parsing}=1;
  1633.  
  1634.     $t->{twig_parser}= $p; 
  1635.     if( $weakrefs) { weaken( $t->{twig_parser}); }
  1636.  
  1637.     # in case they had been created by a previous parse
  1638.     delete $t->{twig_dtd};
  1639.     delete $t->{twig_doctype};
  1640.     delete $t->{twig_xmldecl};
  1641.     delete $t->{twig_root};
  1642.  
  1643.     # if needed set the output filehandle
  1644.     $t->_set_fh_to_twig_output_fh();
  1645.     return;
  1646.   }
  1647.  
  1648. # uses eval to catch the parser's death
  1649. sub safe_parse
  1650.   { my $t= shift;
  1651.     eval { $t->parse( @_); } ;
  1652.     return $@ ? $t->_reset_twig &&  0 : $t;
  1653.   }
  1654.  
  1655. sub safe_parsefile
  1656.   { my $t= shift;
  1657.     eval { $t->parsefile( @_); } ;
  1658.     return $@ ? $t->_reset_twig : $t;
  1659.   }
  1660.  
  1661. # restore a twig in a proper state so it can be reused for a new parse
  1662. sub _reset_twig
  1663.   { my $t= shift;
  1664.     $t->{twig_parsing}= 0;
  1665.     delete $t->{twig_current};
  1666.     delete $t->{extra_data};
  1667.     delete $t->{twig_dtd};
  1668.     delete $t->{twig_in_pcdata};
  1669.     delete $t->{twig_in_cdata};
  1670.     delete $t->{twig_stored_space};
  1671.     delete $t->{twig_entity_list};
  1672.     $t->root->delete if( $t->root);
  1673.     delete $t->{root};
  1674.   }
  1675.  
  1676.  
  1677. sub _add_or_discard_stored_spaces
  1678.   { my $t= shift;
  1679.    
  1680.     my $current= $t->{twig_current} or return; # ugly hack, with ignore on, twig_current can disappear 
  1681.     if( $t->{twig_stored_spaces} || $t->{twig_preserve_space})
  1682.       { if( (exists $current->{'pcdata'}))
  1683.           { $current->{pcdata}.= $t->{twig_stored_spaces}; }
  1684.         else
  1685.           { my $current_gi= $XML::Twig::index2gi[$current->{'gi'}];
  1686.             if( ! defined( $t->{twig_space_policy}->{$current_gi}))
  1687.               { $t->{twig_space_policy}->{$current_gi}= _space_policy( $t, $current_gi); }
  1688.  
  1689.             if(    $t->{twig_space_policy}->{$current_gi} ||  ($t->{twig_stored_spaces}!~ m{\n})
  1690.                 || $t->{twig_preserve_space}
  1691.               )
  1692.               { _insert_pcdata( $t, $t->{twig_stored_spaces} ); }
  1693.             $t->{twig_stored_spaces}='';
  1694.  
  1695.           }
  1696.       }
  1697.     return;
  1698.   }
  1699.  
  1700. # the default twig handlers, which build the tree
  1701. sub _twig_start
  1702.    { # warn " in _twig_start...\n"; # DEBUG handler
  1703.     
  1704.     #foreach my $s (@_) { next if ref $s; warn "$s: ", is_utf8( $s) ? "has flag" : "FLAG NOT SET"; } # YYY
  1705.  
  1706.     my ($p, $gi, @att)= @_;
  1707.     my $t=$p->{twig};
  1708.  
  1709.     # empty the stored pcdata (space stored in case they are really part of 
  1710.     # a pcdata element) or stored it if the space policy dictades so
  1711.     # create a pcdata element with the spaces if need be
  1712.     _add_or_discard_stored_spaces( $t);
  1713.     my $parent= $t->{twig_current};
  1714.  
  1715.     # if we were parsing PCDATA then we exit the pcdata
  1716.     if( $t->{twig_in_pcdata})
  1717.       { $t->{twig_in_pcdata}= 0;
  1718.         delete $parent->{'twig_current'};
  1719.         $parent= $parent->{parent};
  1720.       }
  1721.  
  1722.     # if we choose to keep the encoding then we need to parse the tag
  1723.     if( my $func = $t->{parse_start_tag})
  1724.       { ($gi, @att)= &$func($p->original_string); }
  1725.     elsif( $t->{twig_entities_in_attribute})
  1726.       { 
  1727.        ($gi,@att)= _parse_start_tag( $p->recognized_string); 
  1728.          $t->{twig_entities_in_attribute}=0;
  1729.       }
  1730.  
  1731.     # if we are using an external DTD, we need to fill the default attributes
  1732.     if( $t->{twig_read_external_dtd}) { _fill_default_atts( $t, $gi, \@att); }
  1733.     
  1734.     # filter the input data if need be  
  1735.     if( my $filter= $t->{twig_input_filter})
  1736.       { $gi= $filter->( $gi);
  1737.         foreach my $att (@att) { $att= $filter->($att); } 
  1738.       }
  1739.  
  1740.     if( $t->{twig_map_xmlns}) { _replace_ns( $t, \$gi, \@att); }
  1741.  
  1742.     my $elt= $t->{twig_elt_class}->new( $gi);
  1743.     $elt->set_atts( @att);
  1744.  
  1745.     # now we can store the tag and atts
  1746.     my $context= { _tag => $gi, _elt => $elt, @att};
  1747.     if( $weakrefs) { weaken( $context->{_elt}); }
  1748.     push @{$t->{_twig_context_stack}}, $context;
  1749.  
  1750.     delete $parent->{'twig_current'} if( $parent);
  1751.     $t->{twig_current}= $elt;
  1752.     $elt->{'twig_current'}=1;
  1753.  
  1754.     if( $parent)
  1755.       { my $prev_sibling= $parent->{last_child};
  1756.         if( $prev_sibling) 
  1757.           { $prev_sibling->{next_sibling}=  $elt; 
  1758.             $elt->{prev_sibling}=$prev_sibling; if( $XML::Twig::weakrefs) { weaken( $elt->{prev_sibling});} ;
  1759.           }
  1760.  
  1761.         $elt->{parent}=$parent; if( $XML::Twig::weakrefs) { weaken( $elt->{parent});} ;
  1762.         unless( $parent->{first_child}) { $parent->{first_child}=  $elt; } 
  1763.          delete $parent->{empty}; $parent->{last_child}=$elt; if( $XML::Twig::weakrefs) { weaken( $parent->{last_child});} ;
  1764.       }
  1765.     else 
  1766.       { # processing root
  1767.         $t->set_root( $elt);
  1768.         # call dtd handler if need be
  1769.         $t->{twig_dtd_handler}->($t, $t->{twig_dtd})
  1770.           if( defined $t->{twig_dtd_handler});
  1771.       
  1772.         # set this so we can catch external entities
  1773.         # (the handler was modified during DTD processing)
  1774.         if( $t->{twig_default_print})
  1775.           { $p->setHandlers( Default => \&_twig_print); }
  1776.         elsif( $t->{twig_roots})
  1777.           { $p->setHandlers( Default => sub { return }); }
  1778.         else
  1779.           { $p->setHandlers( Default => \&_twig_default); }
  1780.       }
  1781.   
  1782.     $elt->{empty}=  $p->recognized_string=~ m{/\s*>$}s ? 1 : 0;
  1783.  
  1784.     $elt->{extra_data}= $t->{extra_data} if( $t->{extra_data});
  1785.     $t->{extra_data}='';
  1786.  
  1787.     # if the element is ID-ed then store that info
  1788.     my $id= $elt->{'att'}->{$ID};
  1789.     if( defined $id)
  1790.       { $t->{twig_id_list}->{$id}= $elt; 
  1791.         if( $weakrefs) { weaken( $t->{twig_id_list}->{$id}); }
  1792.       }
  1793.  
  1794.     # call user handler if need be
  1795.     if( $t->{twig_starttag_handlers})
  1796.       { # call all appropriate handlers
  1797.         my @handlers= _handler( $t, $t->{twig_starttag_handlers}, $gi);
  1798.     
  1799.         local $_= $elt;
  1800.     
  1801.         foreach my $handler ( @handlers)
  1802.           { $handler->($t, $elt) || last; }
  1803.         # call _all_ handler if needed
  1804.         if( my $all= $t->{twig_starttag_handlers}->{handlers}->{$ALL})
  1805.           { $all->($t, $elt); }
  1806.       }
  1807.  
  1808.     # check if the tag is in the list of tags to be ignored
  1809.     if( $t->{twig_ignore_elts_handlers})
  1810.       { my @handlers= _handler( $t, $t->{twig_ignore_elts_handlers}, $gi);
  1811.         # only the first handler counts, it contains the action (discard/print/string)
  1812.         if( @handlers) { my $action= shift @handlers; $t->ignore( $action); }
  1813.       }
  1814.  
  1815.     if( $elt->{'att'}->{'xml:space'} && (  $elt->{'att'}->{'xml:space'} eq 'preserve')) { $t->{twig_preserve_space}++; }
  1816.     
  1817.  
  1818.     return;
  1819.   }
  1820.  
  1821. sub _replace_ns
  1822.   { my( $t, $gi, $atts)= @_;
  1823.     foreach my $new_prefix ( $t->parser->new_ns_prefixes)
  1824.       { my $uri= $t->parser->expand_ns_prefix( $new_prefix);
  1825.         # replace the prefix if it is mapped
  1826.         if( !$t->{twig_keep_original_prefix} && (my $mapped_prefix= $t->{twig_map_xmlns}->{$uri}))
  1827.           { $new_prefix= $mapped_prefix; }
  1828.         # now put the namespace declaration back in the element
  1829.         if( $new_prefix eq '#default')
  1830.           { push @$atts, "xmlns" =>  $uri; } 
  1831.         else
  1832.           { push @$atts, "xmlns:$new_prefix" =>  $uri; } 
  1833.       }
  1834.  
  1835.     if( $t->{twig_keep_original_prefix})
  1836.       { # things become more complex: we need to find the original prefix
  1837.         # and store both prefixes
  1838.         my $ns_info= $t->_ns_info( $$gi);
  1839.         my $map_att;
  1840.         if( $ns_info->{mapped_prefix})
  1841.           { $$gi= "$ns_info->{mapped_prefix}:$$gi";
  1842.             $map_att->{$ns_info->{mapped_prefix}}= $ns_info->{prefix};
  1843.           }
  1844.         my $att_name=1;
  1845.         foreach( @$atts) 
  1846.           { if( $att_name) 
  1847.               { 
  1848.                 my $ns_info= $t->_ns_info( $_);
  1849.                 if( $ns_info->{mapped_prefix})
  1850.                   { $_= "$ns_info->{mapped_prefix}:$_";
  1851.                     $map_att->{$ns_info->{mapped_prefix}}= $ns_info->{prefix};
  1852.                   }
  1853.                 $att_name=0; 
  1854.               }
  1855.             else           
  1856.               {  $att_name=1; }
  1857.           }
  1858.         push @$atts, '#original_gi', $map_att if( $map_att);
  1859.       }
  1860.     else
  1861.       { $$gi= $t->_replace_prefix( $$gi); 
  1862.         my $att_name=1;
  1863.         foreach( @$atts) 
  1864.           { if( $att_name) { $_= $t->_replace_prefix( $_); $att_name=0; }
  1865.             else           {  $att_name=1; }
  1866.           }
  1867.       }
  1868.     return;
  1869.   }
  1870.  
  1871.  
  1872. # extract prefix, local_name, uri, mapped_prefix from a name
  1873. # will only work if called from a start or end tag handler
  1874. sub _ns_info
  1875.   { my( $t, $name)= @_;
  1876.     my $ns_info={};
  1877.     my $p= $t->parser;
  1878.     $ns_info->{uri}= $p->namespace( $name); 
  1879.     return $ns_info unless( $ns_info->{uri});
  1880.  
  1881.     $ns_info->{prefix}= _a_proper_ns_prefix( $p, $ns_info->{uri});
  1882.     $ns_info->{mapped_prefix}= $t->{twig_map_xmlns}->{$ns_info->{uri}} || $ns_info->{prefix};
  1883.  
  1884.     return $ns_info;
  1885.   }
  1886.     
  1887. sub _a_proper_ns_prefix
  1888.   { my( $p, $uri)= @_;
  1889.     foreach my $prefix ($p->current_ns_prefixes)
  1890.       { if( $p->expand_ns_prefix( $prefix) eq $uri)
  1891.           { return $prefix; }
  1892.       }
  1893.     return;
  1894.   }
  1895.  
  1896. sub _fill_default_atts
  1897.   { my( $t, $gi, $atts)= @_;
  1898.     my $dtd= $t->{twig_dtd};
  1899.     my $attlist= $dtd->{att}->{$gi};
  1900.     my %value= @$atts;
  1901.     foreach my $att (keys %$attlist)
  1902.       { if(   !exists( $value{$att}) 
  1903.             && exists( $attlist->{$att}->{default})
  1904.             && ( $attlist->{$att}->{default} ne '#IMPLIED')
  1905.           )
  1906.           { # the quotes are included in the default, so we need to remove them
  1907.             my $default_value= substr( $attlist->{$att}->{default}, 1, -1);
  1908.             push @$atts, $att, $default_value;
  1909.           }
  1910.       }
  1911.     return;
  1912.   }
  1913.  
  1914.  
  1915. # the default function to parse a start tag (in keep_encoding mode)
  1916. # can be overridden with the parse_start_tag method
  1917. # only works for 1-byte character sets
  1918. sub _parse_start_tag
  1919.   { my $string= shift;
  1920.     my( $gi, @atts);
  1921.  
  1922.     # get the gi (between < and the first space, / or > character)
  1923.     #if( $string=~ s{^<\s*([^\s>/]*)[\s>/]*}{}s)
  1924.     if( $string=~ s{^<\s*($REG_NAME)\s*[\s>/]}{}s)
  1925.       { $gi= $1; }
  1926.     else
  1927.       { croak "error parsing tag '$string'"; }
  1928.     while( $string=~ s{^([^\s=]*)\s*=\s*(["'])(.*?)\2\s*}{}s)
  1929.       { push @atts, $1, $3; }
  1930.     return $gi, @atts;
  1931.   }
  1932.  
  1933. sub set_root
  1934.   { my( $t, $elt)= @_;
  1935.     $t->{twig_root}= $elt;
  1936.     $elt->{twig}= $t;
  1937.     if( $weakrefs) { weaken(  $elt->{twig}); }
  1938.     return $t;
  1939.   }
  1940.  
  1941. sub _twig_end
  1942.    { # warn " in _twig_end...\n"; # DEBUG handler
  1943.     my ($p, $gi)  = @_;
  1944.  
  1945.     my $t=$p->{twig};
  1946.  
  1947.     if( $t->{twig_map_xmlns}) { $gi= $t->_replace_prefix( $gi); }
  1948.   
  1949.     _add_or_discard_stored_spaces( $t);
  1950.  
  1951.     # the new twig_current is the parent
  1952.     my $elt= $t->{twig_current};
  1953.     delete $elt->{'twig_current'};
  1954.  
  1955.     # if we were parsing PCDATA then we exit the pcdata too
  1956.     if( $t->{twig_in_pcdata})
  1957.       { $t->{twig_in_pcdata}= 0;
  1958.         $elt= $elt->{parent} if($elt->{parent});
  1959.         delete $elt->{'twig_current'};
  1960.       }
  1961.  
  1962.     # parent is the new current element
  1963.     my $parent= $elt->{parent};
  1964.     $t->{twig_current}= $parent;
  1965.  
  1966.     if( $parent)
  1967.       { $parent->{'twig_current'}=1;
  1968.         # twig_to_be_normalized
  1969.         if( $parent->{twig_to_be_normalized}) { $parent->normalize; $parent->{twig_to_be_normalized}=0; }
  1970.       }
  1971.  
  1972.     if( $t->{extra_data}) 
  1973.       { $elt->_set_extra_data_before_end_tag( $t->{extra_data});  
  1974.         $t->{extra_data}='';
  1975.       }
  1976.  
  1977.     if( $t->{twig_handlers})
  1978.       { # look for handlers
  1979.         my @handlers= _handler( $t, $t->{twig_handlers}, $gi);
  1980.         
  1981.         if( $t->{twig_tdh})
  1982.           { if( @handlers) { push @{$t->{twig_handlers_to_trigger}}, [ $elt, \@handlers ]; }
  1983.             if( my $all= $t->{twig_handlers}->{handlers}->{$ALL}) 
  1984.               { push @{$t->{twig_handlers_to_trigger}}, [ $elt, [$all] ]; }
  1985.           }
  1986.         else
  1987.           {
  1988.             local $_= $elt; # so we can use $_ in the handlers
  1989.     
  1990.             foreach my $handler ( @handlers)
  1991.               { $handler->($t, $elt) || last; }
  1992.             # call _all_ handler if needed
  1993.             if( my $all= $t->{twig_handlers}->{handlers}->{$ALL})
  1994.               { $all->($t, $elt); }
  1995.           }
  1996.       }
  1997.  
  1998.     # if twig_roots is set for the element then set appropriate handler
  1999.     if(  $t->{twig_root_depth} and ($p->depth == $t->{twig_root_depth}) )
  2000.       { if( $t->{twig_default_print})
  2001.           { # select the proper fh (and store the currently selected one)
  2002.             $t->_set_fh_to_twig_output_fh(); 
  2003.             if( $t->{twig_keep_encoding})
  2004.               { $p->setHandlers( %twig_handlers_roots_print_original); }
  2005.             else
  2006.               { $p->setHandlers( %twig_handlers_roots_print); }
  2007.           }
  2008.         else
  2009.           { $p->setHandlers( %twig_handlers_roots); }
  2010.       }
  2011.  
  2012.     if( $elt->{'att'}->{'xml:space'} && (  $elt->{'att'}->{'xml:space'} eq 'preserve')) { $t->{twig_preserve_space}--; }
  2013.  
  2014.     pop @{$t->{_twig_context_stack}};
  2015.     return;
  2016.   }
  2017.  
  2018. sub _trigger_tdh
  2019.   { my( $t)= @_;
  2020.  
  2021.     if( @{$t->{twig_handlers_to_trigger}})
  2022.       { my @handlers_to_trigger_now= sort { $a->[0]->cmp( $b->[0]) } @{$t->{twig_handlers_to_trigger}};
  2023.         foreach my $elt_handlers (@handlers_to_trigger_now)
  2024.           { my( $handled_elt, $handlers_to_trigger)= @$elt_handlers;
  2025.             foreach my $handler ( @$handlers_to_trigger) 
  2026.               { local $_= $handled_elt; $handler->($t, $handled_elt) || last; }
  2027.           }
  2028.       }
  2029.     return;
  2030.   }
  2031.  
  2032. # return the list of handler that can be activated for an element 
  2033. # (either of CODE ref's or 1's for twig_roots)
  2034.  
  2035. sub _handler
  2036.   { my( $t, $handlers, $gi)= @_;
  2037.  
  2038.     my @found_handlers=();
  2039.     my $found_handler;
  2040.  
  2041.     foreach my $handler ( map { @$_ } grep { $_ } $handlers->{xpath_handler}->{$gi}, $handlers->{xpath_handler}->{'*'})
  2042.       {  my $trigger= $handler->{trigger};
  2043.          if( my $found_path= $trigger->( $t->{_twig_context_stack}))
  2044.           { my $found_handler= $handler->{handler};
  2045.             push @found_handlers, $found_handler; 
  2046.           }
  2047.       }
  2048.  
  2049.     # if no handler found call default handler if defined
  2050.     if( !@found_handlers && defined $handlers->{handlers}->{$DEFAULT})
  2051.       { push @found_handlers, $handlers->{handlers}->{$DEFAULT}; }
  2052.  
  2053.     if( @found_handlers and $t->{twig_do_not_chain_handlers}) 
  2054.       { @found_handlers= ($found_handlers[0]); }
  2055.  
  2056.     return @found_handlers; # empty if no handler found
  2057.  
  2058.   }
  2059.  
  2060.  
  2061. sub _replace_prefix
  2062.   { my( $t, $name)= @_;
  2063.     my $p= $t->parser;
  2064.     my $uri= $p->namespace( $name);
  2065.     # try to get the namespace from default if none is found (for attributes)
  2066.     # this should probably be an option
  2067.     if( !$uri and( $name!~/^xml/)) { $uri= $p->expand_ns_prefix( '#default'); }
  2068.     if( $uri)
  2069.       { if (my $mapped_prefix= $t->{twig_map_xmlns}->{$uri})
  2070.           { return "$mapped_prefix:$name"; }
  2071.         else
  2072.           { my $prefix= _a_proper_ns_prefix( $p, $uri);
  2073.             if( $prefix eq '#default') { $prefix=''; }
  2074.             return $prefix ? "$prefix:$name" : $name; 
  2075.           }
  2076.       }
  2077.     else
  2078.       { return $name; }
  2079.   }
  2080.  
  2081.  
  2082. sub _twig_char
  2083.    { # warn " in _twig_char...\n"; # DEBUG handler
  2084.     
  2085.     my ($p, $string)= @_;
  2086.     my $t=$p->{twig}; 
  2087.  
  2088.     if( $t->{twig_keep_encoding})
  2089.       { if( !$t->{twig_in_cdata})
  2090.           { $string= $p->original_string(); }
  2091.         else
  2092.           { 
  2093.             use bytes; # > perl 5.5
  2094.             if( length( $string) < 1024)
  2095.               { $string= $p->original_string(); }
  2096.             else
  2097.               { #warn "dodgy case";
  2098.                 # TODO original_string does not hold the entire string, but $string is wrong
  2099.                 # I believe due to a bug in XML::Parser
  2100.                 # for now, we use the original string, even if it means that it's been converted to utf8
  2101.               }
  2102.           }
  2103.       }
  2104.  
  2105.     if( $t->{twig_input_filter}) { $string= $t->{twig_input_filter}->( $string); }
  2106.     if( $t->{twig_char_handler}) { $string= $t->{twig_char_handler}->( $string); }
  2107.  
  2108.     my $elt= $t->{twig_current};
  2109.  
  2110.     if(    $t->{twig_in_cdata})
  2111.       { # text is the continuation of a previously created cdata
  2112.         $elt->{cdata}.=  $t->{twig_stored_spaces} . $string;
  2113.       } 
  2114.     elsif( $t->{twig_in_pcdata})
  2115.       { # text is the continuation of a previously created pcdata
  2116.         if( $t->{extra_data})
  2117.           { $elt->_push_extra_data_in_pcdata( $t->{extra_data}, length( $elt->{pcdata}));
  2118.             $t->{extra_data}='';
  2119.           }
  2120.         $elt->{pcdata}.=  $string; 
  2121.       } 
  2122.     else
  2123.       { # text is just space, which might be discarded later
  2124.         if( $string=~/\A\s*\Z/s)
  2125.           { 
  2126.             if( $t->{extra_data})
  2127.               { # we got extra data (comment, pi), lets add the spaces to it
  2128.                 $t->{extra_data} .= $string; 
  2129.               }
  2130.             else
  2131.               { # no extra data, just store the spaces
  2132.                 $t->{twig_stored_spaces}.= $string;
  2133.               }
  2134.           } 
  2135.         else
  2136.           { my $new_elt= _insert_pcdata( $t, $t->{twig_stored_spaces}.$string);
  2137.             delete $elt->{'twig_current'};
  2138.             $new_elt->{'twig_current'}=1;
  2139.             $t->{twig_current}= $new_elt;
  2140.             $t->{twig_in_pcdata}=1;
  2141.             if( $t->{extra_data})
  2142.               { $new_elt->_push_extra_data_in_pcdata( $t->{extra_data}, 0);
  2143.                 $t->{extra_data}='';
  2144.               }
  2145.           }
  2146.       }
  2147.     return; 
  2148.   }
  2149.  
  2150. sub _twig_cdatastart
  2151.    { # warn " in _twig_cdatastart...\n"; # DEBUG handler
  2152.     
  2153.     my $p= shift;
  2154.     my $t=$p->{twig};
  2155.  
  2156.     $t->{twig_in_cdata}=1;
  2157.     my $cdata=  $t->{twig_elt_class}->new( $CDATA);
  2158.     my $twig_current= $t->{twig_current};
  2159.  
  2160.     if( $t->{twig_in_pcdata})
  2161.       { # create the node as a sibling of the PCDATA
  2162.         $cdata->{prev_sibling}=$twig_current; if( $XML::Twig::weakrefs) { weaken( $cdata->{prev_sibling});} ;
  2163.         $twig_current->{next_sibling}=  $cdata;
  2164.         my $parent= $twig_current->{parent};
  2165.         $cdata->{parent}=$parent; if( $XML::Twig::weakrefs) { weaken( $cdata->{parent});} ;
  2166.          delete $parent->{empty}; $parent->{last_child}=$cdata; if( $XML::Twig::weakrefs) { weaken( $parent->{last_child});} ;
  2167.         $t->{twig_in_pcdata}=0;
  2168.       }
  2169.     else
  2170.       { # we have to create a PCDATA element if we need to store spaces
  2171.         if( $t->_space_policy($XML::Twig::index2gi[$twig_current->{'gi'}]) && $t->{twig_stored_spaces})
  2172.           { _insert_pcdata( $t, $t->{twig_stored_spaces}); }
  2173.         $t->{twig_stored_spaces}='';
  2174.       
  2175.         # create the node as a child of the current element      
  2176.         $cdata->{parent}=$twig_current; if( $XML::Twig::weakrefs) { weaken( $cdata->{parent});} ;
  2177.         if( my $prev_sibling= $twig_current->{last_child})
  2178.           { $cdata->{prev_sibling}=$prev_sibling; if( $XML::Twig::weakrefs) { weaken( $cdata->{prev_sibling});} ;
  2179.             $prev_sibling->{next_sibling}=  $cdata;
  2180.           }
  2181.         else
  2182.           { $twig_current->{first_child}=  $cdata; }
  2183.          delete $twig_current->{empty}; $twig_current->{last_child}=$cdata; if( $XML::Twig::weakrefs) { weaken( $twig_current->{last_child});} ;
  2184.       
  2185.       }
  2186.  
  2187.     delete $twig_current->{'twig_current'};
  2188.     $t->{twig_current}= $cdata;
  2189.     $cdata->{'twig_current'}=1;
  2190.     if( $t->{extra_data}) { $cdata->set_extra_data( $t->{extra_data}); $t->{extra_data}='' };
  2191.     return;
  2192.   }
  2193.  
  2194. sub _twig_cdataend
  2195.    { # warn " in _twig_cdataend...\n"; # DEBUG handler
  2196.     
  2197.     my $p= shift;
  2198.     my $t=$p->{twig};
  2199.  
  2200.     $t->{twig_in_cdata}=0;
  2201.  
  2202.     my $elt= $t->{twig_current};
  2203.     delete $elt->{'twig_current'};
  2204.     my $cdata= $elt->{cdata};
  2205.     $elt->_set_cdata( $cdata);
  2206.  
  2207.     push @{$t->{_twig_context_stack}}, { _tag => $CDATA };
  2208.  
  2209.     if( $t->{twig_handlers})
  2210.       { # look for handlers
  2211.         my @handlers= _handler( $t, $t->{twig_handlers}, $CDATA);
  2212.         local $_= $elt; # so we can use $_ in the handlers
  2213.         foreach my $handler ( @handlers) { $handler->($t, $elt) || last; }
  2214.       }
  2215.  
  2216.     pop @{$t->{_twig_context_stack}};
  2217.  
  2218.     $elt= $elt->{parent};
  2219.     $t->{twig_current}= $elt;
  2220.     $elt->{'twig_current'}=1;
  2221.  
  2222.     $t->{twig_long_cdata}=0;
  2223.     return;
  2224.   }
  2225.  
  2226. sub _pi_elt_handlers
  2227.   { my( $t, $pi)= @_;
  2228.     my $pi_handlers= $t->{twig_handlers}->{pi_handlers} || return;
  2229.     foreach my $handler ( $pi_handlers->{$pi->{target}}, $pi_handlers->{''})
  2230.       { if( $handler) { local $_= $pi; $handler->( $t, $pi) || last; } }
  2231.   }
  2232.  
  2233. sub _pi_text_handler
  2234.   { my( $t, $target, $data)= @_;
  2235.     if( my $handler= $t->{twig_handlers}->{pi_handlers}->{$target})
  2236.       { return $handler->( $t, $target, $data); }
  2237.     if( my $handler= $t->{twig_handlers}->{pi_handlers}->{''})
  2238.       { return $handler->( $t, $target, $data); }
  2239.     return defined( $data) && $data ne ''  ? "<?$target $data?>" : "<?$target?>" ;
  2240.   }
  2241.  
  2242. sub _comment_elt_handler
  2243.   { my( $t, $comment)= @_; 
  2244.     if( my $handler= $t->{twig_handlers}->{handlers}->{$COMMENT})
  2245.       { local $_= $comment; $handler->($t, $comment); }
  2246.   }
  2247.  
  2248. sub _comment_text_handler
  2249.   { my( $t, $comment)= @_; 
  2250.     if( my $handler= $t->{twig_handlers}->{handlers}->{$COMMENT})
  2251.       { $comment= $handler->($t, $comment); 
  2252.         if( !defined $comment || $comment eq '') { return ''; }
  2253.       }
  2254.     return "<!--$comment-->";
  2255.   }
  2256.  
  2257.  
  2258.  
  2259. sub _twig_comment
  2260.    { # warn " in _twig_comment...\n"; # DEBUG handler
  2261.     
  2262.     my( $p, $comment_text)= @_;
  2263.     my $t=$p->{twig};
  2264.  
  2265.     if( $t->{twig_keep_encoding}) { $comment_text= substr( $p->original_string(), 4, -3); }
  2266.     
  2267.     $t->_twig_pi_comment( $p, $COMMENT, $t->{twig_keep_comments}, $t->{twig_process_comments},
  2268.                           '_set_comment', '_comment_elt_handler', '_comment_text_handler', $comment_text
  2269.                         );
  2270.     return;
  2271.   }
  2272.  
  2273. sub _twig_pi
  2274.    { # warn " in _twig_pi...\n"; # DEBUG handler
  2275.     
  2276.     my( $p, $target, $data)= @_;
  2277.     my $t=$p->{twig};
  2278.  
  2279.     if( $t->{twig_keep_encoding}) 
  2280.       { my $pi_text= substr( $p->original_string(), 2, -2); 
  2281.         ($target, $data)= split( /\s+/, $pi_text, 2);
  2282.       }
  2283.  
  2284.     $t->_twig_pi_comment( $p, $PI, $t->{twig_keep_pi}, $t->{twig_process_pi},
  2285.                           '_set_pi', '_pi_elt_handlers', '_pi_text_handler', $target, $data
  2286.                         );
  2287.     return;
  2288.   }
  2289.  
  2290. sub _twig_pi_comment
  2291.   { my( $t, $p, $type, $keep, $process, $set, $elt_handler, $text_handler, @parser_args)= @_;
  2292.  
  2293.     if( $t->{twig_input_filter})
  2294.           { foreach my $arg (@parser_args) { $arg= $t->{twig_input_filter}->( $arg); } }
  2295.           
  2296.     # if pi/comments are to be kept then we piggiback them to the current element
  2297.     if( $keep)
  2298.       { # first add spaces
  2299.         if( $t->{twig_stored_spaces})
  2300.               { $t->{extra_data}.= $t->{twig_stored_spaces};
  2301.                 $t->{twig_stored_spaces}= '';
  2302.               }
  2303.  
  2304.         my $extra_data= $t->$text_handler( @parser_args);
  2305.         $t->{extra_data}.= $extra_data;
  2306.  
  2307.       }
  2308.     elsif( $process)
  2309.       {
  2310.         my $twig_current= $t->{twig_current}; # defined unless we are outside of the root
  2311.  
  2312.         my $elt= $t->{twig_elt_class}->new( $type);
  2313.         $elt->$set( @parser_args);
  2314.         if( $t->{extra_data}) 
  2315.           { $elt->set_extra_data( $t->{extra_data});
  2316.             $t->{extra_data}='';
  2317.           }
  2318.  
  2319.         unless( $t->root) 
  2320.           { $t->_add_cpi_outside_of_root( leading_cpi => $elt);
  2321.           }
  2322.         elsif( $t->{twig_in_pcdata})
  2323.           { # create the node as a sibling of the PCDATA
  2324.             $elt->paste_after( $twig_current);
  2325.             $t->{twig_in_pcdata}=0;
  2326.           }
  2327.         elsif( $twig_current)
  2328.           { # we have to create a PCDATA element if we need to store spaces
  2329.             if( $t->_space_policy($XML::Twig::index2gi[$twig_current->{'gi'}]) && $t->{twig_stored_spaces})
  2330.               { _insert_pcdata( $t, $t->{twig_stored_spaces}); }
  2331.             $t->{twig_stored_spaces}='';
  2332.             # create the node as a child of the current element
  2333.             $elt->paste_last_child( $twig_current);
  2334.           }
  2335.         else
  2336.           { $t->_add_cpi_outside_of_root( trailing_cpi => $elt); }
  2337.  
  2338.         if( $twig_current)
  2339.           { delete $twig_current->{'twig_current'};
  2340.             my $parent= $elt->{parent};
  2341.             $t->{twig_current}= $parent;
  2342.             $parent->{'twig_current'}=1;
  2343.           }
  2344.  
  2345.         $t->$elt_handler( $elt);
  2346.       }
  2347.  
  2348.   }
  2349.     
  2350.  
  2351. # add a comment or pi before the first element
  2352. sub _add_cpi_outside_of_root
  2353.   { my($t, $type, $elt)= @_; # $type is 'leading_cpi' or 'trailing_cpi'
  2354.     $t->{$type} ||= $t->{twig_elt_class}->new( '#CPI');
  2355.     # create the node as a child of the current element
  2356.     $elt->paste_last_child( $t->{$type});
  2357.     return $t;
  2358.   }
  2359.   
  2360. sub _twig_final
  2361.    { # warn " in _twig_final...\n"; # DEBUG handler
  2362.     
  2363.     my $p= shift;
  2364.     my $t= $p->isa( 'XML::Twig') ? $p : $p->{twig};
  2365.  
  2366.     # store trailing data
  2367.     if( $t->{extra_data}) { $t->{trailing_cpi_text} = $t->{extra_data}; $t->{extra_data}=''; }
  2368.     $t->{trailing_spaces}= $t->{twig_stored_spaces} || ''; 
  2369.     my $s=  $t->{twig_stored_spaces}; $s=~s{\n}{\\n}g;
  2370.     if( $t->{twig_stored_spaces}) { my $s=  $t->{twig_stored_spaces}; }
  2371.  
  2372.     # restore the selected filehandle if needed
  2373.     $t->_set_fh_to_selected_fh();
  2374.  
  2375.     $t->_trigger_tdh if( $t->{twig_tdh});
  2376.  
  2377.     select $t->{twig_original_selected_fh} if($t->{twig_original_selected_fh}); # probably dodgy
  2378.  
  2379.     if( exists $t->{twig_autoflush_data})
  2380.       { my @args;
  2381.         push @args,  $t->{twig_autoflush_data}->{fh}      if( $t->{twig_autoflush_data}->{fh});
  2382.         push @args,  @{$t->{twig_autoflush_data}->{args}} if( $t->{twig_autoflush_data}->{args});
  2383.         $t->flush( @args);
  2384.         delete $t->{twig_autoflush_data};
  2385.         $t->root->delete;
  2386.       }
  2387.  
  2388.     # tries to clean-up (probably not very well at the moment)
  2389.     #undef $p->{twig};
  2390.     undef $t->{twig_parser};
  2391.     delete $t->{twig_parsing};
  2392.     @{$t}{ qw( twig_parser twig_parsing _twig_context_stack twig_current) }=();
  2393.  
  2394.     return $t;
  2395.   }
  2396.  
  2397. sub _insert_pcdata
  2398.   { my( $t, $string)= @_;
  2399.     # create a new PCDATA element
  2400.     my $parent= $t->{twig_current};    # always defined
  2401.     my $elt=  $t->{twig_elt_class}->new( $PCDATA);
  2402.     $elt->_set_pcdata( $string);
  2403.     my $prev_sibling= $parent->{last_child};
  2404.     if( $prev_sibling) 
  2405.       { $prev_sibling->{next_sibling}=  $elt; 
  2406.         $elt->{prev_sibling}=$prev_sibling; if( $XML::Twig::weakrefs) { weaken( $elt->{prev_sibling});} ;
  2407.       }
  2408.     else
  2409.       { $parent->{first_child}=  $elt; }
  2410.  
  2411.     $elt->{parent}=$parent; if( $XML::Twig::weakrefs) { weaken( $elt->{parent});} ;
  2412.      delete $parent->{empty}; $parent->{last_child}=$elt; if( $XML::Twig::weakrefs) { weaken( $parent->{last_child});} ;
  2413.     $t->{twig_stored_spaces}='';
  2414.     return $elt;
  2415.   }
  2416.  
  2417. sub _space_policy
  2418.   { my( $t, $gi)= @_;
  2419.     my $policy;
  2420.     $policy=0 if( $t->{twig_discard_spaces});
  2421.     $policy=1 if( $t->{twig_keep_spaces});
  2422.     $policy=1 if( $t->{twig_keep_spaces_in}
  2423.                && $t->{twig_keep_spaces_in}->{$gi});
  2424.     $policy=0 if( $t->{twig_discard_spaces_in} 
  2425.                && $t->{twig_discard_spaces_in}->{$gi});
  2426.     return $policy;
  2427.   }
  2428.  
  2429.  
  2430. sub _twig_entity
  2431.    { # warn " in _twig_entity...\n"; # DEBUG handler
  2432.     my( $p, $name, $val, $sysid, $pubid, $ndata, $param)= @_;
  2433.     my $t=$p->{twig};
  2434.  
  2435.     #{ no warnings; my $base= $p->base; warn "_twig_entity called: expand: '$t->{twig_expand_external_ents}', base: '$base', name: '$name', val: '$val', sysid: '$sysid', pubid: '$pubid', ndata: '$ndata', param: '$param'\n";}
  2436.  
  2437.     my $missing_entity=0;
  2438.  
  2439.     if( $sysid) 
  2440.       { if($ndata)
  2441.           { if( ! -f _based_filename( $sysid, $p->base)) { $missing_entity= 1; }
  2442.           }
  2443.         else
  2444.           { if( $t->{twig_expand_external_ents})
  2445.               { $val= eval { _slurp_uri( $sysid, $p->base) };
  2446.                 if( ! defined $val) 
  2447.                   { if( $t->{twig_extern_ent_nofail}) 
  2448.                       { $missing_entity= 1; }
  2449.                     else
  2450.                       { _croak( "cannot load SYSTEM entity '$name' from '$sysid': $@", 3); }
  2451.                   }
  2452.               }
  2453.           }
  2454.       }
  2455.  
  2456.     my $ent=XML::Twig::Entity->new( $name, $val, $sysid, $pubid, $ndata, $param);
  2457.     if( $missing_entity) { $t->{twig_missing_system_entities}->{$name}= $ent; }
  2458.  
  2459.     my $entity_list= $t->entity_list;
  2460.     if( $entity_list) { $entity_list->add( $ent); }
  2461.  
  2462.     if( $parser_version > 2.27) 
  2463.       { # this is really ugly, but with some versions of XML::Parser the value 
  2464.         # of the entity is not properly returned by the default handler
  2465.         my $ent_decl= $ent->text;
  2466.         if( $t->{twig_keep_encoding})
  2467.           { if( defined $ent->{val} && ($ent_decl !~ /["']/))
  2468.               { my $val=  $ent->{val};
  2469.                 $ent_decl .= $val =~ /"/ ? qq{'$val' } : qq{"$val" }; 
  2470.               }
  2471.             # for my solaris box (perl 5.6.1, XML::Parser 2.31, expat?)
  2472.             $t->{twig_doctype}->{internal}=~ s{<!ENTITY\s+$name\s+$}{substr( $ent_decl, 0, -1)}e;
  2473.           }
  2474.         $t->{twig_doctype}->{internal} .= $ent_decl 
  2475.           unless( $t->{twig_doctype}->{internal}=~ m{<!ENTITY\s+$name\s+});
  2476.       }
  2477.  
  2478.     return;
  2479.   }
  2480.  
  2481.  
  2482. sub _twig_extern_ent
  2483.    { # warn " in _twig_extern_ent...I (", $_[0]->original_string, ")\n"; # DEBUG handler
  2484.     my( $p, $base, $sysid, $pubid)= @_;
  2485.     my $t= $p->{twig};
  2486.     if( $t->{twig_no_expand}) 
  2487.       { my $ent_name= $t->{twig_keep_encoding} ? $p->original_string : $p->recognized_string;
  2488.         _twig_insert_ent( $t, $ent_name);
  2489.         return '';
  2490.       }
  2491.     my $ent_content= eval { $t->{twig_ext_ent_handler}->( $p, $base, $sysid) };
  2492.     if( ! defined $ent_content)
  2493.       { 
  2494.         my $ent_name = $p->recognized_string;
  2495.         my $file     =  _based_filename( $sysid, $base);
  2496.         my $error_message= "cannot expand $ent_name - cannot load '$file'";
  2497.         if( $t->{twig_extern_ent_nofail}) { return "<!-- $error_message -->"; }
  2498.         else                              { _croak( $error_message);   }
  2499.       }
  2500.     return $ent_content; 
  2501.   }
  2502.  
  2503. # I use this so I can change the $Carp::CarpLevel (which determines how many call frames to skip when reporting an error)
  2504. sub _croak
  2505.   { my( $message, $level)= @_;
  2506.     $Carp::CarpLevel= $level || 0;
  2507.     croak $message;
  2508.   }
  2509.  
  2510. sub _twig_xmldecl
  2511.    { # warn " in _twig_xmldecl...\n"; # DEBUG handler
  2512.     
  2513.     my $p= shift;
  2514.     my $t=$p->{twig};
  2515.     $t->{twig_xmldecl}||={};                 # could have been set by set_output_encoding
  2516.     $t->{twig_xmldecl}->{version}= shift;
  2517.     $t->{twig_xmldecl}->{encoding}= shift; 
  2518.     $t->{twig_xmldecl}->{standalone}= shift;
  2519.     return;
  2520.   }
  2521.  
  2522. sub _twig_doctype
  2523.    { # warn " in _twig_doctype...\n"; # DEBUG handler
  2524.     my( $p, $name, $sysid, $pub, $internal)= @_;
  2525.     my $t=$p->{twig};
  2526.     $t->{twig_doctype}||= {};                   # create 
  2527.     $t->{twig_doctype}->{name}= $name;          # always there
  2528.     $t->{twig_doctype}->{sysid}= $sysid;        #  
  2529.     $t->{twig_doctype}->{pub}= $pub;            #  
  2530.  
  2531.     # now let's try to cope with XML::Parser 2.28 and above
  2532.     if( $parser_version > 2.27)
  2533.       { @saved_default_handler= $p->setHandlers( Default     => \&_twig_store_internal_dtd,
  2534.                                                  Entity      => \&_twig_entity,
  2535.                                                );
  2536.       $p->setHandlers( DoctypeFin  => \&_twig_stop_storing_internal_dtd);
  2537.       $t->{twig_doctype}->{internal}='';
  2538.       }
  2539.     else            
  2540.       # for XML::Parser before 2.28
  2541.       { $internal||='';
  2542.         $internal=~ s{^\s*\[}{}; 
  2543.         $internal=~ s{]\s*$}{}; 
  2544.         $t->{twig_doctype}->{internal}=$internal; 
  2545.       }
  2546.  
  2547.     # now check if we want to get the DTD info
  2548.     if( $t->{twig_read_external_dtd} && $sysid)
  2549.       { # let's build a fake document with an internal DTD
  2550.         my $dtd=  "<!DOCTYPE $name [" . _slurp_uri( $sysid) .  "]><$name/>";
  2551.        
  2552.         $t->save_global_state();            # save the globals (they will be reset by the following new)  
  2553.         my $t_dtd= XML::Twig->new( load_DTD => 1, ParseParamEnt => 1, error_context => $t->{ErrorContext} || 0);          # create a temp twig
  2554.         $t_dtd->parse( $dtd);               # parse it
  2555.         $t->{twig_dtd}= $t_dtd->{twig_dtd}; # grab the dtd info
  2556.         #$t->{twig_dtd_is_external}=1;
  2557.         $t->entity_list->_add_list( $t_dtd->entity_list) if( $t_dtd->entity_list); # grab the entity info
  2558.         $t->restore_global_state();
  2559.       }
  2560.     return;
  2561.   }
  2562.  
  2563. sub _twig_element
  2564.    { # warn " in _twig_element...\n"; # DEBUG handler
  2565.     
  2566.     my( $p, $name, $model)= @_;
  2567.     my $t=$p->{twig};
  2568.     $t->{twig_dtd}||= {};                      # may create the dtd 
  2569.     $t->{twig_dtd}->{model}||= {};             # may create the model hash 
  2570.     $t->{twig_dtd}->{elt_list}||= [];          # ordered list of elements 
  2571.     push @{$t->{twig_dtd}->{elt_list}}, $name; # store the elt
  2572.     $t->{twig_dtd}->{model}->{$name}= $model;  # store the model
  2573.     if( ($parser_version > 2.27) && ($t->{twig_doctype}->{internal}=~ m{(^|>)\s*$}) ) 
  2574.       { my $text= $XML::Twig::Elt::keep_encoding ? $p->original_string : $p->recognized_string; 
  2575.         unless( $text)
  2576.           { # this version of XML::Parser does not return the text in the *_string method
  2577.             # we need to rebuild it
  2578.             $text= "<!ELEMENT $name $model>";
  2579.           }
  2580.         $t->{twig_doctype}->{internal} .= $text;
  2581.       }
  2582.     return;
  2583.   }
  2584.  
  2585. sub _twig_attlist
  2586.    { # warn " in _twig_attlist...\n"; # DEBUG handler
  2587.     
  2588.     my( $p, $gi, $att, $type, $default, $fixed)= @_;
  2589.     #warn "in attlist: gi: '$gi', att: '$att', type: '$type', default: '$default', fixed: '$fixed'\n";
  2590.     my $t=$p->{twig};
  2591.     $t->{twig_dtd}||= {};                      # create dtd if need be 
  2592.     $t->{twig_dtd}->{$gi}||= {};               # create elt if need be 
  2593.     #$t->{twig_dtd}->{$gi}->{att}||= {};        # create att if need be 
  2594.     if( ($parser_version > 2.27) && ($t->{twig_doctype}->{internal}=~ m{(^|>)\s*$}) ) 
  2595.       { my $text= $XML::Twig::Elt::keep_encoding ? $p->original_string : $p->recognized_string; 
  2596.         unless( $text)
  2597.           { # this version of XML::Parser does not return the text in the *_string method
  2598.             # we need to rebuild it
  2599.             my $att_decl="$att $type";
  2600.             $att_decl .= " #FIXED"   if( $fixed);
  2601.             $att_decl .= " $default" if( defined $default);
  2602.             # 2 cases: there is already an attlist on that element or not
  2603.             if( $t->{twig_dtd}->{att}->{$gi})
  2604.               { # there is already an attlist, add to it
  2605.                 $t->{twig_doctype}->{internal}=~ s{(<!ATTLIST\s*$gi )(.*?)\n?>}
  2606.                                                   { "$1$2\n" . ' ' x length( $1) . "$att_decl\n>"}es;
  2607.               }
  2608.             else
  2609.               { # create the attlist
  2610.                  $t->{twig_doctype}->{internal}.= "<!ATTLIST $gi $att_decl>"
  2611.               }
  2612.           }
  2613.       }
  2614.     $t->{twig_dtd}->{att}->{$gi}->{$att}= {} ;
  2615.     $t->{twig_dtd}->{att}->{$gi}->{$att}->{type}= $type; 
  2616.     $t->{twig_dtd}->{att}->{$gi}->{$att}->{default}= $default if( defined $default);
  2617.     $t->{twig_dtd}->{att}->{$gi}->{$att}->{fixed}= $fixed; 
  2618.     return;
  2619.   }
  2620.  
  2621. sub _twig_default
  2622.    { # warn " in _twig_default...\n"; # DEBUG handler
  2623.     
  2624.     my( $p, $string)= @_;
  2625.     
  2626.     my $t= $p->{twig};
  2627.    
  2628.     # we need to process the data in 2 cases: entity, or spaces after the closing tag
  2629.  
  2630.     # after the closing tag (no twig_current and root has been created)
  2631.     if(  ! $t->{twig_current} && $t->{twig_root} && $string=~ m{^\s+$}m) { $t->{twig_stored_spaces} .= $string; }
  2632.  
  2633.     # process only if we have an entity
  2634.     if( $string=~ m{^&([^;]*);$})
  2635.       { # the entity has to be pure pcdata, or we have a problem
  2636.         if( ($p->original_string=~ m{^<}) && ($p->original_string=~ m{>$}) ) 
  2637.           { # string is a tag, entity is in an attribute
  2638.             $t->{twig_entities_in_attribute}=1 if( $t->{twig_do_not_escape_amp_in_atts});
  2639.           }
  2640.         else
  2641.           { my $ent;
  2642.             if( $t->{twig_keep_encoding}) 
  2643.               { _twig_char( $p, $string); 
  2644.                 $ent= substr( $string, 1, -1);
  2645.               }
  2646.             else
  2647.               { $ent= _twig_insert_ent( $t, $string); 
  2648.               }
  2649.  
  2650.             return $ent;
  2651.           }
  2652.         return
  2653.       }
  2654.   }
  2655.     
  2656. sub _twig_insert_ent
  2657.   { 
  2658.     my( $t, $string)=@_;
  2659.  
  2660.     my $twig_current= $t->{twig_current};
  2661.  
  2662.     my $ent=  $t->{twig_elt_class}->new( $ENT);
  2663.     $ent->{ent}=  $string;
  2664.  
  2665.     _add_or_discard_stored_spaces( $t);
  2666.     
  2667.     if( $t->{twig_in_pcdata})
  2668.       { # create the node as a sibling of the #PCDATA
  2669.  
  2670.         $ent->{prev_sibling}=$twig_current; if( $XML::Twig::weakrefs) { weaken( $ent->{prev_sibling});} ;
  2671.         $twig_current->{next_sibling}=  $ent;
  2672.         my $parent= $twig_current->{parent};
  2673.         $ent->{parent}=$parent; if( $XML::Twig::weakrefs) { weaken( $ent->{parent});} ;
  2674.          delete $parent->{empty}; $parent->{last_child}=$ent; if( $XML::Twig::weakrefs) { weaken( $parent->{last_child});} ;
  2675.         # the twig_current is now the parent
  2676.         delete $twig_current->{'twig_current'};
  2677.         $t->{twig_current}= $parent;
  2678.         # we left pcdata
  2679.         $t->{twig_in_pcdata}=0;
  2680.       }
  2681.     else
  2682.       { # create the node as a child of the current element
  2683.         $ent->{parent}=$twig_current; if( $XML::Twig::weakrefs) { weaken( $ent->{parent});} ;
  2684.         if( my $prev_sibling= $twig_current->{last_child})
  2685.           { $ent->{prev_sibling}=$prev_sibling; if( $XML::Twig::weakrefs) { weaken( $ent->{prev_sibling});} ;
  2686.             $prev_sibling->{next_sibling}=  $ent;
  2687.           }
  2688.         else
  2689.           { if( $twig_current) { $twig_current->{first_child}=  $ent; } }
  2690.         if( $twig_current) {  delete $twig_current->{empty}; $twig_current->{last_child}=$ent; if( $XML::Twig::weakrefs) { weaken( $twig_current->{last_child});} ; }
  2691.       }
  2692.  
  2693.     # meant to trigger entity handler, does not seem to be activated at this time
  2694.     #if( my $handler= $t->{twig_handlers}->{gi}->{$ENT})
  2695.     #  { local $_= $ent; $handler->( $t, $ent); }
  2696.  
  2697.     return $ent;
  2698.   }
  2699.  
  2700. sub parser
  2701.   { return $_[0]->{twig_parser}; }
  2702.  
  2703. # returns the declaration text (or a default one)
  2704. sub xmldecl
  2705.   { my $t= shift;
  2706.     return '' unless( $t->{twig_xmldecl} || $t->{output_encoding});
  2707.     my $decl_string;
  2708.     my $decl= $t->{twig_xmldecl};
  2709.     if( $decl)
  2710.       { my $version= $decl->{version};
  2711.         $decl_string= q{<?xml};
  2712.         $decl_string .= qq{ version="$version"};
  2713.  
  2714.         # encoding can either have been set (in $decl->{output_encoding})
  2715.         # or come from the document (in $decl->{encoding})
  2716.         if( $t->{output_encoding})
  2717.           { my $encoding= $t->{output_encoding};
  2718.             $decl_string .= qq{ encoding="$encoding"};
  2719.           }
  2720.         elsif( $decl->{encoding})
  2721.           { my $encoding= $decl->{encoding};
  2722.             $decl_string .= qq{ encoding="$encoding"};
  2723.           }
  2724.     
  2725.         if( defined( $decl->{standalone}))
  2726.           { $decl_string .= q{ standalone="};  
  2727.             $decl_string .= $decl->{standalone} ? "yes" : "no";  
  2728.             $decl_string .= q{"}; 
  2729.           }
  2730.       
  2731.         $decl_string .= "?>\n";
  2732.       }
  2733.     else
  2734.       { my $encoding= $t->{output_encoding};
  2735.         $decl_string= qq{<?xml version="1.0" encoding="$encoding"?>};
  2736.       }
  2737.       
  2738.     my $output_filter= XML::Twig::Elt::output_filter();
  2739.     return $output_filter ? $output_filter->( $decl_string) : $decl_string;
  2740.   }
  2741.  
  2742. sub set_doctype
  2743.   { my( $t, $name, $system, $public, $internal)= @_;
  2744.     $t->{twig_doctype}= {} unless defined $t->{twig_doctype};
  2745.     my $doctype= $t->{twig_doctype};
  2746.     $doctype->{name}     = $name     if( defined $name);
  2747.     $doctype->{sysid}    = $system   if( defined $system);
  2748.     $doctype->{pub}      = $public   if( defined $public);
  2749.     $doctype->{internal} = $internal if( defined $internal);
  2750.   }
  2751.  
  2752. sub doctype_name
  2753.   { my $t= shift;
  2754.     my $doctype= $t->{twig_doctype} or return '';
  2755.     return $doctype->{name} || '';
  2756.   }
  2757.  
  2758. sub system_id
  2759.   { my $t= shift;
  2760.     my $doctype= $t->{twig_doctype} or return '';
  2761.     return $doctype->{sysid} || '';
  2762.   }
  2763.  
  2764. sub public_id
  2765.   { my $t= shift;
  2766.     my $doctype= $t->{twig_doctype} or return '';
  2767.     return $doctype->{pub} || '';
  2768.   }
  2769.  
  2770. sub internal_subset
  2771.   { my $t= shift;
  2772.     my $doctype= $t->{twig_doctype} or return '';
  2773.     return $doctype->{internal} || '';
  2774.   }
  2775.  
  2776. # return the dtd object
  2777. sub dtd
  2778.   { my $t= shift;
  2779.     return $t->{twig_dtd};
  2780.   }
  2781.  
  2782. # return an element model, or the list of element models
  2783. sub model
  2784.   { my $t= shift;
  2785.     my $elt= shift;
  2786.     return $t->dtd->{model}->{$elt} if( $elt);
  2787.     return (sort keys %{$t->dtd->{model}});
  2788.   }
  2789.  
  2790.         
  2791. # return the entity_list object 
  2792. sub entity_list
  2793.   { my $t= shift;
  2794.     return $t->{twig_entity_list};
  2795.   }
  2796.  
  2797. # return the list of entity names 
  2798. sub entity_names
  2799.   { my $t= shift;
  2800.     return $t->entity_list->entity_names;
  2801.   }
  2802.  
  2803. # return the entity object 
  2804. sub entity
  2805.   { my $t= shift;
  2806.     my $entity_name= shift;
  2807.     return $t->entity_list->ent( $entity_name);
  2808.   }
  2809.  
  2810.  
  2811. sub print_prolog
  2812.   { my $t= shift;
  2813.     my $fh=  isa( $_[0], 'GLOB') || isa( $_[0], 'IO::Scalar')  ? shift : $t->{twig_output_fh} || select() || \*STDOUT;
  2814.     ## no critic (TestingAndDebugging::ProhibitNoStrict);
  2815.     no strict 'refs';
  2816.     print {$fh} $t->prolog( @_);
  2817.   }
  2818.  
  2819. sub prolog
  2820.   { my $t= shift;
  2821.     if( $t->{no_prolog}){ return ''; }
  2822.  
  2823.     return   $t->{no_prolog}             ? '' 
  2824.            : defined $t->{no_dtd_output} ? $t->xmldecl
  2825.            :                               $t->xmldecl . $t->doctype( @_);
  2826.   }
  2827.  
  2828. sub doctype
  2829.   { my $t= shift;
  2830.     my %args= _normalize_args( @_);
  2831.     my $update_dtd = $args{UpdateDTD} || '';
  2832.     my $doctype_text='';
  2833.     
  2834.     my $doctype= $t->{twig_doctype};
  2835.  
  2836.     if( $doctype)
  2837.       { $doctype_text .= qq{<!DOCTYPE $doctype->{name}} if( $doctype->{name});
  2838.         $doctype_text .= qq{ PUBLIC "$doctype->{pub}"}  if( $doctype->{pub});
  2839.         $doctype_text .= qq{ SYSTEM}                    if( $doctype->{sysid} && !$doctype->{pub});
  2840.         $doctype_text .= qq{ "$doctype->{sysid}"}       if( $doctype->{sysid});
  2841.       }
  2842.  
  2843.     if( $update_dtd)
  2844.       { if( $doctype)  
  2845.           { my $internal=$doctype->{internal};
  2846.             # awfull hack, but at least it works a little better that what was there before
  2847.             if( $internal)
  2848.               { # remove entity declarations (they will be re-generated from the updated entity list)
  2849.                 $internal=~ s{<! \s* ENTITY \s+ $REG_NAME \s+ ( ("[^"]*"|'[^']*') \s* | SYSTEM [^>]*) >\s*}{}xg;
  2850.                 $internal=~ s{^\n}{};
  2851.               }
  2852.             $internal .= $t->entity_list->text ||'' if( $t->entity_list);
  2853.             if( $internal) { $doctype_text .= "[\n$internal]>\n"; }
  2854.           }
  2855.         elsif( !$t->{'twig_dtd'} && keys %{$t->entity_list}) 
  2856.           { $doctype_text .= "<!DOCTYPE " . $t->root->gi . " [\n" . $t->entity_list->text . "\n]>";;}
  2857.         else
  2858.           { my $doctype_text= $t->{twig_dtd};
  2859.             $doctype_text .= $t->dtd_text;
  2860.           }            
  2861.       }
  2862.     elsif( $doctype)
  2863.       { if( my $internal= $doctype->{internal}) 
  2864.           { # add opening and closing brackets if not already there
  2865.             # plus some spaces and newlines for a nice formating
  2866.             # I test it here because I can't remember which version of
  2867.             # XML::Parser need it or not, nor guess which one will in the
  2868.             # future, so this about the best I can do
  2869.             $internal=~ s{^\s*(\[\s*)?}{ [\n};
  2870.             $internal=~ s{\s*(\]\s*(>\s*)?)?\s*$}{\n]>\n};
  2871.             $doctype_text .=  $internal; 
  2872.           }
  2873.       }
  2874.       
  2875.     if( $doctype_text)
  2876.       {
  2877.         # terrible hack, as I can't figure out in which case the darn prolog
  2878.         # should get an extra > (depends on XML::Parser and expat versions)
  2879.         $doctype_text=~ s/(>\s*)*$/>\n/; # if($doctype_text);
  2880.  
  2881.         my $output_filter= XML::Twig::Elt::output_filter();
  2882.         return $output_filter ? $output_filter->( $doctype_text) : $doctype_text;
  2883.       }
  2884.     else
  2885.       { return $doctype_text; }
  2886.   }
  2887.  
  2888. sub _leading_cpi
  2889.   { my $t= shift;
  2890.     my $leading_cpi= $t->{leading_cpi} || return '';
  2891.     return $leading_cpi->sprint( 1);
  2892.   }
  2893.  
  2894. sub _trailing_cpi
  2895.   { my $t= shift;
  2896.     my $trailing_cpi= $t->{trailing_cpi} || return '';
  2897.     return $trailing_cpi->sprint( 1);
  2898.   }
  2899.  
  2900. sub _trailing_cpi_text
  2901.   { my $t= shift;
  2902.     return $t->{trailing_cpi_text} || '';
  2903.   }
  2904.  
  2905. sub print_to_file
  2906.   { my( $t, $filename)= (shift, shift);
  2907.     open( TWIG_PRINT_TO_FILE, ">$filename") or _croak( "cannot create file $filename: $!");
  2908.     $t->print( \*TWIG_PRINT_TO_FILE, @_);
  2909.     close TWIG_PRINT_TO_FILE;
  2910.     return $t;
  2911.   }
  2912.  
  2913. sub print
  2914.   { my $t= shift;
  2915.     my $fh=  isa( $_[0], 'GLOB') || isa( $_[0], 'IO::Scalar')  ? shift : undef;
  2916.     my %args= _normalize_args( @_);
  2917.  
  2918.     my $old_select    = defined $fh                  ? select $fh                                 : undef;
  2919.     my $old_pretty    = defined ($args{PrettyPrint}) ? $t->set_pretty_print( $args{PrettyPrint})  : undef;
  2920.     my $old_empty_tag = defined ($args{EmptyTags})   ? $t->set_empty_tag_style( $args{EmptyTags}) : undef;
  2921.  
  2922.     #if( !$t->{encoding} || lc( $t->{encoding}) eq 'utf-8') { my $out= $fh || \*STDOUT; binmode $out, ':utf8'; }
  2923.  
  2924.     if( $] > 5.006 && ! $t->{twig_keep_encoding}) 
  2925.       { if( grep /useperlio=define/, `$^X -V`) # we can only use binmode :utf8 if perl was compiled with useperlio
  2926.           { binmode( $fh || \*STDOUT, ":utf8" ); }
  2927.       }
  2928.  
  2929.      print  $t->prolog( %args) . $t->_leading_cpi( %args);
  2930.      $t->{twig_root}->print;
  2931.      print $t->_trailing_cpi        # trailing comments and pi's (elements, in 'process' mode)
  2932.          . $t->_trailing_cpi_text   # trailing comments and pi's (in 'keep' mode)
  2933.          . ( ($t->{twig_keep_spaces}||'') && ($t->{trailing_spaces} || ''))
  2934.          ;
  2935.  
  2936.     
  2937.     $t->set_pretty_print( $old_pretty)       if( defined $old_pretty); 
  2938.     $t->set_empty_tag_style( $old_empty_tag) if( defined $old_empty_tag); 
  2939.     if( $fh) { select $old_select; }
  2940.  
  2941.     return $t;
  2942.   }
  2943.  
  2944.  
  2945. sub flush
  2946.   { my $t= shift;
  2947.  
  2948.     $t->_trigger_tdh if $t->{twig_tdh};
  2949.  
  2950.     return if( $t->{twig_completely_flushed});
  2951.   
  2952.     my $fh=  isa( $_[0], 'GLOB') || isa( $_[0], 'IO::Scalar') ? shift : undef;
  2953.     my $old_select= defined $fh ? select $fh : undef;
  2954.     my $up_to= ref $_[0] ? shift : undef;
  2955.     my %args= _normalize_args( @_);
  2956.  
  2957.     my $old_pretty;
  2958.     if( defined $args{PrettyPrint})
  2959.       { $old_pretty= $t->set_pretty_print( $args{PrettyPrint}); 
  2960.         delete $args{PrettyPrint};
  2961.       }
  2962.  
  2963.      my $old_empty_tag_style;
  2964.      if( $args{EmptyTags})
  2965.       { $old_empty_tag_style= $t->set_empty_tag_style( $args{EmptyTags}); 
  2966.         delete $args{EmptyTags};
  2967.       }
  2968.  
  2969.  
  2970.     # the "real" last element processed, as _twig_end has closed it
  2971.     my $last_elt;
  2972.     my $flush_trailing_data=0;
  2973.     if( $up_to)
  2974.       { $last_elt= $up_to; }
  2975.     elsif( $t->{twig_current})
  2976.       { $last_elt= $t->{twig_current}->_last_child; }
  2977.     else
  2978.       { $last_elt= $t->{twig_root};
  2979.         $flush_trailing_data=1;
  2980.         $t->{twig_completely_flushed}=1;
  2981.       }
  2982.  
  2983.     # flush the DTD unless it has ready flushed (ie root has been flushed)
  2984.     my $elt= $t->{twig_root};
  2985.     unless( $elt->_flushed)
  2986.       { # store flush info so we can auto-flush later
  2987.         if( $t->{twig_autoflush})
  2988.           { $t->{twig_autoflush_data}={};
  2989.             $t->{twig_autoflush_data}->{fh}   = $fh  if( $fh);
  2990.             $t->{twig_autoflush_data}->{args} = \@_  if( @_);
  2991.           }
  2992.         $t->print_prolog( %args); 
  2993.         print $t->_leading_cpi;
  2994.       }
  2995.  
  2996.     while( $elt)
  2997.       { my $next_elt; 
  2998.         if( $last_elt && $last_elt->in( $elt))
  2999.           { 
  3000.             unless( $elt->_flushed) 
  3001.               { # just output the front tag
  3002.                 print $elt->start_tag();
  3003.                 $elt->_set_flushed;
  3004.               }
  3005.             $next_elt= $elt->{first_child};
  3006.           }
  3007.         else
  3008.           { # an element before the last one or the last one,
  3009.             $next_elt= $elt->{next_sibling};  
  3010.             $elt->_flush();
  3011.             $elt->delete; 
  3012.             last if( $last_elt && ($elt == $last_elt));
  3013.           }
  3014.         $elt= $next_elt;
  3015.       }
  3016.  
  3017.     if( $flush_trailing_data)
  3018.       { print $t->_trailing_cpi        # trailing comments and pi's (elements, in 'process' mode)
  3019.             , $t->_trailing_cpi_text   # trailing comments and pi's (in 'keep' mode)
  3020.       }
  3021.  
  3022.     select $old_select if( defined $old_select);
  3023.     $t->set_pretty_print( $old_pretty) if( defined $old_pretty); 
  3024.     $t->set_empty_tag_style( $old_empty_tag_style) if( defined $old_empty_tag_style); 
  3025.  
  3026.     if( my $ids= $t->{twig_id_list}) 
  3027.       { while( my ($id, $elt)= each %$ids) { if( ! defined $elt) { delete $t->{twig_id_list}->{$id} } } }
  3028.  
  3029.     return $t;
  3030.   }
  3031.  
  3032.  
  3033. # flushes up to an element
  3034. # this method just reorders the arguments and calls flush
  3035. sub flush_up_to
  3036.   { my $t= shift;
  3037.     my $up_to= shift;
  3038.     if( isa( $_[0], 'GLOB') || isa( $_[0], 'IO::Scalar'))
  3039.       { my $fh=  shift;
  3040.         $t->flush( $fh, $up_to, @_);
  3041.       }
  3042.     else
  3043.       { $t->flush( $up_to, @_); }
  3044.  
  3045.     return $t;
  3046.   }
  3047.  
  3048.     
  3049. # same as print except the entire document text is returned as a string
  3050. sub sprint
  3051.   { my $t= shift;
  3052.     my %args= _normalize_args( @_);
  3053.  
  3054.     my $old_pretty;
  3055.     if( defined $args{PrettyPrint})
  3056.       { $old_pretty= $t->set_pretty_print( $args{PrettyPrint}); 
  3057.         delete $args{PrettyPrint};
  3058.       }
  3059.  
  3060.      my $old_empty_tag_style;
  3061.      if( defined $args{EmptyTags})
  3062.       { $old_empty_tag_style= $t->set_empty_tag_style( $args{EmptyTags}); 
  3063.         delete $args{EmptyTags};
  3064.       }
  3065.       
  3066.     my $string=   $t->prolog( %args)       # xml declaration and doctype
  3067.                 . $t->_leading_cpi( %args) # leading comments and pi's in 'process' mode
  3068.                 . $t->{twig_root}->sprint  
  3069.                 . $t->_trailing_cpi        # trailing comments and pi's (elements, in 'process' mode)
  3070.                 . $t->_trailing_cpi_text   # trailing comments and pi's (in 'keep' mode)
  3071.                 ;
  3072.     if( $t->{twig_keep_spaces} && $t->{trailing_spaces}) { $string .= $t->{trailing_spaces}; }
  3073.  
  3074.     $t->set_pretty_print( $old_pretty) if( defined $old_pretty); 
  3075.     $t->set_empty_tag_style( $old_empty_tag_style) if( defined $old_empty_tag_style); 
  3076.  
  3077.     return $string;
  3078.   }
  3079.     
  3080.  
  3081. # this method discards useless elements in a tree
  3082. # it does the same thing as a flush except it does not print it
  3083. # the second argument is an element, the last purged element
  3084. # (this argument is usually set through the purge_up_to method)
  3085. sub purge
  3086.   { my $t= shift;
  3087.     my $up_to= shift;
  3088.  
  3089.     $t->_trigger_tdh if $t->{twig_tdh};
  3090.  
  3091.     # the "real" last element processed, as _twig_end has closed it
  3092.     my $last_elt;
  3093.     if( $up_to)
  3094.       { $last_elt= $up_to; }
  3095.     elsif( $t->{twig_current})
  3096.       { $last_elt= $t->{twig_current}->_last_child; }
  3097.     else
  3098.       { $last_elt= $t->{twig_root}; }
  3099.     
  3100.     my $elt= $t->{twig_root};
  3101.  
  3102.     while( $elt)
  3103.       { my $next_elt; 
  3104.         if( $last_elt && $last_elt->in( $elt))
  3105.           { $elt->_set_flushed;
  3106.             $next_elt= $elt->{first_child};
  3107.           }
  3108.         else
  3109.           { # an element before the last one or the last one,
  3110.             $next_elt= $elt->{next_sibling};  
  3111.             $elt->delete; 
  3112.             last if( $last_elt && ($elt == $last_elt) );
  3113.           }
  3114.         $elt= $next_elt;
  3115.       }
  3116.  
  3117.     if( my $ids= $t->{twig_id_list}) 
  3118.       { while( my ($id, $elt)= each %$ids) { if( ! defined $elt) { delete $t->{twig_id_list}->{$id} } } }
  3119.  
  3120.     return $t;
  3121.   }
  3122.     
  3123. # flushes up to an element. This method just calls purge
  3124. sub purge_up_to
  3125.   { my $t= shift;
  3126.     return $t->purge( @_);
  3127.   }
  3128.  
  3129. sub root
  3130.   { return $_[0]->{twig_root}; }
  3131.  
  3132. sub normalize
  3133.   { return $_[0]->root->normalize; }
  3134.  
  3135. # create accessor methods on attribute names
  3136. { my %accessor; # memorize accessor names so re-creating them won't trigger an error
  3137. sub create_accessors
  3138.   { 
  3139.     _croak( "cannot use the create_accessors method with perl 5.005") if( $] < 5.006);
  3140.  
  3141.     my $twig_or_class= shift;
  3142.     my $elt_class= ref $twig_or_class ? $twig_or_class->{twig_elt_class}
  3143.                                       : 'XML::Twig::Elt'
  3144.                                       ;
  3145.     ## no critic (TestingAndDebugging::ProhibitNoStrict);
  3146.     no strict 'refs';
  3147.     foreach my $att (@_)
  3148.       { _croak( "attempt to redefine existing method $att using create_accessors")
  3149.           if( $elt_class->can( $att) && !$accessor{$att});
  3150.  
  3151.         if( !$accessor{$att})                                # > perl 5.5
  3152.           { *{"$elt_class\::$att"}=                          # > perl 5.5
  3153.                 sub :lvalue                                  # > perl 5.5
  3154.                   { my $elt= shift;                          # > perl 5.5
  3155.                     if( @_) { $elt->{att}->{$att}= $_[0]; }  # > perl 5.5
  3156.                     $elt->{att}->{$att};                     # > perl 5.5
  3157.                   };                                         # > perl 5.5
  3158.             $accessor{$att}=1;                               # > perl 5.5
  3159.           }                                                  # > perl 5.5
  3160.       }
  3161.     return $twig_or_class;
  3162.   }
  3163. }
  3164.  
  3165.  
  3166. sub first_elt
  3167.   { my( $t, $cond)= @_;
  3168.     my $root= $t->root || return undef;
  3169.     return $root if( $root->passes( $cond));
  3170.     return $root->next_elt( $cond); 
  3171.   }
  3172.  
  3173. sub last_elt
  3174.   { my( $t, $cond)= @_;
  3175.     my $root= $t->root || return undef;
  3176.     return $root->last_descendant( $cond); 
  3177.   }
  3178.  
  3179. sub next_n_elt
  3180.   { my( $t, $offset, $cond)= @_;
  3181.     $offset -- if( $t->root->matches( $cond) );
  3182.     return $t->root->next_n_elt( $offset, $cond);
  3183.   }
  3184.  
  3185. sub get_xpath
  3186.   { my $twig= shift;
  3187.     if( isa( $_[0], 'ARRAY'))
  3188.       { my $elt_array= shift;
  3189.         return _unique_elts( map { $_->get_xpath( @_) } @$elt_array);
  3190.       }
  3191.     else
  3192.       { return $twig->root->get_xpath( @_); }
  3193.   }
  3194.  
  3195. # get a list of elts and return a sorted list of unique elts
  3196. sub _unique_elts
  3197.   { my @sorted= sort { $a ->cmp( $b) } @_;
  3198.     my @unique;
  3199.     while( my $current= shift @sorted)
  3200.       { push @unique, $current unless( @unique && ($unique[-1] == $current)); }
  3201.     return @unique;
  3202.   }
  3203.  
  3204. sub findvalue
  3205.   { my $twig= shift;
  3206.     if( isa( $_[0], 'ARRAY'))
  3207.       { my $elt_array= shift;
  3208.         return join( '', map { $_->findvalue( @_) } @$elt_array);
  3209.       }
  3210.     else
  3211.       { return $twig->root->findvalue( @_); }
  3212.   }
  3213.  
  3214. sub set_id_seed
  3215.   { my $t= shift;
  3216.     XML::Twig::Elt->set_id_seed( @_);
  3217.     return $t;
  3218.   }
  3219.  
  3220. # return an array ref to an index, or undef
  3221. sub index
  3222.   { my( $twig, $name, $index)= @_;
  3223.     return defined( $index) ? $twig->{_twig_index}->{$name}->[$index] : $twig->{_twig_index}->{$name};
  3224.   }
  3225.  
  3226. # return a list with just the root
  3227. # if a condition is given then return an empty list unless the root matches
  3228. sub children
  3229.   { my( $t, $cond)= @_;
  3230.     my $root= $t->root;
  3231.     unless( $cond && !($root->passes( $cond)) )
  3232.       { return ($root); }
  3233.     else
  3234.       { return (); }
  3235.   }
  3236.   
  3237. sub _children { return ($_[0]->root); }
  3238.  
  3239. # weird, but here for completude
  3240. # used to solve (non-sensical) /doc[1] XPath queries
  3241. sub child
  3242.   { my $t= shift;
  3243.     my $nb= shift;
  3244.     return ($t->children( @_))[$nb];
  3245.   }
  3246.  
  3247. sub descendants
  3248.   { my( $t, $cond)= @_;
  3249.     my $root= $t->root;
  3250.     if( $root->passes( $cond) )
  3251.       { return ($root, $root->descendants( $cond)); }
  3252.     else
  3253.       { return ( $root->descendants( $cond)); }
  3254.   }
  3255.  
  3256. sub simplify  { my $t= shift; $t->root->simplify( @_);  }
  3257. sub subs_text { my $t= shift; $t->root->subs_text( @_); }
  3258. sub trim      { my $t= shift; $t->root->trim( @_);      }
  3259.  
  3260.  
  3261. sub set_keep_encoding
  3262.   { my( $t, $keep)= @_;
  3263.     $t->{twig_keep_encoding}= $keep;
  3264.     $t->{NoExpand}= $keep;
  3265.     return XML::Twig::Elt::set_keep_encoding( $keep);
  3266.    }
  3267.  
  3268. sub set_expand_external_entities
  3269.   { return XML::Twig::Elt::set_expand_external_entities( @_); }
  3270.  
  3271. sub escape_gt
  3272.   { my $t= shift; $t->{twig_escape_gt}= 1; return XML::Twig::Elt::escape_gt( @_); }
  3273.  
  3274. sub do_not_escape_gt
  3275.   { my $t= shift; $t->{twig_escape_gt}= 0; return XML::Twig::Elt::do_not_escape_gt( @_); }
  3276.  
  3277. # WARNING: at the moment the id list is not updated reliably
  3278. sub elt_id
  3279.   { return $_[0]->{twig_id_list}->{$_[1]}; }
  3280.  
  3281. # change it in ALL twigs at the moment
  3282. sub change_gi 
  3283.   { my( $twig, $old_gi, $new_gi)= @_;
  3284.     my $index;
  3285.     return unless($index= $XML::Twig::gi2index{$old_gi});
  3286.     $XML::Twig::index2gi[$index]= $new_gi;
  3287.     delete $XML::Twig::gi2index{$old_gi};
  3288.     $XML::Twig::gi2index{$new_gi}= $index;
  3289.     return $twig;
  3290.   }
  3291.  
  3292.  
  3293. # builds the DTD from the stored (possibly updated) data
  3294. sub dtd_text
  3295.   { my $t= shift;
  3296.     my $dtd= $t->{twig_dtd};
  3297.     my $doctype= $t->{twig_doctype} or return '';
  3298.     my $string= "<!DOCTYPE ".$doctype->{name};
  3299.  
  3300.     $string .= " [\n";
  3301.  
  3302.     foreach my $gi (@{$dtd->{elt_list}})
  3303.       { $string.= "<!ELEMENT $gi ".$dtd->{model}->{$gi}.">\n" ;
  3304.         if( $dtd->{att}->{$gi})
  3305.           { my $attlist= $dtd->{att}->{$gi};
  3306.             $string.= "<!ATTLIST $gi\n";
  3307.             foreach my $att ( sort keys %{$attlist})
  3308.               { 
  3309.                 if( $attlist->{$att}->{fixed})
  3310.                   { $string.= "   $att $attlist->{$att}->{type} #FIXED $attlist->{$att}->{default}"; }
  3311.                 else
  3312.                   { $string.= "   $att $attlist->{$att}->{type} $attlist->{$att}->{default}"; }
  3313.                 $string.= "\n";
  3314.               }
  3315.             $string.= ">\n";
  3316.           }
  3317.       }
  3318.     $string.= $t->entity_list->text if( $t->entity_list);
  3319.     $string.= "\n]>\n";
  3320.     return $string;
  3321.   }
  3322.         
  3323. # prints the DTD from the stored (possibly updated) data
  3324. sub dtd_print
  3325.   { my $t= shift;
  3326.     my $fh=  isa( $_[0], 'GLOB') || isa( $_[0], 'IO::Scalar')  ? shift : undef;
  3327.     if( $fh) { print $fh $t->dtd_text; }
  3328.     else     { print $t->dtd_text;     }
  3329.     return $t;
  3330.   }
  3331.  
  3332. # build the subs that call directly expat
  3333. BEGIN
  3334.   { my @expat_methods= qw( depth in_element within_element context
  3335.                            current_line current_column current_byte
  3336.                            recognized_string original_string 
  3337.                            xpcroak xpcarp 
  3338.                            xml_escape
  3339.                            base current_element element_index 
  3340.                            position_in_context);
  3341.     foreach my $method (@expat_methods)
  3342.       { 
  3343.         ## no critic (TestingAndDebugging::ProhibitNoStrict);
  3344.         no strict 'refs';
  3345.         *{$method}= sub { my $t= shift;
  3346.                           _croak( "calling $method after parsing is finished") unless( $t->{twig_parsing}); 
  3347.                           return $t->{twig_parser}->$method(@_); 
  3348.                         };
  3349.       }
  3350.   }
  3351.  
  3352. sub path
  3353.   { my( $t, $gi)= @_;
  3354.     if( $t->{twig_map_xmlns})
  3355.       { return "/" . join( "/", map { $t->_replace_prefix( $_)} ($t->{twig_parser}->context, $gi)); }
  3356.     else
  3357.       { return "/" . join( "/", ($t->{twig_parser}->context, $gi)); }
  3358.   }
  3359.  
  3360. sub finish
  3361.   { my $t= shift;
  3362.     return $t->{twig_parser}->finish;
  3363.   }
  3364.  
  3365. # just finish the parse by printing the rest of the document
  3366. sub finish_print
  3367.   { my( $t, $fh)= @_;
  3368.     my $old_fh;
  3369.     unless( defined $fh)
  3370.       { $t->_set_fh_to_twig_output_fh(); }
  3371.     elsif( defined $fh)
  3372.       { $old_fh= select $fh; 
  3373.         $t->{twig_original_selected_fh}= $old_fh if( $old_fh); 
  3374.       }
  3375.     
  3376.     my $p=$t->{twig_parser};
  3377.     if( $t->{twig_keep_encoding})
  3378.       { $p->setHandlers( %twig_handlers_finish_print); }
  3379.     else
  3380.       { $p->setHandlers( %twig_handlers_finish_print_original); }
  3381.     return $t;
  3382.   }
  3383.  
  3384. sub set_remove_cdata { return XML::Twig::Elt::set_remove_cdata( @_); }
  3385.  
  3386. sub output_filter          { return XML::Twig::Elt::output_filter( @_);          }
  3387. sub set_output_filter      { return XML::Twig::Elt::set_output_filter( @_);      }
  3388.  
  3389. sub output_text_filter     { return XML::Twig::Elt::output_text_filter( @_);     }
  3390. sub set_output_text_filter { return XML::Twig::Elt::set_output_text_filter( @_); }
  3391.  
  3392. sub set_input_filter
  3393.   { my( $t, $input_filter)= @_;
  3394.     my $old_filter= $t->{twig_input_filter};
  3395.       if( !$input_filter || isa( $input_filter, 'CODE') )
  3396.         { $t->{twig_input_filter}= $input_filter; }
  3397.       elsif( $input_filter eq 'latin1')
  3398.         {  $t->{twig_input_filter}= latin1(); }
  3399.       elsif( $filter{$input_filter})
  3400.         {  $t->{twig_input_filter}= $filter{$input_filter}; }
  3401.       else
  3402.         { _croak( "invalid input filter: $input_filter"); }
  3403.       
  3404.       return $old_filter;
  3405.     }
  3406.  
  3407. sub set_empty_tag_style
  3408.   { return XML::Twig::Elt::set_empty_tag_style( @_); }
  3409.  
  3410. sub set_pretty_print
  3411.   { return XML::Twig::Elt::set_pretty_print( @_); }
  3412.  
  3413. sub set_quote
  3414.   { return XML::Twig::Elt::set_quote( @_); }
  3415.  
  3416. sub set_indent
  3417.   { return XML::Twig::Elt::set_indent( @_); }
  3418.  
  3419. sub set_keep_atts_order
  3420.   { shift; return XML::Twig::Elt::set_keep_atts_order( @_); }
  3421.  
  3422. sub keep_atts_order
  3423.   { return XML::Twig::Elt::keep_atts_order( @_); }
  3424.  
  3425. sub set_do_not_escape_amp_in_atts
  3426.   { return XML::Twig::Elt::set_do_not_escape_amp_in_atts( @_); }
  3427.  
  3428. # save and restore package globals (the ones in XML::Twig::Elt)
  3429. # should probably return the XML::Twig object itself, but instead
  3430. # returns the state (as a hashref) for backward compatibility
  3431. sub save_global_state
  3432.   { my $t= shift;
  3433.     return $t->{twig_saved_state}= XML::Twig::Elt::global_state();
  3434.   }
  3435.  
  3436. sub restore_global_state
  3437.   { my $t= shift;
  3438.     XML::Twig::Elt::set_global_state( $t->{twig_saved_state});
  3439.   }
  3440.  
  3441. sub global_state
  3442.   { return XML::Twig::Elt::global_state(); }
  3443.  
  3444. sub set_global_state
  3445.   {  return XML::Twig::Elt::set_global_state( $_[1]); }
  3446.  
  3447. sub dispose
  3448.   { my $t= shift;
  3449.     $t->DESTROY;
  3450.     return;
  3451.   }
  3452.   
  3453. sub DESTROY
  3454.   { my $t= shift;
  3455.     if( $t->{twig_root} && isa(  $t->{twig_root}, 'XML::Twig')) 
  3456.       { $t->{twig_root}->delete } 
  3457.  
  3458.     # added to break circular references
  3459.     undef $t->{twig};
  3460.     undef $t->{twig_root}->{twig} if( $t->{twig_root});
  3461.     undef $t->{twig_parser};
  3462.     
  3463.     $t={}; # prevents memory leaks (especially when using mod_perl)
  3464.     undef $t;
  3465.   }        
  3466.  
  3467.  
  3468. #
  3469. #  non standard handlers
  3470. #
  3471.  
  3472. # kludge: expat 1.95.2 calls both Default AND Doctype handlers
  3473. # so if the default handler finds '<!DOCTYPE' then it must 
  3474. # unset itself (_twig_print_doctype will reset it)
  3475. sub _twig_print_check_doctype
  3476.    { # warn " in _twig_print_check_doctype...\n"; # DEBUG handler
  3477.     
  3478.     my $p= shift;
  3479.     my $string= $p->recognized_string();
  3480.     if( $string eq '<!DOCTYPE') 
  3481.       { 
  3482.         $p->setHandlers( Default => undef); 
  3483.         $p->setHandlers( Entity => undef); 
  3484.         $expat_1_95_2=1; 
  3485.       }
  3486.     else                        
  3487.       { print $string; }
  3488.  
  3489.     return;
  3490.   }
  3491.  
  3492.  
  3493. sub _twig_print
  3494.    { # warn " in _twig_print...\n"; # DEBUG handler
  3495.     my $p= shift;
  3496.     if( $expat_1_95_2 && ($p->recognized_string eq '[') && !$p->{twig}->{expat_1_95_2_seen_bracket})
  3497.       { # otherwise the opening square bracket of the doctype gets printed twice 
  3498.         $p->{twig}->{expat_1_95_2_seen_bracket}=1;
  3499.       }
  3500.     else
  3501.       { print $p->recognized_string(); }
  3502.     return;
  3503.   }
  3504. # recognized_string does not seem to work for entities, go figure!
  3505. # so this handler is used to print them anyway
  3506. sub _twig_print_entity
  3507.    { # warn " in _twig_print_entity...\n"; # DEBUG handler
  3508.     my $p= shift; 
  3509.     XML::Twig::Entity->new( @_)->print;
  3510.   }
  3511.  
  3512. # kludge: expat 1.95.2 calls both Default AND Doctype handlers
  3513. # so if the default handler finds '<!DOCTYPE' then it must 
  3514. # unset itself (_twig_print_doctype will reset it)
  3515. sub _twig_print_original_check_doctype
  3516.    { # warn " in _twig_print_original_check_doctype...\n"; # DEBUG handler
  3517.     
  3518.     my $p= shift;
  3519.     my $string= $p->original_string();
  3520.     if( $string eq '<!DOCTYPE') 
  3521.       { $p->setHandlers( Default => undef); 
  3522.         $p->setHandlers( Entity => undef); 
  3523.         $expat_1_95_2=1; 
  3524.       }
  3525.     else                        
  3526.       { print $string; }
  3527.  
  3528.     return;    
  3529.   }
  3530.  
  3531. sub _twig_print_original
  3532.    { # warn " in _twig_print_original...\n"; # DEBUG handler
  3533.     my $p= shift; 
  3534.     print $p->original_string();
  3535.     return;    
  3536.   }
  3537.  
  3538.  
  3539. sub _twig_print_original_doctype
  3540.    { # warn " in _twig_print_original_doctype...\n"; # DEBUG handler
  3541.     
  3542.     my(  $p, $name, $sysid, $pubid, $internal)= @_;
  3543.     if( $name)
  3544.       { # with recent versions of XML::Parser original_string does not work,
  3545.         # hence we need to rebuild the doctype declaration
  3546.         my $doctype='';
  3547.         $doctype .= qq{<!DOCTYPE $name}    if( $name);
  3548.         $doctype .=  qq{ PUBLIC  "$pubid"}  if( $pubid);
  3549.         $doctype .=  qq{ SYSTEM}            if( $sysid && !$pubid);
  3550.         $doctype .=  qq{ "$sysid"}          if( $sysid); 
  3551.         $doctype .=  ' [' if( $internal && !$expat_1_95_2) ;
  3552.         $doctype .=  qq{>} unless( $internal || $expat_1_95_2);
  3553.         $p->{twig}->{twig_doctype}->{has_internal}=$internal;
  3554.         print $doctype;
  3555.       }
  3556.     $p->setHandlers( Default => \&_twig_print_original);
  3557.     return;    
  3558.   }
  3559.  
  3560. sub _twig_print_doctype
  3561.    { # warn " in _twig_print_doctype...\n"; # DEBUG handler
  3562.     my(  $p, $name, $sysid, $pubid, $internal)= @_;
  3563.     if( $name)
  3564.       { # with recent versions of XML::Parser original_string does not work,
  3565.         # hence we need to rebuild the doctype declaration
  3566.         my $doctype='';
  3567.         $doctype .= qq{<!DOCTYPE $name}    if( $name);
  3568.         $doctype .=  qq{ PUBLIC  "$pubid"}  if( $pubid);
  3569.         $doctype .=  qq{ SYSTEM}            if( $sysid && !$pubid);
  3570.         $doctype .=  qq{ "$sysid"}          if( $sysid); 
  3571.         $doctype .=  ' [' if( $internal) ;
  3572.         $doctype .=  qq{>} unless( $internal || $expat_1_95_2);
  3573.         $p->{twig}->{twig_doctype}->{has_internal}=$internal;
  3574.         print $doctype;
  3575.       }
  3576.     $p->setHandlers( Default => \&_twig_print);
  3577.     return;    
  3578.   }
  3579.  
  3580.  
  3581. sub _twig_print_original_default
  3582.    { # warn " in _twig_print_original_default...\n"; # DEBUG handler
  3583.     my $p= shift;
  3584.     print $p->original_string();
  3585.     return;    
  3586.   }
  3587.  
  3588. # account for the case where the element is empty
  3589. sub _twig_print_end_original
  3590.    { # warn " in _twig_print_end_original...\n"; # DEBUG handler
  3591.     my $p= shift;
  3592.     print $p->original_string();
  3593.     return;    
  3594.   }
  3595.  
  3596. sub _twig_start_check_roots
  3597.    { # warn " in _twig_start_check_roots...\n"; # DEBUG handler
  3598.     my $p= shift;
  3599.     my $gi= shift;
  3600.     
  3601.     my $t= $p->{twig};
  3602.     
  3603.     my $fh= $t->{twig_output_fh} || select() || \*STDOUT;
  3604.  
  3605.     unless( $p->depth == 0)
  3606.       { if( $t->{twig_map_xmlns}) { _replace_ns( $t, \$gi, \@_); }
  3607.       }
  3608.  
  3609.     push @{$t->{_twig_context_stack}}, { _tag => $gi, @_};
  3610.     my %att= @_;
  3611.  
  3612.     if( _handler( $t, $t->{twig_roots}, $gi))
  3613.       { $p->setHandlers( %twig_handlers); # restore regular handlers
  3614.         $t->{twig_root_depth}= $p->depth; 
  3615.         pop @{$t->{_twig_context_stack}}; # will be pushed back in _twig_start
  3616.         _twig_start( $p, $gi, @_);
  3617.         return;
  3618.       }
  3619.  
  3620.     # $tag will always be true if it needs to be printed (the tag string is never empty)
  3621.     my $tag= $t->{twig_default_print} ? $t->{twig_keep_encoding} ? $p->original_string
  3622.                                                                  : $p->recognized_string
  3623.                                       : '';
  3624.  
  3625.     if( $p->depth == 0)
  3626.       { 
  3627.         ## no critic (TestingAndDebugging::ProhibitNoStrict);
  3628.         no strict 'refs';
  3629.         print {$fh} $tag if( $tag);
  3630.         pop @{$t->{_twig_context_stack}}; # will be pushed back in _twig_start
  3631.         _twig_start( $p, $gi, @_);
  3632.         $t->root->_set_flushed; # or the root start tag gets output the first time we flush
  3633.       }
  3634.     elsif( $t->{twig_starttag_handlers})
  3635.       { # look for start tag handlers
  3636.  
  3637.         my @handlers= _handler( $t, $t->{twig_starttag_handlers}, $gi);
  3638.         my $last_handler_res;
  3639.         foreach my $handler ( @handlers)
  3640.           { $last_handler_res= $handler->($t, $gi, %att);
  3641.             last unless $last_handler_res;
  3642.           }
  3643.         ## no critic (TestingAndDebugging::ProhibitNoStrict);
  3644.         no strict 'refs';
  3645.         print {$fh} $tag if( $tag && (!@handlers || $last_handler_res));   
  3646.       }
  3647.     else
  3648.       { 
  3649.         ## no critic (TestingAndDebugging::ProhibitNoStrict);
  3650.         no strict 'refs';
  3651.         print {$fh} $tag if( $tag); 
  3652.       }  
  3653.     return;    
  3654.   }
  3655.  
  3656. sub _twig_end_check_roots
  3657.    { # warn " in _twig_end_check_roots...\n"; # DEBUG handler
  3658.     
  3659.     my( $p, $gi, %att)= @_;
  3660.     my $t= $p->{twig};
  3661.     # $tag can be empty (<elt/>), hence the undef and the tests for defined
  3662.     my $tag= $t->{twig_default_print} ? $t->{twig_keep_encoding} ? $p->original_string
  3663.                                                                  : $p->recognized_string
  3664.                                       : undef;
  3665.     my $fh= $t->{twig_output_fh} || select() || \*STDOUT;
  3666.     
  3667.     if( $t->{twig_endtag_handlers})
  3668.       { # look for end tag handlers
  3669.         my @handlers= _handler( $t, $t->{twig_endtag_handlers}, $gi);
  3670.         my $last_handler_res=1;
  3671.         foreach my $handler ( @handlers)
  3672.           { $last_handler_res= $handler->($t, $gi) || last; }
  3673.         if( ! $last_handler_res) 
  3674.           { pop @{$t->{_twig_context_stack}};
  3675.             return;
  3676.           }
  3677.       }
  3678.     {
  3679.       ## no critic (TestingAndDebugging::ProhibitNoStrict);
  3680.       no strict 'refs';
  3681.       print {$fh} $tag if( defined $tag);
  3682.     }
  3683.     if( $p->depth == 0)
  3684.       { 
  3685.         _twig_end( $p, $gi);  
  3686.         $t->root->{end_tag_flushed}=1;
  3687.       }
  3688.  
  3689.     pop @{$t->{_twig_context_stack}};
  3690.     return;    
  3691.   }
  3692.  
  3693. sub _twig_pi_check_roots
  3694.    { # warn " in _twig_pi_check_roots...\n"; # DEBUG handler
  3695.     my( $p, $target, $data)= @_;
  3696.     my $t= $p->{twig};
  3697.     my $pi= $t->{twig_default_print} ? $t->{twig_keep_encoding} ? $p->original_string
  3698.                                                                 : $p->recognized_string
  3699.                                     : undef;
  3700.     my $fh= $t->{twig_output_fh} || select() || \*STDOUT;
  3701.     
  3702.     if( my $handler=    $t->{twig_handlers}->{pi_handlers}->{$target}
  3703.                      || $t->{twig_handlers}->{pi_handlers}->{''}
  3704.       )
  3705.       { # if handler is called on pi, then it needs to be processed as a regular node
  3706.         my @flags= qw( twig_process_pi twig_keep_pi);
  3707.         my @save= @{$t}{@flags}; # save pi related flags
  3708.         @{$t}{@flags}= (1, 0);   # override them, pi needs to be processed
  3709.         _twig_pi( @_);           # call handler on the pi
  3710.         @{$t}{@flags}= @save;;   # restore flag
  3711.       }
  3712.     else
  3713.       { 
  3714.         ## no critic (TestingAndDebugging::ProhibitNoStrict);
  3715.         no strict 'refs';
  3716.         print  {$fh} $pi if( defined( $pi));
  3717.       }
  3718.     return;    
  3719.   }
  3720.  
  3721.  
  3722. sub _twig_ignore_start
  3723.    { # warn " in _twig_ignore_start...\n"; # DEBUG handler
  3724.     
  3725.     my( $p, $gi)= @_;
  3726.     my $t= $p->{twig};
  3727.     $t->{twig_ignore_level}++;
  3728.     my $action= $t->{twig_ignore_action};
  3729.     if( $action eq 'print' ) { _twig_print_original( @_); }
  3730. #    elsif( $action eq 'string' )
  3731. #      { $t->{twig_buffered_string} .= $p->original_string(); }
  3732.     return;    
  3733.   }
  3734.  
  3735. sub _twig_ignore_end
  3736.    { # warn " in _twig_ignore_end...\n"; # DEBUG handler
  3737.     
  3738.     my( $p, $gi)= @_;
  3739.     my $t= $p->{twig};
  3740.  
  3741.     my $action= $t->{twig_ignore_action};
  3742.  
  3743.     if( $action eq 'print')
  3744.       { _twig_print_original( $p, $gi); }
  3745. #    elsif( $action eq 'string')
  3746. #      { $t->{twig_buffered_string} .= $p->original_string(); }
  3747.  
  3748.     $t->{twig_ignore_level}--;
  3749.  
  3750.     if( ! $t->{twig_ignore_level})
  3751.       { 
  3752.         $t->{twig_current}   = $t->{twig_ignore_elt};
  3753.         $t->{twig_current}->set_twig_current;
  3754.  
  3755.         $t->{twig_ignore_elt}->cut;  # there could possibly be a memory leak here (delete would avoid it,
  3756.                                      # but could also delete elements that should not be deleted)
  3757.  
  3758.         # restore the saved stack to the current level
  3759.         splice( @{$t->{_twig_context_stack}}, $p->depth+ 1 );
  3760.         #warn "stack: ", _dump_stack( $t->{_twig_context_stack}), "\n";
  3761.  
  3762.         $p->setHandlers( @{$t->{twig_saved_handlers}});
  3763.         # test for handlers
  3764.         if( $t->{twig_endtag_handlers})
  3765.           { # look for end tag handlers
  3766.             my @handlers= _handler( $t, $t->{twig_endtag_handlers}, $gi);
  3767.             my $last_handler_res=1;
  3768.             foreach my $handler ( @handlers)
  3769.               { $last_handler_res= $handler->($t, $gi) || last; }
  3770.           }
  3771.         pop @{$t->{_twig_context_stack}};
  3772.       };
  3773.     return;    
  3774.   }
  3775.  
  3776. #sub _dump_stack { my( $stack)= @_; return join( ":", map { $_->{_tag} } @$stack); }
  3777.     
  3778. sub ignore
  3779.   { my( $t, $elt)= @_;
  3780.     my $current= $t->{twig_current};
  3781.  
  3782.     if( ! ($elt && ref( $elt) && isa( $elt, 'XML::Twig::Elt'))) { $elt= $current; }
  3783.  
  3784.     #warn "ignore:  current = ", $current->tag, ", elt = ", $elt->tag, ")\n";
  3785.  
  3786.     # we need the ($elt == $current->{last_child}) test because the current element is set to the
  3787.     # parent _before_ handlers are called (and I can't figure out how to fix this)
  3788.     unless( ($elt == $current) || ($current->{last_child} && ($elt == $current->{last_child})) || $current->in( $elt)) 
  3789.       { _croak( "element to be ignored must be ancestor of current element"); }
  3790.  
  3791.     $t->{twig_ignore_level}= $current == $elt ? 1 : $t->_level_in_stack( $current) - $t->_level_in_stack($elt) + 1;
  3792.     #warn "twig_ignore_level:  $t->{twig_ignore_level} (current: ", $current->tag, ", elt: ", $elt->tag, ")\n";
  3793.     $t->{twig_ignore_elt}  = $elt;     # save it, so we can delete it later
  3794.  
  3795.     my $action= shift || 1; 
  3796.     $t->{twig_ignore_action}= $action;
  3797.  
  3798.     my $p= $t->{twig_parser};
  3799.     my @saved_handlers= $p->setHandlers( %twig_handlers_ignore); # set handlers
  3800.     if( $action eq 'print')
  3801.       { $p->setHandlers( Default => \&_twig_print_original); }
  3802. #    elsif( $action eq 'string')
  3803. #      { # not used at the moment
  3804. #        $t->{twig_buffered_string}='';
  3805. #        $p->setHandlers( Default => \&twig_buffer_original);
  3806. #      }
  3807.  
  3808.     $t->{twig_saved_handlers}= \@saved_handlers;        # save current handlers
  3809.   }
  3810.  
  3811. sub _level_in_stack
  3812.   { my( $t, $elt)= @_;
  3813.     my $level=1;
  3814.     foreach my $elt_in_stack ( @{$t->{_twig_context_stack}} )
  3815.       { if( $elt_in_stack->{_elt} && ($elt == $elt_in_stack->{_elt})) { return $level }
  3816.         $level++;
  3817.       }
  3818.   }
  3819.  
  3820.  
  3821.  
  3822. # select $t->{twig_output_fh} and store the current selected fh 
  3823. sub _set_fh_to_twig_output_fh
  3824.   { my $t= shift;
  3825.     my $output_fh= $t->{twig_output_fh};
  3826.     if( $output_fh && !$t->{twig_output_fh_selected})
  3827.       { # there is an output fh
  3828.         $t->{twig_selected_fh}= select(); # store the currently selected fh
  3829.         $t->{twig_output_fh_selected}=1;
  3830.         select $output_fh;                # select the output fh for the twig
  3831.       }
  3832.   }
  3833.  
  3834. # select the fh that was stored in $t->{twig_selected_fh} 
  3835. # (before $t->{twig_output_fh} was selected)
  3836. sub _set_fh_to_selected_fh
  3837.   { my $t= shift;
  3838.     return unless( $t->{twig_output_fh});
  3839.     my $selected_fh= $t->{twig_selected_fh};
  3840.     $t->{twig_output_fh_selected}=0;
  3841.     select $selected_fh;
  3842.     return;
  3843.   }
  3844.   
  3845.  
  3846. sub encoding
  3847.   { return $_[0]->{twig_xmldecl}->{encoding} if( $_[0]->{twig_xmldecl}); }
  3848.  
  3849. sub set_encoding
  3850.   { my( $t, $encoding)= @_;
  3851.     $t->{twig_xmldecl} ||={};
  3852.     $t->set_xml_version( "1.0") unless( $t->xml_version);
  3853.     $t->{twig_xmldecl}->{encoding}= $encoding;
  3854.     return $t;
  3855.   }
  3856.  
  3857. sub output_encoding
  3858.   { return $_[0]->{output_encoding}; }
  3859.   
  3860. sub set_output_encoding
  3861.   { my( $t, $encoding)= @_;
  3862.     my $output_filter= $t->output_filter || '';
  3863.  
  3864.     if( ($encoding && $encoding !~ m{^utf-?8$}i) || $t->{twig_keep_encoding} || $output_filter)
  3865.       { $t->set_output_filter( _encoding_filter( $encoding || '')); }
  3866.  
  3867.     $t->{output_encoding}= $encoding;
  3868.     return $t;
  3869.   }
  3870.  
  3871. sub xml_version
  3872.   { return $_[0]->{twig_xmldecl}->{version} if( $_[0]->{twig_xmldecl}); }
  3873.  
  3874. sub set_xml_version
  3875.   { my( $t, $version)= @_;
  3876.     $t->{twig_xmldecl} ||={};
  3877.     $t->{twig_xmldecl}->{version}= $version;
  3878.     return $t;
  3879.   }
  3880.  
  3881. sub standalone
  3882.   { return $_[0]->{twig_xmldecl}->{standalone} if( $_[0]->{twig_xmldecl}); }
  3883.  
  3884. sub set_standalone
  3885.   { my( $t, $standalone)= @_;
  3886.     $t->{twig_xmldecl} ||={};
  3887.     $t->set_xml_version( "1.0") unless( $t->xml_version);
  3888.     $t->{twig_xmldecl}->{standalone}= $standalone;
  3889.     return $t;
  3890.   }
  3891.  
  3892.  
  3893. # SAX methods
  3894.  
  3895. sub toSAX1
  3896.   { _croak( "cannot use toSAX1 while parsing (use flush_toSAX1)") if (defined $_[0]->{twig_parser});
  3897.     shift(@_)->_toSAX(@_, \&XML::Twig::Elt::_start_tag_data_SAX1,
  3898.                           \&XML::Twig::Elt::_end_tag_data_SAX1
  3899.              ); 
  3900.   }
  3901.  
  3902. sub toSAX2
  3903.   { _croak( "cannot use toSAX2 while parsing (use flush_toSAX2)") if (defined $_[0]->{twig_parser});
  3904.     shift(@_)->_toSAX(@_, \&XML::Twig::Elt::_start_tag_data_SAX2,
  3905.                           \&XML::Twig::Elt::_end_tag_data_SAX2
  3906.              ); 
  3907.   }
  3908.  
  3909.  
  3910. sub _toSAX
  3911.   { my( $t, $handler, $start_tag_data, $end_tag_data) = @_;
  3912.  
  3913.     if( my $start_document =  $handler->can( 'start_document'))
  3914.       { $start_document->( $handler); }
  3915.     
  3916.     $t->_prolog_toSAX( $handler);
  3917.     
  3918.     if( $t->root) { $t->root->_toSAX( $handler, $start_tag_data, $end_tag_data) ; }
  3919.     if( my $end_document =  $handler->can( 'end_document'))
  3920.       { $end_document->( $handler); }
  3921.   }
  3922.  
  3923.  
  3924. sub flush_toSAX1
  3925.   { shift(@_)->_flush_toSAX(@_, \&XML::Twig::Elt::_start_tag_data_SAX1,
  3926.                                \&XML::Twig::Elt::_end_tag_data_SAX1
  3927.              ); 
  3928.   }
  3929.  
  3930. sub flush_toSAX2
  3931.   { shift(@_)->_flush_toSAX(@_, \&XML::Twig::Elt::_start_tag_data_SAX2,
  3932.                                \&XML::Twig::Elt::_end_tag_data_SAX2
  3933.              ); 
  3934.   }
  3935.  
  3936. sub _flush_toSAX
  3937.   { my( $t, $handler, $start_tag_data, $end_tag_data)= @_;
  3938.  
  3939.     # the "real" last element processed, as _twig_end has closed it
  3940.     my $last_elt;
  3941.     if( $t->{twig_current})
  3942.       { $last_elt= $t->{twig_current}->_last_child; }
  3943.     else
  3944.       { $last_elt= $t->{twig_root}; }
  3945.  
  3946.     my $elt= $t->{twig_root};
  3947.     unless( $elt->_flushed)
  3948.       { # init unless already done (ie root has been flushed)
  3949.         if( my $start_document =  $handler->can( 'start_document'))
  3950.           { $start_document->( $handler); }
  3951.         # flush the DTD
  3952.         $t->_prolog_toSAX( $handler) 
  3953.       }
  3954.  
  3955.     while( $elt)
  3956.       { my $next_elt; 
  3957.         if( $last_elt && $last_elt->in( $elt))
  3958.           { 
  3959.             unless( $elt->_flushed) 
  3960.               { # just output the front tag
  3961.                 if( my $start_element = $handler->can( 'start_element'))
  3962.                  { if( my $tag_data= $start_tag_data->( $elt))
  3963.                      { $start_element->( $handler, $tag_data); }
  3964.                  }
  3965.                 $elt->_set_flushed;
  3966.               }
  3967.             $next_elt= $elt->{first_child};
  3968.           }
  3969.         else
  3970.           { # an element before the last one or the last one,
  3971.             $next_elt= $elt->{next_sibling};  
  3972.             $elt->_toSAX( $handler, $start_tag_data, $end_tag_data);
  3973.             $elt->delete; 
  3974.             last if( $last_elt && ($elt == $last_elt));
  3975.           }
  3976.         $elt= $next_elt;
  3977.       }
  3978.     if( !$t->{twig_parsing}) 
  3979.       { if( my $end_document =  $handler->can( 'end_document'))
  3980.           { $end_document->( $handler); }
  3981.       }
  3982.   }
  3983.  
  3984.  
  3985. sub _prolog_toSAX
  3986.   { my( $t, $handler)= @_;
  3987.     $t->_xmldecl_toSAX( $handler);
  3988.     $t->_DTD_toSAX( $handler);
  3989.   }
  3990.  
  3991. sub _xmldecl_toSAX
  3992.   { my( $t, $handler)= @_;
  3993.     my $decl= $t->{twig_xmldecl};
  3994.     my $data= { Version    => $decl->{version},
  3995.                 Encoding   => $decl->{encoding},
  3996.                 Standalone => $decl->{standalone},
  3997.           };
  3998.     if( my $xml_decl= $handler->can( 'xml_decl'))
  3999.       { $xml_decl->( $handler, $data); }
  4000.   }
  4001.                 
  4002. sub _DTD_toSAX
  4003.   { my( $t, $handler)= @_;
  4004.     my $doctype= $t->{twig_doctype};
  4005.     return unless( $doctype);
  4006.     my $data= { Name     => $doctype->{name},
  4007.                 PublicId => $doctype->{pub},
  4008.                 SystemId => $doctype->{sysid},
  4009.               };
  4010.  
  4011.     if( my $start_dtd= $handler->can( 'start_dtd'))
  4012.       { $start_dtd->( $handler, $data); }
  4013.  
  4014.     # I should call code to export the internal subset here 
  4015.     
  4016.     if( my $end_dtd= $handler->can( 'end_dtd'))
  4017.       { $end_dtd->( $handler); }
  4018.   }
  4019.  
  4020. # input/output filters
  4021.  
  4022. sub latin1 
  4023.   { local $SIG{__DIE__};
  4024.     if( _use(  'Encode'))
  4025.       { return encode_convert( 'ISO-8859-15'); }
  4026.     elsif( _use( 'Text::Iconv'))
  4027.       { return iconv_convert( 'ISO-8859-15'); }
  4028.     elsif( _use( 'Unicode::Map8') && _use( 'Unicode::String'))
  4029.       { return unicode_convert( 'ISO-8859-15'); }
  4030.     else
  4031.       { return \®exp2latin1; }
  4032.   }
  4033.  
  4034. sub _encoding_filter
  4035.   { 
  4036.       { local $SIG{__DIE__};
  4037.         my $encoding= $_[1] || $_[0];
  4038.         if( _use( 'Encode'))
  4039.           { my $sub= encode_convert( $encoding);
  4040.             return $sub;
  4041.           }
  4042.         elsif( _use( 'Text::Iconv'))
  4043.           { return iconv_convert( $encoding); }
  4044.         elsif( _use( 'Unicode::Map8') && _use( 'Unicode::String'))
  4045.           { return unicode_convert( $encoding); }
  4046.         }
  4047.     _croak( "Encode, Text::Iconv or Unicode::Map8 and Unicode::String need to be installed in order to use encoding options");
  4048.   }
  4049.  
  4050. # shamelessly lifted from XML::TyePYX (works only with XML::Parse 2.27)
  4051. sub regexp2latin1
  4052.   { my $text=shift;
  4053.     $text=~s{([\xc0-\xc3])(.)}{ my $hi = ord($1);
  4054.                                 my $lo = ord($2);
  4055.                                 chr((($hi & 0x03) <<6) | ($lo & 0x3F))
  4056.                               }ge;
  4057.     return $text;
  4058.   }
  4059.  
  4060.  
  4061. sub html_encode
  4062.   { _use( 'HTML::Entities') or croak "cannot use html_encode: missing HTML::Entities";
  4063.     return HTML::Entities::encode_entities($_[0] );
  4064.   }
  4065.  
  4066. sub safe_encode
  4067.   {   my $str= shift;
  4068.       if( $] < 5.008)
  4069.         { # the no utf8 makes the regexp work in 5.6
  4070.           $str =~ s{([\xC0-\xDF].|[\xE0-\xEF]..|[\xF0-\xFF]...)}
  4071.                    {_XmlUtf8Decode($1)}egs; 
  4072.         }
  4073.       else
  4074.         { $str= encode( ascii => $str, $FB_HTMLCREF); }
  4075.       return $str;
  4076.   }
  4077.  
  4078. sub safe_encode_hex
  4079.   {   my $str= shift;
  4080.       if( $] < 5.008)
  4081.         { # the no utf8 makes the regexp work in 5.6
  4082.           $str =~ s{([\xC0-\xDF].|[\xE0-\xEF]..|[\xF0-\xFF]...)}
  4083.                    {_XmlUtf8Decode($1, 1)}egs; 
  4084.         }
  4085.       else
  4086.         { $str= encode( ascii => $str, $FB_XMLCREF); }
  4087.       return $str;
  4088.   }
  4089.  
  4090. # this one shamelessly lifted from XML::DOM
  4091. # does NOT work on 5.8.0
  4092. sub _XmlUtf8Decode
  4093.   { my ($str, $hex) = @_;
  4094.     my $len = length ($str);
  4095.     my $n;
  4096.  
  4097.     if ($len == 2)
  4098.       { my @n = unpack "C2", $str;
  4099.         $n = (($n[0] & 0x3f) << 6) + ($n[1] & 0x3f);
  4100.       }
  4101.     elsif ($len == 3)
  4102.       { my @n = unpack "C3", $str;
  4103.         $n = (($n[0] & 0x1f) << 12) + (($n[1] & 0x3f) << 6) + ($n[2] & 0x3f);
  4104.       }
  4105.     elsif ($len == 4)
  4106.       { my @n = unpack "C4", $str;
  4107.         $n = (($n[0] & 0x0f) << 18) + (($n[1] & 0x3f) << 12) 
  4108.            + (($n[2] & 0x3f) << 6) + ($n[3] & 0x3f);
  4109.       }
  4110.     elsif ($len == 1)    # just to be complete...
  4111.       { $n = ord ($str); }
  4112.     else
  4113.       { croak "bad value [$str] for _XmlUtf8Decode"; }
  4114.  
  4115.     my $char= $hex ? sprintf ("&#x%x;", $n) : "&#$n;";
  4116.     return $char;
  4117.   }
  4118.  
  4119.  
  4120. sub unicode_convert
  4121.   { my $enc= $_[1] ? $_[1] : $_[0]; # so the method can be called on the twig or directly
  4122.     _use( 'Unicode::Map8') or croak "Unicode::Map8 not available, needed for encoding filter: $!";
  4123.     _use( 'Unicode::String') or croak "Unicode::String not available, needed for encoding filter: $!";
  4124.     import Unicode::String qw(utf8);
  4125.     my $sub= eval qq{ { $NO_WARNINGS;
  4126.                         my \$cnv;
  4127.                         BEGIN {  \$cnv= Unicode::Map8->new(\$enc) 
  4128.                                      or croak "Can't create converter to \$enc";
  4129.                               }
  4130.                         sub { return  \$cnv->to8 (utf8(\$_[0])->ucs2); } 
  4131.                       } 
  4132.                     };
  4133.     unless( $sub) { croak $@; }
  4134.     return $sub;
  4135.   }
  4136.  
  4137. sub iconv_convert
  4138.   { my $enc= $_[1] ? $_[1] : $_[0]; # so the method can be called on the twig or directly
  4139.     _use( 'Text::Iconv') or croak "Text::Iconv not available, needed for encoding filter: $!";
  4140.     my $sub= eval qq{ { $NO_WARNINGS;
  4141.                         my \$cnv;
  4142.                         BEGIN { \$cnv = Text::Iconv->new( 'utf8', \$enc) 
  4143.                                      or croak "Can't create iconv converter to \$enc";
  4144.                               }
  4145.                         sub { return  \$cnv->convert( \$_[0]); } 
  4146.                       }       
  4147.                     };
  4148.     unless( $sub)
  4149.       { if( $@=~ m{^Unsupported conversion: Invalid argument})
  4150.           { croak "Unsupported encoding: $enc"; }
  4151.         else
  4152.           { croak $@; }
  4153.       }
  4154.  
  4155.     return $sub;
  4156.   }
  4157.  
  4158. sub encode_convert
  4159.   { my $enc= $_[1] ? $_[1] : $_[0]; # so the method can be called on the twig or directly
  4160.     my $sub=  eval qq{sub { $NO_WARNINGS; return encode( "$enc", \$_[0]); } };
  4161.     croak "can't create Encode-based filter: $@" unless( $sub);
  4162.     return $sub;
  4163.   }
  4164.  
  4165.  
  4166. # XML::XPath compatibility
  4167. sub getRootNode        { return $_[0]; }
  4168. sub getParentNode      { return undef; }
  4169. sub getChildNodes      { my @children= ($_[0]->root); return wantarray ? @children : \@children; }
  4170.  
  4171. sub _weakrefs
  4172.   { return $weakrefs; }
  4173.  
  4174. sub _dump
  4175.   { my $t= shift;
  4176.     my $dump='';
  4177.  
  4178.     $dump="document\n"; # should dump twig level data here
  4179.     if( $t->root) { $dump .= $t->root->_dump( @_); }
  4180.  
  4181.     return $dump;
  4182.     
  4183.   }
  4184.  
  4185.  
  4186. 1;
  4187.  
  4188. ######################################################################
  4189. package XML::Twig::Entity_list;
  4190. ######################################################################
  4191.  
  4192. *isa= *UNIVERSAL::isa;
  4193.  
  4194. sub new
  4195.   { my $class = shift;
  4196.     my $self={ entities => {}, updated => 0};
  4197.  
  4198.     bless $self, $class;
  4199.     return $self;
  4200.  
  4201.   }
  4202.  
  4203. sub add_new_ent
  4204.   { my $ent_list= shift;
  4205.     my $ent= XML::Twig::Entity->new( @_);
  4206.     $ent_list->add( $ent);
  4207.     return $ent_list;
  4208.   }
  4209.  
  4210. sub _add_list
  4211.   { my( $ent_list, $to_add)= @_;
  4212.     my $ents_to_add= $to_add->{entities};
  4213.     return $ent_list unless( $ents_to_add && %$ents_to_add);
  4214.     @{$ent_list->{entities}}{keys %$ents_to_add}= values %$ents_to_add;
  4215.     $ent_list->{updated}=1;
  4216.     return $ent_list;
  4217.   }
  4218.  
  4219. sub add
  4220.   { my( $ent_list, $ent)= @_;
  4221.     $ent_list->{entities}->{$ent->{name}}= $ent;
  4222.     $ent_list->{updated}=1;
  4223.     return $ent_list;
  4224.   }
  4225.  
  4226. sub ent
  4227.   { my( $ent_list, $ent_name)= @_;
  4228.     return $ent_list->{entities}->{$ent_name};
  4229.   }
  4230.  
  4231. # can be called with an entity or with an entity name
  4232. sub delete
  4233.   { my $ent_list= shift;
  4234.     if( isa( ref $_[0], 'XML::Twig::Entity'))
  4235.       { # the second arg is an entity
  4236.         my $ent= shift;
  4237.         delete $ent_list->{entities}->{$ent->{name}};
  4238.       }
  4239.     else
  4240.       { # the second arg was not entity, must be a string then
  4241.         my $name= shift;
  4242.         delete $ent_list->{entities}->{$name};
  4243.       }
  4244.     $ent_list->{updated}=1;
  4245.     return $ent_list;
  4246.   }
  4247.  
  4248. sub print
  4249.   { my ($ent_list, $fh)= @_;
  4250.     my $old_select= defined $fh ? select $fh : undef;
  4251.  
  4252.     foreach my $ent_name ( sort keys %{$ent_list->{entities}})
  4253.       { my $ent= $ent_list->{entities}->{$ent_name};
  4254.         # we have to test what the entity is or un-defined entities can creep in
  4255.         if( isa( $ent, 'XML::Twig::Entity')) { $ent->print(); }
  4256.       }
  4257.     select $old_select if( defined $old_select);
  4258.     return $ent_list;
  4259.   }
  4260.  
  4261. sub text
  4262.   { my ($ent_list)= @_;
  4263.     return join "\n", map { $ent_list->{entities}->{$_}->text} sort keys %{$ent_list->{entities}};
  4264.   }
  4265.  
  4266. # return the list of entity names 
  4267. sub entity_names
  4268.   { my $ent_list= shift;
  4269.     return (sort keys %{$ent_list->{entities}}) ;
  4270.   }
  4271.  
  4272.  
  4273. sub list
  4274.   { my ($ent_list)= @_;
  4275.     return map { $ent_list->{entities}->{$_} } sort keys %{$ent_list->{entities}};
  4276.   }
  4277.  
  4278. 1;
  4279.  
  4280. ######################################################################
  4281. package XML::Twig::Entity;
  4282. ######################################################################
  4283.  
  4284. #*isa= *UNIVERSAL::isa;
  4285.  
  4286. sub new
  4287.   { my( $class, $name, $val, $sysid, $pubid, $ndata, $param)= @_;
  4288.     $class= ref( $class) || $class;
  4289.  
  4290.     my $self={};
  4291.     
  4292.     $self->{name}  = $name;
  4293.     $self->{val}   = $val   if( defined $val  );
  4294.     $self->{sysid} = $sysid if( defined $sysid);
  4295.     $self->{pubid} = $pubid if( defined $pubid);
  4296.     $self->{ndata} = $ndata if( defined $ndata);
  4297.     $self->{param} = $param if( defined $param);
  4298.  
  4299.     bless $self, $class;
  4300.     return $self;
  4301.   }
  4302.  
  4303.  
  4304. sub name  { return $_[0]->{name}; }
  4305. sub val   { return $_[0]->{val}; }
  4306. sub sysid { return defined( $_[0]->{sysid}) ? $_[0]->{sysid} : ''; }
  4307. sub pubid { return defined( $_[0]->{pubid}) ? $_[0]->{pubid} : ''; }
  4308. sub ndata { return defined( $_[0]->{ndata}) ? $_[0]->{ndata} : ''; }
  4309. sub param { return defined( $_[0]->{param}) ? $_[0]->{param} : ''; }
  4310.  
  4311.  
  4312. sub print
  4313.   { my ($ent, $fh)= @_;
  4314.     my $text= $ent->text;
  4315.     if( !defined( $text)) { $text=''; }
  4316.     if( $fh) { print $fh $text . "\n"; }
  4317.     else     { print $text . "\n"; }
  4318.   }
  4319.  
  4320. sub sprint
  4321.   { my ($ent)= @_;
  4322.     my $text= $ent->text;
  4323.     if( !defined( $text)) { $text=''; }
  4324.     return $text;
  4325.   }
  4326. sub text
  4327.   { my ($ent)= @_;
  4328.     #warn "text called: '", $ent->_dump, "'\n";
  4329.     return '' if( !$ent->{name});
  4330.     my @tokens;
  4331.     push @tokens, '<!ENTITY';
  4332.    
  4333.     push @tokens, '%' if( $ent->{param});
  4334.     push @tokens, $ent->{name};
  4335.  
  4336.     if( defined $ent->{val} && !defined( $ent->{sysid}) && !defined($ent->{pubid}) )
  4337.       { push @tokens, _quoted_val( $ent->{val});
  4338.       }
  4339.     elsif( defined $ent->{sysid})
  4340.       { push @tokens, 'PUBLIC', _quoted_val( $ent->{pubid}) if( $ent->{pubid});
  4341.         push @tokens, 'SYSTEM' unless( $ent->{pubid});
  4342.         push @tokens, _quoted_val( $ent->{sysid}); 
  4343.         push @tokens, 'NDATA', $ent->{ndata} if( $ent->{ndata});
  4344.       }
  4345.     return join( ' ', @tokens) . '>';
  4346.   }
  4347.  
  4348. sub _quoted_val
  4349.   { my $q= $_[0]=~ m{"} ? q{'} : q{"};
  4350.     return qq{$q$_[0]$q};
  4351.   }
  4352.  
  4353. sub _dump
  4354.   { my( $ent)= @_; return join( " - ", map { "$_ => '$ent->{$_}'" } grep { defined $ent->{$_} } sort keys %$ent); }
  4355.                 
  4356. 1;
  4357.  
  4358. ######################################################################
  4359. package XML::Twig::Elt;
  4360. ######################################################################
  4361.  
  4362. use Carp;
  4363. *isa= *UNIVERSAL::isa;
  4364.  
  4365. my $CDATA_START    = "<![CDATA[";
  4366. my $CDATA_END      = "]]>";
  4367. my $PI_START       = "<?";
  4368. my $PI_END         = "?>";
  4369. my $COMMENT_START  = "<!--";
  4370. my $COMMENT_END    = "-->";
  4371.  
  4372. my $XMLNS_URI      = 'http://www.w3.org/2000/xmlns/';
  4373.  
  4374.  
  4375. BEGIN
  4376.   { # set some aliases for methods
  4377.     *tag           = *gi; 
  4378.     *name          = *gi; 
  4379.     *set_tag       = *set_gi; 
  4380.     *set_name      = *set_gi; 
  4381.     *find_nodes    = *get_xpath; # as in XML::DOM
  4382.     *findnodes     = *get_xpath; # as in XML::LibXML
  4383.     *field         = *first_child_text;
  4384.     *trimmed_field = *first_child_trimmed_text;
  4385.     *is_field      = *contains_only_text;
  4386.     *is            = *passes;
  4387.     *matches       = *passes;
  4388.     *has_child     = *first_child;
  4389.     *has_children  = *first_child;
  4390.     *all_children_pass = *all_children_are;
  4391.     *all_children_match= *all_children_are;
  4392.     *getElementsByTagName= *descendants;
  4393.     *find_by_tag_name= *descendants_or_self;
  4394.     *unwrap          = *erase;
  4395.     *inner_xml       = *xml_string;
  4396.     *outer_xml       = *sprint;
  4397.   
  4398.     *first_child_is  = *first_child_matches;
  4399.     *last_child_is   = *last_child_matches;
  4400.     *next_sibling_is = *next_sibling_matches;
  4401.     *prev_sibling_is = *prev_sibling_matches;
  4402.     *next_elt_is     = *next_elt_matches;
  4403.     *prev_elt_is     = *prev_elt_matches;
  4404.     *parent_is       = *parent_matches;
  4405.     *child_is        = *child_matches;
  4406.     *inherited_att   = *inherit_att;
  4407.  
  4408.     *sort_children_by_value= *sort_children_on_value;
  4409.  
  4410.     *has_atts= *att_nb;
  4411.  
  4412.     # imports from XML::Twig
  4413.     *_is_fh= *XML::Twig::_is_fh;
  4414.  
  4415.     # XML::XPath compatibility
  4416.     *string_value       = *text;
  4417.     *toString           = *sprint;
  4418.     *getName            = *gi;
  4419.     *getRootNode        = *twig;  
  4420.     *getNextSibling     = *_next_sibling;
  4421.     *getPreviousSibling = *_prev_sibling;
  4422.     *isElementNode      = *is_elt;
  4423.     *isTextNode         = *is_text;
  4424.     *isPI               = *is_pi;
  4425.     *isPINode           = *is_pi;
  4426.     *isProcessingInstructionNode= *is_pi;
  4427.     *isComment          = *is_comment;
  4428.     *isCommentNode      = *is_comment;
  4429.     *getTarget          = *target;
  4430.     *getFirstChild      = *_first_child;
  4431.     *getLastChild      = *_last_child;
  4432.  
  4433.     # try using weak references
  4434.     # test whether we can use weak references
  4435.     { local $SIG{__DIE__};
  4436.       if( eval 'require Scalar::Util' && defined( &Scalar::Util::weaken) )
  4437.         { import Scalar::Util qw(weaken); }
  4438.       elsif( eval 'require WeakRef')
  4439.         { import WeakRef; }
  4440.     }
  4441. }
  4442.  
  4443.  
  4444. # can be called as XML::Twig::Elt->new( [[$gi, $atts, [@content]])
  4445. # - gi is an optional gi given to the element
  4446. # - $atts is a hashref to attributes for the element
  4447. # - @content is an optional list of text and elements that will
  4448. #   be inserted under the element 
  4449. sub new 
  4450.   { my $class= shift;
  4451.     $class= ref $class || $class;
  4452.     my $elt  = {};
  4453.     bless ($elt, $class);
  4454.  
  4455.     return $elt unless @_;
  4456.  
  4457.     # if a gi is passed then use it
  4458.     my $gi= shift;
  4459.     $elt->{gi}=$XML::Twig::gi2index{$gi} or $elt->set_gi( $gi);
  4460.  
  4461.  
  4462.     my $atts= ref $_[0] eq 'HASH' ? shift : undef;
  4463.  
  4464.     if( $atts && defined $atts->{$CDATA})
  4465.       { delete $atts->{$CDATA};
  4466.  
  4467.         my $cdata= $class->new( $CDATA => @_);
  4468.         return $class->new( $gi, $atts, $cdata);
  4469.       }
  4470.  
  4471.     if( $gi eq $PCDATA)
  4472.       { if( grep { ref $_ } @_) { croak "element $PCDATA can only be created from text"; }
  4473.         $elt->_set_pcdata( join( '', @_)); 
  4474.       }
  4475.     elsif( $gi eq $ENT)
  4476.       { $elt->{ent}=  shift; }
  4477.     elsif( $gi eq $CDATA)
  4478.       { if( grep { ref $_ } @_) { croak "element $CDATA can only be created from text"; }
  4479.         $elt->_set_cdata( join( '', @_)); 
  4480.       }
  4481.     elsif( $gi eq $COMMENT)
  4482.       { if( grep { ref $_ } @_) { croak "element $COMMENT can only be created from text"; }
  4483.         $elt->_set_comment( join( '', @_)); 
  4484.       }
  4485.     elsif( $gi eq $PI)
  4486.       { if( grep { ref $_ } @_) { croak "element $PI can only be created from text"; }
  4487.         $elt->_set_pi( shift, join( '', @_));
  4488.       }
  4489.     else
  4490.       { # the rest of the arguments are the content of the element
  4491.         if( @_)
  4492.           { $elt->set_content( @_); }
  4493.         else
  4494.           { $elt->{empty}=  1;    }
  4495.       }
  4496.  
  4497.     if( $atts)
  4498.       { # the attribute hash can be used to pass the asis status 
  4499.         if( defined $atts->{$ASIS})  { $elt->set_asis(  $atts->{$ASIS} ); delete $atts->{$ASIS};  }
  4500.         if( defined $atts->{$EMPTY}) { $elt->{empty}=  $atts->{$EMPTY}; delete $atts->{$EMPTY}; }
  4501.         if( keys %$atts) { $elt->set_atts( $atts); }
  4502.         $elt->_set_id( $atts->{$ID}) if( $atts->{$ID});
  4503.       }
  4504.  
  4505.     return $elt;
  4506.   }
  4507.  
  4508. # optimized version of $elt->new( PCDATA, $text);
  4509. sub _new_pcdata
  4510.   { my $class= $_[0];
  4511.     $class= ref $class || $class;
  4512.     my $elt  = {};
  4513.     bless $elt, $class;
  4514.     $elt->{gi}=$XML::Twig::gi2index{$PCDATA} or $elt->set_gi( $PCDATA);
  4515.     $elt->_set_pcdata( $_[1]);
  4516.     return $elt;
  4517.   }
  4518.     
  4519. # this function creates an XM:::Twig::Elt from a string
  4520. # it is quite clumsy at the moment, as it just creates a
  4521. # new twig then returns its root
  4522. # there might also be memory leaks there
  4523. # additional arguments are passed to new XML::Twig
  4524. sub parse
  4525.   { my $class= shift;
  4526.     if( ref( $class)) { $class= ref( $class); }
  4527.     my $string= shift;
  4528.     my %args= @_;
  4529.     my $t= XML::Twig->new(%args);
  4530.     $t->parse( $string);
  4531.     my $elt= $t->root;
  4532.     # clean-up the node 
  4533.     delete $elt->{twig};         # get rid of the twig data
  4534.     delete $elt->{twig_current}; # better get rid of this too
  4535.     if( $t->{twig_id_list}) { $elt->{twig_id_list}= $t->{twig_id_list}; }
  4536.     return $elt;
  4537.   }
  4538.    
  4539. sub set_inner_xml
  4540.   { my( $elt, $xml, @args)= @_;
  4541.     my $new_elt= $elt->parse( "<dummy>$xml</dummy>", @args);
  4542.     $elt->cut_children;
  4543.     $new_elt->paste_first_child( $elt);
  4544.     $new_elt->erase;
  4545.     return $elt;
  4546.   }
  4547.   
  4548. sub set_inner_html
  4549.   { my( $elt, $html)= @_;
  4550.     my $t= XML::Twig->new->parse_html( "<html>$html</html>");
  4551.     my $new_elt= $t->root;
  4552.     if( $elt->tag eq 'head')
  4553.       { $new_elt->first_child( 'head')->unwrap;
  4554.         $new_elt->first_child( 'body')->cut;
  4555.       }
  4556.     elsif( $elt->tag ne 'html')
  4557.       { $new_elt->first_child( 'head')->cut;
  4558.         $new_elt->first_child( 'body')->unwrap;
  4559.       }
  4560.     $new_elt->cut;
  4561.     $elt->cut_children;
  4562.     $new_elt->paste_first_child( $elt);
  4563.     $new_elt->erase;
  4564.     return $elt;
  4565.   }
  4566.  
  4567. sub set_gi 
  4568.   { my ($elt, $gi)= @_;
  4569.     unless( defined $XML::Twig::gi2index{$gi})
  4570.       { # new gi, create entries in %gi2index and @index2gi
  4571.         push  @XML::Twig::index2gi, $gi;
  4572.         $XML::Twig::gi2index{$gi}= $#XML::Twig::index2gi;
  4573.       }
  4574.     $elt->{gi}= $XML::Twig::gi2index{$gi};
  4575.     return $elt; 
  4576.   }
  4577.  
  4578. sub gi  { return $XML::Twig::index2gi[$_[0]->{gi}]; }
  4579.  
  4580. sub local_name 
  4581.   { my $elt= shift;
  4582.     return _local_name( $XML::Twig::index2gi[$elt->{'gi'}]);
  4583.   }
  4584.  
  4585. sub ns_prefix
  4586.   { my $elt= shift;
  4587.     return _ns_prefix( $XML::Twig::index2gi[$elt->{'gi'}]);
  4588.   }
  4589.  
  4590. # namespace prefix for any qname (can be used for elements or attributes)
  4591. sub _ns_prefix
  4592.   { my $qname= shift;
  4593.     if( $qname=~ m{^([^:]*):})
  4594.       { return $1; }
  4595.     else
  4596.       { return( ''); } # should it be '' ?
  4597.   }
  4598.  
  4599. # local name for any qname (can be used for elements or attributes)
  4600. sub _local_name
  4601.   { my $qname= shift;
  4602.     (my $local= $qname)=~ s{^[^:]*:}{};
  4603.     return $local;
  4604.   }
  4605.  
  4606. BEGIN 
  4607.   { my %DEFAULT_NS= ( xml   => "http://www.w3.org/XML/1998/namespace",
  4608.                       xmlns => "http://www.w3.org/2000/xmlns/",
  4609.                     );
  4610.  
  4611.     #sub get_namespace
  4612.     sub namespace ## no critic (Subroutines::ProhibitNestedSubs);
  4613.       { my $elt= shift;
  4614.         my $prefix= defined $_[0] ? shift() : $elt->ns_prefix;
  4615.         my $ns_att= $prefix ? "xmlns:$prefix" : "xmlns";
  4616.         my $expanded= $DEFAULT_NS{$prefix} || $elt->_inherit_att_through_cut( $ns_att) || '';
  4617.         return $expanded;
  4618.       }
  4619.  
  4620.     sub declare_missing_ns ## no critic (Subroutines::ProhibitNestedSubs);
  4621.       { my $root= shift;
  4622.         my %missing_prefix;
  4623.         my $map= $root->_current_ns_prefix_map;
  4624.  
  4625.         foreach my $prefix (keys %$map)
  4626.           { my $prefix_att= $prefix eq '#default' ? 'xmlns' : "xmlns:$prefix";
  4627.             if( ! $root->{'att'}->{$prefix_att}) 
  4628.               { $root->set_att( $prefix_att => $map->{$prefix}); }
  4629.           }
  4630.         return $root;
  4631.       }
  4632.  
  4633.   }
  4634.  
  4635. sub _current_ns_prefix_map
  4636.   { my( $elt)= shift;
  4637.     my $map;
  4638.     while( $elt)
  4639.       { foreach my $att ($elt->att_names)
  4640.           { my $prefix= $att eq 'xmlns'        ? '#default'
  4641.                       : $att=~ m{^xmlns:(.*)$} ? $1
  4642.                       : next
  4643.                       ;
  4644.             if( ! exists $map->{$prefix}) { $map->{$prefix}= $elt->{'att'}->{$att}; }
  4645.           }
  4646.         $elt= $elt->{parent} || $elt->former_parent;
  4647.       }
  4648.     return $map;
  4649.   }
  4650.  
  4651. sub set_ns_decl
  4652.   { my( $elt, $uri, $prefix)= @_;
  4653.     my $ns_att=  $prefix ? "xmlns:$prefix" : 'xmlns';
  4654.     $elt->set_att( $ns_att => $uri);
  4655.     return $elt;
  4656.   }
  4657.  
  4658. sub set_ns_as_default
  4659.   { my( $root, $uri)= @_;
  4660.     my @ns_decl_to_remove;
  4661.     foreach my $elt ($root->descendants_or_self)
  4662.       { if( $elt->_ns_prefix && $elt->namespace eq $uri) 
  4663.           { $elt->set_tag( $elt->local_name); }
  4664.         # store any namespace declaration for that uri
  4665.         foreach my $ns_decl (grep { $_=~ m{xmlns(:|$)} && $elt->{'att'}->{$_} eq $uri } $elt->att_names)
  4666.           { push @ns_decl_to_remove, [$elt, $ns_decl]; }
  4667.       }
  4668.     $root->set_ns_decl( $uri);
  4669.     # now remove the ns declarations (if done earlier then descendants of an element with the ns declaration
  4670.     # are not considered being in the namespace
  4671.     foreach my $ns_decl_to_remove ( @ns_decl_to_remove)
  4672.       { my( $elt, $ns_decl)= @$ns_decl_to_remove;
  4673.         $elt->del_att( $ns_decl);
  4674.       }
  4675.     
  4676.     return $root;
  4677.   }
  4678.      
  4679.  
  4680.  
  4681. # return #ELT for an element and #PCDATA... for others
  4682. sub get_type
  4683.   { my $gi_nb= $_[0]->{gi}; # the number, not the string
  4684.     return $ELT if( $gi_nb >= $XML::Twig::SPECIAL_GI);
  4685.     return $_[0]->gi;
  4686.   }
  4687.  
  4688. # return the gi if it's a "real" element, 0 otherwise
  4689. sub is_elt
  4690.   { if(  $_[0]->{gi} >=  $XML::Twig::SPECIAL_GI)
  4691.      { return $_[0]->gi; }
  4692.     else
  4693.       { return 0; }
  4694.   }
  4695.  
  4696.  
  4697. sub is_pcdata
  4698.   { my $elt= shift;
  4699.     return (exists $elt->{'pcdata'});
  4700.   }
  4701.  
  4702. sub is_cdata
  4703.   { my $elt= shift;
  4704.     return (exists $elt->{'cdata'});
  4705.   }
  4706.  
  4707. sub is_pi
  4708.   { my $elt= shift;
  4709.     return (exists $elt->{'target'});
  4710.   }
  4711.  
  4712. sub is_comment
  4713.   { my $elt= shift;
  4714.     return (exists $elt->{'comment'});
  4715.   }
  4716.  
  4717. sub is_ent
  4718.   { my $elt= shift;
  4719.     return (exists $elt->{ent} || $elt->{ent_name});
  4720.   } 
  4721.  
  4722.  
  4723. sub is_text
  4724.   { my $elt= shift;
  4725.     return (exists( $elt->{'pcdata'}) || (exists $elt->{'cdata'}));
  4726.   }
  4727.  
  4728. sub is_empty
  4729.   { return $_[0]->{empty} || 0; }
  4730.  
  4731. sub set_empty
  4732.   { $_[0]->{empty}= defined( $_[1]) ? $_[1] : 1; return $_[0]; }
  4733.  
  4734. sub set_not_empty
  4735.   { delete $_[0]->{empty} if( $_[0]->{'empty'}); return $_[0]; }
  4736.  
  4737.  
  4738. sub set_asis
  4739.   { my $elt=shift;
  4740.  
  4741.     foreach my $descendant ($elt, $elt->_descendants )
  4742.       { $descendant->{asis}= 1;
  4743.         if( (exists $descendant->{'cdata'}))
  4744.           { $descendant->{gi}=$XML::Twig::gi2index{$PCDATA} or $descendant->set_gi( $PCDATA);
  4745.             $descendant->_set_pcdata( $descendant->{cdata});
  4746.           }
  4747.  
  4748.       }
  4749.     return $elt;
  4750.   }
  4751.  
  4752. sub set_not_asis
  4753.   { my $elt=shift;
  4754.     foreach my $descendant ($elt, $elt->descendants)
  4755.       { delete $descendant->{asis} if $descendant->{asis};}
  4756.     return $elt;
  4757.   }
  4758.  
  4759. sub is_asis
  4760.   { return $_[0]->{asis}; }
  4761.  
  4762. sub closed 
  4763.   { my $elt= shift;
  4764.     my $t= $elt->twig || return;
  4765.     my $curr_elt= $t->{twig_current};
  4766.     return 1 unless( $curr_elt);
  4767.     return $curr_elt->in( $elt);
  4768.   }
  4769.  
  4770. sub set_pcdata 
  4771.   { my( $elt, $pcdata)= @_;
  4772.   
  4773.     if( $elt->{extra_data_in_pcdata})
  4774.       { _try_moving_extra_data( $elt, $pcdata);
  4775.       }
  4776.     $elt->{pcdata}= $pcdata;
  4777.     return $elt; 
  4778.   }
  4779.  
  4780. sub _extra_data_in_pcdata      { return $_[0]->{extra_data_in_pcdata}; }
  4781. sub _set_extra_data_in_pcdata  { $_[0]->{extra_data_in_pcdata}= $_[1]; return $_[0]; }
  4782. sub _del_extra_data_in_pcdata  { delete $_[0]->{extra_data_in_pcdata}; return $_[0]; }
  4783. sub _unshift_extra_data_in_pcdata { unshift @{shift()->{extra_data_in_pcdata}}, { text => shift(), offset => shift() }; }
  4784. sub _push_extra_data_in_pcdata    { push @{shift()->{extra_data_in_pcdata}},    { text => shift(), offset => shift() }; }
  4785.  
  4786. sub _extra_data_before_end_tag     { return $_[0]->{extra_data_before_end_tag} || ''; }
  4787. sub _set_extra_data_before_end_tag { $_[0]->{extra_data_before_end_tag}= $_[1]; return $_[0]}
  4788. sub _del_extra_data_before_end_tag { delete $_[0]->{extra_data_before_end_tag}; return $_[0]}
  4789. sub _prefix_extra_data_before_end_tag 
  4790.   { my( $elt, $data)= @_;
  4791.     if($elt->{extra_data_before_end_tag})
  4792.       { $elt->{extra_data_before_end_tag}= $data . $elt->{extra_data_before_end_tag}; }
  4793.     else  
  4794.       { $elt->{extra_data_before_end_tag}= $data; }
  4795.     return $elt;
  4796.   }
  4797.  
  4798. # internal, in cases where we know there is no extra_data (inlined anyway!)
  4799. sub _set_pcdata { $_[0]->{pcdata}= $_[1]; }
  4800.  
  4801. # try to figure out if we can keep the extra_data around
  4802. sub _try_moving_extra_data
  4803.   { my( $elt, $modified)=@_;
  4804.     my $initial= $elt->{pcdata};
  4805.     my $cpis= $elt->{extra_data_in_pcdata};
  4806.  
  4807.     if( (my $offset= index( $modified, $initial)) != -1) 
  4808.       { # text has been added
  4809.         foreach (@$cpis) { $_->{offset}+= $offset; }
  4810.       }
  4811.     elsif( ($offset= index( $initial, $modified)) != -1)
  4812.       { # text has been cut
  4813.         my $len= length( $modified);
  4814.         foreach my $cpi (@$cpis) { $cpi->{offset} -= $offset; }
  4815.         $elt->_set_extra_data_in_pcdata( [ grep { $_->{offset} >= 0 && $_->{offset} < $len } @$cpis ]);
  4816.       } 
  4817.     else
  4818.       {    _match_extra_data_words( $elt, $initial, $modified)
  4819.         || _match_extra_data_chars( $elt, $initial, $modified)
  4820.         || $elt->_del_extra_data_in_pcdata;
  4821.       }
  4822.   }
  4823.  
  4824. sub _match_extra_data_words
  4825.   { my( $elt, $initial, $modified)= @_;
  4826.     my @initial= split /\b/, $initial; 
  4827.     my @modified= split /\b/, $modified;
  4828.        
  4829.     return _match_extra_data( $elt, length( $initial), \@initial, \@modified);
  4830.   }
  4831.   
  4832. sub _match_extra_data_chars
  4833.   { my( $elt, $initial, $modified)= @_;
  4834.     my @initial= split //, $initial; 
  4835.     my @modified= split //, $modified;
  4836.        
  4837.     return _match_extra_data( $elt, length( $initial), \@initial, \@modified);
  4838.   }
  4839.  
  4840. sub _match_extra_data
  4841.   { my( $elt, $length, $initial, $modified)= @_;
  4842.         
  4843.     my $cpis= $elt->{extra_data_in_pcdata};
  4844.  
  4845.     if( @$initial <= @$modified)
  4846.       { 
  4847.         my( $ok, $positions, $offsets)= _pos_offset( $initial, $modified);
  4848.         if( $ok) 
  4849.           { my $offset=0;
  4850.             my $pos= shift @$positions;
  4851.             foreach my $cpi (@$cpis)
  4852.               { while( $cpi->{offset} >= $pos)
  4853.                   { $offset= shift @$offsets; 
  4854.                     $pos= shift @$positions || $length +1;
  4855.                   }
  4856.                 $cpi->{offset} += $offset;
  4857.               }
  4858.             return 1;
  4859.           }
  4860.       }
  4861.     else
  4862.       { my( $ok, $positions, $offsets)= _pos_offset( $modified, $initial);
  4863.         if( $ok)
  4864.           { #print STDERR "pos:    ", join( ':', @$positions), "\n",
  4865.             #             "offset: ", join( ':', @$offsets), "\n";
  4866.             my $offset=0;
  4867.             my $pos= shift @$positions;
  4868.             my $prev_pos= 0;
  4869.             
  4870.             foreach my $cpi (@$cpis)
  4871.               { while( $cpi->{offset} >= $pos)
  4872.                   { $offset= shift @$offsets;
  4873.                     $prev_pos= $pos;
  4874.                     $pos= shift @$positions || $length +1;
  4875.                   }
  4876.                 $cpi->{offset} -= $offset;
  4877.                 if( $cpi->{offset} < $prev_pos) { delete $cpi->{text}; }
  4878.               }
  4879.             $elt->_set_extra_data_in_pcdata( [ grep { exists $_->{text} } @$cpis ]);
  4880.             return 1;
  4881.           }
  4882.       }
  4883.     return 0;
  4884.   }
  4885.  
  4886.           
  4887. sub _pos_offset
  4888.   { my( $short, $long)= @_;
  4889.     my( @pos, @offset);
  4890.     my( $s_length, $l_length)=(0,0);
  4891.     while (@$short)
  4892.       { my $s_word= shift @$short;
  4893.         my $l_word= shift @$long;
  4894.         if( $s_word ne $l_word)
  4895.           { while( @$long && $s_word ne $l_word)
  4896.               { $l_length += length( $l_word);
  4897.                 $l_word= shift @$long;
  4898.               }
  4899.             if( !@$long && $s_word ne $l_word) { return 0; }
  4900.             push @pos, $s_length;
  4901.             push @offset, $l_length - $s_length;
  4902.           }
  4903.         my $length= length( $s_word);
  4904.         $s_length += $length;
  4905.         $l_length += $length;
  4906.       }
  4907.     return( 1, \@pos, \@offset);
  4908.   }
  4909.  
  4910. sub append_pcdata
  4911.   { delete $_[0]->{empty};
  4912.     $_[0]->{'pcdata'}.= $_[1];
  4913.     return $_[0]; 
  4914.   }
  4915.  
  4916. sub pcdata        { return $_[0]->{pcdata}; }
  4917.  
  4918.  
  4919. sub append_extra_data 
  4920.   {  $_[0]->{extra_data}.= $_[1];
  4921.      return $_[0]; 
  4922.   }
  4923.   
  4924. sub set_extra_data 
  4925.   { $_[0]->{extra_data}= $_[1];
  4926.     return $_[0]; 
  4927.   }
  4928. sub extra_data { return $_[0]->{extra_data} || ''; }
  4929.  
  4930. sub set_target 
  4931.   { my( $elt, $target)= @_;
  4932.     $elt->{target}= $target;
  4933.     return $elt; 
  4934.   }
  4935. sub target { return $_[0]->{target}; }
  4936.  
  4937. sub set_data 
  4938.   { $_[0]->{'data'}= $_[1]; 
  4939.     return $_[0];
  4940.   }
  4941. sub data { return $_[0]->{data}; }
  4942.  
  4943. sub set_pi
  4944.   { my $elt= shift;
  4945.     unless( $elt->{gi} == $XML::Twig::gi2index{$PI})
  4946.       { $elt->cut_children;
  4947.         $elt->{gi}=$XML::Twig::gi2index{$PI} or $elt->set_gi( $PI);
  4948.       }
  4949.     return $elt->_set_pi( @_);
  4950.   }
  4951.  
  4952. sub _set_pi
  4953.   { $_[0]->{target}=  $_[1];
  4954.     $_[0]->{data}=  $_[2];
  4955.     return $_[0]; 
  4956.   }
  4957.  
  4958. sub pi_string { my $string= $PI_START . $_[0]->{target};
  4959.                 my $data= $_[0]->{data};
  4960.                 if( defined( $data) && $data ne '') { $string .= " $data"; }
  4961.                 $string .= $PI_END ;
  4962.                 return $string;
  4963.               }
  4964.  
  4965. sub set_comment
  4966.   { my $elt= shift;
  4967.     unless( $elt->{gi} == $XML::Twig::gi2index{$COMMENT})
  4968.       { $elt->cut_children;
  4969.         $elt->{gi}=$XML::Twig::gi2index{$COMMENT} or $elt->set_gi( $COMMENT);
  4970.       }
  4971.     return $elt->_set_comment( @_);
  4972.   }
  4973.  
  4974. sub _set_comment   { $_[0]->{comment}= $_[1]; return $_[0]; }
  4975. sub comment        { return $_[0]->{comment}; }
  4976. sub comment_string { return $COMMENT_START . $_[0]->{comment} . $COMMENT_END; }
  4977.  
  4978. sub set_ent  { $_[0]->{ent}= $_[1]; return $_[0]; }
  4979. sub ent      { return $_[0]->{ent}; }
  4980. sub ent_name { return substr( $_[0]->{ent}, 1, -1);}
  4981.  
  4982. sub set_cdata 
  4983.   { my $elt= shift;
  4984.     unless( $elt->{gi} == $XML::Twig::gi2index{$CDATA})
  4985.       { $elt->cut_children;
  4986.         $elt->insert_new_elt( first_child => $CDATA, @_);
  4987.         return $elt;
  4988.       }
  4989.     return $elt->_set_cdata( @_);
  4990.   }
  4991.   
  4992. sub _set_cdata 
  4993.   { delete $_[0]->{empty};
  4994.     $_[0]->{cdata}= $_[1]; 
  4995.     return $_[0];
  4996.   }
  4997.  
  4998. sub append_cdata
  4999.   { $_[0]->{cdata}.= $_[1]; 
  5000.     return $_[0];
  5001.   }
  5002. sub cdata { return $_[0]->{cdata}; }
  5003.  
  5004.  
  5005. sub contains_only_text
  5006.   { my $elt= shift;
  5007.     return 0 unless $elt->is_elt;
  5008.     foreach my $child ($elt->_children)
  5009.       { return 0 if $child->is_elt; }
  5010.     return $elt;
  5011.   } 
  5012.   
  5013. sub contains_only
  5014.   { my( $elt, $exp)= @_;
  5015.     my @children= do { my $elt= $elt; my @children=(); my $child= $elt->{first_child}; while( $child) { push @children, $child; $child= $child->{next_sibling}; } @children; };
  5016.     foreach my $child (@children)
  5017.       { return 0 unless $child->is( $exp); }
  5018.     return @children || 1;
  5019.   } 
  5020.  
  5021. sub contains_a_single
  5022.   { my( $elt, $exp)= @_;
  5023.     my $child= $elt->{first_child} or return 0;
  5024.     return 0 unless $child->passes( $exp);
  5025.     return 0 if( $child->{next_sibling});
  5026.     return $child;
  5027.   } 
  5028.  
  5029.  
  5030. sub root 
  5031.   { my $elt= shift;
  5032.     while( $elt->{parent}) { $elt= $elt->{parent}; }
  5033.     return $elt;
  5034.   }
  5035.  
  5036. sub _root_through_cut
  5037.   { my $elt= shift;
  5038.     while( $elt->{parent} || $elt->former_parent) { $elt= $elt->{parent} || $elt->former_parent; }
  5039.     return $elt;
  5040.   }
  5041.  
  5042. sub twig 
  5043.   { my $elt= shift;
  5044.     my $root= $elt->root;
  5045.     return $root->{twig};
  5046.   }
  5047.  
  5048. sub _twig_through_cut
  5049.   { my $elt= shift;
  5050.     my $root= $elt->_root_through_cut;
  5051.     return $root->{twig};
  5052.   }
  5053.  
  5054.  
  5055. # returns undef or the element, depending on whether $elt passes $cond
  5056. # $cond can be
  5057. # - empty: the element passes the condition
  5058. # - ELT ('#ELT'): the element passes the condition if it is a "real" element
  5059. # - TEXT ('#TEXT'): the element passes if it is a CDATA or PCDATA element
  5060. # - a string with an XPath condition (only a subset of XPath is actually
  5061. #   supported).
  5062. # - a regexp: the element passes if its gi matches the regexp
  5063. # - a code ref: the element passes if the code, applied on the element,
  5064. #   returns true
  5065.  
  5066. my %cond_cache; # expression => coderef
  5067.  
  5068. sub reset_cond_cache { %cond_cache=(); }
  5069.  
  5070.    sub _install_cond
  5071.     { my $cond= shift;
  5072.       my $test;
  5073.       my $init='';
  5074.  
  5075.       my $original_cond= $cond;
  5076.  
  5077.       my $not= ($cond=~ s{^\s*!}{}) ? '!' : '';
  5078.  
  5079.       if( ref $cond eq 'CODE') { return $cond; }
  5080.     
  5081.       if( ref $cond eq 'Regexp')
  5082.         { $test = qq{(\$_[0]->gi=~ /$cond/)}; }
  5083.       else
  5084.         { # the condition is a string
  5085.           if( $cond eq $ELT)     
  5086.             { $test = qq{\$_[0]->is_elt}; }
  5087.           elsif( $cond eq $TEXT) 
  5088.             { $test = qq{\$_[0]->is_text}; }
  5089.           elsif( $cond=~ m{^\s*($REG_NAME_WC)\s*$}o)                  
  5090.             { $test= _gi_test( $1); } 
  5091.           elsif( $cond=~ m{^\s*($REG_REGEXP)\s*$}o)
  5092.             { # /regexp/
  5093.               $test = qq{ \$_[0]->gi=~ $1 }; 
  5094.             }
  5095.           elsif( $cond=~ m{^\s*($REG_NAME_WC)?\s*  # $1
  5096.                            \[\s*(-?)\s*(\d+)\s*\] #   [$2]
  5097.                            \s*$}xo
  5098.                )
  5099.             { my( $gi, $neg, $index)= ($1, $2, $3);
  5100.               my $siblings= $neg ? q{$_[0]->_next_siblings} : q{$_[0]->_prev_siblings};
  5101.               if( $gi && ($gi ne '*')) 
  5102.                 #{ $test= qq{((\$_[0]->gi eq "$gi") && (scalar( grep { \$_->gi eq "$gi" } $siblings) + 1 == $index))}; }
  5103.                 { $test= _and( _gi_test( $gi), qq{ (scalar( grep { \$_->gi eq "$gi" } $siblings) + 1 == $index)}); }
  5104.               else
  5105.                 { $test= qq{(scalar( $siblings) + 1 == $index)}; }
  5106.             }
  5107.           elsif( $cond=~ m{^\s*\.([\w-]+)\s*$}o)
  5108.             { # .class
  5109.               my $class= $1;
  5110.               $test = qq{(\$_[0]->in_class( "$class")) }; 
  5111.             }
  5112.           elsif( $cond=~ m{^\s*($REG_NAME_WC?)\s*($REG_PREDICATE)\s*$})
  5113.             { my( $gi, $predicate)= ( $1, $2);
  5114.               $test = _and( _gi_test( $gi), _parse_predicate_in_step( $predicate));
  5115.             }
  5116.           elsif( $cond=~ m{^\s*($REG_NAKED_PREDICATE)\s*$})
  5117.             { $test .=   _parse_predicate_in_step( $1); }
  5118.           else
  5119.             { croak "wrong navigation condition '$original_cond' ($@)"; }
  5120.         }
  5121.  
  5122.       #warn "init: '$init' - test: '$test'\n";
  5123.  
  5124.       my $sub= qq{sub { $NO_WARNINGS; $init; return $not($test) ? \$_[0] : undef; } };
  5125.       my $s= eval $sub; 
  5126.       #warn "cond: $cond\n$sub\n";
  5127.       if( $@) 
  5128.         { croak "wrong navigation condition '$original_cond' ($@);" }
  5129.       return $s;
  5130.     }
  5131.  
  5132.   sub _gi_test
  5133.     { my( $full_gi)= @_;
  5134.  
  5135.       # optimize if the gi exists, including the case where the gi includes a dot
  5136.       my $index= $XML::Twig::gi2index{$full_gi};
  5137.       if( $index) { return qq{\$_[0]->{gi} == $index}; }
  5138.  
  5139.       my( $gi, $class)= $full_gi=~ m{^(.*?)(?:\.([^.]*))?$};
  5140.  
  5141.       my $gi_test='';
  5142.       if( $gi && $gi ne '*' )
  5143.         { # 2 options, depending on whether the gi exists in gi2index
  5144.           # start optimization
  5145.           my $index= $XML::Twig::gi2index{$gi};
  5146.           if( $index)
  5147.             { # the gi exists, use its index as a faster shortcut
  5148.               $gi_test = qq{\$_[0]->{gi} == $index};
  5149.             }
  5150.           else
  5151.           # end optimization
  5152.             { # it does not exist (but might be created later), compare the strings
  5153.               $gi_test = qq{ \$_[0]->gi eq "$gi"}; 
  5154.             }
  5155.         }
  5156.       else
  5157.         { $gi_test= 1; }
  5158.  
  5159.       my $class_test='';
  5160.       #warn "class: '$class'";
  5161.       if( $class)
  5162.         { $class_test = qq{ defined( \$_[0]->{att}->{class}) && \$_[0]->{att}->{class}=~ m{\\b$class\\b} }; }
  5163.       #warn "gi_test: '$gi_test' - class_test: '$class_test' returning ",  _and( $gi_test, $class_test);
  5164.       return _and( $gi_test, $class_test);
  5165.   }
  5166.  
  5167.  
  5168.   # input: the original predicate
  5169.   sub _parse_predicate_in_step
  5170.     { my $cond= shift; 
  5171.       my %PERL_ALPHA_TEST= ( '=' => ' eq ', '!=' => ' ne ', '>' => ' gt ', '>=' => ' ge ', '<' => ' lt ', '<=' => ' le ');
  5172.  
  5173.       $cond=~ s{^\s*\[\s*}{};
  5174.       $cond=~ s{\s*\]\s*$}{};
  5175.       $cond=~ s{(   ($REG_STRING|$REG_REGEXP)            # strings or regexps
  5176.                    |\@($REG_NAME)(?=\s*(?:[><=!]|!~|=~)) # @att (followed by a comparison operator)
  5177.                    |\@($REG_NAME)                        # @att (not followed by a comparison operator)
  5178.                    |=~|!~                                # matching operators
  5179.                    |([><]=?|=|!=)(?=\s*[\d+-])           # test before a number
  5180.                    |([><]=?|=|!=)                        # test, other cases
  5181.                    |($REG_FUNCTION)                      # no arg functions
  5182.                    # this bit is a mess, but it is the only solution with this half-baked parser
  5183.                    |((?:string|text)\(\s*$REG_NAME\s*\)\s*$REG_MATCH\s*$REG_REGEXP) # string( child) =~ /regexp/
  5184.                    |((?:string|text)\(\s*$REG_NAME\s*\)\s*!?=\s*$REG_VALUE)         # string( child) = "value" (or !=)
  5185.                    |((?:string|text)\(\s*$REG_NAME\s*\)\s*[<>]=?\s*$REG_VALUE)      # string( child) > "value"
  5186.                    |(and|or)
  5187.                 )}
  5188.                { my( $token, $string, $att, $bare_att, $num_test, $alpha_test, $func, $string_regexp, $string_eq, $string_test, $and_or)
  5189.                  = ( $1,     $2,      $3,   $4,        $5,        $6,          $7,    $8,             $9,         $10,          $11);
  5190.       
  5191.                  if( defined $string)   { $token }
  5192.                  elsif( $att)           { "( \$_[0]->{att} && exists( \$_[0]->{att}->{'$att'}) && \$_[0]->{att}->{'$att'})"; }
  5193.                  elsif( $bare_att)      { "(\$_[0]->{att} && defined( \$_[0]->{att}->{'$bare_att'}))"; }
  5194.                  elsif( $num_test && ($num_test eq '=') ) { "==" } # others tests are unchanged
  5195.                  elsif( $alpha_test)    { $PERL_ALPHA_TEST{$alpha_test} }
  5196.                  elsif( $func && $func=~ m{^(?:string|text)})
  5197.                                         { "\$_[0]->text"; }
  5198.                  elsif( $string_regexp && $string_regexp =~ m{(?:string|text)\(\s*($REG_NAME)\s*\)\s*($REG_MATCH)\s*($REG_REGEXP)})
  5199.                                         { "(XML::Twig::_first_n { (\$_->gi eq '$1') && (\$_->text $2 $3) } 1, \$_[0]->_children)"; }
  5200.                  elsif( $string_eq     && $string_eq     =~ m{(?:string|text)\(\s*($REG_NAME)\s*\)\s*(!?=)\s*($REG_VALUE)})
  5201.                                         {"(XML::Twig::_first_n { (\$_->gi eq '$1') && (\$_->text $PERL_ALPHA_TEST{$2} $3) } 1, \$_[0]->_children)"; }
  5202.                  elsif( $string_test   && $string_test   =~ m{(?:string|text)\(\s*($REG_NAME)\s*\)\s*([<>]=?)\s*($REG_VALUE)})
  5203.                                         { "(XML::Twig::_first_n { (\$_->gi eq '$1') && (\$_->text $2 $3) } 1, \$_[0]->_children)"; }
  5204.                  elsif( $and_or)        { $and_or eq 'and' ? '&&' : '||' ; }
  5205.                  else                   { $token; }
  5206.                }gexs;
  5207.       return "($cond)";
  5208.     }
  5209.   
  5210.  
  5211.   sub _op
  5212.     { my $op= shift;
  5213.       if(    $op eq '=')  { $op= 'eq'; }
  5214.       elsif( $op eq '!=') { $op= 'ne'; }
  5215.       return $op;
  5216.     }
  5217.  
  5218.   sub passes
  5219.     { my( $elt, $cond)= @_;
  5220.       return $elt unless $cond;
  5221.       my $sub= ($cond_cache{$cond} ||= _install_cond( $cond));
  5222.       return $sub->( $elt);
  5223.     }
  5224. }
  5225.  
  5226. sub set_parent 
  5227.   { $_[0]->{parent}= $_[1];
  5228.     if( $XML::Twig::weakrefs) { weaken( $_[0]->{parent}); }
  5229.   }
  5230.  
  5231. sub parent
  5232.   { my $elt= shift;
  5233.     my $cond= shift || return $elt->{parent};
  5234.     do { $elt= $elt->{parent} || return; } until (!$elt || $elt->passes( $cond));
  5235.     return $elt;
  5236.   }
  5237.  
  5238. sub set_first_child 
  5239.   { delete $_[0]->{empty};
  5240.     $_[0]->{'first_child'}= $_[1]; 
  5241.   }
  5242.  
  5243. sub first_child
  5244.   { my $elt= shift;
  5245.     my $cond= shift || return $elt->{first_child};
  5246.     my $child= $elt->{first_child};
  5247.     my $test_cond= ($cond_cache{$cond} ||= _install_cond( $cond));
  5248.     while( $child && !$test_cond->( $child)) 
  5249.        { $child= $child->{next_sibling}; }
  5250.     return $child;
  5251.   }
  5252.   
  5253. sub _first_child   { return $_[0]->{first_child};  }
  5254. sub _last_child    { return $_[0]->{last_child};   }
  5255. sub _next_sibling  { return $_[0]->{next_sibling}; }
  5256. sub _prev_sibling  { return $_[0]->{prev_sibling}; }
  5257. sub _parent        { return $_[0]->{parent};       }
  5258. sub _next_siblings { my $elt= shift; my @siblings; while( $elt= $elt->{next_sibling}) { push @siblings, $elt; } return @siblings; }
  5259. sub _prev_siblings { my $elt= shift; my @siblings; while( $elt= $elt->{prev_sibling}) { push @siblings, $elt; } return @siblings; }
  5260.  
  5261. # sets a field
  5262. # arguments $record, $cond, @content
  5263. sub set_field
  5264.   { my $record = shift;
  5265.     my $cond = shift;
  5266.     my $child= $record->first_child( $cond);
  5267.     if( $child)
  5268.       { $child->set_content( @_); }
  5269.     else
  5270.       { if( $cond=~ m{^\s*($REG_NAME)})
  5271.           { my $gi= $1;
  5272.             $child= $record->insert_new_elt( last_child => $gi, @_); 
  5273.           }
  5274.         else
  5275.           { croak "can't create a field name from $cond"; }
  5276.       } 
  5277.     return $child;
  5278.   }
  5279.  
  5280. sub set_last_child 
  5281.   { delete $_[0]->{empty};
  5282.     $_[0]->{'last_child'}= $_[1];
  5283.     if( $XML::Twig::weakrefs) { weaken( $_[0]->{'last_child'}); }
  5284.   }
  5285.  
  5286. sub last_child
  5287.   { my $elt= shift;
  5288.     my $cond= shift || return $elt->{last_child};
  5289.     my $test_cond= ($cond_cache{$cond} ||= _install_cond( $cond));
  5290.     my $child= $elt->{last_child};
  5291.     while( $child && !$test_cond->( $child) )
  5292.       { $child= $child->{prev_sibling}; }
  5293.     return $child
  5294.   }
  5295.  
  5296.  
  5297. sub set_prev_sibling 
  5298.   { $_[0]->{'prev_sibling'}= $_[1]; 
  5299.     if( $XML::Twig::weakrefs) { weaken( $_[0]->{'prev_sibling'}); } 
  5300.   }
  5301.  
  5302. sub prev_sibling
  5303.   { my $elt= shift;
  5304.     my $cond= shift || return $elt->{prev_sibling};
  5305.     my $test_cond= ($cond_cache{$cond} ||= _install_cond( $cond));
  5306.     my $sibling= $elt->{prev_sibling};
  5307.     while( $sibling && !$test_cond->( $sibling) )
  5308.           { $sibling= $sibling->{prev_sibling}; }
  5309.     return $sibling;
  5310.   }
  5311.  
  5312. sub set_next_sibling { $_[0]->{'next_sibling'}= $_[1]; }
  5313.  
  5314. sub next_sibling
  5315.   { my $elt= shift;
  5316.     my $cond= shift || return $elt->{next_sibling};
  5317.     my $test_cond= ($cond_cache{$cond} ||= _install_cond( $cond));
  5318.     my $sibling= $elt->{next_sibling};
  5319.     while( $sibling && !$test_cond->( $sibling) )
  5320.           { $sibling= $sibling->{next_sibling}; }
  5321.     return $sibling;
  5322.   }
  5323.  
  5324. # methods dealing with the class attribute, convenient if you work with xhtml
  5325. sub class     { my( $elt)= @_; return $elt->{'att'}->{'class'}; }
  5326. sub set_class { my( $elt, $class)= @_; $elt->set_att( class => $class); }
  5327.  
  5328. # adds a class to an element
  5329. sub add_to_class
  5330.   { my( $elt, $new_class)= @_;
  5331.     return $elt unless $new_class;
  5332.     my $class= $elt->class;
  5333.     my %class= $class ? map { $_ => 1 } split /\s+/, $class : ();
  5334.     $class{$new_class}= 1;
  5335.     $elt->set_class( join( ' ', sort keys %class));
  5336.   }
  5337.  
  5338. sub att_to_class      { my( $elt, $att)= @_; $elt->set_class( $elt->{'att'}->{$att}); }
  5339. sub add_att_to_class  { my( $elt, $att)= @_; $elt->add_to_class( $elt->{'att'}->{$att}); }
  5340. sub move_att_to_class { my( $elt, $att)= @_; $elt->add_to_class( $elt->{'att'}->{$att});
  5341.                         $elt->del_att( $att); 
  5342.                       }
  5343. sub tag_to_class      { my( $elt)= @_; $elt->set_class( $elt->tag);    }
  5344. sub add_tag_to_class  { my( $elt)= @_; $elt->add_to_class( $elt->tag); }
  5345. sub set_tag_class     { my( $elt, $new_tag)= @_; $elt->add_tag_to_class; $elt->set_tag( $new_tag); }
  5346.  
  5347. sub tag_to_span       
  5348.   { my( $elt)= @_; 
  5349.     $elt->set_class( $elt->tag) unless( $elt->tag eq 'span' && $elt->class); # set class to span unless it would mean replacing it with span
  5350.     $elt->set_tag( 'span'); 
  5351.   }
  5352.  
  5353. sub tag_to_div    
  5354.   { my( $elt)= @_; 
  5355.     $elt->set_class( $elt->tag) unless( $elt->tag eq 'div' && $elt->class); # set class to div unless it would mean replacing it with div
  5356.     $elt->set_tag( 'div');
  5357.   }
  5358.  
  5359. sub in_class          
  5360.   { my( $elt, $class)= @_;
  5361.     my $elt_class= $elt->class;
  5362.     return unless( defined $elt_class);
  5363.     return $elt->class=~ m{(?:^|\s)\Q$class\E(?:\s|$)} ? $elt : 0;
  5364.   }
  5365.  
  5366.  
  5367. # get or set all attributes
  5368. # argument can be a hash or a hashref
  5369. sub set_atts 
  5370.   { my $elt= shift;
  5371.     my %atts;
  5372.     tie %atts, 'Tie::IxHash' if( keep_atts_order());
  5373.     %atts= ( (ref( $_[0] || '') eq 'HASH') || isa( $_[0] || '', 'HASH')) ? %{$_[0]} : @_;
  5374.     $elt->{att}= \%atts;
  5375.     if( exists $atts{$ID}) { $elt->_set_id( $atts{$ID}); }
  5376.     return $elt;
  5377.   }
  5378.  
  5379. sub atts      { return $_[0]->{att};                }
  5380. sub att_names { return (sort keys %{$_[0]->{att}}); }
  5381. sub del_atts  { $_[0]->{att}={}; return $_[0];      }
  5382.  
  5383. # get or set a single attribute (set works for several atts)
  5384. sub set_att 
  5385.   { my $elt= shift;
  5386.  
  5387.     if( $_[0] && ref( $_[0]) && !$_[1]) 
  5388.       { croak "improper call to set_att, usage is \$elt->set_att( att1 => 'val1', att2 => 'val2',...)"; }
  5389.     
  5390.     unless( $elt->{att})
  5391.       { $elt->{att}={};
  5392.         tie %{$elt->{att}}, 'Tie::IxHash' if( keep_atts_order());
  5393.       }
  5394.  
  5395.     while(@_) 
  5396.       { my( $att, $val)= (shift, shift);
  5397.         $elt->{att}->{$att}= $val;
  5398.         if( $att eq $ID) { $elt->_set_id( $val); } 
  5399.       }
  5400.     return $elt;
  5401.   }
  5402.  
  5403. sub att { return $_[0]->{att}->{$_[1]}; }
  5404. sub del_att 
  5405.   { my $elt= shift;
  5406.     while( @_) { delete $elt->{'att'}->{shift()}; }
  5407.     return $elt;
  5408.   }
  5409.  
  5410. sub att_exists { return exists  $_[0]->{att}->{$_[1]}; }
  5411.  
  5412. # delete an attribute from all descendants of an element
  5413. sub strip_att
  5414.   { my( $elt, $att)= @_;
  5415.     $_->del_att( $att) foreach ($elt->descendants_or_self( qq{*[\@$att]}));
  5416.     return $elt;
  5417.   }
  5418.  
  5419. sub change_att_name
  5420.   { my( $elt, $old_name, $new_name)= @_;
  5421.     my $value= $elt->{'att'}->{$old_name};
  5422.     return $elt unless( defined $value);
  5423.     $elt->del_att( $old_name)
  5424.         ->set_att( $new_name => $value);
  5425.     return $elt;
  5426.   }
  5427.  
  5428. sub lc_attnames
  5429.   { my $elt= shift;
  5430.     foreach my $att ($elt->att_names)
  5431.       { if( $att ne lc $att) { $elt->change_att_name( $att, lc $att); } }
  5432.     return $elt;
  5433.   }
  5434.  
  5435. sub set_twig_current { $_[0]->{twig_current}=1; }
  5436. sub del_twig_current { delete $_[0]->{twig_current}; }
  5437.  
  5438.  
  5439. # get or set the id attribute
  5440. sub set_id 
  5441.   { my( $elt, $id)= @_;
  5442.     $elt->del_id() if( exists $elt->{att}->{$ID});
  5443.     $elt->set_att($ID, $id); 
  5444.     $elt->_set_id( $id);
  5445.     return $elt;
  5446.   }
  5447.  
  5448. # only set id, does not update the attribute value
  5449. sub _set_id
  5450.   { my( $elt, $id)= @_;
  5451.     my $t= $elt->twig || $elt;
  5452.     $t->{twig_id_list}->{$id}= $elt;
  5453.     if( $XML::Twig::weakrefs) { weaken(  $t->{twig_id_list}->{$id}); }
  5454.     return $elt;
  5455.   }
  5456.  
  5457. sub id { return $_[0]->{att}->{$ID}; }
  5458.  
  5459. # methods used to add ids to elements that don't have one
  5460. BEGIN 
  5461. { my $id_nb   = "0001";
  5462.   my $id_seed = "twig_id_";
  5463.  
  5464.   sub set_id_seed ## no critic (Subroutines::ProhibitNestedSubs);
  5465.     { $id_seed= $_[1]; $id_nb=1; }
  5466.  
  5467.   sub add_id ## no critic (Subroutines::ProhibitNestedSubs);
  5468.     { my $elt= shift; 
  5469.       if( defined $elt->{'att'}->{$ID})
  5470.         { return $elt->{'att'}->{$ID}; }
  5471.       else
  5472.         { my $id= $_[0] && ref( $_[0]) && isa( $_[0], 'CODE') ? $_[0]->( $elt) : $id_seed . $id_nb++; 
  5473.           $elt->set_id( $id);
  5474.           return $id;
  5475.         }
  5476.     }
  5477. }
  5478.  
  5479.  
  5480.  
  5481. # delete the id attribute and remove the element from the id list
  5482. sub del_id 
  5483.   { my $elt= shift;
  5484.     unless( exists $elt->{'att'}) { return $elt }; 
  5485.     unless( exists $elt->{'att'}->{$ID}) { return $elt }; 
  5486.     my $id= $elt->{'att'}->{$ID};
  5487.  
  5488.     delete $elt->{'att'}->{$ID}; 
  5489.  
  5490.     my $t= shift || $elt->twig;
  5491.     unless( $t) { return $elt; }
  5492.     if( exists $t->{twig_id_list}->{$id}) { delete $t->{twig_id_list}->{$id}; }
  5493.  
  5494.     return $elt;
  5495.   }
  5496.  
  5497. # return the list of children
  5498. sub children
  5499.   { my $elt= shift;
  5500.     my @children;
  5501.     my $child= $elt->first_child( @_);
  5502.     while( $child) 
  5503.       { push @children, $child;
  5504.         $child= $child->next_sibling( @_);
  5505.       } 
  5506.     return @children;
  5507.   }
  5508.  
  5509. sub _children
  5510.   { my $elt= shift;
  5511.     my @children=();
  5512.     my $child= $elt->{first_child};
  5513.     while( $child) 
  5514.       { push @children, $child;
  5515.         $child= $child->{next_sibling};
  5516.       } 
  5517.     return @children;
  5518.   }
  5519.  
  5520. sub children_copy
  5521.   { my $elt= shift;
  5522.     my @children;
  5523.     my $child= $elt->first_child( @_);
  5524.     while( $child) 
  5525.       { push @children, $child->copy;
  5526.         $child= $child->next_sibling( @_);
  5527.       } 
  5528.     return @children;
  5529.   }
  5530.  
  5531.  
  5532. sub children_count
  5533.   { my $elt= shift;
  5534.     my $cond= shift;
  5535.     my $count=0;
  5536.     my $child= $elt->{first_child};
  5537.     while( $child)
  5538.       { $count++ if( $child->passes( $cond)); 
  5539.         $child= $child->{next_sibling};
  5540.       }
  5541.     return $count;
  5542.   }
  5543.  
  5544. sub children_text
  5545.   { my $elt= shift;
  5546.     return wantarray() ? map { $_->text} $elt->children( @_)
  5547.                        : join( '', map { $_->text} $elt->children( @_) )
  5548.                        ;
  5549.   }
  5550.  
  5551. sub children_trimmed_text
  5552.   { my $elt= shift;
  5553.     return wantarray() ? map { $_->trimmed_text} $elt->children( @_)
  5554.                        : join( '', map { $_->trimmed_text} $elt->children( @_) )
  5555.                        ;
  5556.   }
  5557.  
  5558. sub all_children_are
  5559.   { my( $parent, $cond)= @_;
  5560.     foreach my $child ($parent->_children)
  5561.       { return 0 unless( $child->passes( $cond)); }
  5562.     return 1;
  5563.   }
  5564.  
  5565.  
  5566. sub ancestors
  5567.   { my( $elt, $cond)= @_;
  5568.     my @ancestors;
  5569.     while( $elt->{parent})
  5570.       { $elt= $elt->{parent};
  5571.         push @ancestors, $elt if( $elt->passes( $cond));
  5572.       }
  5573.     return @ancestors;
  5574.   }
  5575.  
  5576. sub ancestors_or_self
  5577.   { my( $elt, $cond)= @_;
  5578.     my @ancestors;
  5579.     while( $elt)
  5580.       { push @ancestors, $elt if( $elt->passes( $cond));
  5581.         $elt= $elt->{parent};
  5582.       }
  5583.     return @ancestors;
  5584.   }
  5585.  
  5586.  
  5587. sub _ancestors
  5588.   { my( $elt, $include_self)= @_;
  5589.     my @ancestors= $include_self ? ($elt) : ();
  5590.     while( $elt= $elt->{parent}) { push @ancestors, $elt; }
  5591.     return @ancestors;
  5592.   }
  5593.  
  5594.  
  5595. sub inherit_att
  5596.   { my $elt= shift;
  5597.     my $att= shift;
  5598.     my %tags= map { ($_, 1) } @_;
  5599.  
  5600.     do 
  5601.       { if(   (defined $elt->{'att'}->{$att})
  5602.            && ( !%tags || $tags{$XML::Twig::index2gi[$elt->{'gi'}]})
  5603.           )
  5604.           { return $elt->{'att'}->{$att}; }
  5605.       } while( $elt= $elt->{parent});
  5606.     return undef;
  5607.   }
  5608.  
  5609. sub _inherit_att_through_cut
  5610.   { my $elt= shift;
  5611.     my $att= shift;
  5612.     my %tags= map { ($_, 1) } @_;
  5613.  
  5614.     do 
  5615.       { if(   (defined $elt->{'att'}->{$att})
  5616.            && ( !%tags || $tags{$XML::Twig::index2gi[$elt->{'gi'}]})
  5617.           )
  5618.           { return $elt->{'att'}->{$att}; }
  5619.       } while( $elt= $elt->{parent} || $elt->former_parent);
  5620.     return undef;
  5621.   }
  5622.  
  5623.  
  5624. sub current_ns_prefixes
  5625.   { my $elt= shift;
  5626.     my %prefix;
  5627.     $prefix{''}=1 if( $elt->namespace( ''));
  5628.     while( $elt)
  5629.       { my @ns= grep { !m{^xml} } map { m{^([^:]+):} } ($XML::Twig::index2gi[$elt->{'gi'}], $elt->att_names);
  5630.         $prefix{$_}=1 foreach (@ns);
  5631.         $elt= $elt->{parent};
  5632.       }
  5633.  
  5634.     return (sort keys %prefix);
  5635.   }
  5636.  
  5637. # kinda counter-intuitive actually:
  5638. # the next element is found by looking for the next open tag after from the
  5639. # current one, which is the first child, if it exists, or the next sibling
  5640. # or the first next sibling of an ancestor
  5641. # optional arguments are: 
  5642. #   - $subtree_root: a reference to an element, when the next element is not 
  5643. #                    within $subtree_root anymore then next_elt returns undef
  5644. #   - $cond: a condition, next_elt returns the next element matching the condition
  5645.                  
  5646. sub next_elt
  5647.   { my $elt= shift;
  5648.     my $subtree_root= 0;
  5649.     $subtree_root= shift if( ref( $_[0]) && isa( $_[0], 'XML::Twig::Elt'));
  5650.     my $cond= shift;
  5651.     my $next_elt;
  5652.  
  5653.     my $ind;                                                              # optimization
  5654.     my $test_cond;
  5655.     if( $cond)                                                            # optimization
  5656.       { unless( defined( $ind= $XML::Twig::gi2index{$cond}) )             # optimization
  5657.           { $test_cond= ($cond_cache{$cond} ||= _install_cond( $cond)); } # optimization
  5658.       }                                                                   # optimization
  5659.     
  5660.     do
  5661.       { if( $next_elt= $elt->{first_child})
  5662.           { # simplest case: the elt has a child
  5663.           }
  5664.          elsif( $next_elt= $elt->{next_sibling}) 
  5665.           { # no child but a next sibling (just check we stay within the subtree)
  5666.           
  5667.             # case where elt is subtree_root, is empty and has a sibling
  5668.             return undef if( $subtree_root && ($elt == $subtree_root));
  5669.             
  5670.           }
  5671.         else
  5672.           { # case where the element has no child and no next sibling:
  5673.             # get the first next sibling of an ancestor, checking subtree_root 
  5674.           
  5675.             # case where elt is subtree_root, is empty and has no sibling
  5676.             return undef if( $subtree_root && ($elt == $subtree_root));
  5677.              
  5678.             $next_elt= $elt->{parent};
  5679.  
  5680.             until( $next_elt->{next_sibling})
  5681.               { return undef if( $subtree_root && ($subtree_root == $next_elt));
  5682.                 $next_elt= $next_elt->{parent} || return undef;
  5683.               }
  5684.             return undef if( $subtree_root && ($subtree_root == $next_elt)); 
  5685.             $next_elt= $next_elt->{next_sibling};   
  5686.           }  
  5687.       $elt= $next_elt;                   # just in case we need to loop
  5688.     } until(    ! defined $elt 
  5689.              || ! defined $cond 
  5690.          || (defined $ind       && ($elt->{gi} eq $ind))   # optimization
  5691.          || (defined $test_cond && ($test_cond->( $elt)))
  5692.                );
  5693.     
  5694.       return $elt;
  5695.       }
  5696.  
  5697. # return the next_elt within the element
  5698. # just call next_elt with the element as first and second argument
  5699. sub first_descendant { return $_[0]->next_elt( @_); }
  5700.  
  5701. # get the last descendant, # then return the element found or call prev_elt with the condition
  5702. sub last_descendant
  5703.   { my( $elt, $cond)= @_;
  5704.     my $last_descendant= $elt->_last_descendant;
  5705.     if( !$cond || $last_descendant->matches( $cond))
  5706.       { return $last_descendant; }
  5707.     else
  5708.       { return $last_descendant->prev_elt( $elt, $cond); }
  5709.   }
  5710.  
  5711. # no argument allowed here, just go down the last_child recursively
  5712. sub _last_descendant
  5713.   { my $elt= shift;
  5714.     while( my $child= $elt->{last_child}) { $elt= $child; }
  5715.     return $elt;
  5716.   }
  5717.  
  5718. # counter-intuitive too:
  5719. # the previous element is found by looking
  5720. # for the first open tag backwards from the current one
  5721. # it's the last descendant of the previous sibling 
  5722. # if it exists, otherwise it's simply the parent
  5723. sub prev_elt
  5724.   { my $elt= shift;
  5725.     my $subtree_root= 0;
  5726.     if( defined $_[0] and (ref( $_[0]) && isa( $_[0], 'XML::Twig::Elt')))
  5727.       { $subtree_root= shift ;
  5728.         return undef if( $elt == $subtree_root);
  5729.       }
  5730.     my $cond= shift;
  5731.     # get prev elt
  5732.     my $prev_elt;
  5733.     do
  5734.       { return undef if( $elt == $subtree_root);
  5735.         if( $prev_elt= $elt->{prev_sibling})
  5736.           { while( $prev_elt->{last_child})
  5737.               { $prev_elt= $prev_elt->{last_child}; }
  5738.           }
  5739.         else
  5740.           { $prev_elt= $elt->{parent} || return undef; }
  5741.         $elt= $prev_elt;     # in case we need to loop 
  5742.       } until( $elt->passes( $cond));
  5743.  
  5744.     return $elt;
  5745.   }
  5746.  
  5747. sub _following_elt
  5748.   { my( $elt)= @_;
  5749.     while( $elt && !$elt->{next_sibling})
  5750.       { $elt= $elt->{parent}; }
  5751.     return $elt ? $elt->{next_sibling} : undef;
  5752.   }
  5753.  
  5754. sub following_elt
  5755.   { my( $elt, $cond)= @_;
  5756.     $elt= $elt->_following_elt || return undef;
  5757.     return $elt if( !$cond || $elt->matches( $cond));
  5758.     return $elt->next_elt( $cond);
  5759.   }
  5760.  
  5761. sub following_elts
  5762.   { my( $elt, $cond)= @_;
  5763.     if( !$cond) { undef $cond; }
  5764.     my $following= $elt->following_elt( $cond);
  5765.     if( $following)
  5766.       { my @followings= $following;
  5767.         while( $following= $following->next_elt( $cond))
  5768.           { push @followings, $following; }
  5769.         return( @followings);
  5770.       }
  5771.     else
  5772.       { return (); }
  5773.   }
  5774.  
  5775. sub _preceding_elt
  5776.   { my( $elt)= @_;
  5777.     while( $elt && !$elt->{prev_sibling})
  5778.       { $elt= $elt->{parent}; }
  5779.     return $elt ? $elt->{prev_sibling}->_last_descendant : undef;
  5780.   }
  5781.  
  5782. sub preceding_elt
  5783.   { my( $elt, $cond)= @_;
  5784.     $elt= $elt->_preceding_elt || return undef;
  5785.     return $elt if( !$cond || $elt->matches( $cond));
  5786.     return $elt->prev_elt( $cond);
  5787.   }
  5788.  
  5789. sub preceding_elts
  5790.   { my( $elt, $cond)= @_;
  5791.     if( !$cond) { undef $cond; }
  5792.     my $preceding= $elt->preceding_elt( $cond);
  5793.     if( $preceding)
  5794.       { my @precedings= $preceding;
  5795.         while( $preceding= $preceding->prev_elt( $cond))
  5796.           { push @precedings, $preceding; }
  5797.         return( @precedings);
  5798.       }
  5799.     else
  5800.       { return (); }
  5801.   }
  5802.  
  5803. # used in get_xpath
  5804. sub _self
  5805.   { my( $elt, $cond)= @_;
  5806.     return $cond ? $elt->matches( $cond) : $elt;
  5807.   }
  5808.  
  5809. sub next_n_elt
  5810.   { my $elt= shift;
  5811.     my $offset= shift || return undef;
  5812.     foreach (1..$offset)
  5813.       { $elt= $elt->next_elt( @_) || return undef; }
  5814.     return $elt;
  5815.   }
  5816.  
  5817. # checks whether $elt is included in $ancestor, returns 1 in that case
  5818. sub in
  5819.   { my ($elt, $ancestor)= @_;
  5820.     if( ref( $ancestor) && isa( $ancestor, 'XML::Twig::Elt'))
  5821.       { # element
  5822.         while( $elt= $elt->{parent}) { return $elt if( $elt ==  $ancestor); } 
  5823.       }
  5824.     else
  5825.       { # condition
  5826.         while( $elt= $elt->{parent}) { return $elt if( $elt->matches( $ancestor)); } 
  5827.       }
  5828.     return 0;           
  5829.   }
  5830.  
  5831. sub first_child_text  
  5832.   { my $elt= shift;
  5833.     my $dest=$elt->first_child(@_) or return '';
  5834.     return $dest->text;
  5835.   }
  5836.  
  5837. sub fields  
  5838.   { my $elt= shift;
  5839.     return map { $elt->field( $_) } @_;
  5840.   }
  5841.  
  5842. sub first_child_trimmed_text  
  5843.   { my $elt= shift;
  5844.     my $dest=$elt->first_child(@_) or return '';
  5845.     return $dest->trimmed_text;
  5846.   }
  5847.   
  5848. sub first_child_matches
  5849.   { my $elt= shift;
  5850.     my $dest= $elt->{first_child} or return undef;
  5851.     return $dest->passes( @_);
  5852.   }
  5853.   
  5854. sub last_child_text  
  5855.   { my $elt= shift;
  5856.     my $dest=$elt->last_child(@_) or return '';
  5857.     return $dest->text;
  5858.   }
  5859.   
  5860. sub last_child_trimmed_text  
  5861.   { my $elt= shift;
  5862.     my $dest=$elt->last_child(@_) or return '';
  5863.     return $dest->trimmed_text;
  5864.   }
  5865.   
  5866. sub last_child_matches
  5867.   { my $elt= shift;
  5868.     my $dest= $elt->{last_child} or return undef;
  5869.     return $dest->passes( @_);
  5870.   }
  5871.   
  5872. sub child_text
  5873.   { my $elt= shift;
  5874.     my $dest=$elt->child(@_) or return '';
  5875.     return $dest->text;
  5876.   }
  5877.   
  5878. sub child_trimmed_text
  5879.   { my $elt= shift;
  5880.     my $dest=$elt->child(@_) or return '';
  5881.     return $dest->trimmed_text;
  5882.   }
  5883.   
  5884. sub child_matches
  5885.   { my $elt= shift;
  5886.     my $nb= shift;
  5887.     my $dest= $elt->child( $nb) or return undef;
  5888.     return $dest->passes( @_);
  5889.   }
  5890.  
  5891. sub prev_sibling_text  
  5892.   { my $elt= shift;
  5893.     my $dest=$elt->_prev_sibling(@_) or return '';
  5894.     return $dest->text;
  5895.   }
  5896.   
  5897. sub prev_sibling_trimmed_text  
  5898.   { my $elt= shift;
  5899.     my $dest=$elt->_prev_sibling(@_) or return '';
  5900.     return $dest->trimmed_text;
  5901.   }
  5902.   
  5903. sub prev_sibling_matches
  5904.   { my $elt= shift;
  5905.     my $dest= $elt->{prev_sibling} or return undef;
  5906.     return $dest->passes( @_);
  5907.   }
  5908.   
  5909. sub next_sibling_text  
  5910.   { my $elt= shift;
  5911.     my $dest=$elt->next_sibling(@_) or return '';
  5912.     return $dest->text;
  5913.   }
  5914.   
  5915. sub next_sibling_trimmed_text  
  5916.   { my $elt= shift;
  5917.     my $dest=$elt->next_sibling(@_) or return '';
  5918.     return $dest->trimmed_text;
  5919.   }
  5920.   
  5921. sub next_sibling_matches
  5922.   { my $elt= shift;
  5923.     my $dest= $elt->{next_sibling} or return undef;
  5924.     return $dest->passes( @_);
  5925.   }
  5926.   
  5927. sub prev_elt_text  
  5928.   { my $elt= shift;
  5929.     my $dest=$elt->prev_elt(@_) or return '';
  5930.     return $dest->text;
  5931.   }
  5932.   
  5933. sub prev_elt_trimmed_text  
  5934.   { my $elt= shift;
  5935.     my $dest=$elt->prev_elt(@_) or return '';
  5936.     return $dest->trimmed_text;
  5937.   }
  5938.   
  5939. sub prev_elt_matches
  5940.   { my $elt= shift;
  5941.     my $dest= $elt->prev_elt or return undef;
  5942.     return $dest->passes( @_);
  5943.   }
  5944.   
  5945. sub next_elt_text  
  5946.   { my $elt= shift;
  5947.     my $dest=$elt->next_elt(@_) or return '';
  5948.     return $dest->text;
  5949.   }
  5950.   
  5951. sub next_elt_trimmed_text  
  5952.   { my $elt= shift;
  5953.     my $dest=$elt->next_elt(@_) or return '';
  5954.     return $dest->trimmed_text;
  5955.   }
  5956.   
  5957. sub next_elt_matches
  5958.   { my $elt= shift;
  5959.     my $dest= $elt->next_elt or return undef;
  5960.     return $dest->passes( @_);
  5961.   }
  5962.   
  5963. sub parent_text  
  5964.   { my $elt= shift;
  5965.     my $dest=$elt->parent(@_) or return '';
  5966.     return $dest->text;
  5967.   }
  5968.   
  5969. sub parent_trimmed_text  
  5970.   { my $elt= shift;
  5971.     my $dest=$elt->parent(@_) or return '';
  5972.     return $dest->trimmed_text;
  5973.   }
  5974.   
  5975. sub parent_matches
  5976.   { my $elt= shift;
  5977.     my $dest= $elt->{parent} or return undef;
  5978.     return $dest->passes( @_);
  5979.   }
  5980.  
  5981. sub is_first_child
  5982.   { my $elt= shift;
  5983.     my $parent= $elt->{parent} or return 0;
  5984.     my $first_child= $parent->first_child( @_) or return 0;
  5985.     return ($first_child == $elt) ? $elt : 0;
  5986.   }
  5987.  
  5988. sub is_last_child
  5989.   { my $elt= shift;
  5990.     my $parent= $elt->{parent} or return 0;
  5991.     my $last_child= $parent->last_child( @_) or return 0;
  5992.     return ($last_child == $elt) ? $elt : 0;
  5993.   }
  5994.  
  5995. # returns the depth level of the element
  5996. # if 2 parameter are used then counts the 2cd element name in the
  5997. # ancestors list
  5998. sub level
  5999.   { my( $elt, $cond)= @_;
  6000.     my $level=0;
  6001.     my $name=shift || '';
  6002.     while( $elt= $elt->{parent}) { $level++ if( !$cond || $elt->matches( $cond)); }
  6003.     return $level;           
  6004.   }
  6005.  
  6006. # checks whether $elt has an ancestor that satisfies $cond, returns the ancestor
  6007. sub in_context
  6008.   { my ($elt, $cond, $level)= @_;
  6009.     $level= -1 unless( $level) ;  # $level-- will never hit 0
  6010.  
  6011.     while( $level)
  6012.       { $elt= $elt->{parent} or return 0;
  6013.         if( $elt->matches( $cond)) { return $elt; }
  6014.         $level--;
  6015.       }
  6016.     return 0;
  6017.   }
  6018.  
  6019. sub _descendants
  6020.   { my( $subtree_root, $include_self)= @_;
  6021.     my @descendants= $include_self ? ($subtree_root) : ();
  6022.  
  6023.     my $elt= $subtree_root; 
  6024.     my $next_elt;   
  6025.  
  6026.     MAIN: while( 1)  
  6027.       { if( $next_elt= $elt->{first_child})
  6028.           { # simplest case: the elt has a child
  6029.           }
  6030.         elsif( $next_elt= $elt->{next_sibling}) 
  6031.           { # no child but a next sibling (just check we stay within the subtree)
  6032.           
  6033.             # case where elt is subtree_root, is empty and has a sibling
  6034.             last MAIN if( $elt == $subtree_root);
  6035.           }
  6036.         else
  6037.           { # case where the element has no child and no next sibling:
  6038.             # get the first next sibling of an ancestor, checking subtree_root 
  6039.                 
  6040.             # case where elt is subtree_root, is empty and has no sibling
  6041.             last MAIN if( $elt == $subtree_root);
  6042.                
  6043.             # backtrack until we find a parent with a next sibling
  6044.             $next_elt= $elt->{parent} || last;
  6045.             until( $next_elt->{next_sibling})
  6046.               { last MAIN if( $subtree_root == $next_elt);
  6047.                 $next_elt= $next_elt->{parent} || last MAIN;
  6048.               }
  6049.             last MAIN if( $subtree_root == $next_elt); 
  6050.             $next_elt= $next_elt->{next_sibling};   
  6051.           }  
  6052.         $elt= $next_elt || last MAIN;
  6053.         push @descendants, $elt;
  6054.       }
  6055.     return @descendants;
  6056.   }
  6057.  
  6058.  
  6059. sub descendants
  6060.   { my( $subtree_root, $cond)= @_;
  6061.     my @descendants=(); 
  6062.     my $elt= $subtree_root;
  6063.     
  6064.     # this branch is pure optimization for speed: if $cond is a gi replace it
  6065.     # by the index of the gi and loop here 
  6066.     # start optimization
  6067.     my $ind;
  6068.     if( !$cond || ( defined ( $ind= $XML::Twig::gi2index{$cond})) )
  6069.       {
  6070.         my $next_elt;
  6071.  
  6072.         while( 1)  
  6073.           { if( $next_elt= $elt->{first_child})
  6074.                 { # simplest case: the elt has a child
  6075.                 }
  6076.              elsif( $next_elt= $elt->{next_sibling}) 
  6077.               { # no child but a next sibling (just check we stay within the subtree)
  6078.            
  6079.                 # case where elt is subtree_root, is empty and has a sibling
  6080.                 last if( $subtree_root && ($elt == $subtree_root));
  6081.               }
  6082.             else
  6083.               { # case where the element has no child and no next sibling:
  6084.                 # get the first next sibling of an ancestor, checking subtree_root 
  6085.                 
  6086.                 # case where elt is subtree_root, is empty and has no sibling
  6087.                 last if( $subtree_root && ($elt == $subtree_root));
  6088.                
  6089.                 # backtrack until we find a parent with a next sibling
  6090.                 $next_elt= $elt->{parent} || last undef;
  6091.                 until( $next_elt->{next_sibling})
  6092.                   { last if( $subtree_root && ($subtree_root == $next_elt));
  6093.                     $next_elt= $next_elt->{parent} || last;
  6094.                   }
  6095.                 last if( $subtree_root && ($subtree_root == $next_elt)); 
  6096.                 $next_elt= $next_elt->{next_sibling};   
  6097.               }  
  6098.             $elt= $next_elt || last;
  6099.             push @descendants, $elt if( !$cond || ($elt->{gi} eq $ind));
  6100.           }
  6101.       }
  6102.     else
  6103.     # end optimization
  6104.       { # branch for a complex condition: use the regular (slow but simple) way
  6105.         while( $elt= $elt->next_elt( $subtree_root, $cond))
  6106.           { push @descendants, $elt; }
  6107.       }
  6108.     return @descendants;
  6109.   }
  6110.  
  6111.  
  6112. sub descendants_or_self
  6113.   { my( $elt, $cond)= @_;
  6114.     my @descendants= $elt->passes( $cond) ? ($elt) : (); 
  6115.     push @descendants, $elt->descendants( $cond);
  6116.     return @descendants;
  6117.   }
  6118.   
  6119. sub sibling
  6120.   { my $elt= shift;
  6121.     my $nb= shift;
  6122.     if( $nb > 0)
  6123.       { foreach( 1..$nb)
  6124.           { $elt= $elt->next_sibling( @_) or return undef; }
  6125.       }
  6126.     elsif( $nb < 0)
  6127.       { foreach( 1..(-$nb))
  6128.           { $elt= $elt->prev_sibling( @_) or return undef; }
  6129.       }
  6130.     else # $nb == 0
  6131.       { return $elt->passes( $_[0]); }
  6132.     return $elt;
  6133.   }
  6134.  
  6135. sub sibling_text
  6136.   { my $elt= sibling( @_);
  6137.     return $elt ? $elt->text : undef;
  6138.   }
  6139.  
  6140.  
  6141. sub child
  6142.   { my $elt= shift;
  6143.     my $nb= shift;
  6144.     if( $nb >= 0)
  6145.       { $elt= $elt->first_child( @_) or return undef;
  6146.         foreach( 1..$nb)
  6147.           { $elt= $elt->next_sibling( @_) or return undef; }
  6148.       }
  6149.     else
  6150.       { $elt= $elt->last_child( @_) or return undef;
  6151.         foreach( 2..(-$nb))
  6152.           { $elt= $elt->prev_sibling( @_) or return undef; }
  6153.       }
  6154.     return $elt;
  6155.   }
  6156.  
  6157. sub prev_siblings
  6158.   { my $elt= shift;
  6159.     my @siblings=();
  6160.     while( $elt= $elt->prev_sibling( @_))
  6161.       { unshift @siblings, $elt; }
  6162.     return @siblings;
  6163.   }
  6164.  
  6165. sub pos
  6166.   { my $elt= shift;
  6167.     return 0 if ($_[0] && !$elt->matches( @_));
  6168.     my $pos=1;
  6169.     $pos++ while( $elt= $elt->prev_sibling( @_));
  6170.     return $pos;
  6171.   }
  6172.  
  6173.  
  6174. sub next_siblings
  6175.   { my $elt= shift;
  6176.     my @siblings=();
  6177.     while( $elt= $elt->next_sibling( @_))
  6178.       { push @siblings, $elt; }
  6179.     return @siblings;
  6180.   }
  6181.  
  6182.  
  6183. # used by get_xpath: parses the xpath expression and generates a sub that performs the
  6184. # search
  6185. { my %axis2method;
  6186.   BEGIN { %axis2method= ( child               => 'children',
  6187.                           descendant          => 'descendants',
  6188.                          'descendant-or-self' => 'descendants_or_self',
  6189.                           parent              => 'parent_is',
  6190.                           ancestor            => 'ancestors',
  6191.                          'ancestor-or-self'   => 'ancestors_or_self',
  6192.                          'following-sibling'  => 'next_siblings',
  6193.                          'preceding-sibling'  => 'prev_siblings',
  6194.                           following           => 'following_elts',
  6195.                           preceding           => 'preceding_elts',
  6196.                           self                => '_self',
  6197.                         );
  6198.         }
  6199.  
  6200.   sub _install_xpath
  6201.     { my( $xpath_exp, $type)= @_;
  6202.       my $original_exp= $xpath_exp;
  6203.       my $sub= 'my $elt= shift; my @results;';
  6204.       
  6205.       # grab the root if expression starts with a /
  6206.       if( $xpath_exp=~ s{^/}{})
  6207.         { $sub .= '@results= ($elt->twig) || croak "cannot use an XPath query starting with a / on a node not attached to a whole twig";'; }
  6208.       elsif( $xpath_exp=~ s{^\./}{})
  6209.         { $sub .= '@results= ($elt);'; }
  6210.       else
  6211.         { $sub .= '@results= ($elt);'; }
  6212.   
  6213.  
  6214.      #warn "xpath_exp= '$xpath_exp'\n";
  6215.       while( $xpath_exp &&
  6216.              $xpath_exp=~s{^\s*(/?)                            
  6217.                             # the xxx=~/regexp/ is a pain as it includes /  
  6218.                             (\s*(?:(?:($REG_AXIS)::)?(\*|$REG_NAME|\.\.|\.)\s*)?($REG_PREDICATE_ALT*)
  6219.                             )
  6220.                             (/|$)}{}xo)
  6221.   
  6222.         { my( $wildcard, $sub_exp, $axis, $gi, $predicates)= ($1, $2, $3, $4, $5);
  6223.           
  6224.           # grab a parent
  6225.           if( $sub_exp eq '..')
  6226.             { _croak_and_doublecheck_xpath( $original_exp, "error in xpath expression $original_exp") if( $wildcard);
  6227.               $sub .= '@results= map { $_->{parent}} @results;';
  6228.             }
  6229.           # test the element itself
  6230.           elsif( $sub_exp=~ m{^\.(.*)$}s)
  6231.             { $sub .= "\@results= grep { \$_->matches( q{$1}) } \@results;" }
  6232.               # grab children
  6233.           else       
  6234.             { 
  6235.               if( !$axis)             
  6236.                 { $axis= $wildcard ? 'descendant' : 'child'; }
  6237.               if( !$gi or $gi eq '*') { $gi=''; }
  6238.               my $function;
  6239.   
  6240.               # "special" predicates, that return just one element
  6241.               if( $predicates && ($predicates =~ m{^\s*\[\s*((-\s*)?\d+)\s*\]\s*$}))
  6242.                 { # [<nb>]
  6243.                   my $offset= $1;
  6244.                   $offset-- if( $offset > 0);
  6245.                   $function=  $axis eq 'descendant' ? "next_n_elt( $offset, '$gi')" 
  6246.                            :  $axis eq 'child'      ? "child( $offset, '$gi')"
  6247.                            :                          _croak_and_doublecheck_xpath( $original_exp, "error [$1] not supported along axis '$axis'")
  6248.                            ;
  6249.                   $sub .= "\@results= grep { \$_ } map { \$_->$function } \@results;"
  6250.                 }
  6251.               elsif( $predicates && ($predicates =~ m{^\s*\[\s*last\s*\(\s*\)\s*\]\s*$}) )
  6252.                 { # last()
  6253.                   _croak_and_doublecheck_xpath( $original_exp, "error in xpath expression $original_exp, usage of // and last() not supported") if( $wildcard);
  6254.                    $sub .= "\@results= map { \$_->last_child( '$gi') } \@results;";
  6255.                 }
  6256.               else
  6257.                 { # follow the axis
  6258.                   #warn "axis: '$axis' - method: '$axis2method{$axis}' - gi: '$gi'\n";
  6259.  
  6260.                   my $follow_axis= " \$_->$axis2method{$axis}( '$gi')";
  6261.                   my $step= $follow_axis;
  6262.                   
  6263.                   # now filter using the predicate
  6264.                   while( $predicates=~ s{^\s*($REG_PREDICATE_ALT)\s*}{}o)
  6265.                     { my $pred= $1;
  6266.                       $pred=~ s{^\s*\[\s*}{};
  6267.                       $pred=~ s{\s*\]\s*$}{};
  6268.                       my $test="";
  6269.                       my $pos;
  6270.                       if( $pred=~ m{^(-?\s*\d+)$})
  6271.                         { my $pos= $1;
  6272.                           if( $step=~ m{^\s*grep(.*) (\$_->\w+\(\s*'[^']*'\s*\))})
  6273.                             { $step= "XML::Twig::_first_n $1 $pos, $2"; }
  6274.                           else
  6275.                             { if( $pos > 0) { $pos--; }
  6276.                               $step= "($step)[$pos]"; 
  6277.                             }
  6278.                           #warn "number predicate '$pos' - generated step '$step'\n";
  6279.                         }
  6280.                       else
  6281.                         { my $syntax_error=0;
  6282.                           do
  6283.                             { if( $pred =~ s{^string\(\s*\)\s*=\s*($REG_STRING)\s*}{}o)  # string()="string" pred
  6284.                                 { $test .= "\$_->text eq $1"; }
  6285.                              elsif( $pred =~ s{^string\(\s*\)\s*($REG_MATCH)\s*($REG_REGEXP)\s*}{}o)  # string()=~/regex/ pred
  6286.                                 { my( $match, $regexp)= ($1, $2);
  6287.                                   $test .= "\$_->text $match $regexp"; 
  6288.                                 }
  6289.                              elsif( $pred=~ s{^@($REG_NAME)\s*($REG_OP)\s*($REG_STRING|$REG_NUMBER)}{}o)  # @att="val" pred
  6290.                                 { my( $att, $oper, $val)= ($1, _op( $2), $3);
  6291.                                   $test .= qq{((defined \$_->{'att'}->{"$att"})  && (\$_->{'att'}->{"$att"} $oper $val))};
  6292.                                 }
  6293.                              elsif( $pred =~ s{^@($REG_NAME)\s*($REG_MATCH)\s*($REG_REGEXP)\s*}{}o)  # @att=~/regex/ pred XXX
  6294.                                 { my( $att, $match, $regexp)= ($1, $2, $3);
  6295.                                   $test .= qq{((defined \$_->{'att'}->{"$att"})  && (\$_->{'att'}->{"$att"} $match $regexp))};; 
  6296.                                 }
  6297.                              elsif( $pred=~ s{^@($REG_NAME)\s*}{}o)                      # @att pred
  6298.                                 { $test .= qq{(defined \$_->{'att'}->{"$1"})}; }
  6299.                              elsif( $pred=~ s{^\s*(?:not|!)\s*@($REG_NAME)\s*}{}o)       # not @att pred
  6300.                                 { $test .= qq{((\$_->is_elt) && (not defined \$_->{'att'}->{"$1"}))}; }
  6301.                               elsif( $pred=~ s{^\s*([()])}{})                            # ( or ) (just add to the test)
  6302.                                 { $test .= qq{$1};           }
  6303.                               elsif( $pred=~ s{^\s*(and|or)\s*}{})
  6304.                                 { $test .= lc " $1 "; }
  6305.                               else
  6306.                                 { $syntax_error=1; }
  6307.                              
  6308.                              } while( !$syntax_error && $pred);
  6309.                            _croak_and_doublecheck_xpath( $original_exp, "error in xpath expression $original_exp at $pred") if( $pred);
  6310.                            $step= " grep { $test } $step ";
  6311.                         }
  6312.                     }
  6313.                   #warn "step: '$step'";
  6314.                   $sub .= "\@results= grep { \$_ } map { $step } \@results;"; 
  6315.                 }
  6316.             }
  6317.         }
  6318.   
  6319.       if( $xpath_exp)
  6320.         { _croak_and_doublecheck_xpath( $original_exp, "error in xpath expression $original_exp around $xpath_exp"); }
  6321.         
  6322.       $sub .= q{return XML::Twig::_unique_elts( @results); };
  6323.       #warn "generated: '$sub'\n";
  6324.       my $s= eval "sub { $NO_WARNINGS; $sub }";
  6325.       if( $@) 
  6326.         { _croak_and_doublecheck_xpath( $original_exp, "error in xpath expression $original_exp ($@);") }
  6327.       return( $s); 
  6328.     }
  6329. }
  6330.  
  6331. sub _croak_and_doublecheck_xpath
  6332.   { my $xpath_expression= shift;
  6333.     my $mess= join( "\n", @_);
  6334.     if( $XML::Twig::XPath::VERSION || 0) 
  6335.       { my $check_twig= XML::Twig::XPath->new;
  6336.         if( eval { $check_twig->{twig_xp}->_parse( $xpath_expression) })
  6337.           { $mess .= "\nthe expression is a valid XPath statement, and you are using XML::Twig::XPath, but"
  6338.                    . "\nyou are using either 'find_nodes' or 'get_xpath' where the method you likely wanted"
  6339.                    . "\nto use is 'findnodes', which is the only one that uses the full XPath engine\n";
  6340.           }
  6341.       } 
  6342.     croak $mess;
  6343.   }
  6344.     
  6345.     
  6346.            
  6347. { # extremely elaborate caching mechanism
  6348.   my %xpath; # xpath_expression => subroutine_code;  
  6349.   sub get_xpath
  6350.     { my( $elt, $xpath_exp, $offset)= @_;
  6351.       my $sub= ($xpath{$xpath_exp} ||= _install_xpath( $xpath_exp));
  6352.       return $sub->( $elt) unless( defined $offset); 
  6353.       my @res= $sub->( $elt);
  6354.       return $res[$offset];
  6355.     }
  6356. }
  6357.  
  6358.  
  6359. sub findvalue
  6360.   { my $elt= shift;
  6361.     return join '', map { $_->text } $elt->get_xpath( @_);
  6362.   }
  6363.  
  6364.  
  6365. # XML::XPath compatibility
  6366. sub getElementById     { return $_[0]->twig->elt_id( $_[1]); }
  6367. sub getChildNodes      { my @children= do { my $elt= $_[0]; my @children=(); my $child= $elt->{first_child}; while( $child) { push @children, $child; $child= $child->{next_sibling}; } @children; }; return wantarray ? @children : \@children; }
  6368.  
  6369. sub _flushed     { return $_[0]->{flushed}; }
  6370. sub _set_flushed { $_[0]->{flushed}=1;      }
  6371. sub _del_flushed { delete $_[0]->{flushed}; }
  6372.  
  6373. sub cut
  6374.   { my $elt= shift;
  6375.     my( $parent, $prev_sibling, $next_sibling, $last_elt);
  6376.  
  6377.     # you can't cut the root, sorry
  6378.     unless( $parent= $elt->{parent}) { return; }
  6379.  
  6380.     # save the old links, that'll make it easier for some loops
  6381.     foreach my $link ( qw(parent prev_sibling next_sibling) )
  6382.       { $elt->{former}->{$link}= $elt->{$link};
  6383.          if( $XML::Twig::weakrefs) { weaken( $elt->{former}->{$link}); }
  6384.       }
  6385.  
  6386.     # it we cut the current element then its parent becomes the current elt
  6387.     if( $elt->{twig_current})
  6388.       { my $twig_current= $elt->{parent};
  6389.         my $t= $elt->twig;
  6390.         $t->{twig_current}= $twig_current;
  6391.         $twig_current->{'twig_current'}=1;
  6392.         delete $elt->{'twig_current'};
  6393.       }
  6394.  
  6395.     if( $parent->{first_child} == $elt)
  6396.       { $parent->{first_child}=  $elt->{next_sibling};
  6397.         unless( $elt->{next_sibling}) { $parent->{empty}= 1; }
  6398.       }
  6399.     if( $parent->{last_child} == $elt) {  delete $parent->{empty}; $parent->{last_child}=$elt->{prev_sibling}; if( $XML::Twig::weakrefs) { weaken( $parent->{last_child});} ; }
  6400.  
  6401.     if( $prev_sibling= $elt->{prev_sibling})
  6402.       { $prev_sibling->{next_sibling}=  $elt->{next_sibling}; }
  6403.     if( $next_sibling= $elt->{next_sibling})
  6404.       { $next_sibling->{prev_sibling}=$elt->{prev_sibling}; if( $XML::Twig::weakrefs) { weaken( $next_sibling->{prev_sibling});} ; }
  6405.  
  6406.  
  6407.     $elt->{parent}=undef; if( $XML::Twig::weakrefs) { weaken( $elt->{parent});} ;
  6408.     $elt->{prev_sibling}=undef; if( $XML::Twig::weakrefs) { weaken( $elt->{prev_sibling});} ;
  6409.     $elt->{next_sibling}=  undef;
  6410.  
  6411.     if( $prev_sibling && $next_sibling && $prev_sibling->is_text && ( $XML::Twig::index2gi[$prev_sibling->{'gi'}] eq $XML::Twig::index2gi[$next_sibling->{'gi'}]))
  6412.       { $prev_sibling->merge_text( $next_sibling); }
  6413.  
  6414.     return $elt;
  6415.   }
  6416.  
  6417.  
  6418. sub former_next_sibling { return $_[0]->{former}->{next_sibling}; }
  6419. sub former_prev_sibling { return $_[0]->{former}->{prev_sibling}; }
  6420. sub former_parent       { return $_[0]->{former}->{parent};       }
  6421.  
  6422. sub cut_children
  6423.   { my( $elt, $exp)= @_;
  6424.     my @children= $elt->children( $exp);
  6425.     foreach (@children) { $_->cut; }
  6426.     return @children;
  6427.   }
  6428.  
  6429.  
  6430. sub erase
  6431.   { my $elt= shift;
  6432.     #you cannot erase the current element
  6433.     if( $elt->{twig_current})
  6434.       { croak "trying to erase an element before it has been completely parsed"; }
  6435.     unless( $elt->{parent})
  6436.       { # trying to erase the root (of a twig or of a cut/new element)
  6437.         my @children= do { my $elt= $elt; my @children=(); my $child= $elt->{first_child}; while( $child) { push @children, $child; $child= $child->{next_sibling}; } @children; };
  6438.         unless( @children == 1)
  6439.           { croak "can only erase an element with no parent if it has a single child"; }
  6440.         $elt->_move_extra_data_after_erase;
  6441.         my $child= shift @children;
  6442.         $child->{parent}=undef; if( $XML::Twig::weakrefs) { weaken( $child->{parent});} ;
  6443.         my $twig= $elt->twig;
  6444.         $twig->set_root( $child);
  6445.       }
  6446.     else     
  6447.       { # normal case
  6448.         $elt->_move_extra_data_after_erase;
  6449.         my @children= do { my $elt= $elt; my @children=(); my $child= $elt->{first_child}; while( $child) { push @children, $child; $child= $child->{next_sibling}; } @children; };
  6450.         if( @children)
  6451.           { # elt has children, move them up
  6452.  
  6453.             my $first_child= $elt->{first_child};
  6454.             my $prev_sibling=$elt->{prev_sibling};
  6455.             if( $prev_sibling)
  6456.               { # connect first child to previous sibling
  6457.                 $first_child->{prev_sibling}=$prev_sibling; if( $XML::Twig::weakrefs) { weaken( $first_child->{prev_sibling});} ;      
  6458.                 $prev_sibling->{next_sibling}=  $first_child; 
  6459.               }
  6460.             else
  6461.               { # elt was the first child
  6462.                 $elt->{parent}->set_first_child( $first_child);
  6463.               }
  6464.  
  6465.             my $last_child= $elt->{last_child};
  6466.             my $next_sibling= $elt->{next_sibling};
  6467.             if( $next_sibling)
  6468.               { # connect last child to next sibling
  6469.                 $last_child->{next_sibling}=  $next_sibling;      
  6470.                 $next_sibling->{prev_sibling}=$last_child; if( $XML::Twig::weakrefs) { weaken( $next_sibling->{prev_sibling});} ; 
  6471.               }
  6472.             else
  6473.               { # elt was the last child
  6474.                 $elt->{parent}->set_last_child( $last_child);
  6475.               }
  6476.             # update parent for all siblings
  6477.             foreach my $child (@children)
  6478.               { $child->{parent}=$elt->{parent}; if( $XML::Twig::weakrefs) { weaken( $child->{parent});} ; }
  6479.  
  6480.             # merge consecutive text elements if need be
  6481.             if( $prev_sibling && $prev_sibling->is_text && ($XML::Twig::index2gi[$first_child->{'gi'}] eq $XML::Twig::index2gi[$prev_sibling->{'gi'}]) )
  6482.               { $prev_sibling->merge_text( $first_child); }
  6483.             if( $next_sibling && $next_sibling->is_text && ($XML::Twig::index2gi[$last_child->{'gi'}] eq $XML::Twig::index2gi[$next_sibling->{'gi'}]) )
  6484.               { $last_child->merge_text( $next_sibling); }
  6485.  
  6486.             # if parsing and have now a PCDATA text, mark so we can normalize later on if need be
  6487.             if( $elt->{parent}->{twig_current} && $elt->{last_child}->is_text) {  $elt->{parent}->{twig_to_be_normalized}=1; }
  6488.  
  6489.             # elt is not referenced any more, so it will be DESTROYed
  6490.             # so we'd better break the links to its children
  6491.             undef $elt->{first_child};
  6492.             undef $elt->{last_child};
  6493.             undef $elt->{parent};
  6494.             undef $elt->{prev_sibling};
  6495.             undef $elt->{next_sibling};
  6496.  
  6497.           }
  6498.           { # elt had no child, delete it
  6499.              $elt->delete;
  6500.           }
  6501.               
  6502.       }
  6503.     return $elt;
  6504.  
  6505.   }
  6506.  
  6507. sub _move_extra_data_after_erase
  6508.   { my( $elt)= @_;
  6509.     # extra_data
  6510.     if( my $extra_data= $elt->{extra_data}) 
  6511.       { my $target= $elt->{first_child} || $elt->{next_sibling};
  6512.         if( $target)
  6513.           {
  6514.             if( $target->is( $ELT))
  6515.               { $target->set_extra_data( $extra_data . ($target->extra_data || '')); }
  6516.             elsif( $target->is( $TEXT))
  6517.               { $target->_unshift_extra_data_in_pcdata( $extra_data, 0); }  # TO CHECK
  6518.           }
  6519.         else
  6520.           { my $parent= $elt->{parent}; # always exists or the erase cannot be performed
  6521.             $parent->_prefix_extra_data_before_end_tag( $extra_data); 
  6522.           }
  6523.       }
  6524.       
  6525.      # extra_data_before_end_tag
  6526.     if( my $extra_data= $elt->{extra_data_before_end_tag}) 
  6527.       { if( my $target= $elt->{next_sibling})
  6528.           { if( $target->is( $ELT))
  6529.               { $target->set_extra_data( $extra_data . ($target->extra_data || '')); }
  6530.             elsif( $target->is( $TEXT))
  6531.               { 
  6532.                 $target->_unshift_extra_data_in_pcdata( $extra_data, 0);
  6533.              }
  6534.           }
  6535.         elsif( my $parent= $elt->{parent})
  6536.           { $parent->_prefix_extra_data_before_end_tag( $extra_data); }
  6537.        }
  6538.  
  6539.     return $elt;
  6540.  
  6541.   }
  6542. BEGIN
  6543.   { my %method= ( before      => \&paste_before,
  6544.                   after       => \&paste_after,
  6545.                   first_child => \&paste_first_child,
  6546.                   last_child  => \&paste_last_child,
  6547.                   within      => \&paste_within,
  6548.         );
  6549.     
  6550.     # paste elt somewhere around ref
  6551.     # pos can be first_child (default), last_child, before, after or within
  6552.     sub paste ## no critic (Subroutines::ProhibitNestedSubs);
  6553.       { my $elt= shift;
  6554.         if( $elt->{parent}) 
  6555.           { croak "cannot paste an element that belongs to a tree"; }
  6556.         my $pos;
  6557.         my $ref;
  6558.         if( ref $_[0]) 
  6559.           { $pos= 'first_child'; 
  6560.             croak "wrong argument order in paste, should be $_[1] first" if($_[1]); 
  6561.           }
  6562.         else
  6563.           { $pos= shift; }
  6564.  
  6565.         if( my $method= $method{$pos})
  6566.           {
  6567.             unless( ref( $_[0]) && isa( $_[0], 'XML::Twig::Elt'))
  6568.               { if( ! defined( $_[0]))
  6569.                   { croak "missing target in paste"; }
  6570.                 elsif( ! ref( $_[0]))
  6571.                   { croak "wrong target type in paste (not a reference), should be XML::Twig::Elt or a subclass"; }
  6572.                 else
  6573.                   { my $ref= ref $_[0];
  6574.                     croak "wrong target type in paste: '$ref', should be XML::Twig::Elt or a subclass";
  6575.                   }
  6576.               }
  6577.             $ref= $_[0];
  6578.             # check here so error message lists the caller file/line
  6579.             if( !$ref->{parent} && ($pos=~ m{^(before|after)$}) && !(exists $elt->{'target'}) && !(exists $elt->{'comment'})) 
  6580.               { croak "cannot paste $1 root"; }
  6581.             $elt->$method( @_); 
  6582.           }
  6583.         else
  6584.           { croak "tried to paste in wrong position '$pos', allowed positions " . 
  6585.               " are 'first_child', 'last_child', 'before', 'after' and "    .
  6586.               "'within'";
  6587.           }
  6588.         if( (my $ids= $elt->{twig_id_list}) && (my $t= $ref->twig) )
  6589.           { $t->{twig_id_list}||={};
  6590.             foreach my $id (keys %$ids)
  6591.               { $t->{twig_id_list}->{$id}= $ids->{$id}; 
  6592.                 if( $XML::Twig::weakrefs) { weaken( $t->{twig_id_list}->{$id}); }
  6593.               }
  6594.           }
  6595.         return $elt;
  6596.       }
  6597.   
  6598.  
  6599.     sub paste_before
  6600.       { my( $elt, $ref)= @_;
  6601.         my( $parent, $prev_sibling, $next_sibling );
  6602.         
  6603.         # trying to paste before an orphan (root or detached wlt)
  6604.         unless( $ref->{parent}) 
  6605.           { if( my $t= $ref->twig)
  6606.               { if( (exists $elt->{'comment'}) || (exists $elt->{'target'})) # we can still do this
  6607.                   { $t->_add_cpi_outside_of_root( leading_cpi => $elt); return; }
  6608.                 else
  6609.                   { croak "cannot paste before root"; }
  6610.               }
  6611.             else
  6612.               { croak "cannot paste before an orphan element"; }
  6613.           }
  6614.         $parent= $ref->{parent};
  6615.         $prev_sibling= $ref->{prev_sibling};
  6616.         $next_sibling= $ref;
  6617.  
  6618.         $elt->{parent}=$parent; if( $XML::Twig::weakrefs) { weaken( $elt->{parent});} ;
  6619.         if( $parent->{first_child} == $ref) { $parent->{first_child}=  $elt; }
  6620.  
  6621.         if( $prev_sibling) { $prev_sibling->{next_sibling}=  $elt; }
  6622.         $elt->{prev_sibling}=$prev_sibling; if( $XML::Twig::weakrefs) { weaken( $elt->{prev_sibling});} ;
  6623.  
  6624.         $next_sibling->{prev_sibling}=$elt; if( $XML::Twig::weakrefs) { weaken( $next_sibling->{prev_sibling});} ;
  6625.         $elt->{next_sibling}=  $ref;
  6626.         return $elt;
  6627.       }
  6628.      
  6629.      sub paste_after
  6630.       { my( $elt, $ref)= @_;
  6631.         my( $parent, $prev_sibling, $next_sibling );
  6632.  
  6633.         # trying to paste after an orphan (root or detached wlt)
  6634.         unless( $ref->{parent}) 
  6635.             { if( my $t= $ref->twig)
  6636.                 { if( (exists $elt->{'comment'}) || (exists $elt->{'target'})) # we can still do this
  6637.                     { $t->_add_cpi_outside_of_root( trailing_cpi => $elt); return; }
  6638.                   else
  6639.                     { croak "cannot paste after root"; }
  6640.                 }
  6641.               else
  6642.                 { croak "cannot paste after an orphan element"; }
  6643.             }
  6644.         $parent= $ref->{parent};
  6645.         $prev_sibling= $ref;
  6646.         $next_sibling= $ref->{next_sibling};
  6647.  
  6648.         $elt->{parent}=$parent; if( $XML::Twig::weakrefs) { weaken( $elt->{parent});} ;
  6649.         if( $parent->{last_child}== $ref) {  delete $parent->{empty}; $parent->{last_child}=$elt; if( $XML::Twig::weakrefs) { weaken( $parent->{last_child});} ; }
  6650.  
  6651.         $prev_sibling->{next_sibling}=  $elt;
  6652.         $elt->{prev_sibling}=$prev_sibling; if( $XML::Twig::weakrefs) { weaken( $elt->{prev_sibling});} ;
  6653.  
  6654.         if( $next_sibling) { $next_sibling->{prev_sibling}=$elt; if( $XML::Twig::weakrefs) { weaken( $next_sibling->{prev_sibling});} ; }
  6655.         $elt->{next_sibling}=  $next_sibling;
  6656.         return $elt;
  6657.  
  6658.       }
  6659.  
  6660.     sub paste_first_child
  6661.       { my( $elt, $ref)= @_;
  6662.         my( $parent, $prev_sibling, $next_sibling );
  6663.         $parent= $ref;
  6664.         $next_sibling= $ref->{first_child};
  6665.         delete $ref->{empty};
  6666.  
  6667.         $elt->{parent}=$parent; if( $XML::Twig::weakrefs) { weaken( $elt->{parent});} ;
  6668.         $parent->{first_child}=  $elt;
  6669.         unless( $parent->{last_child}) {  delete $parent->{empty}; $parent->{last_child}=$elt; if( $XML::Twig::weakrefs) { weaken( $parent->{last_child});} ; }
  6670.  
  6671.         $elt->{prev_sibling}=undef; if( $XML::Twig::weakrefs) { weaken( $elt->{prev_sibling});} ;
  6672.  
  6673.         if( $next_sibling) { $next_sibling->{prev_sibling}=$elt; if( $XML::Twig::weakrefs) { weaken( $next_sibling->{prev_sibling});} ; }
  6674.         $elt->{next_sibling}=  $next_sibling;
  6675.         return $elt;
  6676.       }
  6677.       
  6678.     sub paste_last_child
  6679.       { my( $elt, $ref)= @_;
  6680.         my( $parent, $prev_sibling, $next_sibling );
  6681.         $parent= $ref;
  6682.         $prev_sibling= $ref->{last_child};
  6683.         delete $ref->{empty};
  6684.  
  6685.         $elt->{parent}=$parent; if( $XML::Twig::weakrefs) { weaken( $elt->{parent});} ;
  6686.          delete $parent->{empty}; $parent->{last_child}=$elt; if( $XML::Twig::weakrefs) { weaken( $parent->{last_child});} ;
  6687.         unless( $parent->{first_child}) { $parent->{first_child}=  $elt; }
  6688.  
  6689.         $elt->{prev_sibling}=$prev_sibling; if( $XML::Twig::weakrefs) { weaken( $elt->{prev_sibling});} ;
  6690.         if( $prev_sibling) { $prev_sibling->{next_sibling}=  $elt; }
  6691.  
  6692.         $elt->{next_sibling}=  undef;
  6693.         return $elt;
  6694.       }
  6695.  
  6696.     sub paste_within
  6697.       { my( $elt, $ref, $offset)= @_;
  6698.         my $text= $ref->is_text ? $ref : $ref->next_elt( $TEXT, $ref);
  6699.         my $new= $text->split_at( $offset);
  6700.         $elt->paste_before( $new);
  6701.         return $elt;
  6702.       }
  6703.   }
  6704.  
  6705. # load an element into a structure similar to XML::Simple's
  6706. sub simplify
  6707.   { my $elt= shift;
  6708.  
  6709.     # normalize option names
  6710.     my %options= @_;
  6711.     %options= map { my ($key, $val)= ($_, $options{$_});
  6712.                        $key=~ s{(\w)([A-Z])}{$1_\L$2}g;
  6713.                        $key => $val
  6714.                      } keys %options;
  6715.  
  6716.     # check options
  6717.     my @allowed_options= qw( keyattr forcearray noattr content_key
  6718.                              var var_regexp variables var_attr 
  6719.                              group_tags forcecontent
  6720.                              normalise_space normalize_space
  6721.                    );
  6722.     my %allowed_options= map { $_ => 1 } @allowed_options;
  6723.     foreach my $option (keys %options)
  6724.       { carp "invalid option $option\n" unless( $allowed_options{$option}); }
  6725.  
  6726.     $options{normalise_space} ||= $options{normalize_space} || 0;
  6727.     
  6728.     $options{content_key} ||= 'content';
  6729.     if( $options{content_key}=~ m{^-})
  6730.       { # need to remove the - and to activate extra folding
  6731.         $options{content_key}=~ s{^-}{};
  6732.         $options{extra_folding}= 1;
  6733.       }
  6734.     else
  6735.       { $options{extra_folding}= 0; }
  6736.    
  6737.     $options{forcearray} ||=0; 
  6738.     if( isa( $options{forcearray}, 'ARRAY'))
  6739.       { my %forcearray_tags= map { $_ => 1 } @{$options{forcearray}};
  6740.         $options{forcearray_tags}= \%forcearray_tags;
  6741.         $options{forcearray}= 0;
  6742.       }
  6743.  
  6744.     $options{keyattr}     ||= ['name', 'key', 'id'];
  6745.     if( ref $options{keyattr} eq 'ARRAY')
  6746.       { foreach my $keyattr (@{$options{keyattr}})
  6747.           { my( $prefix, $att)= ($keyattr=~ m{^([+-])?(.*)});
  6748.             $prefix ||= '';
  6749.             $options{key_for_all}->{$att}= 1;
  6750.             $options{remove_key_for_all}->{$att}=1 unless( $prefix eq '+');
  6751.             $options{prefix_key_for_all}->{$att}=1 if( $prefix eq '-');
  6752.           }
  6753.       }
  6754.     elsif( ref $options{keyattr} eq 'HASH')
  6755.       { while( my( $elt, $keyattr)= each %{$options{keyattr}})
  6756.          { my( $prefix, $att)= ($keyattr=~ m{^([+-])?(.*)});
  6757.            $prefix ||='';
  6758.            $options{key_for_elt}->{$elt}= $att;
  6759.            $options{remove_key_for_elt}->{"$elt#$att"}=1 unless( $prefix);
  6760.            $options{prefix_key_for_elt}->{"$elt#$att"}=1 if( $prefix eq '-');
  6761.          }
  6762.       }
  6763.        
  6764.  
  6765.     $options{var}||= $options{var_attr}; # for compat with XML::Simple
  6766.     if( $options{var}) { $options{var_values}= {}; }
  6767.     else               { $options{var}='';         }
  6768.  
  6769.     if( $options{variables}) 
  6770.       { $options{var}||= 1;
  6771.         $options{var_values}= $options{variables};
  6772.       }
  6773.  
  6774.     if( $options{var_regexp} and !$options{var})
  6775.       { warn "var option not used, var_regexp option ignored\n"; }
  6776.     $options{var_regexp} ||= '\$\{?(\w+)\}?';
  6777.       
  6778.     $elt->_simplify( \%options);
  6779.  
  6780.  }
  6781.  
  6782. sub _simplify
  6783.   { my( $elt, $options)= @_;
  6784.  
  6785.     my $data;
  6786.  
  6787.     my $gi= $XML::Twig::index2gi[$elt->{'gi'}];
  6788.     my @children= do { my $elt= $elt; my @children=(); my $child= $elt->{first_child}; while( $child) { push @children, $child; $child= $child->{next_sibling}; } @children; };
  6789.     my %atts= $options->{noattr} || !$elt->{att} ? () : %{$elt->{att}};
  6790.     my $nb_atts= keys %atts;
  6791.     my $nb_children= $elt->children_count + $nb_atts;
  6792.  
  6793.     my %nb_children;
  6794.     foreach (@children)   { $nb_children{$_->tag}++; }
  6795.     foreach (keys %atts)  { $nb_children{$_}++;      }
  6796.  
  6797.     my $arrays; # tag => array where elements are stored
  6798.  
  6799.  
  6800.     # store children
  6801.     foreach my $child (@children)
  6802.       { if( $child->is_text)
  6803.           { # generate with a content key
  6804.             my $text= $elt->_text_with_vars( $options);
  6805.             if( $options->{normalise_space} >= 2) { $text= _normalize_space( $text); }
  6806.             if(    $options->{force_content}
  6807.                 || $nb_atts 
  6808.                 || (scalar @children > 1)
  6809.               )
  6810.               { $data->{$options->{content_key}}= $text; }
  6811.             else
  6812.               { $data= $text; }
  6813.           }
  6814.         else
  6815.           { # element with sub elements
  6816.             my $child_gi= $XML::Twig::index2gi[$child->{'gi'}];
  6817.  
  6818.             my $child_data= $child->_simplify( $options);
  6819.  
  6820.             # first see if we need to simplify further the child data
  6821.             # simplify because of grouped tags
  6822.             if( my $grouped_tag= $options->{group_tags}->{$child_gi})
  6823.               { # check that the child data is a hash with a single field
  6824.                 unless(    (ref( $child_data) eq 'HASH')
  6825.                         && (keys %$child_data == 1)
  6826.                         && defined ( my $grouped_child_data= $child_data->{$grouped_tag})
  6827.                       )
  6828.                   { croak "error in grouped tag $child_gi"; }
  6829.                 else
  6830.                   { $child_data=  $grouped_child_data; }
  6831.               }
  6832.             # simplify because of extra folding
  6833.             if( $options->{extra_folding})
  6834.               { if(    (ref( $child_data) eq 'HASH')
  6835.                     && (keys %$child_data == 1)
  6836.                     && defined( my $content= $child_data->{$options->{content_key}})
  6837.                   )
  6838.                   { $child_data= $content; }
  6839.               }
  6840.  
  6841.             if( my $keyatt= $child->_key_attr( $options))
  6842.               { # simplify element with key
  6843.                 my $key= $child->{'att'}->{$keyatt};
  6844.                 if( $options->{normalise_space} >= 1) { $key= _normalize_space( $key); }
  6845.                 $data->{$child_gi}->{$key}= $child_data;
  6846.               }
  6847.             elsif(      $options->{forcearray}
  6848.                    ||   $options->{forcearray_tags}->{$child_gi}
  6849.                    || ( $nb_children{$child_gi} > 1)
  6850.                  )
  6851.               { # simplify element to store in an array
  6852.                 $data->{$child_gi} ||= [];
  6853.                 push @{$data->{$child_gi}}, $child_data;
  6854.               }
  6855.             else
  6856.               { # simplify element to store as a hash field
  6857.                 $data->{$child_gi}= $child_data;
  6858.               }
  6859.           }
  6860.     }
  6861.  
  6862.     # store atts
  6863.     # TODO: deal with att that already have an element by that name
  6864.     foreach my $att (keys %atts)
  6865.       { # do not store if the att is a key that needs to be removed
  6866.         if(    $options->{remove_key_for_all}->{$att}
  6867.             || $options->{remove_key_for_elt}->{"$gi#$att"}
  6868.           )
  6869.           { next; }
  6870.  
  6871.         my $att_text= $options->{var} ?  _replace_vars_in_text( $atts{$att}, $options) : $atts{$att} ;
  6872.         if( $options->{normalise_space} >= 2) { $att_text= _normalize_space( $att_text); }
  6873.         
  6874.         if(    $options->{prefix_key_for_all}->{$att}
  6875.             || $options->{prefix_key_for_elt}->{"$gi#$att"}
  6876.           )
  6877.           { # prefix the att
  6878.             $data->{"-$att"}= $att_text;
  6879.           } 
  6880.         else
  6881.           { # normal case
  6882.             $data->{$att}= $att_text; 
  6883.           }
  6884.       }
  6885.     
  6886.     return $data;
  6887.   }
  6888.  
  6889. sub _key_attr
  6890.   { my( $elt, $options)=@_;
  6891.     return if( $options->{noattr});
  6892.     if( $options->{key_for_all})
  6893.       { foreach my $att ($elt->att_names)
  6894.           { if( $options->{key_for_all}->{$att})
  6895.               { return $att; }
  6896.           }
  6897.       }
  6898.     elsif( $options->{key_for_elt})
  6899.       { if( my $key_for_elt= $options->{key_for_elt}->{$XML::Twig::index2gi[$elt->{'gi'}]} )
  6900.           { return $key_for_elt if( defined( $elt->{'att'}->{$key_for_elt})); }
  6901.       }
  6902.     return;
  6903.   }
  6904.  
  6905. sub _text_with_vars
  6906.   { my( $elt, $options)= @_;
  6907.     my $text;
  6908.     if( $options->{var}) 
  6909.       { $text= _replace_vars_in_text( $elt->text, $options); 
  6910.         $elt->_store_var( $options);
  6911.       }
  6912.      else
  6913.       { $text= $elt->text; }
  6914.     return $text;
  6915.   }
  6916.  
  6917.  
  6918. sub _normalize_space
  6919.   { my $text= shift;
  6920.     $text=~ s{\s+}{ }sg;
  6921.     $text=~ s{^\s}{};
  6922.     $text=~ s{\s$}{};
  6923.     return $text;
  6924.   }
  6925.  
  6926.  
  6927. sub att_nb
  6928.   { return 0 unless( my $atts= $_[0]->{att});
  6929.     return scalar keys %$atts;
  6930.   }
  6931.     
  6932. sub has_no_atts
  6933.   { return 1 unless( my $atts= $_[0]->{att});
  6934.     return scalar keys %$atts ? 0 : 1;
  6935.   }
  6936.     
  6937. sub _replace_vars_in_text
  6938.   { my( $text, $options)= @_;
  6939.  
  6940.     $text=~ s{($options->{var_regexp})}
  6941.              { if( defined( my $value= $options->{var_values}->{$2}))
  6942.                  { $value }
  6943.                else
  6944.                  { warn "unknown variable $2\n";
  6945.                    $1
  6946.                  }
  6947.              }gex;
  6948.     return $text;
  6949.   }
  6950.  
  6951. sub _store_var
  6952.   { my( $elt, $options)= @_;
  6953.     if( defined (my $var_name= $elt->{'att'}->{$options->{var}}))
  6954.        { $options->{var_values}->{$var_name}= $elt->text; 
  6955.        }
  6956.   }
  6957.  
  6958.  
  6959. # split a text element at a given offset
  6960. sub split_at
  6961.   { my( $elt, $offset)= @_;
  6962.     my $text_elt= $elt->is_text ? $elt : $elt->first_child( $TEXT) || return '';
  6963.     my $string= $text_elt->text; 
  6964.     my $left_string= substr( $string, 0, $offset);
  6965.     my $right_string= substr( $string, $offset);
  6966.     $text_elt->set_pcdata( $left_string);
  6967.     my $new_elt= $elt->new( $XML::Twig::index2gi[$elt->{'gi'}], $right_string);
  6968.     $new_elt->paste( after => $elt);
  6969.     return $new_elt;
  6970.   }
  6971.  
  6972.     
  6973. # split an element or its text descendants into several, in place
  6974. # all elements (new and untouched) are returned
  6975. sub split    
  6976.   { my $elt= shift;
  6977.     my @text_chunks;
  6978.     my @result;
  6979.     if( $elt->is_text) { @text_chunks= ($elt); }
  6980.     else               { @text_chunks= $elt->descendants( $TEXT); }
  6981.     foreach my $text_chunk (@text_chunks)
  6982.       { push @result, $text_chunk->_split( 1, @_); }
  6983.     return @result;
  6984.   }
  6985.  
  6986. # split an element or its text descendants into several, in place
  6987. # created elements (those which match the regexp) are returned
  6988. sub mark
  6989.   { my $elt= shift;
  6990.     my @text_chunks;
  6991.     my @result;
  6992.     if( $elt->is_text) { @text_chunks= ($elt); }
  6993.     else               { @text_chunks= $elt->descendants( $TEXT); }
  6994.     foreach my $text_chunk (@text_chunks)
  6995.       { push @result, $text_chunk->_split( 0, @_); }
  6996.     return @result;
  6997.   }
  6998.  
  6999. # split a single text element
  7000. # return_all defines what is returned: if it is true 
  7001. # only returns the elements created by matches in the split regexp
  7002. # otherwise all elements (new and untouched) are returned
  7003.  
  7004.  
  7005.   sub _split
  7006.     { my $elt= shift;
  7007.       my $return_all= shift;
  7008.       my $regexp= shift;
  7009.       my @tags;
  7010.  
  7011.       while( my $tag= shift())
  7012.         { if( ref $_[0]) 
  7013.             { push @tags, { tag => $tag, atts => shift }; }
  7014.           else
  7015.             { push @tags, { tag => $tag }; }
  7016.         }
  7017.  
  7018.       unless( @tags) { @tags= { tag => $elt->{parent}->gi }; }
  7019.           
  7020.       my @result;                                 # the returned list of elements
  7021.       my $text= $elt->text;
  7022.       my $gi= $XML::Twig::index2gi[$elt->{'gi'}];
  7023.   
  7024.       # 2 uses: if split matches then the first substring reuses $elt
  7025.       #         once a split has occured then the last match needs to be put in
  7026.       #         a new element      
  7027.       my $previous_match= 0;
  7028.  
  7029.       while( my( $pre_match, @matches)= $text=~ /^(.*?)$regexp(.*)$/gcs)
  7030.         { $text= pop @matches;
  7031.           if( $previous_match)
  7032.             { # match, not the first one, create a new text ($gi) element
  7033.               _utf8_ify( $pre_match) if( $[ < 5.010);
  7034.               $elt= $elt->insert_new_elt( after => $gi, $pre_match);
  7035.               push @result, $elt if( $return_all);
  7036.             }
  7037.           else
  7038.             { # first match in $elt, re-use $elt for the first sub-string
  7039.               _utf8_ify( $pre_match) if( $[ < 5.010);
  7040.               $elt->set_text( $pre_match);
  7041.               $previous_match++;                # store the fact that there was a match
  7042.               push @result, $elt if( $return_all);
  7043.             }
  7044.  
  7045.           # now deal with matches captured in the regexp
  7046.           if( @matches)
  7047.             { # match, with capture
  7048.               my $i=0;
  7049.               foreach my $match (@matches)
  7050.                 { # create new element, text is the match
  7051.                   _utf8_ify( $match) if( $[ < 5.010);
  7052.                   my $tag  = $tags[$i]->{tag};
  7053.                   my $atts = \%{$tags[$i]->{atts}} || {};
  7054.                   $elt= $elt->insert_new_elt( after => $tag, $atts, $match);
  7055.                   push @result, $elt;
  7056.                   $i= ($i + 1) % @tags;
  7057.                 }
  7058.             }
  7059.           else
  7060.             { # match, no captures
  7061.               my $tag  = $tags[0]->{tag};
  7062.               my $atts = \%{$tags[0]->{atts}} || {};
  7063.               $elt=  $elt->insert_new_elt( after => $tag, $atts);
  7064.               push @result, $elt;
  7065.             }
  7066.         }
  7067.       if( $previous_match && $text)
  7068.         { # there was at least 1 match, and there is text left after the match
  7069.           $elt= $elt->insert_new_elt( after => $gi, $text);
  7070.         }
  7071.  
  7072.       push @result, $elt if( $return_all);
  7073.  
  7074.       return @result; # return all elements
  7075.    }
  7076.  
  7077.   # evil hack needed as sometimes 
  7078.   my $encode_is_loaded=0;   # so we only load Encode once
  7079.   sub _utf8_ify
  7080.     { 
  7081.       if( $] >= 5.008 and $] < 5.010 and !_keep_encoding()) 
  7082.         { unless( $encode_is_loaded) { require Encode; import Encode; $encode_is_loaded++; }
  7083.           Encode::_utf8_on( $_[0]); # the flag should be set but is not
  7084.         }
  7085.     }
  7086.  
  7087.  
  7088. }
  7089.  
  7090. { my %replace_sub; # cache for complex expressions (expression => sub)
  7091.  
  7092.   sub subs_text
  7093.     { my( $elt, $regexp, $replace)= @_;
  7094.   
  7095.       my $replacement_string;
  7096.       my $is_string= _is_string( $replace);
  7097.       foreach my $text_elt ($elt->descendants_or_self( $TEXT))
  7098.         { if( $is_string)
  7099.             { my $text= $text_elt->text;
  7100.               $text=~ s{$regexp}{ _replace_var( $replace, $1, $2, $3, $4, $5, $6, $7, $8, $9)}egx;
  7101.               $text_elt->set_text( $text);
  7102.            }
  7103.           else
  7104.             { 
  7105.               my $replace_sub= ( $replace_sub{$replace} ||= _install_replace_sub( $replace)); 
  7106.               my $text= $text_elt->text;
  7107.               my $pos=0;  # used to skip text that was previously matched
  7108.               while( my( $pre_match_string, $match_string, @var)= ($text=~ m{(.*?)($regexp)}sg))
  7109.                 { my $match_start  = length( $pre_match_string);
  7110.                   my $match        = $text_elt->split_at( $match_start + $pos);
  7111.                   my $match_length = length( $match_string);
  7112.                   my $post_match   = $match->split_at( $match_length);
  7113.                   $replace_sub->( $match, @var);
  7114.                   # merge previous text with current one
  7115.                   my $next_sibling;
  7116.                   if(    ($next_sibling= $text_elt->{next_sibling})
  7117.                       && ($XML::Twig::index2gi[$text_elt->{'gi'}] eq $XML::Twig::index2gi[$next_sibling->{'gi'}])
  7118.                     )
  7119.                     { $text_elt->merge_text( $next_sibling); }
  7120.                     
  7121.                   # if the match is at the beginning of the text an empty #PCDATA is left: remove it 
  7122.                   if( !$text_elt->text) { $text_elt->delete; } 
  7123.                   
  7124.                   # go to next 
  7125.                   $text_elt= $post_match;
  7126.                   $text= $post_match->text;
  7127.                   # merge last text element with next one if needed,
  7128.                   # the match will be against the non-matched text,
  7129.                   # so $pos is used to skip the merged part
  7130.                   my $prev_sibling;
  7131.                   if(    ($prev_sibling=  $post_match->{prev_sibling})
  7132.                       && ($XML::Twig::index2gi[$post_match->{'gi'}] eq $XML::Twig::index2gi[$prev_sibling->{'gi'}])
  7133.                     )
  7134.                     { $pos= length( $prev_sibling->text);
  7135.                       $prev_sibling->merge_text( $post_match); 
  7136.                     }
  7137.  
  7138.                   # if the match is at the end of the text an empty #PCDATA is left: remove it 
  7139.                   if( !$text_elt->text) { $text_elt->delete; } 
  7140.                   
  7141.                 }
  7142.               
  7143.             }
  7144.         }
  7145.       return $elt;
  7146.     }
  7147.  
  7148.  
  7149.   sub _is_string
  7150.     { return ($_[0]=~ m{&e[ln]t}) ? 0: 1 }
  7151.  
  7152.   sub _replace_var
  7153.     { my( $string, @var)= @_;
  7154.       unshift @var, undef;
  7155.       $string=~ s{\$(\d)}{$var[$1]}g;
  7156.       return $string;
  7157.     }
  7158.  
  7159.   sub _install_replace_sub
  7160.     { my $replace_exp= shift;
  7161.       my @item= split m{(&e[ln]t\s*\([^)]*\))}, $replace_exp;
  7162.       my $sub= q{ my( $match, @var)= @_; unshift @var, undef; my $new; };
  7163.       my( $gi, $exp);
  7164.       foreach my $item (@item)
  7165.         { if(    $item=~ m{^&elt\s*\(([^)]*)\)})
  7166.             { $exp= $1;
  7167.             }
  7168.           elsif( $item=~ m{^&ent\s*\(\s*([^\s)]*)\s*\)})
  7169.             { $exp= " '#ENT' => $1"; }
  7170.           else
  7171.             { $exp= qq{ '#PCDATA' => "$item"}; }
  7172.           $exp=~ s{\$(\d)}{\$var[$1]}g; # replace references to matches
  7173.           $sub.= qq{ \$new= \$match->new( $exp); };
  7174.           $sub .= q{ $new->paste( before => $match); };
  7175.         }
  7176.       $sub .= q{ $match->delete; };
  7177.       #$sub=~ s/;/;\n/g;
  7178.       my $coderef= eval "sub { $NO_WARNINGS; $sub }";
  7179.       if( $@) { croak( "invalid replacement expression $replace_exp: ",$@); }
  7180.       return $coderef;
  7181.     }
  7182.  
  7183.   }
  7184.  
  7185.  
  7186. sub merge_text
  7187.   { my( $e1, $e2)= @_;
  7188.     croak "invalid merge: can only merge 2 elements" 
  7189.         unless( isa( $e2, 'XML::Twig::Elt'));
  7190.     croak "invalid merge: can only merge 2 text elements" 
  7191.         unless( $e1->is_text && $e2->is_text && ($e1->gi eq $e2->gi));
  7192.  
  7193.     my $text1= $e1->text; if( ! defined $text1) { $text1= ''; }
  7194.     my $text2= $e2->text; if( ! defined $text2) { $text2= ''; }
  7195.  
  7196.     $e1->set_text( $text1 . $text2);
  7197.  
  7198.     my $extra_data= $e1->_extra_data_before_end_tag . $e2->extra_data;
  7199.     if( $extra_data) 
  7200.       { $e1->_del_extra_data_before_end_tag;
  7201.         $e1->_push_extra_data_in_pcdata( $extra_data, length( $text1)); 
  7202.       }
  7203.  
  7204.     if( $extra_data= $e2->_extra_data_in_pcdata)
  7205.       { foreach my $data (@$extra_data) { $e1->_push_extra_data_in_pcdata( $data->{text}, $data->{offset} + length( $text1)); } }
  7206.  
  7207.     if( my $extra_data_before_end_tag= $e2->_extra_data_before_end_tag) 
  7208.       { $e1->_set_extra_data_before_end_tag( $extra_data_before_end_tag); }
  7209.  
  7210.     $e2->delete;
  7211.  
  7212.     return $e1;
  7213.   }
  7214.  
  7215. sub merge
  7216.   { my( $e1, $e2)= @_;
  7217.     my @e2_children= $e2->_children;
  7218.     if(     $e1->_last_child && $e1->_last_child->is_pcdata
  7219.         &&  @e2_children && $e2_children[0]->is_pcdata
  7220.       )
  7221.       { $e1->_last_child->{pcdata} .= $e2_children[0]->{pcdata}; shift @e2_children; }
  7222.     foreach my $e (@e2_children) { $e->move( last_child => $e1); } 
  7223.     $e2->delete;
  7224.     return $e1;
  7225.   }
  7226.  
  7227.  
  7228. # recursively copy an element and returns the copy (can be huge and long)
  7229. sub copy
  7230.   { my $elt= shift;
  7231.     my $copy= $elt->new( $XML::Twig::index2gi[$elt->{'gi'}]);
  7232.  
  7233.     if( $elt->extra_data) { $copy->set_extra_data( $elt->extra_data); }
  7234.     if( $elt->{extra_data_before_end_tag}) { $copy->_set_extra_data_before_end_tag( $elt->{extra_data_before_end_tag}); }
  7235.  
  7236.     if( $elt->is_asis)   { $copy->set_asis; }
  7237.     $copy->{empty}=  $elt->{'empty'}; # do not swap or speedup will mess up this                         
  7238.  
  7239.     if( (exists $elt->{'pcdata'})) 
  7240.       { $copy->set_pcdata( $elt->{pcdata}); 
  7241.         if( $elt->{extra_data_in_pcdata}) { $copy->_set_extra_data_in_pcdata( $elt->{extra_data_in_pcdata}); }
  7242.       }
  7243.     elsif( (exists $elt->{'cdata'}))
  7244.       { $copy->_set_cdata( $elt->{cdata}); 
  7245.         if( $elt->{extra_data_in_pcdata}) { $copy->_set_extra_data_in_pcdata( $elt->{extra_data_in_pcdata}); }
  7246.       }
  7247.     elsif( (exists $elt->{'target'}))
  7248.       { $copy->_set_pi( $elt->{target}, $elt->{data}); }
  7249.     elsif( (exists $elt->{'comment'}))
  7250.       { $copy->_set_comment( $elt->{comment}); }
  7251.     elsif( (exists $elt->{'ent'}))
  7252.       { $copy->{ent}=  $elt->{ent}; }
  7253.     else
  7254.       { my @children= do { my $elt= $elt; my @children=(); my $child= $elt->{first_child}; while( $child) { push @children, $child; $child= $child->{next_sibling}; } @children; };
  7255.         if( my $atts= $elt->{att})
  7256.           { my %atts;
  7257.             tie %atts, 'Tie::IxHash' if (keep_atts_order());
  7258.             %atts= %{$atts}; # we want to do a real copy of the attributes
  7259.             $copy->set_atts( \%atts);
  7260.           }
  7261.         foreach my $child (@children)
  7262.           { my $child_copy= $child->copy;
  7263.             $child_copy->paste( 'last_child', $copy);
  7264.           }
  7265.       }
  7266.     # save links to the original location, which can be convenient and is used for namespace resolution
  7267.     foreach my $link ( qw(parent prev_sibling next_sibling) )
  7268.       { $copy->{former}->{$link}= $elt->{$link};
  7269.         if( $XML::Twig::weakrefs) { weaken( $copy->{former}->{$link}); }
  7270.       }
  7271.  
  7272.     return $copy;
  7273.   }
  7274.  
  7275.  
  7276. sub delete
  7277.   { my $elt= shift;
  7278.     $elt->cut;
  7279.     $elt->DESTROY unless( $XML::Twig::weakrefs);
  7280.     return undef;
  7281.   }
  7282.  
  7283.   sub DESTROY
  7284.     { my $elt= shift;
  7285.       return if( $XML::Twig::weakrefs);
  7286.       my $t= shift || $elt->twig; # optional argument, passed in recursive calls
  7287.  
  7288.       foreach( @{[$elt->_children]}) { $_->DESTROY( $t); }
  7289.  
  7290.       # the id reference needs to be destroyed
  7291.       # lots of tests to avoid warnings during the cleanup phase
  7292.       $elt->del_id( $t) if( $ID && $t && defined( $elt->{att}) && exists( $elt->{att}->{$ID}));
  7293.       undef $elt;
  7294.     }
  7295. }
  7296.  
  7297.  
  7298. # ignores the element
  7299. sub ignore
  7300.   { my $elt= shift;
  7301.     my $t= $elt->twig;
  7302.     $t->ignore( $elt, @_);
  7303.   }
  7304.  
  7305. BEGIN {
  7306.   my $pretty                    = 0;
  7307.   my $quote                     = '"';
  7308.   my $INDENT                    = '  ';
  7309.   my $empty_tag_style           = 0;
  7310.   my $remove_cdata              = 0;
  7311.   my $keep_encoding             = 0;
  7312.   my $expand_external_entities  = 0;
  7313.   my $keep_atts_order           = 0;
  7314.   my $do_not_escape_amp_in_atts = 0;
  7315.   my $WRAP                      = '80';
  7316.   my $REPLACED_ENTS             = qq{&<};
  7317.  
  7318.   my ($NSGMLS, $NICE, $INDENTED, $INDENTEDCT, $INDENTEDC, $WRAPPED, $RECORD1, $RECORD2, $INDENTEDA)= (1..9);
  7319.   my %KEEP_TEXT_TAG_ON_ONE_LINE= map { $_ => 1 } ( $INDENTED, $INDENTEDCT, $INDENTEDC, $INDENTEDA, $WRAPPED);
  7320.  
  7321.   my %pretty_print_style=
  7322.     ( none       => 0,          # no added \n
  7323.       nsgmls     => $NSGMLS,    # nsgmls-style, \n in tags
  7324.       # below this line styles are UNSAFE (the generated XML can be well-formed but invalid)
  7325.       nice       => $NICE,      # \n after open/close tags except when the 
  7326.                                 # element starts with text
  7327.       indented   => $INDENTED,  # nice plus idented
  7328.       indented_close_tag   => $INDENTEDCT,  # nice plus idented
  7329.       indented_c => $INDENTEDC, # slightly more compact than indented (closing
  7330.                                 # tags are on the same line)
  7331.       wrapped    => $WRAPPED,   # text is wrapped at column 
  7332.       record_c   => $RECORD1,   # for record-like data (compact)
  7333.       record     => $RECORD2,   # for record-like data  (not so compact)
  7334.       indented_a => $INDENTEDA, # nice, indented, and with attributes on separate
  7335.                                 # lines as the nsgmls style, as well as wrapped
  7336.                                 # lines - to make the xml friendly to line-oriented tools
  7337.       cvs        => $INDENTEDA, # alias for indented_a
  7338.     );
  7339.  
  7340.   my ($HTML, $EXPAND)= (1..2);
  7341.   my %empty_tag_style=
  7342.     ( normal => 0,        # <tag/>
  7343.       html   => $HTML,    # <tag />
  7344.       xhtml  => $HTML,    # <tag />
  7345.       expand => $EXPAND,  # <tag></tag>
  7346.     );
  7347.  
  7348.   my %quote_style=
  7349.     ( double  => '"',    
  7350.       single  => "'", 
  7351.       # smart  => "smart", 
  7352.     );
  7353.  
  7354.   my $xml_space_preserve; # set when an element includes xml:space="preserve"
  7355.  
  7356.   my $output_filter;      # filters the entire output (including < and >)
  7357.   my $output_text_filter; # filters only the text part (tag names, attributes, pcdata)
  7358.  
  7359.   my $replaced_ents= $REPLACED_ENTS;
  7360.  
  7361.  
  7362.   # returns those pesky "global" variables so you can switch between twigs 
  7363.   sub global_state ## no critic (Subroutines::ProhibitNestedSubs);
  7364.     { return
  7365.        { pretty                    => $pretty,
  7366.          quote                     => $quote,
  7367.          indent                    => $INDENT,
  7368.          empty_tag_style           => $empty_tag_style,
  7369.          remove_cdata              => $remove_cdata,
  7370.          keep_encoding             => $keep_encoding,
  7371.          expand_external_entities  => $expand_external_entities,
  7372.          output_filter             => $output_filter,
  7373.          output_text_filter        => $output_text_filter,
  7374.          keep_atts_order           => $keep_atts_order,
  7375.          do_not_escape_amp_in_atts => $do_not_escape_amp_in_atts,
  7376.          wrap                      => $WRAP,
  7377.          replaced_ents             => $replaced_ents,
  7378.         };
  7379.     }
  7380.  
  7381.   # restores the global variables
  7382.   sub set_global_state
  7383.     { my $state= shift;
  7384.       $pretty                    = $state->{pretty};
  7385.       $quote                     = $state->{quote};
  7386.       $INDENT                    = $state->{indent};
  7387.       $empty_tag_style           = $state->{empty_tag_style};
  7388.       $remove_cdata              = $state->{remove_cdata};
  7389.       $keep_encoding             = $state->{keep_encoding};
  7390.       $expand_external_entities  = $state->{expand_external_entities};
  7391.       $output_filter             = $state->{output_filter};
  7392.       $output_text_filter        = $state->{output_text_filter};
  7393.       $keep_atts_order           = $state->{keep_atts_order};
  7394.       $do_not_escape_amp_in_atts = $state->{do_not_escape_amp_in_atts};
  7395.       $WRAP                      = $state->{wrap};
  7396.       $replaced_ents             = $state->{replaced_ents},
  7397.     }
  7398.  
  7399.   # sets global state to defaults
  7400.   sub init_global_state
  7401.     { set_global_state(
  7402.        { pretty                    => 0,
  7403.          quote                     => '"',
  7404.          indent                    => $INDENT,
  7405.          empty_tag_style           => 0,
  7406.          remove_cdata              => 0,
  7407.          keep_encoding             => 0,
  7408.          expand_external_entities  => 0,
  7409.          output_filter             => undef,
  7410.          output_text_filter        => undef,
  7411.          keep_atts_order           => undef,
  7412.          do_not_escape_amp_in_atts => 0,
  7413.          wrap                      => $WRAP,
  7414.          replaced_ents             => $REPLACED_ENTS,
  7415.         });
  7416.     }
  7417.  
  7418.  
  7419.   # set the pretty_print style (in $pretty) and returns the old one
  7420.   # can be called from outside the package with 2 arguments (elt, style)
  7421.   # or from inside with only one argument (style)
  7422.   # the style can be either a string (one of the keys of %pretty_print_style
  7423.   # or a number (presumably an old value saved)
  7424.   sub set_pretty_print
  7425.     { my $style= lc( defined $_[1] ? $_[1] : $_[0]); # so we cover both cases 
  7426.       my $old_pretty= $pretty;
  7427.       if( $style=~ /^\d+$/)
  7428.         { croak "invalid pretty print style $style" unless( $style < keys %pretty_print_style);
  7429.           $pretty= $style;
  7430.         }
  7431.       else
  7432.         { croak "invalid pretty print style '$style'" unless( exists $pretty_print_style{$style});
  7433.           $pretty= $pretty_print_style{$style};
  7434.         }
  7435.       if( ($pretty == $WRAPPED) || ($pretty == $INDENTEDA) )
  7436.         { XML::Twig::_use( 'Text::Wrap') or croak( "Text::Wrap not available, cannot use 'wrapped' style"); }
  7437.       return $old_pretty;
  7438.     }
  7439.  
  7440.   sub _pretty_print { return $pretty; } 
  7441.   
  7442.   # set the empty tag style (in $empty_tag_style) and returns the old one
  7443.   # can be called from outside the package with 2 arguments (elt, style)
  7444.   # or from inside with only one argument (style)
  7445.   # the style can be either a string (one of the keys of %empty_tag_style
  7446.   # or a number (presumably an old value saved)
  7447.   sub set_empty_tag_style
  7448.     { my $style= lc( defined $_[1] ? $_[1] : $_[0]); # so we cover both cases 
  7449.       my $old_style= $empty_tag_style;
  7450.       if( $style=~ /^\d+$/)
  7451.         { croak "invalid empty tag style $style"
  7452.         unless( $style < keys %empty_tag_style);
  7453.         $empty_tag_style= $style;
  7454.         }
  7455.       else
  7456.         { croak "invalid empty tag style '$style'"
  7457.             unless( exists $empty_tag_style{$style});
  7458.           $empty_tag_style= $empty_tag_style{$style};
  7459.         }
  7460.       return $old_style;
  7461.     }
  7462.  
  7463.   sub _pretty_print_styles
  7464.     { return (sort { $pretty_print_style{$a} <=> $pretty_print_style{$b} || $a cmp $b } keys %pretty_print_style); }
  7465.       
  7466.   sub set_quote
  7467.     { my $style= $_[1] || $_[0];
  7468.       my $old_quote= $quote;
  7469.       croak "invalid quote '$style'" unless( exists $quote_style{$style});
  7470.       $quote= $quote_style{$style};
  7471.       return $old_quote;
  7472.     }
  7473.     
  7474.   sub set_remove_cdata
  7475.     { my $new_value= defined $_[1] ? $_[1] : $_[0];
  7476.       my $old_value= $remove_cdata;
  7477.       $remove_cdata= $new_value;
  7478.       return $old_value;
  7479.     }
  7480.       
  7481.       
  7482.   sub set_indent
  7483.     { my $new_value= defined $_[1] ? $_[1] : $_[0];
  7484.       my $old_value= $INDENT;
  7485.       $INDENT= $new_value;
  7486.       return $old_value;
  7487.     }
  7488.  
  7489.   sub set_wrap
  7490.     { my $new_value= defined $_[1] ? $_[1] : $_[0];
  7491.       my $old_value= $WRAP;
  7492.       $WRAP= $new_value;
  7493.       return $old_value;
  7494.     }
  7495.        
  7496.        
  7497.   sub set_keep_encoding
  7498.     { my $new_value= defined $_[1] ? $_[1] : $_[0];
  7499.       my $old_value= $keep_encoding;
  7500.       $keep_encoding= $new_value;
  7501.       return $old_value;
  7502.    }
  7503.  
  7504.   sub set_replaced_ents
  7505.     { my $new_value= defined $_[1] ? $_[1] : $_[0];
  7506.       my $old_value= $replaced_ents;
  7507.       $replaced_ents= $new_value;
  7508.       return $old_value;
  7509.    }
  7510.  
  7511.   sub do_not_escape_gt
  7512.     { my $old_value= $replaced_ents;
  7513.       $replaced_ents= q{&<}; # & needs to be first
  7514.       return $old_value;
  7515.     }
  7516.  
  7517.   sub escape_gt
  7518.     { my $old_value= $replaced_ents;
  7519.       $replaced_ents= qq{&<>}; # & needs to be first
  7520.       return $old_value;
  7521.     }
  7522.  
  7523.   sub _keep_encoding { return $keep_encoding; } # so I can use elsewhere in the module
  7524.  
  7525.   sub set_do_not_escape_amp_in_atts
  7526.     { my $new_value= defined $_[1] ? $_[1] : $_[0];
  7527.       my $old_value= $do_not_escape_amp_in_atts;
  7528.       $do_not_escape_amp_in_atts= $new_value;
  7529.       return $old_value;
  7530.    }
  7531.  
  7532.   sub output_filter      { return $output_filter; }
  7533.   sub output_text_filter { return $output_text_filter; }
  7534.  
  7535.   sub set_output_filter
  7536.     { my $new_value= defined $_[1] ? $_[1] : $_[0]; # can be called in object/non-object mode
  7537.       # if called in object mode with no argument, the filter is undefined
  7538.       if( isa( $new_value, 'XML::Twig::Elt') || isa( $new_value, 'XML::Twig')) { undef $new_value; }
  7539.       my $old_value= $output_filter;
  7540.       if( !$new_value || isa( $new_value, 'CODE') )
  7541.         { $output_filter= $new_value; }
  7542.       elsif( $new_value eq 'latin1')
  7543.         { $output_filter= XML::Twig::latin1();
  7544.         }
  7545.       elsif( $XML::Twig::filter{$new_value})
  7546.         {  $output_filter= $XML::Twig::filter{$new_value}; }
  7547.       else
  7548.         { croak "invalid output filter '$new_value'"; }
  7549.       
  7550.       return $old_value;
  7551.     }
  7552.        
  7553.   sub set_output_text_filter
  7554.     { my $new_value= defined $_[1] ? $_[1] : $_[0]; # can be called in object/non-object mode
  7555.       # if called in object mode with no argument, the filter is undefined
  7556.       if( isa( $new_value, 'XML::Twig::Elt') || isa( $new_value, 'XML::Twig')) { undef $new_value; }
  7557.       my $old_value= $output_text_filter;
  7558.       if( !$new_value || isa( $new_value, 'CODE') )
  7559.         { $output_text_filter= $new_value; }
  7560.       elsif( $new_value eq 'latin1')
  7561.         { $output_text_filter= XML::Twig::latin1();
  7562.         }
  7563.       elsif( $XML::Twig::filter{$new_value})
  7564.         {  $output_text_filter= $XML::Twig::filter{$new_value}; }
  7565.       else
  7566.         { croak "invalid output text filter '$new_value'"; }
  7567.       
  7568.       return $old_value;
  7569.     }
  7570.        
  7571.   sub set_expand_external_entities
  7572.     { my $new_value= defined $_[1] ? $_[1] : $_[0];
  7573.       my $old_value= $expand_external_entities;
  7574.       $expand_external_entities= $new_value;
  7575.       return $old_value;
  7576.     }
  7577.        
  7578.   sub set_keep_atts_order
  7579.     { my $new_value= defined $_[1] ? $_[1] : $_[0];
  7580.       my $old_value= $keep_atts_order;
  7581.       $keep_atts_order= $new_value;
  7582.       return $old_value;
  7583.     
  7584.    }
  7585.  
  7586.   sub keep_atts_order { return $keep_atts_order; } # so I can use elsewhere in the module
  7587.  
  7588.   my %html_empty_elt;
  7589.   BEGIN { %html_empty_elt= map { $_ => 1} qw( base meta link hr br param img area input col); }
  7590.  
  7591.   sub start_tag
  7592.     { my( $elt, $option)= @_;
  7593.  
  7594.  
  7595.       return if( $elt->{gi} < $XML::Twig::SPECIAL_GI);
  7596.  
  7597.       my $extra_data= $elt->{extra_data} || '';
  7598.  
  7599.       my $gi= $XML::Twig::index2gi[$elt->{'gi'}];
  7600.       my $att= $elt->{att}; # should be $elt->{att}, optimized into a pure hash look-up
  7601.  
  7602.       my $ns_map= $att ? $att->{'#original_gi'} : '';
  7603.       if( $ns_map) { $gi= _restore_original_prefix( $ns_map, $gi); }
  7604.       $gi=~ s{^#default:}{}; # remove default prefix
  7605.  
  7606.       if( $output_text_filter) { $gi= $output_text_filter->( $gi); }
  7607.   
  7608.       # get the attribute and their values
  7609.       my $att_sep = $pretty==$NSGMLS    ? "\n"
  7610.                   : $pretty==$INDENTEDA ? "\n" . $INDENT x ($elt->level+1) . '  '
  7611.                   :                       ' '
  7612.                   ;
  7613.  
  7614.       my $replace_in_att_value= $replaced_ents . $quote;
  7615.       if( $option->{escape_gt} && $replaced_ents !~ m{>}) { $replace_in_att_value.= '>'; }
  7616.  
  7617.       my $tag;
  7618.       my @att_names= grep { !( $_=~ m{^#(?!default:)} ) } $keep_atts_order ?  keys %{$att} : sort keys %{$att};
  7619.       if( @att_names)
  7620.         { my $atts= join $att_sep, map  { my $output_att_name= $ns_map ? _restore_original_prefix( $ns_map, $_) : $_;
  7621.                                           if( $output_text_filter)
  7622.                                             { $output_att_name=  $output_text_filter->( $output_att_name); }
  7623.                                           $output_att_name . '=' . $quote . _att_xml_string( $att->{$_}, $replace_in_att_value) . $quote
  7624.  
  7625.                                         } 
  7626.                                         @att_names
  7627.                                    ;
  7628.            if( $pretty==$INDENTEDA && @att_names == 1) { $att_sep= ' '; }
  7629.            $tag= "<$gi$att_sep$atts";
  7630.         }
  7631.       else
  7632.         { $tag= "<$gi"; }
  7633.   
  7634.       $tag .= "\n" if($pretty==$NSGMLS);
  7635.  
  7636.  
  7637.       # force empty if suitable HTML tag, otherwise use the value from the input tree
  7638.       if( ($empty_tag_style eq $HTML) && !$elt->{first_child} && !$elt->{extra_data_before_end_tag} && $html_empty_elt{$gi})
  7639.         { $elt->{empty}= 1; }
  7640.  
  7641.       $tag .= (!$elt->{empty} || $elt->{extra_data_before_end_tag})  ? '>'            # element has content
  7642.             : (($empty_tag_style eq $HTML) && $html_empty_elt{$gi}) ? ' />'          # html empty element 
  7643.                                                                                      # cvs-friendly format
  7644.             : ( $pretty == $INDENTEDA && @att_names > 1)            ? "\n" .  $INDENT x $elt->level . "/>"  
  7645.             : ( $pretty == $INDENTEDA && @att_names == 1)           ? " />"  
  7646.             : $empty_tag_style                                      ? "></" . $XML::Twig::index2gi[$elt->{'gi'}] . ">" # $empty_tag_style is $HTML or $EXPAND
  7647.             :                                                         '/>'
  7648.             ;
  7649.  
  7650.       if( ( (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 1) eq '#') && (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 9) ne '#default:') )) { $tag= ''; }
  7651.  
  7652. #warn "TRACE: ", $tag,": ", Encode::is_utf8( $tag) ? "has flag" : "FLAG NOT SET";
  7653.  
  7654.       unless( $pretty) { return defined( $extra_data) ? $extra_data . $tag : $tag;  }
  7655.  
  7656.       my $prefix='';
  7657.       my $return='';   # '' or \n is to be printed before the tag
  7658.       my $indent=0;    # number of indents before the tag
  7659.  
  7660.       if( $pretty==$RECORD1)
  7661.         { my $level= $elt->level;
  7662.           $return= "\n" if( $level < 2);
  7663.           $indent= 1 if( $level == 1);
  7664.         }
  7665.  
  7666.      elsif( $pretty==$RECORD2)
  7667.         { $return= "\n";
  7668.           $indent= $elt->level;
  7669.         }
  7670.  
  7671.       elsif( $pretty==$NICE)
  7672.         { my $parent= $elt->{parent};
  7673.           unless( !$parent || $parent->{contains_text}) 
  7674.             { $return= "\n"; }
  7675.           $elt->{contains_text}= 1 if( ($parent && $parent->{contains_text})
  7676.                                      || $elt->contains_text);
  7677.         }
  7678.  
  7679.       elsif( $KEEP_TEXT_TAG_ON_ONE_LINE{$pretty})
  7680.         { my $parent= $elt->{parent};
  7681.           unless( !$parent || $parent->{contains_text}) 
  7682.             { $return= "\n"; 
  7683.               $indent= $elt->level; 
  7684.             }
  7685.           $elt->{contains_text}= 1 if( ($parent && $parent->{contains_text})
  7686.                                      || $elt->contains_text);
  7687.         }
  7688.  
  7689.       if( $return || $indent)
  7690.         { # check for elements in which spaces should be kept
  7691.           my $t= $elt->twig;
  7692.           return $extra_data . $tag if( $xml_space_preserve);
  7693.           if( $t && $t->{twig_keep_spaces_in})
  7694.             { foreach my $ancestor ($elt->ancestors)
  7695.                 { return $extra_data . $tag if( $t->{twig_keep_spaces_in}->{$XML::Twig::index2gi[$ancestor->{'gi'}]}) }
  7696.             }
  7697.         
  7698.           $prefix= $INDENT x $indent;
  7699.           if( $extra_data)
  7700.             { $extra_data=~ s{\s+$}{};
  7701.               $extra_data=~ s{^\s+}{};
  7702.               $extra_data= $prefix .  $extra_data . $return;
  7703.             }
  7704.         }
  7705.  
  7706.  
  7707.       return $return . $extra_data . $prefix . $tag;
  7708.     }
  7709.   
  7710.   sub end_tag
  7711.     { my $elt= shift;
  7712.       return  '' if(    ($elt->{gi}<$XML::Twig::SPECIAL_GI) 
  7713.                      || ($elt->{'empty'} && !$elt->{extra_data_before_end_tag})
  7714.                    );
  7715.       my $tag= "<";
  7716.       my $gi= $XML::Twig::index2gi[$elt->{'gi'}];
  7717.  
  7718.       if( my $map= $elt->{'att'}->{'#original_gi'}) { $gi= _restore_original_prefix( $map, $gi); }
  7719.       $gi=~ s{^#default:}{}; # remove default prefix
  7720.  
  7721.       if( $output_text_filter) { $gi= $output_text_filter->( $XML::Twig::index2gi[$elt->{'gi'}]); } 
  7722.       $tag .=  "/$gi>";
  7723.  
  7724.       $tag = ($elt->{extra_data_before_end_tag} || '') . $tag;
  7725.  
  7726.       if( ( (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 1) eq '#') && (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 9) ne '#default:') )) { $tag= ''; }
  7727.  
  7728.       return $tag unless $pretty;
  7729.  
  7730.       my $prefix='';
  7731.       my $return=0;    # 1 if a \n is to be printed before the tag
  7732.       my $indent=0;    # number of indents before the tag
  7733.  
  7734.       if( $pretty==$RECORD1)
  7735.         { $return= 1 if( $elt->level == 0);
  7736.         }
  7737.  
  7738.      elsif( $pretty==$RECORD2)
  7739.         { unless( $elt->contains_text)
  7740.             { $return= 1 ;
  7741.               $indent= $elt->level;
  7742.             }
  7743.         }
  7744.  
  7745.       elsif( $pretty==$NICE)
  7746.         { my $parent= $elt->{parent};
  7747.           if( (    ($parent && !$parent->{contains_text}) || !$parent )
  7748.             && ( !$elt->{contains_text}  
  7749.              && ($elt->{has_flushed_child} || $elt->{first_child})           
  7750.            )
  7751.          )
  7752.             { $return= 1; }
  7753.         }
  7754.  
  7755.       elsif( $KEEP_TEXT_TAG_ON_ONE_LINE{$pretty})
  7756.         { my $parent= $elt->{parent};
  7757.           if( (    ($parent && !$parent->{contains_text}) || !$parent )
  7758.             && ( !$elt->{contains_text}  
  7759.              && ($elt->{has_flushed_child} || $elt->{first_child})           
  7760.            )
  7761.          )
  7762.             { $return= 1; 
  7763.               $indent= $elt->level; 
  7764.             }
  7765.         }
  7766.  
  7767.       if( $return || $indent)
  7768.         { # check for elements in which spaces should be kept
  7769.           my $t= $elt->twig;
  7770.           return $tag if( $xml_space_preserve);
  7771.           if( $t && $t->{twig_keep_spaces_in})
  7772.             { foreach my $ancestor ($elt, $elt->ancestors)
  7773.                 { return $tag if( $t->{twig_keep_spaces_in}->{$XML::Twig::index2gi[$ancestor->{'gi'}]}) }
  7774.             }
  7775.       
  7776.           if( $return) { $prefix= ($pretty== $INDENTEDCT) ? "\n$INDENT" : "\n"; }
  7777.           $prefix.= $INDENT x $indent;
  7778.     }
  7779.  
  7780.       # add a \n at the end of the document (after the root element)
  7781.       $tag .= "\n" unless( $elt->{parent});
  7782.   
  7783.       return $prefix . $tag;
  7784.     }
  7785.  
  7786.   sub _restore_original_prefix
  7787.     { my( $map, $name)= @_;
  7788.       my $prefix= _ns_prefix( $name);
  7789.       if( my $original_prefix= $map->{$prefix})
  7790.         { if( $original_prefix eq '#default')
  7791.             { $name=~ s{^$prefix:}{}; }
  7792.           else
  7793.             { $name=~ s{^$prefix(?=:)}{$original_prefix}; }
  7794.         }
  7795.       return $name;
  7796.     }
  7797.  
  7798.   # buffer used to hold the text to print/sprint, to avoid passing it back and forth between methods
  7799.   my @sprint;
  7800.  
  7801.   # $elt is an element to print
  7802.   # $fh is an optional filehandle to print to
  7803.   # $pretty is an optional value, if true a \n is printed after the < of the
  7804.   # opening tag
  7805.   sub print
  7806.     { my $elt= shift;
  7807.  
  7808.       my $pretty;
  7809.       my $fh= isa( $_[0], 'GLOB') || isa( $_[0], 'IO::Scalar') ? shift : undef;
  7810.       my $old_select= defined $fh ? select $fh : undef;
  7811.       my $old_pretty= defined ($pretty= shift) ? set_pretty_print( $pretty) : undef;
  7812.       $pretty ||=0;
  7813.       $pretty = $pretty_print_style{$pretty} || $pretty;
  7814.  
  7815.       $xml_space_preserve= ( ($elt->inherit_att( 'xml:space') || '') eq 'preserve');
  7816.  
  7817.       #$elt->_print;       # no need to duplicate logic in _sprint
  7818.       #print $elt->sprint; # but that's too slow
  7819.  
  7820.       @sprint=();
  7821.       $elt->_sprint(@_);
  7822.       if( $output_filter || ((($pretty== $WRAPPED) || ($pretty==$INDENTEDC)) && !$xml_space_preserve))
  7823.         { my $sprint= join( '', @sprint);
  7824.           if( $output_filter) { $sprint= $output_filter->( $sprint); }
  7825.  
  7826.           if( ( ($pretty== $WRAPPED) || ($pretty==$INDENTEDC)) && !$xml_space_preserve)
  7827.             { $sprint= _wrap_text( $sprint); }
  7828.           print  $sprint;
  7829.         }
  7830.       else
  7831.         { 
  7832. #foreach my $s (@sprint) { print "TRACE s $s: ", Encode::is_utf8( $s) ? "has flag\n" : "FLAG NOT SET\n"; }
  7833.           print @sprint; 
  7834. #warn "FATTO";
  7835.         }
  7836.  
  7837.       $xml_space_preserve= 0;
  7838.     
  7839.       select $old_select if( defined $old_select);
  7840.       set_pretty_print( $old_pretty) if( defined $old_pretty);
  7841.     }
  7842.       
  7843.   
  7844.   # same as print but does not output the start tag if the element
  7845.   # is marked as flushed
  7846.   sub flush 
  7847.     { my $elt= shift; 
  7848.       my $up_to= $_[0] && isa( $_[0], 'XML::Twig::Elt') ? shift : $elt;
  7849.       $elt->twig->flush_up_to( $up_to, @_); 
  7850.     }
  7851.   sub purge
  7852.     { my $elt= shift; 
  7853.       my $up_to= $_[0] && isa( $_[0], 'XML::Twig::Elt') ? shift : $elt;
  7854.       $elt->twig->purge_up_to( $up_to, @_); 
  7855.     }
  7856.   
  7857.   sub _flush
  7858.     { my $elt= shift;
  7859.   
  7860.       my $pretty;
  7861.       my $fh=  isa( $_[0], 'GLOB') || isa( $_[0], 'IO::Scalar') ? shift : undef;
  7862.       my $old_select= defined $fh ? select $fh : undef;
  7863.       my $old_pretty= defined ($pretty= shift) ? set_pretty_print( $pretty) : undef;
  7864.  
  7865.       $xml_space_preserve= 1 if( ($elt->inherit_att( 'xml:space') || '') eq 'preserve');
  7866.  
  7867.       $elt->__flush();
  7868.  
  7869.       $xml_space_preserve= 0;
  7870.  
  7871.       select $old_select if( defined $old_select);
  7872.       set_pretty_print( $old_pretty) if( defined $old_pretty);
  7873.     }
  7874.  
  7875.   sub __flush
  7876.     { my $elt= shift;
  7877.   
  7878.       if( $elt->{gi} >= $XML::Twig::SPECIAL_GI)
  7879.         { my $preserve= ($elt->{'att'}->{'xml:space'} || '') eq 'preserve';
  7880.           $xml_space_preserve++ if $preserve;
  7881.           unless( $elt->_flushed)
  7882.             { print $elt->start_tag();
  7883.             }
  7884.       
  7885.           # flush the children
  7886.           my @children= do { my $elt= $elt; my @children=(); my $child= $elt->{first_child}; while( $child) { push @children, $child; $child= $child->{next_sibling}; } @children; };
  7887.           foreach my $child (@children)
  7888.             { $child->_flush( $pretty); }
  7889.           unless( $elt->{end_tag_flushed}) { print $elt->end_tag; }
  7890.           $xml_space_preserve-- if $preserve;
  7891.           # used for pretty printing
  7892.           if( my $parent= $elt->{parent}) { $parent->{has_flushed_child}= 1; }
  7893.         }
  7894.       else # text or special element
  7895.         { my $text;
  7896.           if( (exists $elt->{'pcdata'}))     { $text= $elt->pcdata_xml_string; 
  7897.                                      if( my $parent= $elt->{parent}) 
  7898.                                        { $parent->{contains_text}= 1; }
  7899.                                    }
  7900.           elsif( (exists $elt->{'cdata'}))   { $text= $elt->cdata_string;        
  7901.                                      if( my $parent= $elt->{parent}) 
  7902.                                        { $parent->{contains_text}= 1; }
  7903.                                    }
  7904.           elsif( (exists $elt->{'target'}))      { $text= $elt->pi_string;          }
  7905.           elsif( (exists $elt->{'comment'})) { $text= $elt->comment_string;     }
  7906.           elsif( (exists $elt->{'ent'}))     { $text= $elt->ent_string;         }
  7907.  
  7908.           print $output_filter ? $output_filter->( $text) : $text;
  7909.         }
  7910.     }
  7911.   
  7912.  
  7913.   sub xml_text
  7914.     { my( $elt, @options)= @_;
  7915.  
  7916.       if( @options && grep { lc( $_) eq 'no_recurse' } @options) { return $elt->xml_text_only; }
  7917.  
  7918.       my $string='';
  7919.  
  7920.       if( ($elt->{gi} >= $XML::Twig::SPECIAL_GI) )
  7921.         { # sprint the children
  7922.           my $child= $elt->{first_child} || '';
  7923.           while( $child)
  7924.             { $string.= $child->xml_text;
  7925.             } continue { $child= $child->{next_sibling}; }
  7926.         }
  7927.       elsif( (exists $elt->{'pcdata'}))  { $string .= $output_filter ?  $output_filter->($elt->pcdata_xml_string) 
  7928.                                                            : $elt->pcdata_xml_string; 
  7929.                                }
  7930.       elsif( (exists $elt->{'cdata'}))   { $string .= $output_filter ?  $output_filter->($elt->cdata_string)  
  7931.                                                            : $elt->cdata_string;      
  7932.                                }
  7933.       elsif( (exists $elt->{'ent'}))     { $string .= $elt->ent_string; }
  7934.  
  7935.       return $string;
  7936.     }
  7937.  
  7938.   sub xml_text_only
  7939.     { return join '', map { $_->xml_text if( $_->is_text || (exists $_->{'ent'})) } $_[0]->_children; }
  7940.  
  7941.   # same as print but except... it does not print but rather returns the string
  7942.   # if the second parameter is set then only the content is returned, not the
  7943.   # start and end tags of the element (but the tags of the included elements are
  7944.   # returned)
  7945.  
  7946.   sub sprint
  7947.     { my $elt= shift;
  7948.       my( $old_pretty, $old_empty_tag_style);
  7949.  
  7950.       if( $_[0] && isa( $_[0], 'HASH'))
  7951.         { my %args= XML::Twig::_normalize_args( %{shift()}); 
  7952.           if( defined $args{PrettyPrint}) { $old_pretty          = set_pretty_print( $args{PrettyPrint});  }
  7953.            if( defined $args{EmptyTags})  { $old_empty_tag_style = set_empty_tag_style( $args{EmptyTags}); }
  7954.         }
  7955.  
  7956.       $xml_space_preserve= 1 if( ($elt->inherit_att( 'xml:space') || '') eq 'preserve');
  7957.  
  7958.       @sprint=();
  7959.       $elt->_sprint( @_);
  7960.       my $sprint= join( '', @sprint);
  7961.       if( $output_filter) { $sprint= $output_filter->( $sprint); }
  7962.  
  7963.       if( ( ($pretty== $WRAPPED) || ($pretty==$INDENTEDC)) && !$xml_space_preserve)
  7964.         { $sprint= _wrap_text( $sprint); }
  7965.       $xml_space_preserve= 0;
  7966.  
  7967.  
  7968.       if( defined $old_pretty)          { set_pretty_print( $old_pretty);             } 
  7969.       if( defined $old_empty_tag_style) { set_empty_tag_style( $old_empty_tag_style); }
  7970.  
  7971.       return $sprint;
  7972.     }
  7973.   
  7974.   sub _wrap_text
  7975.     { my( $string)= @_;
  7976.       my $wrapped;
  7977.       foreach my $line (split /\n/, $string)
  7978.         { my( $initial_indent)= $line=~ m{^(\s*)};
  7979.           my $wrapped_line= Text::Wrap::wrap(  '',  $initial_indent . $INDENT, $line) . "\n";
  7980.           
  7981.           # fix glitch with Text::wrap when the first line is long and does not include spaces
  7982.           # the first line ends up being too short by 2 chars, but we'll have to live with it!
  7983.           $wrapped_line=~ s{^ +\n  }{}s; # this prefix needs to be removed
  7984.       
  7985.           $wrapped .= $wrapped_line;
  7986.         }
  7987.      
  7988.       return $wrapped;
  7989.     }
  7990.       
  7991.   
  7992.   sub _sprint
  7993.     { my $elt= shift;
  7994.       my $no_tag= shift || 0;
  7995.       # in case there's some comments or PI's piggybacking
  7996.  
  7997.       if( $elt->{gi} >= $XML::Twig::SPECIAL_GI)
  7998.         {
  7999.           my $preserve= ($elt->{'att'}->{'xml:space'} || '') eq 'preserve';
  8000.           $xml_space_preserve++ if $preserve;
  8001.  
  8002.           push @sprint, $elt->start_tag unless( $no_tag);
  8003.       
  8004.           # sprint the children
  8005.           my $child= $elt->{first_child};
  8006.           while( $child)
  8007.             { $child->_sprint;
  8008.               $child= $child->{next_sibling};
  8009.             }
  8010.           push @sprint, $elt->end_tag unless( $no_tag);
  8011.           $xml_space_preserve-- if $preserve;
  8012.         }
  8013.       else
  8014.         { push @sprint, $elt->{extra_data} if( $elt->{extra_data}) ;
  8015.           if(    (exists $elt->{'pcdata'}))  { push @sprint, $elt->pcdata_xml_string; }
  8016.           elsif( (exists $elt->{'cdata'}))   { push @sprint, $elt->cdata_string;      }
  8017.           elsif( (exists $elt->{'target'}))      { push @sprint, $elt->pi_string;
  8018.                                      if( ($pretty >= $INDENTED) && !$elt->{parent}->{contains_text}) { push @sprint, "\n"; }
  8019.                                    }
  8020.           elsif( (exists $elt->{'comment'})) { push @sprint, $elt->comment_string;    
  8021.                                      if( ($pretty >= $INDENTED) && !$elt->{parent}->{contains_text}) { push @sprint, "\n"; }
  8022.                                    }
  8023.           elsif( (exists $elt->{'ent'}))     { push @sprint, $elt->ent_string;        }
  8024.         }
  8025.  
  8026.       return;
  8027.     }
  8028.  
  8029.   # just a shortcut to $elt->sprint( 1)
  8030.   sub xml_string
  8031.     { my $elt= shift;
  8032.       isa( $_[0], 'HASH') ?  $elt->sprint( shift(), 1) : $elt->sprint( 1);
  8033.     }
  8034.  
  8035.   sub pcdata_xml_string 
  8036.     { my $elt= shift;
  8037.       if( defined( my $string= $elt->{pcdata}) )
  8038.         { 
  8039.           if( ! $elt->{extra_data_in_pcdata})
  8040.             { 
  8041.               $string=~ s/([$replaced_ents])/$XML::Twig::base_ent{$1}/g unless( $keep_encoding || $elt->{asis});  
  8042.               $string=~ s{\Q]]>}{]]>}g;
  8043.             }
  8044.           else
  8045.             { _gen_mark( $string); # used by _(un)?protect_extra_data
  8046.               foreach my $data (reverse @{$elt->{extra_data_in_pcdata}})
  8047.                 { my $substr= substr( $string, $data->{offset});
  8048.                   if( $keep_encoding || $elt->{asis})
  8049.                     { substr( $string, $data->{offset}, 0, $data->{text}); }
  8050.                   else
  8051.                     { substr( $string, $data->{offset}, 0, _protect_extra_data( $data->{text})); }
  8052.                 }
  8053.               unless( $keep_encoding || $elt->{asis})
  8054.                 { 
  8055.                   $string=~ s{([$replaced_ents])}{$XML::Twig::base_ent{$1}}g ;
  8056.                   $string=~ s{\Q]]>}{]]>}g;
  8057.                   _unprotect_extra_data( $string);
  8058.                 }
  8059.             }
  8060.           return $output_text_filter ? $output_text_filter->( $string) : $string;
  8061.         }
  8062.       else
  8063.         { return ''; }
  8064.     }
  8065.  
  8066.   { my $mark;
  8067.     my( %char2ent, %ent2char);
  8068.     BEGIN
  8069.       { %char2ent= ( '<' => 'lt', '&' => 'amp', '>' => 'gt');
  8070.         %ent2char= map { $char2ent{$_} => $_ } keys %char2ent;
  8071.       }
  8072.  
  8073.     # generate a unique mark (a string) not found in the string, 
  8074.     # used to mark < and & in the extra data
  8075.     sub _gen_mark
  8076.       { $mark="AAAA";
  8077.         $mark++ while( index( $_[0], $mark) > -1);
  8078.         return $mark;
  8079.       }
  8080.       
  8081.     sub _protect_extra_data
  8082.       { my( $extra_data)= @_;
  8083.         $extra_data=~ s{([<&>])}{:$mark:$char2ent{$1}:}g;
  8084.         return $extra_data;
  8085.       }
  8086.  
  8087.     sub _unprotect_extra_data
  8088.       { $_[0]=~ s{:$mark:(\w+):}{$ent2char{$1}}g; }
  8089.  
  8090.   } 
  8091.   
  8092.   sub cdata_string
  8093.     { my $cdata= $_[0]->{cdata};
  8094.       unless( defined $cdata) { return ''; }
  8095.       if( $remove_cdata)
  8096.         { $cdata=~ s/([$replaced_ents])/$XML::Twig::base_ent{$1}/g; }
  8097.       else
  8098.         { $cdata= $CDATA_START . $cdata . $CDATA_END; }
  8099.       return $cdata;
  8100.    }
  8101.  
  8102.   sub att_xml_string 
  8103.     { my $elt= shift;
  8104.       my $att= shift;
  8105.  
  8106.       my $replace= $replaced_ents . $quote;
  8107.       if($_[0] && $_[0]->{escape_gt} && ($replace!~ m{>}) ) { $replace .='>'; }
  8108.  
  8109.       if( defined (my $string= $elt->{att}->{$att}))
  8110.         { return _att_xml_string( $string, $replace); }
  8111.       else
  8112.         { return ''; }
  8113.     }
  8114.     
  8115.   # escaped xml string for an attribute value
  8116.   sub _att_xml_string 
  8117.     { my( $string, $escape)= @_;
  8118.       if( !defined( $string)) { return ''; }
  8119.       unless( $keep_encoding)
  8120.         { 
  8121.           if( $do_not_escape_amp_in_atts)
  8122.             { $escape=~ s{^.}{}; # seems like the most backward compatible way to remove & from the list
  8123.               $string=~ s{([$escape])}{$XML::Twig::base_ent{$1}}g; 
  8124.               $string=~ s{&(?!(\w+|#\d+|[xX][0-9a-fA-F]+);)}{&}g; # dodgy: escape & that do not start an entity
  8125.             }
  8126.           else
  8127.             { $string=~ s{([$escape])}{$XML::Twig::base_ent{$1}}g; 
  8128.               $string=~ s{\Q]]>}{]]>}g;
  8129.             }
  8130.         }
  8131.  
  8132.       return $output_text_filter ? $output_text_filter->( $string) : $string;
  8133.     }
  8134.  
  8135.   sub ent_string 
  8136.     { my $ent= shift;
  8137.       my $ent_text= $ent->{ent};
  8138.       my( $t, $el, $ent_string);
  8139.       if(    $expand_external_entities
  8140.           && ($t= $ent->twig) 
  8141.           && ($el= $t->entity_list)
  8142.           && ($ent_string= $el->{entities}->{$ent->ent_name}->{val})
  8143.         )
  8144.         { return $ent_string; }
  8145.        else
  8146.          { return $ent_text;  }
  8147.     }
  8148.  
  8149.   # returns just the text, no tags, for an element
  8150.   sub text
  8151.     { my( $elt, @options)= @_;
  8152.  
  8153.       if( @options && grep { lc( $_) eq 'no_recurse' } @options) { return $elt->text_only; }
  8154.  
  8155.       my $string;
  8156.   
  8157.       if( (exists $elt->{'pcdata'}))     { return  $elt->{pcdata};   }
  8158.       elsif( (exists $elt->{'cdata'}))   { return  $elt->{cdata};    }
  8159.       elsif( (exists $elt->{'target'}))      { return  $elt->pi_string;}
  8160.       elsif( (exists $elt->{'comment'})) { return  $elt->{comment};  }
  8161.       elsif( (exists $elt->{'ent'}))     { return  $elt->{ent} ;     }
  8162.   
  8163.       my $child= $elt->{first_child} ||'';
  8164.       while( $child)
  8165.         {
  8166.           my $child_text= $child->text;
  8167.           $string.= defined( $child_text) ? $child_text : '';
  8168.         } continue { $child= $child->{next_sibling}; }
  8169.  
  8170.       unless( defined $string) { $string=''; }
  8171.  
  8172.       return $output_text_filter ? $output_text_filter->( $string) : $string;
  8173.     }
  8174.  
  8175.   sub text_only
  8176.     { return join '', map { $_->text if( $_->is_text || (exists $_->{'ent'})) } $_[0]->_children; }
  8177.  
  8178.   sub trimmed_text
  8179.     { my $elt= shift;
  8180.       my $text= $elt->text( @_);
  8181.       $text=~ s{\s+}{ }sg;
  8182.       $text=~ s{^\s*}{};
  8183.       $text=~ s{\s*$}{};
  8184.       return $text;
  8185.     }
  8186.  
  8187.   sub trim
  8188.     { my( $elt)= @_;
  8189.       my $pcdata= $elt->first_descendant( $TEXT);
  8190.       (my $pcdata_text= $pcdata->text)=~ s{^\s+}{}s;
  8191.       $pcdata->set_text( $pcdata_text);
  8192.       $pcdata= $elt->last_descendant( $TEXT);
  8193.       ($pcdata_text= $pcdata->text)=~ s{\s+$}{};
  8194.       $pcdata->set_text( $pcdata_text);
  8195.       foreach my $pcdata ($elt->descendants( $TEXT))
  8196.         { ($pcdata_text= $pcdata->text)=~ s{\s+}{ }g;
  8197.           $pcdata->set_text( $pcdata_text);
  8198.         }
  8199.       return $elt;
  8200.     }
  8201.   
  8202.  
  8203.   # remove cdata sections (turns them into regular pcdata) in an element 
  8204.   sub remove_cdata 
  8205.     { my $elt= shift;
  8206.       foreach my $cdata ($elt->descendants_or_self( $CDATA))
  8207.         { if( $keep_encoding)
  8208.             { my $data= $cdata->{cdata};
  8209.               $data=~ s{([&<"'])}{$XML::Twig::base_ent{$1}}g;
  8210.               $cdata->set_pcdata( $data);
  8211.             }
  8212.           else
  8213.             { $cdata->set_pcdata( $cdata->{cdata}); }
  8214.           $cdata->{gi}=$XML::Twig::gi2index{$PCDATA} or $cdata->set_gi( $PCDATA);
  8215.           undef $cdata->{cdata};
  8216.         }
  8217.     }
  8218.  
  8219. sub _is_private      { return _is_private_name( $_[0]->gi); }
  8220. sub _is_private_name { return $_[0]=~ m{^#(?!default:)};                }
  8221.  
  8222.  
  8223. } # end of block containing package globals ($pretty_print, $quotes, keep_encoding...)
  8224.  
  8225. sub normalize
  8226.   { my( $elt)= @_;
  8227.     my @descendants= $elt->descendants( $PCDATA);
  8228.     while( my $desc= shift @descendants)
  8229.       { while( @descendants && $desc->{next_sibling} && $desc->{next_sibling}== $descendants[0])
  8230.           { my $to_merge= shift @descendants;
  8231.             $desc->{pcdata}.= $to_merge->{pcdata};
  8232.             $to_merge->delete;
  8233.           }
  8234.       }
  8235.     return $elt;
  8236.   }
  8237.  
  8238. # SAX export methods
  8239. sub toSAX1
  8240.   { _toSAX(@_, \&_start_tag_data_SAX1, \&_end_tag_data_SAX1); }
  8241.  
  8242. sub toSAX2
  8243.   { _toSAX(@_, \&_start_tag_data_SAX2, \&_end_tag_data_SAX2); }
  8244.  
  8245. sub _toSAX
  8246.   { my( $elt, $handler, $start_tag_data, $end_tag_data)= @_;
  8247.     if( $elt->{gi} >= $XML::Twig::SPECIAL_GI)
  8248.       { my $data= $start_tag_data->( $elt);
  8249.         _start_prefix_mapping( $elt, $handler, $data);
  8250.         if( $data && (my $start_element = $handler->can( 'start_element')))
  8251.           { unless( $elt->_flushed) { $start_element->( $handler, $data); } }
  8252.       
  8253.         foreach my $child ($elt->_children)
  8254.           { $child->_toSAX( $handler, $start_tag_data, $end_tag_data); }
  8255.  
  8256.         if( (my $data= $end_tag_data->( $elt)) && (my $end_element = $handler->can( 'end_element')) )
  8257.           { $end_element->( $handler, $data); }
  8258.         _end_prefix_mapping( $elt, $handler);
  8259.       }
  8260.     else # text or special element
  8261.       { if( (exists $elt->{'pcdata'}) && (my $characters= $handler->can( 'characters')))
  8262.           { $characters->( $handler, { Data => $elt->{pcdata} });  }
  8263.         elsif( (exists $elt->{'cdata'}))  
  8264.           { if( my $start_cdata= $handler->can( 'start_cdata'))
  8265.               { $start_cdata->( $handler); }
  8266.             if( my $characters= $handler->can( 'characters'))
  8267.               { $characters->( $handler, {Data => $elt->{cdata} });  }
  8268.             if( my $end_cdata= $handler->can( 'end_cdata'))
  8269.               { $end_cdata->( $handler); }
  8270.           }
  8271.         elsif( ((exists $elt->{'target'}))  && (my $pi= $handler->can( 'processing_instruction')))
  8272.           { $pi->( $handler, { Target =>$elt->{target}, Data => $elt->{data} });  }
  8273.         elsif( ((exists $elt->{'comment'}))  && (my $comment= $handler->can( 'comment')))
  8274.           { $comment->( $handler, { Data => $elt->{comment} });  }
  8275.         elsif( ((exists $elt->{'ent'})))
  8276.           { 
  8277.             if( my $se=   $handler->can( 'skipped_entity'))
  8278.               { $se->( $handler, { Name => $elt->ent_name });  }
  8279.             elsif( my $characters= $handler->can( 'characters'))
  8280.               { if( defined $elt->ent_string)
  8281.                   { $characters->( $handler, {Data => $elt->ent_string});  }
  8282.                 else
  8283.                   { $characters->( $handler, {Data => $elt->ent_name});  }
  8284.               }
  8285.           }
  8286.       
  8287.       }
  8288.   }
  8289.   
  8290. sub _start_tag_data_SAX1
  8291.   { my( $elt)= @_;
  8292.     my $name= $XML::Twig::index2gi[$elt->{'gi'}];
  8293.     return if( ( (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 1) eq '#') && (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 9) ne '#default:') ));
  8294.     my $attributes={};
  8295.     my $atts= $elt->{att};
  8296.     while( my( $att, $value)= each %$atts)
  8297.       { $attributes->{$att}= $value unless( ( $att=~ m{^#(?!default:)} )); }
  8298.     my $data= { Name => $name, Attributes => $attributes};
  8299.     return $data;
  8300.   }
  8301.  
  8302. sub _end_tag_data_SAX1
  8303.   { my( $elt)= @_;
  8304.     return if( ( (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 1) eq '#') && (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 9) ne '#default:') ));
  8305.     return  { Name => $XML::Twig::index2gi[$elt->{'gi'}] };
  8306.   } 
  8307.   
  8308. sub _start_tag_data_SAX2
  8309.   { my( $elt)= @_;
  8310.     my $data={};
  8311.     
  8312.     my $name= $XML::Twig::index2gi[$elt->{'gi'}];
  8313.     return if( ( (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 1) eq '#') && (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 9) ne '#default:') ));
  8314.     $data->{Name}         = $name;
  8315.     $data->{Prefix}       = $elt->ns_prefix; 
  8316.     $data->{LocalName}    = $elt->local_name;
  8317.     $data->{NamespaceURI} = $elt->namespace;
  8318.  
  8319.     # save a copy of the data so we can re-use it for the end tag
  8320.     my %sax2_data= %$data;
  8321.     $elt->{twig_elt_SAX2_data}= \%sax2_data;
  8322.    
  8323.     # add the attributes
  8324.     $data->{Attributes}= $elt->_atts_to_SAX2;
  8325.  
  8326.     return $data;
  8327.   }
  8328.  
  8329. sub _atts_to_SAX2
  8330.   { my $elt= shift;
  8331.     my $SAX2_atts= {};
  8332.     foreach my $att (keys %{$elt->{att}})
  8333.       { 
  8334.         next if( ( $att=~ m{^#(?!default:)} ));
  8335.         my $SAX2_att={};
  8336.         $SAX2_att->{Name}         = $att;
  8337.         $SAX2_att->{Prefix}       = _ns_prefix( $att); 
  8338.         $SAX2_att->{LocalName}    = _local_name( $att);
  8339.         $SAX2_att->{NamespaceURI} = $elt->namespace( $SAX2_att->{Prefix});
  8340.         $SAX2_att->{Value}        = $elt->{'att'}->{$att};
  8341.         my $SAX2_att_name= "{$SAX2_att->{NamespaceURI}}$SAX2_att->{LocalName}";
  8342.  
  8343.         $SAX2_atts->{$SAX2_att_name}= $SAX2_att;
  8344.       }
  8345.     return $SAX2_atts;
  8346.   }
  8347.  
  8348. sub _start_prefix_mapping
  8349.   { my( $elt, $handler, $data)= @_;
  8350.     if( my $start_prefix_mapping= $handler->can( 'start_prefix_mapping')
  8351.         and my @new_prefix_mappings= grep { /^\{[^}]*\}xmlns/ || /^\{$XMLNS_URI\}/ } keys %{$data->{Attributes}}
  8352.       )
  8353.       { foreach my $prefix (@new_prefix_mappings)
  8354.           { my $prefix_string= $data->{Attributes}->{$prefix}->{LocalName};
  8355.             if( $prefix_string eq 'xmlns') { $prefix_string=''; }
  8356.             my $prefix_data=
  8357.               {  Prefix       => $prefix_string,
  8358.                  NamespaceURI => $data->{Attributes}->{$prefix}->{Value}
  8359.               };
  8360.             $start_prefix_mapping->( $handler, $prefix_data);
  8361.             $elt->{twig_end_prefix_mapping} ||= [];
  8362.             push @{$elt->{twig_end_prefix_mapping}}, $prefix_string;
  8363.           }
  8364.       }
  8365.   }
  8366.  
  8367. sub _end_prefix_mapping
  8368.   { my( $elt, $handler)= @_;
  8369.     if( my $end_prefix_mapping= $handler->can( 'end_prefix_mapping'))
  8370.       { foreach my $prefix (@{$elt->{twig_end_prefix_mapping}})
  8371.           { $end_prefix_mapping->( $handler, { Prefix => $prefix} ); }
  8372.       }
  8373.   }
  8374.              
  8375. sub _end_tag_data_SAX2
  8376.   { my( $elt)= @_;
  8377.     return if( ( (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 1) eq '#') && (substr( $XML::Twig::index2gi[$elt->{'gi'}], 0, 9) ne '#default:') ));
  8378.     return $elt->{twig_elt_SAX2_data};
  8379.   } 
  8380.  
  8381. sub contains_text
  8382.   { my $elt= shift;
  8383.     my $child= $elt->{first_child};
  8384.     while ($child)
  8385.       { return 1 if( $child->is_text || (exists $child->{'ent'})); 
  8386.         $child= $child->{next_sibling};
  8387.       }
  8388.     return 0;
  8389.   }
  8390.  
  8391. # creates a single pcdata element containing the text as child of the element
  8392. # options: 
  8393. #   - force_pcdata: when set to a true value forces the text to be in a #PCDATA
  8394. #                   even if the original element was a #CDATA
  8395. sub set_text
  8396.   { my( $elt, $string, %option)= @_;
  8397.  
  8398.     if( $XML::Twig::index2gi[$elt->{'gi'}] eq $PCDATA) 
  8399.       { return $elt->set_pcdata( $string); }
  8400.     elsif( $XML::Twig::index2gi[$elt->{'gi'}] eq $CDATA)  
  8401.       { if( $option{force_pcdata})
  8402.           { $elt->{gi}=$XML::Twig::gi2index{$PCDATA} or $elt->set_gi( $PCDATA);
  8403.             $elt->_set_cdata('');
  8404.             return $elt->set_pcdata( $string);
  8405.           }
  8406.         else
  8407.           { return $elt->_set_cdata( $string); }
  8408.       }
  8409.     elsif( $elt->contains_a_single( $PCDATA) )
  8410.       { # optimized so we have a slight chance of not loosing embedded comments and pi's
  8411.         $elt->{first_child}->set_pcdata( $string);
  8412.         return $elt;
  8413.       }
  8414.  
  8415.     foreach my $child (@{[$elt->_children]})
  8416.       { $child->delete; }
  8417.  
  8418.     my $pcdata= $elt->_new_pcdata( $string);
  8419.     $pcdata->paste( $elt);
  8420.  
  8421.     delete $elt->{empty};
  8422.  
  8423.     return $elt;
  8424.   }
  8425.  
  8426. # set the content of an element from a list of strings and elements
  8427. sub set_content
  8428.   { my $elt= shift;
  8429.  
  8430.     return $elt unless defined $_[0];
  8431.  
  8432.     # attributes can be given as a hash (passed by ref)
  8433.     if( ref $_[0] eq 'HASH')
  8434.       { my $atts= shift;
  8435.         $elt->del_atts; # usually useless but better safe than sorry
  8436.         $elt->set_atts( $atts);
  8437.         return $elt unless defined $_[0];
  8438.       }
  8439.  
  8440.     # check next argument for #EMPTY
  8441.     if( !(ref $_[0]) && ($_[0] eq $EMPTY) ) 
  8442.       { $elt->{empty}= 1; return $elt; }
  8443.  
  8444.     # case where we really want to do a set_text, the element is '#PCDATA'
  8445.     # or contains a single PCDATA and we only want to add text in it
  8446.     if( ($XML::Twig::index2gi[$elt->{'gi'}] eq $PCDATA || $elt->contains_a_single( $PCDATA)) 
  8447.         && (@_ == 1) && !( ref $_[0]))
  8448.       { $elt->set_text( $_[0]);
  8449.         return $elt;
  8450.       }
  8451.     elsif( ($XML::Twig::index2gi[$elt->{'gi'}] eq $CDATA) && (@_ == 1) && !( ref $_[0]))
  8452.       { $elt->_set_cdata( $_[0]);
  8453.         return $elt;
  8454.       }
  8455.  
  8456.     # delete the children
  8457.     foreach my $child (@{[$elt->_children]})
  8458.       { $child->delete; }
  8459.  
  8460.     foreach my $child (@_)
  8461.       { if( ref( $child) && isa( $child, 'XML::Twig::Elt'))
  8462.           { # argument is an element
  8463.             $child->paste( 'last_child', $elt);
  8464.           }
  8465.         else
  8466.           { # argument is a string
  8467.             if( (my $pcdata= $elt->{last_child}) && $elt->{last_child}->is_pcdata)
  8468.               { # previous child is also pcdata: just concatenate
  8469.                 $pcdata->set_pcdata( $pcdata->{pcdata} . $child) 
  8470.               }
  8471.             else
  8472.               { # previous child is not a string: creat a new pcdata element
  8473.                 $pcdata= $elt->_new_pcdata( $child);
  8474.                 $pcdata->paste( 'last_child', $elt);  
  8475.               }
  8476.           }
  8477.       }
  8478.  
  8479.     delete $elt->{empty};
  8480.  
  8481.     return $elt;
  8482.   }
  8483.  
  8484. # inserts an element (whose gi is given) as child of the element
  8485. # all children of the element are now children of the new element
  8486. # returns the new element
  8487. sub insert
  8488.   { my ($elt, @args)= @_;
  8489.     # first cut the children
  8490.     my @children= do { my $elt= $elt; my @children=(); my $child= $elt->{first_child}; while( $child) { push @children, $child; $child= $child->{next_sibling}; } @children; };
  8491.     foreach my $child (@children)
  8492.       { $child->cut; }
  8493.     # insert elements
  8494.     while( my $gi= shift @args)
  8495.       { my $new_elt= $elt->new( $gi);
  8496.         # add attributes if needed
  8497.         if( defined( $args[0]) && ( isa( $args[0], 'HASH')) )
  8498.           { $new_elt->set_atts( shift @args); }
  8499.         # paste the element
  8500.         $new_elt->paste( $elt);
  8501.         delete $elt->{empty};
  8502.         $elt= $new_elt;
  8503.       }
  8504.     # paste back the children
  8505.     foreach my $child (@children)
  8506.       { $child->paste( 'last_child', $elt); }
  8507.     return $elt;
  8508.   }
  8509.  
  8510. # insert a new element 
  8511. # $elt->insert_new_element( $opt_position, $gi, $opt_atts_hash, @opt_content); 
  8512. # the element is created with the same syntax as new
  8513. # position is the same as in paste, first_child by default
  8514. sub insert_new_elt
  8515.   { my $elt= shift;
  8516.     my $position= $_[0];
  8517.     if(     ($position eq 'before') || ($position eq 'after')
  8518.          || ($position eq 'first_child') || ($position eq 'last_child'))
  8519.       { shift; }
  8520.     else
  8521.       { $position= 'first_child'; }
  8522.  
  8523.     my $new_elt= $elt->new( @_);
  8524.     $new_elt->paste( $position, $elt);
  8525.  
  8526.     #if( defined $new_elt->{'att'}->{$ID}) { $new_elt->set_id( $new_elt->{'att'}->{$ID}); }
  8527.     
  8528.     return $new_elt;
  8529.   }
  8530.  
  8531. # wraps an element in elements which gi's are given as arguments
  8532. # $elt->wrap_in( 'td', 'tr', 'table') wraps the element as a single
  8533. # cell in a table for example
  8534. # returns the new element
  8535. sub wrap_in
  8536.   { my $elt= shift;
  8537.     while( my $gi = shift @_)
  8538.       { my $new_elt = $elt->new( $gi);
  8539.         if( $elt->{twig_current})
  8540.           { my $t= $elt->twig;
  8541.             $t->{twig_current}= $new_elt;
  8542.             delete $elt->{'twig_current'};
  8543.             $new_elt->{'twig_current'}=1;
  8544.           }
  8545.  
  8546.         if( my $parent= $elt->{parent})
  8547.           { $new_elt->{parent}=$parent; if( $XML::Twig::weakrefs) { weaken( $new_elt->{parent});} ; 
  8548.             if( $parent->{first_child} == $elt) { $parent->{first_child}=  $new_elt; }
  8549.              if( $parent->{last_child} == $elt) {  delete $parent->{empty}; $parent->{last_child}=$new_elt; if( $XML::Twig::weakrefs) { weaken( $parent->{last_child});} ;  }
  8550.           }
  8551.         else
  8552.           { # wrapping the root
  8553.             my $twig= $elt->twig;
  8554.             if( $twig && $twig->root && ($twig->root eq $elt) )
  8555.               { $twig->{twig_root}= $new_elt; }
  8556.           }
  8557.  
  8558.         if( my $prev_sibling= $elt->{prev_sibling})
  8559.           { $new_elt->{prev_sibling}=$prev_sibling; if( $XML::Twig::weakrefs) { weaken( $new_elt->{prev_sibling});} ;
  8560.             $prev_sibling->{next_sibling}=  $new_elt;
  8561.           }
  8562.  
  8563.         if( my $next_sibling= $elt->{next_sibling})
  8564.           { $new_elt->{next_sibling}=  $next_sibling;
  8565.             $next_sibling->{prev_sibling}=$new_elt; if( $XML::Twig::weakrefs) { weaken( $next_sibling->{prev_sibling});} ;
  8566.           }
  8567.         $new_elt->{first_child}=  $elt;
  8568.          delete $new_elt->{empty}; $new_elt->{last_child}=$elt; if( $XML::Twig::weakrefs) { weaken( $new_elt->{last_child});} ;
  8569.  
  8570.         $elt->{parent}=$new_elt; if( $XML::Twig::weakrefs) { weaken( $elt->{parent});} ;
  8571.         $elt->{prev_sibling}=undef; if( $XML::Twig::weakrefs) { weaken( $elt->{prev_sibling});} ;
  8572.         $elt->{next_sibling}=  undef;
  8573.  
  8574.         # add the attributes if the next argument is a hash ref
  8575.         if( defined( $_[0]) && (isa( $_[0], 'HASH')) )
  8576.           { $new_elt->set_atts( shift @_); }
  8577.  
  8578.         $elt= $new_elt;
  8579.       }
  8580.       
  8581.     return $elt;
  8582.   }
  8583.  
  8584. sub replace
  8585.   { my( $elt, $ref)= @_;
  8586.  
  8587.     if( $elt->{parent}) { $elt->cut; }
  8588.  
  8589.     if( my $parent= $ref->{parent})
  8590.       { $elt->{parent}=$parent; if( $XML::Twig::weakrefs) { weaken( $elt->{parent});} ;
  8591.         if( $parent->{first_child} == $ref) { $parent->{first_child}=  $elt; }
  8592.         if( $parent->{last_child} == $ref)  {  delete $parent->{empty}; $parent->{last_child}=$elt; if( $XML::Twig::weakrefs) { weaken( $parent->{last_child});}  ; }
  8593.       }
  8594.     if( my $prev_sibling= $ref->{prev_sibling})
  8595.       { $elt->{prev_sibling}=$prev_sibling; if( $XML::Twig::weakrefs) { weaken( $elt->{prev_sibling});} ;
  8596.         $prev_sibling->{next_sibling}=  $elt;
  8597.       }
  8598.     if( my $next_sibling= $ref->{next_sibling})
  8599.       { $elt->{next_sibling}=  $next_sibling;
  8600.         $next_sibling->{prev_sibling}=$elt; if( $XML::Twig::weakrefs) { weaken( $next_sibling->{prev_sibling});} ;
  8601.       }
  8602.    
  8603.     $ref->{parent}=undef; if( $XML::Twig::weakrefs) { weaken( $ref->{parent});} ;
  8604.     $ref->{prev_sibling}=undef; if( $XML::Twig::weakrefs) { weaken( $ref->{prev_sibling});} ;
  8605.     $ref->{next_sibling}=  undef;
  8606.     return $ref;
  8607.   }
  8608.  
  8609. sub replace_with
  8610.   { my $ref= shift;
  8611.     my $elt= shift;
  8612.     $elt->replace( $ref);
  8613.     foreach my $new_elt (reverse @_)
  8614.       { $new_elt->paste( after => $elt); }
  8615.     return $elt;
  8616.   }
  8617.  
  8618.  
  8619. # move an element, same syntax as paste, except the element is first cut
  8620. sub move
  8621.   { my $elt= shift;
  8622.     $elt->cut;
  8623.     $elt->paste( @_);
  8624.     return $elt;
  8625.   }
  8626.  
  8627.  
  8628. # adds a prefix to an element, creating a pcdata child if needed
  8629. sub prefix
  8630.   { my ($elt, $prefix, $option)= @_;
  8631.     my $asis= ($option && ($option eq 'asis')) ? 1 : 0;
  8632.     if( (exists $elt->{'pcdata'}) 
  8633.         && (($asis && $elt->{asis}) || (!$asis && ! $elt->{asis}))
  8634.       )
  8635.       { $elt->set_pcdata( $prefix . $elt->{pcdata}); }
  8636.     elsif( $elt->{first_child} && $elt->{first_child}->is_pcdata
  8637.         && (   ($asis && $elt->{first_child}->{asis}) 
  8638.             || (!$asis && ! $elt->{first_child}->{asis}))
  8639.          )
  8640.       { 
  8641.         $elt->{first_child}->set_pcdata( $prefix . $elt->{first_child}->pcdata); 
  8642.       }
  8643.     else
  8644.       { my $new_elt= $elt->_new_pcdata( $prefix);
  8645.         $new_elt->paste( $elt);
  8646.         if( $asis) { $new_elt->set_asis; }
  8647.       }
  8648.     return $elt;
  8649.   }
  8650.  
  8651. # adds a suffix to an element, creating a pcdata child if needed
  8652. sub suffix
  8653.   { my ($elt, $suffix, $option)= @_;
  8654.     my $asis= ($option && ($option eq 'asis')) ? 1 : 0;
  8655.     if( (exists $elt->{'pcdata'})
  8656.         && (($asis && $elt->{asis}) || (!$asis && ! $elt->{asis}))
  8657.       )
  8658.       { $elt->set_pcdata( $elt->{pcdata} . $suffix); }
  8659.     elsif( $elt->{last_child} && $elt->{last_child}->is_pcdata
  8660.         && (   ($asis && $elt->{last_child}->{asis}) 
  8661.             || (!$asis && ! $elt->{last_child}->{asis}))
  8662.          )
  8663.       { $elt->{last_child}->set_pcdata( $elt->{last_child}->pcdata . $suffix); }
  8664.     else
  8665.       { my $new_elt= $elt->_new_pcdata( $suffix);
  8666.         $new_elt->paste( 'last_child', $elt);
  8667.         if( $asis) { $new_elt->set_asis; }
  8668.       }
  8669.     return $elt;
  8670.   }
  8671.  
  8672. # create a path to an element ('/root/.../gi)
  8673. sub path
  8674.   { my $elt= shift;
  8675.     my @context= ( $elt, $elt->ancestors);
  8676.     return "/" . join( "/", reverse map {$_->gi} @context);
  8677.   }
  8678.  
  8679. sub xpath
  8680.   { my $elt= shift;
  8681.     my $xpath;
  8682.     foreach my $ancestor (reverse $elt->ancestors_or_self)
  8683.       { my $gi= $XML::Twig::index2gi[$ancestor->{'gi'}];
  8684.         $xpath.= "/$gi";
  8685.         my $index= $ancestor->prev_siblings( $gi) + 1;
  8686.         unless( ($index == 1) && !$ancestor->next_sibling( $gi))
  8687.           { $xpath.= "[$index]"; }
  8688.       }
  8689.     return $xpath;
  8690.   }
  8691.  
  8692. # methods used mainly by wrap_children
  8693.  
  8694. # return a string with the 
  8695. # for an element <foo><elt att="val">...</elt><elt2/><elt>...</elt></foo>
  8696. # returns '<elt att="val"><elt2><elt>'
  8697. sub _stringify_struct
  8698.   { my( $elt, %opt)= @_;
  8699.     my $string='';
  8700.     my $pretty_print= set_pretty_print( 'none');
  8701.     foreach my $child ($elt->_children)
  8702.       { $child->add_id; $string .= $child->start_tag( { escape_gt => 1 }) ||''; }
  8703.     set_pretty_print( $pretty_print);
  8704.     return $string;
  8705.   }
  8706.  
  8707. # wrap a series of elements in a new one
  8708. sub _wrap_range
  8709.   { my $elt= shift;
  8710.     my $gi= shift;
  8711.     my $atts= isa( $_[0], 'HASH') ? shift : undef;
  8712.     my $range= shift; # the string with the tags to wrap
  8713.  
  8714.     my $t= $elt->twig;
  8715.  
  8716.     # get the tags to wrap
  8717.     my @to_wrap;
  8718.     while( $range=~ m{<\w+\s+[^>]*id=("[^"]*"|'[^']*')[^>]*>}g)
  8719.       { push @to_wrap, $t->elt_id( substr( $1, 1, -1)); }
  8720.  
  8721.     return '' unless @to_wrap;
  8722.     
  8723.     my $to_wrap= shift @to_wrap;
  8724.     my %atts= %$atts;
  8725.     my $new_elt= $to_wrap->wrap_in( $gi, \%atts);
  8726.     $_->move( last_child => $new_elt) foreach (@to_wrap);
  8727.  
  8728.     return '';
  8729.   }
  8730.     
  8731. # wrap children matching a regexp in a new element
  8732. sub wrap_children
  8733.   { my( $elt, $regexp, $gi, $atts)= @_;
  8734.  
  8735.     $atts ||={};
  8736.  
  8737.     my $elt_as_string= $elt->_stringify_struct; # stringify the elt structure
  8738.     $regexp=~ s{(<[^>]*>)}{_match_expr( $1)}eg; # in the regexp, replace gi's by the proper regexp 
  8739.     $elt_as_string=~ s{($regexp)}{$elt->_wrap_range( $gi, $atts, $1)}eg; # then do the actual replace
  8740.   
  8741.     return $elt; 
  8742.   }
  8743.  
  8744. sub _match_expr
  8745.   { my $tag= shift;
  8746.     my( $gi, %atts)= XML::Twig::_parse_start_tag( $tag);
  8747.     return _match_tag( $gi, %atts);
  8748.   }
  8749.  
  8750.  
  8751. sub _match_tag
  8752.   { my( $elt, %atts)= @_;
  8753.     my $string= "<$elt\\b";
  8754.     foreach my $key (sort keys %atts)
  8755.       { my $val= qq{\Q$atts{$key}\E};
  8756.         $string.= qq{[^>]*$key=(?:"$val"|'$val')};
  8757.       }
  8758.     $string.=  qq{[^>]*>};
  8759.     return "(?:$string)";
  8760.   }
  8761.  
  8762. sub field_to_att
  8763.   { my( $elt, $cond, $att)= @_;
  8764.     $att ||= $cond;
  8765.     my $child= $elt->first_child( $cond) or return undef;
  8766.     $elt->set_att( $att => $child->text);
  8767.     $child->cut;
  8768.     return $elt;
  8769.   }
  8770.  
  8771. sub att_to_field
  8772.   { my( $elt, $att, $tag)= @_;
  8773.     $tag ||= $att;
  8774.     my $child= $elt->insert_new_elt( first_child => $tag, $elt->{'att'}->{$att});
  8775.     $elt->del_att( $att);
  8776.     return $elt;
  8777.   }
  8778.  
  8779. # sort children methods
  8780.  
  8781. sub sort_children_on_field
  8782.   { my $elt   = shift;
  8783.     my $field = shift;
  8784.     my $get_key= sub { return $_[0]->field( $field) };
  8785.     return $elt->sort_children( $get_key, @_); 
  8786.   }
  8787.  
  8788. sub sort_children_on_att
  8789.   { my $elt = shift;
  8790.     my $att = shift;
  8791.     my $get_key= sub { return $_[0]->{'att'}->{$att} };
  8792.     return $elt->sort_children( $get_key, @_); 
  8793.   }
  8794.  
  8795. sub sort_children_on_value
  8796.   { my $elt   = shift;
  8797.     #my $get_key= eval qq{ sub { $NO_WARNINGS; return \$_[0]->text } };
  8798.     my $get_key= \&text;
  8799.     return $elt->sort_children( $get_key, @_); 
  8800.   }
  8801.  
  8802.  
  8803. sub sort_children
  8804.   { my( $elt, $get_key, %opt)=@_;
  8805.     $opt{order} ||= 'normal';
  8806.     $opt{type}  ||= 'alpha';
  8807.     my( $par_a, $par_b)= ($opt{order} eq 'reverse') ? qw( b a) : qw ( a b) ;
  8808.     my $op= ($opt{type} eq 'numeric') ? '<=>' : 'cmp' ;
  8809.     my @children= $elt->cut_children;
  8810.     if( $opt{type} eq 'numeric')
  8811.       {  @children= map  { $_->[1] }
  8812.                     sort { $a->[0] <=> $b->[0] }
  8813.                     map  { [ $get_key->( $_), $_] } @children;
  8814.       }
  8815.     elsif( $opt{type} eq 'alpha')
  8816.       {  @children= map  { $_->[1] }
  8817.                     sort { $a->[0] cmp $b->[0] }
  8818.                     map  { [ $get_key->( $_), $_] } @children;
  8819.       }
  8820.     else
  8821.       { croak "wrong sort type '$opt{type}', should be either 'alpha' or 'numeric'"; }
  8822.  
  8823.     @children= reverse @children if( $opt{order} eq 'reverse');
  8824.     $elt->set_content( @children);
  8825.   }
  8826.  
  8827.  
  8828. # comparison methods
  8829.  
  8830. sub before
  8831.   { my( $a, $b)=@_;
  8832.     if( $a->cmp( $b) == -1) { return 1; } else { return 0; }
  8833.   }
  8834.  
  8835. sub after
  8836.   { my( $a, $b)=@_;
  8837.     if( $a->cmp( $b) == 1) { return 1; } else { return 0; }
  8838.   }
  8839.  
  8840. sub lt
  8841.   { my( $a, $b)=@_;
  8842.     return 1 if( $a->cmp( $b) == -1);
  8843.     return 0;
  8844.   }
  8845.  
  8846. sub le
  8847.   { my( $a, $b)=@_;
  8848.     return 1 unless( $a->cmp( $b) == 1);
  8849.     return 0;
  8850.   }
  8851.  
  8852. sub gt
  8853.   { my( $a, $b)=@_;
  8854.     return 1 if( $a->cmp( $b) == 1);
  8855.     return 0;
  8856.   }
  8857.  
  8858. sub ge
  8859.   { my( $a, $b)=@_;
  8860.     return 1 unless( $a->cmp( $b) == -1);
  8861.     return 0;
  8862.   }
  8863.  
  8864.  
  8865. sub cmp
  8866.   { my( $a, $b)=@_;
  8867.  
  8868.     # easy cases
  8869.     return  0 if( $a == $b);    
  8870.     return  1 if( $a->in($b)); # a in b => a starts after b 
  8871.     return -1 if( $b->in($a)); # b in a => a starts before b
  8872.  
  8873.     # ancestors does not include the element itself
  8874.     my @a_pile= ($a, $a->ancestors); 
  8875.     my @b_pile= ($b, $b->ancestors);
  8876.  
  8877.     # the 2 elements are not in the same twig
  8878.     return undef unless( $a_pile[-1] == $b_pile[-1]);
  8879.  
  8880.     # find the first non common ancestors (they are siblings)
  8881.     my $a_anc= pop @a_pile;
  8882.     my $b_anc= pop @b_pile;
  8883.  
  8884.     while( $a_anc == $b_anc) 
  8885.       { $a_anc= pop @a_pile;
  8886.         $b_anc= pop @b_pile;
  8887.       }
  8888.  
  8889.     # from there move left and right and figure out the order
  8890.     my( $a_prev, $a_next, $b_prev, $b_next)= ($a_anc, $a_anc, $b_anc, $b_anc);
  8891.     while()
  8892.       { $a_prev= $a_prev->{prev_sibling} || return( -1);
  8893.         return 1 if( $a_prev == $b_next);
  8894.         $a_next= $a_next->{next_sibling} || return( 1);
  8895.         return -1 if( $a_next == $b_prev);
  8896.         $b_prev= $b_prev->{prev_sibling} || return( 1);
  8897.         return -1 if( $b_prev == $a_next);
  8898.         $b_next= $b_next->{next_sibling} || return( -1);
  8899.         return 1 if( $b_next == $a_prev);
  8900.       }
  8901.   }
  8902.     
  8903. sub _dump
  8904.   { my( $elt, $option)= @_; 
  8905.   
  8906.     my $atts       = defined $option->{atts}       ? $option->{atts}       :  1;
  8907.     my $extra      = defined $option->{extra}      ? $option->{extra}      :  0;
  8908.     my $short_text = defined $option->{short_text} ? $option->{short_text} : 40;
  8909.  
  8910.     my $sp= '| ';
  8911.     my $indent= $sp x $elt->level;
  8912.     my $indent_sp= '  ' x $elt->level;
  8913.     
  8914.     my $dump='';
  8915.     if( $elt->is_elt)
  8916.       { 
  8917.         $dump .= $indent  . '|-' . $XML::Twig::index2gi[$elt->{'gi'}];
  8918.         
  8919.         if( $atts && (my @atts= $elt->att_names) )
  8920.           { $dump .= ' ' . join( ' ', map { qq{$_="} . $elt->{'att'}->{$_} . qq{"} } @atts); }
  8921.  
  8922.         $dump .= "\n";
  8923.         if( $extra) { $dump .= $elt->_dump_extra_data( $indent, $indent_sp, $short_text); }
  8924.         $dump .= join( "", map { $_->_dump( $option) } do { my $elt= $elt; my @children=(); my $child= $elt->{first_child}; while( $child) { push @children, $child; $child= $child->{next_sibling}; } @children; });
  8925.       }
  8926.     else
  8927.       { 
  8928.         if( (exists $elt->{'pcdata'}))
  8929.           { $dump .= "$indent|-PCDATA:  '"  . _short_text( $elt->{pcdata}, $short_text) . "'\n" }
  8930.         elsif( (exists $elt->{'ent'}))
  8931.           { $dump .= "$indent|-ENTITY:  '" . _short_text( $elt->{ent}, $short_text) . "'\n" }
  8932.         elsif( (exists $elt->{'cdata'}))
  8933.           { $dump .= "$indent|-CDATA:   '" . _short_text( $elt->{cdata}, $short_text) . "'\n" }
  8934.         elsif( (exists $elt->{'comment'}))
  8935.           { $dump .= "$indent|-COMMENT: '" . _short_text( $elt->comment_string, $short_text) . "'\n" }
  8936.         elsif( (exists $elt->{'target'}))
  8937.           { $dump .= "$indent|-PI:      '"      . $elt->{target} . "' - '" . _short_text( $elt->{data}, $short_text) . "'\n" }
  8938.         if( $extra) { $dump .= $elt->_dump_extra_data( $indent, $indent_sp, $short_text); }
  8939.       }
  8940.     return $dump;
  8941.   }
  8942.  
  8943. sub _dump_extra_data
  8944.   { my( $elt, $indent, $indent_sp, $short_text)= @_;
  8945.     my $dump='';
  8946.     if( $elt->extra_data)
  8947.       { my $extra_data = $indent . "|-- (cpi before) '" . _short_text( $elt->extra_data, $short_text) . "'";
  8948.         $extra_data=~ s{\n}{$indent_sp}g;
  8949.         $dump .= $extra_data . "\n";
  8950.       }
  8951.     if( $elt->{extra_data_in_pcdata})
  8952.       { foreach my $data ( @{$elt->{extra_data_in_pcdata}})
  8953.           { my $extra_data = $indent . "|-- (cpi offset $data->{offset}) '" . _short_text( $data->{text}, $short_text) . "'";
  8954.             $extra_data=~ s{\n}{$indent_sp}g;
  8955.             $dump .= $extra_data . "\n";
  8956.           }
  8957.       } 
  8958.     if( $elt->{extra_data_before_end_tag})
  8959.       { my $extra_data = $indent . "|-- (cpi end) '" . _short_text( $elt->{extra_data_before_end_tag}, $short_text) . "'";
  8960.         $extra_data=~ s{\n}{$indent_sp}g;
  8961.         $dump .= $extra_data . "\n";
  8962.       } 
  8963.     return $dump;
  8964.   }
  8965.  
  8966.  
  8967. sub _short_text
  8968.   { my( $string, $length)= @_;
  8969.     if( !$length || (length( $string) < $length) ) { return $string; }
  8970.     my $l1= (length( $string) -5) /2;
  8971.     my $l2= length( $string) - ($l1 + 5);
  8972.     return substr( $string, 0, $l1) . ' ... ' . substr( $string, -$l2);
  8973.   }
  8974.  
  8975.  
  8976. sub _and { return _join_defined( ' && ',  @_); }
  8977. sub _join_defined { return join( shift(), grep { $_ } @_); }
  8978.  
  8979. 1;
  8980. __END__
  8981.  
  8982. =head1 NAME
  8983.  
  8984. XML::Twig - A perl module for processing huge XML documents in tree mode.
  8985.  
  8986. =head1 SYNOPSIS
  8987.  
  8988. Note that this documentation is intended as a reference to the module.
  8989.  
  8990. Complete docs, including a tutorial, examples, an easier to use HTML version,
  8991. a quick reference card and a FAQ are available at L<http://www.xmltwig.com/xmltwig>
  8992.  
  8993. Small documents (loaded in memory as a tree):
  8994.  
  8995.   my $twig=XML::Twig->new();    # create the twig
  8996.   $twig->parsefile( 'doc.xml'); # build it
  8997.   my_process( $twig);           # use twig methods to process it 
  8998.   $twig->print;                 # output the twig
  8999.  
  9000. Huge documents (processed in combined stream/tree mode):
  9001.  
  9002.   # at most one div will be loaded in memory
  9003.   my $twig=XML::Twig->new(   
  9004.     twig_handlers => 
  9005.       { title   => sub { $_->set_tag( 'h2') }, # change title tags to h2
  9006.         para    => sub { $_->set_tag( 'p')  }, # change para to p
  9007.         hidden  => sub { $_->delete;       },  # remove hidden elements
  9008.         list    => \&my_list_process,          # process list elements
  9009.         div     => sub { $_[0]->flush;     },  # output and free memory
  9010.       },
  9011.     pretty_print => 'indented',                # output will be nicely formatted
  9012.     empty_tags   => 'html',                    # outputs <empty_tag />
  9013.                          );
  9014.     $twig->flush;                              # flush the end of the document
  9015.  
  9016. See L<XML::Twig 101|/XML::Twig 101> for other ways to use the module, as a 
  9017. filter for example.
  9018.  
  9019.  
  9020. =head1 DESCRIPTION
  9021.  
  9022. This module provides a way to process XML documents. It is build on top
  9023. of C<XML::Parser>.
  9024.  
  9025. The module offers a tree interface to the document, while allowing you
  9026. to output the parts of it that have been completely processed.
  9027.  
  9028. It allows minimal resource (CPU and memory) usage by building the tree
  9029. only for the parts of the documents that need actual processing, through the 
  9030. use of the C<L<twig_roots|/twig_roots> > and 
  9031. C<L<twig_print_outside_roots|/twig_print_outside_roots> > options. The 
  9032. C<L<finish|/finish> > and C<L<finish_print|/finish_print> > methods also help 
  9033. to increase performances.
  9034.  
  9035. XML::Twig tries to make simple things easy so it tries its best to takes care 
  9036. of a lot of the (usually) annoying (but sometimes necessary) features that 
  9037. come with XML and XML::Parser.
  9038.  
  9039. =head1 XML::Twig 101
  9040.  
  9041. XML::Twig can be used either on "small" XML documents (that fit in memory)
  9042. or on huge ones, by processing parts of the document and outputting or
  9043. discarding them once they are processed.
  9044.  
  9045.  
  9046. =head2 Loading an XML document and processing it
  9047.  
  9048.   my $t= XML::Twig->new();
  9049.   $t->parse( '<d><title>title</title><para>p 1</para><para>p 2</para></d>');
  9050.   my $root= $t->root;
  9051.   $root->set_tag( 'html');              # change doc to html
  9052.   $title= $root->first_child( 'title'); # get the title
  9053.   $title->set_tag( 'h1');               # turn it into h1
  9054.   my @para= $root->children( 'para');   # get the para children
  9055.   foreach my $para (@para)
  9056.     { $para->set_tag( 'p'); }           # turn them into p
  9057.   $t->print;                            # output the document
  9058.  
  9059. Other useful methods include:
  9060.  
  9061. L<att|/att>: C<< $elt->{'att'}->{'foo'} >> return the C<foo> attribute for an 
  9062. element,
  9063.  
  9064. L<set_att|/set_att> : C<< $elt->set_att( foo => "bar") >> sets the C<foo> 
  9065. attribute to the C<bar> value,
  9066.  
  9067. L<next_sibling|/next_sibling>: C<< $elt->{next_sibling} >> return the next sibling
  9068. in the document (in the example C<< $title->{next_sibling} >> is the first
  9069. C<para>, you can also (and actually should) use 
  9070. C<< $elt->next_sibling( 'para') >> to get it 
  9071.  
  9072. The document can also be transformed through the use of the L<cut|/cut>, 
  9073. L<copy|/copy>, L<paste|/paste> and L<move|/move> methods: 
  9074. C<< $title->cut; $title->paste( after => $p); >> for example
  9075.  
  9076. And much, much more, see L<XML::Twig::Elt>.
  9077.  
  9078. =head2 Processing an XML document chunk by chunk
  9079.  
  9080. One of the strengths of XML::Twig is that it let you work with files that do 
  9081. not fit in memory (BTW storing an XML document in memory as a tree is quite
  9082. memory-expensive, the expansion factor being often around 10).
  9083.  
  9084. To do this you can define handlers, that will be called once a specific 
  9085. element has been completely parsed. In these handlers you can access the
  9086. element and process it as you see fit, using the navigation and the
  9087. cut-n-paste methods, plus lots of convenient ones like C<L<prefix|/prefix> >.
  9088. Once the element is completely processed you can then C<L<flush|/flush> > it, 
  9089. which will output it and free the memory. You can also C<L<purge|/purge> > it 
  9090. if you don't need to output it (if you are just extracting some data from 
  9091. the document for example). The handler will be called again once the next 
  9092. relevant element has been parsed.
  9093.  
  9094.   my $t= XML::Twig->new( twig_handlers => 
  9095.                           { section => \§ion,
  9096.                             para   => sub { $_->set_tag( 'p'); }
  9097.                           },
  9098.                        );
  9099.   $t->parsefile( 'doc.xml');
  9100.   $t->flush; # don't forget to flush one last time in the end or anything
  9101.              # after the last </section> tag will not be output 
  9102.  
  9103.   # the handler is called once a section is completely parsed, ie when 
  9104.   # the end tag for section is found, it receives the twig itself and
  9105.   # the element (including all its sub-elements) as arguments
  9106.   sub section 
  9107.     { my( $t, $section)= @_;      # arguments for all twig_handlers
  9108.       $section->set_tag( 'div');  # change the tag name.4, my favourite method...
  9109.       # let's use the attribute nb as a prefix to the title
  9110.       my $title= $section->first_child( 'title'); # find the title
  9111.       my $nb= $title->{'att'}->{'nb'}; # get the attribute
  9112.       $title->prefix( "$nb - ");  # easy isn't it?
  9113.       $section->flush;            # outputs the section and frees memory
  9114.     }
  9115.  
  9116.  
  9117. There is of course more to it: you can trigger handlers on more elaborate 
  9118. conditions than just the name of the element, C<section/title> for example.
  9119.  
  9120.   my $t= XML::Twig->new( twig_handlers => 
  9121.                            { 'section/title' => sub { $_->print } }
  9122.                        )
  9123.                   ->parsefile( 'doc.xml');
  9124.  
  9125. Here C<< sub { $_->print } >> simply prints the current element (C<$_> is aliased
  9126. to the element in the handler).
  9127.  
  9128. You can also trigger a handler on a test on an attribute:
  9129.  
  9130.   my $t= XML::Twig->new( twig_handlers => 
  9131.                       { 'section[@level="1"]' => sub { $_->print } }
  9132.                        );
  9133.                   ->parsefile( 'doc.xml');
  9134.  
  9135. You can also use C<L<start_tag_handlers|/start_tag_handlers> > to process an 
  9136. element as soon as the start tag is found. Besides C<L<prefix|/prefix> > you
  9137. can also use C<L<suffix|/suffix> >, 
  9138.  
  9139. =head2 Processing just parts of an XML document
  9140.  
  9141. The twig_roots mode builds only the required sub-trees from the document
  9142. Anything outside of the twig roots will just be ignored:
  9143.  
  9144.   my $t= XML::Twig->new( 
  9145.        # the twig will include just the root and selected titles 
  9146.            twig_roots   => { 'section/title' => \&print_n_purge,
  9147.                              'annex/title'   => \&print_n_purge
  9148.            }
  9149.                       );
  9150.   $t->parsefile( 'doc.xml');
  9151.  
  9152.   sub print_n_purge 
  9153.     { my( $t, $elt)= @_;
  9154.       print $elt->text;    # print the text (including sub-element texts)
  9155.       $t->purge;           # frees the memory
  9156.     }
  9157.  
  9158. You can use that mode when you want to process parts of a documents but are
  9159. not interested in the rest and you don't want to pay the price, either in
  9160. time or memory, to build the tree for the it.
  9161.  
  9162.  
  9163. =head2 Building an XML filter
  9164.  
  9165. You can combine the C<twig_roots> and the C<twig_print_outside_roots> options to 
  9166. build filters, which let you modify selected elements and will output the rest 
  9167. of the document as is.
  9168.  
  9169. This would convert prices in $ to prices in Euro in a document:
  9170.  
  9171.   my $t= XML::Twig->new( 
  9172.            twig_roots   => { 'price' => \&convert, },   # process prices 
  9173.            twig_print_outside_roots => 1,               # print the rest
  9174.                       );
  9175.   $t->parsefile( 'doc.xml');
  9176.  
  9177.   sub convert 
  9178.     { my( $t, $price)= @_;
  9179.       my $currency=  $price->{'att'}->{'currency'};          # get the currency
  9180.       if( $currency eq 'USD')
  9181.         { $usd_price= $price->text;                     # get the price
  9182.           # %rate is just a conversion table 
  9183.           my $euro_price= $usd_price * $rate{usd2euro};
  9184.           $price->set_text( $euro_price);               # set the new price
  9185.           $price->set_att( currency => 'EUR');          # don't forget this!
  9186.         }
  9187.       $price->print;                                    # output the price
  9188.     }
  9189.  
  9190. =head2 XML::Twig and various versions of Perl, XML::Parser and expat:
  9191.  
  9192. Before being uploaded to CPAN, XML::Twig 3.22 has been tested under the 
  9193. following environments:
  9194.  
  9195. =over 4
  9196.  
  9197. =item linux-x86
  9198.  
  9199. perl 5.6.2, expat 1.95.8, XML::Parser 2.34
  9200. perl 5.8.0, expat 1.95.8, XML::Parser 2.34
  9201. perl 5.8.7, expat 1.95.8, XML::Parser2.34
  9202.  
  9203. =item Solaris
  9204.  
  9205. perl 5.6.1, expat 1.95.2, XML::Parser 2.31
  9206.  
  9207. =back
  9208.  
  9209. XML::Twig is a lot more sensitive to variations in versions of perl, 
  9210. XML::Parser and expat than to the OS, so this should cover some
  9211. reasonable configurations.
  9212.  
  9213. The "recommended configuration" is perl 5.8.3+ (for good Unicode
  9214. support), XML::Parser 2.31+ and expat 1.95.5+
  9215.  
  9216. See L<http://testers.cpan.org/search?request=dist&dist=XML-Twig> for the
  9217. CPAN testers reports on XML::Twig, which list all tested configurations.
  9218.  
  9219. An Atom feed of the CPAN Testers results is available at
  9220. L<http://xmltwig.com/rss/twig_testers.rss>
  9221.  
  9222. Finally: 
  9223.  
  9224. =over 4
  9225.  
  9226. =item XML::Twig does B<NOT> work with expat 1.95.4
  9227.  
  9228. =item  XML::Twig only works with XML::Parser 2.27 in perl 5.6.*  
  9229.  
  9230. Note that I can't compile XML::Parser 2.27 anymore, so I can't guarantee 
  9231. that it still works
  9232.  
  9233. =item XML::Parser 2.28 does not really work
  9234.  
  9235. =back
  9236.  
  9237. When in doubt, upgrade expat, XML::Parser and Scalar::Util
  9238.  
  9239. Finally, for some optional features, XML::Twig depends on some additional
  9240. modules. The complete list, which depends somewhat on the version of Perl
  9241. that you are running, is given by running C<t/zz_dump_config.t>
  9242.  
  9243. =head1 Simplifying XML processing
  9244.  
  9245. =over 4
  9246.  
  9247. =item Whitespaces
  9248.  
  9249. Whitespaces that look non-significant are discarded, this behaviour can be 
  9250. controlled using the C<L<keep_spaces|/keep_spaces> >, 
  9251. C<L<keep_spaces_in|/keep_spaces_in> > and 
  9252. C<L<discard_spaces_in|/discard_spaces_in> > options.
  9253.  
  9254. =item Encoding
  9255.  
  9256. You can specify that you want the output in the same encoding as the input
  9257. (provided you have valid XML, which means you have to specify the encoding
  9258. either in the document or when you create the Twig object) using the 
  9259. C<L<keep_encoding|/keep_encoding> > option
  9260.  
  9261. You can also use C<L<output_encoding>> to convert the internal UTF-8 format
  9262. to the required encoding.
  9263.  
  9264. =item Comments and Processing Instructions (PI)
  9265.  
  9266. Comments and PI's can be hidden from the processing, but still appear in the
  9267. output (they are carried by the "real" element closer to them)
  9268.  
  9269. =item Pretty Printing
  9270.  
  9271. XML::Twig can output the document pretty printed so it is easier to read for
  9272. us humans.
  9273.  
  9274. =item Surviving an untimely death
  9275.  
  9276. XML parsers are supposed to react violently when fed improper XML. 
  9277. XML::Parser just dies.
  9278.  
  9279. XML::Twig provides the C<L<safe_parse|/safe_parse> > and the 
  9280. C<L<safe_parsefile|/safe_parsefile> > methods which wrap the parse in an eval
  9281. and return either the parsed twig or 0 in case of failure.
  9282.  
  9283. =item Private attributes
  9284.  
  9285. Attributes with a name starting with # (illegal in XML) will not be
  9286. output, so you can safely use them to store temporary values during
  9287. processing. Note that you can store anything in a private attribute, 
  9288. not just text, it's just a regular Perl variable, so a reference to
  9289. an object or a huge data structure is perfectly fine.
  9290.  
  9291. =back
  9292.  
  9293. =head1 CLASSES
  9294.  
  9295. XML::Twig uses a very limited number of classes. The ones you are most likely to use
  9296. are C<L<XML::Twig>> of course, which represents a complete XML document, including the 
  9297. document itself (the root of the document itself is C<L<root>>), its handlers, its
  9298. input or output filters... The other main class is C<L<XML::Twig::Elt>>, which models 
  9299. an XML element. Element here has a very wide definition: it can be a regular element, or
  9300. but also text, with an element C<L<tag>> of C<#PCDATA> (or C<#CDATA>), an entity (tag is
  9301. C<#ENT>), a Processing Instruction (C<#PI>), a comment (C<#COMMENT>). 
  9302.  
  9303. Those are the 2 commonly used classes.
  9304.  
  9305. You might want to look the C<L<elt_class>> option if you want to subclass C<XML::Twig::Elt>.
  9306.  
  9307. Attributes are just attached to their parent element, they are not objects per se. (Please
  9308. use the provided methods C<L<att>> and C<L<set_att>> to access them, if you access them
  9309. as a hash, then your code becomes implementaion dependent and might break in the future).
  9310.  
  9311. Other classes that are seldom used are C<L<XML::Twig::Entity_list>> and C<L<XML::Twig::Entity>>.
  9312.  
  9313. If you use C<L<XML::Twig::XPath>> instead of C<XML::Twig>, elements are then created as
  9314. C<L<XML::Twig::XPath::Elt>>
  9315.  
  9316.  
  9317. =head1 METHODS
  9318.  
  9319. =head2 XML::Twig 
  9320.  
  9321. A twig is a subclass of XML::Parser, so all XML::Parser methods can be
  9322. called on a twig object, including parse and parsefile.
  9323. C<setHandlers> on the other hand cannot be used, see C<L<BUGS|/BUGS> >
  9324.  
  9325.  
  9326. =over 4
  9327.  
  9328. =item new 
  9329.  
  9330. This is a class method, the constructor for XML::Twig. Options are passed
  9331. as keyword value pairs. Recognized options are the same as XML::Parser,
  9332. plus some XML::Twig specifics.
  9333.  
  9334. New Options:
  9335.  
  9336. =over 4
  9337.  
  9338. =item twig_handlers
  9339.  
  9340. This argument consists of a hash C<{ expression => \&handler}> where 
  9341. expression is a an I<XPath-like expression> (+ some others). 
  9342.  
  9343. XPath expressions are limited to using the child and descendant axis
  9344. (indeed you can't specify an axis), and predicates cannot be nested.
  9345. You can use the C<string>, or C<< string(<tag>) >> function (except 
  9346. in C<twig_roots> triggers).
  9347.  
  9348. Additionally you can use regexps (/ delimited) to match attribute
  9349. and string values.
  9350.  
  9351. Examples:
  9352.  
  9353.   foo
  9354.   foo/bar
  9355.   foo//bar
  9356.   /foo/bar
  9357.   /foo//bar
  9358.   /foo/bar[@att1 = "val1" and @att2 = "val2"]/baz[@a >= 1]
  9359.   foo[string()=~ /^duh!+/]
  9360.   /foo[string(bar)=~ /\d+/]/baz[@att != 3]
  9361.  
  9362. #CDATA can be used to call a handler for a CDATA.
  9363. #COMMENT can be used to call a handler for comments
  9364.  
  9365. Some additional (non-XPath) expressions are also provided for convenience: 
  9366.  
  9367. =over 4
  9368.  
  9369. =item processing instructions
  9370.  
  9371. C<'?'> or C<'#PI'> triggers the handler for any processing instruction,
  9372. and C<< '?<target>' >> or C<< '#PI <target>' >> triggers a handler for processing
  9373. instruction with the given target( ex: C<'#PI xml-stylesheet'>).
  9374.  
  9375. =item level(<level>)
  9376.  
  9377. Triggers the handler on any element at that level in the tree (root is level 1)
  9378.  
  9379. =item _all_
  9380.  
  9381. Triggers the handler for B<all> elements in the tree
  9382.  
  9383. =item _default_
  9384.  
  9385. Triggers the handler for each element that does NOT have any other handler.
  9386.  
  9387. =back
  9388.  
  9389. Expressions are evaluated against the input document. 
  9390. Which means that even if you have changed the tag of an element (changing the
  9391. tag of a parent element from a handler for example) the change will not impact
  9392. the expression evaluation. There is an exception to this: "private" attributes
  9393. (which name start with a '#', and can only be created during the parsing, as
  9394. they are not valid XML) are checked against the current twig. 
  9395.  
  9396. Handlers are triggered in fixed order, sorted by their type (xpath expressions
  9397. first, then regexps, then level), then by whether they specify a full path 
  9398. (starting at the root element) or
  9399. not, then by by number of steps in the expression , then number of
  9400. predicates, then number of tests in predicates. Handlers where the last
  9401. step does not specify a step (C<foo/bar/*>) are triggered after other XPath handlers.
  9402. Finally C<_all_> handlers are triggered last. 
  9403.  
  9404. B<Important>: once a handler has been triggered if it returns 0 then no other
  9405. handler is called, except a C<_all_> handler which will be called anyway.
  9406.  
  9407. If a handler returns a true value and other handlers apply, then the next
  9408. applicable handler will be called. Repeat, rinse, lather..; The exception
  9409. to that rule is when the C<L<do_not_chain_handlers|/do_not_chain_handlers>>
  9410. option is set, in which case only the first handler will be called.
  9411.  
  9412. Note that it might be a good idea to explicitly return a short true value
  9413. (like 1) from handlers: this ensures that other applicable handlers are 
  9414. called even if the last statement for the handler happens to evaluate to
  9415. false. This might also speedup the code by avoiding the result of the last 
  9416. statement of the code to be copied and passed to the code managing handlers.
  9417. It can really pay to have 1 instead of a long string returned.
  9418.  
  9419. When an element is CLOSED the corresponding handler is called, with 2
  9420. arguments: the twig and the C<L<Element> >. The twig includes the 
  9421. document tree that has been built so far, the element is the complete sub-tree
  9422. for the element. This means that handlers for inner elements are called before
  9423. handlers for outer elements.
  9424.  
  9425. C<$_> is also set to the element, so it is easy to write inline handlers like
  9426.  
  9427.   para => sub { $_->set_tag( 'p'); }
  9428.  
  9429. Text is stored in elements whose tag is #PCDATA (due to mixed content, text
  9430. and sub-element in an element there is no way to store the text as just an
  9431. attribute of the enclosing element).
  9432.  
  9433. B<Warning>: if you have used purge or flush on the twig the element might not
  9434. be complete, some of its children might have been entirely flushed or purged,
  9435. and the start tag might even have been printed (by C<flush>) already, so changing
  9436. its tag might not give the expected result.
  9437.  
  9438.  
  9439. =item twig_roots
  9440.  
  9441. This argument let's you build the tree only for those elements you are
  9442. interested in. 
  9443.  
  9444.   Example: my $t= XML::Twig->new( twig_roots => { title => 1, subtitle => 1});
  9445.            $t->parsefile( file);
  9446.            my $t= XML::Twig->new( twig_roots => { 'section/title' => 1});
  9447.            $t->parsefile( file);
  9448.  
  9449.  
  9450. return a twig containing a document including only C<title> and C<subtitle> 
  9451. elements, as children of the root element.
  9452.  
  9453. You can use I<generic_attribute_condition>, I<attribute_condition>,
  9454. I<full_path>, I<partial_path>, I<tag>, I<tag_regexp>, I<_default_> and 
  9455. I<_all_> to trigger the building of the twig. 
  9456. I<string_condition> and I<regexp_condition> cannot be used as the content 
  9457. of the element, and the string, have not yet been parsed when the condition
  9458. is checked.
  9459.  
  9460. B<WARNING>: path are checked for the document. Even if the C<twig_roots> option
  9461. is used they will be checked against the full document tree, not the virtual
  9462. tree created by XML::Twig
  9463.  
  9464.  
  9465. B<WARNING>: twig_roots elements should NOT be nested, that would hopelessly
  9466. confuse XML::Twig ;--(
  9467.  
  9468. Note: you can set handlers (twig_handlers) using twig_roots
  9469.   Example: my $t= XML::Twig->new( twig_roots => 
  9470.                                    { title    => sub { $_{1]->print;}, 
  9471.                                      subtitle => \&process_subtitle 
  9472.                                    }
  9473.                                );
  9474.            $t->parsefile( file);
  9475.  
  9476.  
  9477. =item twig_print_outside_roots
  9478.  
  9479. To be used in conjunction with the C<twig_roots> argument. When set to a true 
  9480. value this will print the document outside of the C<twig_roots> elements.
  9481.  
  9482.  Example: my $t= XML::Twig->new( twig_roots => { title => \&number_title },
  9483.                                 twig_print_outside_roots => 1,
  9484.                                );
  9485.            $t->parsefile( file);
  9486.            { my $nb;
  9487.            sub number_title
  9488.              { my( $twig, $title);
  9489.                $nb++;
  9490.                $title->prefix( "$nb "; }
  9491.                $title->print;
  9492.              }
  9493.            }
  9494.  
  9495.  
  9496. This example prints the document outside of the title element, calls 
  9497. C<number_title> for each C<title> element, prints it, and then resumes printing
  9498. the document. The twig is built only for the C<title> elements. 
  9499.  
  9500. If the value is a reference to a file handle then the document outside the
  9501. C<twig_roots> elements will be output to this file handle:
  9502.  
  9503.   open( OUT, ">out_file") or die "cannot open out file out_file:$!";
  9504.   my $t= XML::Twig->new( twig_roots => { title => \&number_title },
  9505.                          # default output to OUT
  9506.                          twig_print_outside_roots => \*OUT, 
  9507.                        );
  9508.  
  9509.          { my $nb;
  9510.            sub number_title
  9511.              { my( $twig, $title);
  9512.                $nb++;
  9513.                $title->prefix( "$nb "; }
  9514.                $title->print( \*OUT);    # you have to print to \*OUT here
  9515.              }
  9516.            }
  9517.  
  9518.  
  9519. =item start_tag_handlers
  9520.  
  9521. A hash C<{ expression => \&handler}>. Sets element handlers that are called when
  9522. the element is open (at the end of the XML::Parser C<Start> handler). The handlers
  9523. are called with 2 params: the twig and the element. The element is empty at 
  9524. that point, its attributes are created though. 
  9525.  
  9526. You can use I<generic_attribute_condition>, I<attribute_condition>,
  9527. I<full_path>, I<partial_path>, I<tag>, I<tag_regexp>, I<_default_>  and I<_all_> 
  9528. to trigger the handler. 
  9529.  
  9530. I<string_condition> and I<regexp_condition> cannot be used as the content of 
  9531. the element, and the string, have not yet been parsed when the condition is 
  9532. checked.
  9533.  
  9534. The main uses for those handlers are to change the tag name (you might have to 
  9535. do it as soon as you find the open tag if you plan to C<flush> the twig at some
  9536. point in the element, and to create temporary attributes that will be used
  9537. when processing sub-element with C<twig_hanlders>. 
  9538.  
  9539. You should also use it to change tags if you use C<flush>. If you change the tag 
  9540. in a regular C<twig_handler> then the start tag might already have been flushed. 
  9541.  
  9542. B<Note>: C<start_tag> handlers can be called outside of C<twig_roots> if this 
  9543. argument is used, in this case handlers are called with the following arguments:
  9544. C<$t> (the twig), C<$tag> (the tag of the element) and C<%att> (a hash of the 
  9545. attributes of the element). 
  9546.  
  9547. If the C<twig_print_outside_roots> argument is also used, if the last handler
  9548. called returns  a C<true> value, then the the start tag will be output as it
  9549. appeared in the original document, if the handler returns a a C<false> value
  9550. then the start tag will B<not> be printed (so you can print a modified string 
  9551. yourself for example).
  9552.  
  9553. Note that you can use the L<ignore|/ignore> method in C<start_tag_handlers> 
  9554. (and only there). 
  9555.  
  9556. =item end_tag_handlers
  9557.  
  9558. A hash C<{ expression => \&handler}>. Sets element handlers that are called when
  9559. the element is closed (at the end of the XML::Parser C<End> handler). The handlers
  9560. are called with 2 params: the twig and the tag of the element. 
  9561.  
  9562. I<twig_handlers> are called when an element is completely parsed, so why have 
  9563. this redundant option? There is only one use for C<end_tag_handlers>: when using
  9564. the C<twig_roots> option, to trigger a handler for an element B<outside> the roots.
  9565. It is for example very useful to number titles in a document using nested 
  9566. sections: 
  9567.  
  9568.   my @no= (0);
  9569.   my $no;
  9570.   my $t= XML::Twig->new( 
  9571.           start_tag_handlers => 
  9572.            { section => sub { $no[$#no]++; $no= join '.', @no; push @no, 0; } },
  9573.           twig_roots         => 
  9574.            { title   => sub { $_[1]->prefix( $no); $_[1]->print; } },
  9575.           end_tag_handlers   => { section => sub { pop @no;  } },
  9576.           twig_print_outside_roots => 1
  9577.                       );
  9578.    $t->parsefile( $file);
  9579.  
  9580. Using the C<end_tag_handlers> argument without C<twig_roots> will result in an
  9581. error.
  9582.  
  9583. =item do_not_chain_handlers
  9584.  
  9585. If this option is set to a true value, then only one handler will be called for
  9586. each element, even if several satisfy the condition
  9587.  
  9588. Note that the C<_all_> handler will still be called regardless
  9589.  
  9590. =item ignore_elts
  9591.  
  9592. This option lets you ignore elements when building the twig. This is useful 
  9593. in cases where you cannot use C<twig_roots> to ignore elements, for example if
  9594. the element to ignore is a sibling of elements you are interested in.
  9595.  
  9596. Example:
  9597.  
  9598.   my $twig= XML::Twig->new( ignore_elts => { elt => 1 });
  9599.   $twig->parsefile( 'doc.xml');
  9600.  
  9601. This will build the complete twig for the document, except that all C<elt> 
  9602. elements (and their children) will be left out.
  9603.  
  9604.  
  9605. =item char_handler
  9606.  
  9607. A reference to a subroutine that will be called every time C<PCDATA> is found.
  9608.  
  9609. The subroutine receives the string as argument, and returns the modified string:
  9610.  
  9611.   # we want all strings in upper case
  9612.   sub my_char_handler
  9613.     { my( $text)= @_;
  9614.       $text= uc( $text);
  9615.       return $text;
  9616.     }
  9617.  
  9618. =item elt_class
  9619.  
  9620. The name of a class used to store elements. this class should inherit from
  9621. C<XML::Twig::Elt> (and by default it is C<XML::Twig::Elt>). This option is used
  9622. to subclass the element class and extend it with new methods.
  9623.  
  9624. This option is needed because during the parsing of the XML, elements are created
  9625. by C<XML::Twig>, without any control from the user code.
  9626.  
  9627. =item keep_atts_order
  9628.  
  9629. Setting this option to a true value causes the attribute hash to be tied to
  9630. a C<Tie::IxHash> object.
  9631. This means that C<Tie::IxHash> needs to be installed for this option to be 
  9632. available. It also means that the hash keeps its order, so you will get 
  9633. the attributes in order. This allows outputting the attributes in the same 
  9634. order as they were in the original document.
  9635.  
  9636. =item keep_encoding
  9637.  
  9638. This is a (slightly?) evil option: if the XML document is not UTF-8 encoded and
  9639. you want to keep it that way, then setting keep_encoding will use theC<Expat> 
  9640. original_string method for character, thus keeping the original encoding, as 
  9641. well as the original entities in the strings.
  9642.  
  9643. See the C<t/test6.t> test file to see what results you can expect from the 
  9644. various encoding options.
  9645.  
  9646. B<WARNING>: if the original encoding is multi-byte then attribute parsing will
  9647. be EXTREMELY unsafe under any Perl before 5.6, as it uses regular expressions
  9648. which do not deal properly with multi-byte characters. You can specify an 
  9649. alternate function to parse the start tags with the C<parse_start_tag> option 
  9650. (see below)
  9651.  
  9652. B<WARNING>: this option is NOT used when parsing with the non-blocking parser 
  9653. (C<parse_start>, C<parse_more>, parse_done methods) which you probably should 
  9654. not use with XML::Twig anyway as they are totally untested!
  9655.  
  9656. =item output_encoding
  9657.  
  9658. This option generates an output_filter using C<Encode>,  C<Text::Iconv> or 
  9659. C<Unicode::Map8> and C<Unicode::Strings>, and sets the encoding in the XML
  9660. declaration. This is the easiest way to deal with encodings, if you need 
  9661. more sophisticated features, look at C<output_filter> below
  9662.  
  9663.  
  9664. =item output_filter
  9665.  
  9666. This option is used to convert the character encoding of the output document.
  9667. It is passed either a string corresponding to a predefined filter or
  9668. a subroutine reference. The filter will be called every time a document or 
  9669. element is processed by the "print" functions (C<print>, C<sprint>, C<flush>). 
  9670.  
  9671. Pre-defined filters: 
  9672.  
  9673. =over 4 
  9674.  
  9675. =item latin1 
  9676.  
  9677. uses either C<Encode>, C<Text::Iconv> or C<Unicode::Map8> and C<Unicode::String>
  9678. or a regexp (which works only with XML::Parser 2.27), in this order, to convert 
  9679. all characters to ISO-8859-1 (aka latin1)
  9680.  
  9681. =item html
  9682.  
  9683. does the same conversion as C<latin1>, plus encodes entities using
  9684. C<HTML::Entities> (oddly enough you will need to have HTML::Entities installed 
  9685. for it to be available). This should only be used if the tags and attribute 
  9686. names themselves are in US-ASCII, or they will be converted and the output will
  9687. not be valid XML any more
  9688.  
  9689. =item safe
  9690.  
  9691. converts the output to ASCII (US) only  plus I<character entities> (C<&#nnn;>) 
  9692. this should be used only if the tags and attribute names themselves are in 
  9693. US-ASCII, or they will be converted and the output will not be valid XML any 
  9694. more
  9695.  
  9696. =item safe_hex
  9697.  
  9698. same as C<safe> except that the character entities are in hexa (C<&#xnnn;>)
  9699.  
  9700. =item encode_convert ($encoding)
  9701.  
  9702. Return a subref that can be used to convert utf8 strings to C<$encoding>).
  9703. Uses C<Encode>.
  9704.  
  9705.    my $conv = XML::Twig::encode_convert( 'latin1');
  9706.    my $t = XML::Twig->new(output_filter => $conv);
  9707.  
  9708. =item iconv_convert ($encoding)
  9709.  
  9710. this function is used to create a filter subroutine that will be used to 
  9711. convert the characters to the target encoding using C<Text::Iconv> (which needs
  9712. to be installed, look at the documentation for the module and for the
  9713. C<iconv> library to find out which encodings are available on your system)
  9714.  
  9715.    my $conv = XML::Twig::iconv_convert( 'latin1');
  9716.    my $t = XML::Twig->new(output_filter => $conv);
  9717.  
  9718. =item unicode_convert ($encoding)
  9719.  
  9720. this function is used to create a filter subroutine that will be used to 
  9721. convert the characters to the target encoding using  C<Unicode::Strings> 
  9722. and C<Unicode::Map8> (which need to be installed, look at the documentation 
  9723. for the modules to find out which encodings are available on your system)
  9724.  
  9725.    my $conv = XML::Twig::unicode_convert( 'latin1');
  9726.    my $t = XML::Twig->new(output_filter => $conv);
  9727.  
  9728. =back
  9729.  
  9730. The C<text> and C<att> methods do not use the filter, so their 
  9731. result are always in unicode.
  9732.  
  9733. Those predeclared filters are based on subroutines that can be used
  9734. by themselves (as C<XML::Twig::foo>). 
  9735.  
  9736. =over 4
  9737.  
  9738. =item html_encode ($string)
  9739.  
  9740. Use C<HTML::Entities> to encode a utf8 string
  9741.  
  9742. =item safe_encode ($string)
  9743.  
  9744. Use either a regexp (perl < 5.8) or C<Encode> to encode non-ascii characters
  9745. in the string in C<< &#<nnnn>; >> format
  9746.  
  9747. =item safe_encode_hex ($string)
  9748.  
  9749. Use either a regexp (perl < 5.8) or C<Encode> to encode non-ascii characters
  9750. in the string in C<< &#x<nnnn>; >> format
  9751.  
  9752. =item regexp2latin1 ($string)
  9753.  
  9754. Use a regexp to encode a utf8 string into latin 1 (ISO-8859-1). Does not
  9755. work with Perl 5.8.0!
  9756.  
  9757. =back
  9758.  
  9759. =item output_text_filter
  9760.  
  9761. same as output_filter, except it doesn't apply to the brackets and quotes 
  9762. around attribute values. This is useful for all filters that could change
  9763. the tagging, basically anything that does not just change the encoding of
  9764. the output. C<html>, C<safe> and C<safe_hex> are better used with this option.
  9765.  
  9766. =item input_filter
  9767.  
  9768. This option is similar to C<output_filter> except the filter is applied to 
  9769. the characters before they are stored in the twig, at parsing time.
  9770.  
  9771. =item remove_cdata
  9772.  
  9773. Setting this option to a true value will force the twig to output CDATA 
  9774. sections as regular (escaped) PCDATA
  9775.  
  9776. =item parse_start_tag
  9777.  
  9778. If you use the C<keep_encoding> option then this option can be used to replace
  9779. the default parsing function. You should provide a coderef (a reference to a 
  9780. subroutine) as the argument, this subroutine takes the original tag (given
  9781. by XML::Parser::Expat C<original_string()> method) and returns a tag and the
  9782. attributes in a hash (or in a list attribute_name/attribute value).
  9783.  
  9784. =item expand_external_ents
  9785.  
  9786. When this option is used external entities (that are defined) are expanded
  9787. when the document is output using "print" functions such as C<L<print> >,
  9788. C<L<sprint|/sprint> >, C<L<flush|/flush> > and C<L<xml_string|/xml_string> >. 
  9789. Note that in the twig the entity will be stored as an element with a 
  9790. tag 'C<#ENT>', the entity will not be expanded there, so you might want to 
  9791. process the entities before outputting it.
  9792.  
  9793. If an external entity is not available, then the parse will fail.
  9794.  
  9795. A special case is when the value of this option is -1. In that case a missing
  9796. entity will not cause the parser to die, but its C<name>, C<sysid> and C<pubid>
  9797. will be stored in the twig as C<< $twig->{twig_missing_system_entities} >>
  9798. (a reference to an array of hashes { name => <name>, sysid => <sysid>,
  9799. pubid => <pubid> }). Yes, this is a bit of a hack, but it's useful in some
  9800. cases.  
  9801.  
  9802. =item load_DTD
  9803.  
  9804. If this argument is set to a true value, C<parse> or C<parsefile> on the twig
  9805. will load  the DTD information. This information can then be accessed through 
  9806. the twig, in a C<DTD_handler> for example. This will load even an external DTD.
  9807.  
  9808. Default and fixed values for attributes will also be filled, based on the DTD.
  9809.  
  9810. Note that to do this the module will generate a temporary file in the current
  9811. directory. If this is a problem let me know and I will add an option to
  9812. specify an alternate directory.
  9813.  
  9814. See L<DTD Handling> for more information
  9815.  
  9816. =item DTD_handler
  9817.  
  9818. Set a handler that will be called once the doctype (and the DTD) have been 
  9819. loaded, with 2 arguments, the twig and the DTD.
  9820.  
  9821. =item no_prolog
  9822.  
  9823. Does not output a prolog (XML declaration and DTD)
  9824.  
  9825. =item id
  9826.  
  9827. This optional argument gives the name of an attribute that can be used as
  9828. an ID in the document. Elements whose ID is known can be accessed through
  9829. the elt_id method. id defaults to 'id'.
  9830. See C<L<BUGS|/BUGS> >
  9831.  
  9832. =item discard_spaces
  9833.  
  9834. If this optional argument is set to a true value then spaces are discarded
  9835. when they look non-significant: strings containing only spaces are discarded.
  9836. This argument is set to true by default.
  9837.  
  9838. =item keep_spaces
  9839.  
  9840. If this optional argument is set to a true value then all spaces in the
  9841. document are kept, and stored as C<PCDATA>.
  9842.  
  9843. B<Warning>: adding this option can result in changes in the twig generated:
  9844. space that was previously discarded might end up in a new text element. see
  9845. the difference by calling the following code with 0 and 1 as arguments:
  9846.  
  9847.   perl -MXML::Twig -e'print XML::Twig->new( keep_spaces => shift)->parse( "<d> \n<e/></d>")->_dump'
  9848.  
  9849.  
  9850. C<keep_spaces> and C<discard_spaces> cannot be both set.
  9851.  
  9852. =item discard_spaces_in
  9853.  
  9854. This argument sets C<keep_spaces> to true but will cause the twig builder to
  9855. discard spaces in the elements listed.
  9856.  
  9857. The syntax for using this argument is:
  9858.  
  9859.   XML::Twig->new( discard_spaces_in => [ 'elt1', 'elt2']);
  9860.  
  9861. =item keep_spaces_in
  9862.  
  9863. This argument sets C<discard_spaces> to true but will cause the twig builder to
  9864. keep spaces in the elements listed.
  9865.  
  9866. The syntax for using this argument is: 
  9867.  
  9868.   XML::Twig->new( keep_spaces_in => [ 'elt1', 'elt2']);
  9869.  
  9870. B<Warning>: adding this option can result in changes in the twig generated:
  9871. space that was previously discarded might end up in a new text element.
  9872.  
  9873. =item pretty_print
  9874.  
  9875. Set the pretty print method, amongst 'C<none>' (default), 'C<nsgmls>', 
  9876. 'C<nice>', 'C<indented>', 'C<indented_c>', 'C<indented_a>', 
  9877. 'C<indented_close_tag>', 'C<cvs>', 'C<wrapped>', 'C<record>' and 'C<record_c>'
  9878.  
  9879. pretty_print formats:
  9880.  
  9881. =over 4
  9882.  
  9883. =item none
  9884.  
  9885. The document is output as one ling string, with no line breaks except those 
  9886. found within text elements
  9887.  
  9888. =item nsgmls
  9889.  
  9890. Line breaks are inserted in safe places: that is within tags, between a tag 
  9891. and an attribute, between attributes and before the > at the end of a tag.
  9892.  
  9893. This is quite ugly but better than C<none>, and it is very safe, the document 
  9894. will still be valid (conforming to its DTD).
  9895.  
  9896. This is how the SGML parser C<sgmls> splits documents, hence the name.
  9897.  
  9898. =item nice
  9899.  
  9900. This option inserts line breaks before any tag that does not contain text (so
  9901. element with textual content are not broken as the \n is the significant).
  9902.  
  9903. B<WARNING>: this option leaves the document well-formed but might make it
  9904. invalid (not conformant to its DTD). If you have elements declared as
  9905.  
  9906.   <!ELEMENT foo (#PCDATA|bar)>
  9907.  
  9908. then a C<foo> element including a C<bar> one will be printed as
  9909.  
  9910.   <foo>
  9911.   <bar>bar is just pcdata</bar>
  9912.   </foo>
  9913.  
  9914. This is invalid, as the parser will take the line break after the C<foo> tag 
  9915. as a sign that the element contains PCDATA, it will then die when it finds the 
  9916. C<bar> tag. This may or may not be important for you, but be aware of it!
  9917.  
  9918. =item indented
  9919.  
  9920. Same as C<nice> (and with the same warning) but indents elements according to 
  9921. their level 
  9922.  
  9923. =item indented_c
  9924.  
  9925. Same as C<indented> but a little more compact: the closing tags are on the 
  9926. same line as the preceding text
  9927.  
  9928. =item indented_close_tag
  9929.  
  9930. Same as C<indented> except that the closing tag is also indented, to line up 
  9931. with the tags within the element
  9932.  
  9933. =item idented_a
  9934.  
  9935. This formats XML files in a line-oriented version control friendly way. 
  9936. The format is described in L<http://tinyurl.com/2kwscq> (that's an Oracle
  9937. document with an insanely long URL).
  9938.  
  9939. Note that to be totaly conformant to the "spec", the order of attributes
  9940. should not be changed, so if they are not already in alphabetical order
  9941. you will need to use the C<L<keep_atts_order>> option.
  9942.  
  9943. =item cvs
  9944.  
  9945. Same as C<L<idented_a>>.
  9946.  
  9947. =item wrapped
  9948.  
  9949. Same as C<indented_c> but lines are wrapped using L<Text::Wrap::wrap>. The 
  9950. default length for lines is the default for C<$Text::Wrap::columns>, and can
  9951. be changed by changing that variable.
  9952.  
  9953. =item record
  9954.  
  9955. This is a record-oriented pretty print, that display data in records, one field 
  9956. per line (which looks a LOT like C<indented>)
  9957.  
  9958. =item record_c
  9959.  
  9960. Stands for record compact, one record per line
  9961.  
  9962. =back
  9963.  
  9964.  
  9965. =item empty_tags
  9966.  
  9967. Set the empty tag display style ('C<normal>', 'C<html>' or 'C<expand>').
  9968.  
  9969. C<normal> outputs an empty tag 'C<< <tag/> >>', C<html> adds a space 
  9970. 'C<< <tag /> >>' for elements that can be empty in XHTML and C<expand> outputs
  9971. 'C<< <tag></tag> >>'
  9972.  
  9973. =item quote
  9974.  
  9975. Set the quote character for attributes ('C<single>' or 'C<double>').
  9976.  
  9977. =item escape_gt
  9978.  
  9979. By default XML::Twig does not escape the character > in its output, as it is not
  9980. mandated by the XML spec. With this option on, > will be replaced by C<>>
  9981.  
  9982. =item comments
  9983.  
  9984. Set the way comments are processed: 'C<drop>' (default), 'C<keep>' or 
  9985. 'C<process>' 
  9986.  
  9987. Comments processing options:
  9988.  
  9989. =over 4
  9990.  
  9991. =item drop
  9992.  
  9993. drops the comments, they are not read, nor printed to the output
  9994.  
  9995. =item keep
  9996.  
  9997. comments are loaded and will appear on the output, they are not 
  9998. accessible within the twig and will not interfere with processing
  9999. though
  10000.  
  10001. B<Note>: comments in the middle of a text element such as 
  10002.  
  10003.   <p>text <!-- comment --> more text --></p>
  10004.  
  10005. are kept at their original position in the text. Using "print"
  10006. methods like C<print> or C<sprint> will return the comments in the
  10007. text. Using C<text> or C<field> on the other hand will not.
  10008.  
  10009. Any use of C<set_pcdata> on the C<#PCDATA> element (directly or 
  10010. through other methods like C<set_content>) will delete the comment(s).
  10011.  
  10012. =item process
  10013.  
  10014. comments are loaded in the twig and will be treated as regular elements 
  10015. (their C<tag> is C<#COMMENT>) this can interfere with processing if you
  10016. expect C<< $elt->{first_child} >> to be an element but find a comment there.
  10017. Validation will not protect you from this as comments can happen anywhere.
  10018. You can use C<< $elt->first_child( 'tag') >> (which is a good habit anyway)
  10019. to get where you want. 
  10020.  
  10021. Consider using C<process> if you are outputting SAX events from XML::Twig.
  10022.  
  10023. =back
  10024.  
  10025. =item pi
  10026.  
  10027. Set the way processing instructions are processed: 'C<drop>', 'C<keep>' 
  10028. (default) or 'C<process>'
  10029.  
  10030. Note that you can also set PI handlers in the C<twig_handlers> option: 
  10031.  
  10032.   '?'       => \&handler
  10033.   '?target' => \&handler 2
  10034.  
  10035. The handlers will be called with 2 parameters, the twig and the PI element if
  10036. C<pi> is set to C<process>, and with 3, the twig, the target and the data if
  10037. C<pi> is set to C<keep>. Of course they will not be called if C<pi> is set to 
  10038. C<drop>.
  10039.  
  10040. If C<pi> is set to C<keep> the handler should return a string that will be used
  10041. as-is as the PI text (it should look like "C< <?target data?> >" or '' if you
  10042. want to remove the PI), 
  10043.  
  10044. Only one handler will be called, C<?target> or C<?> if no specific handler for
  10045. that target is available.
  10046.  
  10047. =item map_xmlns 
  10048.  
  10049. This option is passed a hashref that maps uri's to prefixes. The prefixes in
  10050. the document will be replaced by the ones in the map. The mapped prefixes can
  10051. (actually have to) be used to trigger handlers, navigate or query the document.
  10052.  
  10053. Here is an example:
  10054.  
  10055.   my $t= XML::Twig->new( map_xmlns => {'http://www.w3.org/2000/svg' => "svg"},
  10056.                          twig_handlers => 
  10057.                            { 'svg:circle' => sub { $_->set_att( r => 20) } },
  10058.                          pretty_print => 'indented', 
  10059.                        )
  10060.                   ->parse( '<doc xmlns:gr="http://www.w3.org/2000/svg">
  10061.                               <gr:circle cx="10" cy="90" r="10"/>
  10062.                            </doc>'
  10063.                          )
  10064.                   ->print;
  10065.  
  10066. This will output:
  10067.  
  10068.   <doc xmlns:svg="http://www.w3.org/2000/svg">
  10069.      <svg:circle cx="10" cy="90" r="20"/>
  10070.   </doc>
  10071.  
  10072. =item keep_original_prefix
  10073.  
  10074. When used with C<L<map_xmlns>> this option will make C<XML::Twig> use the original
  10075. namespace prefixes when outputting a document. The mapped prefix will still be used
  10076. for triggering handlers and in navigation and query methods.
  10077.  
  10078.   my $t= XML::Twig->new( map_xmlns => {'http://www.w3.org/2000/svg' => "svg"},
  10079.                          twig_handlers => 
  10080.                            { 'svg:circle' => sub { $_->set_att( r => 20) } },
  10081.                          keep_original_prefix => 1,
  10082.                          pretty_print => 'indented', 
  10083.                        )
  10084.                   ->parse( '<doc xmlns:gr="http://www.w3.org/2000/svg">
  10085.                               <gr:circle cx="10" cy="90" r="10"/>
  10086.                            </doc>'
  10087.                          )
  10088.                   ->print;
  10089.  
  10090. This will output:
  10091.  
  10092.   <doc xmlns:gr="http://www.w3.org/2000/svg">
  10093.      <gr:circle cx="10" cy="90" r="20"/>
  10094.   </doc>
  10095.  
  10096. =item index ($arrayref or $hashref)
  10097.  
  10098. This option creates lists of specific elements during the parsing of the XML.
  10099. It takes a reference to either a list of triggering expressions or to a hash 
  10100. name => expression, and for each one generates the list of elements that 
  10101. match the expression. The list can be accessed through the C<L<index>> method.
  10102.  
  10103. example:
  10104.  
  10105.   # using an array ref
  10106.   my $t= XML::Twig->new( index => [ 'div', 'table' ])
  10107.                   ->parsefile( "foo.xml');
  10108.   my $divs= $t->index( 'div');
  10109.   my $first_div= $divs->[0];
  10110.   my $last_table= $t->index( table => -1);
  10111.  
  10112.   # using a hashref to name the indexes
  10113.   my $t= XML::Twig->new( index => { email => 'a[@href=~/^\s*mailto:/]')
  10114.                   ->parsefile( "foo.xml');
  10115.   my $last_emails= $t->index( email => -1);
  10116.  
  10117. Note that the index is not maintained after the parsing. If elements are 
  10118. deleted, renamed or otherwise hurt during processing, the index is NOT updated.
  10119.  
  10120.  
  10121. =back
  10122.  
  10123. B<Note>: I _HATE_ the Java-like name of arguments used by most XML modules.
  10124. So in pure TIMTOWTDI fashion all arguments can be written either as
  10125. C<UglyJavaLikeName> or as C<readable_perl_name>: C<twig_print_outside_roots>
  10126. or C<TwigPrintOutsideRoots> (or even C<twigPrintOutsideRoots> {shudder}). 
  10127. XML::Twig normalizes them before processing them.
  10128.  
  10129. =item parse ( $source)
  10130.  
  10131. The C<$source> parameter should either be a string containing the whole XML
  10132. document, or it should be an open C<IO::Handle>. Constructor options to
  10133. C<XML::Parser::Expat> given as keyword-value pairs may follow theC<$source> 
  10134. parameter. These override, for this call, any options or attributes passed
  10135. through from the XML::Parser instance.
  10136.  
  10137. A die call is thrown if a parse error occurs. Otherwise it will return 
  10138. the twig built by the parse. Use C<safe_parse> if you want the parsing
  10139. to return even when an error occurs.
  10140.  
  10141. If this method is called as a class method
  10142. (C<< XML::Twig->parse( $some_xml_or_html) >>) then an XML::Twig object is 
  10143. created, using the parameters except the last one (eg 
  10144. C<< XML::Twig->parse( pretty_print => 'indented', $some_xml_or_html) >>)
  10145. and C<L<xparse>> is called on it.
  10146.  
  10147. =item parsestring
  10148.  
  10149. This is just an alias for C<parse> for backwards compatibility.
  10150.  
  10151. =item parsefile (FILE [, OPT => OPT_VALUE [...]])
  10152.  
  10153. Open C<FILE> for reading, then call C<parse> with the open handle. The file
  10154. is closed no matter how C<parse> returns. 
  10155.  
  10156. A C<die> call is thrown if a parse error occurs. Otherwise it will return 
  10157. the twig built by the parse. Use C<safe_parsefile> if you want the parsing
  10158. to return even when an error occurs.
  10159.  
  10160. =item parsefile_inplace ( $file, $optional_extension)
  10161.  
  10162. Parse and update a file "in place". It does this by creating a temp file,
  10163. selecting it as the default for print() statements (and methods), then parsing
  10164. the input file. If the parsing is successful, then the temp file is 
  10165. moved to replace the input file.
  10166.  
  10167. If an extension is given then the original file is backed-up (the rules for
  10168. the extension are the same as the rule for the -i option in perl).
  10169.  
  10170. =item parsefile_html_inplace ( $file, $optional_extension)
  10171.  
  10172. Same as parsefile_inplace, except that it parses HTML instead of XML 
  10173.  
  10174. =item parseurl ($url $optional_user_agent)
  10175.  
  10176. Gets the data from C<$url> and parse it. The data is piped to the parser in 
  10177. chunks the size of the XML::Parser::Expat buffer, so memory consumption and
  10178. hopefully speed are optimal.
  10179.  
  10180. For most (read "small") XML it is probably as efficient (and easier to debug)
  10181. to just C<get> the XML file and then parse it as a string.
  10182.  
  10183.   use XML::Twig;
  10184.   use LWP::Simple;
  10185.   my $twig= XML::Twig->new();
  10186.   $twig->parse( LWP::Simple::get( $URL ));
  10187.  
  10188. or
  10189.  
  10190.   use XML::Twig;
  10191.   my $twig= XML::Twig->nparse( $URL);
  10192.  
  10193.  
  10194. If the C<$optional_user_agent> argument is used then it is used, otherwise a
  10195. new one is created.
  10196.  
  10197. =item safe_parse ( SOURCE [, OPT => OPT_VALUE [...]])
  10198.  
  10199. This method is similar to C<parse> except that it wraps the parsing in an
  10200. C<eval> block. It returns the twig on success and 0 on failure (the twig object
  10201. also contains the parsed twig). C<$@> contains the error message on failure.
  10202.  
  10203. Note that the parsing still stops as soon as an error is detected, there is
  10204. no way to keep going after an error.
  10205.  
  10206. =item safe_parsefile (FILE [, OPT => OPT_VALUE [...]])
  10207.  
  10208. This method is similar to C<parsefile> except that it wraps the parsing in an
  10209. C<eval> block. It returns the twig on success and 0 on failure (the twig object
  10210. also contains the parsed twig) . C<$@> contains the error message on failure
  10211.  
  10212. Note that the parsing still stops as soon as an error is detected, there is
  10213. no way to keep going after an error.
  10214.  
  10215. =item safe_parseurl ($url $optional_user_agent)
  10216.  
  10217. Same as C<parseurl> except that it wraps the parsing in an C<eval> block. It 
  10218. returns the twig on success and 0 on failure (the twig object also contains
  10219. the parsed twig) . C<$@> contains the error message on failure
  10220.  
  10221. =item parse_html ($string_or_fh)
  10222.  
  10223. parse an HTML string or file handle (by converting it to XML using
  10224. HTML::TreeBuilder, which needs to be available).
  10225.  
  10226. This works nicely, but some information gets lost in the process:
  10227. newlines are removed, and (at least on the version I use), comments
  10228. get get an extra CDATA section inside ( <!-- foo --> becomes
  10229. <!-- <![CDATA[ foo ]]> -->
  10230.  
  10231. =item parsefile_html
  10232.  
  10233. parse an HTML file (by converting it to XML using HTML::TreeBuilder, which 
  10234. needs to be available). The file is loaded completely in memory and converted
  10235. to XML before being parsed.
  10236.  
  10237. B<Alpha>: implementation, and thus generated XML could change. 
  10238.  
  10239. =item safe_parseurl_html ($url $optional_user_agent)
  10240.  
  10241. Same as C<L<parseurl_html>>> except that it wraps the parsing in an C<eval>
  10242. block.  It returns the twig on success and 0 on failure (the twig object also
  10243. contains the parsed twig) . C<$@> contains the error message on failure
  10244.  
  10245. =item safe_parsefile_html ($file $optional_user_agent)
  10246.  
  10247. Same as C<L<parsefile_html>>> except that it wraps the parsing in an C<eval> 
  10248. block.  It returns the twig on success and 0 on failure (the twig object also 
  10249. contains the parsed twig) . C<$@> contains the error message on failure
  10250.  
  10251. =item safe_parse_html ($string_or_fh)
  10252.  
  10253. Same as C<L<parse_html>> except that it wraps the parsing in an C<eval> block. 
  10254. It returns the twig on success and 0 on failure (the twig object also contains
  10255. the parsed twig) . C<$@> contains the error message on failure
  10256.  
  10257. =item xparse ($thing_to_parse)
  10258.  
  10259. parse the C<$thing_to_parse>, whether it is a filehandle, a string, an HTML 
  10260. file, an HTML URL, an URL or a file.
  10261.  
  10262. Note that this is mostly a convenience method for one-off scripts. For example
  10263. files that end in '.htm' or '.html' are parsed first as XML, and if this fails
  10264. as HTML. This is certainly not the most efficient way to do this in general.
  10265.  
  10266. =item nparse ($optional_twig_options, $thing_to_parse)
  10267.  
  10268. create a twig with the C<$optional_options>, and parse the C<$thing_to_parse>, 
  10269. whether it is a filehandle, a string, an HTML file, an HTML URL, an URL or a 
  10270. file.
  10271.  
  10272. Examples:
  10273.  
  10274.    XML::Twig->nparse( "file.xml");
  10275.    XML::Twig->nparse( error_context => 1, "file://file.xml");
  10276.  
  10277. =item nparse_pp ($optional_twig_options, $thing_to_parse)
  10278.  
  10279. same as C<L<nparse>> but also sets the C<pretty_print> option to C<indented>.
  10280.  
  10281. =item nparse_e ($optional_twig_options, $thing_to_parse)
  10282.  
  10283. same as C<L<nparse>> but also sets the C<error_context> option to 1.
  10284.  
  10285. =item nparse_ppe ($optional_twig_options, $thing_to_parse)
  10286.  
  10287. same as C<L<nparse>> but also sets the C<pretty_print> option to C<indented>
  10288. and the C<error_context> option to 1.
  10289.  
  10290. =item parser
  10291.  
  10292. This method returns the C<expat> object (actually the XML::Parser::Expat object) 
  10293. used during parsing. It is useful for example to call XML::Parser::Expat methods
  10294. on it. To get the line of a tag for example use C<< $t->parser->current_line >>.
  10295.  
  10296. =item setTwigHandlers ($handlers)
  10297.  
  10298. Set the twig_handlers. C<$handlers> is a reference to a hash similar to the
  10299. one in the C<twig_handlers> option of new. All previous handlers are unset.
  10300. The method returns the reference to the previous handlers.
  10301.  
  10302. =item setTwigHandler ($exp $handler)
  10303.  
  10304. Set a single twig_handler for elements matching C<$exp>. C<$handler> is a 
  10305. reference to a subroutine. If the handler was previously set then the reference 
  10306. to the previous handler is returned.
  10307.  
  10308. =item setStartTagHandlers ($handlers)
  10309.  
  10310. Set the start_tag handlers. C<$handlers> is a reference to a hash similar to the
  10311. one in the C<start_tag_handlers> option of new. All previous handlers are unset.
  10312. The method returns the reference to the previous handlers.
  10313.  
  10314. =item setStartTagHandler ($exp $handler)
  10315.  
  10316. Set a single start_tag handlers for elements matching C<$exp>. C<$handler> is a 
  10317. reference to a subroutine. If the handler was previously set then the reference
  10318. to the previous handler is returned.
  10319.  
  10320. =item setEndTagHandlers ($handlers)
  10321.  
  10322. Set the end_tag handlers. C<$handlers> is a reference to a hash similar to the
  10323. one in the C<end_tag_handlers> option of new. All previous handlers are unset.
  10324. The method returns the reference to the previous handlers.
  10325.  
  10326. =item setEndTagHandler ($exp $handler)
  10327.  
  10328. Set a single end_tag handlers for elements matching C<$exp>. C<$handler> is a 
  10329. reference to a subroutine. If the handler was previously set then the 
  10330. reference to the previous handler is returned.
  10331.  
  10332. =item setTwigRoots ($handlers)
  10333.  
  10334. Same as using the C<L<twig_roots>> option when creating the twig
  10335.  
  10336. =item setCharHandler ($exp $handler)
  10337.  
  10338. Set a C<char_handler>
  10339.  
  10340. =item setIgnoreEltsHandler ($exp)
  10341.  
  10342. Set a C<ignore_elt> handler (elements that match C<$exp> will be ignored
  10343.  
  10344. =item setIgnoreEltsHandlers ($exp)
  10345.  
  10346. Set all C<ignore_elt> handlers (previous handlers are replaced)
  10347.  
  10348. =item dtd
  10349.  
  10350. Return the dtd (an L<XML::Twig::DTD> object) of a twig
  10351.  
  10352. =item xmldecl
  10353.  
  10354. Return the XML declaration for the document, or a default one if it doesn't
  10355. have one
  10356.  
  10357. =item doctype
  10358.  
  10359. Return the doctype for the document
  10360.  
  10361. =item doctype_name
  10362.  
  10363. returns the doctype of the document from the doctype declaration
  10364.  
  10365. =item system_id
  10366.  
  10367. returns the system value of the DTD of the document from the doctype declaration
  10368.  
  10369. =item public_id
  10370.  
  10371. returns the public doctype of the document from the doctype declaration
  10372.  
  10373. =item internal_subset
  10374.  
  10375. returns the internal subset of the DTD
  10376.  
  10377. =item dtd_text
  10378.  
  10379. Return the DTD text
  10380.  
  10381. =item dtd_print
  10382.  
  10383. Print the DTD
  10384.  
  10385. =item model ($tag)
  10386.  
  10387. Return the model (in the DTD) for the element C<$tag>
  10388.  
  10389. =item root
  10390.  
  10391. Return the root element of a twig
  10392.  
  10393. =item set_root ($elt)
  10394.  
  10395. Set the root of a twig
  10396.  
  10397. =item first_elt ($optional_condition)
  10398.  
  10399. Return the first element matching C<$optional_condition> of a twig, if
  10400. no condition is given then the root is returned
  10401.  
  10402. =item last_elt ($optional_condition)
  10403.  
  10404. Return the last element matching C<$optional_condition> of a twig, if
  10405. no condition is given then the last element of the twig is returned
  10406.  
  10407. =item elt_id        ($id)
  10408.  
  10409. Return the element whose C<id> attribute is $id
  10410.  
  10411. =item getEltById
  10412.  
  10413. Same as C<L<elt_id>>
  10414.  
  10415. =item index ($index_name, $optional_index)
  10416.  
  10417. If the C<$optional_index> argument is present, return the corresponding element
  10418. in the index (created using the C<index> option for C<XML::Twig->new>)
  10419.  
  10420. If the argument is not present, return an arrayref to the index
  10421.  
  10422. =item normalize
  10423.  
  10424. merge together all consecutive pcdata elements in the document (if for example
  10425. you have turned some elements into pcdata using C<L<erase>>, this will give you
  10426. a "clean" document in which there all text elements are as long as possible).
  10427.  
  10428. =item encoding
  10429.  
  10430. This method returns the encoding of the XML document, as defined by the 
  10431. C<encoding> attribute in the XML declaration (ie it is C<undef> if the attribute
  10432. is not defined)
  10433.  
  10434. =item set_encoding
  10435.  
  10436. This method sets the value of the C<encoding> attribute in the XML declaration. 
  10437. Note that if the document did not have a declaration it is generated (with
  10438. an XML version of 1.0)
  10439.  
  10440. =item xml_version
  10441.  
  10442. This method returns the XML version, as defined by the C<version> attribute in 
  10443. the XML declaration (ie it is C<undef> if the attribute is not defined)
  10444.  
  10445. =item set_xml_version
  10446.  
  10447. This method sets the value of the C<version> attribute in the XML declaration. 
  10448. If the declaration did not exist it is created.
  10449.  
  10450. =item standalone
  10451.  
  10452. This method returns the value of the C<standalone> declaration for the document
  10453.  
  10454. =item set_standalone
  10455.  
  10456. This method sets the value of the C<standalone> attribute in the XML 
  10457. declaration.  Note that if the document did not have a declaration it is 
  10458. generated (with an XML version of 1.0)
  10459.  
  10460. =item set_output_encoding
  10461.  
  10462. Set the C<encoding> "attribute" in the XML declaration
  10463.  
  10464. =item set_doctype ($name, $system, $public, $internal)
  10465.  
  10466. Set the doctype of the element. If an argument is C<undef> (or not present)
  10467. then its former value is retained, if a false ('' or 0) value is passed then
  10468. the former value is deleted;
  10469.  
  10470. =item entity_list
  10471.  
  10472. Return the entity list of a twig
  10473.  
  10474. =item entity_names
  10475.  
  10476. Return the list of all defined entities
  10477.  
  10478. =item entity ($entity_name)
  10479.  
  10480. Return the entity 
  10481.  
  10482. =item change_gi      ($old_gi, $new_gi)
  10483.  
  10484. Performs a (very fast) global change. All elements C<$old_gi> are now 
  10485. C<$new_gi>. This is a bit dangerous though and should be avoided if
  10486. < possible, as the new tag might be ignored in subsequent processing.
  10487.  
  10488. See C<L<BUGS|/BUGS> >
  10489.  
  10490. =item flush            ($optional_filehandle, %options)
  10491.  
  10492. Flushes a twig up to (and including) the current element, then deletes
  10493. all unnecessary elements from the tree that's kept in memory.
  10494. C<flush> keeps track of which elements need to be open/closed, so if you
  10495. flush from handlers you don't have to worry about anything. Just keep 
  10496. flushing the twig every time you're done with a sub-tree and it will
  10497. come out well-formed. After the whole parsing don't forget toC<flush> 
  10498. one more time to print the end of the document.
  10499. The doctype and entity declarations are also printed.
  10500.  
  10501. flush take an optional filehandle as an argument.
  10502.  
  10503. options: use the C<update_DTD> option if you have updated the (internal) DTD 
  10504. and/or the entity list and you want the updated DTD to be output 
  10505.  
  10506. The C<pretty_print> option sets the pretty printing of the document.
  10507.  
  10508.    Example: $t->flush( Update_DTD => 1);
  10509.             $t->flush( $filehandle, pretty_print => 'indented');
  10510.             $t->flush( \*FILE);
  10511.  
  10512.  
  10513. =item flush_up_to ($elt, $optional_filehandle, %options)
  10514.  
  10515. Flushes up to the C<$elt> element. This allows you to keep part of the
  10516. tree in memory when you C<flush>.
  10517.  
  10518. options: see flush.
  10519.  
  10520. =item purge
  10521.  
  10522. Does the same as a C<flush> except it does not print the twig. It just deletes
  10523. all elements that have been completely parsed so far.
  10524.  
  10525. =item purge_up_to ($elt)
  10526.  
  10527. Purges up to the C<$elt> element. This allows you to keep part of the tree in 
  10528. memory when you C<purge>.
  10529.  
  10530. =item print            ($optional_filehandle, %options)
  10531.  
  10532. Prints the whole document associated with the twig. To be used only AFTER the
  10533. parse.
  10534.  
  10535. options: see C<flush>.
  10536.  
  10537. =item print_to_file    ($filename, %options)
  10538.  
  10539. Prints the whole document associated with the twig to file C<$filename>.
  10540. To be used only AFTER the parse.
  10541.  
  10542. options: see C<flush>.
  10543.  
  10544. =item sprint
  10545.  
  10546. Return the text of the whole document associated with the twig. To be used only
  10547. AFTER the parse.
  10548.  
  10549. options: see C<flush>.
  10550.  
  10551. =item trim
  10552.  
  10553. Trim the document: gets rid of initial and trailing spaces, and replaces multiple spaces
  10554. by a single one.
  10555.  
  10556. =item toSAX1 ($handler)
  10557.  
  10558. Send SAX events for the twig to the SAX1 handler C<$handler>
  10559.  
  10560. =item toSAX2 ($handler)
  10561.  
  10562. Send SAX events for the twig to the SAX2 handler C<$handler>
  10563.  
  10564. =item flush_toSAX1 ($handler)
  10565.  
  10566. Same as flush, except that SAX events are sent to the SAX1 handler
  10567. C<$handler> instead of the twig being printed
  10568.  
  10569. =item flush_toSAX2 ($handler)
  10570.  
  10571. Same as flush, except that SAX events are sent to the SAX2 handler
  10572. C<$handler> instead of the twig being printed
  10573.  
  10574. =item ignore
  10575.  
  10576. This method should be called during parsing, usually in C<start_tag_handlers>.
  10577. It causes the element to be skipped during the parsing: the twig is not built
  10578. for this element, it will not be accessible during parsing or after it. The 
  10579. element will not take up any memory and parsing will be faster.
  10580.  
  10581. Note that this method can also be called on an element. If the element is a 
  10582. parent of the current element then this element will be ignored (the twig will
  10583. not be built any more for it and what has already been built will be deleted).
  10584.  
  10585. =item set_pretty_print  ($style)
  10586.  
  10587. Set the pretty print method, amongst 'C<none>' (default), 'C<nsgmls>', 
  10588. 'C<nice>', 'C<indented>', C<indented_c>, 'C<wrapped>', 'C<record>' and 
  10589. 'C<record_c>'
  10590.  
  10591. B<WARNING:> the pretty print style is a B<GLOBAL> variable, so once set it's
  10592. applied to B<ALL> C<print>'s (and C<sprint>'s). Same goes if you use XML::Twig
  10593. with C<mod_perl> . This should not be a problem as the XML that's generated 
  10594. is valid anyway, and XML processors (as well as HTML processors, including
  10595. browsers) should not care. Let me know if this is a big problem, but at the
  10596. moment the performance/cleanliness trade-off clearly favors the global 
  10597. approach.
  10598.  
  10599. =item set_empty_tag_style  ($style)
  10600.  
  10601. Set the empty tag display style ('C<normal>', 'C<html>' or 'C<expand>'). As 
  10602. with C<L<set_pretty_print>> this sets a global flag.  
  10603.  
  10604. C<normal> outputs an empty tag 'C<< <tag/> >>', C<html> adds a space 
  10605. 'C<< <tag /> >>' for elements that can be empty in XHTML and C<expand> outputs
  10606. 'C<< <tag></tag> >>'
  10607.  
  10608. =item set_remove_cdata  ($flag)
  10609.  
  10610. set (or unset) the flag that forces the twig to output CDATA sections as 
  10611. regular (escaped) PCDATA
  10612.  
  10613. =item print_prolog     ($optional_filehandle, %options)
  10614.  
  10615. Prints the prolog (XML declaration + DTD + entity declarations) of a document.
  10616.  
  10617. options: see C<L<flush>>.
  10618.  
  10619. =item prolog     ($optional_filehandle, %options)
  10620.  
  10621. Return the prolog (XML declaration + DTD + entity declarations) of a document.
  10622.  
  10623. options: see C<L<flush>>.
  10624.  
  10625. =item finish
  10626.  
  10627. Call Expat C<finish> method.
  10628. Unsets all handlers (including internal ones that set context), but expat
  10629. continues parsing to the end of the document or until it finds an error.
  10630. It should finish up a lot faster than with the handlers set.
  10631.  
  10632. =item finish_print
  10633.  
  10634. Stops twig processing, flush the twig and proceed to finish printing the 
  10635. document as fast as possible. Use this method when modifying a document and 
  10636. the modification is done. 
  10637.  
  10638. =item finish_now
  10639.  
  10640. Stops twig processing, does not finish parsing the document (which could
  10641. actually be not well-formed after the point where C<finish_now> is called).
  10642. Execution resumes after the C<Lparse>> or C<L<parsefile>> call. The content
  10643. of the twig is what has been parsed so far (all open elements at the time 
  10644. C<finish_now> is called are considered closed).
  10645.  
  10646. =item set_expand_external_entities
  10647.  
  10648. Same as using the C<L<expand_external_ents>> option when creating the twig
  10649.  
  10650. =item set_input_filter
  10651.  
  10652. Same as using the C<L<input_filter>> option when creating the twig
  10653.  
  10654. =item set_keep_atts_order
  10655.  
  10656. Same as using the C<L<keep_atts_order>> option when creating the twig
  10657.  
  10658. =item set_keep_encoding
  10659.  
  10660. Same as using the C<L<keep_encoding>> option when creating the twig
  10661.  
  10662. =item escape_gt
  10663.  
  10664. usually XML::Twig does not escape > in its output. Using this option
  10665. makes it replace > by >
  10666.  
  10667. =item do_not_escape_gt
  10668.  
  10669. reverts XML::Twig behavior to its default of not escaping > in its output.
  10670.  
  10671. =item set_output_filter
  10672.  
  10673. Same as using the C<L<output_filter>> option when creating the twig
  10674.  
  10675. =item set_output_text_filter
  10676.  
  10677. Same as using the C<L<output_text_filter>> option when creating the twig
  10678.  
  10679. =item add_stylesheet ($type, @options)
  10680.  
  10681. Adds an external stylesheet to an XML document.
  10682.  
  10683. Supported types and options:
  10684.  
  10685. =over 4
  10686.  
  10687. =item xsl
  10688.  
  10689. option: the url of the stylesheet
  10690.  
  10691. Example:
  10692.  
  10693.   $t->add_stylesheet( xsl => "xsl_style.xsl");
  10694.  
  10695. will generate the following PI at the beginning of the document:
  10696.  
  10697.   <?xml-stylesheet type="text/xsl" href="xsl_style.xsl"?>
  10698.  
  10699. =item css
  10700.  
  10701. option: the url of the stylesheet
  10702.  
  10703.  
  10704. =back
  10705.  
  10706. =item Methods inherited from XML::Parser::Expat
  10707.  
  10708. A twig inherits all the relevant methods from XML::Parser::Expat. These 
  10709. methods can only be used during the parsing phase (they will generate
  10710. a fatal error otherwise).
  10711.  
  10712. Inherited methods are:
  10713.  
  10714. =over 4
  10715.  
  10716. =item depth
  10717.  
  10718. Returns the size of the context list.
  10719.  
  10720. =item in_element
  10721.  
  10722. Returns true if NAME is equal to the name of the innermost
  10723. currently opened element. If namespace processing is being used and
  10724. you want to check against a name that may be in a namespace, then
  10725. use the generate_ns_name method to create the NAME argument.
  10726.  
  10727. =item within_element
  10728.  
  10729. Returns the number of times the given name appears in the context
  10730. list.  If namespace processing is being used and you want to check
  10731. against a name that may be in a namespace, then use the
  10732. generate_ns_name method to create the NAME argument.
  10733.  
  10734. =item context
  10735.  
  10736. Returns a list of element names that represent open elements, with
  10737. the last one being the innermost. Inside start and end tag
  10738. handlers, this will be the tag of the parent element.
  10739.  
  10740. =item current_line
  10741.  
  10742. Returns the line number of the current position of the parse.
  10743.  
  10744. =item current_column
  10745.  
  10746. Returns the column number of the current position of the parse.
  10747.  
  10748. =item current_byte
  10749.  
  10750. Returns the current position of the parse.
  10751.  
  10752. =item position_in_context
  10753.  
  10754. Returns a string that shows the current parse position. LINES
  10755. should be an integer >= 0 that represents the number of lines on
  10756. either side of the current parse line to place into the returned
  10757. string.
  10758.  
  10759. =item base ([NEWBASE])
  10760.  
  10761. Returns the current value of the base for resolving relative URIs.
  10762. If NEWBASE is supplied, changes the base to that value.
  10763.  
  10764. =item current_element
  10765.  
  10766. Returns the name of the innermost currently opened element. Inside
  10767. start or end handlers, returns the parent of the element associated
  10768. with those tags.
  10769.  
  10770. =item element_index
  10771.  
  10772. Returns an integer that is the depth-first visit order of the
  10773. current element. This will be zero outside of the root element. For
  10774. example, this will return 1 when called from the start handler for
  10775. the root element start tag.
  10776.  
  10777. =item recognized_string
  10778.  
  10779. Returns the string from the document that was recognized in order
  10780. to call the current handler. For instance, when called from a start
  10781. handler, it will give us the the start-tag string. The string is
  10782. encoded in UTF-8.  This method doesn't return a meaningful string
  10783. inside declaration handlers.
  10784.  
  10785. =item original_string
  10786.  
  10787. Returns the verbatim string from the document that was recognized
  10788. in order to call the current handler. The string is in the original
  10789. document encoding. This method doesn't return a meaningful string
  10790. inside declaration handlers.
  10791.  
  10792. =item xpcroak
  10793.  
  10794. Concatenate onto the given message the current line number within
  10795. the XML document plus the message implied by ErrorContext. Then
  10796. croak with the formed message.
  10797.  
  10798. =item xpcarp 
  10799.  
  10800. Concatenate onto the given message the current line number within
  10801. the XML document plus the message implied by ErrorContext. Then
  10802. carp with the formed message.
  10803.  
  10804. =item xml_escape(TEXT [, CHAR [, CHAR ...]])
  10805.  
  10806. Returns TEXT with markup characters turned into character entities.
  10807. Any additional characters provided as arguments are also turned
  10808. into character references where found in TEXT.
  10809.  
  10810. (this method is broken on some versions of expat/XML::Parser)
  10811.  
  10812. =back
  10813.  
  10814. =item path ( $optional_tag)
  10815.  
  10816. Return the element context in a form similar to XPath's short
  10817. form: 'C</root/tag1/../tag>'
  10818.  
  10819. =item get_xpath  ( $optional_array_ref, $xpath, $optional_offset)
  10820.  
  10821. Performs a C<get_xpath> on the document root (see <Elt|"Elt">)
  10822.  
  10823. If the C<$optional_array_ref> argument is used the array must contain
  10824. elements. The C<$xpath> expression is applied to each element in turn 
  10825. and the result is union of all results. This way a first query can be
  10826. refined in further steps.
  10827.  
  10828.  
  10829. =item find_nodes ( $optional_array_ref, $xpath, $optional_offset)
  10830.  
  10831. same as C<get_xpath> 
  10832.  
  10833. =item findnodes ( $optional_array_ref, $xpath, $optional_offset)
  10834.  
  10835. same as C<get_xpath> (similar to the XML::LibXML method)
  10836.  
  10837. =item findvalue ( $optional_array_ref, $xpath, $optional_offset)
  10838.  
  10839. Return the C<join> of all texts of the results of applying C<L<get_xpath>>
  10840. to the node (similar to the XML::LibXML method)
  10841.  
  10842. =item subs_text ($regexp, $replace)
  10843.  
  10844. subs_text does text substitution on the whole document, similar to perl's 
  10845. C< s///> operator.
  10846.  
  10847. =item dispose
  10848.  
  10849. Useful only if you don't have C<Scalar::Util> or C<WeakRef> installed.
  10850.  
  10851. Reclaims properly the memory used by an XML::Twig object. As the object has
  10852. circular references it never goes out of scope, so if you want to parse lots 
  10853. of XML documents then the memory leak becomes a problem. Use
  10854. C<< $twig->dispose >> to clear this problem.
  10855.  
  10856. =item create_accessors (list_of_attribute_names)
  10857.  
  10858. A convenience method that creates l-valued accessors for attributes. 
  10859. So C<< $twig->create_accessors( 'foo') >> will create a C<foo> method
  10860. that can be called on elements:
  10861.  
  10862.   $elt->foo;         # equivalent to $elt->{'att'}->{'foo'};
  10863.   $elt->foo( 'bar'); # equivalent to $elt->set_att( foo => 'bar');
  10864.  
  10865. =item set_do_not_escape_amp_in_atts
  10866.  
  10867. An evil method, that I only document because Test::Pod::Coverage complaints otherwise,
  10868. but really, you don't want to know about it.
  10869.  
  10870. =back 
  10871.  
  10872. =head2 XML::Twig::Elt
  10873.  
  10874. =over 4
  10875.  
  10876. =item new          ($optional_tag, $optional_atts, @optional_content)
  10877.  
  10878. The C<tag> is optional (but then you can't have a content ), the C<$optional_atts> 
  10879. argument is a reference to a hash of attributes, the content can be just a 
  10880. string or a list of strings and element. A content of 'C<#EMPTY>' creates an empty 
  10881. element;
  10882.  
  10883.  Examples: my $elt= XML::Twig::Elt->new();
  10884.            my $elt= XML::Twig::Elt->new( para => { align => 'center' });  
  10885.            my $elt= XML::Twig::Elt->new( para => { align => 'center' }, 'foo');  
  10886.            my $elt= XML::Twig::Elt->new( br   => '#EMPTY');
  10887.            my $elt= XML::Twig::Elt->new( 'para');
  10888.            my $elt= XML::Twig::Elt->new( para => 'this is a para');  
  10889.            my $elt= XML::Twig::Elt->new( para => $elt3, 'another para'); 
  10890.  
  10891. The strings are not parsed, the element is not attached to any twig.
  10892.  
  10893. B<WARNING>: if you rely on ID's then you will have to set the id yourself. At
  10894. this point the element does not belong to a twig yet, so the ID attribute
  10895. is not known so it won't be stored in the ID list.
  10896.  
  10897. Note that C<#COMMENT>, C<#PCDATA> or C<#CDATA> are valid tag names, that will 
  10898. create text elements.
  10899.  
  10900. To create an element C<foo> containing a CDATA section:
  10901.  
  10902.            my $foo= XML::Twig::Elt->new( '#CDATA' => "content of the CDATA section")
  10903.                                   ->wrap_in( 'foo');
  10904.  
  10905. An attribute of '#CDATA', will create the content of the element as CDATA:
  10906.  
  10907.   my $elt= XML::Twig::Elt->new( 'p' => { '#CDATA' => 1}, 'foo < bar');
  10908.  
  10909. creates an element 
  10910.  
  10911.   <p><![CDATA[foo < bar]]></>
  10912.  
  10913. =item parse         ($string, %args)
  10914.  
  10915. Creates an element from an XML string. The string is actually
  10916. parsed as a new twig, then the root of that twig is returned.
  10917. The arguments in C<%args> are passed to the twig.
  10918. As always if the parse fails the parser will die, so use an
  10919. eval if you want to trap syntax errors.
  10920.  
  10921. As obviously the element does not exist beforehand this method has to be 
  10922. called on the class: 
  10923.  
  10924.   my $elt= parse XML::Twig::Elt( "<a> string to parse, with <sub/>
  10925.                                   <elements>, actually tons of </elements>
  10926.                   h</a>");
  10927.  
  10928. =item set_inner_xml ($string)
  10929.  
  10930. Sets the content of the element to be the tree created from the string
  10931.  
  10932. =item set_inner_html ($string)
  10933.  
  10934. Sets the content of the element, after parsing the string with an HTML
  10935. parser (HTML::Parser)
  10936.  
  10937. =item print         ($optional_filehandle, $optional_pretty_print_style)
  10938.  
  10939. Prints an entire element, including the tags, optionally to a 
  10940. C<$optional_filehandle>, optionally with a C<$pretty_print_style>.
  10941.  
  10942. The print outputs XML data so base entities are escaped.
  10943.  
  10944. =item sprint       ($elt, $optional_no_enclosing_tag)
  10945.  
  10946. Return the xml string for an entire element, including the tags. 
  10947. If the optional second argument is true then only the string inside the 
  10948. element is returned (the start and end tag for $elt are not).
  10949. The text is XML-escaped: base entities (& and < in text, & < and " in
  10950. attribute values) are turned into entities.
  10951.  
  10952. =item gi                       
  10953.  
  10954. Return the gi of the element (the gi is the C<generic identifier> the tag
  10955. name in SGML parlance).
  10956.  
  10957. C<tag> and C<name> are synonyms of C<gi>.
  10958.  
  10959. =item tag
  10960.  
  10961. Same as C<L<gi|/gi>>
  10962.  
  10963. =item name
  10964.  
  10965. Same as C<L<tag|/tag>>
  10966.  
  10967. =item set_gi         ($tag)
  10968.  
  10969. Set the gi (tag) of an element
  10970.  
  10971. =item set_tag        ($tag)
  10972.  
  10973. Set the tag (=C<L<tag|/tag>>) of an element
  10974.  
  10975. =item set_name       ($name)
  10976.  
  10977. Set the name (=C<L<tag|/tag>>) of an element
  10978.  
  10979. =item root 
  10980.  
  10981. Return the root of the twig in which the element is contained.
  10982.  
  10983. =item twig 
  10984.  
  10985. Return the twig containing the element. 
  10986.  
  10987. =item parent        ($optional_condition)
  10988.  
  10989. Return the parent of the element, or the first ancestor matching the 
  10990. C<$optional_condition>
  10991.  
  10992. =item first_child   ($optional_condition)
  10993.  
  10994. Return the first child of the element, or the first child matching the 
  10995. C<$optional_condition>
  10996.  
  10997. =item has_child ($optional_condition)
  10998.  
  10999. Return the first child of the element, or the first child matching the 
  11000. C<$optional_condition> (same as L<first_child>)
  11001.  
  11002. =item has_children ($optional_condition)
  11003.  
  11004. Return the first child of the element, or the first child matching the 
  11005. C<$optional_condition> (same as L<first_child>)
  11006.  
  11007.  
  11008. =item first_child_text   ($optional_condition)
  11009.  
  11010. Return the text of the first child of the element, or the first child
  11011.  matching the C<$optional_condition>
  11012. If there is no first_child then returns ''. This avoids getting the
  11013. child, checking for its existence then getting the text for trivial cases.
  11014.  
  11015. Similar methods are available for the other navigation methods: 
  11016.  
  11017. =over 4
  11018.  
  11019. =item last_child_text
  11020.  
  11021. =item prev_sibling_text
  11022.  
  11023. =item next_sibling_text
  11024.  
  11025. =item prev_elt_text
  11026.  
  11027. =item next_elt_text
  11028.  
  11029. =item child_text
  11030.  
  11031. =item parent_text
  11032.  
  11033. =back
  11034.  
  11035. All this methods also exist in "trimmed" variant: 
  11036.  
  11037. =over 4
  11038.  
  11039. =item first_child_trimmed_text
  11040.  
  11041. =item last_child_trimmed_text
  11042.  
  11043. =item prev_sibling_trimmed_text
  11044.  
  11045. =item next_sibling_trimmed_text
  11046.  
  11047. =item prev_elt_trimmed_text
  11048.  
  11049. =item next_elt_trimmed_text
  11050.  
  11051. =item child_trimmed_text
  11052.  
  11053. =item parent_trimmed_text
  11054.  
  11055. =back
  11056.  
  11057. =item field         ($condition)
  11058.  
  11059. Same method as C<first_child_text> with a different name
  11060.  
  11061. =item fields         ($condition_list)
  11062.  
  11063. Return the list of field (text of first child matching the conditions),
  11064. missing fields are returned as the empty string.
  11065.  
  11066. Same method as C<first_child_text> with a different name
  11067.  
  11068. =item trimmed_field         ($optional_condition)
  11069.  
  11070. Same method as C<first_child_trimmed_text> with a different name
  11071.  
  11072. =item set_field ($condition, $optional_atts, @list_of_elt_and_strings)
  11073.  
  11074. Set the content of the first child of the element that matches
  11075. C<$condition>, the rest of the arguments is the same as for C<L<set_content>>
  11076.  
  11077. If no child matches C<$condition> _and_ if C<$condition> is a valid
  11078. XML element name, then a new element by that name is created and 
  11079. inserted as the last child.
  11080.  
  11081. =item first_child_matches   ($optional_condition)
  11082.  
  11083. Return the element if the first child of the element (if it exists) passes
  11084. the C<$optional_condition> C<undef> otherwise
  11085.  
  11086.   if( $elt->first_child_matches( 'title')) ... 
  11087.  
  11088. is equivalent to
  11089.  
  11090.   if( $elt->{first_child} && $elt->{first_child}->passes( 'title')) 
  11091.  
  11092. C<first_child_is> is another name for this method
  11093.  
  11094. Similar methods are available for the other navigation methods: 
  11095.  
  11096. =over 4
  11097.  
  11098. =item last_child_matches
  11099.  
  11100. =item prev_sibling_matches
  11101.  
  11102. =item next_sibling_matches
  11103.  
  11104. =item prev_elt_matches
  11105.  
  11106. =item next_elt_matches
  11107.  
  11108. =item child_matches
  11109.  
  11110. =item parent_matches
  11111.  
  11112. =back
  11113.  
  11114. =item is_first_child ($optional_condition)
  11115.  
  11116. returns true (the element) if the element is the first child of its parent
  11117. (optionally that satisfies the C<$optional_condition>)
  11118.  
  11119. =item is_last_child ($optional_condition)
  11120.  
  11121. returns true (the element) if the element is the last child of its parent
  11122. (optionally that satisfies the C<$optional_condition>)
  11123.  
  11124. =item prev_sibling  ($optional_condition)
  11125.  
  11126. Return the previous sibling of the element, or the previous sibling matching
  11127. C<$optional_condition>
  11128.  
  11129. =item next_sibling  ($optional_condition)
  11130.  
  11131. Return the next sibling of the element, or the first one matching 
  11132. C<$optional_condition>.
  11133.  
  11134. =item next_elt     ($optional_elt, $optional_condition)
  11135.  
  11136. Return the next elt (optionally matching C<$optional_condition>) of the element. This 
  11137. is defined as the next element which opens after the current element opens.
  11138. Which usually means the first child of the element.
  11139. Counter-intuitive as it might look this allows you to loop through the
  11140. whole document by starting from the root.
  11141.  
  11142. The C<$optional_elt> is the root of a subtree. When the C<next_elt> is out of the
  11143. subtree then the method returns undef. You can then walk a sub tree with:
  11144.  
  11145.   my $elt= $subtree_root;
  11146.   while( $elt= $elt->next_elt( $subtree_root)
  11147.     { # insert processing code here
  11148.     }
  11149.  
  11150. =item prev_elt     ($optional_condition)
  11151.  
  11152. Return the previous elt (optionally matching C<$optional_condition>) of the
  11153. element. This is the first element which opens before the current one.
  11154. It is usually either the last descendant of the previous sibling or
  11155. simply the parent
  11156.  
  11157. =item next_n_elt   ($offset, $optional_condition)
  11158.  
  11159. Return the C<$offset>-th element that matches the C<$optional_condition> 
  11160.  
  11161. =item following_elt
  11162.  
  11163. Return the following element (as per the XPath following axis)
  11164.  
  11165. =item preceding_elt
  11166.  
  11167. Return the preceding element (as per the XPath preceding axis)
  11168.  
  11169. =item following_elts
  11170.  
  11171. Return the list of following elements (as per the XPath following axis)
  11172.  
  11173. =item preceding_elts
  11174.  
  11175. Return the pst of preceding elements (as per the XPath preceding axis)
  11176.  
  11177. =item children     ($optional_condition)
  11178.  
  11179. Return the list of children (optionally which matches C<$optional_condition>) of 
  11180. the element. The list is in document order.
  11181.  
  11182. =item children_count ($optional_condition)
  11183.  
  11184. Return the number of children of the element (optionally which matches 
  11185. C<$optional_condition>)
  11186.  
  11187. =item children_text ($optional_condition)
  11188.  
  11189. In array context, reeturns an array containing the text of children of the
  11190. element (optionally which matches C<$optional_condition>)
  11191.  
  11192. In scalar context, returns the concatenation of the text of children of
  11193. the element
  11194.  
  11195. =item children_trimmed_text ($optional_condition)
  11196.  
  11197. In array context, returns an array containing the trimmed text of children 
  11198. of the element (optionally which matches C<$optional_condition>)
  11199.  
  11200. In scalar context, returns the concatenation of the trimmed text of children of
  11201. the element
  11202.  
  11203.  
  11204. =item children_copy ($optional_condition)
  11205.  
  11206. Return a list of elements that are copies of the children of the element, 
  11207. optionally which matches C<$optional_condition>
  11208.  
  11209. =item descendants     ($optional_condition)
  11210.  
  11211. Return the list of all descendants (optionally which matches 
  11212. C<$optional_condition>) of the element. This is the equivalent of the 
  11213. C<getElementsByTagName> of the DOM (by the way, if you are really a DOM 
  11214. addict, you can use C<getElementsByTagName> instead)
  11215.  
  11216. =item getElementsByTagName ($optional_condition)
  11217.  
  11218. Same as C<L<descendants>>
  11219.  
  11220. =item find_by_tag_name ($optional_condition)
  11221.  
  11222. Same as C<L<descendants>>
  11223.  
  11224. =item descendants_or_self ($optional_condition)
  11225.  
  11226. Same as C<L<descendants>> except that the element itself is included in the list
  11227. if it matches the C<$optional_condition> 
  11228.  
  11229. =item first_descendant  ($optional_condition)
  11230.  
  11231. Return the first descendant of the element that matches the condition  
  11232.  
  11233. =item last_descendant  ($optional_condition)
  11234.  
  11235. Return the last descendant of the element that matches the condition  
  11236.  
  11237. =item ancestors    ($optional_condition)
  11238.  
  11239. Return the list of ancestors (optionally matching C<$optional_condition>) of the 
  11240. element.  The list is ordered from the innermost ancestor to the outermost one
  11241.  
  11242. NOTE: the element itself is not part of the list, in order to include it 
  11243. you will have to use ancestors_or_self
  11244.  
  11245. =item ancestors_or_self     ($optional_condition)
  11246.  
  11247. Return the list of ancestors (optionally matching C<$optional_condition>) of the 
  11248. element, including the element (if it matches the condition>).  
  11249. The list is ordered from the innermost ancestor to the outermost one
  11250.  
  11251. =item passes ($condition)
  11252.  
  11253. Return the element if it passes the C<$condition> 
  11254.  
  11255. =item att          ($att)
  11256.  
  11257. Return the value of attribute C<$att> or C<undef>
  11258.  
  11259. =item set_att      ($att, $att_value)
  11260.  
  11261. Set the attribute of the element to the given value
  11262.  
  11263. You can actually set several attributes this way:
  11264.  
  11265.   $elt->set_att( att1 => "val1", att2 => "val2");
  11266.  
  11267. =item del_att      ($att)
  11268.  
  11269. Delete the attribute for the element
  11270.  
  11271. You can actually delete several attributes at once:
  11272.  
  11273.   $elt->del_att( 'att1', 'att2', 'att3');
  11274.  
  11275. =item att_exists ($att)
  11276.  
  11277. Returns true if the attribute C<$att> exists for the element, false 
  11278. otherwise
  11279.  
  11280. =item cut
  11281.  
  11282. Cut the element from the tree. The element still exists, it can be copied
  11283. or pasted somewhere else, it is just not attached to the tree anymore.
  11284.  
  11285. Note that the "old" links to the parent, previous and next siblings can
  11286. still be accessed using the former_* methods
  11287.  
  11288. =item former_next_sibling
  11289.  
  11290. Returns the former next sibling of a cut node (or undef if the node has not been cut)
  11291.  
  11292. This makes it easier to write loops where you cut elements:
  11293.  
  11294.     my $child= $parent->first_child( 'achild');
  11295.     while( $child->{'att'}->{'cut'}) 
  11296.       { $child->cut; $child= $child->former_next_sibling; }
  11297.  
  11298. =item former_prev_sibling
  11299.  
  11300. Returns the former previous sibling of a cut node (or undef if the node has not been cut)
  11301.  
  11302. =item former_parent
  11303.  
  11304. Returns the former parent of a cut node (or undef if the node has not been cut)
  11305.  
  11306. =item cut_children ($optional_condition)
  11307.  
  11308. Cut all the children of the element (or all of those which satisfy the
  11309. C<$optional_condition>).
  11310.  
  11311. Return the list of children 
  11312.  
  11313. =item copy        ($elt)
  11314.  
  11315. Return a copy of the element. The copy is a "deep" copy: all sub elements of 
  11316. the element are duplicated.
  11317.  
  11318. =item paste       ($optional_position, $ref)
  11319.  
  11320. Paste a (previously C<cut> or newly generated) element. Die if the element
  11321. already belongs to a tree.
  11322.  
  11323. Note that the calling element is pasted:
  11324.  
  11325.   $child->paste( first_child => $existing_parent);
  11326.   $new_sibling->paste( after => $this_sibling_is_already_in_the_tree);
  11327.  
  11328. or
  11329.  
  11330.   my $new_elt= XML::Twig::Elt->new( tag => $content);
  11331.   $new_elt->paste( $position => $existing_elt);
  11332.  
  11333. Example:
  11334.  
  11335.   my $t= XML::Twig->new->parse( 'doc.xml')
  11336.   my $toc= $t->root->new( 'toc');
  11337.   $toc->paste( $t->root); # $toc is pasted as first child of the root 
  11338.   foreach my $title ($t->findnodes( '/doc/section/title'))
  11339.     { my $title_toc= $title->copy;
  11340.       # paste $title_toc as the last child of toc
  11341.       $title_toc->paste( last_child => $toc) 
  11342.     }
  11343.  
  11344. Position options:
  11345.  
  11346. =over 4
  11347.  
  11348. =item first_child (default)
  11349.  
  11350. The element is pasted as the first child of C<$ref>
  11351.  
  11352. =item last_child
  11353.  
  11354. The element is pasted as the last child of C<$ref>
  11355.  
  11356. =item before
  11357.  
  11358. The element is pasted before C<$ref>, as its previous sibling.
  11359.  
  11360. =item after
  11361.  
  11362. The element is pasted after C<$ref>, as its next sibling.
  11363.  
  11364. =item within
  11365.  
  11366. In this case an extra argument, C<$offset>, should be supplied. The element
  11367. will be pasted in the reference element (or in its first text child) at the
  11368. given offset. To achieve this the reference element will be split at the 
  11369. offset.
  11370.  
  11371. =back
  11372.  
  11373. Note that you can call directly the underlying method:
  11374.  
  11375. =over 4
  11376.  
  11377. =item paste_before
  11378.  
  11379. =item paste_after
  11380.  
  11381. =item paste_first_child
  11382.  
  11383. =item paste_last_child
  11384.  
  11385. =item paste_within
  11386.  
  11387. =back
  11388.  
  11389. =item move       ($optional_position, $ref)
  11390.  
  11391. Move an element in the tree.
  11392. This is just a C<cut> then a C<paste>.  The syntax is the same as C<paste>.
  11393.  
  11394. =item replace       ($ref)
  11395.  
  11396. Replaces an element in the tree. Sometimes it is just not possible toC<cut> 
  11397. an element then C<paste> another in its place, so C<replace> comes in handy.
  11398. The calling element replaces C<$ref>.
  11399.  
  11400. =item replace_with   (@elts)
  11401.  
  11402. Replaces the calling element with one or more elements 
  11403.  
  11404. =item delete
  11405.  
  11406. Cut the element and frees the memory.
  11407.  
  11408. =item prefix       ($text, $optional_option)
  11409.  
  11410. Add a prefix to an element. If the element is a C<PCDATA> element the text
  11411. is added to the pcdata, if the elements first child is a C<PCDATA> then the
  11412. text is added to it's pcdata, otherwise a new C<PCDATA> element is created 
  11413. and pasted as the first child of the element.
  11414.  
  11415. If the option is C<asis> then the prefix is added asis: it is created in
  11416. a separate C<PCDATA> element with an C<asis> property. You can then write:
  11417.  
  11418.   $elt1->prefix( '<b>', 'asis');
  11419.  
  11420. to create a C<< <b> >> in the output of C<print>.
  11421.  
  11422. =item suffix       ($text, $optional_option)
  11423.  
  11424. Add a suffix to an element. If the element is a C<PCDATA> element the text
  11425. is added to the pcdata, if the elements last child is a C<PCDATA> then the
  11426. text is added to it's pcdata, otherwise a new PCDATA element is created 
  11427. and pasted as the last child of the element.
  11428.  
  11429. If the option is C<asis> then the suffix is added asis: it is created in
  11430. a separate C<PCDATA> element with an C<asis> property. You can then write:
  11431.  
  11432.   $elt2->suffix( '</b>', 'asis');
  11433.  
  11434. =item trim
  11435.  
  11436. Trim the element in-place: spaces at the beginning and at the end of the element
  11437. are discarded and multiple spaces within the element (or its descendants) are 
  11438. replaced by a single space.
  11439.  
  11440. Note that in some cases you can still end up with multiple spaces, if they are
  11441. split between several elements:
  11442.  
  11443.   <doc>  text <b>  hah! </b>  yep</doc>
  11444.  
  11445. gets trimmed to
  11446.  
  11447.   <doc>text <b> hah! </b> yep</doc>
  11448.  
  11449. This is somewhere in between a bug and a feature.
  11450.  
  11451. =item normalize
  11452.  
  11453. merge together all consecutive pcdata elements in the element (if for example
  11454. you have turned some elements into pcdata using C<L<erase>>, this will give you
  11455. a "clean" element in which there all text fragments are as long as possible).
  11456.  
  11457.  
  11458. =item simplify (%options)
  11459.  
  11460. Return a data structure suspiciously similar to XML::Simple's. Options are
  11461. identical to XMLin options, see XML::Simple doc for more details (or use
  11462. DATA::dumper or YAML to dump the data structure)
  11463.  
  11464. =over 4
  11465.  
  11466. =item content_key
  11467.  
  11468. =item forcearray 
  11469.  
  11470. =item keyattr 
  11471.  
  11472. =item noattr 
  11473.  
  11474. =item normalize_space
  11475.  
  11476. aka normalise_space
  11477.  
  11478. =item variables (%var_hash)
  11479.  
  11480. %var_hash is a hash { name => value }
  11481.  
  11482. This option allows variables in the XML to be expanded when the file is read. (there is no facility for putting the variable names back if you regenerate XML using XMLout).
  11483.  
  11484. A 'variable' is any text of the form ${name} (or $name) which occurs in an attribute value or in the text content of an element. If 'name' matches a key in the supplied hashref, ${name} will be replaced with the corresponding value from the hashref. If no matching key is found, the variable will not be replaced. 
  11485.  
  11486. =item var_att ($attribute_name)
  11487.  
  11488. This option gives the name of an attribute that will be used to create 
  11489. variables in the XML:
  11490.  
  11491.   <dirs>
  11492.     <dir name="prefix">/usr/local</dir>
  11493.     <dir name="exec_prefix">$prefix/bin</dir>
  11494.   </dirs>
  11495.  
  11496. use C<< var => 'name' >> to get $prefix replaced by /usr/local in the
  11497. generated data structure  
  11498.  
  11499. By default variables are captured by the following regexp: /$(\w+)/
  11500.  
  11501. =item var_regexp (regexp)
  11502.  
  11503. This option changes the regexp used to capture variables. The variable
  11504. name should be in $1
  11505.  
  11506. =item group_tags { grouping tag => grouped tag, grouping tag 2 => grouped tag 2...}
  11507.  
  11508. Option used to simplify the structure: elements listed will not be used.
  11509. Their children will be, they will be considered children of the element
  11510. parent.
  11511.  
  11512. If the element is:
  11513.  
  11514.   <config host="laptop.xmltwig.com">
  11515.     <server>localhost</server>
  11516.     <dirs>
  11517.       <dir name="base">/home/mrodrigu/standards</dir>
  11518.       <dir name="tools">$base/tools</dir>
  11519.     </dirs>
  11520.     <templates>
  11521.       <template name="std_def">std_def.templ</template>
  11522.       <template name="dummy">dummy</template>
  11523.     </templates>
  11524.   </config>
  11525.  
  11526. Then calling simplify with C<< group_tags => { dirs => 'dir',
  11527. templates => 'template'} >>
  11528. makes the data structure be exactly as if the start and end tags for C<dirs> and
  11529. C<templates> were not there.
  11530.  
  11531. A YAML dump of the structure 
  11532.  
  11533.   base: '/home/mrodrigu/standards'
  11534.   host: laptop.xmltwig.com
  11535.   server: localhost
  11536.   template:
  11537.     - std_def.templ
  11538.     - dummy.templ
  11539.   tools: '$base/tools'
  11540.  
  11541.  
  11542. =back
  11543.  
  11544. =item split_at        ($offset)
  11545.  
  11546. Split a text (C<PCDATA> or C<CDATA>) element in 2 at C<$offset>, the original
  11547. element now holds the first part of the string and a new element holds the
  11548. right part. The new element is returned
  11549.  
  11550. If the element is not a text element then the first text child of the element
  11551. is split
  11552.  
  11553. =item split        ( $optional_regexp, $tag1, $atts1, $tag2, $atts2...)
  11554.  
  11555. Split the text descendants of an element in place, the text is split using 
  11556. the C<$regexp>, if the regexp includes () then the matched separators will be 
  11557. wrapped in elements.  C<$1> is wrapped in $tag1, with attributes C<$atts1> if
  11558. C<$atts1> is given (as a hashref), C<$2> is wrapped in $tag2... 
  11559.  
  11560. if $elt is C<< <p>tati tata <b>tutu tati titi</b> tata tati tata</p> >>
  11561.  
  11562.   $elt->split( qr/(ta)ti/, 'foo', {type => 'toto'} )
  11563.  
  11564. will change $elt to
  11565.  
  11566.   <p><foo type="toto">ta</foo> tata <b>tutu <foo type="toto">ta</foo>
  11567.       titi</b> tata <foo type="toto">ta</foo> tata</p> 
  11568.  
  11569. The regexp can be passed either as a string or as C<qr//> (perl 5.005 and 
  11570. later), it defaults to \s+ just as the C<split> built-in (but this would be 
  11571. quite a useless behaviour without the C<$optional_tag> parameter)
  11572.  
  11573. C<$optional_tag> defaults to PCDATA or CDATA, depending on the initial element
  11574. type
  11575.  
  11576. The list of descendants is returned (including un-touched original elements 
  11577. and newly created ones)
  11578.  
  11579. =item mark        ( $regexp, $optional_tag, $optional_attribute_ref)
  11580.  
  11581. This method behaves exactly as L<split|/split>, except only the newly created 
  11582. elements are returned
  11583.  
  11584. =item wrap_children ( $regexp_string, $tag, $optional_attribute_hashref)
  11585.  
  11586. Wrap the children of the element that match the regexp in an element C<$tag>.
  11587. If $optional_attribute_hashref is passed then the new element will
  11588. have these attributes.
  11589.  
  11590. The $regexp_string includes tags, within pointy brackets, as in 
  11591. C<< <title><para>+ >> and the usual Perl modifiers (+*?...). 
  11592. Tags can be further qualified with attributes:
  11593. C<< <para type="warning" classif="cosmic_secret">+ >>. The values
  11594. for attributes should be xml-escaped: C<< <candy type="M&Ms">* >>
  11595. (C<E<lt>>, C<&> B<C<E<gt>>> and C<"> should be escaped). 
  11596.  
  11597. Note that elements might get extra C<id> attributes in the process. See L<add_id>.
  11598. Use L<strip_att> to remove unwanted id's. 
  11599.  
  11600. Here is an example:
  11601.  
  11602. If the element C<$elt> has the following content:
  11603.  
  11604.   <elt>
  11605.    <p>para 1</p>
  11606.    <l_l1_1>list 1 item 1 para 1</l_l1_1>
  11607.      <l_l1>list 1 item 1 para 2</l_l1>
  11608.    <l_l1_n>list 1 item 2 para 1 (only para)</l_l1_n>
  11609.    <l_l1_n>list 1 item 3 para 1</l_l1_n>
  11610.      <l_l1>list 1 item 3 para 2</l_l1>
  11611.      <l_l1>list 1 item 3 para 3</l_l1>
  11612.    <l_l1_1>list 2 item 1 para 1</l_l1_1>
  11613.      <l_l1>list 2 item 1 para 2</l_l1>
  11614.    <l_l1_n>list 2 item 2 para 1 (only para)</l_l1_n>
  11615.    <l_l1_n>list 2 item 3 para 1</l_l1_n>
  11616.      <l_l1>list 2 item 3 para 2</l_l1>
  11617.      <l_l1>list 2 item 3 para 3</l_l1>
  11618.   </elt>
  11619.  
  11620. Then the code
  11621.  
  11622.   $elt->wrap_children( q{<l_l1_1><l_l1>*} , li => { type => "ul1" });
  11623.   $elt->wrap_children( q{<l_l1_n><l_l1>*} , li => { type => "ul" });
  11624.  
  11625.   $elt->wrap_children( q{<li type="ul1"><li type="ul">+}, "ul");
  11626.   $elt->strip_att( 'id');
  11627.   $elt->strip_att( 'type');
  11628.   $elt->print;
  11629.  
  11630. will output:
  11631.  
  11632.   <elt>
  11633.      <p>para 1</p>
  11634.      <ul>
  11635.        <li>
  11636.          <l_l1_1>list 1 item 1 para 1</l_l1_1>
  11637.          <l_l1>list 1 item 1 para 2</l_l1>
  11638.        </li>
  11639.        <li>
  11640.          <l_l1_n>list 1 item 2 para 1 (only para)</l_l1_n>
  11641.        </li>
  11642.        <li>
  11643.          <l_l1_n>list 1 item 3 para 1</l_l1_n>
  11644.          <l_l1>list 1 item 3 para 2</l_l1>
  11645.          <l_l1>list 1 item 3 para 3</l_l1>
  11646.        </li>
  11647.      </ul>
  11648.      <ul>
  11649.        <li>
  11650.          <l_l1_1>list 2 item 1 para 1</l_l1_1>
  11651.          <l_l1>list 2 item 1 para 2</l_l1>
  11652.        </li>
  11653.        <li>
  11654.          <l_l1_n>list 2 item 2 para 1 (only para)</l_l1_n>
  11655.        </li>
  11656.        <li>
  11657.          <l_l1_n>list 2 item 3 para 1</l_l1_n>
  11658.          <l_l1>list 2 item 3 para 2</l_l1>
  11659.          <l_l1>list 2 item 3 para 3</l_l1>
  11660.        </li>
  11661.      </ul>
  11662.   </elt>
  11663.  
  11664. =item subs_text ($regexp, $replace)
  11665.  
  11666. subs_text does text substitution, similar to perl's C< s///> operator.
  11667.  
  11668. C<$regexp> must be a perl regexp, created with the C<qr> operator.
  11669.  
  11670. C<$replace> can include C<$1, $2>... from the C<$regexp>. It can also be
  11671. used to create element and entities, by using 
  11672. C<< &elt( tag => { att => val }, text) >> (similar syntax as C<L<new>>) and
  11673. C<< &ent( name) >>.
  11674.  
  11675. Here is a rather complex example:
  11676.  
  11677.   $elt->subs_text( qr{(?<!do not )link to (http://([^\s,]*))},
  11678.                    'see &elt( a =>{ href => $1 }, $2)'
  11679.                  );
  11680.  
  11681. This will replace text like I<link to http://www.xmltwig.com> by 
  11682. I<< see <a href="www.xmltwig.com">www.xmltwig.com</a> >>, but not
  11683. I<do not link to...>
  11684.  
  11685. Generating entities (here replacing spaces with  ):
  11686.  
  11687.   $elt->subs_text( qr{ }, '&ent( " ")');
  11688.  
  11689. or, using a variable:
  11690.  
  11691.   my $ent=" ";
  11692.   $elt->subs_text( qr{ }, "&ent( '$ent')");
  11693.  
  11694. Note that the substitution is always global, as in using the C<g> modifier
  11695. in a perl substitution, and that it is performed on all text descendants
  11696. of the element.
  11697.  
  11698. B<Bug>: in the C<$regexp>, you can only use C<\1>, C<\2>... if the replacement
  11699. expression does not include elements or attributes. eg
  11700.  
  11701.   t->subs_text( qr/((t[aiou])\2)/, '$2');             # ok, replaces toto, tata, titi, tutu by to, ta, ti, tu
  11702.   t->subs_text( qr/((t[aiou])\2)/, '&elt(p => $1)' ); # NOK, does not find toto...
  11703.  
  11704. =item add_id ($optional_coderef)
  11705.  
  11706. Add an id to the element.
  11707.  
  11708. The id is an attribute, C<id> by default, see the C<id> option for XML::Twig
  11709. C<new> to change it. Use an id starting with C<#> to get an id that's not 
  11710. output by L<print>, L<flush> or L<sprint>, yet that allows you to use the
  11711. L<elt_id> method to get the element easily.
  11712.  
  11713. If the element already has an id, no new id is generated.
  11714.  
  11715. By default the method create an id of the form C<< twig_id_<nnnn> >>,
  11716. where C<< <nnnn> >> is a number, incremented each time the method is called
  11717. successfully.
  11718.  
  11719. =item set_id_seed ($prefix)
  11720.  
  11721. by default the id generated by C<L<add_id>> is C<< twig_id_<nnnn> >>, 
  11722. C<set_id_seed> changes the prefix to C<$prefix> and resets the number
  11723. to 1
  11724.  
  11725. =item strip_att ($att)
  11726.  
  11727. Remove the attribute C<$att> from all descendants of the element (including 
  11728. the element)
  11729.  
  11730. Return the element
  11731.  
  11732. =item change_att_name ($old_name, $new_name)
  11733.  
  11734. Change the name of the attribute from C<$old_name> to C<$new_name>. If there is no
  11735. attribute C<$old_name> nothing happens.
  11736.  
  11737. =item lc_attnames
  11738.  
  11739. Lower cases the name all the attributes of the element.
  11740.  
  11741. =item sort_children_on_value( %options)
  11742.  
  11743. Sort the children of the element in place according to their text.
  11744. All children are sorted. 
  11745.  
  11746. Return the element, with its children sorted.
  11747.  
  11748.  
  11749. C<%options> are
  11750.  
  11751.   type  : numeric |  alpha     (default: alpha)
  11752.   order : normal  |  reverse   (default: normal)
  11753.  
  11754. Return the element, with its children sorted
  11755.  
  11756.  
  11757. =item sort_children_on_att ($att, %options)
  11758.  
  11759. Sort the children of the  element in place according to attribute C<$att>. 
  11760. C<%options> are the same as for C<sort_children_on_value>
  11761.  
  11762. Return the element.
  11763.  
  11764.  
  11765. =item sort_children_on_field ($tag, %options)
  11766.  
  11767. Sort the children of the element in place, according to the field C<$tag> (the 
  11768. text of the first child of the child with this tag). C<%options> are the same
  11769. as for C<sort_children_on_value>.
  11770.  
  11771. Return the element, with its children sorted
  11772.  
  11773.  
  11774. =item sort_children( $get_key, %options) 
  11775.  
  11776. Sort the children of the element in place. The C<$get_key> argument is
  11777. a reference to a function that returns the sort key when passed an element.
  11778.  
  11779. For example:
  11780.  
  11781.   $elt->sort_children( sub { $_[0]->{'att'}->{"nb"} + $_[0]->text }, 
  11782.                        type => 'numeric', order => 'reverse'
  11783.                      );
  11784.  
  11785. =item field_to_att ($cond, $att)
  11786.  
  11787. Turn the text of the first sub-element matched by C<$cond> into the value of 
  11788. attribute C<$att> of the element. If C<$att> is omitted then C<$cond> is used 
  11789. as the name of the attribute, which makes sense only if C<$cond> is a valid
  11790. element (and attribute) name.
  11791.  
  11792. The sub-element is then cut.
  11793.  
  11794. =item att_to_field ($att, $tag)
  11795.  
  11796. Take the value of attribute C<$att> and create a sub-element C<$tag> as first
  11797. child of the element. If C<$tag> is omitted then C<$att> is used as the name of
  11798. the sub-element. 
  11799.  
  11800.  
  11801. =item get_xpath  ($xpath, $optional_offset)
  11802.  
  11803. Return a list of elements satisfying the C<$xpath>. C<$xpath> is an XPATH-like 
  11804. expression.
  11805.  
  11806. A subset of the XPATH abbreviated syntax is covered:
  11807.  
  11808.   tag
  11809.   tag[1] (or any other positive number)
  11810.   tag[last()]
  11811.   tag[@att] (the attribute exists for the element)
  11812.   tag[@att="val"]
  11813.   tag[@att=~ /regexp/]
  11814.   tag[att1="val1" and att2="val2"]
  11815.   tag[att1="val1" or att2="val2"]
  11816.   tag[string()="toto"] (returns tag elements which text (as per the text method) 
  11817.                        is toto)
  11818.   tag[string()=~/regexp/] (returns tag elements which text (as per the text 
  11819.                           method) matches regexp)
  11820.   expressions can start with / (search starts at the document root)
  11821.   expressions can start with . (search starts at the current element)
  11822.   // can be used to get all descendants instead of just direct children
  11823.   * matches any tag
  11824.  
  11825. So the following examples from the 
  11826. F<XPath recommendationL<http://www.w3.org/TR/xpath.html#path-abbrev>> work:
  11827.  
  11828.   para selects the para element children of the context node
  11829.   * selects all element children of the context node
  11830.   para[1] selects the first para child of the context node
  11831.   para[last()] selects the last para child of the context node
  11832.   */para selects all para grandchildren of the context node
  11833.   /doc/chapter[5]/section[2] selects the second section of the fifth chapter 
  11834.      of the doc 
  11835.   chapter//para selects the para element descendants of the chapter element 
  11836.      children of the context node
  11837.   //para selects all the para descendants of the document root and thus selects
  11838.      all para elements in the same document as the context node
  11839.   //olist/item selects all the item elements in the same document as the 
  11840.      context node that have an olist parent
  11841.   .//para selects the para element descendants of the context node
  11842.   .. selects the parent of the context node
  11843.   para[@type="warning"] selects all para children of the context node that have
  11844.      a type attribute with value warning 
  11845.   employee[@secretary and @assistant] selects all the employee children of the
  11846.      context node that have both a secretary attribute and an assistant 
  11847.      attribute
  11848.  
  11849.  
  11850. The elements will be returned in the document order.
  11851.  
  11852. If C<$optional_offset> is used then only one element will be returned, the one 
  11853. with the appropriate offset in the list, starting at 0
  11854.  
  11855. Quoting and interpolating variables can be a pain when the Perl syntax and the 
  11856. XPATH syntax collide, so use alternate quoting mechanisms like q or qq 
  11857. (I like q{} and qq{} myself).
  11858.  
  11859. Here are some more examples to get you started:
  11860.  
  11861.   my $p1= "p1";
  11862.   my $p2= "p2";
  11863.   my @res= $t->get_xpath( qq{p[string( "$p1") or string( "$p2")]});
  11864.  
  11865.   my $a= "a1";
  11866.   my @res= $t->get_xpath( qq{//*[@att="$a"]});
  11867.  
  11868.   my $val= "a1";
  11869.   my $exp= qq{//p[ \@att='$val']}; # you need to use \@ or you will get a warning
  11870.   my @res= $t->get_xpath( $exp);
  11871.  
  11872. Note that the only supported regexps delimiters are / and that you must 
  11873. backslash all / in regexps AND in regular strings.
  11874.  
  11875. XML::Twig does not provide natively full XPATH support, but you can use 
  11876. C<L<XML::Twig::XPath>> to get C<findnodes> to use C<XML::XPath> as the
  11877. XPath engine, with full coverage of the spec.
  11878.  
  11879. C<L<XML::Twig::XPath>> to get C<findnodes> to use C<XML::XPath> as the
  11880. XPath engine, with full coverage of the spec.
  11881.  
  11882. =item find_nodes
  11883.  
  11884. same asC<get_xpath> 
  11885.  
  11886. =item findnodes
  11887.  
  11888. same as C<get_xpath> 
  11889.  
  11890.  
  11891. =item text @optional_options
  11892.  
  11893. Return a string consisting of all the C<PCDATA> and C<CDATA> in an element, 
  11894. without any tags. The text is not XML-escaped: base entities such as C<&> 
  11895. and C<< < >> are not escaped.
  11896.  
  11897. The 'C<no_recurse>' option will only return the text of the element, not
  11898. of any included sub-elements (same as C<L<text_only>>).
  11899.  
  11900. =item text_only
  11901.  
  11902. Same as C<L<text>> except that the text returned doesn't include 
  11903. the text of sub-elements.
  11904.  
  11905. =item trimmed_text
  11906.  
  11907. Same as C<text> except that the text is trimmed: leading and trailing spaces
  11908. are discarded, consecutive spaces are collapsed
  11909.  
  11910. =item set_text        ($string)
  11911.  
  11912. Set the text for the element: if the element is a C<PCDATA>, just set its
  11913. text, otherwise cut all the children of the element and create a single
  11914. C<PCDATA> child for it, which holds the text.
  11915.  
  11916. =item merge ($elt2)
  11917.  
  11918. Move the content of C<$elt2> within the element
  11919.  
  11920. =item insert         ($tag1, [$optional_atts1], $tag2, [$optional_atts2],...)
  11921.  
  11922. For each tag in the list inserts an element C<$tag> as the only child of the 
  11923. element.  The element gets the optional attributes inC<< $optional_atts<n>. >> 
  11924. All children of the element are set as children of the new element.
  11925. The upper level element is returned.
  11926.  
  11927.   $p->insert( table => { border=> 1}, 'tr', 'td') 
  11928.  
  11929. put C<$p> in a table with a visible border, a single C<tr> and a single C<td> 
  11930. and return the C<table> element:
  11931.  
  11932.   <p><table border="1"><tr><td>original content of p</td></tr></table></p>
  11933.  
  11934. =item wrap_in        (@tag)
  11935.  
  11936. Wrap elements in C<@tag> as the successive ancestors of the element, returns the 
  11937. new element.
  11938. C<< $elt->wrap_in( 'td', 'tr', 'table') >> wraps the element as a single cell in a 
  11939. table for example.
  11940.  
  11941. Optionally each tag can be followed by a hashref of attributes, that will be 
  11942. set on the wrapping element:
  11943.  
  11944.   $elt->wrap_in( p => { class => "advisory" }, div => { class => "intro", id => "div_intro });
  11945.  
  11946. =item insert_new_elt ($opt_position, $tag, $opt_atts_hashref, @opt_content)
  11947.  
  11948. Combines a C<L<new|/new> > and a C<L<paste|/paste> >: creates a new element using 
  11949. C<$tag>, C<$opt_atts_hashref >and C<@opt_content> which are arguments similar 
  11950. to those for C<new>, then paste it, using C<$opt_position> or C<'first_child'>,
  11951. relative to C<$elt>.
  11952.  
  11953. Return the newly created element
  11954.  
  11955. =item erase
  11956.  
  11957. Erase the element: the element is deleted and all of its children are
  11958. pasted in its place.
  11959.  
  11960. =item set_content    ( $optional_atts, @list_of_elt_and_strings)
  11961.                      ( $optional_atts, '#EMPTY')
  11962.  
  11963. Set the content for the element, from a list of strings and
  11964. elements.  Cuts all the element children, then pastes the list
  11965. elements as the children.  This method will create a C<PCDATA> element
  11966. for any strings in the list.
  11967.  
  11968. The C<$optional_atts> argument is the ref of a hash of attributes. If this
  11969. argument is used then the previous attributes are deleted, otherwise they
  11970. are left untouched. 
  11971.  
  11972. B<WARNING>: if you rely on ID's then you will have to set the id yourself. At
  11973. this point the element does not belong to a twig yet, so the ID attribute
  11974. is not known so it won't be stored in the ID list.
  11975.  
  11976. A content of 'C<#EMPTY>' creates an empty element;
  11977.  
  11978. =item namespace ($optional_prefix)
  11979.  
  11980. Return the URI of the namespace that C<$optional_prefix> or the element name
  11981. belongs to. If the name doesn't belong to any namespace, C<undef> is returned.
  11982.  
  11983. =item local_name
  11984.  
  11985. Return the local name (without the prefix) for the element
  11986.  
  11987. =item ns_prefix
  11988.  
  11989. Return the namespace prefix for the element
  11990.  
  11991. =item current_ns_prefixes
  11992.  
  11993. Return a list of namespace prefixes valid for the element. The order of the
  11994. prefixes in the list has no meaning. If the default namespace is currently 
  11995. bound, '' appears in the list.
  11996.  
  11997.  
  11998. =item inherit_att  ($att, @optional_tag_list)
  11999.  
  12000. Return the value of an attribute inherited from parent tags. The value
  12001. returned is found by looking for the attribute in the element then in turn
  12002. in each of its ancestors. If the C<@optional_tag_list> is supplied only those
  12003. ancestors whose tag is in the list will be checked. 
  12004.  
  12005. =item all_children_are ($optional_condition)
  12006.  
  12007. return 1 if all children of the element pass the C<$optional_condition>, 
  12008. 0 otherwise
  12009.  
  12010. =item level       ($optional_condition)
  12011.  
  12012. Return the depth of the element in the twig (root is 0).
  12013. If C<$optional_condition> is given then only ancestors that match the condition are 
  12014. counted.
  12015.  
  12016. B<WARNING>: in a tree created using the C<twig_roots> option this will not return
  12017. the level in the document tree, level 0 will be the document root, level 1 
  12018. will be the C<twig_roots> elements. During the parsing (in a C<twig_handler>)
  12019. you can use the C<depth> method on the twig object to get the real parsing depth.
  12020.  
  12021. =item in           ($potential_parent)
  12022.  
  12023. Return true if the element is in the potential_parent (C<$potential_parent> is 
  12024. an element)
  12025.  
  12026. =item in_context   ($cond, $optional_level)
  12027.  
  12028. Return true if the element is included in an element which passes C<$cond>
  12029. optionally within C<$optional_level> levels. The returned value is the 
  12030. including element.
  12031.  
  12032. =item pcdata
  12033.  
  12034. Return the text of a C<PCDATA> element or C<undef> if the element is not 
  12035. C<PCDATA>.
  12036.  
  12037. =item pcdata_xml_string
  12038.  
  12039. Return the text of a C<PCDATA> element or undef if the element is not C<PCDATA>. 
  12040. The text is "XML-escaped" ('&' and '<' are replaced by '&' and '<')
  12041.  
  12042. =item set_pcdata     ($text)
  12043.  
  12044. Set the text of a C<PCDATA> element. This method does not check that the element is
  12045. indeed a C<PCDATA> so usually you should use C<L<set_text>> instead. 
  12046.  
  12047. =item append_pcdata  ($text)
  12048.  
  12049. Add the text at the end of a C<PCDATA> element.
  12050.  
  12051. =item is_cdata
  12052.  
  12053. Return 1 if the element is a C<CDATA> element, returns 0 otherwise.
  12054.  
  12055. =item is_text
  12056.  
  12057. Return 1 if the element is a C<CDATA> or C<PCDATA> element, returns 0 otherwise.
  12058.  
  12059. =item cdata
  12060.  
  12061. Return the text of a C<CDATA> element or C<undef> if the element is not 
  12062. C<CDATA>.
  12063.  
  12064. =item cdata_string
  12065.  
  12066. Return the XML string of a C<CDATA> element, including the opening and
  12067. closing markers.
  12068.  
  12069. =item set_cdata     ($text)
  12070.  
  12071. Set the text of a C<CDATA> element. 
  12072.  
  12073. =item append_cdata  ($text)
  12074.  
  12075. Add the text at the end of a C<CDATA> element.
  12076.  
  12077. =item remove_cdata
  12078.  
  12079. Turns all C<CDATA> sections in the element into regular C<PCDATA> elements. This is useful
  12080. when converting XML to HTML, as browsers do not support CDATA sections. 
  12081.  
  12082. =item extra_data 
  12083.  
  12084. Return the extra_data (comments and PI's) attached to an element
  12085.  
  12086. =item set_extra_data     ($extra_data)
  12087.  
  12088. Set the extra_data (comments and PI's) attached to an element
  12089.  
  12090. =item append_extra_data  ($extra_data)
  12091.  
  12092. Append extra_data to the existing extra_data before the element (if no
  12093. previous extra_data exists then it is created)
  12094.  
  12095. =item set_asis
  12096.  
  12097. Set a property of the element that causes it to be output without being XML
  12098. escaped by the print functions: if it contains C<< a < b >> it will be output
  12099. as such and not as C<< a < b >>. This can be useful to create text elements
  12100. that will be output as markup. Note that all C<PCDATA> descendants of the 
  12101. element are also marked as having the property (they are the ones that are
  12102. actually impacted by the change).
  12103.  
  12104. If the element is a C<CDATA> element it will also be output asis, without the
  12105. C<CDATA> markers. The same goes for any C<CDATA> descendant of the element
  12106.  
  12107. =item set_not_asis
  12108.  
  12109. Unsets the C<asis> property for the element and its text descendants.
  12110.  
  12111. =item is_asis
  12112.  
  12113. Return the C<asis> property status of the element ( 1 or C<undef>)
  12114.  
  12115. =item closed                   
  12116.  
  12117. Return true if the element has been closed. Might be useful if you are
  12118. somewhere in the tree, during the parse, and have no idea whether a parent
  12119. element is completely loaded or not.
  12120.  
  12121. =item get_type
  12122.  
  12123. Return the type of the element: 'C<#ELT>' for "real" elements, or 'C<#PCDATA>',
  12124. 'C<#CDATA>', 'C<#COMMENT>', 'C<#ENT>', 'C<#PI>'
  12125.  
  12126. =item is_elt
  12127.  
  12128. Return the tag if the element is a "real" element, or 0 if it is C<PCDATA>, 
  12129. C<CDATA>...
  12130.  
  12131. =item contains_only_text
  12132.  
  12133. Return 1 if the element does not contain any other "real" element
  12134.  
  12135. =item contains_only ($exp)
  12136.  
  12137. Return the list of children if all children of the element match
  12138. the expression C<$exp> 
  12139.  
  12140.   if( $para->contains_only( 'tt')) { ... }
  12141.  
  12142. =item contains_a_single ($exp)
  12143.  
  12144. If the element contains a single child that matches the expression C<$exp>
  12145. returns that element. Otherwise returns 0.
  12146.  
  12147. =item is_field
  12148.  
  12149. same as C<contains_only_text> 
  12150.  
  12151. =item is_pcdata
  12152.  
  12153. Return 1 if the element is a C<PCDATA> element, returns 0 otherwise.
  12154.  
  12155. =item is_ent
  12156.  
  12157. Return 1 if the element is an entity (an unexpanded entity) element, 
  12158. return 0 otherwise.
  12159.  
  12160. =item is_empty
  12161.  
  12162. Return 1 if the element is empty, 0 otherwise
  12163.  
  12164. =item set_empty
  12165.  
  12166. Flags the element as empty. No further check is made, so if the element
  12167. is actually not empty the output will be messed. The only effect of this 
  12168. method is that the output will be C<< <tag att="value""/> >>.
  12169.  
  12170. =item set_not_empty
  12171.  
  12172. Flags the element as not empty. if it is actually empty then the element will
  12173. be output as C<< <tag att="value""></tag> >>
  12174.  
  12175. =item is_pi
  12176.  
  12177. Return 1 if the element is a processing instruction (C<#PI>) element,
  12178. return 0 otherwise.
  12179.  
  12180. =item target
  12181.  
  12182. Return the target of a processing instruction
  12183.  
  12184. =item set_target ($target)
  12185.  
  12186. Set the target of a processing instruction
  12187.  
  12188. =item data
  12189.  
  12190. Return the data part of a processing instruction
  12191.  
  12192. =item set_data ($data)
  12193.  
  12194. Set the data of a processing instruction
  12195.  
  12196. =item set_pi ($target, $data)
  12197.  
  12198. Set the target and data of a processing instruction
  12199.  
  12200. =item pi_string
  12201.  
  12202. Return the string form of a processing instruction
  12203. (C<< <?target data?> >>)
  12204.  
  12205. =item is_comment
  12206.  
  12207. Return 1 if the element is a comment (C<#COMMENT>) element,
  12208. return 0 otherwise.
  12209.  
  12210. =item set_comment ($comment_text)
  12211.  
  12212. Set the text for a comment
  12213.  
  12214. =item comment
  12215.  
  12216. Return the content of a comment (just the text, not the C<< <!-- >>
  12217. and C<< --> >>)
  12218.  
  12219. =item comment_string 
  12220.  
  12221. Return the XML string for a comment (C<< <!-- comment --> >>)
  12222.  
  12223. =item set_ent ($entity)
  12224.  
  12225. Set an (non-expanded) entity (C<#ENT>). C<$entity>) is the entity
  12226. text (C<&ent;>)
  12227.  
  12228. =item ent
  12229.  
  12230. Return the entity for an entity (C<#ENT>) element (C<&ent;>)
  12231.  
  12232. =item ent_name
  12233.  
  12234. Return the entity name for an entity (C<#ENT>) element (C<ent>)
  12235.  
  12236. =item ent_string
  12237.  
  12238. Return the entity, either expanded if the expanded version is available,
  12239. or non-expanded (C<&ent;>) otherwise
  12240.  
  12241. =item child ($offset, $optional_condition)
  12242.  
  12243. Return the C<$offset>-th child of the element, optionally the C<$offset>-th 
  12244. child that matches C<$optional_condition>. The children are treated as a list, so 
  12245. C<< $elt->child( 0) >> is the first child, while C<< $elt->child( -1) >> is 
  12246. the last child.
  12247.  
  12248. =item child_text ($offset, $optional_condition)
  12249.  
  12250. Return the text of a child or C<undef> if the sibling does not exist. Arguments
  12251. are the same as child.
  12252.  
  12253. =item last_child    ($optional_condition)
  12254.  
  12255. Return the last child of the element, or the last child matching 
  12256. C<$optional_condition> (ie the last of the element children matching
  12257. the condition).
  12258.  
  12259. =item last_child_text   ($optional_condition)
  12260.  
  12261. Same as C<first_child_text> but for the last child.
  12262.  
  12263. =item sibling  ($offset, $optional_condition)
  12264.  
  12265. Return the next or previous C<$offset>-th sibling of the element, or the 
  12266. C<$offset>-th one matching C<$optional_condition>. If C<$offset> is negative then a 
  12267. previous sibling is returned, if $offset is positive then  a next sibling is 
  12268. returned. C<$offset=0> returns the element if there is no condition or
  12269. if the element matches the condition>, C<undef> otherwise.
  12270.  
  12271. =item sibling_text ($offset, $optional_condition)
  12272.  
  12273. Return the text of a sibling or C<undef> if the sibling does not exist. 
  12274. Arguments are the same as C<sibling>.
  12275.  
  12276. =item prev_siblings ($optional_condition)
  12277.  
  12278. Return the list of previous siblings (optionally matching C<$optional_condition>)
  12279. for the element. The elements are ordered in document order.
  12280.  
  12281. =item next_siblings ($optional_condition)
  12282.  
  12283. Return the list of siblings (optionally matching C<$optional_condition>)
  12284. following the element. The elements are ordered in document order.
  12285.  
  12286. =item pos ($optional_condition)
  12287.  
  12288. Return the position of the element in the children list. The first child has a
  12289. position of 1 (as in XPath).
  12290.  
  12291. If the C<$optional_condition> is given then only siblings that match the condition 
  12292. are counted. If the element itself does not match the  condition then
  12293. 0 is returned.
  12294.  
  12295. =item atts
  12296.  
  12297. Return a hash ref containing the element attributes
  12298.  
  12299. =item set_atts      ({ att1=>$att1_val, att2=> $att2_val... })
  12300.  
  12301. Set the element attributes with the hash ref supplied as the argument. The previous 
  12302. attributes are lost (ie the attributes set by C<set_atts> replace all of the
  12303. attributes of the element).
  12304.  
  12305. You can also pass a list instead of a hashref: C<< $elt->set_atts( att1 => 'val1',...) >>
  12306.  
  12307. =item del_atts
  12308.  
  12309. Deletes all the element attributes.
  12310.  
  12311. =item att_nb
  12312.  
  12313. Return the number of attributes for the element
  12314.  
  12315. =item has_atts
  12316.  
  12317. Return true if the element has attributes (in fact return the number of
  12318. attributes, thus being an alias to C<L<att_nb>>
  12319.  
  12320. =item has_no_atts
  12321.  
  12322. Return true if the element has no attributes, false (0) otherwise
  12323.  
  12324. =item att_names
  12325.  
  12326. return a list of the attribute names for the element
  12327.  
  12328. =item att_xml_string ($att, $options)
  12329.  
  12330. Return the attribute value, where '&', '<' and quote (" or the value of the quote option
  12331. at twig creation) are XML-escaped. 
  12332.  
  12333. The options are passed as a hashref, setting C<escape_gt> to a true value will also escape 
  12334. '>' ($elt( 'myatt', { escape_gt => 1 });
  12335.  
  12336. =item set_id       ($id)
  12337.  
  12338. Set the C<id> attribute of the element to the value.
  12339. See C<L<elt_id|/elt_id> > to change the id attribute name
  12340.  
  12341. =item id
  12342.  
  12343. Gets the id attribute value
  12344.  
  12345. =item del_id       ($id)
  12346.  
  12347. Deletes the C<id> attribute of the element and remove it from the id list
  12348. for the document
  12349.  
  12350. =item class
  12351.  
  12352. Return the C<class> attribute for the element (methods on the C<class>
  12353. attribute are quite convenient when dealing with XHTML, or plain XML that
  12354. will eventually be displayed using CSS)
  12355.  
  12356. =item set_class ($class)
  12357.  
  12358. Set the C<class> attribute for the element to C<$class>
  12359.  
  12360. =item add_to_class ($class)
  12361.  
  12362. Add C<$class> to the element C<class> attribute: the new class is added
  12363. only if it is not already present. Note that classes are sorted alphabetically,
  12364. so the C<class> attribute can be changed even if the class is already there
  12365.  
  12366. =item att_to_class ($att)
  12367.  
  12368. Set the C<class> attribute to the value of attribute C<$att>
  12369.  
  12370. =item add_att_to_class ($att)
  12371.  
  12372. Add the value of attribute C<$att> to the C<class> attribute of the element
  12373.  
  12374. =item move_att_to_class ($att)
  12375.  
  12376. Add the value of attribute C<$att> to the C<class> attribute of the element
  12377. and delete the attribute
  12378.  
  12379. =item tag_to_class
  12380.  
  12381. Set the C<class> attribute of the element to the element tag
  12382.  
  12383. =item add_tag_to_class
  12384.  
  12385. Add the element tag to its C<class> attribute
  12386.  
  12387. =item set_tag_class ($new_tag)
  12388.  
  12389. Add the element tag to its C<class> attribute and sets the tag to C<$new_tag>
  12390.  
  12391. =item in_class ($class)
  12392.  
  12393. Return true (C<1>) if the element is in the class C<$class> (if C<$class> is
  12394. one of the tokens in the element C<class> attribute)
  12395.  
  12396. =item tag_to_span
  12397.  
  12398. Change the element tag tp C<span> and set its class to the old tag
  12399.  
  12400. =item tag_to_div
  12401.  
  12402. Change the element tag tp C<div> and set its class to the old tag
  12403.  
  12404. =item DESTROY
  12405.  
  12406. Frees the element from memory.
  12407.  
  12408. =item start_tag
  12409.  
  12410. Return the string for the start tag for the element, including 
  12411. the C<< /> >> at the end of an empty element tag
  12412.  
  12413. =item end_tag
  12414.  
  12415. Return the string for the end tag of an element.  For an empty
  12416. element, this returns the empty string ('').
  12417.  
  12418. =item xml_string @optional_options
  12419.  
  12420. Equivalent to C<< $elt->sprint( 1) >>, returns the string for the entire 
  12421. element, excluding the element's tags (but nested element tags are present)
  12422.  
  12423. The 'C<no_recurse>' option will only return the text of the element, not
  12424. of any included sub-elements (same as C<L<xml_text_only>>).
  12425.  
  12426. =item inner_xml
  12427.  
  12428. Another synonym for xml_string
  12429.  
  12430. =item outer_xml
  12431.  
  12432. Another synonym for sprint
  12433.  
  12434. =item xml_text 
  12435.  
  12436. Return the text of the element, encoded (and processed by the current 
  12437. C<L<output_filter>> or C<L<output_encoding>> options, without any tag.
  12438.  
  12439. =item xml_text_only
  12440.  
  12441. Same as C<L<xml_text>> except that the text returned doesn't include 
  12442. the text of sub-elements.
  12443.  
  12444. =item set_pretty_print ($style)
  12445.  
  12446. Set the pretty print method, amongst 'C<none>' (default), 'C<nsgmls>', 
  12447. 'C<nice>', 'C<indented>', 'C<record>' and 'C<record_c>'
  12448.  
  12449. pretty_print styles:
  12450.  
  12451. =over 4
  12452.  
  12453. =item none
  12454.  
  12455. the default, no C<\n> is used
  12456.  
  12457. =item nsgmls
  12458.  
  12459. nsgmls style, with C<\n> added within tags
  12460.  
  12461. =item nice
  12462.  
  12463. adds C<\n> wherever possible (NOT SAFE, can lead to invalid XML)
  12464.  
  12465. =item indented
  12466.  
  12467. same as C<nice> plus indents elements (NOT SAFE, can lead to invalid XML) 
  12468.  
  12469. =item record
  12470.  
  12471. table-oriented pretty print, one field per line 
  12472.  
  12473. =item record_c
  12474.  
  12475. table-oriented pretty print, more compact than C<record>, one record per line 
  12476.  
  12477. =back
  12478.  
  12479. =item set_empty_tag_style ($style)
  12480.  
  12481. Set the method to output empty tags, amongst 'C<normal>' (default), 'C<html>',
  12482. and 'C<expand>', 
  12483.  
  12484. C<normal> outputs an empty tag 'C<< <tag/> >>', C<html> adds a space 
  12485. 'C<< <tag /> >>' for elements that can be empty in XHTML and C<expand> outputs
  12486. 'C<< <tag></tag> >>'
  12487.  
  12488. =item set_remove_cdata  ($flag)
  12489.  
  12490. set (or unset) the flag that forces the twig to output CDATA sections as 
  12491. regular (escaped) PCDATA
  12492.  
  12493.  
  12494. =item set_indent ($string)
  12495.  
  12496. Set the indentation for the indented pretty print style (default is 2 spaces)
  12497.  
  12498. =item set_quote ($quote)
  12499.  
  12500. Set the quotes used for attributes. can be 'C<double>' (default) or 'C<single>'
  12501.  
  12502. =item cmp       ($elt)
  12503.  
  12504.   Compare the order of the 2 elements in a twig.
  12505.  
  12506.   C<$a> is the <A>..</A> element, C<$b> is the <B>...</B> element
  12507.  
  12508.   document                        $a->cmp( $b)
  12509.   <A> ... </A> ... <B>  ... </B>     -1
  12510.   <A> ... <B>  ... </B> ... </A>     -1
  12511.   <B> ... </B> ... <A>  ... </A>      1
  12512.   <B> ... <A>  ... </A> ... </B>      1
  12513.    $a == $b                           0
  12514.    $a and $b not in the same tree   undef
  12515.  
  12516. =item before       ($elt)
  12517.  
  12518. Return 1 if C<$elt> starts before the element, 0 otherwise. If the 2 elements 
  12519. are not in the same twig then return C<undef>.
  12520.  
  12521.     if( $a->cmp( $b) == -1) { return 1; } else { return 0; }
  12522.  
  12523. =item after       ($elt)
  12524.  
  12525. Return 1 if $elt starts after the element, 0 otherwise. If the 2 elements 
  12526. are not in the same twig then return C<undef>.
  12527.  
  12528.     if( $a->cmp( $b) == -1) { return 1; } else { return 0; }
  12529.  
  12530. =item other comparison methods
  12531.  
  12532. =over 4
  12533.  
  12534. =item lt
  12535.  
  12536. =item le
  12537.  
  12538. =item gt
  12539.  
  12540. =item ge
  12541.  
  12542. =back
  12543.  
  12544. =item path
  12545.  
  12546. Return the element context in a form similar to XPath's short
  12547. form: 'C</root/tag1/../tag>'
  12548.  
  12549. =item xpath
  12550.  
  12551. Return a unique XPath expression that can be used to find the element
  12552. again. 
  12553.  
  12554. It looks like C</doc/sect[3]/title>: unique elements do not have an index,
  12555. the others do.
  12556.  
  12557. =item private methods
  12558.  
  12559. Low-level methods on the twig:
  12560.  
  12561. =over 4
  12562.  
  12563. =item set_parent        ($parent)
  12564.  
  12565. =item set_first_child   ($first_child)
  12566.  
  12567. =item set_last_child    ($last_child)
  12568.  
  12569. =item set_prev_sibling  ($prev_sibling)
  12570.  
  12571. =item set_next_sibling  ($next_sibling)
  12572.  
  12573. =item set_twig_current
  12574.  
  12575. =item del_twig_current
  12576.  
  12577. =item twig_current
  12578.  
  12579. =item flush
  12580.  
  12581. This method should NOT be used, always flush the twig, not an element.
  12582.  
  12583. =item contains_text
  12584.  
  12585. =back
  12586.  
  12587. Those methods should not be used, unless of course you find some creative 
  12588. and interesting, not to mention useful, ways to do it.
  12589.  
  12590. =back
  12591.  
  12592. =head2 cond
  12593.  
  12594. Most of the navigation functions accept a condition as an optional argument
  12595. The first element (or all elements for C<L<children|/children> > or 
  12596. C<L<ancestors|/ancestors> >) that passes the condition is returned.
  12597.  
  12598. The condition is a single step of an XPath expression using the XPath subset
  12599. defined by C<L<get_xpath>>. Additional conditions are:
  12600.  
  12601. The condition can be 
  12602.  
  12603. =over 4
  12604.  
  12605. =item #ELT
  12606.  
  12607. return a "real" element (not a PCDATA, CDATA, comment or pi element) 
  12608.  
  12609. =item #TEXT
  12610.  
  12611. return a PCDATA or CDATA element
  12612.  
  12613. =item regular expression
  12614.  
  12615. return an element whose tag matches the regexp. The regexp has to be created 
  12616. with C<qr//> (hence this is available only on perl 5.005 and above)
  12617.  
  12618. =item code reference
  12619.  
  12620. applies the code, passing the current element as argument, if the code returns
  12621. true then the element is returned, if it returns false then the code is applied
  12622. to the next candidate.
  12623.  
  12624. =back
  12625.  
  12626. =head2 XML::Twig::XPath
  12627.  
  12628. XML::Twig implements a subset of XPath through the C<L<get_xpath>> method. 
  12629.  
  12630. If you want to use the whole XPath power, then you can use C<XML::Twig::XPath>
  12631. instead. In this case C<XML::Twig> uses C<XML::XPath> to execute XPath queries.
  12632. You will of course need C<XML::XPath> installed to be able to use C<XML::Twig::XPath>.
  12633.  
  12634. See L<XML::XPath> for more information.
  12635.  
  12636. The methods you can use are:
  12637.  
  12638. =over 4
  12639.  
  12640. =item findnodes              ($path)
  12641.  
  12642. return a list of nodes found by C<$path>.
  12643.  
  12644. =item findnodes_as_string    ($path)
  12645.  
  12646. return the nodes found reproduced as XML. The result is not guaranteed
  12647. to be valid XML though.
  12648.  
  12649. =item findvalue              ($path)
  12650.  
  12651. return the concatenation of the text content of the result nodes
  12652.  
  12653. =back
  12654.  
  12655. In order for C<XML::XPath> to be used as the XPath engine the following methods
  12656. are included in C<XML::Twig>:
  12657.  
  12658. in XML::Twig
  12659.  
  12660. =over 4
  12661.  
  12662. =item getRootNode
  12663.  
  12664. =item getParentNode
  12665.  
  12666. =item getChildNodes 
  12667.  
  12668. =back
  12669.  
  12670. in XML::Twig::Elt
  12671.  
  12672. =over 4
  12673.  
  12674. =item string_value
  12675.  
  12676. =item toString
  12677.  
  12678. =item getName
  12679.  
  12680. =item getRootNode
  12681.  
  12682. =item getNextSibling
  12683.  
  12684. =item getPreviousSibling
  12685.  
  12686. =item isElementNode
  12687.  
  12688. =item isTextNode
  12689.  
  12690. =item isPI
  12691.  
  12692. =item isPINode
  12693.  
  12694. =item isProcessingInstructionNode
  12695.  
  12696. =item isComment
  12697.  
  12698. =item isCommentNode
  12699.  
  12700. =item getTarget 
  12701.  
  12702. =item getChildNodes 
  12703.  
  12704. =item getElementById
  12705.  
  12706. =back
  12707.  
  12708. =head2 XML::Twig::XPath::Elt
  12709.  
  12710. The methods you can use are the same as on C<XML::Twig::XPath> elements:
  12711.  
  12712. =over 4
  12713.  
  12714. =item findnodes              ($path)
  12715.  
  12716. return a list of nodes found by C<$path>.
  12717.  
  12718. =item findnodes_as_string    ($path)
  12719.  
  12720. return the nodes found reproduced as XML. The result is not guaranteed
  12721. to be valid XML though.
  12722.  
  12723. =item findvalue              ($path)
  12724.  
  12725. return the concatenation of the text content of the result nodes
  12726.  
  12727. =back
  12728.  
  12729.  
  12730. =head2 XML::Twig::Entity_list
  12731.  
  12732. =over 4
  12733.  
  12734. =item new
  12735.  
  12736. Create an entity list.
  12737.  
  12738. =item add         ($ent)
  12739.  
  12740. Add an entity to an entity list.
  12741.  
  12742. =item add_new_ent ($name, $val, $sysid, $pubid, $ndata, $param)
  12743.  
  12744. Create a new entity and add it to the entity list
  12745.  
  12746. =item delete     ($ent or $tag).
  12747.  
  12748. Delete an entity (defined by its name or by the Entity object)
  12749. from the list.
  12750.  
  12751. =item print      ($optional_filehandle)
  12752.  
  12753. Print the entity list.
  12754.  
  12755. =item list
  12756.  
  12757. Return the list as an array
  12758.  
  12759. =back
  12760.  
  12761.  
  12762. =head2 XML::Twig::Entity
  12763.  
  12764. =over 4
  12765.  
  12766. =item new        ($name, $val, $sysid, $pubid, $ndata, $param)
  12767.  
  12768. Same arguments as the Entity handler for XML::Parser.
  12769.  
  12770. =item print       ($optional_filehandle)
  12771.  
  12772. Print an entity declaration.
  12773.  
  12774. =item name 
  12775.  
  12776. Return the name of the entity
  12777.  
  12778. =item val  
  12779.  
  12780. Return the value of the entity
  12781.  
  12782. =item sysid
  12783.  
  12784. Return the system id for the entity (for NDATA entities)
  12785.  
  12786. =item pubid
  12787.  
  12788. Return the public id for the entity (for NDATA entities)
  12789.  
  12790. =item ndata
  12791.  
  12792. Return true if the entity is an NDATA entity
  12793.  
  12794. =item param
  12795.  
  12796. Return true if the entity is a parameter entity
  12797.  
  12798.  
  12799. =item text
  12800.  
  12801. Return the entity declaration text.
  12802.  
  12803. =back
  12804.  
  12805.  
  12806. =head1 EXAMPLES
  12807.  
  12808. Additional examples (and a complete tutorial) can be found  on the
  12809. F<XML::Twig PageL<http://www.xmltwig.com/xmltwig/>>
  12810.  
  12811. To figure out what flush does call the following script with an
  12812. XML file and an element name as arguments
  12813.  
  12814.   use XML::Twig;
  12815.  
  12816.   my ($file, $elt)= @ARGV;
  12817.   my $t= XML::Twig->new( twig_handlers => 
  12818.       { $elt => sub {$_[0]->flush; print "\n[flushed here]\n";} });
  12819.   $t->parsefile( $file, ErrorContext => 2);
  12820.   $t->flush;
  12821.   print "\n";
  12822.  
  12823.  
  12824. =head1 NOTES
  12825.  
  12826. =head2 Subclassing XML::Twig
  12827.  
  12828. Useful methods:
  12829.  
  12830. =over 4
  12831.  
  12832. =item elt_class
  12833.  
  12834. In order to subclass C<XML::Twig> you will probably need to subclass also
  12835. C<L<XML::Twig::Elt>>. Use the C<elt_class> option when you create the
  12836. C<XML::Twig> object to get the elements created in a different class
  12837. (which should be a subclass of C<XML::Twig::Elt>.
  12838.  
  12839. =item add_options
  12840.  
  12841. If you inherit C<XML::Twig> new method but want to add more options to it
  12842. you can use this method to prevent XML::Twig to issue warnings for those
  12843. additional options.
  12844.  
  12845. =back
  12846.  
  12847. =head2 DTD Handling
  12848.  
  12849. There are 3 possibilities here.  They are:
  12850.  
  12851. =over 4
  12852.  
  12853. =item No DTD
  12854.  
  12855. No doctype, no DTD information, no entity information, the world is simple...
  12856.  
  12857. =item Internal DTD
  12858.  
  12859. The XML document includes an internal DTD, and maybe entity declarations.
  12860.  
  12861. If you use the load_DTD option when creating the twig the DTD information and
  12862. the entity declarations can be accessed.
  12863.  
  12864. The DTD and the entity declarations will be C<flush>'ed (or C<print>'ed) either
  12865. as is (if they have not been modified) or as reconstructed (poorly, comments 
  12866. are lost, order is not kept, due to it's content this DTD should not be viewed 
  12867. by anyone) if they have been modified. You can also modify them directly by 
  12868. changing the C<< $twig->{twig_doctype}->{internal} >> field (straight from 
  12869. XML::Parser, see the C<Doctype> handler doc)
  12870.  
  12871. =item External DTD
  12872.  
  12873. The XML document includes a reference to an external DTD, and maybe entity 
  12874. declarations.
  12875.  
  12876. If you use the C<load_DTD> when creating the twig the DTD information and the 
  12877. entity declarations can be accessed. The entity declarations will be
  12878. C<flush>'ed (or C<print>'ed) either as is (if they have not been modified) or
  12879. as reconstructed (badly, comments are lost, order is not kept).
  12880.  
  12881. You can change the doctype through the C<< $twig->set_doctype >> method and 
  12882. print the dtd through the C<< $twig->dtd_text >> or C<< $twig->dtd_print >>
  12883.  methods.
  12884.  
  12885. If you need to modify the entity list this is probably the easiest way to do it.
  12886.  
  12887. =back
  12888.  
  12889.  
  12890. =head2 Flush
  12891.  
  12892. If you set handlers and use C<flush>, do not forget to flush the twig one
  12893. last time AFTER the parsing, or you might be missing the end of the document.
  12894.  
  12895. Remember that element handlers are called when the element is CLOSED, so
  12896. if you have handlers for nested elements the inner handlers will be called
  12897. first. It makes it for example trickier than it would seem to number nested
  12898. clauses.
  12899.  
  12900.  
  12901.  
  12902. =head1 BUGS
  12903.  
  12904. =over 4
  12905.  
  12906. =item entity handling
  12907.  
  12908. Due to XML::Parser behaviour, non-base entities in attribute values disappear:
  12909. C<att="val&ent;"> will be turned into C<< att => val >>, unless you use the 
  12910. C<keep_encoding> argument to C<< XML::Twig->new >> 
  12911.  
  12912. =item DTD handling
  12913.  
  12914. The DTD handling methods are quite bugged. No one uses them and
  12915. it seems very difficult to get them to work in all cases, including with 
  12916. several slightly incompatible versions of XML::Parser and of libexpat.
  12917.  
  12918. Basically you can read the DTD, output it back properly, and update entities,
  12919. but not much more.
  12920.  
  12921. So use XML::Twig with standalone documents, or with documents refering to an
  12922. external DTD, but don't expect it to properly parse and even output back the
  12923. DTD.
  12924.  
  12925. =item memory leak
  12926.  
  12927. If you use a lot of twigs you might find that you leak quite a lot of memory
  12928. (about 2Ks per twig). You can use the C<L<dispose|/dispose> > method to free 
  12929. that memory after you are done.
  12930.  
  12931. If you create elements the same thing might happen, use the C<L<delete|/delete>>
  12932. method to get rid of them.
  12933.  
  12934. Alternatively installing the C<Scalar::Util> (or C<WeakRef>) module on a version 
  12935. of Perl that supports it (>5.6.0) will get rid of the memory leaks automagically.
  12936.  
  12937. =item ID list
  12938.  
  12939. The ID list is NOT updated when elements are cut or deleted.
  12940.  
  12941. =item change_gi
  12942.  
  12943. This method will not function properly if you do:
  12944.  
  12945.      $twig->change_gi( $old1, $new);
  12946.      $twig->change_gi( $old2, $new);
  12947.      $twig->change_gi( $new, $even_newer);
  12948.  
  12949. =item sanity check on XML::Parser method calls
  12950.  
  12951. XML::Twig should really prevent calls to some XML::Parser methods, especially 
  12952. the C<setHandlers> method.
  12953.  
  12954. =item pretty printing
  12955.  
  12956. Pretty printing (at least using the 'C<indented>' style) is hard to get right! 
  12957. Only elements that belong to the document will be properly indented. Printing 
  12958. elements that do not belong to the twig makes it impossible for XML::Twig to 
  12959. figure out their depth, and thus their indentation level.
  12960.  
  12961. Also there is an unavoidable bug when using C<flush> and pretty printing for
  12962. elements with mixed content that start with an embedded element:
  12963.  
  12964.   <elt><b>b</b>toto<b>bold</b></elt>
  12965.  
  12966.   will be output as 
  12967.  
  12968.   <elt>
  12969.     <b>b</b>toto<b>bold</b></elt>
  12970.  
  12971. if you flush the twig when you find the C<< <b> >> element
  12972.  
  12973.  
  12974. =back
  12975.  
  12976. =head1 Globals
  12977.  
  12978. These are the things that can mess up calling code, especially if threaded.
  12979. They might also cause problem under mod_perl. 
  12980.  
  12981. =over 4
  12982.  
  12983. =item Exported constants
  12984.  
  12985. Whether you want them or not you get them! These are subroutines to use
  12986. as constant when creating or testing elements
  12987.  
  12988.   PCDATA  return '#PCDATA'
  12989.   CDATA   return '#CDATA'
  12990.   PI      return '#PI', I had the choice between PROC and PI :--(
  12991.  
  12992. =item Module scoped values: constants
  12993.  
  12994. these should cause no trouble:
  12995.  
  12996.   %base_ent= ( '>' => '>',
  12997.                '<' => '<',
  12998.                '&' => '&',
  12999.                "'" => ''',
  13000.                '"' => '"',
  13001.              );
  13002.   CDATA_START   = "<![CDATA[";
  13003.   CDATA_END     = "]]>";
  13004.   PI_START      = "<?";
  13005.   PI_END        = "?>";
  13006.   COMMENT_START = "<!--";
  13007.   COMMENT_END   = "-->";
  13008.  
  13009. pretty print styles
  13010.  
  13011.   ( $NSGMLS, $NICE, $INDENTED, $INDENTED_C, $WRAPPED, $RECORD1, $RECORD2)= (1..7);
  13012.  
  13013. empty tag output style
  13014.  
  13015.   ( $HTML, $EXPAND)= (1..2);
  13016.  
  13017. =item Module scoped values: might be changed
  13018.  
  13019. Most of these deal with pretty printing, so the worst that can
  13020. happen is probably that XML output does not look right, but is
  13021. still valid and processed identically by XML processors.
  13022.  
  13023. C<$empty_tag_style> can mess up HTML bowsers though and changing C<$ID> 
  13024. would most likely create problems.
  13025.  
  13026.   $pretty=0;           # pretty print style
  13027.   $quote='"';          # quote for attributes
  13028.   $INDENT= '  ';       # indent for indented pretty print
  13029.   $empty_tag_style= 0; # how to display empty tags
  13030.   $ID                  # attribute used as an id ('id' by default)
  13031.  
  13032. =item Module scoped values: definitely changed
  13033.  
  13034. These 2 variables are used to replace tags by an index, thus 
  13035. saving some space when creating a twig. If they really cause
  13036. you too much trouble, let me know, it is probably possible to
  13037. create either a switch or at least a version of XML::Twig that 
  13038. does not perform this optimization.
  13039.  
  13040.   %gi2index;     # tag => index
  13041.   @index2gi;     # list of tags
  13042.  
  13043. =back
  13044.  
  13045. If you need to manipulate all those values, you can use the following methods on the
  13046. XML::Twig object:
  13047.  
  13048. =over 4
  13049.  
  13050. =item global_state
  13051.  
  13052. Return a hashref with all the global variables used by XML::Twig
  13053.  
  13054. The hash has the following fields:  C<pretty>, C<quote>, C<indent>, 
  13055. C<empty_tag_style>, C<keep_encoding>, C<expand_external_entities>, 
  13056. C<output_filter>, C<output_text_filter>, C<keep_atts_order>
  13057.  
  13058. =item set_global_state ($state)
  13059.  
  13060. Set the global state, C<$state> is a hashref
  13061.  
  13062. =item save_global_state
  13063.  
  13064. Save the current global state
  13065.  
  13066. =item restore_global_state
  13067.  
  13068. Restore the previously saved (using C<Lsave_global_state>> state
  13069.  
  13070. =back
  13071.  
  13072. =head1 TODO 
  13073.  
  13074. =over 4
  13075.  
  13076. =item SAX handlers
  13077.  
  13078. Allowing XML::Twig to work on top of any SAX parser
  13079.  
  13080. =item multiple twigs are not well supported
  13081.  
  13082. A number of twig features are just global at the moment. These include
  13083. the ID list and the "tag pool" (if you use C<change_gi> then you change the tag 
  13084. for ALL twigs).
  13085.  
  13086. A future version will try to support this while trying not to be to
  13087. hard on performance (at least when a single twig is used!).
  13088.  
  13089.  
  13090. =back
  13091.  
  13092.  
  13093. =head1 AUTHOR
  13094.  
  13095. Michel Rodriguez <mirod@xmltwig.com>
  13096.  
  13097. =head1 LICENSE
  13098.  
  13099. This library is free software; you can redistribute it and/or modify
  13100. it under the same terms as Perl itself.
  13101.  
  13102. Bug reports should be sent using:
  13103. F<RT L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=XML-Twig>>
  13104.  
  13105. Comments can be sent to mirod@xmltwig.com
  13106.  
  13107. The XML::Twig page is at L<http://www.xmltwig.com/xmltwig/>
  13108. It includes the development version of the module, a slightly better version 
  13109. of the documentation, examples, a tutorial and a: 
  13110. F<Processing XML efficiently with Perl and XML::Twig: 
  13111. L<http://www.xmltwig.com/xmltwig/tutorial/index.html>>
  13112.  
  13113. =head1 SEE ALSO
  13114.  
  13115. Complete docs, including a tutorial, examples, an easier to use HTML version of
  13116. the docs, a quick reference card and a FAQ are available at 
  13117. L<http://www.xmltwig.com/xmltwig/>
  13118.  
  13119. git repository at L<http://github.com/mirod/xmltwig>
  13120.  
  13121. L<XML::Parser>, L<XML::Parser::Expat>, L<XML::XPath>, L<Encode>, 
  13122. L<Text::Iconv>, L<Scalar::Utils>
  13123.  
  13124.  
  13125. =head2 Alternative Modules
  13126.  
  13127. XML::Twig is not the only XML::Processing module available on CPAN (far from 
  13128. it!).
  13129.  
  13130. The main alternative I would recommend is L<XML::LibXML>. 
  13131.  
  13132. Here is a quick comparison of the 2 modules:
  13133.  
  13134. XML::LibXML, actually C<libxml2> on which it is based, sticks to the standards,
  13135. and implements a good number of them in a rather strict way: XML, XPath, DOM, 
  13136. RelaxNG, I must be forgetting a couple (XInclude?). It is fast and rather 
  13137. frugal memory-wise.
  13138.  
  13139. XML::Twig is older: when I started writing it XML::Parser/expat was the only 
  13140. game in town. It implements XML and that's about it (plus a subset of XPath, 
  13141. and you can use XML::Twig::XPath if you have XML::XPathEngine installed for full 
  13142. support). It is slower and requires more memory for a full tree than 
  13143. XML::LibXML. On the plus side (yes, there is a plus side!) it lets you process
  13144. a big document in chunks, and thus let you tackle documents that couldn't be 
  13145. loaded in memory by XML::LibXML, and it offers a lot (and I mean a LOT!) of 
  13146. higher-level methods, for everything, from adding structure to "low-level" XML,
  13147. to shortcuts for XHTML conversions and more. It also DWIMs quite a bit, getting
  13148. comments and non-significant whitespaces out of the way but preserving them in 
  13149. the output for example. As it does not stick to the DOM, is also usually leads 
  13150. to shorter code than in XML::LibXML.
  13151.  
  13152. Beyond the pure features of the 2 modules, XML::LibXML seems to be preferred by
  13153. "XML-purists", while XML::Twig seems to be more used by Perl Hackers who have 
  13154. to deal with XML. As you have noted, XML::Twig also comes with quite a lot of 
  13155. docs, but I am sure if you ask for help about XML::LibXML here or on Perlmonks
  13156. you will get answers.
  13157.  
  13158. Note that it is actually quite hard for me to compare the 2 modules: on one hand
  13159. I know XML::Twig inside-out and I can get it to do pretty much anything I need 
  13160. to (or I improve it ;--), while I have a very basic knowledge of XML::LibXML. 
  13161. So feature-wise, I'd rather use XML::Twig ;--). On the other hand, I am 
  13162. painfully aware of some of the deficiencies, potential bugs and plain ugly code
  13163. that lurk in XML::Twig, even though you are unlikely to be affected by them 
  13164. (unless for example you need to change the DTD of a document programatically),
  13165. while I haven't looked much into XML::LibXML so it still looks shinny and clean
  13166. to me.
  13167.  
  13168. That said, if you need to process a document that is too big to fit memory
  13169. and XML::Twig is too slow for you, my reluctant advice would be to use "bare"
  13170. XML::Parser.  It won't be as easy to use as XML::Twig: basically with XML::Twig
  13171. you trade some speed (depending on what you do from a factor 3 to... none) 
  13172. for ease-of-use, but it will be easier IMHO than using SAX (albeit not 
  13173. standard), and at this point a LOT faster (see the last test in
  13174. L<http://www.xmltwig.com/article/simple_benchmark/>).
  13175.  
  13176. =cut
  13177.  
  13178.  
  13179.