001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
003     * agreements. See the NOTICE file distributed with this work for additional information regarding
004     * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
005     * "License"); you may not use this file except in compliance with the License. You may obtain a
006     * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
007     * law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
008     * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
009     * for the specific language governing permissions and limitations under the License.
010     */
011    package javax.portlet.faces.component;
012    
013    import java.io.Serializable;
014    
015    import javax.faces.context.FacesContext;
016    import javax.faces.component.NamingContainer;
017    import javax.faces.component.UIViewRoot;
018    import javax.faces.context.ExternalContext;
019    
020    import javax.portlet.faces.Bridge;
021    import javax.portlet.faces.BridgeUtil;
022    import javax.portlet.faces.annotation.PortletNamingContainer;
023    
024    
025    /**
026     * <code>UIViewRoot</code> that implements portlet specific <code>NamingContainer</code>
027     * that ensures the consumer's unique portlet Id is encoded in all tree components.
028     * The class is annotated by <code>javax.portlet.faces.annotation.PortletNamingContainer</code>
029     * allowing the bridge to recognize that this specific <code>UIViewRoot</code>
030     * implements the behavior.
031     */
032    @PortletNamingContainer
033    public class PortletNamingContainerUIViewRoot extends UIViewRoot implements Serializable, NamingContainer
034    {
035    
036      //TODO: This should be regenerated each time this is modified.  Can this be added to maven?
037      private static final long   serialVersionUID = -4524288011655837711L;
038      private static final String PORTLET_NAMESPACE_ID_PREFIX = "_jpfcpncuivr_";
039      
040    
041      public PortletNamingContainerUIViewRoot()
042      {
043        super();
044        
045      }
046    
047      /**
048       * NamingContainer semantics worked generically (serviced by subclasses) as long as the class
049       * is marked as implementing NamingContainer and we use the portletNamespace Id as
050       * (part of) the component's id.
051       */
052    
053      @Override
054      public String getContainerClientId(FacesContext context)
055      {
056        if (BridgeUtil.isPortletRequest())
057        {
058          // Some impls (Facelets don't set an id on the UIViewRoot)  -- Also handles the action case
059          if ((this.getId() == null || !this.getId().startsWith(PORTLET_NAMESPACE_ID_PREFIX)))
060          {
061            setId(this.getId()); // setId can handle the null
062          }
063          return super.getContainerClientId(context);
064        }
065        else
066        {
067          return null;
068        }
069    
070      }
071      
072      @Override
073      public void setId(String id)
074      {
075        if (BridgeUtil.isPortletRequest()) 
076        {
077          // Turns out that in Facelets the UIViewRoot doesn't seem to have its id set -- i.e. its null
078          // So recognize null and change to a uniqueId
079          if (id == null)
080          {
081            id = createUniqueId();
082          }
083          
084          // Turns out some Faces impls (the RI) , on restoreView, manually sets the id from 
085          // the extracted state prior to telling the component to restore itself from this state.
086          // (At which point the self restore overwrites any id set prior.).  As this manual
087          // set is using the already encoded (saved) value we could end up with a doubly
088          // encoded id until the restore overwrites.  To avoid this -- first test if
089          // its already encoded and don't re-encode.
090          if (!id.startsWith(PORTLET_NAMESPACE_ID_PREFIX))
091          {
092            ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
093            id = PORTLET_NAMESPACE_ID_PREFIX + ec.encodeNamespace("") + "_" + id;
094          }
095        }
096        super.setId(id);
097      }
098      
099    }