Subject: | Support for !NOTATION elements |
XML::Twig is currently lacking in support for manipulating !NOTATION elements found in the DTD/Doctype section.
I'm submitting a patch that is just a cut/paste/update of the current !ENTITY support to handle !NOTATION. This patch is lacking tests and various parsing style updates, but works in basic cases.
This patch was tested with 3.48, but I have applied to 3.49 and it went in clean.
Subject: | XML-Twig-3.48-Notation.patch |
diff -uNr XML-Twig-3.48/Twig.pm XML-Twig-3.48-Notation/Twig.pm
--- XML-Twig-3.48/Twig.pm 2014-03-30 04:54:15.000000000 -0400
+++ XML-Twig-3.48-Notation/Twig.pm 2016-01-05 14:16:11.924926200 -0500
@@ -117,7 +117,7 @@
my %DEFAULT_URI2NS= map { $DEFAULT_NS{$_} => $_ } keys %DEFAULT_NS;
# constants
-my( $PCDATA, $CDATA, $PI, $COMMENT, $ENT, $ELT, $TEXT, $ASIS, $EMPTY, $BUFSIZE);
+my( $PCDATA, $CDATA, $PI, $COMMENT, $ENT, $NOTATION, $ELT, $TEXT, $ASIS, $EMPTY, $BUFSIZE);
# used when an HTML doc only has a PUBLIC declaration, to generate the SYSTEM one
# this should really be done by HTML::TreeBuilder, but as of HTML::TreeBuilder 4.2 it isn't
@@ -173,17 +173,20 @@
import XML::Twig::Elt;
import XML::Twig::Entity;
import XML::Twig::Entity_list;
+import XML::Twig::Notation;
+import XML::Twig::Notation_list;
# used to store the gi's
# should be set for each twig really, at least when there are several
# the init ensures that special gi's are always the same
# constants: element types
-$PCDATA = '#PCDATA';
-$CDATA = '#CDATA';
-$PI = '#PI';
-$COMMENT = '#COMMENT';
-$ENT = '#ENT';
+$PCDATA = '#PCDATA';
+$CDATA = '#CDATA';
+$PI = '#PI';
+$COMMENT = '#COMMENT';
+$ENT = '#ENT';
+$NOTATION = '#NOTATION';
# element classes
$ELT = '#ELT';
@@ -237,6 +240,7 @@
End => \&_twig_end,
Char => \&_twig_char,
Entity => \&_twig_entity,
+ Notation => \&_twig_notation,
XMLDecl => \&_twig_xmldecl,
Doctype => \&_twig_doctype,
Element => \&_twig_element,
@@ -731,7 +735,8 @@
# XML::Parser level
$self->setHandlers( Init => \&_twig_init, Final => \&_twig_final);
- $self->{twig_entity_list}= XML::Twig::Entity_list->new;
+ $self->{twig_entity_list} = XML::Twig::Entity_list->new;
+ $self->{twig_notation_list} = XML::Twig::Notation_list->new;
$self->{twig_id}= $ID;
$self->{twig_stored_spaces}='';
@@ -2829,6 +2834,19 @@
return;
}
+sub _twig_notation
+ {
+ # warn " in _twig_notation...\n";
+ my( $p, $name, $base, $sysid, $pubid ) = @_;
+ my $t = $p->{twig};
+
+ my $notation = XML::Twig::Notation->new( $name, $base, $sysid, $pubid );
+ my $notation_list = $t->notation_list();
+ if( $notation_list ) { $notation_list->add( $notation ); }
+
+ return;
+ }
+
sub _twig_extern_ent
{ # warn " in _twig_extern_ent...I (", $_[0]->original_string, ")\n"; # DEBUG handler
@@ -3157,6 +3175,25 @@
return $t->entity_list->ent( $entity_name);
}
+# return the notation_list object
+sub notation_list
+ { my $t= shift;
+ return $t->{twig_notation_list};
+ }
+
+# return the list of notation names
+sub notation_names
+ { my $t= shift;
+ return $t->notation_list->notation_names;
+ }
+
+# return the notation object
+sub notation
+ { my $t= shift;
+ my $notation_name= shift;
+ return $t->notation_list->notation( $notation_name);
+ }
+
sub print_prolog
{ my $t= shift;
@@ -3195,15 +3232,18 @@
{ my $internal=$doctype->{internal};
# awful hack, but at least it works a little better that what was there before
if( $internal)
- { # remove entity declarations (they will be re-generated from the updated entity list)
+ { # remove entity and notation declarations (they will be re-generated from the updated lists)
$internal=~ s{<! \s* ENTITY \s+ $REG_TAG_NAME \s+ ( ("[^"]*"|'[^']*') \s* | SYSTEM [^>]*) >\s*}{}xg;
+ $internal=~ s{<! \s* NOTATION .*? >\s*}{}xg;
$internal=~ s{^\n}{};
}
- $internal .= $t->entity_list->text ||'' if( $t->entity_list);
+ $internal .= $t->entity_list->text ||'' if( $t->entity_list);
+ $internal .= $t->notation_list->text ||'' if( $t->notation_list);
+
if( $internal) { $doctype_text .= "[\n$internal]>\n"; }
}
- elsif( !$t->{'twig_dtd'} && keys %{$t->entity_list})
- { $doctype_text .= "<!DOCTYPE " . $t->root->gi . " [\n" . $t->entity_list->text . "\n]>";;}
+ elsif( !$t->{'twig_dtd'} && ( keys %{$t->entity_list} || keys %{$t->notation_list} ))
+ { $doctype_text .= "<!DOCTYPE " . $t->root->gi . " [\n" . $t->entity_list->text . $t->notation_list->text . "\n]>";;}
else
{ $doctype_text= $t->{twig_dtd};
$doctype_text .= $t->dtd_text;
@@ -4837,6 +4877,161 @@
1;
######################################################################
+package XML::Twig::Notation_list;
+######################################################################
+
+*isa= *UNIVERSAL::isa;
+
+sub new
+ { my $class = shift;
+ my $self={ notations => {}, updated => 0};
+
+ bless $self, $class;
+ return $self;
+
+ }
+
+sub add_new_notation
+ { my $notation_list= shift;
+ my $notation= XML::Twig::Notation->new( @_);
+ $notation_list->add( $notation);
+ return $notation_list;
+ }
+
+sub _add_list
+ { my( $notation_list, $to_add)= @_;
+ my $notations_to_add= $to_add->{notations};
+ return $notation_list unless( $notations_to_add && %$notations_to_add);
+ @{$notation_list->{notations}}{keys %$notations_to_add}= values %$notations_to_add;
+ $notation_list->{updated}=1;
+ return $notation_list;
+ }
+
+sub add
+ { my( $notation_list, $notation)= @_;
+ $notation_list->{notations}->{$notation->{name}}= $notation;
+ $notation_list->{updated}=1;
+ return $notation_list;
+ }
+
+sub notation
+ { my( $notation_list, $notation_name)= @_;
+ return $notation_list->{notations}->{$notation_name};
+ }
+
+# can be called with an notationity or with an notationity name
+sub delete
+ { my $notation_list= shift;
+ if( isa( ref $_[0], 'XML::Twig::Notation'))
+ { # the second arg is an notationity
+ my $notation= shift;
+ delete $notation_list->{notations}->{$notation->{name}};
+ }
+ else
+ { # the second arg was not notationity, must be a string then
+ my $name= shift;
+ delete $notation_list->{notations}->{$name};
+ }
+ $notation_list->{updated}=1;
+ return $notation_list;
+ }
+
+sub print
+ { my ($notation_list, $fh)= @_;
+ my $old_select= defined $fh ? select $fh : undef;
+
+ foreach my $notation_name ( sort keys %{$notation_list->{notations}})
+ { my $notation= $notation_list->{notations}->{$notation_name};
+ # we have to test what the notationity is or un-defined notations can creep in
+ if( isa( $notation, 'XML::Twig::Notation')) { $notation->print(); }
+ }
+ select $old_select if( defined $old_select);
+ return $notation_list;
+ }
+
+sub text
+ { my ($notation_list)= @_;
+ return join "\n", map { $notation_list->{notations}->{$_}->text} sort keys %{$notation_list->{notations}};
+ }
+
+# return the list of notationity names
+sub notationity_names
+ { my $notation_list= shift;
+ return (sort keys %{$notation_list->{notations}}) ;
+ }
+
+
+sub list
+ { my ($notation_list)= @_;
+ return map { $notation_list->{notations}->{$_} } sort keys %{$notation_list->{notations}};
+ }
+
+1;
+
+######################################################################
+package XML::Twig::Notation;
+######################################################################
+
+#*isa= *UNIVERSAL::isa;
+
+sub new
+ { my( $class, $name, $base, $sysid, $pubid)= @_;
+ $class= ref( $class) || $class;
+
+ my $self={};
+
+ $self->{name} = $name;
+ $self->{base} = $base if( defined $base );
+ $self->{sysid} = $sysid if( defined $sysid);
+ $self->{pubid} = $pubid if( defined $pubid);
+
+ bless $self, $class;
+ return $self;
+ }
+
+
+sub name { return $_[0]->{name}; }
+sub base { return $_[0]->{base}; }
+sub sysid { return $_[0]->{sysid}; }
+sub pubid { return $_[0]->{pubid}; }
+
+
+sub print
+ { my ($notation, $fh)= @_;
+ my $text= $notation->text;
+ if( $fh) { print $fh $text . "\n"; }
+ else { print $text . "\n"; }
+ }
+
+sub sprint
+ { my ($notation)= @_;
+ return $notation->text;
+ }
+
+sub text
+ { my ($notation)= @_;
+ #warn "text called: '", $notation->_dump, "'\n";
+ return '' if( !$notation->{name});
+ my @tokens;
+ push @tokens, '<!NOTATION';
+ push @tokens, $notation->{name};
+ push @tokens, ( 'PUBLIC', _quoted_val( $notation->{pubid} ) ) if $notation->{pubid};
+ push @tokens, ( 'SYSTEM', _quoted_val( $notation->{sysid} ) ) if $notation->{sysid};
+
+ return join( ' ', @tokens) . '>';
+ }
+
+sub _quoted_val
+ { my $q= $_[0]=~ m{"} ? q{'} : q{"};
+ return qq{$q$_[0]$q};
+ }
+
+sub _dump
+ { my( $notation)= @_; return join( " - ", map { "$_ => '$notation->{$_}'" } grep { defined $notation->{$_} } sort keys %$notation); }
+
+1;
+
+######################################################################
package XML::Twig::Elt;
######################################################################
@@ -11189,6 +11384,18 @@
Return the entity
+=item notation_list
+
+Return the notation list of a twig
+
+=item notation_names
+
+Return the list of all defined notations
+
+=item notation ($notation_name)
+
+Return the notation
+
=item change_gi ($old_gi, $new_gi)
Performs a (very fast) global change. All elements C<$old_gi> are now
@@ -13621,6 +13828,72 @@
=back
+=head2 XML::Twig::Notation_list
+
+=over 4
+
+=item new
+
+Create an notation list.
+
+=item add ($notation)
+
+Add an notation to an notation list.
+
+=item add_new_notation ($name, $base, $sysid, $pubid)
+
+Create a new notation and add it to the notation list
+
+=item delete ($notation or $tag).
+
+Delete an notation (defined by its name or by the Notation object)
+from the list.
+
+=item print ($optional_filehandle)
+
+Print the notation list.
+
+=item list
+
+Return the list as an array
+
+=back
+
+
+=head2 XML::Twig::Notation
+
+=over 4
+
+=item new ($name, $base, $sysid, $pubid)
+
+Same argumnotations as the Notation handler for XML::Parser.
+
+=item print ($optional_filehandle)
+
+Print an notation declaration.
+
+=item name
+
+Return the name of the notation
+
+=item base
+
+Return the base to be used for resolving a relative URI
+
+=item sysid
+
+Return the system id for the notation
+
+=item pubid
+
+Return the public id for the notation
+
+
+=item text
+
+Return the notation declaration text.
+
+=back
=head1 EXAMPLES