Package org.apache.myfaces.trinidadinternal.renderkit.core.ppr

Source Code of org.apache.myfaces.trinidadinternal.renderkit.core.ppr.PartialPageContextImpl

/*
* 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.trinidadinternal.renderkit.core.ppr;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.faces.component.NamingContainer;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;

import org.apache.myfaces.trinidad.component.UIXComponent;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitHint;
import javax.faces.component.visit.VisitResult;
import org.apache.myfaces.trinidad.context.RequestContext;

import org.apache.myfaces.trinidadinternal.context.RequestContextImpl;
import org.apache.myfaces.trinidad.context.PartialPageContext;

import org.apache.myfaces.trinidad.logging.TrinidadLogger;

/**
* Context object which is used to track the targets of a partial
* page render during the partial page rendering pass.
* Clients never need to explicitly create PartialPageContext
* objects.
* <p>
* During the partial rendering pass, some Renderer implementations
* may modify the set of partial targets that are rendered.
* (For example, the FormRenderer adds a partial target for its
* shared hidden fields if any children of the form are rendered.)
* After the partial render pass, getPartialTargets() can be
* called to determine the actual set of partial targets that were
* rendered.
*
* @version $Name:  $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/renderkit/core/ppr/PartialPageContext.java#0 $) $Date: 10-nov-2005.19:02:58 $
*/
public class PartialPageContextImpl extends PartialPageContext
{
  /**
   * Creates a PartialPageContext to use to render the partial targets with
   * the specified ids.
   */
  public PartialPageContextImpl(
    FacesContext   context,
    RequestContext reqContext)
  {
    _targets = new HashMap<String, Boolean>();
    _renderedTargets = new HashSet<String>();
    _targetIds = new HashSet<String>();

    // Intialize subtreeClientIds collection
    _subtreeClientIds = new HashMap<String, Collection<String>>();
   
    // Pre-allocate the rendered stack
    _currentTargetStack = new Stack<String>();
   
    // Create the VisitContext delegating back to the
    // PartialPageContext
    _visitContext = new PartialPageVisitContext();

    _context = (context != null)
                ? context
                : FacesContext.getCurrentInstance();
   
    if (reqContext instanceof RequestContextImpl)
    {
      // Components may add themselves to the partialTargets list in earlier
      // phases (we don't get here until render response). If so, the IDs have
      // been kept on the RequestContext. We'll grab them now and add them to the
      // target list.
      RequestContextImpl requestContext =
        (RequestContextImpl) reqContext;
      Iterator<String> targetIter = requestContext.getPartialTargets();
      while (targetIter.hasNext())
        addPartialTarget(targetIter.next());
    }
   
    if (_targets.isEmpty())
    {
      _LOG.fine("PPR is about to render without any targets");
    }
   
    // unmodifiable list of targets for use from VisitContext
    _unmodifiableTargets = Collections.unmodifiableSet(_targets.keySet());
  }

  /**
   * Returns the set of partial targets for this rendering pass.
   */
  @Override
  public Iterator<String> getPartialTargets()
  {
    return _targets.keySet().iterator();
  }



  /**
   * Tests whether the specified id is the client id of a UIComponent that
   * should be rendered as part of the partial rendering pass.
   */
  @Override
  public boolean isPartialTarget(String clientId)
  {
    return (clientId != null) && _targets.containsKey(clientId);
  }

  /**
   * <p>
   * Tests whether the specified component id is a component id of a UIComponent that
   * might be rendered in this partial rendering pass.
   * </p>
   * <p>
   * As calculating clientIds is expensive, this method allows a cheap test to reject components
   * that shouldn't be rendered. If this method returns true, a more
   * exact test using <code>isPartialTarget</code> with the desired clientId should be performed.
   * </p>
   * @return <code>true</code> if a component with this id should be rendered.
   * @see #isPartialTarget
   */
  @Override
  public boolean isPossiblePartialTarget(String componentId)
  {
    return (componentId != null) && _targetIds.contains(componentId);
  }

  /**
   * Tests whether the specified partial target has been rendered.
   */
  @Override
  public boolean isPartialTargetRendered(String id)
  {
    return _renderedTargets.contains(id);
  }

  /**
   * Returns <code>true</code> if all of the partial targets have been rendered.
   * @return <code>true</code> if all of the partial targets have been rendered.
   */
  @Override
  public boolean areAllTargetsProcessed()
  {
    return _renderedTargets.size() == _targets.size();
  }

  /**
   * Adds a new partial target to render.
   * <p>
   * This method may be called during the partial rendering pass to
   * add to the set of partial targets, but only if the pass has
   * not yet been completed.  Clients should first check to see
   * whether the partial rendering pass has finished by calling
   * isPartialPassComplete() before calling this method.
   *
   * @param clientId The clientId of the partial target to render
   */
  @Override
  public void addPartialTarget(String clientId)
  {
    _targets.put(clientId, Boolean.FALSE);
   
    int lastFragmentIndex = clientId.lastIndexOf(NamingContainer.SEPARATOR_CHAR);
   
    String id = (lastFragmentIndex != -1)
                  ? clientId.substring(lastFragmentIndex + 1)
                  : clientId;
   
    _targetIds.add(id);

    // Update the subtree ids collection
    _addSubtreeClientId(clientId);
  }

  /**
   * Returns true if we are inside of a partial target.
   */
  @Override
  public boolean isInsidePartialTarget()
  {
    return _getCurrentPartialTarget() != null;
  }

  /**
   * Adds a partial target that has already been rendered;  this
   * is needed if the "clientId" of a component does not match
   * up to the top element (or elements).
   */
  @Override
  public void addRenderedPartialTarget(String clientId)
  {
    _renderedTargets.add(clientId);
  }

  @Override
  public Iterator<String> getRenderedPartialTargets()
  {
    return _renderedTargets.iterator();
  }
 
  /**
   * Returns the VisitContext to use when partial rendering.
   * @return the VisitContext to use when partial rendering.
   */
  @Override
  public VisitContext getVisitContext()
  {
    return _visitContext;
  }

  /**
   * Notifies the PartialPageContext that the specified partial target is
   * about to be rendered.
   * <p>
   * This method is called automatically by Trinidad during the partial
   * rendering pass when a partial target is about to be rendered.
   * Clients should never need to call this method.
   *
   * @param clientId The clientId of the partial target that is about to be rendered
   * @see #popRenderedPartialTarget
   */
  public void pushRenderedPartialTarget(
    String clientId
    )
  {
    if (_LOG.isFine())
    {
      if (!_targets.containsKey(clientId))
        _LOG.fine("Rendering partial target {0}, which was not requested", clientId);
    }

    _targets.put(clientId, Boolean.TRUE);
    _currentTargetStack.push(clientId);

    if (_LOG.isFiner())
    {
      _LOG.finer("Pushed rendered PPR target " + clientId);
    }
  }

  /**
   * Notifies the PartialPageContext that the current partial target
   * has finished rendering.
   * <p>
   * This method is called automatically by Trinidad during the partial
   * rendering pass when a partial target has finished rendering.
   * Clients should never need to call this method.
   */
  public void popRenderedPartialTarget()
  {
    _currentTargetStack.pop();
  }


  /**
   * Returns the ID of the partial target that is currently being
   * rendered, if any.
   */
  private String _getCurrentPartialTarget()
  {
    if (_currentTargetStack.empty())
      return null;

    return _currentTargetStack.peek();
  }

  // Given a single client id, populate the subtree map with all possible
  // subtree client ids
  private void _addSubtreeClientId(String clientId)
  {
    // Loop over the client id and find the substring corresponding to
    // each ancestor NamingContainer client id.  For each ancestor
    // NamingContainer, add an entry into the map for the full client
    // id.
    char separator = NamingContainer.SEPARATOR_CHAR;
   
    int length = clientId.length();

    for (int i = 0; i < length; i++)
    {
      if (clientId.charAt(i) == separator)
      {
        // We found an ancestor NamingContainer client id - add
        // an entry to the map.
        String namingContainerClientId = clientId.substring(0, i);

        // Check to see whether we've already ids under this
        // NamingContainer client id.  If not, create the
        // Collection for this NamingContainer client id and
        // stash it away in our map
        Collection<String> c = _subtreeClientIds.get(namingContainerClientId);

        if (c == null)
        {
          // TODO: smarter initial size?
          c = new ArrayList<String>();
          _subtreeClientIds.put(namingContainerClientId, c);
        }

        // Stash away the client id
        c.add(clientId);
      }
    }
  }

  /**
   * Internal implementation of VisitContext used for optimised PPR rendering
   * ensuring a single source of truth by delegating back to the PartialPageContext
   * so that additions to the PartialPageContext's partial targets are reflected
   * in the set of visited components
   */
  private class PartialPageVisitContext extends VisitContext
  {
    public FacesContext getFacesContext()
    {
      return _context;
    }

    public PhaseId getPhaseId()
    {
      return PhaseId.RENDER_RESPONSE;
    }

    public Collection<String> getIdsToVisit()
    {
      // because we don't delegate back to the PartialPageContextImpl, this needs to
      // prevent modification
      return _unmodifiableTargets;
    }

    public Collection<String> getSubtreeIdsToVisit(UIComponent component)
    {
      // Make sure component is a NamingContainer
      if (!(component instanceof NamingContainer))
      {
        throw new IllegalArgumentException("Component is not a NamingContainer: " + component);
      }

      String clientId = component.getClientId(getFacesContext());
      Collection<String> ids = _subtreeClientIds.get(clientId);

      if (ids == null)
        return Collections.emptyList();
      else
        return Collections.unmodifiableCollection(ids);    
    }

    public VisitResult invokeVisitCallback(UIComponent component,
                                           VisitCallback callback)
    {
      // First sure that we should visit this component - ie.
      // that this component is represented in our id set.
      VisitResult result;
    
      if (component instanceof UIXComponent)
      {
        // delegate to the UIXComponent to let it control partial encoding behavior
        result = ((UIXComponent)component).partialEncodeVisit(this,
                                                              PartialPageContextImpl.this,
                                                              callback);
      }
      else
      {
        // check that this component should be encoded
        if (_targetIds.contains(component.getId()) &&
            _targets.containsKey(component.getClientId(_context)))
        {
          // If we made it this far, the component matches one of
          // client ids, so perform the visit.
          result = callback.visit(this, component);        
        }
        else
        {
          // Not visiting this component, but allow visit to
          // continue into this subtree in case we've got
          // visit targets there.
          result = VisitResult.ACCEPT;
        }
      }

      // If we've encoded everything.
      // Return VisitResult.COMPLETE to terminate the visit.
      if (areAllTargetsProcessed())
        return VisitResult.COMPLETE;
      else
      {
        // Otherwise, just return the callback's result
        return result;
      }
    }

    public Set<VisitHint> getHints()
    {
      return _PPR_VISIT_HINTS;
    }                                                            
  }

  private static final Set<VisitHint> _PPR_VISIT_HINTS = Collections.unmodifiableSet(
                                                         EnumSet.of(VisitHint.SKIP_UNRENDERED, VisitHint.EXECUTE_LIFECYCLE));
  private final FacesContext _context;
 
  // if the value is TRUE, then this target has been rendered.  If false, it has yet to be rendered
  private final Map<String, Boolean> _targets;
  private final Set<String> _renderedTargets;
 
  // Set of target ids to render.  This is an optimization so allow
  // fewer calls to getClientId
  private final Set<String> _targetIds;
  private final Set<String> _unmodifiableTargets;

  // This map contains the information needed by getIdsToVisit().
  // The keys in this map are NamingContainer client ids.  The values
  // are collections containing all of the client ids to visit within
  // corresponding naming container.
  private final Map<String, Collection<String>> _subtreeClientIds;

  // The stack of partial targets that are currently being rendered
  // -= Simon Lessard =-
  // FIXME: java.util.Stack... enough said... ArrayList or LinkedList please
  // =-= btsulliv Wait until we can use ArrayDeque
  private final Stack<String> _currentTargetStack;

  private final VisitContext _visitContext;
 
  private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(PartialPageContextImpl.class);
}
TOP

Related Classes of org.apache.myfaces.trinidadinternal.renderkit.core.ppr.PartialPageContextImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.