001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.tag; 020 021import java.io.IOException; 022 023import lucee.commons.lang.StringUtil; 024import lucee.runtime.exp.ExpressionException; 025import lucee.runtime.exp.PageException; 026import lucee.runtime.ext.tag.BodyTagTryCatchFinallyImpl; 027import lucee.runtime.functions.string.CJustify; 028import lucee.runtime.functions.string.LJustify; 029import lucee.runtime.functions.string.RJustify; 030import lucee.runtime.op.Caster; 031 032/** 033* Builds a table in a CFML page. Use the cfcol tag to define table column and row 034* characteristics. The cftable tag renders data either as preformatted text, or, with the HTMLTable 035* attribute, as an HTML table. Use cftable to create tables if you don't want to write HTML table tag 036* code, or if your data can be well presented as preformatted text. 037* 038* 039* 040**/ 041public final class Table extends BodyTagTryCatchFinallyImpl { 042 043 /** 044 * Field <code>ALIGN_LEFT</code> 045 */ 046 public static final short ALIGN_LEFT=0; 047 /** 048 * Field <code>ALIGN_CENTER</code> 049 */ 050 public static final short ALIGN_CENTER=1; 051 /** 052 * Field <code>ALIGN_RIGHT</code> 053 */ 054 public static final short ALIGN_RIGHT=2; 055 056 /** Name of the cfquery from which to draw data. */ 057 private lucee.runtime.type.Query query; 058 059 /** Maximum number of rows to display in the table. */ 060 private int maxrows=Integer.MAX_VALUE; 061 062 /** Specifies the query row from which to start processing. */ 063 private int startrow=1; 064 065 /** Adds a border to the table. Use only when you specify the HTMLTable attribute for the table. */ 066 private boolean border; 067 068 /** Displays headers for each column, as specified in the cfcol tag. */ 069 private boolean colheaders; 070 071 /** Number of spaces to insert between columns 'default is 2'. */ 072 private int colspacing=2; 073 074 /** Renders the table as an HTML 3.0 table. */ 075 private boolean htmltable; 076 077 /** Number of lines to use for the table header. The default is 2, which leaves one line between 078 ** the headers and the first row of the table. */ 079 private int headerlines=2; 080 081 StringBuffer header=new StringBuffer(); 082 StringBuffer body=new StringBuffer(); 083 084 private int initRow; 085 086 private int count=0; 087 private boolean startNewRow; 088 089 @Override 090 public void release() { 091 super.release(); 092 query=null; 093 maxrows=Integer.MAX_VALUE; 094 startrow=1; 095 border=false; 096 colheaders=false; 097 colspacing=2; 098 htmltable=false; 099 headerlines=2; 100 if(header.length()>0)header=new StringBuffer(); 101 body=new StringBuffer(); 102 count=0; 103 } 104 105 /** set the value query 106 * Name of the cfquery from which to draw data. 107 * @param query value to set 108 * @throws PageException 109 **/ 110 public void setQuery(String query) throws PageException { 111 this.query = Caster.toQuery(pageContext.getVariable(query)); 112 } 113 114 /** set the value maxrows 115 * Maximum number of rows to display in the table. 116 * @param maxrows value to set 117 **/ 118 public void setMaxrows(double maxrows) { 119 this.maxrows=(int)maxrows; 120 } 121 122 /** set the value startrow 123 * Specifies the query row from which to start processing. 124 * @param startrow value to set 125 **/ 126 public void setStartrow(double startrow) { 127 this.startrow=(int)startrow; 128 if(this.startrow<=0)this.startrow=1; 129 } 130 131 /** set the value border 132 * Adds a border to the table. Use only when you specify the HTMLTable attribute for the table. 133 * @param border value to set 134 **/ 135 public void setBorder(boolean border) { 136 this.border=border; 137 } 138 139 /** set the value colheaders 140 * Displays headers for each column, as specified in the cfcol tag. 141 * @param colheaders value to set 142 **/ 143 public void setColheaders(boolean colheaders) { 144 this.colheaders=colheaders; 145 } 146 147 /** set the value colspacing 148 * Number of spaces to insert between columns 'default is 2'. 149 * @param colspacing value to set 150 **/ 151 public void setColspacing(double colspacing) { 152 this.colspacing=(int)colspacing; 153 } 154 155 /** set the value htmltable 156 * Renders the table as an HTML 3.0 table. 157 * @param htmltable value to set 158 **/ 159 public void setHtmltable(boolean htmltable) { 160 this.htmltable=htmltable; 161 } 162 163 /** set the value headerlines 164 * Number of lines to use for the table header. The default is 2, which leaves one line between 165 * the headers and the first row of the table. 166 * @param headerlines value to set 167 **/ 168 public void setHeaderlines(double headerlines) { 169 this.headerlines=(int)headerlines; 170 if(this.headerlines<2)this.headerlines=2; 171 } 172 173 174 @Override 175 public int doStartTag() throws PageException { 176 startNewRow=true; 177 initRow=query.getRecordcount(); 178 query.go(startrow,pageContext.getId()); 179 pageContext.undefinedScope().addQuery(query); 180 return query.getRecordcount()>=startrow?EVAL_BODY_INCLUDE:SKIP_BODY; 181 } 182 183 @Override 184 public void doInitBody() { 185 //if(htmltable) body.append("<tr>\n"); 186 } 187 188 @Override 189 public int doAfterBody() throws PageException { 190 if(htmltable) body.append("</tr>\n"); 191 else body.append('\n'); 192 startNewRow=true; 193 //print.out(query.getCurrentrow()+"-"+query.getRecordcount()); 194 return ++count<maxrows && query.next()?EVAL_BODY_AGAIN:SKIP_BODY; 195 } 196 197 @Override 198 public int doEndTag() throws PageException { 199 try { 200 _doEndTag(); 201 } catch (IOException e) { 202 throw Caster.toPageException(e); 203 } 204 return EVAL_PAGE; 205 } 206 private void _doEndTag() throws IOException { 207 if(htmltable) { 208 pageContext.forceWrite("<table colspacing=\""+colspacing+"\""); 209 if(border) { 210 pageContext.forceWrite(" border=\"1\""); 211 } 212 pageContext.forceWrite(">\n"); 213 if(header.length()>0) { 214 pageContext.forceWrite("<tr>\n"); 215 pageContext.forceWrite(header.toString()); 216 pageContext.forceWrite("</tr>\n"); 217 } 218 pageContext.forceWrite(body.toString()); 219 pageContext.forceWrite("</table>"); 220 } 221 else { 222 pageContext.forceWrite("<pre>"); 223 if(header.length()>0) { 224 pageContext.forceWrite(header.toString()); 225 pageContext.forceWrite(StringUtil.repeatString("\n",headerlines-1)); 226 } 227 pageContext.forceWrite(body.toString()); 228 pageContext.forceWrite("</pre>"); 229 } 230 } 231 232 @Override 233 public void doFinally() { 234 try { 235 pageContext.undefinedScope().removeQuery(); 236 if(query!=null)query.go(initRow,pageContext.getId()); 237 } 238 catch (PageException e) { } 239 } 240 241 /** 242 * @param strHeader 243 * @param text 244 * @param align 245 * @param width 246 * @throws ExpressionException 247 */ 248 public void setCol(String strHeader, String text, short align, int width) throws ExpressionException { 249 // HTML 250 if(htmltable) { 251 if(colheaders && count==0 && strHeader.trim().length()>0) { 252 header.append("\t<th"); 253 addAlign(header,align); 254 addWidth(header,width); 255 header.append(">"); 256 header.append(strHeader); 257 header.append("</th>\n"); 258 } 259 if(htmltable && startNewRow) { 260 body.append("<tr>\n"); 261 startNewRow=false; 262 } 263 264 body.append("\t<td"); 265 addAlign(body,align); 266 addWidth(body,width); 267 body.append(">"); 268 body.append(text); 269 body.append("</td>\n"); 270 } 271 // PRE 272 else { 273 if(width<0) width=20; 274 if(colheaders && count==0 && strHeader.trim().length()>0) { 275 addPre(header,align,strHeader,width); 276 } 277 addPre(body,align,text,width); 278 279 } 280 } 281 282 private void addAlign(StringBuffer data,short align) { 283 data.append(" align=\""); 284 data.append(toStringAlign(align)); 285 data.append("\""); 286 } 287 288 private void addWidth(StringBuffer data,int width) { 289 if(width>=-1) { 290 data.append(" width=\""); 291 data.append(width); 292 data.append("%\""); 293 } 294 } 295 296 private void addPre(StringBuffer data,short align,String value, int length) throws ExpressionException { 297 if(align==ALIGN_RIGHT) data.append(RJustify.call(pageContext,value,length)); 298 else if(align==ALIGN_CENTER) data.append(CJustify.call(pageContext,value,length)); 299 else data.append(LJustify.call(pageContext,value,length)); 300 301 } 302 303 private String toStringAlign(short align) { 304 if(align==ALIGN_RIGHT) return "right"; 305 if(align==ALIGN_CENTER) return "center"; 306 return "left"; 307 } 308}