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}