package org.displaytag.export; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.displaytag.decorator.TableDecorator; import org.displaytag.model.Column; import org.displaytag.model.HeaderCell; import org.displaytag.model.Row; import org.displaytag.model.TableModel; import org.displaytag.render.TableTotaler; import org.displaytag.render.TableWriterAdapter; import org.displaytag.util.HtmlAttributeMap; import org.displaytag.util.MultipleHtmlAttribute; import org.displaytag.util.TagConstants; import com.itclinical.system.date.SystemDate; /** * Writes the table as an XML file, including any totals and grouping information. Used by the FOP export. * @author rapruitt * Date: Mar 10, 2010 * Time: 4:21:28 PM * @see FopExportView */ /* Sample fragment <table> <header> <header-cell >AntColumn</header-cell> </header> <data> <subgroup grouped-by="0"> <row> <cell grouped="true">Ant</cell> </row> <subtotal * */ public class XmlTotalsWriter extends TableWriterAdapter { Pattern stylePat = Pattern.compile("\\s*?([\\w\\-]+?)\\s*?:\\s*?([\\w\\-]+?)(?:;|$)"); protected StringBuffer xml = new StringBuffer(); Map<Integer,String> currentGroupingValueByGroup = new HashMap<Integer,String>(); Integer groupId; int currentGroupingLevel = 0; int maxWordLength = 15; public static final TableDecorator NOOP = new TableDecorator() { @Override public String displayGroupedValue(String cellValue, short groupingStatus, int columnNumber) { return cellValue; } }; public XmlTotalsWriter(TableModel m) { setModel(m); } public void setModel(TableModel m) { m.setTableDecorator(NOOP); if (m.getTotaler() == null || m.getTotaler() == TableTotaler.NULL) { TableTotaler tt = new TableTotaler(); tt.init(m); m.setTotaler(tt); } } /** * <code>TableModel</code> */ @Override protected void writeTableOpener(TableModel model) throws Exception { String now = SystemDate.getToday(); String caption = model.getCaption(); xml.append( "<?xml version=\"1.0\"?>\n<table>\n<datetime>"+now+"</datetime>\n<caption>"+caption+"</caption>\n"); //$NON-NLS-1$ } @Override protected void writeSubgroupStart(TableModel model) { TableTotaler tt = model.getTotaler(); // for each newly opened subgroup we need to output the opener, in order; // so we need to know somehow which groups are new since we last wrote out openers; how about we track a list of the // already opened groups, and ask the tt for a list of all known groups? for (int i : tt.getOpenedColumns()) { xml.append("<subgroup grouped-by=\"").append(i).append("\">"); } } @Override protected void writeSubgroupStop(TableModel model) { List<Integer> closed = model.getTotaler().getClosedColumns(); if (! closed.isEmpty()) { // write subtotals writeSubtotals(model, closed); } } protected void writeSubtotals(TableModel model, List<Integer> closedColumns) { TableTotaler tt = model.getTotaler(); Collections.reverse(closedColumns); for (int i : closedColumns) { xml.append("<subtotal>\n"); for (HeaderCell cell : model.getHeaderCellList()) { if (cell.isTotaled()) { xml.append("\t<subtotal-cell "); HtmlAttributeMap atts = cell.getHtmlAttributes(); writeAttributes(atts); xml.append('>'); cdata( tt.formatTotal(cell, tt.getTotalForColumn(cell.getColumnNumber(), tt.asGroup(i)))); xml.append("</subtotal-cell>"); } else { xml.append("\t<subtotal-cell/>"); } } xml.append("\n</subtotal>\n"); writeExtraGroupInfo(model, i); xml.append("</subgroup>\n"); } } protected void writeExtraGroupInfo(TableModel model, int groupColumn){ } protected void writeAttributes(HtmlAttributeMap atts) { if (atts != null) { String style = (String)atts.get(TagConstants.ATTRIBUTE_STYLE); if (StringUtils.isNotBlank(style)) { Matcher m = stylePat.matcher(style); while (m.find()) { xml.append(m.group(1)); xml.append("=\""); xml.append(m.group(2)); xml.append("\" "); } } MultipleHtmlAttribute cssClass = (MultipleHtmlAttribute)atts.get(TagConstants.ATTRIBUTE_CLASS); if (cssClass != null && !cssClass.isEmpty()) { xml.append(" class"); xml.append("=\""); xml.append(cssClass.toString()); xml.append("\""); } } } @Override protected void writeColumnOpener(Column column) throws Exception { boolean grouped = column.getHeaderCell().getGroup() >= currentGroupingLevel ; String attr = ""; if (grouped) { attr = " grouped=\"true\" "; } xml.append( "\t<cell " ); xml.append( attr ); HtmlAttributeMap atts = column.getHeaderCell().getHtmlAttributes(); writeAttributes(atts); xml.append( ">" ); } @Override protected void writeColumnCloser(Column column) throws Exception { xml.append( "</cell>\n" ); } @Override protected void writeTableHeader(TableModel model) throws Exception { Iterator<HeaderCell> iterator = model.getHeaderCellList().iterator(); xml.append("<header>\n"); xml.append("\n"); while (iterator.hasNext()) { // get the header cell HeaderCell headerCell = iterator.next(); xml.append("<header-cell>"); cdata(headerCell.getTitle()); xml.append("</header-cell>\n"); } xml.append("</header>\n"); xml.append("<data>"); xml.append("<subgroup grouped-by=\""+TableTotaler.WHOLE_TABLE+"\">"); } // just use the hyphenate support from fop -- http://xmlgraphics.apache.org/fop/1.0/hyphenation.html protected void cdata(Object str) { xml.append("<![CDATA["); String defStr = StringUtils.defaultString(""+str); xml.append(defStr); xml.append("]]>"); } @Override protected void writeDecoratedTableFinish(TableModel model) { model.getTableDecorator().finish(); } /** * @see org.displaytag.render.TableWriterTemplate#writeDecoratedRowStart(org.displaytag.model.TableModel) */ @Override protected void writeDecoratedRowStart(TableModel model) { xml.append(StringUtils.defaultString(model.getTableDecorator().startRow())); } @Override protected void writeTableCloser(TableModel model) throws Exception { xml.append("</table>"); //$NON-NLS-1$ } protected void writeDecoratedRowFinish(TableModel model) { xml.append(StringUtils.defaultString(model.getTableDecorator().finishRow())); } protected void writeRowOpener(Row row) throws Exception { xml.append("\n<row>\n"); //$NON-NLS-1$ } protected void writeColumnValue(Object value, Column column) throws Exception { Object rawValue = column.getValue(true); cdata(rawValue); } protected void writeRowWithNoColumns(String value) throws Exception { xml.append("<row/>\n"); //$NON-NLS-1$ } protected void writeRowCloser(Row row) throws Exception { xml.append("</row>\n"); //$NON-NLS-1$ } protected void writeTableBodyCloser(TableModel model) { xml.append("\n<!-- grand totals -->\n"); writeSubtotals(model, Arrays.asList(TableTotaler.WHOLE_TABLE) ); xml.append("</data>"); //$NON-NLS-1$ } public String getXml() { return xml.toString(); } }