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