Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

330 lines
10 KiB

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