1 /**
2 * Licensed under the Artistic License; you may not use this file
3 * except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://displaytag.sourceforge.net/license.html
7 *
8 * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12 package org.displaytag.model;
13
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
17
18 import javax.servlet.jsp.PageContext;
19
20 import org.apache.commons.lang.builder.ToStringBuilder;
21 import org.apache.commons.lang.builder.ToStringStyle;
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.displaytag.decorator.TableDecorator;
25 import org.displaytag.properties.MediaTypeEnum;
26 import org.displaytag.properties.TableProperties;
27
28
29 /**
30 * Table Model. Holds table data for presentation.
31 * @author Fabrizio Giustina
32 * @version $Revision: 1081 $ ($Author: fgiust $)
33 */
34 public class TableModel
35 {
36
37 /**
38 * logger.
39 */
40 private static Log log = LogFactory.getLog(TableModel.class);
41
42 /**
43 * list of HeaderCell.
44 */
45 private List headerCellList;
46
47 /**
48 * full list (contains Row objects).
49 */
50 private List rowListFull;
51
52 /**
53 * list of data to be displayed in page.
54 */
55 private List rowListPage;
56
57 /**
58 * Name of the column currently sorted (only used when sort=external).
59 */
60 private String sortedColumnName;
61
62 /**
63 * sort order = ascending?
64 */
65 private boolean sortOrderAscending = true;
66
67 /**
68 * sort full List? (false sort only displayed page).
69 */
70 private boolean sortFullTable = true;
71
72 /**
73 * index of the sorted column (-1 if the table is not sorted).
74 */
75 private int sortedColumn = -1;
76
77 /**
78 * Table decorator.
79 */
80 private TableDecorator tableDecorator;
81
82 /**
83 * id inherited from the TableTag (needed only for logging).
84 */
85 private String id;
86
87 /**
88 * configurable table properties.
89 */
90 private TableProperties properties;
91
92 /**
93 * Starting offset for elements in the viewable list.
94 */
95 private int pageOffset;
96
97 /**
98 * Response encoding.
99 */
100 private String encoding;
101
102 /**
103 * Are we sorting locally? (Default True)
104 */
105 private boolean localSort = true;
106
107 /**
108 * Table caption.
109 */
110 private String caption;
111
112 /**
113 * Table footer.
114 */
115 private String footer;
116
117 /**
118 * Jsp page context.
119 */
120 private PageContext pageContext;
121
122 /**
123 * Current media.
124 */
125 private MediaTypeEnum media;
126
127 /**
128 * Constructor for TableModel.
129 * @param tableProperties table properties
130 * @param charEncoding response encoding
131 */
132 public TableModel(TableProperties tableProperties, String charEncoding, PageContext pageContext)
133 {
134 this.rowListFull = new ArrayList(20);
135 this.headerCellList = new ArrayList(20);
136 this.properties = tableProperties;
137 this.encoding = charEncoding;
138 this.pageContext = pageContext;
139 }
140
141 /**
142 * Returns the jsp page context.
143 * @return page context
144 */
145 protected PageContext getPageContext()
146 {
147 return this.pageContext;
148 }
149
150 /**
151 * Gets the current media type.
152 * @return current media (html, pdf ...)
153 */
154 public MediaTypeEnum getMedia()
155 {
156 return this.media;
157 }
158
159 /**
160 * sets the current media type.
161 * @param media current media (html, pdf ...)
162 */
163 public void setMedia(MediaTypeEnum media)
164 {
165 this.media = media;
166 }
167
168 /**
169 * Sets whether the table performs local in memory sorting of the data.
170 * @param localSort
171 */
172 public void setLocalSort(boolean localSort)
173 {
174 this.localSort = localSort;
175 }
176
177 /**
178 * @return sorting in local memory
179 */
180 public boolean isLocalSort()
181 {
182 return localSort;
183 }
184
185 /**
186 * Sets the starting offset for elements in the viewable list.
187 * @param offset The page offset to set.
188 */
189 public void setPageOffset(int offset)
190 {
191 this.pageOffset = offset;
192 }
193
194 /**
195 * Setter for the tablemodel id.
196 * @param tableId same id of table tag, needed for logging
197 */
198 public void setId(String tableId)
199 {
200 this.id = tableId;
201 }
202
203 /**
204 * get the table id.
205 * @return table id
206 */
207 public String getId()
208 {
209 return this.id;
210 }
211
212 /**
213 * get the full list.
214 * @return the full list containing Row objects
215 */
216 public List getRowListFull()
217 {
218 return this.rowListFull;
219 }
220
221 /**
222 * gets the partial (paginated) list.
223 * @return the partial list to display in page (contains Row objects)
224 */
225 public List getRowListPage()
226 {
227 return this.rowListPage;
228 }
229
230 /**
231 * adds a Row object to the table.
232 * @param row Row
233 */
234 public void addRow(Row row)
235 {
236 row.setParentTable(this);
237
238 if (log.isDebugEnabled())
239 {
240 log.debug("[" + this.id + "] adding row " + row);
241 }
242 this.rowListFull.add(row);
243 }
244
245 /**
246 * sets the name of the currently sorted column
247 * @param sortedColumnName
248 */
249 public void setSortedColumnName(String sortedColumnName)
250 {
251 this.sortedColumnName = sortedColumnName;
252 }
253
254 /**
255 * sets the sort full table property. If true the full list is sorted, if false sorting is applied only to the
256 * displayed sublist.
257 * @param sortFull boolean
258 */
259 public void setSortFullTable(boolean sortFull)
260 {
261 this.sortFullTable = sortFull;
262 }
263
264 /**
265 * return the sort full table property.
266 * @return boolean true if sorting is applied to the full list
267 */
268 public boolean isSortFullTable()
269 {
270 return this.sortFullTable;
271 }
272
273 /**
274 * return the sort order of the page.
275 * @return true if sort order is ascending
276 */
277 public boolean isSortOrderAscending()
278 {
279 return this.sortOrderAscending;
280
281 }
282
283 /**
284 * set the sort order of the list.
285 * @param isSortOrderAscending true to sort in ascending order
286 */
287 public void setSortOrderAscending(boolean isSortOrderAscending)
288 {
289 this.sortOrderAscending = isSortOrderAscending;
290 }
291
292 /**
293 * @param rowList - the new value for this.rowListPage
294 */
295 public void setRowListPage(List rowList)
296 {
297 this.rowListPage = rowList;
298 }
299
300 /**
301 * getter for the Table Decorator.
302 * @return TableDecorator
303 */
304 public TableDecorator getTableDecorator()
305 {
306 return this.tableDecorator;
307 }
308
309 /**
310 * setter for the table decorator.
311 * @param decorator - the TableDecorator object
312 */
313 public void setTableDecorator(TableDecorator decorator)
314 {
315 this.tableDecorator = decorator;
316 }
317
318 /**
319 * returns true if the table is sorted.
320 * @return boolean true if the table is sorted
321 */
322 public boolean isSorted()
323 {
324 return this.sortedColumn != -1;
325 }
326
327 /**
328 * returns the HeaderCell for the sorted column.
329 * @return HeaderCell
330 */
331 public HeaderCell getSortedColumnHeader()
332 {
333 if (this.sortedColumn < 0 || (this.sortedColumn > (this.headerCellList.size() - 1)))
334 {
335 return null;
336 }
337 return (HeaderCell) this.headerCellList.get(this.sortedColumn);
338 }
339
340 /**
341 * return the number of columns in the table.
342 * @return int number of columns
343 */
344 public int getNumberOfColumns()
345 {
346 return this.headerCellList.size();
347 }
348
349 /**
350 * return true is the table has no columns.
351 * @return boolean
352 */
353 public boolean isEmpty()
354 {
355 return this.headerCellList.size() == 0;
356 }
357
358 /**
359 * return the index of the sorted column.
360 * @return index of the sorted column or -1 if the table is not sorted
361 */
362 public int getSortedColumnNumber()
363 {
364 return this.sortedColumn;
365 }
366
367 /**
368 * set the sorted column index.
369 * @param sortIndex - the index of the sorted column
370 */
371 public void setSortedColumnNumber(int sortIndex)
372 {
373 this.sortedColumn = sortIndex;
374 }
375
376 /**
377 * Adds a column header (HeaderCell object).
378 * @param headerCell HeaderCell
379 */
380 public void addColumnHeader(HeaderCell headerCell)
381 {
382 if (this.sortedColumnName == null)
383 {
384 if (this.sortedColumn == this.headerCellList.size())
385 {
386 headerCell.setAlreadySorted();
387 }
388 }
389 else
390 {
391 // the sorted parameter was a string so try and find that column name and set it as sorted
392 if (this.sortedColumnName.equals(headerCell.getSortName()))
393 {
394 headerCell.setAlreadySorted();
395 }
396 }
397 headerCell.setColumnNumber(this.headerCellList.size());
398
399 this.headerCellList.add(headerCell);
400 }
401
402 /**
403 * List containing headerCell objects.
404 * @return List containing headerCell objects
405 */
406 public List getHeaderCellList()
407 {
408 return this.headerCellList;
409 }
410
411 /**
412 * returns a RowIterator on the requested (full|page) list.
413 * @return RowIterator
414 * @param full if <code>true</code> returns an iterator on te full list, if <code>false</code> only on the
415 * viewable part.
416 * @see org.displaytag.model.RowIterator
417 */
418 public RowIterator getRowIterator(boolean full)
419 {
420 RowIterator iterator = new RowIterator(
421 full ? this.rowListFull : this.rowListPage,
422 this.headerCellList,
423 this.tableDecorator,
424 this.pageOffset);
425 // copy id for logging
426 iterator.setId(this.id);
427 return iterator;
428 }
429
430 /**
431 * sorts the given list of Rows. The method is called internally by sortFullList() and sortPageList().
432 * @param list List
433 */
434 private void sortRowList(List list)
435 {
436 if (isSorted())
437 {
438 HeaderCell sortedHeaderCell = getSortedColumnHeader();
439
440 if (sortedHeaderCell != null)
441 {
442 // If it is an explicit value, then sort by that, otherwise sort by the property...
443 if (sortedHeaderCell.getBeanPropertyName() != null
444 || (this.sortedColumn != -1 && this.sortedColumn < this.headerCellList.size()))
445 {
446
447 String sorted = (sortedHeaderCell.getSortProperty() != null)
448 ? sortedHeaderCell.getSortProperty()
449 : sortedHeaderCell.getBeanPropertyName();
450
451 Collections.sort(list, new RowSorter(
452 this.sortedColumn,
453 sorted,
454 getTableDecorator(),
455 this.sortOrderAscending,
456 sortedHeaderCell.getComparator()));
457 }
458 }
459
460 }
461
462 }
463
464 /**
465 * sort the list displayed in page.
466 */
467 public void sortPageList()
468 {
469 if (log.isDebugEnabled())
470 {
471 log.debug("[" + this.id + "] sorting page list");
472 }
473 sortRowList(this.rowListPage);
474
475 }
476
477 /**
478 * sort the full list of data.
479 */
480 public void sortFullList()
481 {
482 if (log.isDebugEnabled())
483 {
484 log.debug("[" + this.id + "] sorting full data");
485 }
486 sortRowList(this.rowListFull);
487 }
488
489 /**
490 * Returns the table properties.
491 * @return the configured table properties.
492 */
493 public TableProperties getProperties()
494 {
495 return this.properties;
496 }
497
498 /**
499 * Getter for character encoding.
500 * @return Returns the encoding used for response.
501 */
502 public String getEncoding()
503 {
504 return encoding;
505 }
506
507 /**
508 * Obtain this table's caption.
509 * @return This table's caption.
510 */
511 public String getCaption()
512 {
513 return this.caption;
514 }
515
516 /**
517 * Set this table's caption.
518 * @param caption This table's caption.
519 */
520 public void setCaption(String caption)
521 {
522 this.caption = caption;
523 }
524
525 /**
526 * Obtain this table's footer.
527 * @return This table's footer.
528 */
529 public String getFooter()
530 {
531 return this.footer;
532 }
533
534 /**
535 * Set this table's footer.
536 * @param footer This table's footer.
537 */
538 public void setFooter(String footer)
539 {
540 this.footer = footer;
541 }
542
543 /**
544 * @see java.lang.Object#toString()
545 */
546 public String toString()
547 {
548 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) //
549 .append("rowListFull", this.rowListFull) //$NON-NLS-1$
550 .append("rowListPage", this.rowListPage) //$NON-NLS-1$
551 .append("properties", this.properties) //$NON-NLS-1$
552 .append("empty", this.isEmpty()) //$NON-NLS-1$
553 .append("encoding", this.encoding) //$NON-NLS-1$
554 .append("numberOfColumns", this.getNumberOfColumns()) //$NON-NLS-1$
555 .append("headerCellList", this.headerCellList) //$NON-NLS-1$
556 .append("sortFullTable", this.sortFullTable) //$NON-NLS-1$
557 .append("sortedColumnNumber", this.getSortedColumnNumber()) //$NON-NLS-1$
558 .append("sortOrderAscending", this.sortOrderAscending) //$NON-NLS-1$
559 .append("sortedColumnHeader", this.getSortedColumnHeader()) //$NON-NLS-1$
560 .append("sorted", this.isSorted()) //$NON-NLS-1$
561 .append("tableDecorator", this.tableDecorator) //$NON-NLS-1$
562 .append("caption", this.caption) //$NON-NLS-1
563 .append("footer", this.footer) //$NON-NLS-1
564 .append("media", this.media) //$NON-NLS-1
565 .toString();
566 }
567
568 }