/* * � Copyright IBM Corp. 2010, 2013 * * 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.ibm.xsp.extlib.renderkit.dojo.form; import java.io.IOException; import java.util.Map; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import com.ibm.commons.util.NotImplementedException; import com.ibm.commons.util.StringUtil; import com.ibm.xsp.ajax.AjaxUtil; import com.ibm.xsp.component.UIViewRootEx; import com.ibm.xsp.dojo.FacesDojoComponent; import com.ibm.xsp.extlib.component.dojo.form.UIDojoFormWidgetBase; import com.ibm.xsp.extlib.renderkit.dojo.DojoRendererUtil; import com.ibm.xsp.extlib.resources.ExtLibResources; import com.ibm.xsp.renderkit.html_basic.BasicInputTextRenderer; import com.ibm.xsp.renderkit.html_basic.InputRendererUtil; import com.ibm.xsp.resource.DojoModuleResource; import com.ibm.xsp.resource.Resource; import com.ibm.xsp.util.JSUtil; /** * Form control renderer. * * Such a renderer renders a dojo form control (inputtext, combobox...) and eventually a * hidden field for carrying out the data (some controls, like editor or toggle button do * not automatically bind to a form field). In that last case, it also generates a piece * of JavaScript for the binding. * * The decoding part is also handled, taking care od multiple values if applicable. * * @author priand * */ public abstract class DojoFormWidgetRenderer extends BasicInputTextRenderer { // ========================================================================== // Rendering Properties // ========================================================================== protected Object getProperty(int prop) { return null; } // This is not needed post 852 protected boolean _isReadOnly(FacesContext context, UIInput input) { return super.isReadOnly(context, input); } // ========================================================================== // Renderer utilities // ========================================================================== // Internal debug flag protected static final boolean DEBUG = false; protected void newLine(ResponseWriter w) throws IOException { JSUtil.writeln(w); } protected void newLine(ResponseWriter w, String comment) throws IOException { if(DEBUG){ if( comment!=null) { w.writeComment(comment); } } JSUtil.writeln(w); } public static final String HIDDEN_SUFFIX = "_field"; // $NON-NLS-1$ @Override public void decode(FacesContext context, UIComponent component) { if(needDecode()) { super.decode(context, component); } } @Override protected void writeTag(FacesContext context, UIInput component, ResponseWriter writer, String currentValue) throws IOException { // Don't care throw new NotImplementedException(""); } /** * Checks if the control should actually decode the value. * As an example, input fields should while push buttons should not. * @return */ protected boolean needDecode() { return true; } /** * This method checks if a hidden field that hold the control value might be generated * to pass a value between the client and the server. * For example, a TextBox should not at it is already generated as an input tag, but a * toggle should, as it has not field backing the value. * @return */ protected boolean needHiddenField() { return false; } /** * Tag generation. * The tag generation is done as: * 1- Main dijit tag with attributes - encodeBegin() * 2- Tag content if applicable - encodeBegin() * 3- Tag children if applicable - encodeChildren() * 4- Main digit tag end - encodeEnd() * 5- Hidden field, if needed - encodeEnd() * 6- JS binding code - encodeEnd() */ @Override public void encodeBegin(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; boolean needHiddenField = needHiddenField(); // And write the value startTag(context, writer, uiInput); //write out dojoType and dojoAttributes if they are present boolean forceId = false; if(component instanceof FacesDojoComponent) { // Should we force the Id all the time here??? writeDojoAttributes(context, (FacesDojoComponent)component); forceId = true; } //id writeId(writer, context, component, forceId); //name if(!needHiddenField) { String name = getNameAttribute(context, component); String currentValue = getCurrentValue(context, uiInput); writer.writeAttribute("name", name, "name"); //$NON-NLS-1$ //$NON-NLS-2$ // Write the actual value // For an input tag, it is passed as a parameter writeValueAttribute(context, uiInput, writer, currentValue); // Render the tag body, if any renderTagBody(context, uiInput, writer, currentValue); } else { // Render the tag body, if any renderTagBody(context, uiInput, writer, null); } } @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; // close the input after writing the event attributes endTag(context, writer, uiInput); boolean needHiddenField = needHiddenField(); if(needHiddenField) { String name = getNameAttribute(context, uiInput); writer.startElement("input", uiInput); //$NON-NLS-1$ writer.writeAttribute("type", "hidden", null); //$NON-NLS-1$ //$NON-NLS-2$ writer.writeAttribute("id", uiInput.getClientId(context)+HIDDEN_SUFFIX, "id"); //$NON-NLS-1$ //$NON-NLS-2$ writer.writeAttribute("name", name, "name"); //$NON-NLS-1$ //$NON-NLS-2$ // Write the actual value // For an input tag, it is passed as a parameter String currentValue = getCurrentValue(context, uiInput); writeValueAttribute(context, uiInput, writer, currentValue); writer.endElement("input"); //$NON-NLS-1$ // If some script is needed... renderJavaScriptBinding(context, writer, uiInput); } // Write the client side validators // Encode the dirty flag update if necessary encodeValidation(context, writer, uiInput); encodeDirtyState(context, writer, uiInput); } protected void encodeValidation(FacesContext context, ResponseWriter writer, UIInput component) throws IOException { // Dojo client side validation is never triggered for dojo control // Dojo uses it form validation capability instead, displaying tooltips //InputRendererUtil.encodeValidation(context, writer, component); } protected void encodeDirtyState(FacesContext context, ResponseWriter writer, UIInput component) throws IOException { InputRendererUtil.encodeDirtyState(context, writer, component); } protected void startTag(FacesContext context, ResponseWriter writer, UIInput component) throws IOException { String tagName = getTagName(); writer.startElement(tagName, component); //$NON-NLS-1$ String type = getInputType(); if(StringUtil.isNotEmpty(type)) { writer.writeAttribute("type", type, null); //$NON-NLS-1$ //$NON-NLS-2$ } } protected void endTag(FacesContext context, ResponseWriter writer, UIInput component) throws IOException { String tagName = getTagName(); writer.endElement(tagName); } protected void writeValueAttribute(FacesContext context, UIInput component, ResponseWriter writer, String currentValue) throws IOException { if(StringUtil.isNotEmpty(currentValue)) { writer.writeAttribute("value", currentValue, "value"); //$NON-NLS-1$ //$NON-NLS-2$ } } protected void renderTagBody(FacesContext context, UIInput component, ResponseWriter writer, String currentValue) throws IOException { // Nothing } protected String getTagName() { return "input"; // $NON-NLS-1$ } protected String getNameAttribute(FacesContext context, UIComponent component) { return component.getClientId(context); } protected String getInputType() { return null; } protected void renderJavaScriptBinding(FacesContext context, ResponseWriter writer, UIInput component) { } protected String writeDojoAttributes(FacesContext context, FacesDojoComponent dojoComponent) throws IOException { UIViewRootEx viewEx = (UIViewRootEx)context.getViewRoot(); String dojoType = dojoComponent.getDojoType(); if(StringUtil.isEmpty(dojoType)) { dojoType = getDefaultDojoType(context, dojoComponent); // If the resources for the dojo type haven't been emitted yet, then do it // Not that the XPages runtime ensures that the resources are not emitted multiple times // This is simply an optimization if(shouldWriteModule(context, viewEx, dojoComponent, dojoType)) { writeDefaultDojoModule(context, viewEx, dojoComponent, dojoType); } } else { if(shouldWriteModule(context, viewEx, dojoComponent, dojoType)) { writeDojoModule(context, viewEx, dojoComponent, dojoType); } } Map<String,String> attrs = DojoRendererUtil.createMap(context); // Compose the list of attributes from the list of dojo attributes DojoRendererUtil.getDojoAttributeMap(dojoComponent,attrs); // Add the attributes specific to this control initDojoAttributes(context, dojoComponent, attrs); // And generate them DojoRendererUtil.writeDojoHtmlAttributes(context,(UIComponent)dojoComponent,dojoType,attrs); return dojoType; } protected boolean shouldWriteModule(FacesContext context, UIViewRootEx viewEx, FacesDojoComponent dojoComponent, String dojoType) { if (StringUtil.isNotEmpty(dojoType)) { if(viewEx.getEncodeProperty(dojoType)==null) { viewEx.putEncodeProperty(dojoType, Boolean.TRUE); return true; } } return false; } protected void writeDojoModule(FacesContext context, UIViewRootEx viewEx, FacesDojoComponent dojoComponent, String dojoType) { } protected void writeDefaultDojoModule(FacesContext context, UIViewRootEx viewEx, FacesDojoComponent dojoComponent, String dojoType) { // Add the default modules DojoModuleResource module = getDefaultDojoModule(context, dojoComponent); if(module!=null) { ExtLibResources.addEncodeResource(viewEx, module); } // Add the extra resources Resource[] res = getExtraResources(context, dojoComponent); if(res!=null) { for(int i=0; i<res.length; i++) { ExtLibResources.addEncodeResource(viewEx, res[i]); } } } protected abstract String getDefaultDojoType(FacesContext context, FacesDojoComponent component); protected abstract DojoModuleResource getDefaultDojoModule(FacesContext context, FacesDojoComponent component); protected Resource[] getExtraResources(FacesContext context, FacesDojoComponent component) { // None by default... return null; } protected boolean isRequirable() { return true; } protected void initDojoAttributes(FacesContext context, FacesDojoComponent dojoComponent, Map<String,String> attrs) throws IOException { if(dojoComponent instanceof UIDojoFormWidgetBase) { UIDojoFormWidgetBase c = (UIDojoFormWidgetBase)dojoComponent; DojoRendererUtil.addDojoHtmlAttributes(attrs,"tooltip",c.getTooltip()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"dir",c.getDir()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"dragRestriction",c.isDragRestriction()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"group",c.getGroup()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"lang",c.getLang()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"style",c.getStyle()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"class",c.getStyleClass()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"title",c.getTitle()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"waiRole",c.getWaiRole()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"waiState",c.getWaiState()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onBlur",c.getOnBlur()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onChange",c.getOnChange()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onClick",c.getOnClick()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onClose",c.getOnClose()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onDblClick",c.getOnDblClick()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onFocus",c.getOnFocus()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onKeyDown",c.getOnKeyDown()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onKeyPress",c.getOnKeyPress()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onKeyUp",c.getOnKeyUp()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onMouseDown",c.getOnMouseDown()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onMouseEnter",c.getOnMouseEnter()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onMouseLeave",c.getOnMouseLeave()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onMouseMove",c.getOnMouseMove()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onMouseOut",c.getOnMouseOut()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onMouseOver",c.getOnMouseOver()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"onMouseUp",c.getOnMouseUp()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"alt",c.getAlt()); // $NON-NLS-1$ if( null == getInputType() ){ DojoRendererUtil.addDojoHtmlAttributes(attrs,"type",c.getType()); // $NON-NLS-1$ }// else use the hard-coded Renderer inputType, instead of the type property set in the XPage source // TODO deprecate the unused type properties. DojoRendererUtil.addDojoHtmlAttributes(attrs,"tabIndex",c.getTabIndex()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"disabled",c.isDisabled()); // $NON-NLS-1$ DojoRendererUtil.addDojoHtmlAttributes(attrs,"intermediateChanges",c.isIntermediateChanges()); // $NON-NLS-1$ // aria-required if(isRequirable() && c.isRequired()) { DojoRendererUtil.addDojoHtmlAttributes(attrs,"aria-required","true"); //$NON-NLS-1$ //$NON-NLS-2$ } // aria-invalid if(!c.isValid()) { DojoRendererUtil.addDojoHtmlAttributes(attrs,"aria-invalid","true"); //$NON-NLS-1$ //$NON-NLS-2$ } // Particular case for the read-only attribute // The flag can come from the readOnly property, or the readonly context? if(_isReadOnly(context,c)) { DojoRendererUtil.addDojoHtmlAttributes(attrs,"readOnly",true); // $NON-NLS-1$ } } } }