Index: project.xml =================================================================== RCS file: /cvsroot/displaytag/displaytag/project.xml,v retrieving revision 1.86 diff -u -r1.86 project.xml --- project.xml 17 Sep 2005 08:00:55 -0000 1.86 +++ project.xml 26 Sep 2005 02:52:36 -0000 @@ -336,7 +336,7 @@ itext itext - 0.99 + 1.3 http://prdownloads.sourceforge.net/itext/ true Index: src/main/java/org/displaytag/model/TableModel.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/java/org/displaytag/model/TableModel.java,v retrieving revision 1.21 diff -u -r1.21 TableModel.java --- src/main/java/org/displaytag/model/TableModel.java 3 Sep 2005 14:26:05 -0000 1.21 +++ src/main/java/org/displaytag/model/TableModel.java 26 Sep 2005 02:52:38 -0000 @@ -26,7 +26,7 @@ /** * Table Model. Holds table data for presentation. * @author Fabrizio Giustina - * @version $Revision: 1.21 $ ($Author: fgiust $) + * @version $Revision: 1.22 $ ($Author: fgiust $) */ public class TableModel { @@ -100,8 +100,18 @@ * Are we sorting locally? (Default True) */ private boolean localSort = true; + + /** + * Table caption. + */ + private String caption; /** + * Table footer. + */ + private String footer; + + /** * Constructor for TableModel. * @param tableProperties table properties * @param charEncoding response encoding @@ -392,24 +402,17 @@ if (sortedHeaderCell.getBeanPropertyName() != null || (this.sortedColumn != -1 && this.sortedColumn < this.headerCellList.size())) { - if (sortedHeaderCell.getSortProperty() != null) - { - Collections.sort(list, new RowSorter( - this.sortedColumn, - sortedHeaderCell.getSortProperty(), - getTableDecorator(), - this.sortOrderAscending, - sortedHeaderCell.getComparator())); - } - else - { - Collections.sort(list, new RowSorter( - this.sortedColumn, - sortedHeaderCell.getBeanPropertyName(), - getTableDecorator(), - this.sortOrderAscending, - sortedHeaderCell.getComparator())); - } + + String sorted = (sortedHeaderCell.getSortProperty() != null) + ? sortedHeaderCell.getSortProperty() + : sortedHeaderCell.getBeanPropertyName(); + + Collections.sort(list, new RowSorter( + this.sortedColumn, + sorted, + getTableDecorator(), + this.sortOrderAscending, + sortedHeaderCell.getComparator())); } } @@ -459,6 +462,42 @@ { return encoding; } + + /** + * Obtain this table's caption. + * @return This table's caption. + */ + public String getCaption() + { + return this.caption; + } + + /** + * Set this table's caption. + * @param caption This table's caption. + */ + public void setCaption(String caption) + { + this.caption = caption; + } + + /** + * Obtain this table's footer. + * @return This table's footer. + */ + public String getFooter() + { + return this.footer; + } + + /** + * Set this table's footer. + * @param footer This table's footer. + */ + public void setFooter(String footer) + { + this.footer = footer; + } /** * @see java.lang.Object#toString() @@ -479,6 +518,8 @@ .append("sortedColumnHeader", this.getSortedColumnHeader()) //$NON-NLS-1$ .append("sorted", this.isSorted()) //$NON-NLS-1$ .append("tableDecorator", this.tableDecorator) //$NON-NLS-1$ + .append("caption", this.getCaption()) //$NON-NLS-1 + .append("footer", this.getFooter()) //$NON-NLS-1 .toString(); } } \ No newline at end of file Index: src/main/java/org/displaytag/properties/TableProperties.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/java/org/displaytag/properties/TableProperties.java,v retrieving revision 1.38 diff -u -r1.38 TableProperties.java --- src/main/java/org/displaytag/properties/TableProperties.java 3 Sep 2005 17:53:30 -0000 1.38 +++ src/main/java/org/displaytag/properties/TableProperties.java 26 Sep 2005 02:52:38 -0000 @@ -254,6 +254,12 @@ * [export type].[property name] */ public static final String PROPERTY_EXPORT_PREFIX = "export"; //$NON-NLS-1$ + + /** + * suffix used to set the export decorator property name. The full property name is export. + * [export type].decorator + */ + public static final String PROPERTY_EXPORT_DECORATOR_SUFFIX = "decorator"; //$NON-NLS-1$ /** * property export.types: holds the list of export available export types. @@ -1279,4 +1285,15 @@ return defaultValue; } + + /** + * Obtain the name of the decorator configured for a given media type. + * @param thatEnum A media type + * @return The name of the decorator configured for a given media type. + */ + public String getExportDecoratorName(MediaTypeEnum thatEnum) + { + return getProperty(PROPERTY_EXPORT_PREFIX + SEP + thatEnum + SEP + + PROPERTY_EXPORT_DECORATOR_SUFFIX); + } } \ No newline at end of file Index: src/main/java/org/displaytag/tags/CaptionTag.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/java/org/displaytag/tags/CaptionTag.java,v retrieving revision 1.7 diff -u -r1.7 CaptionTag.java --- src/main/java/org/displaytag/tags/CaptionTag.java 13 Nov 2004 15:10:58 -0000 1.7 +++ src/main/java/org/displaytag/tags/CaptionTag.java 26 Sep 2005 02:52:38 -0000 @@ -11,11 +11,15 @@ */ package org.displaytag.tags; +import java.util.List; + import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.BodyTagSupport; import org.displaytag.exception.TagStructureException; +import org.displaytag.properties.MediaTypeEnum; import org.displaytag.util.HtmlAttributeMap; +import org.displaytag.util.MediaUtil; import org.displaytag.util.MultipleHtmlAttribute; import org.displaytag.util.TagConstants; @@ -25,14 +29,14 @@ * @author Fabrizio Giustina * @version $Revision: 1.7 $ ($Author: fgiust $) */ -public class CaptionTag extends BodyTagSupport +public class CaptionTag extends BodyTagSupport implements MediaUtil.SupportsMedia { /** * D1597A17A6. */ private static final long serialVersionUID = 899149338534L; - + /** * Map containing all the standard html attributes. */ @@ -44,6 +48,11 @@ private boolean firstIteration = true; /** + * The media supported attribute. + */ + private List supportedMedia; + + /** * setter for the "style" html attribute. * @param value attribute value */ @@ -140,6 +149,12 @@ { throw new TagStructureException(getClass(), "caption", "table"); //$NON-NLS-1$ //$NON-NLS-2$ } + + MediaTypeEnum currentMediaType = (MediaTypeEnum) this.pageContext.findAttribute(TableTag.PAGE_ATTRIBUTE_MEDIA); + if (!MediaUtil.availableForMedia(this, currentMediaType)) + { + return SKIP_BODY; + } // add caption only once if (tableTag.isFirstIteration()) @@ -154,6 +169,31 @@ } /** + * @see org.displaytag.util.MediaUtil.SupportsMedia#setSupportedMedia(java.util.List) + */ + public void setSupportedMedia(List media) + { + this.supportedMedia = media; + } + + /** + * @see org.displaytag.util.MediaUtil.SupportsMedia#getSupportedMedia() + */ + public List getSupportedMedia() + { + return this.supportedMedia; + } + + /** + * Tag setter. + * @param media the space delimited list of supported types + */ + public void setMedia(String media) + { + MediaUtil.setMedia(this, media); + } + + /** * @see javax.servlet.jsp.tagext.Tag#doEndTag() */ public int doEndTag() throws JspException @@ -166,19 +206,22 @@ { throw new TagStructureException(getClass(), "caption", "table"); //$NON-NLS-1$ //$NON-NLS-2$ } - - StringBuffer buffer = new StringBuffer(); - buffer.append(getOpenTag()); + + MediaTypeEnum currentMediaType = (MediaTypeEnum) this.pageContext.findAttribute( + TableTag.PAGE_ATTRIBUTE_MEDIA); + if (currentMediaType != null && !MediaUtil.availableForMedia(this, currentMediaType)) + { + return SKIP_BODY; + } if (getBodyContent() != null) { - buffer.append(getBodyContent().getString()); + // set the caption format-agnostic content so it can be written in various formats. + tableTag.setCaption(getBodyContent().getString()); + // set the nested caption tag to write the caption in html format. See HtmlTableWriter.writeCaption + tableTag.setCaptionTag(this); } - - buffer.append(getCloseTag()); - - tableTag.setCaption(buffer.toString()); - + this.firstIteration = false; } @@ -193,6 +236,7 @@ { super.release(); this.attributeMap.clear(); + this.supportedMedia = null; } } \ No newline at end of file Index: src/main/java/org/displaytag/tags/CaptionTagBeanInfo.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/java/org/displaytag/tags/CaptionTagBeanInfo.java,v retrieving revision 1.6 diff -u -r1.6 CaptionTagBeanInfo.java --- src/main/java/org/displaytag/tags/CaptionTagBeanInfo.java 27 Dec 2004 16:50:41 -0000 1.6 +++ src/main/java/org/displaytag/tags/CaptionTagBeanInfo.java 26 Sep 2005 02:52:38 -0000 @@ -43,6 +43,8 @@ CaptionTag.class, null, "setId")); //$NON-NLS-1$ proplist.add(new PropertyDescriptor("lang", //$NON-NLS-1$ CaptionTag.class, null, "setLang")); //$NON-NLS-1$ + proplist.add(new PropertyDescriptor("media", //$NON-NLS-1$ + CaptionTag.class, null, "setMedia")); //$NON-NLS-1$ proplist.add(new PropertyDescriptor("style", //$NON-NLS-1$ CaptionTag.class, null, "setStyle")); //$NON-NLS-1$ proplist.add(new PropertyDescriptor("title", //$NON-NLS-1$ Index: src/main/java/org/displaytag/tags/ColumnTag.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/java/org/displaytag/tags/ColumnTag.java,v retrieving revision 1.59 diff -u -r1.59 ColumnTag.java --- src/main/java/org/displaytag/tags/ColumnTag.java 13 Jul 2005 21:56:59 -0000 1.59 +++ src/main/java/org/displaytag/tags/ColumnTag.java 26 Sep 2005 02:52:39 -0000 @@ -12,7 +12,6 @@ package org.displaytag.tags; import java.text.Collator; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -39,6 +38,7 @@ import org.displaytag.properties.SortOrderEnum; import org.displaytag.util.Href; import org.displaytag.util.HtmlAttributeMap; +import org.displaytag.util.MediaUtil; import org.displaytag.util.MultipleHtmlAttribute; import org.displaytag.util.TagConstants; @@ -54,9 +54,9 @@ *

* @author mraible * @author Fabrizio Giustina - * @version $Revision: 1.59 $ ($Author: fgiust $) + * @version $Revision: 1.60 $ ($Author: fgiust $) */ -public class ColumnTag extends BodyTagSupport +public class ColumnTag extends BodyTagSupport implements MediaUtil.SupportsMedia { /** @@ -575,21 +575,6 @@ } /** - * Is this column configured for the media type? - * @param mediaType the currentMedia type - * @return true if the column should be displayed for this request - */ - public boolean availableForMedia(MediaTypeEnum mediaType) - { - if (supportedMedia == null) - { - return true; - } - - return this.supportedMedia.contains(mediaType); - } - - /** * Looks up the parent table tag. * @return a table tag instance. */ @@ -604,32 +589,26 @@ */ public void setMedia(String media) { - if (StringUtils.isBlank(media) || media.toLowerCase().indexOf("all") > -1) - { - this.supportedMedia = null; - return; - } - this.supportedMedia = new ArrayList(); - String[] values = StringUtils.split(media); - for (int i = 0; i < values.length; i++) - { - String value = values[i]; - if (!StringUtils.isBlank(value)) - { - MediaTypeEnum type = MediaTypeEnum.fromName(value.toLowerCase()); - if (type == null) - { - log.warn("Unrecognized value for attribute \"media\" value=\"" + value + "\""); - } - else - { - this.supportedMedia.add(type); - } - } - } + MediaUtil.setMedia(this, media); } /** + * @see org.displaytag.util.MediaUtil.SupportsMedia#setSupportedMedia(java.util.List) + */ + public void setSupportedMedia(List media) + { + this.supportedMedia = media; + } + + /** + * @see org.displaytag.util.MediaUtil.SupportsMedia#getSupportedMedia() + */ + public List getSupportedMedia() + { + return this.supportedMedia; + } + + /** * sets the name given to the server when sorting this column * @param sortName name given to the server to sort this column */ @@ -668,7 +647,7 @@ TableTag tableTag = getTableTag(); MediaTypeEnum currentMediaType = (MediaTypeEnum) this.pageContext.findAttribute(TableTag.PAGE_ATTRIBUTE_MEDIA); - if (currentMediaType != null && !availableForMedia(currentMediaType)) + if (currentMediaType != null && !MediaUtil.availableForMedia(this, currentMediaType)) { if (log.isDebugEnabled()) { @@ -751,7 +730,11 @@ headerCell.setSortProperty(this.sortProperty); headerCell.setPropertyConvertor(PropertyConvertorFactory.createNumberConverter(tableTag.getProperties())); headerCell.setTotaled(this.totaled); - headerCell.setComparator(this.comparator); + + Comparator headerComparator = (comparator == null) ? comparator : new DefaultComparator(Collator + .getInstance(tableTag.getProperties().getLocale())); + + headerCell.setComparator(headerComparator); headerCell.setColumnValueClass(valueClass); headerCell.setDefaultSortOrder(this.defaultorder); headerCell.setSortName(this.sortName); @@ -882,17 +865,11 @@ } MediaTypeEnum currentMediaType = (MediaTypeEnum) this.pageContext.findAttribute(TableTag.PAGE_ATTRIBUTE_MEDIA); - if (!availableForMedia(currentMediaType)) + if (!MediaUtil.availableForMedia(this, currentMediaType)) { return SKIP_BODY; } - if (comparator instanceof DefaultComparator) - { - DefaultComparator def = (DefaultComparator) comparator; - def.setCollator(Collator.getInstance(tableTag.getProperties().getLocale())); - } - return super.doStartTag(); } Index: src/main/java/org/displaytag/tags/TableFooterTag.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/java/org/displaytag/tags/TableFooterTag.java,v retrieving revision 1.9 diff -u -r1.9 TableFooterTag.java --- src/main/java/org/displaytag/tags/TableFooterTag.java 13 Jul 2005 21:34:34 -0000 1.9 +++ src/main/java/org/displaytag/tags/TableFooterTag.java 26 Sep 2005 02:52:39 -0000 @@ -11,12 +11,15 @@ */ package org.displaytag.tags; +import java.util.List; import java.util.Map; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.BodyTagSupport; import org.displaytag.exception.TagStructureException; +import org.displaytag.properties.MediaTypeEnum; +import org.displaytag.util.MediaUtil; /** @@ -25,13 +28,18 @@ * @author rapruitt * @version $Revision: 1.9 $ ($Author: fgiust $) */ -public class TableFooterTag extends BodyTagSupport +public class TableFooterTag extends BodyTagSupport implements MediaUtil.SupportsMedia { /** * D1597A17A6. */ private static final long serialVersionUID = 899149338534L; + + /** + * The media supported attribute. + */ + private List supportedMedia; /** * @see javax.servlet.jsp.tagext.Tag#doEndTag() @@ -44,6 +52,12 @@ { throw new TagStructureException(getClass(), "footer", "table"); } + + MediaTypeEnum currentMediaType = (MediaTypeEnum) this.pageContext.findAttribute(TableTag.PAGE_ATTRIBUTE_MEDIA); + if (currentMediaType != null && !MediaUtil.availableForMedia(this, currentMediaType)) + { + return SKIP_BODY; + } if (tableTag.isLastIteration()) { @@ -67,6 +81,12 @@ { throw new TagStructureException(getClass(), "footer", "table"); } + + MediaTypeEnum currentMediaType = (MediaTypeEnum) this.pageContext.findAttribute(TableTag.PAGE_ATTRIBUTE_MEDIA); + if (!MediaUtil.availableForMedia(this, currentMediaType)) + { + return SKIP_BODY; + } // Run the footer only when all of the cells have been populated if (tableTag.isLastIteration()) @@ -82,5 +102,38 @@ return SKIP_BODY; } - + + /** + * @see org.displaytag.util.MediaUtil.SupportsMedia#setSupportedMedia(java.util.List) + */ + public void setSupportedMedia(List media) + { + this.supportedMedia = media; + } + + /** + * @see org.displaytag.util.MediaUtil.SupportsMedia#getSupportedMedia() + */ + public List getSupportedMedia() + { + return this.supportedMedia; + } + + /** + * Tag setter. + * @param media the space delimited list of supported types + */ + public void setMedia(String media) + { + MediaUtil.setMedia(this, media); + } + + /** + * @see javax.servlet.jsp.tagext.Tag#release() + */ + public void release() + { + super.release(); + this.supportedMedia = null; + } } \ No newline at end of file Index: src/main/java/org/displaytag/tags/TableTag.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/java/org/displaytag/tags/TableTag.java,v retrieving revision 1.104 diff -u -r1.104 TableTag.java --- src/main/java/org/displaytag/tags/TableTag.java 3 Sep 2005 17:53:30 -0000 1.104 +++ src/main/java/org/displaytag/tags/TableTag.java 26 Sep 2005 02:52:41 -0000 @@ -18,7 +18,6 @@ import java.text.MessageFormat; import java.util.Collection; import java.util.HashMap; -import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -40,11 +39,9 @@ import org.displaytag.Messages; import org.displaytag.decorator.DecoratorFactory; import org.displaytag.decorator.TableDecorator; -import org.displaytag.exception.DecoratorException; import org.displaytag.exception.ExportException; import org.displaytag.exception.FactoryInstantiationException; import org.displaytag.exception.InvalidTagAttributeValueException; -import org.displaytag.exception.ObjectLookupException; import org.displaytag.exception.WrappedRuntimeException; import org.displaytag.export.BinaryExportView; import org.displaytag.export.ExportView; @@ -52,10 +49,8 @@ import org.displaytag.export.TextExportView; import org.displaytag.model.Cell; import org.displaytag.model.Column; -import org.displaytag.model.ColumnIterator; import org.displaytag.model.HeaderCell; import org.displaytag.model.Row; -import org.displaytag.model.RowIterator; import org.displaytag.model.TableModel; import org.displaytag.pagination.PaginatedList; import org.displaytag.pagination.PaginatedListSmartListHelper; @@ -63,6 +58,7 @@ import org.displaytag.properties.MediaTypeEnum; import org.displaytag.properties.SortOrderEnum; import org.displaytag.properties.TableProperties; +import org.displaytag.render.HtmlTableWriter; import org.displaytag.util.Anchor; import org.displaytag.util.CollectionUtil; import org.displaytag.util.DependencyChecker; @@ -80,7 +76,7 @@ * functionality as the struts tag. * @author mraible * @author Fabrizio Giustina - * @version $Revision: 1.104 $ ($Author: fgiust $) + * @version $Revision: 1.107 $ ($Author: fgiust $) */ public class TableTag extends HtmlTableTag { @@ -236,11 +232,6 @@ // -- end tag attributes -- /** - * Map which contains previous row values. Needed for grouping - */ - private Map previousRow; - - /** * table model - initialized in doStartTag(). */ private TableModel tableModel; @@ -251,11 +242,6 @@ private Row currentRow; /** - * next row. - */ - private Map nextRow; - - /** * Used by various functions when the person wants to do paging - cleaned in doEndTag(). */ private SmartListHelper listHelper; @@ -296,7 +282,7 @@ private ParamEncoder paramEncoder; /** - * static footer added using the footer tag. + * Static footer added using the footer tag. */ private String footer; @@ -306,9 +292,14 @@ private boolean lastIteration; /** - * static caption added using the footer tag. + * Static caption added using the footer tag. */ private String caption; + + /** + * Child caption tag + */ + private CaptionTag captionTag; /** * Included row range. If no rows can be skipped the range is from 0 to Long.MAX_VALUE. Range check should be always @@ -318,15 +309,12 @@ */ private Object filteredRows; - // /** * The paginated list containing the external pagination and sort parameters The presence of this paginated list is * what determines if external pagination and sorting is used or not. */ private PaginatedList paginatedList; - // - /** * Is this the last iteration? * @return boolean true if this is the last iteration @@ -352,6 +340,17 @@ public void setFooter(String string) { this.footer = string; + this.tableModel.setFooter(this.footer); + } + + + /** + * Obtain the footer content. Called by TableWriters to render the footer. + * @return string footer content + */ + public String getFooter() + { + return this.footer; } /** @@ -361,6 +360,25 @@ public void setCaption(String string) { this.caption = string; + this.tableModel.setCaption(this.caption); + } + + /** + * Set the child caption tag. + * @param captionTag Child caption tag + */ + public void setCaptionTag(CaptionTag captionTag) + { + this.captionTag = captionTag; + } + + /** + * Obtain the child caption tag. + * @return The child caption tag + */ + public CaptionTag getCaptionTag() + { + return this.captionTag; } /** @@ -610,11 +628,6 @@ */ public void setUid(String value) { - if (getHtmlId() == null) - { - setHtmlId(value); // by default id is actually used for the html id attribute, if no htmlId is added - } - this.uid = value; } @@ -860,7 +873,6 @@ /** * Reads parameters from the request and initialize all the needed table model attributes. - * @throws ObjectLookupException for problems in evaluating the expression in the "name" attribute * @throws FactoryInstantiationException for problems in instantiating a RequestHelperFactory */ private void initParameters() throws JspTagException, FactoryInstantiationException @@ -1043,10 +1055,11 @@ int fullSize = ((Collection) this.list).size(); start = (this.pageNumber - 1) * this.pagesize; - // invalid page requested, go back to page one + // invalid page requested, go back to last page if (start > fullSize) { - start = 0; + int div = fullSize / this.pagesize; + start = (fullSize % this.pagesize == 0) ? div : div + 1; } end = start + this.pagesize; @@ -1232,7 +1245,20 @@ describeEmptyTable(); } - TableDecorator tableDecorator = DecoratorFactory.loadTableDecorator(this.decoratorName); + //TableDecorator tableDecorator = DecoratorFactory.loadTableDecorator(this.decoratorName); + String tableDecoratorName = null; + Object previousMediaType = this.pageContext.getAttribute(PAGE_ATTRIBUTE_MEDIA); + if (MediaTypeEnum.HTML.equals(this.currentMediaType) + && (previousMediaType == null || MediaTypeEnum.HTML.equals(previousMediaType))) + { + tableDecoratorName = this.decoratorName; + } + else if (!MediaTypeEnum.HTML.equals(this.currentMediaType)) + { + tableDecoratorName = this.getProperties().getExportDecoratorName(this.currentMediaType); + } + TableDecorator tableDecorator = DecoratorFactory.loadTableDecorator(tableDecoratorName); + if (tableDecorator != null) { @@ -1257,7 +1283,7 @@ int returnValue = EVAL_PAGE; // check for nested tables - Object previousMediaType = this.pageContext.getAttribute(PAGE_ATTRIBUTE_MEDIA); + //Object previousMediaType = this.pageContext.getAttribute(PAGE_ATTRIBUTE_MEDIA); if (MediaTypeEnum.HTML.equals(this.currentMediaType) && (previousMediaType == null || MediaTypeEnum.HTML.equals(previousMediaType))) { @@ -1294,14 +1320,13 @@ this.currentMediaType = null; this.baseHref = null; this.caption = null; + this.captionTag = null; this.currentRow = null; this.doAfterBodyExecuted = false; this.footer = null; this.listHelper = null; - this.nextRow = null; this.pageNumber = 0; this.paramEncoder = null; - this.previousRow = null; this.properties = null; this.rowNumber = 1; this.tableIterator = null; @@ -1580,6 +1605,7 @@ } /** + * Uses HtmlTableWriter to write table * called when data have to be displayed in a html page. * @throws JspException generic exception */ @@ -1587,91 +1613,19 @@ { JspWriter out = this.pageContext.getOut(); - if (log.isDebugEnabled()) - { - log.debug("[" + getUid() + "] getHTMLData called for table [" + getUid() + "]"); - } - - boolean noItems = this.tableModel.getRowListPage().size() == 0; - - if (noItems && !this.properties.getEmptyListShowTable()) - { - write(this.properties.getEmptyListMessage(), out); - return; - } - - // variables to hold the previous row columns values. - this.previousRow = new Hashtable(10); - - // variables to hold next row column values. - this.nextRow = new Hashtable(10); - - // Put the page stuff there if it needs to be there... - if (this.properties.getAddPagingBannerTop()) - { - // search result and navigation bar - writeSearchResultAndNavigation(); - } - String css = this.properties.getCssTable(); if (StringUtils.isNotBlank(css)) { this.addClass(css); } - - // open table - write(getOpenTag(), out); - - // caption - if (this.caption != null) - { - write(this.caption, out); - } - - // thead - if (this.properties.getShowHeader()) - { - writeTableHeader(); - } - - if (this.footer != null) - { - write(TagConstants.TAG_TFOOTER_OPEN, out); - write(this.footer, out); - write(TagConstants.TAG_TFOOTER_CLOSE, out); - // reset footer - this.footer = null; - } - - // open table body - write(TagConstants.TAG_TBODY_OPEN, out); - - // write table body - writeTableBody(); - - // close table body - write(TagConstants.TAG_TBODY_CLOSE, out); - - // close table - write(getCloseTag(), out); - - writeTableFooter(); - - if (this.tableModel.getTableDecorator() != null) - { - this.tableModel.getTableDecorator().finish(); - } - - if (log.isDebugEnabled()) - { - log.debug("[" + getUid() + "] getHTMLData end"); - } + //use HtmlTableWriter to write table + new HtmlTableWriter(this, out).writeTable(this.tableModel, this.getUid()); } /** * Generates the table header, including the first row of the table which displays the titles of the columns. */ - private void writeTableHeader() + public void writeTableHeader() { JspWriter out = this.pageContext.getOut(); @@ -1776,7 +1730,9 @@ if (headerCell.getDefaultSortOrder() != null) { boolean sortAscending = SortOrderEnum.ASCENDING.equals(headerCell.getDefaultSortOrder()); - nowOrderAscending = (!headerCell.isAlreadySorted()) ? sortAscending : !sortAscending; + nowOrderAscending = headerCell.isAlreadySorted() + ? !this.tableModel.isSortOrderAscending() + : sortAscending; } else { @@ -1835,155 +1791,6 @@ } /** - * This takes a column value and grouping index as the argument. It then groups the column and returns the - * appropriate string back to the caller. - * @param value String - * @param group int - * @return String - */ - private String groupColumns(String value, int group) - { - - if ((group == 1) && this.nextRow.size() > 0) - { - // we are at the begining of the next row so copy the contents from nextRow to the previousRow. - this.previousRow.clear(); - this.previousRow.putAll(this.nextRow); - this.nextRow.clear(); - } - - if (!this.nextRow.containsKey(new Integer(group))) - { - // Key not found in the nextRow so adding this key now... - // remember all the old values. - this.nextRow.put(new Integer(group), value); - } - - // Start comparing the value we received, along with the grouping index. - // if no matching value is found in the previous row then return the value. - // if a matching value is found then this value should not get printed out - // so return an empty String - if (this.previousRow.containsKey(new Integer(group))) - { - for (int j = 1; j <= group; j++) - { - - if (!((String) this.previousRow.get(new Integer(j))).equals((this.nextRow.get(new Integer(j))))) - { - // no match found so return this value back to the caller. - return value; - } - } - } - - // This is used, for when there is no data in the previous row, - // It gets used only the first time. - if (this.previousRow.size() == 0) - { - return value; - } - - // There is corresponding value in the previous row - // this value doesn't need to be printed, return an empty String - return TagConstants.EMPTY_STRING; - } - - /** - * Writes the table body content. - * @throws ObjectLookupException for errors in looking up properties in objects - * @throws DecoratorException for errors returned by decorators - */ - private void writeTableBody() throws ObjectLookupException, DecoratorException - { - JspWriter out = this.pageContext.getOut(); - - // Ok, start bouncing through our list (only the visible part) - RowIterator rowIterator = this.tableModel.getRowIterator(false); - - // iterator on rows - while (rowIterator.hasNext()) - { - Row row = rowIterator.next(); - if (log.isDebugEnabled()) - { - log.debug("[" + getUid() + "] rowIterator.next()=" + row); - } - if (this.tableModel.getTableDecorator() != null) - { - String stringStartRow = this.tableModel.getTableDecorator().startRow(); - if (stringStartRow != null) - { - write(stringStartRow, out); - } - } - - // open tr - write(row.getOpenTag(), out); - - // iterator on columns - if (log.isDebugEnabled()) - { - log.debug("[" + getUid() + "] creating ColumnIterator on " + this.tableModel.getHeaderCellList()); - } - ColumnIterator columnIterator = row.getColumnIterator(this.tableModel.getHeaderCellList()); - - while (columnIterator.hasNext()) - { - Column column = columnIterator.nextColumn(); - - // Get the value to be displayed for the column - write(column.getOpenTag(), out); - String value = column.getChoppedAndLinkedValue(); - - // check if column is grouped - if (column.getGroup() != -1) - { - value = this.groupColumns(value, column.getGroup()); - } - - // add column value - write(value, out); - write(column.getCloseTag(), out); - } - - // no columns? - if (this.tableModel.isEmpty()) - { - if (log.isDebugEnabled()) - { - log.debug("[" + getUid() + "] table has no columns"); - } - write(TagConstants.TAG_TD_OPEN, out); - write(row.getObject().toString(), out); - write(TagConstants.TAG_TD_CLOSE, out); - } - - // close tr - write(row.getCloseTag(), out); - - if (this.tableModel.getTableDecorator() != null) - { - String endRow = this.tableModel.getTableDecorator().finishRow(); - if (endRow != null) - { - write(endRow, out); - } - } - } - - if (this.varTotals != null) - { - this.pageContext.setAttribute(this.varTotals, getTotals()); - } - - if (this.tableModel.getRowListPage().size() == 0) - { - write(MessageFormat.format(properties.getEmptyListRowMessage(), new Object[]{new Integer(this.tableModel - .getNumberOfColumns())}), out); - } - } - - /** * Get the column totals Map. If there is no varTotals defined, there are no totals. * @return a Map of totals where the key is the column number and the value is the total for that column */ @@ -2019,7 +1826,7 @@ /** * Generates table footer with links for export commands. */ - private void writeTableFooter() + public void writeNavigationAndExportLinks() { // Put the page stuff there if it needs to be there... if (this.properties.getAddPagingBannerBottom()) @@ -2037,7 +1844,7 @@ /** * generates the search result and navigation bar. */ - private void writeSearchResultAndNavigation() + public void writeSearchResultAndNavigation() { if ((this.paginatedList == null && this.pagesize != 0 && this.listHelper != null) || (this.paginatedList != null)) @@ -2118,6 +1925,11 @@ */ public void release() { + if (log.isDebugEnabled()) + { + log.debug("[" + getUid() + "] release() called"); + } + super.release(); // tag attributes @@ -2169,4 +1981,29 @@ return this.paramEncoder.encodeParameterName(parameterName); } + /** + * create the open tag containing all the attributes. + * @return open tag string: %lt;table attribute="value" ... > + */ + public String getOpenTag() + { + + if (this.uid != null && getAttributeMap().get(TagConstants.ATTRIBUTE_ID) == null) + { + // we need to clone the attribute map in order to "fix" the html id when using only the "uid" attribute + Map localAttributeMap = (Map) getAttributeMap().clone(); + localAttributeMap.put(TagConstants.ATTRIBUTE_ID, this.uid); + + StringBuffer buffer = new StringBuffer(); + buffer.append(TagConstants.TAG_OPEN).append(TagConstants.TABLE_TAG_NAME); + buffer.append(localAttributeMap); + buffer.append(TagConstants.TAG_CLOSE); + + return buffer.toString(); + + } + + return super.getOpenTag(); + } + } \ No newline at end of file Index: src/main/java/org/displaytag/tags/el/ELCaptionTag.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/java/org/displaytag/tags/el/ELCaptionTag.java,v retrieving revision 1.4 diff -u -r1.4 ELCaptionTag.java --- src/main/java/org/displaytag/tags/el/ELCaptionTag.java 13 Nov 2004 15:10:59 -0000 1.4 +++ src/main/java/org/displaytag/tags/el/ELCaptionTag.java 26 Sep 2005 02:52:43 -0000 @@ -48,6 +48,11 @@ * Expression for the "lang" tag attribute. */ private String langExpr; + + /** + * Expression for the "media" tag attribute. + */ + private String mediaExpr; /** * Expression for the "style" tag attribute. @@ -90,6 +95,15 @@ { langExpr = value; } + + /** + * @see org.displaytag.tags.CaptionTag#setMedia(java.lang.String) + * @param value EL expression for attribute value + */ + public void setMedia(String value) + { + mediaExpr = value; + } /** * @see org.displaytag.tags.CaptionTag#setStyle(java.lang.String) @@ -140,6 +154,10 @@ { super.setLang(eval.evalString("lang", langExpr)); //$NON-NLS-1$ } + if (mediaExpr != null) + { + super.setMedia(eval.evalString("media", mediaExpr)); //$NON-NLS-1$ + } if (styleExpr != null) { super.setStyle(eval.evalString("style", styleExpr)); //$NON-NLS-1$ @@ -160,6 +178,7 @@ this.dirExpr = null; this.idExpr = null; this.langExpr = null; + this.mediaExpr = null; this.styleExpr = null; this.titleExpr = null; } Index: src/main/java/org/displaytag/tags/el/ELCaptionTagBeanInfo.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/java/org/displaytag/tags/el/ELCaptionTagBeanInfo.java,v retrieving revision 1.3 diff -u -r1.3 ELCaptionTagBeanInfo.java --- src/main/java/org/displaytag/tags/el/ELCaptionTagBeanInfo.java 13 Nov 2004 15:10:59 -0000 1.3 +++ src/main/java/org/displaytag/tags/el/ELCaptionTagBeanInfo.java 26 Sep 2005 02:52:43 -0000 @@ -45,6 +45,8 @@ ELCaptionTag.class, null, "setId")); //$NON-NLS-1$ proplist.add(new PropertyDescriptor("lang", //$NON-NLS-1$ ELCaptionTag.class, null, "setLang")); //$NON-NLS-1$ + proplist.add(new PropertyDescriptor("media", //$NON-NLS-1$ + ELColumnTag.class, null, "setMedia")); //$NON-NLS-1$ proplist.add(new PropertyDescriptor("style", //$NON-NLS-1$ ELCaptionTag.class, null, "setStyle")); //$NON-NLS-1$ proplist.add(new PropertyDescriptor("title", //$NON-NLS-1$ Index: src/main/resources/META-INF/displaytag-el.tld =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/resources/META-INF/displaytag-el.tld,v retrieving revision 1.4 diff -u -r1.4 displaytag-el.tld --- src/main/resources/META-INF/displaytag-el.tld 21 Aug 2005 21:42:30 -0000 1.4 +++ src/main/resources/META-INF/displaytag-el.tld 26 Sep 2005 02:52:43 -0000 @@ -545,6 +545,18 @@ Tag wich should be nested into a table tag to provide a custom table footer. The body of the tag is into the tfoot section of the table. The totals variable, if designated, will be in pageContext in this tag. + + media + false + true + java.lang.String + + Use this attribute to keep a footer from being output during an export. The caption will only render for + the named media type(s) - it won't be added to the table if the current request media is not supported. + Can be any space separated combination of 'html', 'csv', 'xml', 'all', or 'excel'. Defaults to 'all'. + See the export page in the example webapp for more details. + + @@ -604,6 +616,18 @@ true html pass through attribute. + + media + false + true + java.lang.String + + Use this attribute to keep a caption from being output during an export. The caption will only render for + the named media type(s) - it won't be added to the table if the current request media is not supported. + Can be any space separated combination of 'html', 'csv', 'xml', 'all', or 'excel'. Defaults to 'all'. + See the export page in the example webapp for more details. + + Index: src/main/resources/META-INF/displaytag.tld =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/resources/META-INF/displaytag.tld,v retrieving revision 1.4 diff -u -r1.4 displaytag.tld --- src/main/resources/META-INF/displaytag.tld 21 Aug 2005 21:42:30 -0000 1.4 +++ src/main/resources/META-INF/displaytag.tld 26 Sep 2005 02:52:43 -0000 @@ -664,6 +664,18 @@ Tag wich should be nested into a table tag to provide a custom table footer. The body of the tag is into the tfoot section of the table. The totals variable, if designated, will be in pageContext in this tag. + + media + false + true + java.lang.String + + Use this attribute to keep a footer from being output during an export. The caption will only render for + the named media type(s) - it won't be added to the table if the current request media is not supported. + Can be any space separated combination of 'html', 'csv', 'xml', 'all', or 'excel'. Defaults to 'all'. + See the export page in the example webapp for more details. + + @@ -729,6 +741,18 @@ java.lang.String html pass through attribute. + + media + false + true + java.lang.String + + Use this attribute to keep a caption from being output during an export. The caption will only render for + the named media type(s) - it won't be added to the table if the current request media is not supported. + Can be any space separated combination of 'html', 'csv', 'xml', 'all', or 'excel'. Defaults to 'all'. + See the export page in the example webapp for more details. + + Index: src/main/resources/org/displaytag/properties/TableTag.properties =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/main/resources/org/displaytag/properties/TableTag.properties,v retrieving revision 1.2 diff -u -r1.2 TableTag.properties --- src/main/resources/org/displaytag/properties/TableTag.properties 3 Sep 2005 17:58:06 -0000 1.2 +++ src/main/resources/org/displaytag/properties/TableTag.properties 26 Sep 2005 02:52:43 -0000 @@ -57,6 +57,10 @@ export.pdf.include_header=true export.pdf.filename= +export.rtf=false +export.rtf.label=RTF +export.rtf.include_header=true +export.rtf.filename= # messages Index: src/test/java/org/displaytag/test/DisplaytagCase.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/test/java/org/displaytag/test/DisplaytagCase.java,v retrieving revision 1.14 diff -u -r1.14 DisplaytagCase.java --- src/test/java/org/displaytag/test/DisplaytagCase.java 3 Sep 2005 17:53:30 -0000 1.14 +++ src/test/java/org/displaytag/test/DisplaytagCase.java 26 Sep 2005 02:52:43 -0000 @@ -2,6 +2,7 @@ import java.io.File; import java.net.URL; +import java.net.URLDecoder; import junit.framework.TestCase; @@ -73,7 +74,7 @@ // need to pass a web.xml file to setup servletunit working directory ClassLoader classLoader = getClass().getClassLoader(); URL webXmlUrl = classLoader.getResource("WEB-INF/web.xml"); - String path = webXmlUrl.getFile(); + String path = URLDecoder.decode(webXmlUrl.getFile(), "UTF-8"); // start servletRunner runner = new ServletRunner(new File(path), CONTEXT); Index: src/test/java/org/displaytag/test/TestAll.java =================================================================== RCS file: /cvsroot/displaytag/displaytag/src/test/java/org/displaytag/test/TestAll.java,v retrieving revision 1.3 diff -u -r1.3 TestAll.java --- src/test/java/org/displaytag/test/TestAll.java 26 Oct 2004 18:37:22 -0000 1.3 +++ src/test/java/org/displaytag/test/TestAll.java 26 Sep 2005 02:52:43 -0000 @@ -4,6 +4,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -93,7 +94,7 @@ String className = TestAll.class.getName(); URL testFile = TestAll.class.getResource("TestAll.class"); log.debug(testFile.getFile()); - File classRoot = new File(testFile.getFile()).getParentFile(); + File classRoot = new File(URLDecoder.decode(testFile.getFile(), "UTF-8")).getParentFile(); while (className.indexOf(".") > -1) { classRoot = classRoot.getParentFile(); Index: src/main/java/org/displaytag/decorator/itext/DecoratesItext.java =================================================================== RCS file: src/main/java/org/displaytag/decorator/itext/DecoratesItext.java diff -N src/main/java/org/displaytag/decorator/itext/DecoratesItext.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/main/java/org/displaytag/decorator/itext/DecoratesItext.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,39 @@ +/** + * Licensed under the Artistic License; you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://displaytag.sourceforge.net/license.html + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +package org.displaytag.decorator.itext; + +import com.lowagie.text.Font; +import com.lowagie.text.Table; + +/** + * An implementor of this interface decorates tables and columns appearing in iText documents. + * + * @author Jorge L. Barroso + * @version $Revision: $ ($Author: jbarroso$) + */ +public interface DecoratesItext +{ + /** + * Set the iText table used to render a table model. + * + * @param table The iText table used to render a table model. + */ + void setTable(Table table); + + /** + * Set the font used to render a table's content. + * + * @param font The font used to render a table's content. + */ + void setFont(Font font); +} Index: src/main/java/org/displaytag/export/DefaultItextExportView.java =================================================================== RCS file: src/main/java/org/displaytag/export/DefaultItextExportView.java diff -N src/main/java/org/displaytag/export/DefaultItextExportView.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/main/java/org/displaytag/export/DefaultItextExportView.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,126 @@ +/** + * Licensed under the Artistic License; you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://displaytag.sourceforge.net/license.html + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.displaytag.export; + +import java.io.OutputStream; + +import javax.servlet.jsp.JspException; + +import org.displaytag.Messages; +import org.displaytag.exception.BaseNestableJspTagException; +import org.displaytag.exception.SeverityEnum; +import org.displaytag.model.TableModel; +import org.displaytag.render.ItextTableWriter; + +import com.lowagie.text.Document; +import com.lowagie.text.DocumentException; +import com.lowagie.text.PageSize; +import com.lowagie.text.Table; + +/** + * Exporter using iText: subclasses export to any of the iText document types, + * such as PDF and RTF. + * + * @author Jorge L. Barroso + * @version $Revision: 1.3 $ ($Author: jbarroso $) + */ +public abstract class DefaultItextExportView implements BinaryExportView +{ + /** + * TableModel to render. + */ + private TableModel model; + + /** + * @see org.displaytag.export.ExportView#setParameters(TableModel, boolean, boolean, boolean) + */ + public void setParameters(TableModel tableModel, boolean exportFullList, boolean includeHeader, + boolean decorateValues) + { + this.model = tableModel; + } + + /** + * @see org.displaytag.export.BaseExportView#getMimeType() + * Meant to be overwritten by subclasses. + * @return null + */ + public String getMimeType() + { + return null; + } + + /** + * @see org.displaytag.export.BinaryExportView#doExport(OutputStream) + */ + public void doExport(OutputStream out) throws JspException + { + try + { + Document document = new Document(PageSize.A4.rotate(), 60, 60, 40, 40); + this.initItextWriter(document, out); + document.open(); + Table table = new Table(this.model.getNumberOfColumns()); + ItextTableWriter writer = new ItextTableWriter(table, document); + writer.writeTable(this.model, "-1"); + document.add(table); + document.close(); + } + catch (Exception e) + { + throw new ItextGenerationException(e); + } + } + + /** + * Initializes the iText writer used by export view to write iText document, such + * as PDF or RTF iText writer. + * + * @param document The iText document to be written. + * @param out The output stream to which the document is written. + * @throws DocumentException If something goes wrong during initialization. + */ + protected abstract void initItextWriter(Document document, OutputStream out) + throws DocumentException; + + /** + * Wraps iText-generated exceptions. + * @author Fabrizio Giustina + * @version $Revision: 1.3 $ ($Author: jbarroso $) + */ + static class ItextGenerationException extends BaseNestableJspTagException + { + + /** + * D1597A17A6. + */ + private static final long serialVersionUID = 899149338534L; + + /** + * Instantiate a new PdfGenerationException with a fixed message and the given cause. + * @param cause Previous exception + */ + public ItextGenerationException(Throwable cause) + { + super(DefaultItextExportView.class, Messages.getString("DefaultItextExportView.errorexporting"), cause); //$NON-NLS-1$ + this.initCause(cause); + } + + /** + * @see org.displaytag.exception.BaseNestableJspTagException#getSeverity() + */ + public SeverityEnum getSeverity() + { + return SeverityEnum.ERROR; + } + } +} Index: src/main/java/org/displaytag/export/DefaultPdfExportView.java =================================================================== RCS file: src/main/java/org/displaytag/export/DefaultPdfExportView.java diff -N src/main/java/org/displaytag/export/DefaultPdfExportView.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/main/java/org/displaytag/export/DefaultPdfExportView.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,106 @@ +/** + * Licensed under the Artistic License; you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://displaytag.sourceforge.net/license.html + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.displaytag.export; + +import java.io.IOException; +import java.io.OutputStream; + +import com.lowagie.text.Document; +import com.lowagie.text.DocumentException; +import com.lowagie.text.pdf.BaseFont; +import com.lowagie.text.pdf.PdfContentByte; +import com.lowagie.text.pdf.PdfPTable; +import com.lowagie.text.pdf.PdfPageEventHelper; +import com.lowagie.text.pdf.PdfTemplate; +import com.lowagie.text.pdf.PdfWriter; + +/** + * PDF exporter using iText. + * + * @author Jorge L. Barroso + * @version $Revision: $ ($Author: jbarroso $) + */ +public class DefaultPdfExportView extends DefaultItextExportView +{ + /** + * @see org.displaytag.export.BaseExportView#getMimeType() + * @return "application/pdf" + */ + public String getMimeType() + { + return "application/pdf"; //$NON-NLS-1$ + } + + /** + * Initializes the PDF writer this export view uses to write + * the table document. + * + * @param document The iText document to be written. + * @param out The output stream to which the document is written. + * @throws DocumentException If something goes wrong during initialization. + */ + protected void initItextWriter(Document document, OutputStream out) throws DocumentException + { + PdfWriter.getInstance(document, out).setPageEvent(new PageNumber()); + } + + /** + * Prints a page number at the bottom of each page. + * Based on + * http://itextdocs.lowagie.com/examples/com/lowagie/examples/directcontent/pageevents/PageNumbersWatermark.java + * + * @author Jorge L. Barroso + * @version $Revision: $ ($Author: jbarroso $) + */ + private static class PageNumber extends PdfPageEventHelper + { + /** + * @see com.lowagie.text.pdf.PdfPageEventHelper#onEndPage(com.lowagie.text.pdf.PdfWriter, com.lowagie.text.Document) + */ + public void onEndPage(PdfWriter writer, Document document) + { + /** The headertable. */ + PdfPTable table = new PdfPTable(2); + /** A template that will hold the total number of pages. */ + PdfTemplate tpl = writer.getDirectContent().createTemplate(100, 100); + /** The font that will be used. */ + BaseFont helv = null; + try + { + helv = BaseFont.createFont("Helvetica", BaseFont.WINANSI, false); + } + catch (DocumentException e) + { + } + catch (IOException e) + { + } + PdfContentByte cb = writer.getDirectContent(); + cb.saveState(); + // write the headertable + table.setTotalWidth(document.right() - document.left()); + table.writeSelectedRows(0, -1, document.left(), document.getPageSize().height() - 50, cb); + // compose the footer + String text = "Page " + writer.getPageNumber(); + float textSize = helv.getWidthPoint(text, 12); + float textBase = document.bottom() - 20; + cb.beginText(); + cb.setFontAndSize(helv, 12); + float adjust = helv.getWidthPoint("0", 12); + cb.setTextMatrix(document.right() - textSize - adjust, textBase); + cb.showText(text); + cb.endText(); + cb.addTemplate(tpl, document.right() - adjust, textBase); + cb.saveState(); + } + } +} Index: src/main/java/org/displaytag/export/DefaultRtfExportView.java =================================================================== RCS file: src/main/java/org/displaytag/export/DefaultRtfExportView.java diff -N src/main/java/org/displaytag/export/DefaultRtfExportView.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/main/java/org/displaytag/export/DefaultRtfExportView.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,48 @@ +/** + * Licensed under the Artistic License; you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://displaytag.sourceforge.net/license.html + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +package org.displaytag.export; + +import java.io.OutputStream; + +import com.lowagie.text.Document; +import com.lowagie.text.rtf.RtfWriter2; + +/** + * RTF exporter using iText. + * + * @author Jorge L. Barroso + * @version $Revision: $ ($Author: jbarroso $) + */ +public class DefaultRtfExportView extends DefaultItextExportView +{ + /** + * @see org.displaytag.export.BaseExportView#getMimeType() + * @return "application/rtf" + */ + public String getMimeType() + { + return "application/rtf"; //$NON-NLS-1$ + } + + /** + * Initializes the RTF writer this export view uses to write + * the table document. + * + * @param document The iText document to be written. + * @param out The output stream to which the document is written. + */ + protected void initItextWriter(Document document, OutputStream out) + { + RtfWriter2.getInstance(document, out); + } +} Index: src/main/java/org/displaytag/render/HtmlTableWriter.java =================================================================== RCS file: src/main/java/org/displaytag/render/HtmlTableWriter.java diff -N src/main/java/org/displaytag/render/HtmlTableWriter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/main/java/org/displaytag/render/HtmlTableWriter.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,264 @@ +/** + * Licensed under the Artistic License; you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://displaytag.sourceforge.net/license.html + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.displaytag.render; + +import javax.servlet.jsp.JspWriter; + +import org.displaytag.exception.DecoratorException; +import org.displaytag.exception.ObjectLookupException; +import org.displaytag.model.Column; +import org.displaytag.model.Row; +import org.displaytag.model.TableModel; +import org.displaytag.tags.CaptionTag; +import org.displaytag.tags.TableTag; +import org.displaytag.util.TagConstants; + +/** + * A table writer that formats a table in HTML and writes it to a JSP page. + * + * (Background: this code was factored from TableTag.writeHTMLData) + * + * @author Jorge L. Barroso + * @version $Revision: $ ($Author: jbarroso $) + * @see org.displaytag.render.TableWriterTemplate + */ +public class HtmlTableWriter extends TableWriterAdapter +{ + /** + * TableTag callback instance. + */ + private TableTag tableTag; + + /** + * Output destination. + */ + private JspWriter out; + + /** + * This table writer uses a TableTag and a JspWriter to do its work. + * @param tableTag TableTag instance called back by this writer. + * @param out The output destination. + */ + public HtmlTableWriter(TableTag tableTag, JspWriter out) + { + this.tableTag = tableTag; + this.out = out; + } + + /** + * Writes a banner containing search result and paging navigation above an HTML table to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeTopBanner(org.displaytag.model.TableModel) + */ + protected void writeTopBanner(TableModel model) + { + this.tableTag.writeSearchResultAndNavigation(); + } + + /** + * Writes an HTML table's opening tags to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeTableOpener(org.displaytag.model.TableModel) + */ + protected void writeTableOpener(TableModel model) + { + this.write(this.tableTag.getOpenTag(), this.out); + } + + /** + * Writes an HTML table's caption to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeCaption(org.displaytag.model.TableModel) + */ + protected void writeCaption(TableModel model) + { + CaptionTag captionTag = this.tableTag.getCaptionTag(); + this.write(captionTag.getOpenTag() + + model.getCaption() + + captionTag.getCloseTag(), this.out); + } + + /** + * Writes an HTML table's column header to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeTableHeader(org.displaytag.model.TableModel) + */ + protected void writeTableHeader(TableModel model) + { + // thead + this.tableTag.writeTableHeader(); + } + + /** + * Writes an HTML table's footer to a JSP page; + * HTML requires tfoot to appear before tbody. + * @see org.displaytag.render.TableWriterTemplate#writeFooter(org.displaytag.model.TableModel) + */ + protected void writePreBodyFooter(TableModel model) + { + this.write(TagConstants.TAG_TFOOTER_OPEN, this.out); + this.write(model.getFooter(), this.out); + this.write(TagConstants.TAG_TFOOTER_CLOSE, this.out); + // reset footer + this.tableTag.setFooter(null); + } + + /** + * Writes the start of an HTML table's body to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeTableBodyOpener(org.displaytag.model.TableModel) + */ + protected void writeTableBodyOpener(TableModel model) + { + this.write(TagConstants.TAG_TBODY_OPEN, this.out); + + } + + /** + * Writes the end of an HTML table's body to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeTableBodyCloser(org.displaytag.model.TableModel) + */ + protected void writeTableBodyCloser(TableModel model) + { + this.write(TagConstants.TAG_TBODY_CLOSE, this.out); + } + + /** + * Writes the closing structure of an HTML table to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeTableCloser(org.displaytag.model.TableModel) + */ + protected void writeTableCloser(TableModel model) + { + this.write(this.tableTag.getCloseTag(), this.out); + } + + /** + * Writes a banner containing search result, paging navigation, and export links below an HTML + * table to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeBottomBanner(org.displaytag.model.TableModel) + */ + protected void writeBottomBanner(TableModel model) + { + this.tableTag.writeNavigationAndExportLinks(); + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeDecoratedTableFinish(org.displaytag.model.TableModel) + */ + protected void writeDecoratedTableFinish(TableModel model) + { + model.getTableDecorator().finish(); + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowStart(org.displaytag.model.TableModel) + */ + protected void writeDecoratedRowStart(TableModel model) + { + String stringStartRow = model.getTableDecorator().startRow(); + if (stringStartRow != null) + { + this.write(stringStartRow, this.out); + } + } + + /** + * Writes an HTML table's row-opening tag to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeRowOpener(org.displaytag.model.Row) + */ + protected void writeRowOpener(Row row) + { + this.write(row.getOpenTag(), this.out); + } + + /** + * Writes an HTML table's column-opening tag to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeColumnOpener(org.displaytag.model.Column) + */ + protected void writeColumnOpener(Column column) throws ObjectLookupException, DecoratorException + { + this.write(column.getOpenTag(), this.out); + } + + /** + * Writes an HTML table's column-closing tag to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeColumnCloser(org.displaytag.model.Column) + */ + protected void writeColumnCloser(Column column) + { + this.write(column.getCloseTag(), this.out); + } + + /** + * Writes to a JSP page an HTML table row that has no columns. + * @see org.displaytag.render.TableWriterTemplate#writeRowWithNoColumns(java.lang.String) + */ + protected void writeRowWithNoColumns(String rowValue) + { + this.write(TagConstants.TAG_TD_OPEN, this.out); + this.write(rowValue, this.out); + this.write(TagConstants.TAG_TD_CLOSE, this.out); + } + + /** + * Writes an HTML table's row-closing tag to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeRowCloser(org.displaytag.model.Row) + */ + protected void writeRowCloser(Row row) + { + this.write(row.getCloseTag(), this.out); + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowFinish(org.displaytag.model.TableModel) + */ + protected void writeDecoratedRowFinish(TableModel model) + { + String endRow = model.getTableDecorator().finishRow(); + if (endRow != null) + { + this.write(endRow, this.out); + } + } + + /** + * Writes an HTML message to a JSP page explaining that the table model contains no data. + * @see org.displaytag.render.TableWriterTemplate#writeEmptyListMessage(java.lang.String) + */ + protected void writeEmptyListMessage(String emptyListMessage) + { + this.write(emptyListMessage, out); + } + + /** + * Writes a HTML table column value to a JSP page. + * @see org.displaytag.render.TableWriterTemplate#writeColumnValue(java.lang.String,org.displaytag.model.Column) + */ + protected void writeColumnValue(String value, Column column) + { + this.write(value, out); + } + + /** + * Writes an HTML message to a JSP page explaining that the row contains no data. + * @see org.displaytag.render.TableWriterTemplate#writeEmptyListRowMessage(java.lang.String) + */ + protected void writeEmptyListRowMessage(String message) + { + this.write(message, out); + } + + /** + * Utility method. Write a string to the given JspWriter + * @param string String + * @param out JspWriter + */ + private void write(String string, JspWriter out) + { + this.tableTag.write(string, out); + } +} Index: src/main/java/org/displaytag/render/ItextTableWriter.java =================================================================== RCS file: src/main/java/org/displaytag/render/ItextTableWriter.java diff -N src/main/java/org/displaytag/render/ItextTableWriter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/main/java/org/displaytag/render/ItextTableWriter.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,389 @@ +/** + * Licensed under the Artistic License; you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://displaytag.sourceforge.net/license.html + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.displaytag.render; + +import java.awt.Color; +import java.util.Iterator; + +import org.apache.commons.lang.StringUtils; +import org.displaytag.decorator.TableDecorator; +import org.displaytag.decorator.itext.DecoratesItext; +import org.displaytag.exception.DecoratorException; +import org.displaytag.exception.ObjectLookupException; +import org.displaytag.model.Column; +import org.displaytag.model.HeaderCell; +import org.displaytag.model.TableModel; + +import com.lowagie.text.BadElementException; +import com.lowagie.text.Cell; +import com.lowagie.text.Chunk; +import com.lowagie.text.Document; +import com.lowagie.text.DocumentException; +import com.lowagie.text.Element; +import com.lowagie.text.Font; +import com.lowagie.text.FontFactory; +import com.lowagie.text.Paragraph; +import com.lowagie.text.Table; + +/** + * A table writer that formats table as and writes it to an iText document. + * + * @author Jorge L. Barroso + * @version $Revision: $ ($Author: jbarroso $) + * @see org.displaytag.render.TableWriterTemplate + */ +public class ItextTableWriter extends TableWriterAdapter +{ + /** + * iText representation of the table. + */ + private Table table; + + /** + * iText document to which the table is written. + */ + private Document document; + + /** + * The default font used in the document. + */ + private Font defaultFont; + + /** + * This table writer uses an iText table and document to do its work. + * @param table iText representation of the table. + * @param document iText document to which the table is written. + */ + public ItextTableWriter(Table table, Document document) + { + this.table = table; + this.document = document; + } + + /** + * Initialize the main info holder table, like the appropriate number of columns. + * @param model The table being represented as iText. + * @see org.displaytag.render.TableWriterTemplate#writeTableOpener(org.displaytag.model.TableModel) + */ + protected void writeTableOpener(TableModel model) + { + this.table.setDefaultVerticalAlignment(Element.ALIGN_TOP); + this.table.setCellsFitPage(true); + this.table.setWidth(100); + this.table.setPadding(2); + this.table.setSpacing(0); + this.table.setBorder(Table.NO_BORDER); + this.defaultFont = this.getTableFont(); + } + + /** + * Obtain the font used to render text in the table; + * Meant to be overriden if a different font is desired. + * @return The font used to render text in the table. + */ + protected Font getTableFont() + { + return FontFactory.getFont(FontFactory.HELVETICA, 10, Font.NORMAL, new Color(0x00, 0x00, 0x00)); + } + + /** + * Write the table's caption to a iText document. + * @see org.displaytag.render.TableWriterTemplate#writeCaption(org.displaytag.model.TableModel) + */ + protected void writeCaption(TableModel model) + throws Exception + { + this.decorateCaption(model); + } + + /** + * Writes the table caption according to a set style. + * + * @param model The table model containing the caption. + * @throws DocumentException If an error occurrs while decorating the caption. + */ + private void decorateCaption(TableModel model) throws DocumentException + { + Paragraph caption = new Paragraph(new Chunk(model.getCaption(), this.getCaptionFont())); + caption.setAlignment(this.getCaptionHorizontalAlignment()); + this.document.add(caption); + } + + /** + * Obtain the caption font; + * Meant to be overriden if a different style is desired. + * @return The caption font. + */ + protected Font getCaptionFont() + { + return FontFactory.getFont(FontFactory.HELVETICA, 17, Font.BOLD, new Color(0x00, 0x00, 0x00)); + } + + /** + * Obtain the caption horizontal alignment; + * Meant to be overriden if a different style is desired. + * @return The caption horizontal alignment. + */ + protected int getCaptionHorizontalAlignment() + { + return Paragraph.ALIGN_CENTER; + } + + /** + * Write the table's header columns to an iText document. + * @see org.displaytag.render.TableWriterTemplate#writeTableHeader(org.displaytag.model.TableModel) + * @throws BadElementException if an error occurs while writing header. + */ + protected void writeTableHeader(TableModel model) + throws BadElementException + { + Iterator iterator = model.getHeaderCellList().iterator(); + + float[] widths = new float[model.getNumberOfColumns()]; + for (int i = 0; iterator.hasNext(); i++) + { + HeaderCell headerCell = (HeaderCell) iterator.next(); + widths[i] = this.getCellWidth(headerCell); + + String columnHeader = headerCell.getTitle(); + + if (columnHeader == null) + { + columnHeader = StringUtils.capitalize(headerCell.getBeanPropertyName()); + } + + Cell hdrCell = this.getHeaderCell(columnHeader); + this.table.addCell(hdrCell); + } + this.table.setWidths(widths); + this.table.endHeaders(); + } + + /** + * Returns the maximum size of all values in this column. + * @param headerCell Header cell for this column. + * @return The maximum size of all values in this column. + */ + private float getCellWidth(HeaderCell headerCell) + { + int maxWidth = headerCell.getMaxLength(); + return (maxWidth > 0) ? maxWidth : headerCell.getTitle().length(); + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writePostBodyFooter(org.displaytag.model.TableModel) + * @throws DocumentException if an error occurs while writing post-body footer. + */ + protected void writePostBodyFooter(TableModel model) throws DocumentException + { + Chunk cellContent = new Chunk(model.getFooter(), this.getFooterFont()); + this.setFooterFontStyle(cellContent); + Cell cell = new Cell(cellContent); + cell.setLeading(8); + cell.setBackgroundColor(this.getFooterBackgroundColor()); + cell.setHorizontalAlignment(this.getFooterHorizontalAlignment()); + cell.setColspan(model.getNumberOfColumns()); + table.addCell(cell); + } + + /** + * Obtain the footer background color; + * Meant to be overriden if a different style is desired. + * @return The footer background color. + */ + protected Color getFooterBackgroundColor() + { + return new Color(0xce, 0xcf, 0xce); + } + + /** + * Obtain the footer horizontal alignment; + * Meant to be overriden if a different style is desired. + * @return The footer horizontal alignment. + */ + protected int getFooterHorizontalAlignment() + { + return Element.ALIGN_LEFT; + } + + /** + * Set the font style used to render the header text; + * Meant to be overridden if a different header style is desired. + * @param cellContent The header content whose font will be modified. + */ + protected void setFooterFontStyle(Chunk cellContent) + { + this.setBoldStyle(cellContent, this.getFooterFontColor()); + } + + /** + * Obtain the footer font color; + * Meant to be overriden if a different style is desired. + * @return The footer font color. + */ + protected Color getFooterFontColor() + { + return new Color(0x00, 0x00, 0x00); + } + + /** + * Obtain the footer font; + * Meant to be overriden if a different style is desired. + * @return The footer font. + */ + protected Font getFooterFont() + { + return FontFactory.getFont(FontFactory.HELVETICA, 10); + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowStart(org.displaytag.model.TableModel) + */ + protected void writeDecoratedRowStart(TableModel model) + { + DecoratesItext decorator = (DecoratesItext) model.getTableDecorator(); + decorator.setTable(this.table); + decorator.setFont(this.defaultFont); + ((TableDecorator) decorator).startRow(); + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowFinish(org.displaytag.model.TableModel) + */ + protected void writeDecoratedRowFinish(TableModel model) throws Exception + { + model.getTableDecorator().finishRow(); + } + + /** + * Write a column's opening structure to an iText document. + * @see org.displaytag.render.TableWriterTemplate#writeColumnOpener(org.displaytag.model.Column) + */ + protected void writeColumnOpener(Column column) throws ObjectLookupException, DecoratorException + { + column.getOpenTag(); // has side effect, setting its stringValue, which affects grouping logic. + } + + /** + * Write a column's value to a iText document. + * @see org.displaytag.render.TableWriterTemplate#writeColumnValue(java.lang.String,org.displaytag.model.Column) + */ + protected void writeColumnValue(String value, Column column) throws BadElementException + { + this.table.addCell(getCell(value)); + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeDecoratedTableFinish(org.displaytag.model.TableModel) + */ + protected void writeDecoratedTableFinish(TableModel model) + { + model.getTableDecorator().finish(); + } + + /** + * Returns a formatted cell for the given value. + * + * (Background: factored from PdfView) + * + * @param value cell value + * @return Cell + * @throws BadElementException if errors occurs while generating content. + */ + private Cell getCell(String value) throws BadElementException + { + Cell cell = new Cell(new Chunk(StringUtils.trimToEmpty(value), this.defaultFont)); + cell.setVerticalAlignment(Element.ALIGN_TOP); + cell.setLeading(8); + return cell; + } + + /** + * Obtain a header cell. + * @param value Cell content. + * @return A header cell with the given content. + * @throws BadElementException if errors occurs while generating content. + */ + private Cell getHeaderCell(String value) throws BadElementException + { + Chunk cellContent = new Chunk(value, this.getHeaderFont()); + setHeaderFontStyle(cellContent); + Cell cell = new Cell(cellContent); + cell.setLeading(8); + cell.setHeader(true); + cell.setHorizontalAlignment(this.getHeaderHorizontalAlignment()); + cell.setBackgroundColor(this.getHeaderBackgroundColor()); + return cell; + } + + /** + * Obtain the font used to render the header text; + * Meant to be overridden if a different header font is desired. + * @return The font used to render the header text. + */ + protected Font getHeaderFont() + { + return this.defaultFont; + } + + /** + * Obtain the background color used to render the header; + * Meant to be overridden if a different header background color is desired. + * @return The backgrounc color used to render the header. + */ + protected Color getHeaderBackgroundColor() + { + return new Color(0xee, 0xee, 0xee); + } + + /** + * Set the font style used to render the header text; + * Meant to be overridden if a different header style is desired. + * @param cellContent The header content whose font will be modified. + */ + protected void setHeaderFontStyle(Chunk cellContent) + { + setBoldStyle(cellContent, this.getHeaderFontColor()); + } + + /** + * Set the font color used to render the header text; + * Meant to be overridden if a different header style is desired. + * @return The font color used to render the header text. + */ + protected Color getHeaderFontColor() + { + return new Color(0x00, 0x00, 0x00); + } + + /** + * Obtain the horizontal alignment used to render header text; + * Meant to be overridden if a different alignment is desired. + * @return The horizontal alignment used to render header text; + */ + protected int getHeaderHorizontalAlignment() + { + return Element.ALIGN_CENTER; + } + + /** + * Makes chunk content bold. + * @param chunk The chunk whose content is to be rendered bold. + * @param color The font color desired. + */ + private void setBoldStyle(Chunk chunk, Color color) + { + Font font = chunk.font(); + chunk.setFont(FontFactory.getFont(font.getFamilyname(), + font.size(), Font.BOLD, color)); + } +} Index: src/main/java/org/displaytag/render/TableWriterAdapter.java =================================================================== RCS file: src/main/java/org/displaytag/render/TableWriterAdapter.java diff -N src/main/java/org/displaytag/render/TableWriterAdapter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/main/java/org/displaytag/render/TableWriterAdapter.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,175 @@ +/** + * Licensed under the Artistic License; you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://displaytag.sourceforge.net/license.html + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.displaytag.render; + +import org.displaytag.model.Column; +import org.displaytag.model.Row; +import org.displaytag.model.TableModel; + +/** + * Convenience abstract adapter for constructing a table view; contains only stub implementations. + * This class exists as a convenience for creating table-writer objects. Extend this class to create a + * table writer and override the methods of interest. This class also protects subclasses from future + * additions to TableWriterTemplate they may not be interested in. + * + * @author Jorge L. Barroso + * @version $Revision: $ ($Author: jbarroso $) + */ +public abstract class TableWriterAdapter extends TableWriterTemplate +{ + /** + * @see org.displaytag.render.TableWriterTemplate#writeEmptyListMessage(java.lang.String) + */ + protected void writeEmptyListMessage(String emptyListMessage) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeTopBanner(org.displaytag.model.TableModel) + */ + protected void writeTopBanner(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeTableOpener(org.displaytag.model.TableModel) + */ + protected void writeTableOpener(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeCaption(org.displaytag.model.TableModel) + */ + protected void writeCaption(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeTableHeader(org.displaytag.model.TableModel) + */ + protected void writeTableHeader(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writePreBodyFooter(org.displaytag.model.TableModel) + */ + protected void writePreBodyFooter(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeTableBodyOpener(org.displaytag.model.TableModel) + */ + protected void writeTableBodyOpener(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeTableBodyCloser(org.displaytag.model.TableModel) + */ + protected void writeTableBodyCloser(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writePostBodyFooter(org.displaytag.model.TableModel) + */ + protected void writePostBodyFooter(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeTableCloser(org.displaytag.model.TableModel) + */ + protected void writeTableCloser(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeBottomBanner(org.displaytag.model.TableModel) + */ + protected void writeBottomBanner(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeDecoratedTableFinish(org.displaytag.model.TableModel) + */ + protected void writeDecoratedTableFinish(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowStart(org.displaytag.model.TableModel) + */ + protected void writeDecoratedRowStart(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeRowOpener(org.displaytag.model.Row) + */ + protected void writeRowOpener(Row row) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeColumnOpener(org.displaytag.model.Column) + */ + protected void writeColumnOpener(Column column) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeColumnValue(java.lang.String,org.displaytag.model.Column) + */ + protected void writeColumnValue(String value, Column column) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeColumnCloser(org.displaytag.model.Column) + */ + protected void writeColumnCloser(Column column) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeRowWithNoColumns(java.lang.String) + */ + protected void writeRowWithNoColumns(String string) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeRowCloser(org.displaytag.model.Row) + */ + protected void writeRowCloser(Row row) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowFinish(org.displaytag.model.TableModel) + */ + protected void writeDecoratedRowFinish(TableModel model) throws Exception + { + } + + /** + * @see org.displaytag.render.TableWriterTemplate#writeEmptyListRowMessage(java.lang.String) + */ + protected void writeEmptyListRowMessage(String message) throws Exception + { + } +} Index: src/main/java/org/displaytag/render/TableWriterTemplate.java =================================================================== RCS file: src/main/java/org/displaytag/render/TableWriterTemplate.java diff -N src/main/java/org/displaytag/render/TableWriterTemplate.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/main/java/org/displaytag/render/TableWriterTemplate.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,496 @@ +/** + * Licensed under the Artistic License; you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://displaytag.sourceforge.net/license.html + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.displaytag.render; + +import java.text.MessageFormat; +import java.util.Hashtable; +import java.util.Map; + +import javax.servlet.jsp.JspException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.displaytag.model.Column; +import org.displaytag.model.ColumnIterator; +import org.displaytag.model.Row; +import org.displaytag.model.RowIterator; +import org.displaytag.model.TableModel; +import org.displaytag.properties.TableProperties; +import org.displaytag.util.TagConstants; + +/** + * A template that encapsulates and drives the construction of a table based on a given table model + * and configuration. This class is meant to be extended by classes that build tables sharing + * the same structure, sorting, and grouping, but that write them in different formats and to + * various destinations. Subclasses must provide the format- and destination-specific + * implementations of the abstract methods this class calls to build a table. + * + * (Background: This class came about because our users wanted to export tables to Excel and PDF + * just as they were presented in HTML. It originates with the TableTagData.writeHTMLData method, + * factoring its logic so that it can be re-used by classes that write the tables as PDF, + * Excel, RTF and other formats. TableTagData.writeHTMLData now calls an HTML extension of this class + * to write tables in HTML format to a JSP page.) + * + * @author Jorge L. Barroso + * @version $Revision: $ ($Author: jbarroso $) + */ +public abstract class TableWriterTemplate +{ + /** + * logger. + */ + private static Log log = LogFactory.getLog(TableWriterTemplate.class); + + /** + * Holds the previous row columns values. + */ + private Map previousRow; + + /** + * Holds the next row columns values. + */ + private Map nextRow; + + /** + * Table unique id. + */ + private String id; + + /** + * Given a table model, this method creates a table, sorting and grouping it per its + * configuration, while delegating where and how it writes the table to subclass objects. + * + * (Background: This method refactors TableTagData.writeHTMLData method. See above.) + * + * @param model The table model used to build the table. + * @param id This table's page id. + * @throws JspException if any exception thrown while constructing the tablei, it is caught and rethrown + * as a JspException. Extension classes may throw all sorts of exceptions, depending on their + * respective formats and destinations. + */ + public void writeTable(TableModel model, String id) throws JspException + { + try + { + // table id used for logging + this.id = id; + + TableProperties properties = model.getProperties(); + + if (log.isDebugEnabled()) + { + log.debug("[" + this.id + "] writeTable called for table [" + this.id + "]"); + } + + // Handle empty table + boolean noItems = model.getRowListPage().size() == 0; + if (noItems && !properties.getEmptyListShowTable()) + { + writeEmptyListMessage(properties.getEmptyListMessage()); + return; + } + + // variables to hold the previous row columns values. + this.previousRow = new Hashtable(10); + + // variables to hold next row column values. + this.nextRow = new Hashtable(10); + + // Put the page stuff there if it needs to be there... + if (properties.getAddPagingBannerTop()) + { + // search result and navigation bar + writeTopBanner(model); + } + + // open table + writeTableOpener(model); + + // render caption + if (model.getCaption() != null) + { + writeCaption(model); + } + + // render headers + if (model.getProperties().getShowHeader()) + { + writeTableHeader(model); + } + + // render footer prior to body + if (model.getFooter() != null) + { + writePreBodyFooter(model); + } + + // open table body + writeTableBodyOpener(model); + + // render table body + writeTableBody(model); + + // close table body + writeTableBodyCloser(model); + + // render footer after body + if (model.getFooter() != null) + { + writePostBodyFooter(model); + } + + // close table + writeTableCloser(model); + + if (model.getTableDecorator() != null) + { + writeDecoratedTableFinish(model); + } + + writeBottomBanner(model); + + if (log.isDebugEnabled()) + { + log.debug("[" + this.id + "] writeTable end"); + } + } + catch (Exception e) + { + throw new JspException(e); + } + } + + /* + * writeTable callback methods + */ + + /** + * Called by writeTable to write a message explaining that the table model contains no data. + * @param emptyListMessage A message explaining that the table model contains no data. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeEmptyListMessage(String emptyListMessage) + throws Exception; + + /** + * Called by writeTable to write a summary of the search result this table reports and the + * table's pagination interface. + * @param model The table model for which the banner is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeTopBanner(TableModel model) + throws Exception; + + /** + * Called by writeTable to write the start of the table structure. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeTableOpener(TableModel model) + throws Exception; + + /** + * Called by writeTable to write the table's caption. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeCaption(TableModel model) + throws Exception; + + /** + * Called by writeTable to write the table's header columns. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeTableHeader(TableModel model) + throws Exception; + + /** + * Called by writeTable to write table footer before table body. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writePreBodyFooter(TableModel model) + throws Exception; + + /** + * Called by writeTable to write the start of the table's body. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeTableBodyOpener(TableModel model) + throws Exception; + + //protected abstract void writeTableBody(TableModel model); + + /** + * Called by writeTable to write the end of the table's body. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeTableBodyCloser(TableModel model) + throws Exception; + + /** + * Called by writeTable to write table footer after table body. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writePostBodyFooter(TableModel model) + throws Exception; + + /** + * Called by writeTable to write the end of the table's structure. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeTableCloser(TableModel model) + throws Exception; + + /** + * Called by writeTable to decorate the table. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeDecoratedTableFinish(TableModel model) + throws Exception; + + /** + * Called by writeTable to write the table's footer. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeBottomBanner(TableModel model) + throws Exception; + + /** + * Given a table model, writes the table body content, sorting and grouping it per its + * configuration, while delegating where and how it writes to subclass objects. + * + * (Background: This method refactors TableTagData.writeTableBody method. See above.) + * + * @param model The table model used to build the table body. + * @throws Exception if an error is encountered while writing the table body. + */ + private void writeTableBody(TableModel model) throws Exception + { + // Ok, start bouncing through our list (only the visible part) + RowIterator rowIterator = model.getRowIterator(false); + + // iterator on rows + while (rowIterator.hasNext()) + { + Row row = rowIterator.next(); + if (log.isDebugEnabled()) + { + log.debug("[" + this.id + "] rowIterator.next()=" + row); + } + // decorate row start + if (model.getTableDecorator() != null) + { + writeDecoratedRowStart(model); + } + + // open row + writeRowOpener(row); + + // iterator on columns + if (log.isDebugEnabled()) + { + log.debug("[" + this.id + "] creating ColumnIterator on " + model.getHeaderCellList()); + } + ColumnIterator columnIterator = row.getColumnIterator(model.getHeaderCellList()); + + while (columnIterator.hasNext()) + { + Column column = columnIterator.nextColumn(); + + // open column + writeColumnOpener(column); + // get the value to be displayed for the column + String value = column.getChoppedAndLinkedValue(); + + // check if column is grouped + if (column.getGroup() != -1) + { + value = this.groupColumns(value, column.getGroup()); + } + + // render column value + writeColumnValue(value, column); + // close column + writeColumnCloser(column); + } + + // no columns? + if (model.isEmpty()) + { + if (log.isDebugEnabled()) + { + log.debug("[" + this.id + "] table has no columns"); + } + // render empty row + writeRowWithNoColumns(row.getObject().toString()); + } + + // close row + writeRowCloser(row); + + // decorate row finish + if (model.getTableDecorator() != null) + { + writeDecoratedRowFinish(model); + } + } + + // render empty list message + if (model.getRowListPage().size() == 0) + { + writeEmptyListRowMessage( + MessageFormat.format(model.getProperties().getEmptyListRowMessage(), + new Object[]{new Integer(model.getNumberOfColumns())})); + } + } + + /* + * writeTableBody callback methods + */ + + /** + * Called by writeTableBody to write to decorate the table. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeDecoratedRowStart(TableModel model) + throws Exception; + + /** + * Called by writeTableBody to write the start of the row structure. + * @param row The table row for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeRowOpener(Row row) + throws Exception; + + /** + * Called by writeTableBody to write the start of the column structure. + * @param column The table column for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeColumnOpener(Column column) + throws Exception; + + /** + * Called by writeTableBody to write a column's value. + * @param value The column value. + * @param column The table column for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeColumnValue(String value, Column column) + throws Exception; + + /** + * Called by writeTableBody to write the end of the column structure. + * @param column The table column for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeColumnCloser(Column column) + throws Exception; + + /** + * Called by writeTableBody to write a row that has no columns. + * @param value The row value. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeRowWithNoColumns(String value) + throws Exception; + + /** + * Called by writeTableBody to write the end of the row structure. + * @param row The table row for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeRowCloser(Row row) + throws Exception; + + /** + * Called by writeTableBody to decorate the table. + * @param model The table model for which the content is written. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeDecoratedRowFinish(TableModel model) + throws Exception; + + /** + * Called by writeTableBody to write a message explaining that the row contains no data. + * @param message The message explaining that the row contains no data. + * @throws Exception if it encounters an error while writing. + */ + protected abstract void writeEmptyListRowMessage(String message) + throws Exception; + + /** + * Given a column value and grouping index, this method groups the column and returns the + * appropriate string back to the caller. + * + * (Background: This method refactors TableTagData.writeTableBody method. See above.) + * + * @param value String + * @param group int + * @return String + */ + private String groupColumns(String value, int group) + { + if ((group == 1) && this.nextRow.size() > 0) + { + // we are at the begining of the next row so copy the contents from nextRow to the previousRow. + this.previousRow.clear(); + this.previousRow.putAll(this.nextRow); + this.nextRow.clear(); + } + + if (!this.nextRow.containsKey(new Integer(group))) + { + // Key not found in the nextRow so adding this key now... + // remember all the old values. + this.nextRow.put(new Integer(group), value); + } + + // Start comparing the value we received, along with the grouping index. + // if no matching value is found in the previous row then return the value. + // if a matching value is found then this value should not get printed out + // so return an empty String + if (this.previousRow.containsKey(new Integer(group))) + { + for (int j = 1; j <= group; j++) + { + + if (!((String) this.previousRow.get(new Integer(j))).equals((this.nextRow.get(new Integer(j))))) + { + // no match found so return this value back to the caller. + return value; + } + } + } + + // This is used, for when there is no data in the previous row, + // It gets used only the first time. + if (this.previousRow.size() == 0) + { + return value; + } + + // There is corresponding value in the previous row + // this value doesn't need to be printed, return an empty String + return TagConstants.EMPTY_STRING; + } +} Index: src/main/java/org/displaytag/util/MediaUtil.java =================================================================== RCS file: src/main/java/org/displaytag/util/MediaUtil.java diff -N src/main/java/org/displaytag/util/MediaUtil.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/main/java/org/displaytag/util/MediaUtil.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,135 @@ +/** + * Licensed under the Artistic License; you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://displaytag.sourceforge.net/license.html + * + * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.displaytag.util; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.displaytag.properties.MediaTypeEnum; + +/** + * This class provides services for configuring and determining the list of media types an instance + * of SupportsMedia supports. + * + * (Background: ColumnTag, FooterTag and CaptionTag can be configured to support a set of media types. + * This class factors the logic for setting and determining the media instances these objects + * support) + * + * @author Jorge L. Barroso + * @version $Revision: $ ($Author: jbarroso $) + */ +public final class MediaUtil +{ + /** + * logger. + */ + private static Log log = LogFactory.getLog(MediaUtil.class); + + /** + * + * Defines a type of object that can support a list of media types. + * + * @author Jorge L. Barroso + * @version $Revision: $ ($Author: jbarroso $) + */ + public static interface SupportsMedia + { + /** + * Configure the list of media types this object will support. + * + * @param media The list of media types this object will support. + */ + void setSupportedMedia(List media); + /** + * Obtain the list of media types this object supports. + * + * @return The list of media types this object supports. + */ + List getSupportedMedia(); + } + + /** + * Configures the media supported by an object that implements SupportsMedia. + * + * (Background: factored from ColumnTag) + * + * @param mediaSupporter The SupportsMedia instance being configured to support a + * list of media. + * @param media The media being configured on the given SupportsMedia instance. + */ + public static void setMedia(SupportsMedia mediaSupporter, String media) + { + if (mediaSupporter == null) + { + return; + } + + if (StringUtils.isBlank(media) || media.toLowerCase().indexOf("all") > -1) + { + mediaSupporter.setSupportedMedia(null); + return; + } + List supportedMedia = new ArrayList(); + String[] values = StringUtils.split(media); + for (int i = 0; i < values.length; i++) + { + String value = values[i]; + if (!StringUtils.isBlank(value)) + { + MediaTypeEnum type = MediaTypeEnum.fromName(value.toLowerCase()); + if (type == null) + { + log.warn("Unrecognized value for attribute \"media\" value=\"" + value + "\""); + } + else + { + supportedMedia.add(type); + } + } + } + mediaSupporter.setSupportedMedia(supportedMedia); + } + + /** + * Is this media supporter configured for the media type? + * + * (Background: Factored from ColumnTag) + * + * @param mediaSupporter An object that supports various media. + * @param mediaType The currentMedia type + * @return true if the media supporter should be displayed for this request + */ + public static boolean availableForMedia(SupportsMedia mediaSupporter, MediaTypeEnum mediaType) + { + if (mediaSupporter == null) + { + return false; + } + + List supportedMedia = mediaSupporter.getSupportedMedia(); + + if (supportedMedia == null) + { + return true; + } + + return supportedMedia.contains(mediaType); + } + + /** + * Don't instantiate MediaUtil. + */ + private MediaUtil() { } +}