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

  1. package SOAP::Parser;
  2.  
  3. use strict;
  4. use vars qw($VERSION);
  5. $VERSION = '0.23';
  6.  
  7. use SOAP::Defs;
  8. use SOAP::GenericInputStream;
  9. use XML::Parser::Expat;
  10.  
  11. my $enum = 0;
  12. my $m_ctx_soap_stream            = $enum++;
  13. my $m_ctx_id                     = $enum++;
  14. my $m_ctx_is_package             = $enum++;
  15. my $m_ctx_typeuri                = $enum++;
  16. my $m_ctx_typename               = $enum++;
  17. my $m_ctx_child_id               = $enum++;
  18. my $m_ctx_child_is_package       = $enum++;
  19. my $m_ctx_child_accessor_uri     = $enum++;
  20. my $m_ctx_child_typeuri          = $enum++;
  21. my $m_ctx_child_typename         = $enum++;
  22. my $m_ctx_depth                  = $enum++;
  23. my $m_ctx_package                = $enum++;
  24.  
  25. $enum = 0;
  26. my $m_pkgslot_object             = $enum++;
  27. my $m_pkgslot_resolver_list      = $enum++;
  28.  
  29. $enum = 0;
  30. my $c_accessor_type_simple   = $enum++;
  31. my $c_accessor_type_compound = $enum++;
  32.  
  33. my $g_attr_parse_table = {
  34.     $soap_id            => [$soap_namespace, 'id'           ],
  35.     $soap_href          => [$soap_namespace, 'href'         ],
  36.     $soap_package       => [$soap_namespace, 'package'      ],
  37.     $soap_root_with_id  => [$soap_namespace, 'root_with_id' ],
  38.     $xsd_type           => [$xsd_namespace,  'typename'     ],
  39.     $xsd_null           => [$xsd_namespace,  'null'         ],
  40. };
  41.  
  42. sub new {
  43.     my ($class, $type_mapper)  = @_;
  44.  
  45.     $type_mapper ||= SOAP::TypeMapper->defaultMapper();
  46.  
  47.     my $self = {
  48.         type_mapper             => $type_mapper,
  49.         parser                  => undef,
  50.         has_namespaces          => 0,
  51.         handler_stack           => [],
  52.         context_stack           => [],
  53.         text                    => '',
  54.         href                    => undef,
  55.         is_null                 => 0,
  56.         headers                 => [],
  57.         root_with_id            => 0,
  58.     };
  59.  
  60.     bless $self, $class;
  61. }
  62.  
  63. sub DESTROY {
  64.     my ($self) = @_;
  65.  
  66.     # important: Expat has internal circular refs that won't
  67.     #            get cleaned up unless you call release
  68.     $self->{parser}->release() if defined $self->{parser};
  69. }
  70.  
  71. sub parsestring {
  72.     my ($self, $soap_bar) = @_;
  73.     $self->_create_parser()->parsestring($soap_bar);
  74. }
  75.  
  76. sub parsefile {
  77.     my ($self, $file) = @_;
  78.     $self->_create_parser()->parsefile($file);
  79. }
  80.  
  81. sub get_body {
  82.     my ($self) = @_;
  83.     $self->{body_root};
  84. }
  85.  
  86. sub get_headers {
  87.     my ($self) = @_;
  88.     $self->{headers};
  89. }
  90.  
  91. sub _bootstrapper_on_start {
  92.     my ($self, $parser, $element) = (shift, shift, shift);
  93.     __diagnostic_enter_element($parser, $element);
  94.  
  95.     my $depth = $parser->depth();
  96.  
  97.     # look for Envelope
  98.     unless ($soap_envelope eq $element) { $self->_throw("expected $soap_envelope") }
  99.  
  100.     #
  101.     # determine whether or not this SOAP bar uses namespaces
  102.     # by looking for a namespace qualified start tag
  103.     #
  104.     $self->{has_namespaces} = my $has_namespaces = defined $parser->namespace($element);
  105.  
  106.     #
  107.     # if there *is* a namespace, make sure it's the SOAP namespace
  108.     #
  109.     $self->_verify_soap_namespace($parser, $element);
  110.  
  111.     $self->_push_context(undef, 0, 1);
  112.  
  113.     $self->_push_handlers(Start => sub { $self->_envelope_on_start(@_) },
  114.                           Char  => sub { $self->_envelope_on_char (@_) },
  115.                           End   => sub { $self->_envelope_on_end  (@_) },
  116.                          );
  117. }
  118.  
  119. sub _envelope_on_start {
  120.     my ($self, $parser, $element) = (shift, shift, shift);
  121.     __diagnostic_enter_element($parser, $element);
  122.  
  123.     $self->_verify_no_new_namespaces($parser) unless $self->{has_namespaces};
  124.  
  125.     if ($soap_body eq $element) {
  126.         $self->_verify_soap_namespace($parser, $element);
  127.  
  128.         $self->_push_handlers(Start => sub { $self->_body_on_start(@_) },
  129.                               Char  => sub { $self->_body_on_char (@_) },
  130.                               End   => sub { $self->_body_on_end  (@_) },
  131.                               );
  132.     }
  133.     elsif ($soap_header eq $element) {
  134.         unless (2 == $parser->element_index()) { $self->_throw("Unexpected $soap_header element (if present, $soap_header must be the first element under $soap_envelope)") }
  135.         $self->_verify_soap_namespace($parser, $element);
  136.  
  137.         $self->_push_handlers(Start => sub { $self->_header_on_start(@_) },
  138.                               Char  => sub { $self->_header_on_char (@_) },
  139.                               End   => sub { $self->_header_on_end  (@_) },
  140.                               );
  141.     }
  142.     else {
  143.         $self->_throw("Unexpected element: $element");
  144.     }
  145. }
  146.  
  147. sub _envelope_on_char {
  148.     my ($self, $parser, $s) = @_;
  149.     $self->_complain_if_contains_non_whitespace($s);
  150. }
  151.  
  152. sub _envelope_on_end {
  153.     my ($self, $parser, $element) = @_;
  154.     __diagnostic_leave_element($parser, $element);
  155.     
  156.     $self->_pop_context();
  157.     $self->_pop_handlers();
  158. }
  159.  
  160. sub _header_on_start {
  161.     my ($self, $parser, $element) = (shift, shift, shift);
  162.  
  163.     __diagnostic_enter_element($parser, $element);
  164.     $self->_verify_no_new_namespaces($parser) unless $self->{has_namespaces};
  165.  
  166.     # TBD: how can I verify that the header has an explicit namespace qualifier?
  167.     #      (what I'm wondering is if there will be any headers that come out
  168.     #       of urn:schemas-xmlsoap-org:soap.v1
  169.     #
  170.     #      perhaps i can use new_ns_prefixes somehow...
  171.  
  172.     $self->_parse_child_element_attrs($parser, \@_);
  173.  
  174.     my $id = $self->_child_id();
  175.  
  176.     my $is_root = !defined($id) || $self->{root_with_id};
  177.  
  178.     my $child_typeuri;
  179.     my $child_typename;
  180.  
  181.     #
  182.     # if no explicit type is specified, use the element name as the type
  183.     # (this only applies to independent elements)
  184.     #
  185.     unless ($is_root) {    
  186.         $child_typename = $self->_child_typename();
  187.         if (defined $child_typename) {
  188.             $child_typeuri = $self->_child_typeuri();
  189.         }
  190.     }
  191.  
  192.     #
  193.     # pick up the type for header roots (and indep. elems without explicit xsi:type)
  194.     #
  195.     unless (defined $child_typename) {
  196.         $child_typename = $element;
  197.         $child_typeuri  = $parser->namespace($element);
  198.     }
  199.  
  200.     my $resolver;
  201.     if ($is_root) {
  202.         #
  203.         # the roots could potentially be references
  204.         #
  205.         if (my $href = $self->{href}) {
  206.             my ($found_it, $result) = $self->_lookup_href($href);
  207.             if ($found_it) {
  208.                 $self->_add_header($child_typeuri, $child_typename, $result);
  209.             }
  210.             else {
  211.                 push @$result, sub {
  212.                     $self->_add_header($child_typeuri, $child_typename, shift);
  213.                 };
  214.             }
  215.             $self->_push_handlers(Start => sub { $self->_ref_on_start(@_) },
  216.                                   Char  => sub { $self->_ref_on_char (@_) },
  217.                                   End   => sub { $self->_ref_on_end  (@_) },
  218.                                  );
  219.             #
  220.             # there's nothing more to do in this case
  221.             #
  222.             return;
  223.         }
  224.         $resolver = sub {
  225.             my $object = shift;
  226.             $self->_add_header($child_typeuri, $child_typename, $object);
  227.             $self->_found_id($id, $object) if $id;
  228.         };
  229.     }
  230.     else {
  231.         $resolver = sub { $self->_found_id($id, shift) };
  232.     }
  233.  
  234.     my $type_mapper = $self->{type_mapper};
  235.     my $stream = $type_mapper->get_deserializer($child_typeuri,
  236.                                                 $child_typename,
  237.                                                 $resolver);
  238.  
  239.     $self->_push_context($stream, $parser->depth() + 1, $self->_child_is_package());
  240.  
  241.     $self->_push_handlers(Start => sub { $self->_generic_on_start(@_) },
  242.                           Char  => sub { $self->_generic_on_char (@_) },
  243.                           End   => sub { $self->_generic_on_end  (@_) },
  244.                          );
  245. }
  246.  
  247. sub _header_on_char {
  248.     my ($self, $parser, $s) = @_;
  249.     $self->_complain_if_contains_non_whitespace($s);
  250. }
  251.  
  252. sub _header_on_end {
  253.     my ($self, $parser, $element) = @_;
  254.     __diagnostic_leave_element($parser, $element);
  255.  
  256.     #
  257.     # note that both Body and Headers don't pop the root context,
  258.     # rather they defer to Envelope, since Envelope is a package,
  259.     # not Body or Headers.
  260.     #
  261.     $self->_pop_handlers();
  262. }
  263.  
  264. sub _body_on_start {
  265.     my ($self, $parser, $element) = (shift, shift, shift);
  266.  
  267.     __diagnostic_enter_element($parser, $element);
  268.     $self->_verify_no_new_namespaces($parser) unless $self->{has_namespaces};
  269.  
  270.     $self->_parse_child_element_attrs($parser, \@_);
  271.  
  272.     my $id = $self->_child_id();
  273.  
  274.     my $resolver;
  275.     if (exists $parser->{body_root}) {
  276.         # we've already seen the body root, so this is an independent element
  277.         # and independent elements *must* have ids
  278.         unless (defined $id) { $self->_throw("$element is an independent element with no id attribute") }
  279.  
  280.         $resolver = sub { $self->_found_id($id, shift) };
  281.     }
  282.     else {
  283.         # the first element under SOAP:Body is the root - indicate that we've seen it
  284.         $parser->{body_root} = undef;
  285.         #
  286.         # the roots could potentially be references
  287.         #
  288.         if (my $href = $self->{href}) {
  289.             my ($found_it, $result) = $self->_lookup_href($href);
  290.             if ($found_it) {
  291.                 $self->{body_root} = $result;
  292.             }
  293.             else {
  294.                 push @$result, sub { $self->{body_root} = shift; };
  295.             }
  296.             $self->_push_handlers(Start => sub { $self->_ref_on_start(@_) },
  297.                                   Char  => sub { $self->_ref_on_char (@_) },
  298.                                   End   => sub { $self->_ref_on_end  (@_) },
  299.                                  );
  300.             #
  301.             # there's nothing more to do in this case
  302.             #
  303.             return;
  304.         }
  305.         $resolver = sub {
  306.             my $object = shift;
  307.             $self->_found_id($id, $object) if defined $id;
  308.             $self->{body_root} = $object;
  309.         };
  310.     }
  311.     
  312.     #
  313.     # if no explicit type is specified, use the element name as the type
  314.     #
  315.     my $child_typeuri;
  316.     my $child_typename = $self->_child_typename();
  317.     if (defined $child_typename) {
  318.         $child_typeuri = $self->_child_typeuri();
  319.     }
  320.     else {
  321.         $child_typename = $element;
  322.         $child_typeuri  = $parser->namespace($element);
  323.     }
  324.  
  325.     my $type_mapper = $self->{type_mapper};
  326.     my $stream = $type_mapper->get_deserializer($child_typeuri,
  327.                                                 $child_typename,
  328.                                                 $resolver);
  329.  
  330.     $self->_push_context($stream, $parser->depth() + 1, $self->_child_is_package());
  331.  
  332.     $self->_push_handlers(Start => sub { $self->_generic_on_start(@_) },
  333.                           Char  => sub { $self->_generic_on_char (@_) },
  334.                           End   => sub { $self->_generic_on_end  (@_) },
  335.                          );
  336. }
  337.  
  338. sub _body_on_char {
  339.     my ($self, $parser, $s) = @_;
  340.     $self->_complain_if_contains_non_whitespace($s);
  341. }
  342.  
  343. sub _body_on_end {
  344.     my ($self, $parser, $element) = @_;
  345.     __diagnostic_leave_element($parser, $element);
  346.  
  347.     #
  348.     # note that both Body and Headers don't pop the root context,
  349.     # rather they defer to Envelope, since Envelope is a package,
  350.     # not Body or Headers.
  351.     #
  352.     $self->_pop_handlers();
  353. }
  354.  
  355. sub _generic_on_start {
  356.     my ($self, $parser, $element) = (shift, shift, shift);
  357.  
  358.     __diagnostic_enter_element($parser, $element);
  359.     $self->_verify_no_new_namespaces($parser) unless $self->{has_namespaces};
  360.  
  361.     my $depth       = $parser->depth();
  362.     my $ctx_depth   = $self->_get_ctx_depth();
  363.  
  364.     if ($depth != $ctx_depth) {
  365.         #
  366.         # we just drilled down another level, so we need to setup
  367.         # a new marshaler for this node.
  368.         #
  369.         $self->_complain_if_contains_non_whitespace($self->{text});
  370.  
  371.         my $parent_id           = $self->_child_id();
  372.         my $parent_is_package   = $self->_child_is_package();
  373.         my $parent_accessor_uri = $self->_child_accessor_uri();
  374.         my $parent_typeuri      = $self->_child_typeuri();
  375.         my $parent_typename     = $self->_child_typename();
  376.  
  377.         #
  378.         # we need to determine whether this is an accessor or not...
  379.         # right now, we use the presence of the id attribute
  380.         #
  381.         my $is_root = defined $parent_id;
  382.  
  383.         my $parent_accessor = (defined $parent_id) ? undef : $parser->current_element();
  384.  
  385.         my $new_stream;
  386.         if ($is_root) {
  387.             my $resolver = sub { $self->_found_id($parent_id, shift) };
  388.             my $type_mapper = $self->{type_mapper};
  389.             $new_stream = $type_mapper->get_deserializer($parent_typeuri,
  390.                                                          $parent_typename,
  391.                                                          $resolver);
  392.         }
  393.         else {
  394.             #
  395.             # TBD: for an accessor, why would the Parser care about being notified
  396.             #      when the object is unmarshaled? I can see the parent stream wanting
  397.             #      to know (so it can add the object as an accessor). Exactly why do we
  398.             #      pass $resolver as a parameter to compound_accessor???
  399.             #
  400.             my $resolver = 0;
  401.             $new_stream = $self->_soap_stream()->compound_accessor($parent_accessor_uri,
  402.                                                                    $parent_accessor,
  403.                                                                    $parent_typeuri,
  404.                                                                    $parent_typename,
  405.                                                                    $resolver);
  406.             unless ($new_stream) { $self->_throw("Unexpected: compound_accessor failed to return a new stream") }
  407.         }
  408.         $self->_push_context($new_stream, $parser->depth(), $parent_is_package);
  409.  
  410.         #
  411.         # remember important stuff about the new node
  412.         #
  413.         $self->_id        ($parent_id        );
  414.         $self->_typeuri   ($parent_typeuri   );
  415.         $self->_typename  ($parent_typename  );
  416.     }
  417.  
  418.     $self->_parse_child_element_attrs($parser, \@_);
  419.  
  420.     $self->_child_accessor_uri($parser->namespace($element));
  421.     $self->{text} = '';
  422.  
  423.     # TBD: how much checking do we want to do for invalid attribute combinations?
  424.     #      (for instance, if xsd:null="1", then it doesn't make sense
  425.     #       to also have an href attribute)
  426.     if ($self->{is_null}) {
  427.         $self->_push_handlers(Start => sub { $self->_null_on_start(@_) },
  428.                               Char  => sub { $self->_null_on_char (@_) },
  429.                               End   => sub { $self->_null_on_end  (@_) }
  430.                               );
  431.     }
  432.     elsif (my $href = $self->{href}) {
  433.         if (defined $self->_child_id()) { $self->_throw('SOAP elements cannot contain both href and id attributes') }
  434.  
  435.         my ($found_it, $result) = $self->_lookup_href($href);
  436.  
  437.         my $soap_stream = $self->_soap_stream();
  438.         if ($found_it) {
  439.             $soap_stream->reference_accessor($parser->namespace($element),
  440.                                              $element,
  441.                                              $result);
  442.         }
  443.         else {
  444.             push @$result, $soap_stream->forward_reference_accessor($parser->namespace($element),
  445.                                                                     $element);
  446.         }
  447.         $self->_push_handlers(Start => sub { $self->_ref_on_start(@_) },
  448.                               Char  => sub { $self->_ref_on_char (@_) },
  449.                               End   => sub { $self->_ref_on_end  (@_) }
  450.                               );
  451.     }
  452. }
  453.  
  454. sub _generic_on_char {
  455.     my ($self, $parser, $s) = @_;
  456.     $self->{text} .= $s;
  457. }
  458.  
  459. sub _generic_on_end {
  460.     my ($self, $parser, $element) = @_;
  461.     __diagnostic_leave_element($parser, $element);
  462.  
  463.     my $depth     = $parser->depth();
  464.     my $ctx_depth = $self->_get_ctx_depth();
  465.  
  466.     if ($depth == $ctx_depth) {
  467.         #
  468.         # this is a simple accessor
  469.         #
  470.         $self->_soap_stream()->simple_accessor($parser->namespace($element),
  471.                                                $element,
  472.                                                $self->_child_typeuri(),
  473.                                                $self->_child_typename(),
  474.                                                $self->{text});
  475.     }
  476.     else {
  477.         #
  478.         # we just left the scope of the current compound accessor,
  479.         # so we need to close the current marshaling scope
  480.         #
  481.     my $stream = $self->_soap_stream();
  482.     my $text = $self->{text};
  483.     $stream->content($text) if (length $text and $text =~ /\S/);
  484.         $stream->term();
  485.         $self->_pop_context();
  486.     }
  487.     $self->{text} = '';
  488.     $self->_pop_handlers();
  489. }
  490.  
  491. sub _ref_on_start {
  492.     my $self = shift;
  493.     $self->_throw('Elements with the href attribute cannot have child nodes');
  494. }
  495.  
  496. sub _ref_on_char {
  497.     my ($self, $parser, $s) = @_;
  498.  
  499.     $self->_complain_if_contains_non_whitespace($s);
  500. }
  501.  
  502. sub _ref_on_end {
  503.     my ($self, $parser, $element) = @_;
  504.     __diagnostic_leave_element($parser, $element);
  505.  
  506.     $self->_pop_handlers();
  507. }
  508.  
  509. sub _null_on_start {
  510.     my $self = shift;
  511.     $self->_throw('Elements with the xsd:null attribute cannot have child nodes');
  512. }
  513.  
  514. sub _null_on_char {
  515.     my $self = shift;
  516.                                     # TBD: is this correct?
  517.     $self->_throw('Elements with the xsd:null attribute must be empty of content');
  518. }
  519.  
  520. sub _null_on_end {
  521.     my ($self, $parser, $element) = @_;
  522.  
  523.     $self->_soap_stream()->reference_accessor($parser->namespace($element),
  524.                                               $element,
  525.                                               undef);
  526.     $self->_pop_handlers();
  527. }
  528.  
  529. sub _found_id {
  530.     my ($self, $id, $object) = @_;
  531.  
  532.     my $package = $self->_get_package();
  533.     my $slot;
  534.     if (exists $package->{$id}) {
  535.         $slot = $package->{$id};
  536.         if (defined $slot->[$m_pkgslot_object]) { $self->_throw("Duplicate id: $id") }
  537.         $slot->[$m_pkgslot_object] = $object;
  538.         my $resolver_list = pop @$slot;
  539.         foreach my $resolver (@$resolver_list) {
  540.             $resolver->($object);
  541.         }
  542.     }
  543.     else {
  544.         $package->{$id} = [$object];
  545.     }
  546. }
  547.  
  548. sub _lookup_href {
  549.     my ($self, $href) = @_;
  550.  
  551.     my $package = $self->_get_package();
  552.     my $slot;
  553.     if (exists $package->{$href}) {
  554.         $slot = $package->{$href};
  555.     }
  556.     else {
  557.         $slot = $package->{$href} = [undef, []];
  558.     }
  559.  
  560.     if (defined $slot->[$m_pkgslot_object]) {
  561.         (1, $slot->[$m_pkgslot_object]);
  562.     }
  563.     else {
  564.         (0, $slot->[$m_pkgslot_resolver_list]);
  565.     }
  566. }
  567.  
  568. sub _verify_resolved_all_references {
  569.     my ($self, $package) = @_;
  570.  
  571.     my @unresolved_refs;
  572.     while (my ($id, $slot) = each %$package) {
  573.         #
  574.         # resolved slots will only have one entry - the object
  575.         #
  576.         if (1 != @$slot) {
  577.             push @unresolved_refs, $id;
  578.         }
  579.     }
  580.     if (@unresolved_refs) {
  581.         my $ids = join ', ', @unresolved_refs;
  582.         $self->_throw("Could not resolve the following references: $ids");
  583.     }
  584. }
  585.  
  586. sub _push_context {
  587.     my ($self, $soap_stream, $depth, $create_new_package) = @_;
  588.  
  589.     my $package = $create_new_package ? {} : $self->_get_package();
  590.  
  591.     push @{$self->{context_stack}}, [
  592.         $soap_stream,           # $m_ctx_soap_stream
  593.         undef,                  # $m_ctx_id
  594.         $create_new_package,    # $m_ctx_is_package
  595.         undef,                  # $m_ctx_typeuri
  596.         undef,                  # $m_ctx_typename
  597.         undef,                  # $m_ctx_child_id
  598.         undef,                  # $m_ctx_child_is_package
  599.         undef,                  # $m_ctx_child_accessor_uri
  600.         undef,                  # $m_ctx_child_typeuri
  601.         undef,                  # $m_ctx_child_typename
  602.         $depth,                 # $m_ctx_depth
  603.         $package,               # $m_ctx_package
  604.     ];
  605. }
  606.  
  607. sub _pop_context {
  608.     my ($self) = @_;
  609.  
  610.     if ($self->_is_package()) {
  611.         $self->_verify_resolved_all_references($self->_get_package());
  612.     }
  613.     pop @{$self->{context_stack}};
  614. }
  615.  
  616. sub _soap_stream {
  617.     my $self = shift;
  618.     $self->_set_or_get_context_item($m_ctx_soap_stream, @_);
  619. }
  620.  
  621. sub _id {
  622.     my $self = shift;
  623.     $self->_set_or_get_context_item($m_ctx_id, @_);
  624. }
  625.  
  626. sub _is_package {
  627.     my $self = shift;
  628.     $self->_set_or_get_context_item($m_ctx_is_package, @_);
  629. }
  630.  
  631. sub _typeuri {
  632.     my $self = shift;
  633.     $self->_set_or_get_context_item($m_ctx_typeuri, @_);
  634. }
  635.  
  636. sub _typename {
  637.     my $self = shift;
  638.     $self->_set_or_get_context_item($m_ctx_typename, @_);
  639. }
  640.  
  641. sub _child_id {
  642.     my $self = shift;
  643.     $self->_set_or_get_context_item($m_ctx_child_id, @_);
  644. }
  645.  
  646. sub _child_is_package {
  647.     my $self = shift;
  648.     $self->_set_or_get_context_item($m_ctx_child_is_package, @_);
  649. }
  650.  
  651. sub _child_accessor_uri {
  652.     my $self = shift;
  653.     $self->_set_or_get_context_item($m_ctx_child_accessor_uri, @_);
  654. }
  655.  
  656. sub _child_typeuri {
  657.     my $self = shift;
  658.     $self->_set_or_get_context_item($m_ctx_child_typeuri, @_);
  659. }
  660.  
  661. sub _child_typename {
  662.     my $self = shift;
  663.     $self->_set_or_get_context_item($m_ctx_child_typename, @_);
  664. }
  665.  
  666. sub _get_ctx_depth {
  667.     my ($self) = @_;
  668.     $self->{context_stack}[-1][$m_ctx_depth];
  669. }
  670.  
  671. sub _get_package {
  672.     my ($self) = @_;
  673.     $self->{context_stack}[-1][$m_ctx_package];
  674. }
  675.  
  676. sub _set_or_get_context_item {
  677.     my ($self, $index) = @_;
  678.     return $self->{context_stack}[-1][$index] if (2 == @_);
  679.     $self->{context_stack}[-1][$index] = $_[2];
  680. }
  681.  
  682. sub _parse_child_element_attrs {
  683.     my ($self, $parser, $attrs) = @_;
  684.  
  685.     $self->{href}           = undef;
  686.     $self->{is_null}        = 0;
  687.     $self->{root_with_id}   = 0;
  688.  
  689.     $self->_child_id(undef);
  690.     $self->_child_is_package(0);
  691.     $self->_child_typeuri(undef);
  692.     $self->_child_typename(undef);
  693.  
  694.     for (my $i = 0; $i < @$attrs; $i += 2) {
  695.         my $attr = $attrs->[$i];
  696.         if (exists $g_attr_parse_table->{$attr}) {
  697.             my ($ns, $method_suffix) = @{$g_attr_parse_table->{$attr}};
  698.             if ($self->{has_namespaces}) {
  699.                 #
  700.                 # verify namespace
  701.                 #
  702.                 my $expected_qname = $parser->generate_ns_name($attr, $ns);
  703.  
  704.                 #
  705.                 # this code assumes we're being called in the context of a start tag
  706.                 #
  707.                 unless ($parser->eq_name($attr, $expected_qname)) {
  708.                     my $actual_ns = $parser->namespace($attr);
  709.                     if ($actual_ns) {
  710.                         $actual_ns = " (in namespace $actual_ns)";
  711.                     }
  712.                     else {
  713.                         $actual_ns = '';
  714.                     }
  715.                     $self->_throw("Unrecognized attribute $attr$actual_ns");
  716.                 }
  717.             }
  718.             my $method_name = '_parse_attr_' . $method_suffix;
  719.             $self->$method_name($attrs->[$i+1]);
  720.         }
  721.         else {
  722.             $self->_throw("Unrecognized attribute $attr");
  723.         }
  724.     }
  725. }
  726.  
  727. sub _parse_attr_id {
  728.     my ($self, $value) = @_;
  729.  
  730.     $self->_child_id($value);
  731. }
  732.  
  733. sub _parse_attr_href {
  734.     my ($self, $value) = @_;
  735.  
  736.     unless ($value =~ s/^#(.+)/$1/) { $self->_throw('Badly formed href') }
  737.  
  738.     $self->{href} = $value;
  739. }
  740.  
  741. sub _parse_attr_null {
  742.     my ($self, $value) = @_;
  743.  
  744.     $self->{is_null} = $soap_true eq $value;
  745. }
  746.  
  747. sub _parse_attr_package {
  748.     my ($self, $value) = @_;
  749.  
  750.     $self->_child_is_package($soap_true eq $value);
  751. }
  752.  
  753. sub _parse_attr_typename {
  754.     my ($self, $value) = @_;
  755.  
  756.     my ($typeuri, $typename) = $self->_resolve_xsd_type($value);
  757.     $self->_child_typeuri($typeuri);
  758.     $self->_child_typename($typename);
  759. }
  760.  
  761. sub _parse_attr_root_with_id {
  762.     my ($self, $value) = @_;
  763.  
  764.     $self->{root_with_id} = $soap_true eq $value;
  765. }
  766.  
  767. sub _resolve_xsd_type {
  768.     my ($self, $typename) = @_;
  769.  
  770.     #
  771.     # TBD: what if no namespace prefix appears? Do we use the default namespace?
  772.     #
  773.     my $parser = $self->{parser};
  774.     my ($ns, $name);
  775.     if ($typename =~ /([^:]+):(.+$)/) {
  776.         my $ns_prefix = $1;
  777.         $name = $2;
  778.         $ns = $parser->expand_ns_prefix($ns_prefix);
  779.     }
  780.     unless (defined $ns) {
  781.         $name = $typename;
  782.         $ns = $parser->expand_ns_prefix('#default');
  783.     }
  784.     ($ns, $name);
  785. }
  786.  
  787. sub _complain_if_contains_non_whitespace {
  788.     my ($self, $text) = @_;
  789.  
  790.     if ($text =~ /\S/) { $self->_throw('Unexpected non-whitespace character') }
  791. }
  792.  
  793. sub _verify_no_new_namespaces {
  794.     my ($self, $parser) = @_;
  795.     #
  796.     # verify that nobody introduces any namespaces
  797.     # if the root isn't namespace qualified
  798.     #
  799.     if (scalar $parser->new_ns_prefixes()) {
  800.         $self->_throw('Unexpected namespace declaration');
  801.     }
  802. }
  803.  
  804. sub _verify_soap_namespace {
  805.     my ($self, $parser, $element) = @_;
  806.     my $ns = $parser->namespace($element);
  807.     if ($self->{has_namespaces}) {
  808.         if (!defined($ns) || ($soap_namespace ne $ns)) {
  809.             $self->_throw("expected namespace $soap_namespace on element $element");
  810.         }
  811.     }
  812. }
  813.  
  814. sub _create_parser {
  815.     my ($self) = @_;
  816.     $self->_assert(!$self->{parser});
  817.     my $parser = XML::Parser::Expat->new(Namespaces => 1);
  818.     $parser->setHandlers(Start => sub { $self->_bootstrapper_on_start(@_) } );
  819.     $self->{parser} = $parser;
  820. }
  821.  
  822. sub _add_header {
  823.     my ($self, $typeuri, $typename, $object) = @_;
  824.     my $headers = $self->{headers};
  825.     push @$headers, { soap_typeuri  => $typeuri,
  826.               soap_typename => $typename,
  827.               content       => $object
  828.                     };
  829. }
  830.  
  831. sub _push_handlers {
  832.     my $self = shift;
  833.     $self->_assert(@_);
  834.     my $parser        = $self->{parser};
  835.     my $handler_stack = $self->{handler_stack};
  836.  
  837.     my $depth = $parser->depth();
  838.     my @old_handlers = $parser->setHandlers(@_);
  839.     push @$handler_stack, [$depth, \@old_handlers];
  840. }
  841.  
  842. sub _pop_handlers {
  843.     my $self = shift;
  844.     my $parser        = $self->{parser};
  845.     my $handler_stack = $self->{handler_stack};
  846.  
  847.     while(@$handler_stack)
  848.     {
  849.         my $top = $handler_stack->[-1];
  850.     
  851.         last unless ($top->[0] == $parser->depth());
  852.  
  853.         $parser->setHandlers(@{$top->[1]});
  854.         pop @$handler_stack;
  855.     }
  856. }
  857.  
  858. sub _throw {
  859.     my ($self, $msg) = @_;
  860.  
  861.     if (defined $self->{parser}) {
  862.         $self->{parser}->xpcroak($msg);
  863.     }
  864.     else {
  865.         die $msg;
  866.     }
  867. }
  868.  
  869. sub _assert {
  870.     my ($self, $assertion, $msg) = @_;
  871.     $msg ||= '';
  872.     unless($assertion) { $self->_throw('ASSERTION FAILED. ' . $msg) }
  873. }
  874.  
  875. sub __diagnostic_enter_element {
  876. #    my ($parser, $element) = @_;
  877. #    print ' ' x (2 * $parser->depth()), "<$element>\n";
  878. }
  879.  
  880. sub __diagnostic_leave_element {
  881. #    my ($parser, $element) = @_;
  882. #    print ' ' x (2 * $parser->depth()), "</$element>\n";
  883. }
  884.  
  885. 1;
  886.  
  887. __END__
  888.  
  889. =head1 NAME
  890.  
  891. SOAP::Parser - Parses SOAP documents
  892.  
  893. =head1 SYNOPSIS
  894.  
  895.     use SOAP::Parser;
  896.   
  897.     my $parser = SOAP::Parser->new();
  898.  
  899.     $parser->parsefile('soap.xml');
  900.  
  901.     my $headers = $parser->get_headers();
  902.     my $body    = $parser->get_body();
  903.  
  904. =head1 DESCRIPTION
  905.  
  906. SOAP::Parser has all the logic for traversing a SOAP packet, including
  907. Envelope, Header, and Body, dealing with namespaces and tracking down
  908. references. It is basically an extension of a SAX-like parser, which
  909. means that it exposes an event-driven interface that you can implement
  910. to get the results of the parse. By default, SOAP/Perl provides
  911. SOAP::GenericInputStream to handle these events, which simply produces
  912. an object graph of hash references. If you want something
  913. different, on a per type URI basis, you can register alternate handlers
  914. so you can produce different output. See SOAP::TypeMapper for details.
  915.  
  916. The handler needs to implement a set of methods, and these are outlined
  917. in SOAP::GenericInputStream along with descriptions of what the default
  918. behavior is (in other words, what SOAP::GenericInputStream does for each
  919. of these methods).
  920.  
  921. The benefit of this design is that it avoids using a DOM to parse SOAP
  922. packets; rather, the packet is unmarshaled directly into whatever final
  923. form you need. This is more efficient in space and time than first unmarshaling
  924. into a DOM and then traversing the DOM to create an object graph that is
  925. meaningful to your program. To get the full benefit of this, you may need to
  926. implement a handler that creates your custom object graph from the SOAP packet
  927. (see SOAP::GenericInputStream for details). Since SOAP::Parser does all the
  928. hard work, implementing a handler (or set of handlers) is really pretty
  929. painless.
  930.  
  931. =head2 new(TypeMapper)
  932.  
  933. Creates a new parser. Be sure *not* to reuse a parser for multiple SOAP
  934. packets - create one, use it, and then throw it away and get a new one if you
  935. need to parse a second SOAP packet.
  936.  
  937. TypeMapper is an optional parameter that points to an instance of SOAP::TypeMapper
  938. that allows you to register alternate serializers and deserializers for different
  939. classes of objects. See the docs for that class for more details. If you don't
  940. pass this parameter, the system uses a global TypeMapper object.
  941.  
  942. =head2 parsestring(String)
  943.  
  944. Parses the given string.
  945.  
  946. =head2 parsefile(Filename)
  947.  
  948. Parses the given file.
  949.  
  950. =head2 get_headers()
  951.  
  952. After parsing, this function returns the array of headers
  953. in the SOAP envelope.
  954.  
  955. Specifically, this function returns an array reference that
  956. contains zero or more hash references, each
  957. of which always take the following form:
  958.  
  959.   {
  960.     soap_typeuri  => 'namespace qualification of header',
  961.     soap_typename => 'unqualified name of header',
  962.     content       => <header object>
  963.   }
  964.  
  965. For instance, the following header:
  966.  
  967.  <f:MyHeader xmlns:f="urn:foo">42 </f:MyHeader>
  968.  
  969. would be deserialized in this form:
  970.  
  971.   {
  972.     soap_typeuri  => 'urn:foo',
  973.     soap_typename => 'MyHeader',
  974.     content       => 42,
  975.   }
  976.  
  977. while this header:
  978.  
  979.  <f:MyHeader xmlns:f="urn:foo">
  980.   <m1>something</m1>
  981.   <m2>something else</m2>
  982.  </f:MyHeader>
  983.  
  984. would be deserialized (by default) in this form:
  985.  
  986.   {
  987.     soap_typeuri  => 'urn:foo',
  988.     soap_typename => 'MyHeader',
  989.     content       => {
  990.         soap_typeuri  => 'urn:foo',
  991.         soap_typename => 'MyHeader',
  992.         m1 => 'something',
  993.         m2 => 'something else',
  994.     },
  995.   }
  996.  
  997. Note the redundancy of the soap_typeuri and soap_typename isn't
  998. strictly necessary in this case because this information is embedded
  999. in the content itself. However, because of the potential (and common
  1000. need) for sending scalars as the entirety of the header content,
  1001. we need some way of communicating the namespace and typename of the
  1002. header. Thus the content, for consistency, is always packaged in
  1003. a hash along with explicit type information. 
  1004.  
  1005. =head2 get_body()
  1006.  
  1007. After parsing, this function retrieves the body of the SOAP envelope.
  1008.  
  1009. Since it doesn't make sense to send just a scalar as the body
  1010. of a SOAP request, we don't need the redundancy of packaging the content
  1011. inside of a hash along with its type and namespace (as was done above
  1012. with headers). For instance:
  1013.  
  1014.  
  1015.  <f:MyBody xmlns:f="urn:foo">
  1016.   <m1>something</m1>
  1017.   <m2>something else</m2>
  1018.  </f:MyBody>
  1019.  
  1020. would be deserialized (by default) as the following:
  1021.  
  1022.  {
  1023.    soap_typeuri  => 'urn:foo',
  1024.    soap_typename => 'MyBody',
  1025.    m1 => 'something',
  1026.    m2 => 'something else',
  1027.  }
  1028.  
  1029. =head1 DEPENDENCIES
  1030.  
  1031. XML::Parser::Expat
  1032. SOAP::GenericInputStream
  1033. SOAP::Defs
  1034.  
  1035. =head1 AUTHOR
  1036.  
  1037. Keith Brown
  1038.  
  1039. =head1 SEE ALSO
  1040.  
  1041. SOAP::GenericInputStream
  1042.  
  1043. =cut
  1044.