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 javax.servlet.jsp.tagext.Tag;
024
025import lucee.commons.lang.StringUtil;
026import lucee.runtime.db.SQLCaster;
027import lucee.runtime.db.SQLItemImpl;
028import lucee.runtime.exp.ApplicationException;
029import lucee.runtime.exp.DatabaseException;
030import lucee.runtime.exp.PageException;
031import lucee.runtime.ext.tag.TagImpl;
032import lucee.runtime.op.Caster;
033import lucee.runtime.type.Array;
034import lucee.runtime.type.ArrayImpl;
035import lucee.runtime.type.util.ListUtil;
036
037/**
038* Checks the data type of a query parameter. The cfqueryparam tag is nested within a cfquery tag. 
039*   It is embedded within the query SQL statement. If you specify its optional parameters, cfqueryparam 
040*   also performs data validation.
041*
042*
043*
044**/
045public final class QueryParam extends TagImpl {
046        
047    private SQLItemImpl item=new SQLItemImpl();
048    
049        /** Specifies the character that separates values in the list of parameter values in the value 
050        **      attribute. The default is a comma. If you specify a list of values for the value attribute, you must 
051        **      also specify the list attribute. */
052        private String separator=",";
053        
054        /** Yes or No. Indicates that the parameter value of the value attribute is a list of values, 
055        **      separated by a separator character. The default is No */
056        private boolean list;
057
058
059        /** Maximum length of the parameter. The default value is the length of the string specified in 
060        **      the value attribute. */
061        private double maxlength=-1;
062    
063        @Override
064        public void release()   {
065            separator=",";
066            list=false;
067            maxlength=-1;
068            item=new SQLItemImpl();
069        }
070        
071
072        /** set the value list
073        *  Yes or No. Indicates that the parameter value of the value attribute is a list of values, 
074        *       separated by a separator character. The default is No
075        * @param list value to set
076        **/
077        public void setList(boolean list)       {
078                this.list=list;
079        }
080
081        /** set the value null
082        *  Yes or No. Indicates whether the parameter is passed as a null. If Yes, the tag ignores the 
083        *       value attribute. The default is No.
084        * @param nulls value to set
085        **/
086        public void setNull(boolean nulls)      {
087            item.setNulls(nulls);
088        }
089
090        /** set the value value
091        * @param value value to set
092        **/
093        public void setValue(Object value)      {
094            item.setValue(value);
095        }
096
097        /** set the value maxlength
098        *  Maximum length of the parameter. The default value is the length of the string specified in 
099        *       the value attribute.
100        * @param maxlength value to set
101        **/
102        public void setMaxlength(double maxlength)      {
103            this.maxlength=maxlength;
104        }
105
106        /** set the value separator
107        *  Specifies the character that separates values in the list of parameter values in the value 
108        *       attribute. The default is a comma. If you specify a list of values for the value attribute, you must 
109        *       also specify the list attribute.
110        * @param separator value to set
111        **/
112        public void setSeparator(String separator)      {
113            this.separator=separator;
114        }
115
116        /** set the value scale
117        *  Number of decimal places of the parameter. The default value is zero.
118        * @param scale value to set
119        **/
120        public void setScale(double scale)      {
121            item.setScale((int)scale);
122        }
123
124        /** set the value cfsqltype
125        *  The SQL type that the parameter (any type) will be bound to.
126        * @param type value to set
127         * @throws DatabaseException
128        **/
129        public void setCfsqltype(String type) throws DatabaseException  {
130                item.setType(SQLCaster.toSQLType(type));
131                
132        }
133        public void setSqltype(String type) throws DatabaseException    {
134                item.setType(SQLCaster.toSQLType(type));
135                
136        }
137
138        @Override
139        public int doStartTag() throws PageException    {
140            Tag parent = getParent();
141                while(parent!=null && !(parent instanceof Query)) {
142                        parent=parent.getParent();
143                }
144                if(parent instanceof Query) {
145                    Query query = (Query)parent;
146                    if(!item.isNulls() && !item.isValueSet())
147                        throw new ApplicationException("attribute value from tag queryparam is required if attribute null is false");
148                    if(list) {
149                        String v = Caster.toString(item.getValue());
150                        Array arr=null;
151                        if(StringUtil.isEmpty(v)){
152                                arr=new ArrayImpl();
153                                arr.append("");
154                        }
155                        else arr=ListUtil.listToArrayRemoveEmpty(v,separator);
156                                
157                                int len=arr.size();
158                                StringBuffer sb=new StringBuffer();
159                                for(int i=1;i<=len;i++) {
160                                    query.setParam(item.clone(check(arr.getE(i))));
161                                if(i>1)sb.append(',');
162                                sb.append('?');
163                                }
164                                write(sb.toString());
165                    }
166                    else {
167                        check(item.getValue());
168                        query.setParam(item);
169                        write("?");
170                    } 
171                }
172                else {
173                        throw new ApplicationException("Wrong Context, tag QueryParam must be inside a Query tag");     
174                }
175            return SKIP_BODY;
176        }
177        
178        private Object check(Object value) throws PageException {
179        if(maxlength!=-1) {
180            String str = Caster.toString(value);
181            if(str.length()>maxlength)
182                throw new DatabaseException("value ["+value+"] is to large, defined maxlength is ["+Caster.toString(maxlength)+"] but length of value is ["+str.length()+"]",null,null,null);
183        }
184            return value;
185        }
186        
187        private void write(String str) {
188            try {
189            pageContext.write(str);
190        } 
191            catch (IOException e) {}
192        }
193
194}