/* * � Copyright IBM Corp. 2013, 2014 * * 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. */ /* * Author: Maire Kehoe (mkehoe@ie.ibm.com) * Date: 2 Dec 2013 * MobileDateTimeHelperRenderer.java */ package com.ibm.xsp.extlib.renderkit.html_extended.mobile; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Map; import java.util.TimeZone; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import javax.faces.render.Renderer; import javax.faces.validator.Validator; import com.ibm.commons.util.StringUtil; import com.ibm.commons.xml.XMLException; import com.ibm.commons.xml.util.XMIConverter; import com.ibm.xsp.ajax.AjaxUtil; import com.ibm.xsp.complex.Attr; import com.ibm.xsp.component.FacesAttrsObject; import com.ibm.xsp.component.FacesInputComponent; import com.ibm.xsp.component.FacesInputFiltering; import com.ibm.xsp.component.UIInputEx; import com.ibm.xsp.component.UIViewRootEx; import com.ibm.xsp.component.xp.XspInputText; import com.ibm.xsp.context.FacesContextEx; import com.ibm.xsp.context.RequestParameters; import com.ibm.xsp.convert.ClientSideConverter; import com.ibm.xsp.convert.DateTimeConverter; import com.ibm.xsp.converter.ListConverter; import com.ibm.xsp.extlib.beans.DeviceBean; import com.ibm.xsp.extlib.util.ExtLibUtil; import com.ibm.xsp.renderkit.ReadOnlyAdapterRenderer; import com.ibm.xsp.renderkit.html_basic.AttrsUtil; import com.ibm.xsp.renderkit.html_basic.InputRendererUtil; import com.ibm.xsp.resource.DojoModuleResource; import com.ibm.xsp.util.FacesUtil; import com.ibm.xsp.util.JSUtil; import com.ibm.xsp.util.JavaScriptUtil; import com.ibm.xsp.validator.ClientSideValidator; /** * * @author Maire Kehoe (mkehoe@ie.ibm.com) */ public class InputDateRenderer extends Renderer implements ClientSideConverter{ protected static final int TYPE_DATE = 0; protected static final int TYPE_TIME = 1; protected static final int TYPE_TIMESTAMP = 2; @Override public boolean getRendersChildren() { return true; } @Override public void encodeChildren(FacesContext context, UIComponent component) throws IOException { if( ! component.isRendered() ){ return; } FacesUtil.renderChildren(context, component); } @Override public void encodeEnd(FacesContext context, UIComponent component) throws IOException { if (!component.isRendered()) { return; } // Get the response renderer ResponseWriter writer = context.getResponseWriter(); // Do not render if it is not needed if( AjaxUtil.isAjaxNullResponseWriter(writer) ) { return; } // Get the UIInput if (!(component instanceof UIInput)) { return; } UIInput uiInput = (UIInput) component; // And write the value String currentValue = computeValueAsISOString(context, uiInput); writeTag(context, uiInput, writer, currentValue); // TODO should change this to use InputRendererUtil.encodeValidation, // once it has been updated to support delegating to the renderer. // InputRendererUtil.encodeValidation(context, writer, uiInput); encodeValidation(context, writer, uiInput); InputRendererUtil.encodeDirtyState(context, writer, uiInput); } private String computeValueAsISOString(FacesContext context, UIInput input) { DateTimeConverter converter = (DateTimeConverter) input.getConverter(); // the submitted value takes precedence // As it is a string, no conversion should happen Object submittedValue = input.getSubmittedValue(); if (submittedValue != null) { return (String)submittedValue; } Object value = input.getValue(); if( null == value ){ return ""; } return getAsString(context, input, converter, value); } private String getAsString(FacesContext context, UIInput input, DateTimeConverter converter, Object value){ if(value instanceof Date) { Date dateValue = (Date)value; // Adjust the date to the desired timezone // Dojo expect the the date already formatted within the desired time zone // As the SimpleFormat uses the default timezone, we offset the difference so it is // correctly formatted for dojo. long offset = 0; TimeZone clientTimeZone = converter.getTimeZone(); TimeZone serverTimeZone = TimeZone.getDefault(); if( !serverTimeZone.equals(clientTimeZone) ){ // SPR#MKEE9HYGXB cannot use timeZone.getRawOffset() // because client timezone is-summerTime-ness and the // server timezone is-summerTime-ness may be different, // so using the raw offset leads to problems during the // period where one timezone has changed to summer time // but the other timezone has not. Date serverNow = new Date(); Date clientNow = java.util.Calendar.getInstance(clientTimeZone).getTime(); offset = serverTimeZone.getOffset(serverNow.getTime()) - clientTimeZone.getOffset(clientNow.getTime()); } dateValue = new Date(dateValue.getTime()-offset); int type = getValueType(input); return formatValueAsType(dateValue, type); } // TODO should probably not fall through to the non-ISO date converter. if(null == value || "".equals(value)) { return ""; } return value.toString(); } @Override public void decode(FacesContext context, UIComponent component) { // Get the UIInputEx if (!(component instanceof UIInputEx)) { return; } UIInputEx uiInput = (UIInputEx) component; // If the component is disabled, do not change the value of the // component, since its state cannot be changed. if( ReadOnlyAdapterRenderer.isReadOnly(context, uiInput) ) { return; } String clientId = uiInput.getClientId(context); Map<?,?> requestMap = context.getExternalContext().getRequestParameterMap(); // Don't overwrite the value unless you have to! if(requestMap.containsKey(clientId)) { String newValue = (String)requestMap.get(clientId); // Apply the HTML filter in if applicable if(component instanceof FacesInputFiltering) { String filterIn = ((FacesInputFiltering)component).getHtmlFilterInName(); if(filterIn!=null) { newValue = ((FacesContextEx)context).filterHtml(filterIn, newValue); } } uiInput.setSubmittedValue(newValue); } else { // will only occur if the control is disabled (empty control submits empty string) Object newValue = uiInput.getDefaultValue(); if(newValue!=null){ // convert Date default value to string String newValueStr = getDefaultValueAsString(context, uiInput, newValue); uiInput.setSubmittedValue(newValueStr); } } } private String getDefaultValueAsString(FacesContext context, UIInputEx uiInput, Object defaultValue) { DateTimeConverter converter = (DateTimeConverter) uiInput.getConverter(); return getAsString(context, uiInput, converter, defaultValue); } private void writeTag(FacesContext context, UIInput component, ResponseWriter writer, String currentValue) throws IOException { writer.startElement("input", component); //$NON-NLS-1$ boolean isUsingInputPlainText = false; int typeInt = getValueType(component); String type; if(typeInt==TYPE_DATE) { type = "date"; //$NON-NLS-1$ } else if(typeInt==TYPE_TIME) { type = "time"; //$NON-NLS-1$ } else { isUsingInputPlainText = isUsingInputPlainText(context, component); if( isUsingInputPlainText ){ type = "text"; //$NON-NLS-1$ }else{ type = "datetime-local"; //$NON-NLS-1$ // type = "datetime"; //$NON-NLS-1$ } } writer.writeAttribute("type", type, null); //$NON-NLS-1$ // Write the actual value // For an input tag, it is passed as a parameter if(StringUtil.isNotEmpty(currentValue)) { writer.writeAttribute("value", currentValue, "value"); //$NON-NLS-1$ //$NON-NLS-2$ } // id & name use clientID String clientId = component.getClientId(context); //id // only write valid user defined ids String id = component.getId(); if (id != null && !id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX) ) { writer.writeAttribute("id", clientId, "id"); //$NON-NLS-1$ //$NON-NLS-2$ } //name writer.writeAttribute("name", clientId, "clientId"); //$NON-NLS-1$ //$NON-NLS-2$ writeTagHtmlAttributes(context, component, writer, currentValue); writeTagEventAttributes(context,component, writer, currentValue); if (component instanceof FacesAttrsObject) { FacesAttrsObject attrsHolder = (FacesAttrsObject) component; AttrsUtil.encodeAttrs(context, writer, attrsHolder); } if( isUsingInputPlainText ){ boolean placeholderAlreadyDecided = false; if( component instanceof FacesAttrsObject ){ FacesAttrsObject attrsHolder = (FacesAttrsObject) component; List<Attr> attrs = attrsHolder.getAttrs(); if( null != attrs ){ for (Attr attr : attrs) { if( "placeholder".equals(attr.getName()) ){ //$NON-NLS-1$ placeholderAlreadyDecided = true; } } } } if( !placeholderAlreadyDecided ){ // not using the native dateTime picker, so need some hint to the user // as to the date format they should use, so providing the current date in the correct format // in a placeholder (value that disappears onfocus). // placeholder="2014-01-13T15:35" Date currentDateTime = new Date(); String placeholderValue = getAsString(context, component, (DateTimeConverter)component.getConverter(), currentDateTime); writer.writeAttribute("placeholder", placeholderValue, null); //$NON-NLS-1$ } } writer.endElement("input"); //$NON-NLS-1$ } private boolean isUsingInputPlainText(FacesContext context, UIInput component) { // most browsers use <input type=datetime, but for iOS defaulting to type=text boolean isUseInputPlainTextOnIOS = true; String option = ((FacesContextEx)context).getProperty("xsp.theme.mobile.iOS.native.dateTime"); //$NON-NLS-1$ if( null != option ){ // explicitly configured whether to use type=datetime on iOS boolean isNativeOnIOS = "true".equals(option); //$NON-NLS-1$ isUseInputPlainTextOnIOS = ! isNativeOnIOS; } if( isUseInputPlainTextOnIOS ){ Object deviceBeanObj = ExtLibUtil.resolveVariable(context, "deviceBean"); //$NON-NLS-1$ if( deviceBeanObj instanceof DeviceBean ){ DeviceBean deviceBean = (DeviceBean)deviceBeanObj; boolean isIOS = deviceBean.isIphone()|| deviceBean.isIpad() || deviceBean.isIpod(); if( isIOS ){ // is iOS, so by use type=text return true; } // else other devices use type=datetime, not type=text } } // else always use type=datetime, don't need to whether check browser OS is iOS. return false; } private void writeTagHtmlAttributes(FacesContext context, UIInput component, ResponseWriter writer, String currentValue) throws IOException { XspInputText tcomponent = (XspInputText)component; // this mostly encodes the same attributes as the superclass, // except for the event attributes, which are encoded in writeTagEventAttributes // and it also encodes the TEXT_ATTRS listed above. // accessKey String accesskey = tcomponent.getAccesskey(); if (accesskey != null) { writer.writeAttribute("accesskey", accesskey, "accesskey"); // $NON-NLS-1$ $NON-NLS-2$ } // autoComplete String autoComplete = tcomponent.getAutocomplete(); if (autoComplete != null) { // only output the autocomplete attribute if the value // is 'off' since its lack of presence will be interpreted // as 'on' by the browser if ("off".equals(autoComplete)) { //$NON-NLS-1$ writer.writeAttribute("autocomplete","off","autocomplete"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } // dir String dir = tcomponent.getDir(); if(dir!=null) { writer.writeAttribute("dir", dir, "dir"); // $NON-NLS-1$ $NON-NLS-2$ } // role String role = tcomponent.getRole(); if(role!=null) { writer.writeAttribute("role", role, "role"); // $NON-NLS-1$ $NON-NLS-2$ } // aria-required if(tcomponent.isRequired()) { writer.writeAttribute("aria-required", "true", null); //$NON-NLS-1$ //$NON-NLS-2$ } // aria-invalid if(!tcomponent.isValid()) { writer.writeAttribute("aria-invalid", "true", null); //$NON-NLS-1$ //$NON-NLS-2$ } // disabled. boolean disabled = tcomponent.isDisabled(); if(disabled) { // note, write disabled="disabled" (not disabled="true") writer.writeAttribute("disabled", "disabled", "disabled"); // $NON-NLS-1$ $NON-NLS-2$ //$NON-NLS-3$ } // lang String lang = tcomponent.getLang(); if(lang!=null) { writer.writeAttribute("lang", lang, "lang"); // $NON-NLS-1$ $NON-NLS-2$ } // maxlength int maxlength = tcomponent.getMaxlength(); if(maxlength>=0) { writer.writeAttribute("maxlength", maxlength, "maxlength"); // $NON-NLS-1$ $NON-NLS-2$ } // readonly boolean readonly = ReadOnlyAdapterRenderer.isReadOnly(context,component); if(readonly) { writer.writeAttribute("readonly", "readonly", "readonly"); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$ } // size int size = tcomponent.getSize(); if(size>=0) { writer.writeAttribute("size", size, "size"); // $NON-NLS-1$ $NON-NLS-2$ } // style String style = tcomponent.getStyle(); if(style!=null) { writer.writeAttribute("style", style, "style"); // $NON-NLS-1$ $NON-NLS-2$ } // styleClass String styleClass = tcomponent.getStyleClass(); if(styleClass!=null) { writer.writeAttribute("class", styleClass, "styleClass"); // $NON-NLS-2$ $NON-NLS-1$ } // tabindex String tabindex = tcomponent.getTabindex(); if (tabindex != null) { writer.writeAttribute("tabindex", tabindex, "tabindex"); // $NON-NLS-1$ $NON-NLS-2$ } // title String title = tcomponent.getTitle(); if (title != null) { writer.writeAttribute("title", title, "title"); // $NON-NLS-1$ $NON-NLS-2$ } } private void writeTagEventAttributes(FacesContext context, UIInput component, ResponseWriter writer, String currentValue) throws IOException{ XspInputText tcomponent = (XspInputText)component; // write the FOCUS_ATTRS events onblur, onfocus, // and HTML_ATTRS onclick ondblclick String onfocus = tcomponent.getOnfocus(); if( null != onfocus ){ writer.writeAttribute("onfocus", onfocus, null);//$NON-NLS-1$ } String onblur = tcomponent.getOnblur(); if (null != onblur) { writer.writeAttribute("onblur", onblur, null);//$NON-NLS-1$ } String onclick = tcomponent.getOnclick(); if (null != onclick) { writer.writeAttribute("onclick", onclick, null);//$NON-NLS-1$ } String ondblclick = tcomponent.getOndblclick(); if (null != ondblclick) { writer.writeAttribute("ondblclick", ondblclick, null);//$NON-NLS-1$ } // write the HTML_ATTRS key events String onkeydown = tcomponent.getOnkeydown(); if (null != onkeydown) { writer.writeAttribute("onkeydown", onkeydown, null);//$NON-NLS-1$ } String onkeypress = tcomponent.getOnkeypress(); if (null != onkeypress) { writer.writeAttribute("onkeypress", onkeypress, null);//$NON-NLS-1$ } String onkeyup = tcomponent.getOnkeyup(); if (null != onkeyup) { writer.writeAttribute("onkeyup", onkeyup, null);//$NON-NLS-1$ } // write the HTML_ATTRS mouse events String onmousedown = tcomponent.getOnmousedown(); if (null != onmousedown) { writer.writeAttribute("onmousedown", onmousedown, null);//$NON-NLS-1$ } String onmousemove = tcomponent.getOnmousemove(); if (null != onmousemove) { writer.writeAttribute("onmousemove", onmousemove, null);//$NON-NLS-1$ } String onmouseout = tcomponent.getOnmouseout(); if (null != onmouseout) { writer.writeAttribute("onmouseout", onmouseout, null);//$NON-NLS-1$ } String onmouseover = tcomponent.getOnmouseover(); if (null != onmouseover) { writer.writeAttribute("onmouseover", onmouseover, null);//$NON-NLS-1$ } String onmouseup = tcomponent.getOnmouseup(); if (null != onmouseup) { writer.writeAttribute("onmouseup", onmouseup, null);//$NON-NLS-1$ } // write the input-props events String onchange = tcomponent.getOnchange(); if (null != onchange) { writer.writeAttribute("onchange", onchange, null);//$NON-NLS-1$ } String onselect = tcomponent.getOnselect(); if (null != onselect) { writer.writeAttribute("onselect", onselect, null);//$NON-NLS-1$ } } @Override public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException { UIInput uiInput = (UIInput)component; DateTimeConverter converter = (DateTimeConverter) uiInput.getConverter(); String value = (String)submittedValue; return getAsObject(context, uiInput, converter, value); } private Object getAsObject(FacesContext context, UIInput input, DateTimeConverter converter, String value){ if( StringUtil.isEmpty(value) ){ return null; } TimeZone tz = converter.getTimeZone(); // If the date is sent as time, then assume today's date in the browser TZ // So we'll have the proper conversion done. If we don't do that, then java // uses 1/1/70 and it has an issue when rendering it back in summer, as it leads to // one hour difference. This is also in sync with what the Notes backend API does int expectedType = getValueType(input); value = mutateSubmittedToExpectedFormat(context, input, converter, value, expectedType); if(value.startsWith("T")) { SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$ fmt.setTimeZone(tz); value = fmt.format(new Date())+value; } // assume this is a string in ISO format // In this case, dojo returns the right date but with a timezone specification computed // using the browser information. Unfortunately, it doesn't support the daylight savings // So that lead to a wrong time zone (http://trac.dojotoolkit.org/ticket/588) // A solution is to ignore the timezone provided by dojo. // But the date should be converted to the user TimeZone long dt; try{ dt = XMIConverter.readXMIDateStrict(value,false,true); } catch(XMLException ex){ // TODO in 9.0.2, should update this to handle message changes for SPR#MKEE7TXMLG String message; int type = expectedType; if(type==TYPE_DATE) { message = getMessageDate(); } else if(type==TYPE_TIME) { message = getMessageTime(); } else { message = getMessageBoth(); } throw new ConverterException(new FacesMessage(message),ex); } long offset = 0; TimeZone clientTimeZone = tz; TimeZone serverTimeZone = TimeZone.getDefault(); if( !serverTimeZone.equals(clientTimeZone) ){ // SPR#MKEE9HYGXB cannot use timeZone.getRawOffset() // because client timezone is-summerTime-ness and the // server timezone is-summerTime-ness may be different, // so using the raw offset leads to problems during the // period where one timezone has changed to summer time // but the other timezone has not. Date serverNow = new Date(); Date clientNow = java.util.Calendar.getInstance(clientTimeZone).getTime(); offset = serverTimeZone.getOffset(serverNow.getTime()) - clientTimeZone.getOffset(clientNow.getTime()); } dt += offset; Date date = new Date(dt); return date; } /** * @param value * @param expectedType * @return */ protected String mutateSubmittedToExpectedFormat(FacesContext context, UIInput input, DateTimeConverter converter, String value, int expectedType) { boolean hasColon = -1 != value.indexOf(':'); if( TYPE_TIME == expectedType && value.length() == 5 && hasColon && 'T' != value.charAt(0) ){ // in iPhone // from http://tools.ietf.org/html/rfc3339 // partial-time = time-hour ":" time-minute ":" time-second [time-secfrac] // We have something like // 09:30 // we need something like: // T09:30:00 value = "T"+value+":00"; //$NON-NLS-1$ //$NON-NLS-2$ } if( TYPE_TIMESTAMP == expectedType && (-1 != value.indexOf('T')) ){ int tee = value.indexOf('T'); int endTimeIndex = Math.max(value.indexOf('Z', tee), Math.max(value.indexOf('+', tee), value.indexOf('-', tee))); if( -1 == endTimeIndex ){ endTimeIndex = value.length(); } if( 6 == endTimeIndex - tee ){ // in iPhone // We have something like // 2013-12-03T09:30 // we need // 2013-12-03T09:30:00 // in Android // We have something like: // 2013-12-21T09:30Z // we need // 2013-12-21T09:30:00Z value = value.substring(0, endTimeIndex)+":00"+value.substring(endTimeIndex); //$NON-NLS-1$ } } if( TYPE_TIMESTAMP == expectedType || TYPE_DATE == expectedType ){ int endDateIndex = (TYPE_TIMESTAMP == expectedType) ? value.indexOf('T') : value.length(); if( endDateIndex - value.indexOf('-') < 6 ){ // in Android // We have something like // 2013-5-5T09:30 // 2013-5-5 // we need // 2013-05-05T09:30 // 2013-05-05 int firstDash = value.indexOf('-'); int secondDash = (-1 == firstDash)? -1 : value.indexOf('-', firstDash+1); if( -1 != firstDash && -1 != secondDash && 2 == (secondDash - firstDash) ){ // insert 0 after 1st '-' value = value.substring(0,firstDash+1)+'0'+value.substring(firstDash+1); // recompute since the string changed: secondDash = (-1 == firstDash)? -1 : value.indexOf('-', firstDash+1); endDateIndex = (TYPE_TIMESTAMP == expectedType) ? value.indexOf('T') : value.length(); } if( -1 != secondDash && -1 != endDateIndex && 2 == (endDateIndex - secondDash) ){ // insert 0 after 2nd '-' value = value.substring(0,secondDash+1)+'0'+value.substring(secondDash+1); } } } return value; } private String getMessageDate() { // "This field is not a valid date." return com.ibm.xsp.extsn.ResourceHandler.getString("DateTimeConverter.Thisfieldisnotavaliddate"); //$NON-NLS-1$ } private String getMessageTime() { // "This field is not a valid time." return com.ibm.xsp.extsn.ResourceHandler.getString("DateTimeConverter.Thisfieldisnotavalidtime"); //$NON-NLS-1$ } private String getMessageBoth() { // "This field is not a valid DateTime." return com.ibm.xsp.extsn.ResourceHandler.getString("DateTimeConverter.ThisfieldisnotavalidDateTime"); //$NON-NLS-1$ } private int getValueType(UIInput uiInput) { DateTimeConverter converter = (DateTimeConverter) uiInput.getConverter(); // Find what should be used: date, time or both // Default is both... String dateType = converter.getType(); int type = TYPE_TIMESTAMP; if(StringUtil.isNotEmpty(dateType)) { if(dateType.equals(DateTimeConverter.TYPE_DATE)) { type = TYPE_DATE; } else if(dateType.equals(DateTimeConverter.TYPE_TIME)) { type = TYPE_TIME; } } return type; } private String formatValueAsType(Date value, int type) { if(type==TYPE_DATE) { return formatDateAsISODate(value); } else if(type==TYPE_TIME) { return formatTimeAsISOTime(value); } else { return formatDateAsISODateTime(value); } } // Formatting utilities private String formatDateAsISODate(Date date) { // DateFormat are not reentrant - must be created on demand SimpleDateFormat dojoDate = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$ return dojoDate.format(date); } private String formatTimeAsISOTime(Date time) { // DateFormat are not reentrant - must be created on demand //SimpleDateFormat dojoTime = new SimpleDateFormat("'T'HH:mm:ss"); //$NON-NLS-1$ SimpleDateFormat dojoTime = new SimpleDateFormat("HH:mm"); //$NON-NLS-1$ return dojoTime.format(time); } private String formatDateAsISODateTime(Date dateTime) { // DateFormat are not reentrant - must be created on demand //SimpleDateFormat dojoTimestamp = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); //$NON-NLS-1$ SimpleDateFormat dojoTimestamp = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm"); //$NON-NLS-1$ return dojoTimestamp.format(dateTime); } // === Start code from InputRendererUtil === // InputRendererUtil.encodeValidation(context, writer, uiInput); private void encodeValidation(FacesContext context, ResponseWriter writer, UIInput uiInput) throws IOException { if(uiInput instanceof FacesInputComponent) { FacesInputComponent ic = (FacesInputComponent)uiInput; // Write the client side validators RequestParameters p = FacesUtil.getRequestParameters(context); if(p.isClientSideValidation()) { // Either the validators or only the client side validation can be disabled if(!ic.isDisableValidators() && !ic.isDisableClientSideValidation()) { generateClientSideValidation(context,uiInput,writer); } } } } // === continue code from InputRendererUtil === // ClientSide validation // com.ibm.xsp.renderkit.html_basic.InputRendererUtil.generateClientSideValidation(FacesContext, UIInput, ResponseWriter) private void generateClientSideValidation(FacesContext context, UIInput uiInput, ResponseWriter writer) throws IOException { // Check if the input field is required boolean required = uiInput.isRequired(); Converter c = InputRendererUtil.findConverter(context, uiInput); Validator[] v = uiInput.getValidators(); // Check if it might make sense to generate a function boolean validate = required; if(!validate) { validate = c instanceof ClientSideConverter; } if(!validate) { for( int i=0; i<v.length; i++) { validate = v[i] instanceof ClientSideValidator; if(validate) { break; } } } if(validate) { // This flag is maintained if we actually need to generate the function // Some converter/validator may not actually generate any client code, depending on their parameters validate = false; StringBuilder b = new StringBuilder(128); b.append("XSP.attachValidator("); // $NON-NLS-1$ JavaScriptUtil.addString(b, uiInput.getClientId(context)); // Add the required flag if(required) { b.append(",new XSP.RequiredValidator("); // $NON-NLS-1$ JavaScriptUtil.addMessage(b, InputRendererUtil.getRequiredMessage(context, uiInput)); b.append(")"); validate = true; } else { b.append(",null"); // $NON-NLS-1$ } // Add the converter if(c instanceof ClientSideConverter) { ClientSideConverter clientSideConverter = (ClientSideConverter)c; // TODO this is handling of converterRenderer // differs from the original implementation in InputRendererUtil. String s; Renderer renderer = FacesUtil.getRenderer(context, uiInput.getFamily(), uiInput.getRendererType()); ClientSideConverter converterRenderer = (ClientSideConverter) FacesUtil .getRendererAs(renderer, ClientSideConverter.class); if (null != converterRenderer) { // if the control renderer implements ClientSideConverter // delegate to the renderer instead of to the converter. s = converterRenderer.generateClientSideConverter(context, uiInput); }else{ s=clientSideConverter.generateClientSideConverter(context, uiInput); } if(StringUtil.isNotEmpty(s)) { b.append(","); b.append(s); // not JSUtil because contains client script. validate = true; } else { b.append(",null"); // $NON-NLS-1$ } } else { b.append(",null"); // $NON-NLS-1$ } // And add the validator for( int i=0; i<v.length; i++) { if(v[i] instanceof ClientSideValidator) { String s = ((ClientSideValidator)v[i]).generateClientSideValidation(context, uiInput); if(StringUtil.isNotEmpty(s)) { b.append(","); b.append(s); // not JSUtil because contains client script. validate = true; } } } // Finally, check for multiple values String multiSep = null; if(uiInput instanceof UIInputEx) { multiSep = ((UIInputEx)uiInput).getMultipleSeparator(); } if( c instanceof ListConverter ){ multiSep = ((ListConverter)c).getDelimiter(); if( StringUtil.isEmpty(multiSep) ){ multiSep = ","; //$NON-NLS-1$ } } if(StringUtil.isNotEmpty(multiSep)) { b.append(","); JSUtil.addString(b, multiSep); } b.append(");"); //get the scriptcollector component (needed to add script blocks the the rendered output). if(validate) { JavaScriptUtil.addScriptOnLoad(b.toString()); } } } // === end code from InputRendererUtil === private static final DojoModuleResource ISO_DATE_CONVERTER_MODULE = new DojoModuleResource("extlib.date.IsoDateConverter"); //$NON-NLS-1$ private static final DojoModuleResource ISO_DATE_TIME_CONVERTER_MODULE = new DojoModuleResource("extlib.date.IsoDateTimeConverter"); //$NON-NLS-1$ private static final DojoModuleResource ISO_TIME_CONVERTER_MODULE = new DojoModuleResource("extlib.date.IsoTimeConverter"); //$NON-NLS-1$ /** * Implements {@link ClientSideConverter#generateClientSideConverter(FacesContext, UIComponent)} */ public String generateClientSideConverter(FacesContext context, UIComponent component) { UIInput input = (UIInput) component; DateTimeConverter converter = (DateTimeConverter) input.getConverter(); String dateType = converter.getType(); int valueType = TYPE_TIMESTAMP; if(StringUtil.isNotEmpty(dateType)) { if(dateType.equals(DateTimeConverter.TYPE_DATE)) { valueType = TYPE_DATE; } else if(dateType.equals(DateTimeConverter.TYPE_TIME)) { valueType = TYPE_TIME; } } // TODO in 9.0.2, should update this to handle message changes for SPR#MKEE7TXMLG String message; if( TYPE_DATE == valueType ){ message = getMessageDate(); }else if( TYPE_TIME == valueType ){ message = getMessageTime(); }else{ message = getMessageBoth(); } DojoModuleResource module; StringBuilder builder = new StringBuilder(); switch(valueType){ case TYPE_DATE:{ module = ISO_DATE_CONVERTER_MODULE; builder.append("new extlib.date.IsoDateConverter({message:"); //$NON-NLS-1$ JavaScriptUtil.addMessage(builder, message); builder.append("})"); //$NON-NLS-1$ break; } case TYPE_TIME:{ module = ISO_TIME_CONVERTER_MODULE; builder.append("new extlib.date.IsoTimeConverter({message:"); //$NON-NLS-1$ JavaScriptUtil.addMessage(builder, message); builder.append("})"); //$NON-NLS-1$ break; } default:{// TYPE_TIMESTAMP module = ISO_DATE_TIME_CONVERTER_MODULE; builder.append("new extlib.date.IsoDateTimeConverter({message:"); //$NON-NLS-1$ JavaScriptUtil.addMessage(builder, message); builder.append("})"); //$NON-NLS-1$ break; } } if( null != module ){ UIViewRootEx rootEx = (UIViewRootEx) context.getViewRoot(); rootEx.addEncodeResource(context,module); } return builder.toString(); } }