File Coverage

blib/lib/Google/Ads/Common/XPathSAXParser.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             # Copyright 2011, Google Inc. All Rights Reserved.
2             #
3             # Licensed under the Apache License, Version 2.0 (the "License");
4             # you may not use this file except in compliance with the License.
5             # You may obtain a copy of the License at
6             #
7             # http://www.apache.org/licenses/LICENSE-2.0
8             #
9             # Unless required by applicable law or agreed to in writing, software
10             # distributed under the License is distributed on an "AS IS" BASIS,
11             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12             # See the License for the specific language governing permissions and
13             # limitations under the License.
14              
15             package Google::Ads::Common::XPathSAXParser;
16              
17 3     3   1969 use strict;
  3         7  
  3         86  
18 3     3   13 use warnings;
  3         6  
  3         96  
19 3     3   271 use version;
  3         1483  
  3         19  
20              
21             # The following needs to be on one line because CPAN uses a particularly hacky
22             # eval() to determine module versions.
23 3     3   767 use Google::Ads::Common::Constants; our $VERSION = ${Google::Ads::Common::Constants::VERSION};
  3         6  
  3         101  
24              
25 3     3   294 use Class::Std::Fast;
  3         9348  
  3         32  
26 3     3   361 use Carp;
  3         7  
  3         188  
27 3     3   740 use XML::XPath;
  0            
  0            
28              
29             # Keeps a mapping between SOAP objects and XPath nodes.
30             my %OBJECTS_NODES_OF;
31              
32             # SAX handlers used as callbacks during parsing.
33             my %handlers_of : ATTR(:name :default<{}>);
34              
35             # Initial XPath expression to filter results to parse.
36             # Defaults to the root element.
37             my %xpath_expression_of : ATTR(:name :default<"/*">);
38              
39             # Boolean to indicate when namespace prefixes have to be included 0 or
40             # excluded 1 from the reported names of elements parsed. Defaults to 1.
41             my %namespaces_of : ATTR(:name :default<1>);
42              
43             # Stores the current element being parsed.
44             my %current_of : ATTR(:name :default<>);
45              
46             # Start the parsing.
47             # Takes the XML to parse.
48             sub parse {
49             my $self = shift;
50             my $xml = shift;
51             my $xp = XML::XPath->new(xml => $xml);
52             my $node = $xp->find($self->get_xpath_expression())->get_node(0);
53              
54             if ($node) {
55             # Clear out the past cached objects to nodes mapping to avoid memory
56             # leaking.
57             %OBJECTS_NODES_OF = ();
58             $self->__parse_node($node);
59             $self->set_current(undef);
60             } else {
61             die "Given expression " . $self->get_xpath_expresssion() . " didn't " .
62             "match any nodes to parse.";
63             }
64             }
65              
66             # The parsing is done recursively through this method.
67             sub __parse_node {
68             my $self = shift;
69             my $node = shift;
70             my $node_type = $node->getNodeType();
71             my $parent = $node->getParentNode();
72             my %handlers = %{$self->get_handlers()};
73              
74             $self->set_current($node);
75              
76             if ($node_type == XML::XPath::Node::ELEMENT_NODE
77             && ($handlers{Start} || $handlers{End}))
78             {
79             # bundle up attributes
80             my %attribs = ();
81             foreach my $attr (@{$node->getAttributes()}) {
82             my $att_name =
83             $self->get_namespaces() ? $attr->getLocalName() : $attr->getName();
84             $attribs{$att_name} = $attr->getNodeValue();
85             }
86              
87             my $name =
88             $self->get_namespaces() ? $node->getLocalName() : $node->getName();
89             if ($handlers{Start}) {
90             $handlers{Start}($self, $name, \%attribs, $node);
91             }
92             foreach my $childNode (@{$node->getChildNodes()}) {
93             $self->__parse_node($childNode);
94             }
95             if ($handlers{End}) {
96             $handlers{End}($self, $name, \%attribs, $node);
97             }
98             } elsif ($node_type == XML::XPath::Node::TEXT_NODE && $handlers{Char}) {
99             $handlers{Char}($self, $node->getValue(), $node);
100             } elsif ($node_type == XML::XPath::Node::COMMENT_NODE && $handlers{Comment}) {
101             $handlers{Comment}($self, $node->getValue(), $node);
102             } elsif ($node_type == XML::XPath::Node::PROCESSING_INSTRUCTION_NODE
103             && $handlers{Proc})
104             {
105             $handlers{Proc}($self, $node->getTarget(), $node->getData(), $node);
106             } else {
107             croak "Unknown node type: '", ref($node), "' type ", $node_type,
108             " or not handler found.\n";
109             }
110             }
111              
112             # Retrieves the expanded namespace name of the current parsed node or any
113             # given node.
114             sub namespace {
115             my ($self, $node) = @_;
116              
117             $node = $self->get_current() if not defined $node;
118              
119             return $node->getNamespace($node->getPrefix())->getExpanded();
120             }
121              
122             # Retrieves a string representation of the current parsed node or any given
123             # node.
124             sub recognized_string {
125             my ($self, $node) = @_;
126              
127             $node = $self->get_current() if not defined $node;
128              
129             return $node->get_name();
130             }
131              
132             # Associates a SOAP object with an XPath object.
133             sub link_object_to_node {
134             my ($object, $node) = @_;
135             $OBJECTS_NODES_OF{${$object}} = $node;
136             $OBJECTS_NODES_OF{ident($node)} = $object;
137             }
138              
139             # Retrieves an XPath node given its associated SOAP Object.
140             sub get_node_from_object {
141             my ($object) = @_;
142             return $OBJECTS_NODES_OF{${$object}};
143             }
144              
145             # Retrieves a SOAP Object given its associated XPath node.
146             sub get_object_from_node {
147             my ($node) = @_;
148             return $OBJECTS_NODES_OF{ident($node)};
149             }
150              
151             return 1;
152              
153             =pod
154              
155             =head1 NAME
156              
157             Google::Ads::Common::XPathSAXParser
158              
159             =head1 DESCRIPTION
160              
161             Implements a SAX type of XML parser based on the L.
162             And it is basically used during deserialization to tie XPath module nodes
163             with SOAP objects so the XPath module search functionalities can be used
164             to find SOAP objects.
165              
166             =head1 METHODS
167              
168             =head2 parse
169              
170             Main entry point to start the parsing of the XML and report to the registered
171             handlers the parsed node in a depth-first traversal fashion as expected from
172             a SAX parser.
173              
174             =head3 Parameters
175              
176             The XML to parse.
177              
178             =head2 __parse_node
179              
180             The parsing is done recursively through this method.
181              
182             =head3 Parameters
183              
184             The node being parsed.
185              
186             =head2 namespace
187              
188             Retrieves the expanded namespace name of the current parsed node or any
189             given node.
190              
191             =head3 Parameters
192              
193             Optional an XPath node. If none given then the current parsed is used.
194              
195             =head3 Returns
196              
197             The registered namespace.
198              
199             =head2 recognized_string
200              
201             Retrieves a string representation of the current parsed node or any given
202             node.
203              
204             =head3 Parameters
205              
206             Optional an XPath node. If none given then the current parsed is used.
207              
208             =head3 Returns
209              
210             The string representation.
211              
212             =head2 link_object_to_node
213              
214             Links a SOAP Object with its XPath node.
215              
216             =head3 Parameters
217              
218             The SOAP object to associate.
219              
220             The XPath node.
221              
222             =head2 get_node_from_object
223              
224             Retrieves an XPath node given its associated SOAP Object.
225              
226             =head3 Parameters
227              
228             The SOAP object.
229              
230             =head3 Returns
231              
232             The associated XPath node if found.
233              
234             =head2 get_object_from_node
235              
236             Retrieves a SOAP Object given its associated XPath node..
237              
238             =head3 Parameters
239              
240             The XPath node.
241              
242             =head3 Returns
243              
244             The associated SOAP Object.
245              
246             =head1 LICENSE AND COPYRIGHT
247              
248             Copyright 2011 Google Inc.
249              
250             Licensed under the Apache License, Version 2.0 (the "License");
251             you may not use this file except in compliance with the License.
252             You may obtain a copy of the License at
253              
254             http://www.apache.org/licenses/LICENSE-2.0
255              
256             Unless required by applicable law or agreed to in writing, software
257             distributed under the License is distributed on an "AS IS" BASIS,
258             WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
259             See the License for the specific language governing permissions and
260             limitations under the License.
261              
262             =head1 REPOSITORY INFORMATION
263              
264             $Rev: $
265             $LastChangedBy: $
266             $Id: $
267              
268             =cut