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