File Coverage

blib/lib/WebService/Cmis/AtomFeed.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package WebService::Cmis::AtomFeed;
2              
3             =head1 NAME
4              
5             WebService::Cmis::AtomFeed - Representation of an Atom feed.
6              
7             =head1 DESCRIPTION
8              
9             This class is subclassed to further specify the type of entries in this collection.
10              
11             Sub-classes:
12              
13             =over 4
14              
15             =item * L
16              
17             =item * L
18              
19             =item * L
20              
21             =back
22              
23             Sub-classes must implement to specify how to instantiate objects of this feed.
24              
25             =cut
26              
27 1     1   11279 use strict;
  1         4  
  1         51  
28 1     1   6 use warnings;
  1         3  
  1         49  
29 1     1   6 use WebService::Cmis qw(:namespaces :relations :utils);
  1         2  
  1         362  
30 1     1   523 use XML::LibXML ();
  0            
  0            
31              
32             our $CMIS_XPATH_ENTRY = new XML::LibXML::XPathExpression('./*[local-name() = "entry"]');
33             our $CMIS_XPATH_TITLE = new XML::LibXML::XPathExpression('./*[local-name() = "title" and namespace-uri() ="'.ATOM_NS.'"]');
34             our $CMIS_XPATH_UPDATED = new XML::LibXML::XPathExpression('./*[local-name() = "updated" and namespace-uri() ="'.ATOM_NS.'"]');
35             our $CMIS_XPATH_GENERATOR = new XML::LibXML::XPathExpression('./*[local-name() = "generator" and namespace-uri() ="'.ATOM_NS.'"]');
36             our $CMIS_XPATH_NUMITEMS = new XML::LibXML::XPathExpression('./*[local-name() = "numItems" and namespace-uri() ="'.CMISRA_NS.'"]');
37             our $CMIS_XPATH_PAGESIZE = new XML::LibXML::XPathExpression('./*[local-name() = "itemsPerPage" and namespace-uri() ="'.OPENSEARCH_NS.'"]');
38             our $CMIS_XPATH_TOTALRESULTS = new XML::LibXML::XPathExpression('./*[local-name() = "totalResults" and namespace-uri() ="'.OPENSEARCH_NS.'"]');
39              
40             =head1 METHODS
41              
42             =over 4
43              
44             =item new(%params)
45              
46             Create a new WebService::Cmis::AtomFeed object.
47              
48             =cut
49              
50             sub new {
51             my $class = shift;
52              
53             my $this = bless({@_ }, $class);
54              
55             throw Error::Simple("no xmlDoc in constructor") unless defined $this->{xmlDoc};
56              
57             $this->_initData;
58              
59             return $this;
60             }
61              
62             sub _initData {
63             my $this = shift;
64              
65             $this->{index} = 0;
66             $this->{entries} = undef;
67             $this->{title} = undef;
68             $this->{updated} = undef;
69             $this->{generator} = undef;
70             $this->{totalResults} = undef;
71             $this->{pageSize} = undef;
72             }
73              
74             sub DESTROY {
75             my $this = shift;
76              
77             $this->_initData;
78              
79             $this->{repository} = undef;
80             $this->{xmlDoc} = undef;
81             }
82              
83             =item getLink($relation) -> $href
84              
85             returns the link found in the feed's XML for the specified relation
86              
87             =cut
88              
89             sub getLink {
90             my ($this, $relation) = @_;
91              
92             my ($linkNode) = $this->{xmlDoc}->documentElement->findnodes('./*[local-name() = "link" and @rel="'.$relation.'"][1]/@href');
93             return $linkNode->value if $linkNode;
94             }
95              
96             # given a specified $relation, does a get using that link (if one exists)
97             # and then converts the resulting XML into a list of
98             # AtomEntry objects or its appropriate sub-type.
99             # The results are kept around to facilitate repeated calls without moving
100             # the cursor.
101             sub _getPage {
102             my ($this, $relation) = @_;
103              
104             my $link = $this->getLink($relation);
105             return unless $link;
106              
107             #print STDERR "getPage($link)\n";
108              
109             $this->{xmlDoc} = $this->{repository}{client}->get($link);
110              
111             $this->{entries} = undef;
112             $this->{index} = 0;
113              
114             return $this->_getPageEntries;
115             }
116              
117             # returns a list of all AtomEntries on the current page
118             sub _getPageEntries {
119             my $this = shift;
120              
121             unless (defined $this->{entries}) {
122             my @entries = $this->{xmlDoc}->documentElement->findnodes($CMIS_XPATH_ENTRY);
123             $this->{entries} = \@entries;
124             }
125              
126             return $this->{entries};
127             }
128              
129             =item newEntry($xmlDoc) -> $atomEntry
130              
131             creates an item from an xml fragment part of this atom feed. this virtual method
132             must be implemented by subclasses of AtomFeed.
133             name to be used for all entries
134              
135             =cut
136              
137             sub newEntry {
138             throw Error::Simple("virtual method not implemented");
139             }
140              
141             =item getNext -> $nextAtomEntry
142              
143             returns the next AtomEnrty in this feed
144             or undef if we hit the end of the list
145              
146             =cut
147              
148             sub getNext {
149             my $this = shift;
150              
151             my $nrEntries = scalar(@{$this->_getPageEntries});
152              
153             if ($this->{index} >= $nrEntries) {
154             #my $link = $this->getLink(NEXT_REL);
155             #print STDERR "fetching next page from $link\n";
156             return unless $this->_getPage(NEXT_REL);
157             } else {
158             #print STDERR "using current page\n";
159             }
160              
161             my $result = $this->{entries}->[$this->{index}];
162             return unless $result;
163              
164             $this->{index}++;
165              
166             #print STDERR "newEntry ($this)\n";
167             return $this->newEntry($result);
168             }
169              
170             =item getPrev -> $prevAtomEntry
171              
172             returns the previous AtomEnrty in this feed
173             or undef if we hit the beginning of the list
174              
175             =cut
176              
177             sub getPrev {
178             my $this = shift;
179              
180             $this->{index}--;
181              
182             if ($this->{index} < 0) {
183             return unless $this->_getPage(PREV_REL);
184              
185             my $nrEntries = scalar(@{$this->_getPageEntries});
186             return unless $nrEntries;
187            
188             $this->{index} = $nrEntries-1;
189             }
190              
191             my $result = $this->_getPageEntries->[$this->{index}];
192             return unless $result;
193              
194             return $this->newEntry($result);
195             }
196              
197             =item getFirst -> $firstAtomEntry
198              
199             returns the first AtomEntry of the feed.
200              
201             =cut
202              
203             sub getFirst {
204             my $this = shift;
205              
206             $this->rewind;
207             return $this->newEntry($this->_getPageEntries->[$this->{index}]);
208             }
209              
210             =item getLast -> $lastAtomEntry
211              
212             returns the last AtomEntry of the feed.
213              
214             =cut
215              
216             sub getLast {
217             my $this = shift;
218              
219             $this->fastforward;
220             $this->{index}--;
221             return $this->newEntry($this->_getPageEntries->[$this->{index}]);
222             }
223              
224             =item fastforward
225              
226             fetches the last page of the feed and sets the index to the last entry on that
227             page. This is the kind of oposite of rewind(). This only works if the
228             repository returns a "last" link.
229              
230             =cut
231              
232             sub fastforward {
233             my $this = shift;
234              
235             $this->_getPage(LAST_REL);
236             $this->{index} = scalar(@{$this->_getPageEntries});
237             }
238              
239             =item rewind
240              
241             fetches the first page of the feed again and resets the counter
242             so that the next call to getNext() will return the first AtomEntry
243             in the feed again
244              
245             =cut
246              
247             sub rewind {
248             my $this = shift;
249              
250             $this->_getPage(FIRST_REL);
251             $this->{index} = 0;
252             }
253              
254             =item getTitle -> $title
255              
256             getter for the object's atom:title property.
257              
258             =cut
259              
260             sub getTitle {
261             my $this = shift;
262              
263             unless (defined $this->{title}) {
264             $this->{title} = $this->{xmlDoc}->documentElement->findvalue($CMIS_XPATH_TITLE);
265             }
266              
267             return $this->{title};
268             }
269              
270             =item getUpdated -> $epoch
271              
272             getter for the object's atom:updated property.
273              
274             =cut
275              
276             sub getUpdated {
277             my $this = shift;
278              
279             unless (defined $this->{updated}) {
280             require WebService::Cmis::Property;
281             $this->{updated} = WebService::Cmis::Property::parseDateTime($this->{xmlDoc}->documentElement->findvalue($CMIS_XPATH_UPDATED));
282             }
283              
284             return $this->{updated};
285             }
286              
287             =item getGenerator -> $string
288              
289             getter for the object's atom:generator property.
290              
291             =cut
292              
293             sub getGenerator {
294             my $this = shift;
295              
296             unless (defined $this->{generator}) {
297             $this->{generator} = $this->{xmlDoc}->documentElement->findvalue($CMIS_XPATH_GENERATOR);
298             }
299              
300             return $this->{generator};
301             }
302              
303              
304             =item getSize -> $integer
305              
306             get the total number of items in the result set. it first tries to read the cmisra:numItems property.
307             if that's not present the opensearch:totalResults elemt is checked
308              
309             =cut
310              
311             sub getSize {
312             my $this = shift;
313              
314             unless (defined $this->{totalResults}) {
315             $this->{totalResults} =
316             $this->{xmlDoc}->documentElement->findvalue($CMIS_XPATH_TOTALRESULTS) ||
317             $this->{xmlDoc}->documentElement->findvalue($CMIS_XPATH_NUMITEMS) ||
318             scalar(@{$this->_getPageEntries});
319             }
320              
321             return $this->{totalResults};
322             }
323              
324             =item getPageSize -> $integer
325              
326             returns the size of a page in the result set. this should equal the maxItem value if set in a query
327              
328             =cut
329              
330             sub getPageSize {
331             my $this = shift;
332              
333             unless (defined $this->{pageSize}) {
334             $this->{pageSize} =
335             $this->{xmlDoc}->documentElement->findvalue($CMIS_XPATH_PAGESIZE) ||
336             scalar(@{$this->_getPageEntries});
337             }
338              
339             return $this->{pageSize};
340             }
341              
342              
343             =item toString()
344              
345             return a string representation of this object
346              
347             =cut
348              
349             sub toString {
350             my $this = shift;
351              
352             my @result = ();
353             foreach my $elem (@{$this->_getPageEntries}) {
354             my $id = $elem->findvalue('./cmisra:object/cmis:properties/cmis:propertyId[@propertyDefinitionId="cmis:objectId"]');
355             next unless $id;
356             my $name = $elem->findvalue('./cmisra:object/cmis:properties/cmis:propertyString[@propertyDefinitionId="cmis:name"]');
357             push @result, "$id ($name)";
358             }
359              
360             return $this->getTitle.': '.join(", ", @result);
361             }
362              
363              
364             =back
365              
366             =head1 COPYRIGHT AND LICENSE
367              
368             Copyright 2012-2013 Michael Daum
369              
370             This module is free software; you can redistribute it and/or modify it under
371             the same terms as Perl itself. See F.
372              
373             =cut
374              
375             1;