Save This Page
Home » mojarra-1.2_09-b02-FCS-source » com.sun.faces.renderkit.html_basic » [javadoc | source]
    1   /*
    2    * $Id: HtmlBasicRenderer.java,v 1.121.4.3 2007/11/29 00:35:49 youngm Exp $
    3    */
    4   
    5   /*
    6    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    7    * 
    8    * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
    9    * 
   10    * The contents of this file are subject to the terms of either the GNU
   11    * General Public License Version 2 only ("GPL") or the Common Development
   12    * and Distribution License("CDDL") (collectively, the "License").  You
   13    * may not use this file except in compliance with the License. You can obtain
   14    * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
   15    * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
   16    * language governing permissions and limitations under the License.
   17    * 
   18    * When distributing the software, include this License Header Notice in each
   19    * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
   20    * Sun designates this particular file as subject to the "Classpath" exception
   21    * as provided by Sun in the GPL Version 2 section of the License file that
   22    * accompanied this code.  If applicable, add the following below the License
   23    * Header, with the fields enclosed by brackets [] replaced by your own
   24    * identifying information: "Portions Copyrighted [year]
   25    * [name of copyright owner]"
   26    * 
   27    * Contributor(s):
   28    * 
   29    * If you wish your version of this file to be governed by only the CDDL or
   30    * only the GPL Version 2, indicate your decision by adding "[Contributor]
   31    * elects to include this software in this distribution under the [CDDL or GPL
   32    * Version 2] license."  If you don't indicate a single choice of license, a
   33    * recipient has the option to distribute your version of this file under
   34    * either the CDDL, the GPL Version 2 or to extend the choice of license to
   35    * its licensees as provided above.  However, if you add GPL Version 2 code
   36    * and therefore, elected the GPL Version 2 license, then the option applies
   37    * only if the new code is made subject to such option by the copyright
   38    * holder.
   39    */
   40   
   41   // HtmlBasicRenderer.java
   42   
   43   package com.sun.faces.renderkit.html_basic;
   44   
   45   import java.io.IOException;
   46   import java.util.ArrayList;
   47   import java.util.Collections;
   48   import java.util.Iterator;
   49   import java.util.List;
   50   import java.util.Map;
   51   import java.util.logging.Level;
   52   import java.util.logging.Logger;
   53   
   54   import javax.faces.component.NamingContainer;
   55   import javax.faces.component.UIComponent;
   56   import javax.faces.component.UIInput;
   57   import javax.faces.component.UIParameter;
   58   import javax.faces.component.UIViewRoot;
   59   import javax.faces.component.ValueHolder;
   60   import javax.faces.context.FacesContext;
   61   import javax.faces.context.ResponseWriter;
   62   import javax.faces.convert.Converter;
   63   import javax.faces.convert.ConverterException;
   64   import javax.faces.render.Renderer;
   65   
   66   import com.sun.faces.util.FacesLogger;
   67   import com.sun.faces.util.MessageUtils;
   68   import com.sun.faces.util.Util;
   69   
   70   /**
   71    * <B>HtmlBasicRenderer</B> is a base class for implementing renderers
   72    * for HtmlBasicRenderKit.
   73    */
   74   
   75   public abstract class HtmlBasicRenderer extends Renderer {   
   76   
   77   
   78       // Log instance for this class
   79       protected static final Logger logger = FacesLogger.RENDERKIT.getLogger();
   80   
   81       private static final Param[] EMPTY_PARAMS = new Param[0];
   82   
   83       // ------------------------------------------------------------ Constructors
   84   
   85   
   86       public HtmlBasicRenderer() {
   87   
   88           super();
   89   
   90       }
   91   
   92       // ---------------------------------------------------------- Public Methods
   93   
   94   
   95       @Override
   96       public String convertClientId(FacesContext context, String clientId) {
   97   
   98           return clientId;
   99   
  100       }
  101   
  102   
  103       @Override
  104       public void decode(FacesContext context, UIComponent component) {
  105   
  106           rendererParamsNotNull(context, component);
  107   
  108           if (!(component instanceof UIInput)) {
  109               // decode needs to be invoked only for components that are
  110               // instances or subclasses of UIInput.
  111               if (logger.isLoggable(Level.FINE)) {
  112                   logger.log(Level.FINE,
  113                              "No decoding necessary since the component {0} is not an instance or a sub class of UIInput",
  114                              component.getId());
  115               }
  116               return;
  117           }
  118   
  119           if (!shouldDecode(component)) {
  120               return;
  121           }
  122   
  123           String clientId = component.getClientId(context);
  124           assert(clientId != null);
  125           Map<String, String> requestMap =
  126                 context.getExternalContext().getRequestParameterMap();
  127           // Don't overwrite the value unless you have to!
  128           String newValue = requestMap.get(clientId);
  129           if (newValue != null) {
  130               setSubmittedValue(component, newValue);
  131               if (logger.isLoggable(Level.FINE)) {
  132                   logger.log(Level.FINE,
  133                              "new value after decoding {0}",
  134                              newValue);
  135               }
  136           }
  137   
  138       }
  139   
  140   
  141       @Override
  142       public void encodeEnd(FacesContext context, UIComponent component)
  143             throws IOException {
  144   
  145          rendererParamsNotNull(context, component);
  146   
  147           if (!shouldEncode(component)) {
  148               return;
  149           }
  150   
  151           ResponseWriter writer = context.getResponseWriter();
  152           assert(writer != null);
  153   
  154           String currentValue = getCurrentValue(context, component);
  155           if (logger.isLoggable(Level.FINE)) {
  156               logger.log(Level.FINE,
  157                          "Value to be rendered {0}",
  158                          currentValue);
  159           }
  160           getEndTextToRender(context, component, currentValue);
  161   
  162       }
  163   
  164   
  165       @Override
  166       public boolean getRendersChildren() {
  167   
  168           return true;
  169   
  170       }
  171   
  172       // ------------------------------------------------------- Protected Methods
  173   
  174   
  175       /**
  176        * <p>Conditionally augment an id-reference value.</p>
  177        * <p>If the <code>forValue</code> doesn't already include a generated
  178        * suffix, but the id of the <code>fromComponent</code> does include a
  179        * generated suffix, then append the suffix from the
  180        * <code>fromComponent</code> to the <code>forValue</code>.
  181        * Otherwise just return the <code>forValue</code> as is.</p>
  182        *
  183        * @param forValue      - the basic id-reference value.
  184        * @param fromComponent - the component that holds the
  185        *                      code>forValue</code>.
  186        *
  187        * @return the (possibly augmented) <code>forValue<code>.
  188        */
  189       protected String augmentIdReference(String forValue,
  190                                           UIComponent fromComponent) {
  191   
  192           int forSuffix = forValue.lastIndexOf(UIViewRoot.UNIQUE_ID_PREFIX);
  193           if (forSuffix <= 0) {
  194               // if the for-value doesn't already have a suffix present
  195               String id = fromComponent.getId();
  196               int idSuffix = id.lastIndexOf(UIViewRoot.UNIQUE_ID_PREFIX);
  197               if (idSuffix > 0) {
  198                   // but the component's own id does have a suffix
  199                   if (logger.isLoggable(Level.FINE)) {
  200                       logger.fine("Augmenting for attribute with " +
  201                                   id.substring(idSuffix) +
  202                                   " suffix from Id attribute");
  203                   }
  204                   forValue += id.substring(idSuffix);
  205               }
  206           }
  207           return forValue;
  208   
  209       }
  210   
  211   
  212       /**
  213        * <p>Render nested child components by invoking the encode methods
  214        * on those components, but only when the <code>rendered</code>
  215        * property is <code>true</code>.</p>
  216        *
  217        * @param context FacesContext for the current request
  218        * @param component the component to recursively encode
  219        *
  220        * @throws IOException if an error occurrs during the encode process
  221        */
  222       protected void encodeRecursive(FacesContext context, UIComponent component)
  223             throws IOException {
  224   
  225           // suppress rendering if "rendered" property on the component is
  226           // false.
  227           if (!component.isRendered()) {
  228               return;
  229           }
  230   
  231           // Render this component and its children recursively
  232           component.encodeBegin(context);
  233           if (component.getRendersChildren()) {
  234               component.encodeChildren(context);
  235           } else {
  236               Iterator<UIComponent> kids = getChildren(component);
  237               while (kids.hasNext()) {
  238                   UIComponent kid = kids.next();
  239                   encodeRecursive(context, kid);
  240               }
  241           }
  242           component.encodeEnd(context);
  243   
  244       }
  245   
  246   
  247       /**
  248        * @param component <code>UIComponent</code> for which to extract children
  249        *
  250        * @return an Iterator over the children of the specified
  251        *  component, selecting only those that have a
  252        *  <code>rendered</code> property of <code>true</code>.
  253        */
  254       protected Iterator<UIComponent> getChildren(UIComponent component) {
  255   
  256           int childCount = component.getChildCount();
  257           if (childCount > 0) {
  258               return component.getChildren().iterator();
  259           } else {
  260               return Collections.<UIComponent>emptyList().iterator();
  261           }
  262   
  263       }
  264   
  265   
  266       /**
  267        * @param context the FacesContext for the current request
  268        * @param component the UIComponent whose value we're interested in
  269        *
  270        * @return the value to be rendered and formats it if required. Sets to
  271        *  empty string if value is null.
  272        */
  273       protected String getCurrentValue(FacesContext context,
  274                                        UIComponent component) {
  275   
  276           if (component instanceof UIInput) {
  277               Object submittedValue = ((UIInput) component).getSubmittedValue();
  278               if (submittedValue != null) {
  279                   return (String) submittedValue;
  280               }
  281           }
  282   
  283           String currentValue = null;
  284           Object currentObj = getValue(component);
  285           if (currentObj != null) {
  286               currentValue = getFormattedValue(context, component, currentObj);
  287           }
  288           return currentValue;
  289   
  290       }
  291   
  292   
  293       /**
  294        * Renderers override this method to write appropriate HTML content into
  295        * the buffer.
  296        *
  297        * @param context the FacesContext for the current request
  298        * @param component the UIComponent of interest
  299        * @param currentValue <code>component</code>'s current value
  300        *
  301        * @throws IOException if an error occurs rendering the text
  302        */
  303       protected void getEndTextToRender(FacesContext context,
  304                                         UIComponent component,
  305                                         String currentValue) throws IOException {
  306   
  307           // no-op unless overridden
  308   
  309       }
  310   
  311   
  312       /**
  313        * @param component Component from which to return a facet
  314        * @param name      Name of the desired facet
  315        *
  316        * @return the specified facet from the specified component, but
  317        *  <strong>only</strong> if its <code>rendered</code> property is
  318        *  set to <code>true</code>.
  319        */
  320       protected UIComponent getFacet(UIComponent component, String name) {
  321   
  322           UIComponent facet = null;
  323           if (component.getFacetCount() > 0) {
  324               facet = component.getFacet(name);
  325               if ((facet != null) && !facet.isRendered()) {
  326                   facet = null;
  327               }
  328           }
  329           return (facet);
  330   
  331       }
  332   
  333   
  334       /**
  335        * Locates the component identified by <code>forComponent</code>
  336        *
  337        * @param context the FacesContext for the current request
  338        * @param forComponent - the component to search for
  339        * @param component    - the starting point in which to begin the search
  340        *
  341        * @return the component with the the <code>id</code that matches
  342        *         <code>forComponent</code> otheriwse null if no match is found.
  343        */
  344       protected UIComponent getForComponent(FacesContext context,
  345                                             String forComponent,
  346                                             UIComponent component) {
  347   
  348           if (null == forComponent || forComponent.length() == 0) {
  349               return null;
  350           }
  351   
  352           UIComponent result = null;
  353           UIComponent currentParent = component;
  354           try {
  355               // Check the naming container of the current 
  356               // component for component identified by
  357               // 'forComponent'
  358               while (currentParent != null) {
  359                   // If the current component is a NamingContainer,
  360                   // see if it contains what we're looking for.
  361                   result = currentParent.findComponent(forComponent);
  362                   if (result != null) {
  363                       break;
  364                   }
  365                   // if not, start checking further up in the view
  366                   currentParent = currentParent.getParent();
  367               }
  368   
  369               // no hit from above, scan for a NamingContainer
  370               // that contains the component we're looking for from the root.    
  371               if (result == null) {
  372                   result =
  373                         findUIComponentBelow(context.getViewRoot(), forComponent);
  374               }
  375           } catch (Exception e) {
  376               // ignore - log the warning
  377           }
  378           // log a message if we were unable to find the specified
  379           // component (probably a misconfigured 'for' attribute
  380           if (result == null) {
  381               if (logger.isLoggable(Level.WARNING)) {
  382                   logger.warning(MessageUtils.getExceptionMessageString(
  383                         MessageUtils.COMPONENT_NOT_FOUND_IN_VIEW_WARNING_ID,
  384                         forComponent));
  385               }
  386           }
  387           return result;
  388   
  389       }
  390   
  391       /**
  392        * Overloads getFormattedValue to take a advantage of a previously
  393        * obtained converter.
  394        * @param context the FacesContext for the current request
  395        * @param component UIComponent of interest
  396        * @param currentValue the current value of <code>component</code>
  397        * @param converter the component's converter
  398        * @return the currentValue after any associated Converter has been
  399        *  applied
  400        *
  401        * @throws ConverterException if the value cannot be converted
  402        */
  403       protected String getFormattedValue(FacesContext context,
  404                                          UIComponent component,
  405                                          Object currentValue,
  406                                          Converter converter)
  407             throws ConverterException {
  408   
  409           // formatting is supported only for components that support
  410           // converting value attributes.
  411           if (!(component instanceof ValueHolder)) {
  412               if (currentValue != null) {
  413                   return currentValue.toString();
  414               }
  415               return null;
  416           }
  417   
  418           if (converter == null) {
  419               // If there is a converter attribute, use it to to ask application
  420               // instance for a converter with this identifer.
  421               converter = ((ValueHolder) component).getConverter();
  422           }
  423   
  424           if (converter == null) {
  425               // if value is null and no converter attribute is specified, then
  426               // return a zero length String.
  427               if(currentValue == null) {
  428           	return "";
  429               }
  430               // Do not look for "by-type" converters for Strings
  431               if (currentValue instanceof String) {
  432                   return (String) currentValue;
  433               }
  434   
  435               // if converter attribute set, try to acquire a converter
  436               // using its class type.
  437   
  438               Class converterType = currentValue.getClass();
  439               converter = Util.getConverterForClass(converterType, context);
  440   
  441               // if there is no default converter available for this identifier,
  442               // assume the model type to be String.
  443               if (converter == null) {
  444                   return currentValue.toString();
  445               }
  446           }
  447   
  448           return converter.getAsString(context, component, currentValue);
  449       }
  450   
  451   
  452       /**
  453        * @param context the FacesContext for the current request
  454        * @param component UIComponent of interest
  455        * @param currentValue the current value of <code>component</code>
  456        *
  457        * @return the currentValue after any associated Converter has been
  458        *  applied
  459        *
  460        * @throws ConverterException if the value cannot be converted
  461        */
  462       protected String getFormattedValue(FacesContext context,
  463                                          UIComponent component,
  464                                          Object currentValue)
  465             throws ConverterException {
  466   
  467           return getFormattedValue(context, component, currentValue, null);
  468   
  469       }
  470   
  471   
  472       protected Iterator getMessageIter(FacesContext context,
  473                                         String forComponent,
  474                                         UIComponent component) {
  475   
  476           Iterator messageIter;
  477           // Attempt to use the "for" attribute to locate 
  478           // messages.  Three possible scenarios here:
  479           // 1. valid "for" attribute - messages returned
  480           //    for valid component identified by "for" expression.
  481           // 2. zero length "for" expression - global errors
  482           //    not associated with any component returned
  483           // 3. no "for" expression - all messages returned.
  484           if (null != forComponent) {
  485               if (forComponent.length() == 0) {
  486                   messageIter = context.getMessages(null);
  487               } else {
  488                   UIComponent result = getForComponent(context, forComponent,
  489                                                        component);
  490                   if (result == null) {
  491                       messageIter = Collections.EMPTY_LIST.iterator();
  492                   } else {
  493                       messageIter =
  494                             context.getMessages(result.getClientId(context));
  495                   }
  496               }
  497           } else {
  498               messageIter = context.getMessages();
  499           }
  500           return messageIter;
  501   
  502       }
  503   
  504   
  505       /**
  506        * @param command the command which may have parameters
  507        *
  508        * @return an array of parameters
  509        */
  510       protected Param[] getParamList(UIComponent command) {
  511   
  512           if (command.getChildCount() > 0) {
  513               ArrayList<Param> parameterList = new ArrayList<Param>();
  514   
  515               for (UIComponent kid : command.getChildren()) {
  516                   if (kid instanceof UIParameter) {
  517                       UIParameter uiParam = (UIParameter) kid;
  518                       Object value = uiParam.getValue();
  519                       Param param = new Param(uiParam.getName(),
  520                                               (value == null ? null :
  521                                                value.toString()));
  522                       parameterList.add(param);
  523                   }
  524               }
  525               return parameterList.toArray(new Param[parameterList.size()]);
  526           } else {
  527               return EMPTY_PARAMS;
  528           }
  529   
  530   
  531       }
  532   
  533   
  534       protected Object getValue(UIComponent component) {
  535   
  536           // Make sure this method isn't being called except 
  537           // from subclasses that override getValue()!
  538           throw new UnsupportedOperationException();
  539   
  540       }
  541   
  542   
  543       /**
  544        * Renderers override this method to store the previous value
  545        * of the associated component.
  546        *
  547        * @param component the target component to which the submitted value
  548        *  will be set
  549        * @param value the value to set
  550        */
  551       protected void setSubmittedValue(UIComponent component, Object value) {
  552   
  553           // no-op unless overridden
  554   
  555       }
  556   
  557   
  558       /**
  559        * @param component the component of interest
  560        *
  561        * @return true if this renderer should render an id attribute.
  562        */
  563       protected boolean shouldWriteIdAttribute(UIComponent component) {
  564   
  565           String id;
  566           return (null != (id = component.getId()) &&
  567                       !id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX));
  568   
  569       }
  570   
  571   
  572       protected String writeIdAttributeIfNecessary(FacesContext context,
  573                                                    ResponseWriter writer,
  574                                                    UIComponent component) {
  575   
  576           String id = null;
  577           if (shouldWriteIdAttribute(component)) {
  578               try {
  579                   writer.writeAttribute("id", id = component.getClientId(context),
  580                                         "id");
  581               } catch (IOException e) {
  582                   if (logger.isLoggable(Level.WARNING)) {
  583                       String message = MessageUtils.getExceptionMessageString
  584                             (MessageUtils.CANT_WRITE_ID_ATTRIBUTE_ERROR_MESSAGE_ID,
  585                              e.getMessage());
  586                       logger.warning(message);
  587                   }
  588               }
  589           }
  590           return id;
  591   
  592       }
  593   
  594       protected void rendererParamsNotNull(FacesContext context,
  595                                            UIComponent component) {
  596   
  597           Util.notNull("context", context);
  598           Util.notNull("component", component);
  599           
  600       }
  601   
  602   
  603       protected boolean shouldEncode(UIComponent component) {
  604   
  605           // suppress rendering if "rendered" property on the component is
  606           // false.
  607           if (!component.isRendered()) {
  608               if (logger.isLoggable(Level.FINE)) {
  609                   logger.log(Level.FINE,
  610                              "End encoding component {0} since rendered attribute is set to false",
  611                              component.getId());
  612               }
  613               return false;
  614           }
  615           return true;
  616   
  617       }
  618   
  619   
  620       protected boolean shouldDecode(UIComponent component) {
  621   
  622           if (Util.componentIsDisabledOrReadonly(component)) {
  623               if (logger.isLoggable(Level.FINE)) {
  624                   logger.log(Level.FINE,
  625                              "No decoding necessary since the component {0} is disabled or read-only",
  626                              component.getId());
  627               }
  628               return false;
  629           }
  630           return true;
  631   
  632       }
  633   
  634       protected boolean shouldEncodeChildren(UIComponent component) {
  635   
  636           // suppress rendering if "rendered" property on the component is
  637           // false.
  638           if (!component.isRendered()) {
  639               if (logger.isLoggable(Level.FINE)) {
  640                   logger.log(Level.FINE,
  641                               "Children of component {0} will not be encoded since this component's rendered attribute is false",
  642                               component.getId());
  643               }
  644               return false;
  645           }
  646           return true;
  647   
  648       }
  649   
  650   
  651       // --------------------------------------------------------- Private Methods
  652   
  653   
  654       /**
  655        * <p>Recursively searches for {@link NamingContainer}s from the
  656        * given start point looking for the component with the <code>id</code>
  657        * specified by <code>forComponent</code>.
  658        *
  659        * @param startPoint   - the starting point in which to begin the search
  660        * @param forComponent - the component to search for
  661        *
  662        * @return the component with the the <code>id</code that matches
  663        *         <code>forComponent</code> otheriwse null if no match is found.
  664        */
  665       private static UIComponent findUIComponentBelow(UIComponent startPoint,
  666                                                       String forComponent) {
  667   
  668           UIComponent retComp = null;
  669           if (startPoint.getChildCount() > 0) {
  670               List<UIComponent> children = startPoint.getChildren();
  671               for (int i = 0, size = children.size(); i < size; i++) {
  672                   UIComponent comp = children.get(i);
  673   
  674                   if (comp instanceof NamingContainer) {
  675                       try {
  676                           retComp = comp.findComponent(forComponent);
  677                       } catch (IllegalArgumentException iae) {
  678                           continue;
  679                       }
  680                   }
  681   
  682                   if (retComp == null) {
  683                       if (comp.getChildCount() > 0) {
  684                           retComp = findUIComponentBelow(comp, forComponent);
  685                       }
  686                   }
  687   
  688                   if (retComp != null) {
  689                       break;
  690                   }
  691               }
  692           }
  693           return retComp;
  694   
  695       }
  696   
  697   
  698       // ----------------------------------------------------------- Inner Classes
  699   
  700   
  701       /**
  702        * <p>Simple class to encapsulate the name and value of a
  703        * <code>UIParameeter</code>.
  704        */
  705       public static class Param {
  706   
  707   
  708           public String name;
  709           public String value;
  710   
  711           
  712           // -------------------------------------------------------- Constructors
  713   
  714   
  715           public Param(String name, String value) {
  716   
  717               this.name = name;
  718               this.value = value;
  719   
  720           }
  721   
  722       }
  723   
  724   } // end of class HtmlBasicRenderer

Save This Page
Home » mojarra-1.2_09-b02-FCS-source » com.sun.faces.renderkit.html_basic » [javadoc | source]