home *** CD-ROM | disk | FTP | other *** search
/ Chip 2000 May / Chip_2000-05_cd1.bin / zkuste / Perl / ActivePerl-5.6.0.613.msi / 䆊䌷䈹䈙䏵-䞅䞆䞀㡆䞃䄦䠥 / _cf6404e01f9dbd79e7711c9a844969c3 < prev    next >
Text File  |  2000-03-15  |  10KB  |  331 lines

  1. package XML::ValidatingElement;
  2. use XML::Element;
  3. use vars qw( $VERSION @ISA );
  4.  
  5. ###############################################################################
  6. #
  7. # XML::ValidatingElement
  8. #
  9. # Base class for validating elements.  Allows for applying DTD type
  10. # restrictions to elements parsed using the XML::Parser module.
  11. #
  12. ###############################################################################
  13. $VERSION = do { my @r = q$Revision: 1.4 $ =~ /\d+/g; sprintf '%d.'.'%02d'x$#r, @r };
  14.  
  15. ###############################################################################
  16. # Define the validating element class.
  17. ###############################################################################
  18. @ISA = qw( XML::Element );
  19.  
  20. ###############################################################################
  21. # Recursively validate myself and all child elements with all four types of
  22. # validation.  Returns non-zero on success and zero on any errors.
  23. ###############################################################################
  24. sub rvalidate
  25. {
  26.     my $self = shift;
  27.     my $func = shift;
  28.     my $success = 1;
  29.  
  30.     $success &= $self->validate_possible_attrs( $func );
  31.     $success &= $self->validate_required_attrs( $func );
  32.     $success &= $self->validate_possible_kids( $func );
  33.     $success &= $self->validate_required_kids( $func );
  34.  
  35.     foreach (@{$self->{Kids}})
  36.     {
  37.         if ((ref $_) !~ /::Characters$/o)
  38.             { $success &= $_->rvalidate( $func ); }
  39.     }
  40.  
  41.     return $success;
  42. }
  43.  
  44. ###############################################################################
  45. # Validate the element with all four types of validation.  Returns non-zero on
  46. # success any zero if any errors occurred.
  47. ###############################################################################
  48. sub validate
  49. {
  50.     my $self = shift;
  51.     my $func = shift;
  52.     my $success = 1;
  53.  
  54.     $success &= $self->validate_possible_attrs( $func );
  55.     $success &= $self->validate_required_attrs( $func );
  56.     $success &= $self->validate_possible_kids( $func );
  57.     $success &= $self->validate_required_kids( $func );
  58.  
  59.     return $success;
  60. }
  61.  
  62. ###############################################################################
  63. # Validate possible attributes.  Returns non-zero on sucess, and zero if any
  64. # errors occurred.
  65. ###############################################################################
  66. sub validate_possible_attrs
  67. {
  68.     my $self = shift;
  69.     my $func = shift;
  70.     my $attr;
  71.     my $type = ref $self;
  72.     my $success = 1;
  73.  
  74.     my $elem = $type;
  75.     $elem =~ s/.*:://;
  76.  
  77.     my @allattrs;
  78.     push( @allattrs, @{"$type\::oattrs"}, @{"$type\::rattrs"} );
  79.  
  80.     # Check our list of attributes against the list of possible attributes we
  81.     # can have.
  82.     foreach $attr (keys %{$self})
  83.     {
  84.         if ( ($attr ne 'Kids') and ($attr ne 'Text') )
  85.         {
  86.             if (!grep( /^$attr$/, @allattrs ))
  87.             {
  88.                 &$func( "Element '$elem' doesn't allow the '$attr' attribute." );
  89.                 $success = 0;
  90.             }
  91.         }
  92.     }
  93.  
  94.     return $success;
  95. }
  96.  
  97. ###############################################################################
  98. # Validate required attributes.  Returns non-zero on success and zero if any
  99. # errors occurred.
  100. ###############################################################################
  101. sub validate_required_attrs
  102. {
  103.     my $self = shift;
  104.     my $func = shift;
  105.     my $attr;
  106.     my $type = ref $self;
  107.     my $success = 1;
  108.  
  109.     my $elem = $type;
  110.     $elem =~ s/.*:://;
  111.  
  112.     # Check the list of required attributes against the list of attributes
  113.     # which were parsed.
  114.     foreach $attr (@{"$type\::rattrs"})
  115.     {
  116.         if (!grep( /^$attr$/, (keys %{$self}) ))
  117.         {
  118.             &$func( "Element '$elem' must have a '$attr' attribute." );
  119.             $success = 0;
  120.         }
  121.     }
  122.  
  123.     return $success;
  124. }
  125.  
  126. ###############################################################################
  127. # Validate possible child elements.  Returns non-zero on success and zero if
  128. # any errors occurred.
  129. ###############################################################################
  130. sub validate_possible_kids
  131. {
  132.     my $self = shift;
  133.     my $func = shift;
  134.     my $kid;
  135.     my $type = ref $self;
  136.     my $success = 1;
  137.     
  138.     my $elem = $type;
  139.     $elem =~ s/.*:://;
  140.  
  141.     my $base = $type;
  142.     $base =~ s/::[^:]*?$//;
  143.  
  144.     my @allkids;
  145.     push( @allkids, @{"$type\::okids"}, @{"$type\::rkids"} );
  146.  
  147.     foreach $kid (@{ $self->{Kids} })
  148.     {
  149.         my $kid_type = ref $kid;
  150.         $kid_type =~ s/.*:://;
  151.         next if ($kid_type eq 'Characters');    # Don't validate character data
  152.  
  153.         if (!grep( /^$kid_type$/, @allkids ))
  154.         {
  155.             &$func( "Element '$elem' cannot contain a child element '$kid_type'" );
  156.             $success = 0;
  157.         }
  158.     }
  159.  
  160.     return $success;
  161. }
  162.  
  163. ###############################################################################
  164. # Validate required child elements.  Returns non-zero on success and zero if
  165. # any errors occurred.
  166. ###############################################################################
  167. sub validate_required_kids
  168. {
  169.     my $self = shift;
  170.     my $func = shift;
  171.     my $kid;
  172.     my $type = ref $self;
  173.     my $success = 1;
  174.  
  175.     my $elem = $type;
  176.     $elem =~ s/.*:://;
  177.  
  178.     my $base = $type;
  179.     $base =~ s/::[^:]*?$//;
  180.  
  181.     foreach $kid (@{"$type\::rkids"})
  182.     {
  183.         my @kidlist = map( ref, @{$self->{Kids}} );
  184.  
  185.         if (!grep( /^$base\::$kid$/, @kidlist ))
  186.         {
  187.             &$func( "Element '$elem' must contain a '$kid' element." );
  188.             $success = 0;
  189.         }
  190.     }
  191.  
  192.     return $success;
  193. }
  194.  
  195. __END__
  196.  
  197. ###############################################################################
  198. # POD
  199. ###############################################################################
  200.  
  201. =head1 NAME
  202.  
  203. XML::ValidatingElement - XML Element with DTD-like validation rules
  204.  
  205. =head1 SYNOPSIS
  206.  
  207.  use XML::ValidatingElement;
  208.  
  209.  package XML::MyElement;
  210.  @ISA = qw( XML::ValidatingElement );
  211.  @oattrs = qw( BAR );       # Allow for both FOO and BAR attributes
  212.  @rattrs = qw( FOO );
  213.  @okids  = qw( BLEARGH );   # Allow for both BLEARGH and FOOBAR children
  214.  @rkids  = qw( FOOBAR );
  215.  
  216. =head1 DESCRIPTION
  217.  
  218. XML::ValidatingElement inherits from XML::Element.  It extends this class to
  219. support methods for validation to allow for DTD-like restrictions to be places
  220. on documents read in with the XML::Parser module.
  221.  
  222. =head1 VALIDATION RULES
  223.  
  224. In order to set up rules for validation of elements, each element should
  225. define four list values in it's own package namespace.  When validating, this
  226. module will check to ensure that any parsed attributes or child elements are
  227. actually ones that are possible for this element, as well as checking to see
  228. that any required attributes/child elements are present.
  229.  
  230. Note that an attribute/child element only has to be present in either the
  231. optional or required list; when checking for possible attributes/children,
  232. these lists will be combined.
  233.  
  234. Validation lists:
  235.  
  236. =over 4
  237.  
  238. =item @oattrs
  239.  
  240. List of optional attributes.
  241.  
  242. =item @rattrs
  243.  
  244. List of required attributes.
  245.  
  246. =item @opkids
  247.  
  248. List of optional child elements.
  249.  
  250. =item @rkids
  251.  
  252. List of required child elements.
  253.  
  254. =back
  255.  
  256. =head1 METHODS
  257.  
  258. =over 4
  259.  
  260. =item validate( err_handler )
  261.  
  262. Validates the current element.  This method calls four other methods to
  263. validate all of requirements for the element.  Returns non-zero on success and
  264. zero if any errors occurred.
  265.  
  266. =item rvalidate( err_handler )
  267.  
  268. Validates the current element, and recursively validates all child elements.
  269. This method calls four other methods to validate all of the requirements for
  270. the element.  Returns non-zero on success and zero if any errors occurred.
  271.  
  272. =item validate_possible_attrs( err_handler )
  273.  
  274. Checks against the list of attributes possible for this element (taken from
  275. @oattr and @rattr) to ensure that all of the parsed attributes are valid.  If
  276. any parsed attributes are not in the list of possible attributes for this
  277. element, err_handler will be called with a message stating the error.  Returns
  278. non-zero on success and zero if any errors occurred.
  279.  
  280. =item validate_required_attrs( err_handler )
  281.  
  282. Checks against the list of required attributes (taken from @rattr) to ensure
  283. that all of the required attributes are present and have been parsed.  If any
  284. required attributes are missing, err_handler will be called with a message
  285. stating the error.  Returns non-zero on success and zero if any errors
  286. occurred.
  287.  
  288. =item validate_possible_kids( err_handler )
  289.  
  290. Checks against the list of child elements this element can contain (taken from
  291. @okids and @rkids) to ensure that any child elements that have been read in are
  292. valid.  If any child elements have been parsed which are not in the list of
  293. possible children, err_handler will be called with a message stating the
  294. error.  Returns non-zero on success and zero if any errors occurred.
  295.  
  296. =item validate_required_kids( err_handler )
  297.  
  298. Checks against the lsit of required child elements (taken from @rkids) to
  299. ensure that all of the required child elements are present and have been
  300. parsed.  If any of the required child elements are missing, err_handler will be
  301. called with a message stating the error.  Returns non-zero on success and zero
  302. if any errors occurred.
  303.  
  304. =back
  305.  
  306. =head1 LIMITATIONS
  307.  
  308. The XML::ValidatingElement module only provides checks for determining whether
  309. or not the possible/required attributes/children are present.  This module
  310. currently has no support for determining whether or not the values provided are
  311. actually valid (although I imagine it wouldn't be too hard to add this in
  312. somewhere).  This also includes elements which have been declared in a DTD as
  313. being 'EMPTY' elements.
  314.  
  315. =head1 AUTHORS
  316.  
  317. Graham TerMarsch <grahamt@activestate.com>
  318.  
  319. =head1 HISTORY
  320.  
  321. v0.2 - Added failure return values to each of the methods.
  322.  
  323. v0.1 - Initial version
  324.  
  325. =head1 SEE ALSO
  326.  
  327. L<XML::Element>,
  328. L<XML::Parser>
  329.  
  330. =cut
  331.