KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jimm > datavision > layout > LayoutEngine


1 package jimm.datavision.layout;
2 import jimm.datavision.*;
3 import jimm.datavision.field.Field;
4 import jimm.datavision.field.ImageField;
5 import java.io.PrintWriter JavaDoc;
6 import java.util.*;
7
8 /**
9  * A layout engine is responsible for formatting and outputting report data.
10  * <code>LayoutEngine</code> is an abstract class. The Template design
11  * pattern is heavily used to provide a framework for concrete layout
12  * engine subclasses.
13  *
14  * @author Jim Menard, <a HREF="mailto:jimm@io.com">jimm@io.com</a>
15  */

16 public abstract class LayoutEngine {
17
18 /** The number of points per inch. */
19 public static final int POINTS_PER_INCH = 72;
20
21 protected static final int SECT_REPORT_HEADER = 0;
22 protected static final int SECT_REPORT_FOOTER = 1;
23 protected static final int SECT_PAGE_HEADER = 2;
24 protected static final int SECT_PAGE_FOOTER = 3;
25 protected static final int SECT_GROUP_HEADER = 4;
26 protected static final int SECT_GROUP_FOOTER = 5;
27 protected static final int SECT_DETAIL = 6;
28
29 /** Set by report in <code>Report.setLayoutEngine</code>. */
30 protected Report report;
31 protected int pageNumber;
32 protected PrintWriter JavaDoc out;
33 protected boolean newPage;
34 protected double pageHeight;
35 protected double pageWidth;
36 protected double pageHeightUsed;
37 protected Section currentSection;
38 protected boolean wantsMoreData;
39 protected int previousSectionArea;
40
41 /**
42  * Constructor.
43  */

44 public LayoutEngine() {
45     this(null);
46 }
47
48 /**
49  * Constructor.
50  *
51  * @param out output print writer
52  */

53 public LayoutEngine(PrintWriter JavaDoc out) {
54     report = null;
55     this.out = out;
56     wantsMoreData = true;
57     previousSectionArea = -1;
58 }
59
60 public void setReport(Report r) { report = r; }
61
62 /**
63  * Returns the page width in points. This value is only valid after
64  * {@link #start} has been called.
65  *
66  * @return the page width in points
67  */

68 public double pageWidth() { return pageWidth; }
69
70 /**
71  * Returns the page height in points. This value is only valid after
72  * {@link #start} has been called.
73  *
74  * @return the page height in points
75  */

76 public double pageHeight() { return pageHeight; }
77
78 /**
79  * Called by a report before retrieving each row of data, this method returns
80  * <code>true</code> if this layout engine wants more data. Concrete
81  * subclasses can set the <code>wantsMoreData</code> instance variable
82  * to <code>false</code> if the user cancells a report run, for example.
83  *
84  * @return <code>true</code> if this layout engine would like more data
85  */

86 public boolean wantsMoreData() { return wantsMoreData; }
87
88 /**
89  * Called by someone else running the report to cancel all the hard work
90  * this layout engine has performed.
91  */

92 public void cancel() {
93     closeOutput();
94 }
95
96 /**
97  * Called by the report at the beginning of a report run.
98  * <p>
99  * By this time, <code>report</code> will be non-<code>null</code>
100  * because we have been added to a report (which in turn sets our
101  * <code>report</code> instance variable).
102  */

103 public void start() {
104     newPage = true;
105     pageHeightUsed = 0;
106     pageNumber = 0;
107     pageHeight = report.getPaperFormat().getHeight();
108     pageWidth = report.getPaperFormat().getWidth();
109     if (wantsMoreData) {
110     doStart();
111     startPage();
112     }
113 }
114
115 /**
116  * Called by <code>start</code> as a chance to insert behavior when the
117  * report starts.
118  */

119 protected void doStart() { }
120
121 /**
122  * Called by the report at the end of a report run.
123  */

124 public void end() {
125     if (wantsMoreData) {
126     for (Iterator iter = report.footers().iterator(); iter.hasNext(); )
127         outputSection((Section)iter.next(), SECT_REPORT_FOOTER);
128     endPage(true);
129     doEnd();
130     }
131     closeOutput();
132 }
133
134 /**
135  * Called by <code>end</code> as a chance to insert behavior when the
136  * report ends.
137  */

138 protected void doEnd() {}
139
140
141 /**
142  * Called by <code>end</code> to let this layout engine clean up a bit.
143  */

144 protected void closeOutput() {
145     if (out != null) {
146     out.flush();
147     out.close();
148     out = null;
149     }
150 }
151
152 /**
153  * Called by the report when group headers need to be output. Once one
154  * group header is output, we output all remaining group headers.
155  * <p>
156  * We need to explicitly evaluate all formulas in the headers that will be
157  * output because {@link #checkRemainingPageLength} causes formulas in
158  * detail and footers to be evaluated. Those formulas may depend upon
159  * values in these headers.
160  *
161  * @param isLastRow if <code>true</code>, this is the last row of the report
162  */

163 public void groupHeaders(boolean isLastRow) {
164     if (!wantsMoreData)
165     return;
166
167     boolean headerWasOutput = false;
168     for (Iterator iter = report.groups(); iter.hasNext(); ) {
169     Group g = (Group)iter.next();
170     if (headerWasOutput || g.isNewValue()) {
171         for (Iterator i2 = g.headers().iterator(); i2.hasNext(); )
172         ((Section)i2.next()).evaluateFormulas();
173         headerWasOutput = true;
174     }
175     }
176 // boolean headerWasOutput;
177

178     checkRemainingPageLength(isLastRow, true);
179
180     headerWasOutput = false;
181     for (Iterator iter = report.groups(); iter.hasNext(); ) {
182     Group g = (Group)iter.next();
183     if (headerWasOutput || g.isNewValue()) {
184         for (Iterator i2 = g.headers().iterator(); i2.hasNext(); )
185         outputSection((Section)i2.next(), SECT_GROUP_HEADER);
186         headerWasOutput = true;
187     }
188     }
189 }
190
191 /**
192  * Called by the report when a single detail row needs to be output.
193  *
194  * @param isLastRow if <code>true</code>, this is the last row of the report
195  */

196 public void detail(boolean isLastRow) {
197     if (!wantsMoreData)
198     return;
199
200     checkRemainingPageLength(isLastRow, true);
201     for (Iterator iter = report.details().iterator(); iter.hasNext(); )
202     outputSection((Section)iter.next(), SECT_DETAIL);
203 }
204
205 /**
206  * Called by the report when group footers need to be output. When one
207  * group footer is output, we make sure all of the footers before (above)
208  * it are also output.
209  *
210  * @param isLastRow if <code>true</code>, this is the last row of the report
211  */

212 public void groupFooters(boolean isLastRow) {
213     if (!wantsMoreData)
214     return;
215
216     checkRemainingPageLength(isLastRow, false);
217
218     // We need to output group footers backwards (Group n ... Group 1).
219
// When a group footer is output, make sure all of the footers before
220
// (above) it are also output. First, we walk the group list forwards
221
// so we can see which group changed first and grab all groups
222
// after that one.
223
boolean footerWasOutput = false;
224     ArrayList groupsToOutput = new ArrayList();
225     for (Iterator iter = report.groups(); iter.hasNext(); ) {
226     Group g = (Group)iter.next();
227     if (footerWasOutput || g.isNewValue() || isLastRow) {
228         if (footerWasOutput) g.forceFooterOutput();
229         groupsToOutput.add(g);
230         footerWasOutput = true;
231     }
232     }
233
234     // Now we reverse the list of groups and output them.
235
Collections.reverse(groupsToOutput);
236     for (Iterator iter = groupsToOutput.iterator(); iter.hasNext(); ) {
237     Group g = (Group)iter.next();
238     for (Iterator i2 = g.footers().iterator(); i2.hasNext(); )
239         outputSection((Section)i2.next(), SECT_GROUP_FOOTER);
240     }
241 }
242
243 /**
244  * Checks remaining page length and outputs a new page if we are at the
245  * bottom of the page.
246  *
247  * @param isLastRow if <code>true</code>, this is the last row of the report
248  * @param includeDetail if <code>true</code>, include height of detail
249  * sections
250  */

251 protected void checkRemainingPageLength(boolean isLastRow,
252                     boolean includeDetail)
253 {
254     // NOTE: need to do this dynamically each row because each one
255
// of the multiple detail sections may or may not be active for
256
// this row.
257
double detailHeight = includeDetail ? calcDetailHeight() : 0;
258     double footerHeight = calcPageFooterHeight();
259     if (isLastRow) footerHeight += calcReportFooterHeight();
260
261     if ((pageHeightUsed + footerHeight + detailHeight) > pageHeight())
262     endPage(isLastRow);
263
264     if (newPage) startPage();
265 }
266
267 /**
268  * Returns the current page number.
269  *
270  * @return the current page number
271  */

272 public int pageNumber() { return pageNumber; }
273
274 /**
275  * Starts a new page.
276  */

277 protected void startPage() {
278     if (!wantsMoreData)
279     return;
280
281     pageNumber += 1;
282     pageHeightUsed = 0;
283     newPage = false;
284
285     doStartPage();
286     if (pageNumber == 1) {
287     for (Iterator iter = report.headers().iterator(); iter.hasNext(); )
288         outputSection((Section)iter.next(), SECT_REPORT_HEADER);
289     }
290     for (Iterator iter = report.pageHeaders().iterator(); iter.hasNext(); )
291     outputSection((Section)iter.next(), SECT_PAGE_HEADER);
292 }
293
294 /**
295  * Called by <code>startPage</code> as a chance to insert behavior when a
296  * new page starts.
297  */

298 protected void doStartPage() { }
299
300 /**
301  * Ends a new page.
302  *
303  * @param isLastPage if <code>true</code>, this is the last page of the report
304  */

305 protected void endPage(boolean isLastPage) {
306     if (!wantsMoreData)
307     return;
308
309     for (Iterator iter = report.pageFooters().iterator(); iter.hasNext(); )
310     outputSection((Section)iter.next(), SECT_PAGE_FOOTER);
311
312     newPage = true;
313     doEndPage();
314 }
315
316 /**
317  * Called by <code>endPage</code> as a chance to insert behavior when a
318  * new page ends.
319  */

320 protected void doEndPage() { }
321
322 /**
323  * Outputs a section.
324  *
325  * @param sect the section to output
326  * @param which the type of section (for example,
327  * <code>SECT_PAGE_FOOTER</code>)
328  */

329 protected void outputSection(Section sect, int which) {
330     if (!wantsMoreData)
331     return;
332
333     if (sect.isVisibleForCurrentRow()) {
334     // Insert a page break if requested.
335
if (sect.hasPageBreak()
336         && previousSectionArea != SECT_PAGE_HEADER)
337     {
338         endPage(false);
339         startPage();
340     }
341
342     // This must be after page break because calling endPage() and
343
// startPage() changes the currentSection.
344
currentSection = sect;
345     report.evaluateFormulasIn(currentSection);
346     doOutputSection(currentSection);
347
348     pageHeightUsed += sect.getOutputHeight();
349     previousSectionArea = which;
350     }
351     else {
352     // Always eval formulas, even if the section is hidden
353
report.evaluateFormulasIn(sect);
354     }
355 }
356
357 /**
358  * Called by <code>outputSection</code> as a chance to insert behavior
359  * when a section is output.
360  *
361  * @param sect a section
362  */

363 protected void doOutputSection(Section sect) {
364     // Output the fields in the section
365
for (Iterator iter = sect.fields(); iter.hasNext(); ) {
366     Field f = (Field)iter.next();
367     if (f.isVisible()) {
368         if (f instanceof ImageField)
369         outputImage((ImageField)f);
370         else
371         outputField(f);
372     }
373     }
374     // Output the lines
375
for (Iterator iter = sect.lines(); iter.hasNext(); ) {
376     Line l = (Line)iter.next();
377     if (l.isVisible()) outputLine(l);
378     }
379 }
380
381 /**
382  * Outputs a field.
383  *
384  * @param field the field to output
385  */

386 protected void outputField(Field field) {
387     if (wantsMoreData) // Do nothing if we have cancelled
388
doOutputField(field);
389 }
390
391 /**
392  * Called by <code>outputField</code> as a chance to insert behavior
393  * when a field is output.
394  *
395  * @param field a field
396  */

397 protected abstract void doOutputField(Field field);
398
399 /**
400  * Outputs a image.
401  *
402  * @param image the image field to output
403  */

404 protected void outputImage(ImageField image) {
405     if (wantsMoreData) // Do nothing if we have cancelled
406
doOutputImage(image);
407 }
408
409 /**
410  * Called by <code>outputImage</code> as a chance to insert behavior
411  * when a image is output.
412  *
413  * @param image an image field
414  */

415 protected abstract void doOutputImage(ImageField image);
416
417 /**
418  * Outputs a line.
419  *
420  * @param line the line to output
421  */

422 protected void outputLine(Line line) {
423     if (wantsMoreData)
424     doOutputLine(line);
425 }
426
427 /**
428  * Called by <code>outputLine</code> as a chance to insert behavior
429  * when a line is output.
430  *
431  * @param line a line
432  */

433 protected abstract void doOutputLine(Line line);
434
435 /**
436  * Returns the current section type (header, footer, detail) as a string.
437  *
438  * @return a string representation of the current section type
439  */

440 protected String JavaDoc currentSectionTypeAsString() {
441     switch (currentSection.getArea().getArea()) {
442     case SectionArea.REPORT_HEADER: return "report header";
443     case SectionArea.REPORT_FOOTER: return "report footer";
444     case SectionArea.PAGE_HEADER: return "page header";
445     case SectionArea.PAGE_FOOTER: return "page footer";
446     case SectionArea.GROUP_HEADER: return "group header";
447     case SectionArea.GROUP_FOOTER: return "group footer";
448     case SectionArea.DETAIL: return "detail";
449     default: return "unknown"; // Should never happen
450
}
451 }
452
453 /**
454  * Returns the total height of all sections in the specified list.
455  *
456  * @param area a section area
457  * @return the total height of all sections in the list
458  */

459 protected double calcSectionHeights(SectionArea area) {
460     double sum = 0;
461     for (Iterator iter = area.iterator(); iter.hasNext(); ) {
462     Section s = (Section)iter.next();
463     if (s.isVisibleForCurrentRow())
464         sum += s.getOutputHeight();
465     }
466     return sum;
467 }
468
469 /**
470  * Returns the total height of all detail sections.
471  *
472  * @return the total height of all detail sections
473  */

474 protected double calcDetailHeight() {
475     return calcSectionHeights(report.details());
476 }
477
478 /**
479  * Returns the total height of all page footer sections.
480  *
481  * @return the total height of all page footer sections
482  */

483 protected double calcPageFooterHeight() {
484     return calcSectionHeights(report.pageFooters());
485 }
486
487 /**
488  * Returns the total height of all report footer sections.
489  *
490  * @return the total height of all report footer sections
491  */

492 protected double calcReportFooterHeight() {
493     return calcSectionHeights(report.footers());
494 }
495
496 }
497
Popular Tags