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;
022import java.util.Iterator;
023import java.util.Map.Entry;
024
025import javax.servlet.jsp.tagext.Tag;
026
027import lucee.commons.lang.HTMLEntities;
028import lucee.runtime.exp.ApplicationException;
029import lucee.runtime.exp.ExpressionException;
030import lucee.runtime.exp.PageException;
031import lucee.runtime.ext.tag.BodyTagImpl;
032import lucee.runtime.op.Caster;
033import lucee.runtime.op.Operator;
034import lucee.runtime.type.Collection.Key;
035import lucee.runtime.type.Struct;
036import lucee.runtime.type.StructImpl;
037import lucee.runtime.type.util.KeyConstants;
038import lucee.runtime.type.util.ListUtil;
039
040/**
041 * 
042 */
043public final class Select extends BodyTagImpl {
044    
045    private static final int QUERY_POSITION_ABOVE = 0;
046        private static final int QUERY_POSITION_BELOW = 1;
047        private lucee.runtime.type.Query query;
048    private String[] selected;
049    private String value;
050    private String display;
051    private String passthrough;
052    
053    
054    
055    private Struct attributes=new StructImpl();
056    private InputBean input=new InputBean();
057        private boolean editable=false;
058        private int height=-1;
059        private int width=-1;
060        private String label;
061        private boolean visible=true;
062        private String tooltip;
063        private String group;
064        private int queryPosition=QUERY_POSITION_ABOVE;
065        private boolean caseSensitive=false;
066    
067    
068    @Override
069    public void release() {
070        super.release();
071        query=null;
072        selected=null;
073        value=null;
074        display=null;
075        passthrough=null;
076        editable=false;
077        height=-1;
078        width=-1;
079        label=null;
080        visible=true;
081        tooltip=null;
082        group=null;
083        queryPosition=QUERY_POSITION_ABOVE;
084        caseSensitive=false;
085        attributes.clear();
086        input=new InputBean();
087    }
088    
089    /**
090     * @param cssclass The cssclass to set.
091     */
092    public void setClass(String cssclass) {
093        attributes.setEL("class",cssclass);
094    }
095    /**
096     * @param cssstyle The cssstyle to set.
097     */
098    public void setStyle(String cssstyle) {
099        attributes.setEL("style",cssstyle);
100    }
101    /**
102     * @param id The id to set.
103     */
104    public void setId(String id) {
105        attributes.setEL("id",id);
106    }
107    /**
108     * @param multiple The multiple to set.
109     */
110    public void setMultiple(String multiple) {
111        // alles ausser false ist true
112        if(Caster.toBooleanValue(multiple,true)) attributes.setEL("multiple","multiple");
113    }
114    /**
115     * @param name The name to set.
116     */
117    public void setName(String name) {
118        attributes.setEL(KeyConstants._name,name);
119        input.setName(name);
120    }
121    /**
122     * @param size The size to set.
123     */
124    public void setSize(double size) {
125        attributes.setEL(KeyConstants._size,Caster.toString(size));
126    }
127    /**
128     * @param tabindex The tabindex to set.
129     */
130    public void setTabindex(String tabindex) {
131        attributes.setEL("class",tabindex);
132    }
133    /**
134     * @param title The title to set.
135     */
136    public void setTitle(String title) {
137        attributes.setEL(KeyConstants._title,title);
138    }
139    /**
140     * @param title The title to set.
141     */
142    public void setDir(String dir) {
143        attributes.setEL("dir",dir);
144    }
145    /**
146     * @param title The title to set.
147     */
148    public void setLang(String lang) {
149        attributes.setEL("lang",lang);
150    }
151    /**
152     * @param onblur The onblur to set.
153     */
154    public void setOnblur(String onblur) {
155        attributes.setEL("onblur",onblur);
156    }
157    /**
158     * @param onchange The onchange to set.
159     */
160    public void setOnchange(String onchange) {
161        attributes.setEL("onchange",onchange);
162    }
163    /**
164     * @param onclick The onclick to set.
165     */
166    public void setOnclick(String onclick) {
167        attributes.setEL("onclick",onclick);
168    }
169    /**
170     * @param ondblclick The ondblclick to set.
171     */
172    public void setOndblclick(String ondblclick) {
173        attributes.setEL("ondblclick",ondblclick);
174    }
175    /**
176     * @param onmousedown The onmousedown to set.
177     */
178    public void setOnmousedown(String onmousedown) {
179        attributes.setEL("onmousedown",onmousedown);
180    }
181    /**
182     * @param ondblclick The ondblclick to set.
183     */
184    public void setOnmouseup(String onmouseup) {
185        attributes.setEL("onmouseup",onmouseup);
186    }
187    /**
188     * @param ondblclick The ondblclick to set.
189     */
190    public void setOnmouseover(String onmouseover) {
191        attributes.setEL("onmouseover",onmouseover);
192    }
193    /**
194     * @param ondblclick The ondblclick to set.
195     */
196    public void setOnmousemove(String onmousemove) {
197        attributes.setEL("onmousemove",onmousemove);
198    }
199    /**
200     * @param ondblclick The ondblclick to set.
201     */
202    public void setOnmouseout(String onmouseout) {
203        attributes.setEL("onmouseout",onmouseout);
204    }
205    /**
206     * @param ondblclick The ondblclick to set.
207     */
208    public void setOnkeypress(String onkeypress) {
209        attributes.setEL("onkeypress",onkeypress);
210    }
211    /**
212     * @param ondblclick The ondblclick to set.
213     */
214    public void setOnkeydown(String onkeydown) {
215        attributes.setEL("onkeydown",onkeydown);
216    }
217    /**
218     * @param ondblclick The ondblclick to set.
219     */
220    public void setOnkeyup(String onkeyup) {
221        attributes.setEL("onkeyup",onkeyup);
222    }
223    /**
224     * @param onfocus The onfocus to set.
225     */
226    public void setOnfocus(String onfocus) {
227        attributes.setEL("onfocus",onfocus);
228    }
229
230    /**
231     * @param message The message to set.
232     */
233    public void setMessage(String message) {
234        input.setMessage(message);
235    }
236    /**
237     * @param onerror The onerror to set.
238     */
239    public void setOnerror(String onerror) {
240        input.setOnError(onerror);
241    }
242    /**
243     * @param required The required to set.
244     */
245    public void setRequired(boolean required) {
246        input.setRequired(required);
247    }
248    /**
249     * @param passthrough The passthrough to set.
250     * @throws PageException
251     */
252    public void setPassthrough(Object passthrough) throws PageException {
253        if(passthrough instanceof Struct) {
254            Struct sct = (Struct) passthrough;
255            Iterator<Entry<Key, Object>> it = sct.entryIterator();
256            Entry<Key, Object> e;
257            while(it.hasNext()) {
258                e = it.next();
259                attributes.setEL(e.getKey(),e.getValue());
260            }
261        }
262        else this.passthrough = Caster.toString(passthrough);
263    }
264    /**
265     * @param query The query to set.
266     * @throws PageException 
267     */
268    public void setQuery(String strQuery) throws PageException {
269        this.query = Caster.toQuery(pageContext.getVariable(strQuery));
270    }
271    /**
272     * @param display The display to set.
273     */
274    public void setDisplay(String display) {
275        this.display = display;
276    }
277    
278    public void setDataformatas(String dataformatas) throws ApplicationException {
279        dataformatas=dataformatas.trim();
280        String lcDataformatas=dataformatas.toLowerCase();
281        if( "plaintext".equals(lcDataformatas) || 
282                "html".equals(lcDataformatas)) {
283                attributes.setEL("dataformatas",dataformatas);
284        }
285        else 
286                throw new ApplicationException("attribute dataformatas for tag input has an invalid value ["+dataformatas+"], valid values are [plaintext, html");
287    }
288    
289    public void setDatafld(String datafld) {
290        attributes.setEL("datafld",datafld);
291    }
292    
293    public void setDatasrc(String datasrc) {
294        attributes.setEL("datasrc",datasrc);
295    }
296
297    public void setDisabled(String disabled) {
298        // alles ausser false ist true
299        if(Caster.toBooleanValue(disabled,true)) setDisabled(true);
300    }
301
302    private void setDisabled(boolean disabled) {
303        if(disabled) attributes.setEL("disabled","disabled");
304    }
305    
306    /**
307     * @param selected The selected to set.
308     */
309    public void setSelected(String selected) {
310        this.selected = ListUtil.trimItems(ListUtil.listToStringArray(selected,','));
311    }
312    /**
313     * @param value The value to set.
314     */
315    public void setValue(String value) {
316        this.value = value;
317    }
318    
319    @Override
320    public int doStartTag()     {
321                return EVAL_BODY_BUFFERED;
322        }
323    
324    @Override
325    public int doEndTag() throws PageException {
326        try {
327            _doEndTag();
328            return EVAL_PAGE;
329        } catch (IOException e) {
330            throw Caster.toPageException(e);
331        }
332    }
333    
334    
335    private void _doEndTag() throws IOException, ExpressionException, PageException {
336        
337        // check 
338        if(query!=null) {
339                if(value==null)
340                        throw new ApplicationException("if you have defined attribute query for tag select, you must also define attribute value");
341                else if(!query.containsKey(value))
342                        throw new ApplicationException("invalid value for attribute [value], there is no column in query with name ["+value+"]");
343                
344                if(display!=null && !query.containsKey(display))
345                        throw new ApplicationException("invalid value for attribute [display], there is no column in query with name ["+display+"]");
346                
347                if(group!=null && !query.containsKey(group))
348                        throw new ApplicationException("invalid value for attribute [group], there is no column in query with name ["+group+"]");
349        }    
350        
351        input.setType(Input.TYPE_SELECT);
352        Tag parent=getParent();
353        while(parent!=null && !(parent instanceof Form)){
354                        parent=parent.getParent();
355                }
356                if(parent instanceof Form) {
357                    Form form = (Form)parent;
358                    form.setInput(input);       
359                }
360                else { 
361                    throw new ApplicationException("Tag cfselect must be inside a cfform tag");
362                }
363        
364        
365        pageContext.forceWrite("<select");
366        
367        Iterator<Entry<Key, Object>> it = attributes.entryIterator();
368        Entry<Key, Object> e;
369        while(it.hasNext()) {
370                e = it.next();
371            pageContext.forceWrite(" ");
372            pageContext.forceWrite(e.getKey().getString());
373            pageContext.forceWrite("=\"");
374            pageContext.forceWrite(enc(Caster.toString(e.getValue())));
375            pageContext.forceWrite("\"");
376        }
377        
378        if(passthrough!=null) {
379            pageContext.forceWrite(" ");
380            pageContext.forceWrite(passthrough);
381        }
382        pageContext.forceWrite(">\n");
383        
384
385        if(bodyContent!=null && queryPosition==QUERY_POSITION_BELOW)pageContext.forceWrite(bodyContent.getString());
386        
387        // write query options
388        if(query!=null) {
389            int rowCount=query.getRowCount();
390            String v,d,currentGroup=null,tmp;
391            boolean hasDisplay=display!=null;
392            boolean hasGroup=group!=null;
393            
394            for(int i=1;i<=rowCount;i++) {
395                v = Caster.toString(query.getAt(value,i));
396                d = hasDisplay?Caster.toString(query.getAt(display,i)):v;
397                if(hasGroup) {
398                        tmp=Caster.toString(query.getAt(group,i));
399                        if(currentGroup==null || !Operator.equals(currentGroup,tmp,true)) {
400                                if(currentGroup!=null)pageContext.forceWrite("</optgroup>\n");
401                                pageContext.forceWrite("<optgroup label=\""+tmp+"\">\n");
402                                currentGroup=tmp;
403                        }
404                }
405                pageContext.forceWrite("<option"+selected(v,selected)+" value=\""+v+"\">"+d+"</option>\n");
406            }
407                if(hasGroup)pageContext.forceWrite("</optgroup>\n");
408        }
409        
410        if(bodyContent!=null && queryPosition==QUERY_POSITION_ABOVE)pageContext.forceWrite(bodyContent.getString());
411        pageContext.forceWrite("</select>");
412        
413        
414        
415        
416    }
417    
418    private String selected(String str, String[] selected) {
419        if(selected!=null) {
420                for(int i=0;i<selected.length;i++) {
421                        if(caseSensitive) {
422                                if(str.compareTo(selected[i])==0) return " selected";
423                        }
424                        else {
425                                if(Operator.compare(str,selected[i])==0) return " selected";
426                        }
427                        //if(Operator.compare(str,selected[i])==0) return " selected";
428                }
429        }
430        return "";
431    }
432
433    
434    
435    /**
436     * html encode a string
437     * @param str string to encode
438     * @return encoded string
439     */
440    private String enc(String str) {
441        return HTMLEntities.escapeHTML(str,HTMLEntities.HTMLV20);
442    }
443
444        /**
445         * @param editable the editable to set
446         * @throws ApplicationException 
447         */
448        public void setEditable(boolean editable)  {
449                this.editable=editable;
450        }
451
452        /**
453         * @param group the group to set
454         * @throws ApplicationException 
455         */
456        public void setGroup(String group) {
457                this.group=group;
458        }
459
460        /**
461         * @param height the height to set
462         * @throws ApplicationException 
463         */
464        public void setHeight(double height) {
465                this.height=(int)height;
466        }
467
468        /**
469         * @param label the label to set
470         * @throws ApplicationException 
471         */
472        public void setLabel(String label) {
473                this.label=label;
474        }
475
476        /**
477         * @param queryPosition the queryPosition to set
478         * @throws ApplicationException 
479         */
480        public void setQueryposition(String strQueryPosition) throws ApplicationException {
481                strQueryPosition=strQueryPosition.trim().toLowerCase();
482                if( "above".equals(strQueryPosition)) queryPosition=QUERY_POSITION_ABOVE;
483                else if( "below".equals(strQueryPosition)) queryPosition=QUERY_POSITION_BELOW;
484        else 
485                throw new ApplicationException("attribute queryPosition for tag select has an invalid value ["+strQueryPosition+"], " +
486                                "valid values are [above, below]");
487    
488        }
489
490        /**
491         * @param tooltip the tooltip to set
492         * @throws ApplicationException 
493         */
494        public void setTooltip(String tooltip) {
495                this.tooltip=tooltip;
496        }
497
498        /**
499         * @param visible the visible to set
500         * @throws ApplicationException 
501         */
502        public void setVisible(boolean visible) {
503                this.visible=visible;
504        }
505
506        /**
507         * @param width the width to set
508         * @throws ApplicationException 
509         */
510        public void setWidth(double width) {
511                this.width=(int) width;
512        }
513        /**
514         * @param width the width to set
515         * @throws ApplicationException 
516         */
517        public void setEnabled(String enabled) {
518                setDisabled(!Caster.toBooleanValue(enabled,true));
519        }
520
521        /**
522         * @param caseSensitive the caseSensitive to set
523         */
524        public void setCasesensitive(boolean caseSensitive) {
525                this.caseSensitive = caseSensitive;
526        }
527}