com.icesoft.faces.component.selectinputtext.SelectInputText Maven / Gradle / Ivy
/*
* Copyright 2004-2013 ICEsoft Technologies Canada Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS
* IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package com.icesoft.faces.component.selectinputtext;
import com.icesoft.faces.component.CSS_DEFAULT;
import com.icesoft.faces.component.ext.HtmlInputText;
import com.icesoft.faces.component.ext.KeyEvent;
import com.icesoft.faces.component.ext.renderkit.FormRenderer;
import com.icesoft.faces.component.ext.taglib.Util;
import com.icesoft.faces.context.effects.JavascriptContext;
import com.icesoft.faces.renderkit.dom_html_basic.DomBasicRenderer;
import org.icefaces.impl.component.SeriesStateHolder;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.el.MethodBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.FacesEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.event.PhaseId;
import javax.faces.model.SelectItem;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
/**
* SelectInputText is a JSF component class that represents an ICEfaces
* autocomplete input text. This component requires the application developer to
* implement the matching list rearch algorithm in their backing bean.
*
* SelectInputText extends the ICEfaces extended HtmlInputText component.
*
* By default this component is rendered by the "com.icesoft.faces.SelectInputText"
* renderer type.
*/
public class SelectInputText extends HtmlInputText implements NamingContainer,
SeriesStateHolder {
public static final String COMPONENT_TYPE =
"com.icesoft.faces.SelectInputText";
//default style classes
//private properties for style classes
private String styleClass;
public static final String RENDERER_TYPE = "com.icesoft.faces.SelectInputText";
/**
* A request-scope attribute under which the model data for the row selected
* by the current value of the "rowIndex" property will be exposed.
*/
private String listVar = null;
/**
* A state variable for number of rows to be matched.
*/
private Integer rows;
private final int DEFAULT_MAX_MATCHS = 10;
/**
* A state variable for width of both inputtext and dropdownlist
*/
private String width;
private final String DEFAULT_WIDTH = "150";
/**
* A property to store the selectedItem, after successfull match
*/
private SelectItem selectedItem;
/**
* list of selectItems
*/
private transient List itemList;
private String options;
private transient int index = -1;
private MethodBinding textChangeListener;
public SelectInputText() {
super();
setRendererType(RENDERER_TYPE);
JavascriptContext.includeLib(JavascriptContext.ICE_EXTRAS,
FacesContext.getCurrentInstance());
}
/*
* (non-Javadoc)
* @see javax.faces.component.UIComponent#encodeBegin(javax.faces.context.FacesContext)
*/
public void encodeBegin(FacesContext context) throws IOException {
super.encodeBegin(context);
}
/*
* (non-Javadoc)
* @see javax.faces.component.UIComponent#decode(javax.faces.context.FacesContext)
*/
public void decode(FacesContext facesContext) {
setSelectedItem(facesContext);
super.decode(facesContext);
if (Util.isEventSource(facesContext,this)) {
queueEventIfEnterKeyPressed(facesContext);
// queueEvent(new ActionEvent(this));
}
}
/**
* this method is responsible to setting the seletecdItem on the component
*
* @param facesContext
*/
private void setSelectedItem(FacesContext facesContext) {
Map requestMap =
facesContext.getExternalContext().getRequestParameterMap();
String clientId = getClientId(facesContext);
//System.out.println("SelectInputText.setSelectedItem() clientId: " + clientId);
String value = (String) requestMap.get(clientId);
if(value != null) {
boolean changed = isPartialSubmitKeypress(requestMap, clientId);
//System.out.println("SelectInputText.setSelectedItem() changed: " + changed);
if(changed) {
setChangedComponentId( clientId );
//System.out.println("SelectInputText.setSelectedItem() textChangeListener: " + getTextChangeListener());
if( getTextChangeListener() != null ) {
//System.out.println("SelectInputText.setSelectedItem() submittedValue: " + value);
Object oldValue = getSubmittedValue();
if(oldValue == null) {
oldValue = DomBasicRenderer.converterGetAsString(
facesContext, this, getValue());
}
//System.out.println("SelectInputText.setSelectedItem() oldValue: " + oldValue);
TextChangeEvent event = new TextChangeEvent(this, oldValue, value);
event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
queueEvent(event);
}
}
}
String selIdxStr = (String) requestMap.get(
clientId + SelectInputTextRenderer.AUTOCOMPLETE_INDEX);
//System.out.println("SIT.decode() selIdxStr: " + selIdxStr);
if (selIdxStr != null && selIdxStr.trim().length() > 0) {
int selIdx = Integer.parseInt(selIdxStr);
setSelectedIndex(selIdx);
setChangedComponentId( clientId );
}
else {
setSelectedItem(value);
}
}
private static boolean isPartialSubmitKeypress(Map requestMap, String clientId) {
String target = (String) requestMap.get("ice.event.target");
String captured = (String) requestMap.get("ice.event.captured");
if(target == null)
target = "";
if(captured == null)
captured = "";
if( !target.equals(clientId) && !captured.equals(clientId) )
return false;
String type = (String) requestMap.get("ice.event.type");
String partialSubmit = (String) requestMap.get("ice.submit.partial");
if(type == null || type.length() == 0 ||
partialSubmit == null || partialSubmit.length() == 0)
{
return false;
}
if( partialSubmit.equalsIgnoreCase("true") &&
(type.equalsIgnoreCase("onundefined") || type.equalsIgnoreCase("onunknown") ||
type.equalsIgnoreCase("onkeypress") ||
type.equalsIgnoreCase("onkeydown")) )
{
return true;
}
return false;
}
/**
* return true if I had focus when submitted
*
* @param facesContext
* @return focus
*/
private boolean hadFocus(FacesContext facesContext) {
Object focusId = facesContext.getExternalContext()
.getRequestParameterMap().get(FormRenderer.getFocusElementId());
boolean focus = false;
if (focusId != null) {
if (focusId.toString().equals(getClientId(facesContext))) {
focus = true;
}
}
setFocus(focus);
return focus;
}
//this list would be set in populateItemList()
/**
* Return the value of the itemList
property.
*/
public Iterator getItemList() {
if (itemList == null) {
return Collections.EMPTY_LIST.iterator();
}
return itemList.iterator();
}
/**
* Set the value of the index
property.
*/
public void setIndex(int index) {
this.index = index;
}
/**
* Return the value of the clientId
property.
*/
public String getClientId(FacesContext context) {
if (context == null) {
throw new NullPointerException();
}
String baseClientId = super.getClientId(context);
if (index >= 0) {
return (baseClientId + UINamingContainer.getSeparatorChar(context) + index++);
} else {
return (baseClientId);
}
}
/**
* reset parent's and its children's ids
*/
void resetId(UIComponent component) {
String id = component.getId();
component.setId(id); // Forces client id to be reset
if (component.getChildCount() == 0)return;
Iterator kids = component.getChildren().iterator();
while (kids.hasNext()) {
UIComponent kid = (UIComponent) kids.next();
resetId(kid);
}
}
//this method generating the list of selectItems "itemList", which can be bounded
//with the bean, or could be static on jsf page
//matches list can be change after value change event, so we are calling
//this method after value change event in broadcast(), where the method bounded
//with valueChangeListner is being called and updates the data model.
void populateItemList() {
FacesContext facesContext = FacesContext.getCurrentInstance();
if (getSelectFacet() != null) {
//facet being used on jsf page, so get the list from the value binding
itemList = getListValue();
} else {
//selectItem or selectItems has been used on jsf page, so get the selectItems
itemList = Util.getSelectItems(facesContext,this);
}
}
/* (non-Javadoc)
* @see javax.faces.component.UIComponent#broadcast(javax.faces.event.FacesEvent)
*/
public void broadcast(FacesEvent event) throws AbortProcessingException {
//keyevent should be process by this component
super.broadcast(event);
//System.out.println("SelectInputText.broadcast() clientId: " + getClientId(FacesContext.getCurrentInstance()));
//System.out.println("SelectInputText.broadcast() event: " + event);
if (event instanceof TextChangeEvent) {
MethodBinding mb = getTextChangeListener();
//System.out.println("SelectInputText.broadcast() TextChangeEvent mb: " + mb);
if(mb != null) {
mb.invoke( FacesContext.getCurrentInstance(),
new Object[] {event} );
}
}
// else if (event instanceof ValueChangeEvent) {
//System.out.println("SelectInputText.broadcast() ValueChangeEvent old: " + ((ValueChangeEvent)event).getOldValue() + "new: " + ((ValueChangeEvent)event).getNewValue());
// }
}
/**
* Return the value of the selectInputText
property.
*/
public UIComponent getSelectFacet() {
return (UIComponent) getFacet("selectInputText");
}
protected void setSelectedIndex(int index) {
//System.out.println("SIT.setSelectedIndex() index: " + index);
SelectItem selItm = null;
if (index >= 0) {
if (itemList == null) {
populateItemList();
}
if (itemList != null) {
if (index < itemList.size()) {
selItm = (SelectItem) itemList.get(index);
//System.out.println("SIT.setSelectedIndex() selItm: " + selItm);
}
}
}
selectedItem = selItm;
}
/**
* Set the value of the selectedItem
property.
* If there are multiple matches between the key parameter, and the
* data model's SelectItem objects' itemLabel, it won't match any,
* unless one of the matching SelectItem objects equals the last
* selectedItem. Note that this component can only keep a reference
* to the last selectedItem if its value field is Serializable.
*/
public void setSelectedItem(String key) {
//System.out.println("SIT.setSelectedItem() key: " + key);
SelectItem selItm = null;
if (key != null) {
if (itemList == null) {
populateItemList();
}
if (itemList != null) {
SelectItem sticky = null;
boolean multipleMatches = false;
FacesContext facesContext = FacesContext.getCurrentInstance();
for (int i = 0; i < itemList.size(); i++) {
SelectItem item = (SelectItem) itemList.get(i);
String itemLabel = item.getLabel();
if (itemLabel == null) {
itemLabel = DomBasicRenderer.converterGetAsString(
facesContext, this, item.getValue());
}
if (key.equals(itemLabel)) {
//System.out.println("SIT.setSelectedItem() MATCHED with index: " + i);
if (selectedItem != null && selectedItem.equals(item)) {
//System.out.println("SIT.setSelectedItem() MATCHED with selectedItem");
sticky = item;
}
multipleMatches |= (selItm != null);
//if(selItm != null)System.out.println("SIT.setSelectedItem() MULTIPLE MATCHES - UNMATCHING (if no sticky)");
selItm = item;
}
}
if (sticky != null) {
selItm = sticky;
}
else if (multipleMatches) {
selItm = null;
}
}
}
selectedItem = selItm;
}
/**
* Return the value of the selectedItem
property.
*/
public SelectItem getSelectedItem() {
return selectedItem;
}
//property to set the max matches to be displayed
/**
* Set the value of the rows
property.
*/
public void setRows(int rows) {
this.rows = new Integer(rows);
}
/**
* Return the value of the rows
property.
*/
public int getRows() {
if (rows != null) {
// Should always return the original no. of rows. JIRA ICE-1320.
return rows.intValue();
}
ValueBinding vb = getValueBinding("rows");
return vb != null ?
Integer.parseInt(vb.getValue(getFacesContext()).toString()) :
DEFAULT_MAX_MATCHS;
}
/**
* Set the value of the width
property.
*/
public void setWidth(String width) {
this.width = width;
}
/**
* Set the value of the listVar
property.
*/
public void setListVar(String listVar) {
this.listVar = listVar;
}
/**
* Return the value of the listVar
property.
*/
public String getListVar() {
if (listVar != null) {
return listVar;
}
ValueBinding vb = getValueBinding("listVar");
return vb != null ? (String) vb.getValue(getFacesContext()) : null;
}
/**
* Set the value of the listValue
property.
*/
public void setListValue(List listValue) {
this.itemList = listValue;
}
/**
* Return the value of the listValue
property.
*/
public List getListValue() {
ValueBinding vb = getValueBinding("listValue");
return (List) vb.getValue(FacesContext.getCurrentInstance());
}
/**
* Return the value of the width
property.
*/
public String getWidth() {
if (width != null) {
return width;
}
ValueBinding vb = getValueBinding("width");
return vb != null ? vb.getValue(getFacesContext()).toString() :
DEFAULT_WIDTH;
}
String getWidthAsStyle() {
try {//no measurement unit defined, so add the px unit
int width = Integer.parseInt(getWidth());
return "width:" + width + "px;";
} catch (NumberFormatException e) {
return "width:" + getWidth().trim();
}
}
/**
* Set the value of the styleClass
property.
*/
public void setStyleClass(String styleClass) {
this.styleClass = styleClass;
}
/**
* Return the value of the styleClass
property.
*/
public String getStyleClass() {
return Util.getQualifiedStyleClass(this,
styleClass,
CSS_DEFAULT.DEFAULT_SELECT_INPUT,
"styleClass",
isDisabled());
}
/**
* Return the value of the inputTextClass
property.
*/
public String getInputTextClass() {
return Util.getQualifiedStyleClass(this,
CSS_DEFAULT.DEFAULT_SELECT_INPUT_TEXT_CLASS, isDisabled());
}
/**
* Return the value of the listClass
property.
*/
public String getListClass() {
return Util.getQualifiedStyleClass(this,
CSS_DEFAULT.DEFAULT_SELECT_INPUT_LIST_CLASS, isDisabled());
}
/**
* Return the value of the rowClass
property.
*/
public String getRowClass() {
return Util.getQualifiedStyleClass(this,
CSS_DEFAULT.DEFAULT_SELECT_INPUT_ROW_CLASS, isDisabled());
}
/**
* Return the value of the selectedRowClass
property.
*/
public String getSelectedRowClass() {
return Util.getQualifiedStyleClass(this,
CSS_DEFAULT.DEFAULT_SELECT_INPUT_SELECTED_ROW_CLASS, isDisabled());
}
public String getOptions() {
return options;
}
public void setOptions(String options) {
this.options = options;
}
public MethodBinding getTextChangeListener() {
return textChangeListener;
}
public void setTextChangeListener(MethodBinding mb) {
textChangeListener = mb;
}
//the following code is a fix for iraptor bug 347
//on first page submit, all input elements gets valueChangeEvent (null to ""),
//so component's ids can be more then one
private transient List changedComponentIds;
/**
* Set the value of the selectedPanel
property.
*/
void setChangedComponentId(Object id) {
if (id == null) {
if (changedComponentIds != null) {
changedComponentIds.clear();
}
} else {
if (changedComponentIds == null) {
changedComponentIds = new ArrayList(6);
}
changedComponentIds.add(id);
}
}
/**
* Return the value of the selectedPanel
property.
*/
boolean hasChanged() {
if (changedComponentIds == null) {
return false;
}
return changedComponentIds
.contains(this.getClientId(FacesContext.getCurrentInstance()));
}
/**
* queue the event if the enter key was pressed
*
* @param facesContext
*/
private void queueEventIfEnterKeyPressed(FacesContext facesContext) {
try {
Map requestParemeterMap = facesContext.getExternalContext()
.getRequestParameterMap();
KeyEvent keyEvent =
new KeyEvent(this, requestParemeterMap);
if (keyEvent.getKeyCode() == KeyEvent.CARRIAGE_RETURN) {
queueEvent(new ActionEvent(this));
}
if("true".equals(requestParemeterMap.get("ice.event.left"))){
queueEvent(new ActionEvent(this));
} else if("onclick".equals(requestParemeterMap.get("ice.event.type"))){
queueEvent(new ActionEvent(this));
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Gets the state of the instance as a Serializable
* Object.
*/
public Object saveState(FacesContext context) {
Object values[] = new Object[8];
values[0] = super.saveState(context);
values[1] = styleClass;
values[2] = listVar;
values[3] = rows;
values[4] = width;
values[5] = options;
values[6] = saveAttachedState(context, textChangeListener);
values[7] = (selectedItem == null ||
selectedItem.getValue() == null ||
selectedItem.getValue() instanceof Serializable)
? selectedItem : null;
return ((Object) (values));
}
/**
* Perform any processing required to restore the state from the entries
* in the state Object.
*/
public void restoreState(FacesContext context, Object state) {
Object values[] = (Object[]) state;
super.restoreState(context, values[0]);
styleClass = (String) values[1];
listVar = (String) values[2];
rows = (Integer) values[3];
width = (String) values[4];
options = (String)values[5];
textChangeListener = (MethodBinding)
restoreAttachedState(context, values[6]);
selectedItem = (SelectItem) values[7];
}
public String getOnkeypress() {
if (isDisabled() || isReadonly()) return "";
return super.getOnkeypress();
}
public Object saveSeriesState(FacesContext facesContext) {
Object values[] = new Object[6];
values[0] = styleClass;
values[1] = listVar;
values[2] = rows;
values[3] = width;
values[4] = options;
values[5] = (selectedItem == null ||
selectedItem.getValue() == null ||
selectedItem.getValue() instanceof Serializable)
? selectedItem : null;
return ((Object) (values));
}
public void restoreSeriesState(FacesContext facesContext, Object state) {
Object values[] = (Object[]) state;
styleClass = (String) values[0];
listVar = (String) values[1];
rows = (Integer) values[2];
width = (String) values[3];
options = (String)values[4];
selectedItem = (SelectItem) values[5];
}}