All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.trinidad.webapp.UIXComponentELTag Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.myfaces.trinidad.webapp;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;

import java.util.logging.Level;

import javax.el.MethodExpression;
import javax.el.ValueExpression;

import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.webapp.UIComponentClassicTagBase;
import javax.faces.webapp.UIComponentELTag;

import javax.servlet.jsp.JspException;

import org.apache.myfaces.trinidad.bean.FacesBean;
import org.apache.myfaces.trinidad.bean.PropertyKey;
import org.apache.myfaces.trinidad.component.UIXComponent;
import org.apache.myfaces.trinidad.context.RequestContext;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.util.ComponentUtils;


/**
 * Subclass of UIComponentTag to add convenience methods,
 * and optimize where appropriate.
 */
abstract public class UIXComponentELTag extends UIComponentELTag
{
  /**
   * @see #checkChildTagExecution
   */
  protected enum CheckExecutionResult
  {
    /** Execute the child tag and its body */
    ACCEPT,

    /** Skip both the creation of the component, and do not execute the child's body */
    REJECT
  }

  public UIXComponentELTag()
  {
  }

  public void setAttributeChangeListener(MethodExpression attributeChangeListener)
  {
    _attributeChangeListener = attributeChangeListener;
  }

  @Override
  public int doStartTag() throws JspException
  {
    FacesContext context = getFacesContext();
    Map reqMap = context.getExternalContext().getRequestMap();

    Map facesContextAttributes = getFacesContext().getAttributes();

    // Only support skipping the body of a tag when not iterating. This is due to the fact that
    // skipping tag execution usually relies on component attribute state. Since stamping causes
    // component state to change per-stamp, it is not reliable enough to try to determine if
    // a component tag should process its body based on the non-stamped component state. As such,
    // we always execute the tag body when inside a stamping component tag
    if (!_isProcessingStampingComponentTag(facesContextAttributes))
    {
      UIComponentClassicTagBase parentTagBase = getParentUIComponentClassicTagBase(pageContext);
      if (parentTagBase instanceof UIXComponentELTag)
      {
        UIXComponentELTag parentTag = (UIXComponentELTag)parentTagBase;
        String facetName = getFacetName();

        // Check if the component should be created
        if (parentTag.checkChildTagExecution(this, facetName) ==
          CheckExecutionResult.REJECT)
        {
          _skipEndTagSuperCall = true;
          return SKIP_BODY;
        }
      }
    }

    _skipEndTagSuperCall = false;

    int retVal;
    try
    {
      retVal = super.doStartTag();
    }
    catch (RuntimeException rte)
    {
      _logSevereTagProcessingError(context, rte);
      throw rte;
    }
    catch (JspException jspe)
    {
      _logSevereTagProcessingError(context, jspe);
      throw jspe;
    }

    // There could have been some validation error during property setting
    // on the bean, this is the closest opportunity to burst out.
    if (_validationError != null)
      throw new JspException(_validationError);

    _checkStartingStampingTag(facesContextAttributes);

    return retVal;
  }

  @Override
  public int doEndTag()
    throws JspException
  {
    if (_skipEndTagSuperCall)
    {
      _skipEndTagSuperCall = false;
      return EVAL_PAGE;
    }
    else
    {
      _checkEndingStampingTag();
      return super.doEndTag();
    }
  }

  /**
   * Check if a tag that creates components that stamp their children (tag that creates a component
   * that stamps out its contents, potentially rendering the children multiple times)
   * is currently executing (between doStartTag and doEndTag).
   */
  protected boolean isProcessingStampingComponentTag()
  {
    return _isProcessingStampingComponentTag(getFacesContext().getAttributes());
  }

  /**
   * Check if this tag is a stamping tag (tag that creates a component that stamps out its
   * contents, potentially rendering the children multiple times).
   */
  protected boolean isStampingTag()
  {
    return false;
  }

  /**
   * Check if a child component tag should execute or not. Allows the parent tag to control if
   * child components are created for optimization purposes. Only called if the current tag is
   * not in a stamping tag.
   * 

* Called from the doStartTag of the child tag to see if the parent tag wishes to prevent * the execution of the child tag. This is called before the child tag creates its component. *

*

* This may be overridden by a tag to check if a child tag's body should be executed. The * framework will call this method when the child tag is executing. *

*

* If inside of a stamping container this code is not executed as component state * may change per stamp and therefore the tag will not have access to that state since the * component does not stamp during tag execution. Therefore, it is best to avoid trying to * defer child execution when component state is not known. *

*

* This method is called by the framework where the {@link #checkChildTagExecution(UIComponent)} * function is called by sub-classes of {@link UIXComponentELTag}. *

* * @param childTag The child tag * @param facetName The facet, if any, for the child tag * @return if the child tag body should be executed or not */ protected CheckExecutionResult checkChildTagExecution( @SuppressWarnings("unused") UIComponentELTag childTag, @SuppressWarnings("unused") String facetName) { return CheckExecutionResult.ACCEPT; } /** * When within a component binding context, the component bindings (stored in backing bean) * will be cleared so a new component instance can be created. * @param context FacesContext instance * @param newId id for the component */ @Override protected UIComponent createComponent(FacesContext context, String newId) throws JspException { UIComponent component = null; if (RequestContext.isInComponentBindingContext(context) && hasBinding()) { // null out component in binding; this forces a new component to be created. ValueExpression binding = _getBinding(); binding.setValue(getELContext(), null); } component = super.createComponent(context, newId); // if the component was pulled out of a component binding during createComponent() it is likely // to be attached to some component in the tree - and thus the (severe) error is justified if (component != null && component.getParent() != null) _logStaleParent(context, component, component.getParent()); return component; } @Override public void release() { this._binding = null; super.release(); } /** * Allows a child tag to check if its children should be executed based on the grand-parent. * Used for components where a parent-child relationship has been established. For example, * allows the show detail item to ask the parent panelTabbed tag if the show detail item should * allow children components of the show detail item to be created. *

* This method is not called by the framework, but may be called by a child tag on the * parent tag. The parent tag should override this method to determine if a child tag should * execute its children tags. In the above example, the show detail item tag should call this * method on the panelTabbed tag to see if the show detail item's children tags should be * executed. *

*

* This method is called by sub-classes and is not called by the framework at any time. * The framework will invoke the {@link #checkChildTagExecution(UIComponentELTag, String)} * method during the execution of the start tag. *

* * @param childComponent The child component * @return if the children tags of the child component should execute their tag bodies */ public CheckExecutionResult checkChildTagExecution( @SuppressWarnings("unused") UIComponent childComponent) { return CheckExecutionResult.ACCEPT; } protected final void setProperties(UIComponent component) { if (component instanceof UIViewRoot) { throw new IllegalStateException( " was not present on this page; tag " + this + "encountered without an being processed."); } super.setProperties(component); UIXComponent uixComponent = (UIXComponent) component; if (_attributeChangeListener != null) { uixComponent.setAttributeChangeListener(_attributeChangeListener); } setProperties(uixComponent.getFacesBean()); } protected void setProperty( FacesBean bean, PropertyKey key, ValueExpression expression) { if (expression == null) return; if (expression.isLiteralText()) { bean.setProperty(key, expression.getValue(FacesContext.getCurrentInstance().getELContext())); } else { bean.setValueExpression(key, expression); } } /** * Set a property of type java.lang.String[]. If the value * is an EL expression, it will be stored as a ValueExpression. * Otherwise, it will parsed as a whitespace-separated series * of strings. * Null values are ignored. */ protected void setStringArrayProperty( FacesBean bean, PropertyKey key, ValueExpression expression) { if (expression == null) return; if (expression.isLiteralText()) { bean.setProperty(key, TagUtils.parseNameTokens( expression.getValue(FacesContext.getCurrentInstance().getELContext()))); } else { // Support coercion from a string to a string array expression = new StringArrayValueExpression(expression); bean.setValueExpression(key, expression); } } /** * Set a property of type java.util.List<java.lang.String>. If the value * is an EL expression, it will be stored as a ValueExpression. * Otherwise, it will parsed as a whitespace-separated series * of strings. * Null values are ignored. */ protected void setStringListProperty( FacesBean bean, PropertyKey key, ValueExpression expression) { if (expression == null) return; if (expression.isLiteralText()) { bean.setProperty(key, TagUtils.parseNameTokensAsList( expression.getValue(FacesContext.getCurrentInstance().getELContext()))); } else { bean.setValueExpression(key, expression); } } /** * Set a property of type java.util.Set<java.lang.String>. If the value * is an EL expression, it will be stored as a ValueExpression. * Otherwise, it will parsed as a whitespace-separated series * of strings. * Null values are ignored. */ protected void setStringSetProperty( FacesBean bean, PropertyKey key, ValueExpression expression) { if (expression == null) return; if (expression.isLiteralText()) { bean.setProperty(key, TagUtils.parseNameTokensAsSet( expression.getValue(FacesContext.getCurrentInstance().getELContext()))); } else { bean.setValueExpression(key, expression); } } /** * Set a property of type java.lang.Number. If the value * is an EL expression, it will be stored as a ValueBinding. * Otherwise, it will parsed with Integer.valueOf() or Double.valueOf() . * Null values are ignored. */ protected void setNumberProperty( FacesBean bean, PropertyKey key, ValueExpression expression) { if (expression == null) return; if (expression.isLiteralText()) { Object value = expression.getValue(FacesContext.getCurrentInstance().getELContext()); if (value != null) { if (value instanceof Number) { bean.setProperty(key, value); } else { String valueStr = value.toString(); if(valueStr.indexOf('.') == -1) bean.setProperty(key, Integer.valueOf(valueStr)); else bean.setProperty(key, Double.valueOf(valueStr)); } } } else { bean.setValueExpression(key, expression); } } /** * Set a property of type int[]. If the value * is an EL expression, it will be stored as a ValueExpression. * Otherwise, it will parsed as a whitespace-separated series * of ints. * Null values are ignored. */ protected void setIntArrayProperty( FacesBean bean, PropertyKey key, ValueExpression expression) { if (expression == null) return; if (expression.isLiteralText()) { Object value = expression.getValue(FacesContext.getCurrentInstance().getELContext()); if (value != null) { String[] strings = TagUtils.parseNameTokens(value); final int[] ints; if (strings != null) { try { ints = new int[strings.length]; for(int i=0; i facesContextAttributes) { if (isStampingTag()) { AtomicInteger count = (AtomicInteger)facesContextAttributes.get(_STAMPING_COUNT_KEY); if (count == null) { // Use an atomic integer here so that we can increment and decrement the value without // having to store a new integer value into the map each time. This avoids the overhead // of the map.put operation. facesContextAttributes.put(_STAMPING_COUNT_KEY, new AtomicInteger(1)); } else { // Only used on one thread, so use the safe methods for performance (only using the // atomic integer for higher performance than boxing int to Integer) count.set(count.get() + 1); } } } /** * Decrement the counter if this tag returns true from {@link #isStampingTag()}. Allows the code * to check if we are outside of all stamping component tags. * * @see #_checkStartingStampingTag(Map) */ private void _checkEndingStampingTag() { if (isStampingTag()) { Map facesContextAttributes = getFacesContext().getAttributes(); AtomicInteger count = (AtomicInteger)facesContextAttributes.get(_STAMPING_COUNT_KEY); if (count.get() == 1) { facesContextAttributes.remove(_STAMPING_COUNT_KEY); } else { count.set(count.get() - 1); } } } /** * Check if an iterating tag is currently executing (between doStartTag and doEndTag). * This is used to always process the tag body when stamping. This prevents issues when component * state is used to determine if a tag should execute and that state differs per stamp. Since * stamp state is not available during JSP tag execution, it is best to always execute the tag * body when inside a stamping tag. */ private boolean _isProcessingStampingComponentTag( Map facesContextAttributes) { return facesContextAttributes.containsKey(_STAMPING_COUNT_KEY); } /** * Logs a severe warning when an exception is thrown during component tag processing. * @param methodName name of the method throwing the exception * @param e Throwable */ private void _logSevereTagProcessingError(FacesContext context, Throwable e) { UIViewRoot viewRoot = context.getViewRoot(); UIComponent component = this.getComponentInstance(); String scopedId = _getScopedId(component, viewRoot); String parentScopedId = _getParentScopedId(viewRoot); String message = _LOG.getMessage("ERROR_PARSING_COMPONENT_TAG", new Object[] {scopedId, parentScopedId}); _LOG.severe(message, e); } private String _getScopedId(UIComponent component, UIViewRoot viewRoot) { if (component == null) { // use tag id if component was not created return this.getId(); } else { return ComponentUtils.getScopedIdForComponent(component, viewRoot); } } /** * Logs a message when a stale component is detected during create component. * @param context FacesContext * @param child the UIComponent being created * @param oldParent the parent UIComponent */ private void _logStaleParent(FacesContext context, UIComponent child, UIComponent oldParent) { _logStaleParentAtLevel(context, child, oldParent, Level.INFO); } /** * Logs a message at a specified level when a stale component is detected during create component. * @param context FacesContext * @param child the UIComponent being created * @param oldParent the parent UIComponent * @param level the level at which to log the message */ private void _logStaleParentAtLevel(FacesContext context, UIComponent child, UIComponent oldParent, Level level) { if (_LOG.isLoggable(level)) { UIViewRoot viewRoot = context.getViewRoot(); String scopedId = ComponentUtils.getScopedIdForComponent(child, viewRoot); String oldParentScopedId = ComponentUtils.getScopedIdForComponent(oldParent, viewRoot); String newParentScopedId = _getParentScopedId(viewRoot); String bindingEL = _getBindingExpression(); _LOG.log(level, "ERROR_CREATE_COMPONENT_STALE", new Object[] {scopedId, oldParentScopedId, newParentScopedId, bindingEL}); } } /** * Returns the expression set for the component's binding attribute or null. * @return String */ private String _getBindingExpression() { if (_getBinding() != null) { if (_bindingExpression == null) _bindingExpression = _getBinding().getExpressionString(); return _bindingExpression; } return null; } /** * Gets the scopedId of the parent component of the current tag's parent. * @param viewRoot UIViewRoot instance * @return String */ private String _getParentScopedId(UIViewRoot viewRoot) { UIComponentClassicTagBase parentTag = UIComponentClassicTagBase.getParentUIComponentClassicTagBase(pageContext); if (parentTag != null) { UIComponent parent = parentTag.getComponentInstance(); return ComponentUtils.getScopedIdForComponent(parent, viewRoot); } return null; } /** * Parse a string into a java.util.Date object. The * string must be in ISO 9601 format (yyyy-MM-dd). */ static private final Date _parseISODate(Object o) { if (o == null) return null; String stringValue = o.toString(); try { return _getDateFormat().parse(stringValue); } catch (ParseException pe) { _LOG.info("CANNOT_PARSE_VALUE_INTO_DATE", stringValue); return null; } } // We rely strictly on ISO 8601 formats private static DateFormat _getDateFormat() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); TimeZone tz = RequestContext.getCurrentInstance().getTimeZone(); if (tz != null) sdf.setTimeZone(tz); return sdf; } private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(UIXComponentELTag.class); /** @deprecated Not used any more in the session state manager */ @Deprecated public static final String DOCUMENT_CREATED_KEY = "org.apache.myfaces.trinidad.DOCUMENTCREATED"; private final static String _STAMPING_COUNT_KEY = UIXComponentELTag.class.getName() + ".STAMPING"; private MethodExpression _attributeChangeListener; private String _validationError; private String _bindingExpression; private boolean _skipEndTagSuperCall = false; /* *

The value binding expression (if any) used to wire up this component * to a {@link UIComponent} property of a JavaBean class.

*/ private ValueExpression _binding; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy