KickJava   Java API By Example, From Geeks To Geeks.

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


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
17 package org.apache.cocoon.transformation.pagination;
18
19 import java.util.ArrayList JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Map JavaDoc;
23
24 import org.apache.cocoon.Modifiable;
25 import org.apache.cocoon.util.ResizableContainer;
26 import org.xml.sax.Attributes JavaDoc;
27 import org.xml.sax.SAXException JavaDoc;
28 import org.xml.sax.helpers.DefaultHandler JavaDoc;
29
30 /**
31  * Interprets the pagesheet rules to perform pagination.
32  *
33  * <pre>
34  * FIXME (SM): this code sucks! It was done to show the concept of
35  * rule driven pagination (which I find very nice) but
36  * it needs major refactoring in order to be sufficiently
37  * stable to allow any input to enter without breaking
38  * SAX well-formness. I currently don't have the time to make
39  * it any better (along with implementing the char-based rule
40  * that is mostly useful for text documents) but if you want
41  * to blast the code and rewrite it better, you'll make me happy :)
42  * </pre>
43  *
44  * @author <a HREF="mailto:stefano@apache.org">Stefano Mazzocchi</a>
45  * @author <a HREF="mailto:bhtek@yahoo.com">Boon Hian Tek</a>
46  * @version CVS $Id: Pagesheet.java 36466 2004-08-16 10:08:39Z cziegeler $
47  */

48
49 /*
50
51 This is an example pagesheet to show the power of this:
52
53   <?xml version="1.0"?>
54   <pagesheet xmlns="http://apache.org/cocoon/paginate/1.0">
55    <items>
56     <group name="pictures" element="file" namespace="http://apache.org/cocoon/directory/2.0"/>
57    </items>
58    <rules page="1">
59     <count type="element" name="file" namespace="http://apache.org/cocoon/directory/2.0" num="16"/>
60      <link type="unit" num="2"/>
61      <link type="range" value="10"/>
62    </rules>
63    <rules>
64     <count type="element" name="file" namespace="http://apache.org/cocoon/directory/2.0" num="16"/>
65      <link type="unit" num="5"/>
66      <link type="range" value="20"/>
67    </rules>
68    <rules>
69      <count type="element" name="file" namespace="http://apache.org/cocoon/directory/2.0" num="16"/>
70      <link type="unit" num="5"/>
71      <link type="range" value="2"/>
72      <link type="range" value="5"/>
73      <link type="range" value="10"/>
74      <link type="range" value="20"/>
75      <link type="range" value="100"/>
76    </rules>
77   </pagesheet>
78
79 which indicates that:
80
81  1) there is one item group called "picture" and each item is given by the
82     element "file" of the namespace "http://apache.org/cocoon/directory/2.0".
83
84  2) for the first page, the pagination rules indicate that there are two unit
85     links (two above and two below, so linking to page -2 -1 0 +1 +2) and
86     range links have value 10 (so they link to page -10 and +10).
87
88  3) for the rest of the pages, there are three unit links (-3 -2 -1 0 +1 +2 +3)
89     and range goes 20 (so +20 and -20).
90
91  4) if more than one ranges are defined, range links will be created in sequence
92
93  5) range links will be from big to small (eg. 20, 10, then 5) for backward links,
94     range links will be from small to big (eg. 5, 10, then 20) for forward links
95
96  6) range link(s) will have an attribute 'range' to indicate the range size
97
98 */

99 public class Pagesheet extends DefaultHandler JavaDoc implements Cloneable JavaDoc, Modifiable {
100
101     // Used only during parsing of pagesheet document
102
private int level = 0;
103     private int pg = 0;
104     private long lastModified;
105     private PageRules rules;
106
107     // Loaded pagesheet information
108
ResizableContainer pageRules;
109
110     Map JavaDoc itemGroupsPerName;
111     Map JavaDoc itemGroupsPerElement;
112     Map JavaDoc itemListsPerName;
113     Map JavaDoc itemListsPerElement;
114
115     // Runtime information
116
private ResizableContainer pages;
117     private Page currentPage = null;
118     private int pageCounter = 1;
119     private int elementCounter = 0;
120     private int descendant = 0;
121
122     private static class Page {
123         public int elementStart;
124         public int elementEnd;
125         public int characters;
126
127         public Page(PageRules rules, int elementStart) {
128             this.elementStart = elementStart;
129
130             if (rules.elementCount>0) {
131                 this.elementEnd = this.elementStart+rules.elementCount-1;
132             } else {
133                 this.elementEnd = this.elementStart+1;
134             }
135         }
136
137         public boolean validInPage(int elementCounter) {
138             return (this.elementStart<=elementCounter) &&
139                    (elementCounter<=this.elementEnd);
140         }
141     }
142
143     private static class ItemList extends ArrayList JavaDoc {
144         public ItemList(int capacity) {
145             super(capacity);
146         }
147
148         public void addItem(int page) {
149             this.add(new Integer JavaDoc(page));
150         }
151
152         public int getPageForItem(int item) {
153             Integer JavaDoc i = (Integer JavaDoc) this.get(item-1);
154
155             return (i==null) ? 0 : i.intValue();
156         }
157
158         public boolean valid(int item) {
159             return (item==this.size());
160         }
161     }
162
163
164     public Pagesheet() {
165         this.pages = new ResizableContainer(2);
166     }
167
168     private Pagesheet(ResizableContainer rules, Map JavaDoc itemGroupsPerName,
169                       Map JavaDoc itemGroupsPerElement) {
170         this.pageRules = rules;
171         this.itemGroupsPerName = itemGroupsPerName;
172         this.itemGroupsPerElement = itemGroupsPerElement;
173
174         this.pages = new ResizableContainer(5);
175
176         if ((this.itemGroupsPerName!=null) &&
177             (this.itemGroupsPerElement!=null)) {
178             this.itemListsPerName = new HashMap JavaDoc(itemGroupsPerName.size());
179             this.itemListsPerElement = new HashMap JavaDoc(itemGroupsPerName.size());
180
181             Iterator JavaDoc iter = itemGroupsPerName.values().iterator();
182
183             for (; iter.hasNext(); ) {
184                 ItemGroup group = (ItemGroup) iter.next();
185                 ItemList list = new ItemList(10);
186
187                 this.itemListsPerName.put(group.getName(), list);
188                 this.itemListsPerElement.put(group.getElementURI()+
189                                              group.getElementName(), list);
190             }
191         }
192     }
193
194     // --------------- interprets the pagesheet document ----------------
195

196     public void startPrefixMapping(String JavaDoc prefix,
197                                    String JavaDoc uri) throws SAXException JavaDoc {
198         if ( !uri.equals(Paginator.PAGINATE_URI)) {
199             throw new SAXException JavaDoc("The pagesheet's namespace is not supported.");
200         }
201     }
202
203     public void startElement(String JavaDoc uri, String JavaDoc loc, String JavaDoc raw,
204                              Attributes JavaDoc a) throws SAXException JavaDoc {
205         level++;
206         switch (level) {
207             case 1 :
208                 if (loc.equals("pagesheet")) {
209                     // This object represents pagesheet
210
return;
211                 }
212                 break;
213
214             case 2 :
215                 if (loc.equals("rules")) {
216                     if (this.pageRules == null) {
217                         this.pageRules = new ResizableContainer(2);
218                     }
219                     String JavaDoc key = a.getValue("page");
220
221                     if (key!=null) {
222                         try {
223                             pg = Integer.parseInt(key);
224                         } catch (NumberFormatException JavaDoc e) {
225                             throw new SAXException JavaDoc("Syntax error: the attribute 'rules/@page' must contain a number");
226                         }
227                     } else {
228                         pg = 0;
229                     }
230                     rules = new PageRules();
231                     return;
232                 } else if (loc.equals("items")) {
233                     if (this.itemGroupsPerName==null) {
234                         this.itemGroupsPerName = new HashMap JavaDoc(2);
235                     }
236                     if (this.itemGroupsPerElement==null) {
237                         this.itemGroupsPerElement = new HashMap JavaDoc(2);
238                     }
239                     return;
240                 }
241                 break;
242
243             case 3 :
244                 if (loc.equals("count")) {
245                     rules.elementName = a.getValue("name");
246                     rules.elementURI = a.getValue("namespace");
247
248                     if (a.getValue("type").equals("element")) {
249                         try {
250                             rules.elementCount = Integer.parseInt(a.getValue("num"));
251                         } catch (NumberFormatException JavaDoc e) {
252                             throw new SAXException JavaDoc("Syntax error: the attribute 'count/@num' must contain a number");
253                         }
254                     } else if (a.getValue("type").equals("chars")) {
255                         try {
256                             rules.charCount = Integer.parseInt(a.getValue("num"));
257                         } catch (NumberFormatException JavaDoc e) {
258                             throw new SAXException JavaDoc("Syntax error: the attribute 'count/@num' must contain a number.");
259                         }
260                     } else {
261                         throw new SAXException JavaDoc("Syntax error: count type not supported.");
262                     }
263                     return;
264                 } else if (loc.equals("link")) {
265                     if (a.getValue("type").equals("unit")) {
266                         try {
267                             rules.unitLinks = Integer.parseInt(a.getValue("num"));
268                         } catch (NumberFormatException JavaDoc e) {
269                             throw new SAXException JavaDoc("Syntax error: the attribute 'link/@num' must contain a number.");
270                         }
271                     } else if (a.getValue("type").equals("range")) {
272                         try {
273                             rules.addRangeLink(a.getValue("value"));
274                         } catch (NumberFormatException JavaDoc e) {
275                             throw new SAXException JavaDoc("Syntax error: the attribute 'link/@value' must contain a number.");
276                         }
277                     } else {
278                         throw new SAXException JavaDoc("Syntax error: link type not supported.");
279                     }
280                     return;
281                 } else if (loc.equals("group")) {
282                     String JavaDoc name = a.getValue("name");
283
284                     if (name==null) {
285                         throw new SAXException JavaDoc("Syntax error: the attribute 'group/@name' must be present.");
286                     }
287                     String JavaDoc elementName = a.getValue("element");
288
289                     if (elementName==null) {
290                         throw new SAXException JavaDoc("Syntax error: the attribute 'group/@element' must be present.");
291                     }
292                     String JavaDoc elementURI = a.getValue("namespace");
293                     ItemGroup group = new ItemGroup(name, elementURI,
294                                                     elementName);
295
296                     this.itemGroupsPerName.put(name, group);
297                     this.itemGroupsPerElement.put(elementURI+elementName,
298                                                   group);
299                     return;
300                 }
301         }
302         throw new SAXException JavaDoc("Syntax error: element "+raw+
303                                " is not recognized or is misplaced.");
304     }
305
306     public void endElement(String JavaDoc uri, String JavaDoc loc,
307                            String JavaDoc raw) throws SAXException JavaDoc {
308         level--;
309         if (loc.equals("rules")) {
310             pageRules.set(pg, rules);
311         }
312     }
313
314     public void endDocument() throws SAXException JavaDoc {
315         if (pageRules.size() == 0) {
316             throw new SAXException JavaDoc("Pagesheet must contain at least a set of pagination rules.");
317         }
318         if (pageRules.get(0) == null) {
319             throw new SAXException JavaDoc("Pagesheet must contain the global pagination rules.");
320         }
321     }
322
323     // --------------- process the received element events ----------------
324

325     public void processStartElement(String JavaDoc uri, String JavaDoc name) {
326         PageRules rules = getPageRules(pageCounter);
327
328         if (rules.match(name, uri)) {
329             elementCounter++;
330             descendant++;
331
332             if (currentPage==null) {
333                 currentPage = new Page(rules, 1);
334             }
335
336             if (elementCounter>currentPage.elementEnd) {
337                 /*System.out.println(">>>> "+pageCounter+
338                                    ": Starting new page!!! >>> "+
339                                    elementCounter);*/

340                 pageCounter++;
341                 currentPage = new Page(rules, currentPage.elementEnd+1);
342             }
343
344             pages.set(pageCounter, currentPage);
345         }
346
347         if (itemGroupsPerElement!=null) {
348             String JavaDoc qname = uri+name;
349             ItemGroup group = (ItemGroup) this.itemGroupsPerElement.get(qname);
350
351             if ((group!=null) && (group.match(uri))) {
352                 ItemList list = (ItemList) this.itemListsPerElement.get(qname);
353
354                 if (list!=null) {
355                     list.addItem(pageCounter);
356                 }
357             }
358         }
359     }
360
361     public void processEndElement(String JavaDoc uri, String JavaDoc name) {
362         PageRules rules = getPageRules(pageCounter);
363
364         if (rules.match(name, uri)) {
365             descendant--;
366
367             if ((rules.charCount>0) &&
368                 (currentPage.characters>rules.charCount)) {
369                 // We are over character limit. Flip the page.
370
// System.out.println(">>>> " + pageCounter + ": Flipping page!!!");
371
currentPage.elementEnd = elementCounter;
372             } else if (rules.elementCount==0) {
373                 // No limit on elements is specified, and limit on characters is not reached yet.
374
currentPage.elementEnd++;
375             }
376         }
377     }
378
379     public void processCharacters(char[] ch, int index, int len) {
380         if (descendant>0) {
381             // Count amount of characters in the currect page.
382
// System.out.println(">>>> " + pageCounter + ": " + new String(ch, index, len) + " (" + len + " bytes)");
383
currentPage.characters += len;
384         }
385     }
386
387     // --------------- return the pagination information ----------------
388

389     public boolean isInPage(int page, int item, String JavaDoc itemGroup) {
390         return ((descendant==0) || valid(page, item, itemGroup));
391     }
392
393     public int getTotalPages() {
394         return pageCounter;
395     }
396
397     public int getTotalItems(String JavaDoc itemGroup) {
398         if (this.itemListsPerName==null) {
399             return 0;
400         }
401         ItemList list = (ItemList) this.itemListsPerName.get(itemGroup);
402
403         return (list==null) ? 0 : list.size();
404     }
405
406     public int getPageForItem(int item, String JavaDoc itemGroup) {
407         if (this.itemListsPerName==null) {
408             return 0;
409         }
410         ItemList list = (ItemList) this.itemListsPerName.get(itemGroup);
411
412         return (list==null) ? 0 : list.getPageForItem(item);
413     }
414
415     public int itemCount(String JavaDoc elementURI, String JavaDoc elementName) {
416         if (this.itemListsPerElement==null) {
417             return 0;
418         }
419         ItemList list = (ItemList) this.itemListsPerElement.get(elementURI+
420                             elementName);
421
422         return (list==null) ? 0 : list.size();
423     }
424
425     public String JavaDoc getItemGroupName(String JavaDoc elementURI, String JavaDoc elementName) {
426         if (this.itemListsPerElement==null) {
427             return null;
428         }
429         return ((ItemGroup) this.itemGroupsPerElement.get(elementURI+
430             elementName)).getName();
431     }
432
433     // ---------------- miscellaneous methods ----------------------------
434

435     private boolean valid(int page, int item, String JavaDoc itemGroup) {
436         if (item==0) {
437             Page p = (Page) pages.get(page);
438
439             return (p!=null) && (p.validInPage(elementCounter));
440         } else {
441             if (this.itemListsPerElement==null) {
442                 return false;
443             }
444             ItemList list = (ItemList) this.itemListsPerName.get(itemGroup);
445
446             return (list!=null) && (list.valid(item));
447         }
448     }
449
450     public PageRules getPageRules(int page) {
451         PageRules p = (PageRules) pageRules.get(page);
452
453         return (p!=null) ? p : (PageRules) pageRules.get(0);
454     }
455
456     public void setLastModified(long lastModified) {
457         this.lastModified = lastModified;
458     }
459
460     public boolean modifiedSince(long date) {
461         return (this.lastModified == 0 || date!=this.lastModified);
462     }
463
464     public Object JavaDoc clone() {
465         return new Pagesheet(pageRules, itemGroupsPerName,
466                              itemGroupsPerElement);
467     }
468 }
469
Popular Tags