KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > transformation > pagination > Paginator


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.transformation.pagination;
17
18 import org.apache.avalon.framework.activity.Disposable;
19 import org.apache.avalon.framework.parameters.Parameters;
20 import org.apache.avalon.framework.service.ServiceException;
21 import org.apache.avalon.framework.service.ServiceManager;
22 import org.apache.avalon.framework.service.Serviceable;
23 import org.apache.cocoon.ProcessingException;
24 import org.apache.cocoon.caching.CacheableProcessingComponent;
25 import org.apache.cocoon.environment.ObjectModelHelper;
26 import org.apache.cocoon.environment.Request;
27 import org.apache.cocoon.environment.SourceResolver;
28 import org.apache.cocoon.transformation.AbstractTransformer;
29 import org.apache.excalibur.source.Source;
30 import org.apache.excalibur.source.SourceException;
31 import org.apache.excalibur.source.SourceValidity;
32 import org.apache.excalibur.source.impl.validity.AggregatedValidity;
33 import org.apache.excalibur.source.impl.validity.TimeStampValidity;
34 import org.apache.excalibur.store.Store;
35 import org.apache.excalibur.xml.sax.SAXParser;
36 import org.xml.sax.Attributes JavaDoc;
37 import org.xml.sax.InputSource JavaDoc;
38 import org.xml.sax.SAXException JavaDoc;
39 import org.xml.sax.helpers.AttributesImpl JavaDoc;
40
41 import java.io.IOException JavaDoc;
42 import java.io.Serializable JavaDoc;
43 import java.util.Map JavaDoc;
44
45 /**
46  * A paginating transformer.
47  *
48  * @author <a HREF="mailto:stefano@apache.org">Stefano Mazzocchi</a>
49  * @author <a HREF="mailto:stephan@apache.org">Stephan Michels</a>
50  * @author <a HREF="mailto:bhtek@yahoo.com">Boon Hian Tek</a>
51  * @version CVS $Id: Paginator.java 30932 2004-07-29 17:35:38Z vgritsenko $
52  */

53 public class Paginator extends AbstractTransformer
54   implements Serviceable, Disposable, CacheableProcessingComponent {
55
56     public static final String JavaDoc PAGINATE_URI = "http://apache.org/cocoon/paginate/1.0";
57     public static final String JavaDoc PAGINATE_PREFIX = "page";
58     public static final String JavaDoc PAGINATE_PREFIX_TOKEN = PAGINATE_PREFIX + ":";
59
60     private ServiceManager manager;
61     private SAXParser parser;
62     private Store store;
63     private SourceResolver resolver;
64     private Source inputSource;
65     private int page;
66     private int item;
67     private String JavaDoc itemGroup;
68     private String JavaDoc requestURI;
69     private Request request;
70     private Pagesheet pagesheet;
71     private int level;
72     private boolean prefixMapping;
73
74     /**
75      * Set the current <code>ServiceManager</code> instance used by this
76      * <code>Serviceable</code>.
77      *
78      * @param manager Description of the Parameter
79      */

80     public void service(ServiceManager manager) throws ServiceException {
81         try {
82             this.manager = manager;
83             getLogger().debug("Looking up "+SAXParser.ROLE);
84             this.parser = (SAXParser) manager.lookup(SAXParser.ROLE);
85
86             getLogger().debug("Looking up " + Store.TRANSIENT_STORE);
87             this.store = (Store) manager.lookup(Store.TRANSIENT_STORE);
88         } catch (Exception JavaDoc e) {
89             getLogger().error("Could not find component", e);
90         }
91     }
92
93     /**
94      * Dispose this component.
95      */

96     public void dispose() {
97         if (this.parser!=null) {
98             this.manager.release(this.parser);
99         } else {
100             this.parser = null;
101         }
102         if (this.store!=null) {
103             this.manager.release(this.store);
104         } else {
105             this.store = null;
106         }
107     }
108
109     /**
110      * Setup the transformer.
111      */

112     public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc src,
113                       Parameters par)
114                         throws ProcessingException, SAXException JavaDoc,
115                                IOException JavaDoc {
116
117         if (src == null) {
118             throw new ProcessingException("I need the paginate instructions (pagesheet) to continue. Set the 'src' attribute.");
119         }
120
121         try {
122             this.level = 0;
123             this.prefixMapping = false;
124             this.resolver = resolver;
125             this.inputSource = resolver.resolveURI(src);
126             if (getLogger().isDebugEnabled()) {
127                 getLogger().debug("Using pagesheet: '"+
128                                   this.inputSource.getURI()+"' in "+this+
129                                   ", last modified: "+
130                                   this.inputSource.getLastModified());
131             }
132             this.page = par.getParameterAsInteger("page", 1);
133             this.item = par.getParameterAsInteger("item", 0);
134             this.itemGroup = par.getParameter("item-group", "");
135             if (getLogger().isDebugEnabled()) {
136                 getLogger().debug("Paginating with [page = "+this.page+
137                                   ", item = "+this.item+", item-group = "+
138                                   this.itemGroup+"]");
139             }
140
141             this.request = ObjectModelHelper.getRequest(objectModel);
142             this.requestURI = request.getRequestURI();
143
144             // Get the pagesheet factory from the Store if available,
145
// otherwise load it and put it into the store for further request
146
if (store!=null) {
147                 pagesheet = (Pagesheet) store.get(src);
148             }
149
150             // If not in the store or if pagesheet has changed, loads and stores it
151
if ((pagesheet==null) ||
152                 pagesheet.modifiedSince(inputSource.getLastModified())) {
153                 pagesheet = new Pagesheet();
154                 pagesheet.setLastModified(inputSource.getLastModified());
155                 parser.parse(new InputSource JavaDoc(inputSource.getInputStream()),
156                              pagesheet);
157                 if (store!=null) {
158                     store.store(src, pagesheet);
159                 }
160             }
161
162             // Clone it in order to avoid concurrency collisions since the
163
// implementation is not reentrant.
164
this.pagesheet = (Pagesheet) this.pagesheet.clone();
165         } catch (SourceException se) {
166             throw new ProcessingException("Could not retrieve source '" +
167                                           src + "'", se);
168         }
169     }
170
171     public void recycle() {
172         if (null != this.inputSource) {
173             this.resolver.release(this.inputSource);
174             this.inputSource = null;
175         }
176         this.resolver = null;
177         super.recycle();
178     }
179
180     /**
181      * Generate the unique key. This key must be unique inside the space of
182      * this component. This method must be invoked before the
183      * generateValidity() method.
184      *
185      * @return The generated key or <code>null</code> if the component is
186      * currently not cacheable.
187      */

188     public Serializable JavaDoc getKey() {
189         if (this.inputSource.getLastModified()!=0) {
190             return this.inputSource.getURI()+page;
191         } else {
192             return null;
193         }
194     }
195
196     /**
197      * Generate the validity object. Before this method can be invoked the
198      * generateKey() method must be invoked.
199      *
200      * @return The generated validity object or <code>null</code> if the
201      * component is currently not cacheable.
202      */

203     public SourceValidity getValidity() {
204         if (this.inputSource.getLastModified()!=0) {
205             AggregatedValidity validity = new AggregatedValidity();
206
207             validity.add(new TimeStampValidity(page));
208             validity.add(this.inputSource.getValidity());
209             return validity;
210         } else {
211             return null;
212         }
213     }
214
215     /**
216      * Receive notification of the beginning of an element.
217      *
218      * @param uri The Namespace URI, or the empty string if the
219      * element has no Namespace URI or if Namespace processing is not being
220      * performed.
221      * @param loc The local name (without prefix), or the empty
222      * string if Namespace processing is not being performed.
223      * @param raw The raw XML 1.0 name (with prefix), or the empty
224      * string if raw names are not available.
225      * @param a The attributes attached to the element. If there
226      * are no attributes, it shall be an empty Attributes object.
227      */

228     public void startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw,
229                              Attributes JavaDoc a) throws SAXException JavaDoc {
230         if ( !prefixMapping) {
231             super.startPrefixMapping(PAGINATE_PREFIX, PAGINATE_URI);
232             this.prefixMapping = true;
233         }
234         level++;
235         pagesheet.processStartElement(uri, loc);
236         if (pagesheet.isInPage(page, item, itemGroup)) {
237             int itemCount = pagesheet.itemCount(uri, loc);
238
239             if (itemCount>0) {
240                 String JavaDoc itemGroup = pagesheet.getItemGroupName(uri, loc);
241                 AttributesImpl JavaDoc atts = new AttributesImpl JavaDoc(a);
242
243                 atts.addAttribute(PAGINATE_URI, "item",
244                                   PAGINATE_PREFIX_TOKEN+"item", "CDATA",
245                                   String.valueOf(itemCount));
246                 atts.addAttribute(PAGINATE_URI, "item-group",
247                                   PAGINATE_PREFIX_TOKEN+"item-group",
248                                   "CDATA", itemGroup);
249                 super.startElement(uri, loc, raw, atts);
250             } else {
251                 super.startElement(uri, loc, raw, a);
252             }
253         }
254     }
255
256     /**
257      * Receive notification of the end of an element.
258      *
259      * @param uri The Namespace URI, or the empty string if the
260      * element has no Namespace URI or if Namespace processing is not being
261      * performed.
262      * @param loc The local name (without prefix), or the empty
263      * string if Namespace processing is not being performed.
264      * @param raw The raw XML 1.0 name (with prefix), or the empty
265      * string if raw names are not available.
266      */

267     public void endElement(String JavaDoc uri, String JavaDoc loc,
268                            String JavaDoc raw) throws SAXException JavaDoc {
269         level--;
270
271         // Prevent infinite recursive loop.
272
if (PAGINATE_URI.equals(uri)) {
273             super.endElement(uri, loc, raw);
274             return;
275         }
276
277         if (pagesheet.isInPage(page, item, itemGroup)) {
278             if (level==0) {
279                 if (item==0) {
280                     int totalPages = pagesheet.getTotalPages();
281                     PageRules rules = pagesheet.getPageRules(page);
282
283                     Integer JavaDoc[] rangeLinks = rules.getRangeLinks();
284                     int unitLinks = rules.unitLinks;
285                     int currentPage = page;
286
287                     // call add paginate
288
addPaginateTags(rangeLinks, unitLinks, currentPage,
289                                     totalPages, requestURI, this);
290
291                 } else {
292                     int totalItems = pagesheet.getTotalItems(itemGroup);
293                     AttributesImpl JavaDoc atts = new AttributesImpl JavaDoc();
294
295                     atts.addAttribute("", "current", "current", "CDATA",
296                                       String.valueOf(item));
297                     atts.addAttribute("", "total", "total", "CDATA",
298                                       String.valueOf(totalItems));
299                     atts.addAttribute("", "current-uri", "current-uri",
300                                       "CDATA", requestURI);
301                     atts.addAttribute("", "clean-uri", "clean-uri",
302                                       "CDATA", cleanURI(requestURI, item));
303                     atts.addAttribute("", "page", "page", "CDATA",
304                                       String.valueOf(pagesheet.getPageForItem(item,
305                                           itemGroup)));
306                     super.startElement(PAGINATE_URI, "item",
307                                        PAGINATE_PREFIX_TOKEN+"item", atts);
308                     if (item>1) {
309                         atts.clear();
310                         atts.addAttribute("", "type", "type", "CDATA",
311                                           "prev");
312                         atts.addAttribute("", "uri", "uri", "CDATA",
313                                           encodeURI(requestURI, item,
314                                                     item-1));
315                         super.startElement(PAGINATE_URI, "link",
316                                            PAGINATE_PREFIX_TOKEN+"link",
317                                            atts);
318                         super.endElement(PAGINATE_URI, "link",
319                                          PAGINATE_PREFIX_TOKEN+"link");
320                     }
321                     if (item<=totalItems) {
322                         atts.clear();
323                         atts.addAttribute("", "type", "type", "CDATA",
324                                           "next");
325                         atts.addAttribute("", "uri", "uri", "CDATA",
326                                           encodeURI(requestURI, item,
327                                                     item+1));
328                         super.startElement(PAGINATE_URI, "link",
329                                            PAGINATE_PREFIX_TOKEN+"link",
330                                            atts);
331                         super.endElement(PAGINATE_URI, "link",
332                                          PAGINATE_PREFIX_TOKEN+"link");
333                     }
334                     super.endElement(PAGINATE_URI, "item",
335                                      PAGINATE_PREFIX_TOKEN+"item");
336                 }
337
338                 super.endPrefixMapping(PAGINATE_PREFIX);
339             }
340
341             super.endElement(uri, loc, raw);
342         }
343
344         pagesheet.processEndElement(uri, loc);
345     }
346
347     public static void addPaginateTags(Integer JavaDoc[] rangeLinks, int unitLinks,
348                                        int currentPage, int totalPages,
349                                        String JavaDoc requestURI,
350                                        AbstractTransformer saxTransformer)
351                                          throws SAXException JavaDoc {
352         AttributesImpl JavaDoc atts = new AttributesImpl JavaDoc();
353
354         atts.addAttribute("", "current", "current", "CDATA",
355                           String.valueOf(currentPage));
356         atts.addAttribute("", "total", "total", "CDATA",
357                           String.valueOf(totalPages));
358         atts.addAttribute("", "current-uri", "current-uri", "CDATA",
359                           requestURI);
360         atts.addAttribute("", "clean-uri", "clean-uri", "CDATA",
361                           Paginator.cleanURI(requestURI, currentPage));
362         saxTransformer.startElement(Paginator.PAGINATE_URI, "page",
363                                     Paginator.PAGINATE_PREFIX_TOKEN+"page",
364                                     atts);
365
366         for (int i = rangeLinks.length-1; i>-1; i--) {
367             int rangeLink = rangeLinks[i].intValue();
368
369             if ((rangeLink>0) && (currentPage-rangeLink>=1)) {
370                 atts.clear();
371                 atts.addAttribute("", "type", "type", "CDATA", "prev");
372                 atts.addAttribute("", "range", "range", "CDATA",
373                                   rangeLinks[i].toString());
374                 atts.addAttribute("", "uri", "uri", "CDATA",
375                                   Paginator.encodeURI(requestURI,
376                                                       currentPage,
377                                                       currentPage-rangeLink));
378                 atts.addAttribute("", "page", "page", "CDATA",
379                                   String.valueOf(currentPage-rangeLink));
380                 saxTransformer.startElement(Paginator.PAGINATE_URI,
381                                             "range-link",
382                                             Paginator.PAGINATE_PREFIX_TOKEN+
383                                             "range-link", atts);
384                 saxTransformer.endElement(Paginator.PAGINATE_URI,
385                                           "range-link",
386                                           Paginator.PAGINATE_PREFIX_TOKEN+
387                                           "range-link");
388             }
389         }
390
391         for (int i = currentPage-unitLinks; i<currentPage; i++) {
392             if (i>0) {
393                 atts.clear();
394                 atts.addAttribute("", "type", "type", "CDATA", "prev");
395                 atts.addAttribute("", "uri", "uri", "CDATA",
396                                   Paginator.encodeURI(requestURI,
397                                                       currentPage, i));
398                 atts.addAttribute("", "page", "page", "CDATA",
399                                   String.valueOf(i));
400                 saxTransformer.startElement(Paginator.PAGINATE_URI, "link",
401                                             Paginator.PAGINATE_PREFIX_TOKEN+
402                                             "link", atts);
403                 saxTransformer.endElement(Paginator.PAGINATE_URI, "link",
404                                           Paginator.PAGINATE_PREFIX_TOKEN+
405                                           "link");
406             }
407         }
408         for (int i = currentPage+1; i<=currentPage+unitLinks; i++) {
409             if (i<=totalPages) {
410                 atts.clear();
411                 atts.addAttribute("", "type", "type", "CDATA", "next");
412                 atts.addAttribute("", "uri", "uri", "CDATA",
413                                   Paginator.encodeURI(requestURI,
414                                                       currentPage, i));
415                 atts.addAttribute("", "page", "page", "CDATA",
416                                   String.valueOf(i));
417                 saxTransformer.startElement(Paginator.PAGINATE_URI, "link",
418                                             Paginator.PAGINATE_PREFIX_TOKEN+
419                                             "link", atts);
420                 saxTransformer.endElement(Paginator.PAGINATE_URI, "link",
421                                           Paginator.PAGINATE_PREFIX_TOKEN+
422                                           "link");
423             }
424         }
425
426         for (int i = 0; i<rangeLinks.length; i++) {
427             int rangeLink = rangeLinks[i].intValue();
428
429             if ((rangeLink>0) && (currentPage+rangeLink<=totalPages)) {
430                 atts.clear();
431                 atts.addAttribute("", "type", "type", "CDATA", "next");
432                 atts.addAttribute("", "range", "range", "CDATA",
433                                   rangeLinks[i].toString());
434                 atts.addAttribute("", "uri", "uri", "CDATA",
435                                   Paginator.encodeURI(requestURI,
436                                                       currentPage,
437                                                       currentPage+rangeLink));
438                 atts.addAttribute("", "page", "page", "CDATA",
439                                   String.valueOf(currentPage+rangeLink));
440                 saxTransformer.startElement(Paginator.PAGINATE_URI,
441                                             "range-link",
442                                             Paginator.PAGINATE_PREFIX_TOKEN+
443                                             "range-link", atts);
444                 saxTransformer.endElement(Paginator.PAGINATE_URI,
445                                           "range-link",
446                                           Paginator.PAGINATE_PREFIX_TOKEN+
447                                           "range-link");
448             }
449         }
450
451         saxTransformer.endElement(Paginator.PAGINATE_URI, "page",
452                                   Paginator.PAGINATE_PREFIX_TOKEN+"page");
453     }
454
455     /**
456      * Receive notification of character data.
457      *
458      * @param c The characters from the XML document.
459      * @param start The start position in the array.
460      * @param len The number of characters to read from the array.
461      */

462     public void characters(char c[], int start, int len) throws SAXException JavaDoc {
463         pagesheet.processCharacters(c, start, len);
464         if (pagesheet.isInPage(page, item, itemGroup)) {
465             super.characters(c, start, len);
466         }
467     }
468
469     /**
470      * Receive notification of ignorable whitespace in element content.
471      *
472      * @param c The characters from the XML document.
473      * @param start The start position in the array.
474      * @param len The number of characters to read from the array.
475      */

476     public void ignorableWhitespace(char c[], int start,
477                                     int len) throws SAXException JavaDoc {
478         if (pagesheet.isInPage(page, item, itemGroup)) {
479             super.ignorableWhitespace(c, start, len);
480         }
481     }
482
483     /**
484      * Receive notification of a processing instruction.
485      *
486      * @param target The processing instruction target.
487      * @param data The processing instruction data, or null if none
488      * was supplied.
489      */

490     public void processingInstruction(String JavaDoc target,
491                                       String JavaDoc data) throws SAXException JavaDoc {
492         if (pagesheet.isInPage(page, item, itemGroup)) {
493             super.processingInstruction(target, data);
494         }
495     }
496
497     /**
498      * Receive notification of a skipped entity.
499      *
500      * @param name The name of the skipped entity. If it is a
501      * parameter entity, the name will begin with '%'.
502      */

503     public void skippedEntity(String JavaDoc name) throws SAXException JavaDoc {
504         if (pagesheet.isInPage(page, item, itemGroup)) {
505             super.skippedEntity(name);
506         }
507     }
508
509     /**
510      * Report the start of DTD declarations, if any.
511      *
512      * @param name The document type name.
513      * @param publicId The declared public identifier for the external
514      * DTD subset, or null if none was declared.
515      * @param systemId The declared system identifier for the external
516      * DTD subset, or null if none was declared.
517      */

518     public void startDTD(String JavaDoc name, String JavaDoc publicId,
519                          String JavaDoc systemId) throws SAXException JavaDoc {
520         if (pagesheet.isInPage(page, item, itemGroup)) {
521             super.startDTD(name, publicId, systemId);
522         } else {
523             throw new SAXException JavaDoc("Recieved startDTD not in page.");
524         }
525     }
526
527     /**
528      * Report the end of DTD declarations.
529      */

530     public void endDTD() throws SAXException JavaDoc {
531         if (pagesheet.isInPage(page, item, itemGroup)) {
532             super.endDTD();
533         } else {
534             throw new SAXException JavaDoc("Recieved endDTD not in page.");
535         }
536     }
537
538     /**
539      * Report the beginning of an entity.
540      *
541      *@param name The name of the entity. If it is a parameter
542      * entity, the name will begin with '%'.
543      */

544     public void startEntity(String JavaDoc name) throws SAXException JavaDoc {
545         if (pagesheet.isInPage(page, item, itemGroup)) {
546             super.startEntity(name);
547         }
548     }
549
550     /**
551      * Report the end of an entity.
552      *
553      * @param name The name of the entity that is ending.
554      */

555     public void endEntity(String JavaDoc name) throws SAXException JavaDoc {
556         if (pagesheet.isInPage(page, item, itemGroup)) {
557             super.endEntity(name);
558         }
559     }
560
561     /**
562      * Report the start of a CDATA section.
563      */

564     public void startCDATA() throws SAXException JavaDoc {
565         if (pagesheet.isInPage(page, item, itemGroup)) {
566             super.startCDATA();
567         }
568     }
569
570     /**
571      * Report the end of a CDATA section.
572      */

573     public void endCDATA() throws SAXException JavaDoc {
574         if (pagesheet.isInPage(page, item, itemGroup)) {
575             super.endCDATA();
576         }
577     }
578
579     /**
580      * Report an XML comment anywhere in the document.
581      *
582      * @param ch An array holding the characters in the comment.
583      * @param start The starting position in the array.
584      * @param len The number of characters to use from the array.
585      */

586     public void comment(char ch[], int start, int len) throws SAXException JavaDoc {
587         if (pagesheet.isInPage(page, item, itemGroup)) {
588             super.comment(ch, start, len);
589         }
590     }
591
592     /**
593      * Removes the pagination encoding from the URI by removing the page number
594      * and the previous and next character.
595      */

596     public static String JavaDoc cleanURI(String JavaDoc uri, int current) {
597         String JavaDoc currentS = String.valueOf(current);
598         int index = uri.lastIndexOf(currentS);
599
600         if (index==-1) {
601             return uri;
602         } else {
603             return uri.substring(0, index-1)+
604                    uri.substring(index+currentS.length()+1);
605         }
606     }
607
608     /**
609      * Encode the next page in the given URI. First tries to use the existing
610      * encoding by replacing the current page number, but if the current
611      * encoding is not found it appends "(xx)" to the filename (before the file
612      * extention, if any) where "xx" is the next page value.
613      */

614     public static String JavaDoc encodeURI(String JavaDoc uri, int current, int next) {
615         String JavaDoc currentS = String.valueOf(current);
616         String JavaDoc nextS = String.valueOf(next);
617         int index = uri.lastIndexOf(currentS);
618
619         if (index==-1) {
620             index = uri.lastIndexOf('.');
621             if (index==-1) {
622                 return uri+"("+nextS+")";
623             } else {
624                 return uri.substring(0, index)+"("+nextS+")."+
625                        uri.substring(index+1);
626             }
627         } else {
628             return uri.substring(0, index)+nextS+
629                    uri.substring(index+currentS.length());
630         }
631     }
632 }
633
Popular Tags