001: /*
002: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003: *
004: * "The contents of this file are subject to the Mozilla Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
011: * License for the specific language governing rights and limitations under
012: * the License.
013: *
014: * The Original Code is ICEfaces 1.5 open source software code, released
015: * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
016: * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
017: * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
018: *
019: * Contributor(s): _____________________.
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
023: * License), in which case the provisions of the LGPL License are
024: * applicable instead of those above. If you wish to allow use of your
025: * version of this file only under the terms of the LGPL License and not to
026: * allow others to use your version of this file under the MPL, indicate
027: * your decision by deleting the provisions above and replace them with
028: * the notice and other provisions required by the LGPL License. If you do
029: * not delete the provisions above, a recipient may use your version of
030: * this file under either the MPL or the LGPL License."
031: *
032: */
033:
034: package com.icesoft.faces.component.selectinputtext;
035:
036: import com.icesoft.faces.component.CSS_DEFAULT;
037: import com.icesoft.faces.component.ext.HtmlInputText;
038: import com.icesoft.faces.component.ext.KeyEvent;
039: import com.icesoft.faces.component.ext.renderkit.FormRenderer;
040: import com.icesoft.faces.component.ext.taglib.Util;
041: import com.icesoft.faces.context.effects.JavascriptContext;
042:
043: import javax.faces.component.NamingContainer;
044: import javax.faces.component.UIComponent;
045: import javax.faces.context.FacesContext;
046: import javax.faces.el.ValueBinding;
047: import javax.faces.event.AbortProcessingException;
048: import javax.faces.event.ActionEvent;
049: import javax.faces.event.FacesEvent;
050: import javax.faces.event.ValueChangeEvent;
051: import javax.faces.model.SelectItem;
052: import java.io.IOException;
053: import java.util.*;
054: import java.text.DateFormat;
055:
056: /**
057: * SelectInputText is a JSF component class that represents an ICEfaces
058: * autocomplete input text. This component requires the application developer to
059: * implement the matching list rearch algorithm in their backing bean.
060: * <p/>
061: * SelectInputText extends the ICEfaces extended HtmlInputText component.
062: * <p/>
063: * By default this component is rendered by the "com.icesoft.faces.SelectInputText"
064: * renderer type.
065: */
066: public class SelectInputText extends HtmlInputText implements
067: NamingContainer {
068: public static final String COMPONENT_TYPE = "com.icesoft.faces.SelectInputText";
069:
070: //default style classes
071:
072: //private properties for style classes
073: private String styleClass;
074:
075: /**
076: * A request-scope attribute under which the model data for the row selected
077: * by the current value of the "rowIndex" property will be exposed.
078: */
079: private String listVar = null;
080:
081: /**
082: * A state variable for number of rows to be matched.
083: */
084: private Integer rows;
085: private final int DEFAULT_MAX_MATCHS = 10;
086:
087: /**
088: * A state variable for width of both inputtext and dropdownlist
089: */
090: private String width;
091: private final String DEFAULT_WIDTH = "150";
092:
093: /**
094: * A property to store the selectedItem, after successfull match
095: */
096: private SelectItem selectedItem = null;
097:
098: /**
099: * list of selectItems
100: */
101: List itemList;
102:
103: private String options;
104:
105: /**
106: * Map for selectItems, where the key is the "SelectItem.getlabel()" and the
107: * value is the selectItem object
108: */
109: Map itemMap = new HashMap();
110:
111: private int index = -1;
112:
113: public SelectInputText() {
114: super ();
115: JavascriptContext.includeLib(JavascriptContext.ICE_EXTRAS,
116: FacesContext.getCurrentInstance());
117: }
118:
119: /*
120: * (non-Javadoc)
121: * @see javax.faces.component.UIComponent#encodeBegin(javax.faces.context.FacesContext)
122: */
123: public void encodeBegin(FacesContext context) throws IOException {
124: super .encodeBegin(context);
125: }
126:
127: /*
128: * (non-Javadoc)
129: * @see javax.faces.component.UIComponent#decode(javax.faces.context.FacesContext)
130: */
131: public void decode(FacesContext facesContext) {
132: super .decode(facesContext);
133: setSelectedItem(facesContext);
134: if (Util.isEventSource(facesContext, this )) {
135: queueEventIfEnterKeyPressed(facesContext);
136: // queueEvent(new ActionEvent(this));
137: }
138:
139: }
140:
141: /**
142: * this method is responsible to setting the seletecdItem on the component
143: *
144: * @param facesContext
145: */
146: private void setSelectedItem(FacesContext facesContext) {
147: Map requestMap = facesContext.getExternalContext()
148: .getRequestParameterMap();
149: String clientId = getClientId(facesContext);
150: String value = (String) requestMap.get(clientId);
151: setSelectedItem(value);
152: }
153:
154: /**
155: * return true if I had focus when submitted
156: *
157: * @param facesContext
158: * @return focus
159: */
160: private boolean hadFocus(FacesContext facesContext) {
161: Object focusId = facesContext.getExternalContext()
162: .getRequestParameterMap().get(
163: FormRenderer.getFocusElementId());
164: boolean focus = false;
165: if (focusId != null) {
166: if (focusId.toString().equals(getClientId(facesContext))) {
167: focus = true;
168: }
169: }
170: setFocus(focus);
171: return focus;
172: }
173:
174: //this list would be set in populateItemList()
175:
176: /**
177: * <p>Return the value of the <code>itemList</code> property.</p>
178: */
179: public Iterator getItemList() {
180: if (itemList == null) {
181: return Collections.EMPTY_LIST.iterator();
182: }
183: return itemList.iterator();
184: }
185:
186: /**
187: * <p>Set the value of the <code>index</code> property.</p>
188: */
189: public void setIndex(int index) {
190: this .index = index;
191:
192: }
193:
194: /**
195: * <p>Return the value of the <code>clientId</code> property.</p>
196: */
197: public String getClientId(FacesContext context) {
198: if (context == null) {
199: throw new NullPointerException();
200: }
201: String baseClientId = super .getClientId(context);
202: if (index >= 0) {
203: return (baseClientId + NamingContainer.SEPARATOR_CHAR + index++);
204: } else {
205: return (baseClientId);
206: }
207: }
208:
209: /**
210: * reset parent's and its children's ids
211: */
212: void resetId(UIComponent component) {
213: String id = component.getId();
214: component.setId(id); // Forces client id to be reset
215: Iterator kids = component.getChildren().iterator();
216: while (kids.hasNext()) {
217: UIComponent kid = (UIComponent) kids.next();
218: resetId(kid);
219: }
220:
221: }
222:
223: //this method generating the list of selectItems "itemList", which can be bounded
224: //with the bean, or could be static on jsf page
225: //matches list can be change after value change event, so we are calling
226: //this method after value change event in broadcast(), where the method bounded
227: //with valueChangeListner is being called and updates the data model.
228: void populateItemList() {
229: if (getSelectFacet() != null) {
230: //facet being used on jsf page, so get the list from the value binding
231: itemList = getListValue();
232: } else {
233: //selectItem or selectItems has been used on jsf page, so get the selectItems
234: itemList = Util.getSelectItems(FacesContext
235: .getCurrentInstance(), this );
236: }
237: try {
238: Iterator items = itemList.iterator();
239: SelectItem item = null;
240: itemMap.clear();
241: while (items.hasNext()) {
242: item = (SelectItem) items.next();
243: itemMap.put(item.getLabel(), item);
244: }
245: } catch (NullPointerException e) {
246: e.printStackTrace();
247: }
248: }
249:
250: /* (non-Javadoc)
251: * @see javax.faces.component.UIComponent#broadcast(javax.faces.event.FacesEvent)
252: */
253: public void broadcast(FacesEvent event)
254: throws AbortProcessingException {
255: //keyevent should be process by this component
256: super .broadcast(event);
257: if ((event instanceof ValueChangeEvent)) {
258: populateItemList();
259: setChangedComponentId(this .getClientId(FacesContext
260: .getCurrentInstance()));
261: }
262: }
263:
264: /**
265: * <p>Return the value of the <code>selectInputText</code> property.</p>
266: */
267: public UIComponent getSelectFacet() {
268: return (UIComponent) getFacet("selectInputText");
269: }
270:
271: /**
272: * <p>Set the value of the <code>selectedItem</code> property.</p>
273: */
274: public void setSelectedItem(String key) {
275:
276: this .selectedItem = (SelectItem) itemMap.get(key);
277: }
278:
279: /**
280: * <p>Return the value of the <code>selectedItem</code> property.</p>
281: */
282: public SelectItem getSelectedItem() {
283: return selectedItem;
284: }
285:
286: //property to set the max matches to be displayed
287:
288: /**
289: * <p>Set the value of the <code>rows</code> property.</p>
290: */
291: public void setRows(int rows) {
292: this .rows = new Integer(rows);
293: }
294:
295: /**
296: * <p>Return the value of the <code>rows</code> property.</p>
297: */
298: public int getRows() {
299: if (rows != null) {
300: // Should always return the original no. of rows. JIRA ICE-1320.
301: return rows.intValue();
302: /*
303: if (itemMap != null) {
304: return itemMap.size() > 0 && itemMap.size() < rows.intValue() ?
305: itemMap.size() : rows.intValue();
306: } else {
307: return rows.intValue();
308: }
309: */
310: }
311:
312: ValueBinding vb = getValueBinding("rows");
313: return vb != null ? Integer.parseInt(vb.getValue(
314: getFacesContext()).toString()) : DEFAULT_MAX_MATCHS;
315: }
316:
317: /**
318: * <p>Set the value of the <code>width</code> property.</p>
319: */
320: public void setWidth(String width) {
321: this .width = width;
322: }
323:
324: /**
325: * <p>Set the value of the <code>listVar</code> property.</p>
326: */
327: public void setListVar(String listVar) {
328: this .listVar = listVar;
329: }
330:
331: /**
332: * <p>Return the value of the <code>listVar</code> property.</p>
333: */
334: public String getListVar() {
335: if (listVar != null) {
336: return listVar;
337: }
338: ValueBinding vb = getValueBinding("listVar");
339: return vb != null ? (String) vb.getValue(getFacesContext())
340: : null;
341: }
342:
343: /**
344: * <p>Set the value of the <code>listValue</code> property.</p>
345: */
346: public void setListValue(List listValue) {
347: this .itemList = listValue;
348: }
349:
350: /**
351: * <p>Return the value of the <code>listValue</code> property.</p>
352: */
353: public List getListValue() {
354: ValueBinding vb = getValueBinding("listValue");
355: return (List) vb.getValue(FacesContext.getCurrentInstance());
356: }
357:
358: /**
359: * <p>Return the value of the <code>width</code> property.</p>
360: */
361: public String getWidth() {
362: if (width != null) {
363: return width;
364: }
365: ValueBinding vb = getValueBinding("width");
366: return vb != null ? vb.getValue(getFacesContext()).toString()
367: : DEFAULT_WIDTH;
368: }
369:
370: String getWidthAsStyle() {
371: try {//no measurement unit defined, so add the px unit
372: int width = Integer.parseInt(getWidth());
373: return "width:" + width + "px;";
374: } catch (NumberFormatException e) {
375: return "width:" + getWidth().trim();
376: }
377: }
378:
379: /**
380: * <p>Set the value of the <code>styleClass</code> property.</p>
381: */
382: public void setStyleClass(String styleClass) {
383: this .styleClass = styleClass;
384: }
385:
386: /**
387: * <p>Return the value of the <code>styleClass</code> property.</p>
388: */
389: public String getStyleClass() {
390: return Util.getQualifiedStyleClass(this , styleClass,
391: CSS_DEFAULT.DEFAULT_SELECT_INPUT, "styleClass",
392: isDisabled());
393: }
394:
395: /**
396: * <p>Return the value of the <code>inputTextClass</code> property.</p>
397: */
398: public String getInputTextClass() {
399: return Util.getQualifiedStyleClass(this ,
400: CSS_DEFAULT.DEFAULT_SELECT_INPUT_TEXT_CLASS,
401: isDisabled());
402: }
403:
404: /**
405: * <p>Return the value of the <code>listClass</code> property.</p>
406: */
407: public String getListClass() {
408: return Util.getQualifiedStyleClass(this ,
409: CSS_DEFAULT.DEFAULT_SELECT_INPUT_LIST_CLASS,
410: isDisabled());
411: }
412:
413: /**
414: * <p>Return the value of the <code>rowClass</code> property.</p>
415: */
416: public String getRowClass() {
417: return Util.getQualifiedStyleClass(this ,
418: CSS_DEFAULT.DEFAULT_SELECT_INPUT_ROW_CLASS,
419: isDisabled());
420: }
421:
422: /**
423: * <p>Return the value of the <code>selectedRowClass</code> property.</p>
424: */
425: public String getSelectedRowClass() {
426: return Util.getQualifiedStyleClass(this ,
427: CSS_DEFAULT.DEFAULT_SELECT_INPUT_SELECTED_ROW_CLASS,
428: isDisabled());
429: }
430:
431: public String getOptions() {
432: return options;
433: }
434:
435: public void setOptions(String options) {
436: this .options = options;
437: }
438:
439: //the following code is a fix for iraptor bug 347
440: //on first page submit, all input elements gets valueChangeEvent (null to ""),
441: //so component's ids can be more then one
442: private List changedComponentIds = new ArrayList();
443:
444: /**
445: * <p>Set the value of the <code>selectedPanel</code> property.</p>
446: */
447: void setChangedComponentId(Object id) {
448: if (id == null) {
449: changedComponentIds.clear();
450: } else {
451: changedComponentIds.add(id);
452: }
453: }
454:
455: /**
456: * <p>Return the value of the <code>selectedPanel</code> property.</p>
457: */
458: boolean hasChanged() {
459: return changedComponentIds.contains(this
460: .getClientId(FacesContext.getCurrentInstance()));
461: }
462:
463: /**
464: * queue the event if the enter key was pressed
465: *
466: * @param facesContext
467: */
468: private void queueEventIfEnterKeyPressed(FacesContext facesContext) {
469: try {
470: Map requestParemeterMap = facesContext.getExternalContext()
471: .getRequestParameterMap();
472: KeyEvent keyEvent = new KeyEvent(this , requestParemeterMap);
473:
474: if (keyEvent.getKeyCode() == KeyEvent.CARRIAGE_RETURN) {
475: queueEvent(new ActionEvent(this ));
476: }
477: if ("true"
478: .equals(requestParemeterMap.get("ice.event.left"))) {
479: queueEvent(new ActionEvent(this ));
480: }
481: if ("onclick".equals(requestParemeterMap
482: .get("ice.event.type"))) {
483: queueEvent(new ActionEvent(this ));
484: }
485:
486: } catch (Exception e) {
487: e.printStackTrace();
488: }
489: }
490:
491: /**
492: * <p>Gets the state of the instance as a <code>Serializable</code>
493: * Object.</p>
494: */
495: public Object saveState(FacesContext context) {
496: Object values[] = new Object[8];
497: values[0] = super .saveState(context);
498: values[1] = styleClass;
499: values[2] = listVar;
500: values[3] = rows;
501: values[4] = width;
502: values[5] = selectedItem;
503: values[6] = itemList;
504: values[7] = options;
505: return ((Object) (values));
506: }
507:
508: /**
509: * <p>Perform any processing required to restore the state from the entries
510: * in the state Object.</p>
511: */
512: public void restoreState(FacesContext context, Object state) {
513: Object values[] = (Object[]) state;
514: super .restoreState(context, values[0]);
515: styleClass = (String) values[1];
516: listVar = (String) values[2];
517: rows = (Integer) values[3];
518: width = (String) values[4];
519: selectedItem = (SelectItem) values[5];
520: itemList = (List) values[6];
521: options = (String) values[7];
522: }
523: }
|