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_ENCODED_NAMESPACE_ID = Bridge.BRIDGE_PACKAGE_PREFIX + "PortletEncodedNamespaceId";
039      
040    
041      public PortletNamingContainerUIViewRoot()
042      {
043        super();
044        
045        getAttributes().put(PORTLET_ENCODED_NAMESPACE_ID, Boolean.FALSE);
046      }
047    
048      /**
049       * NamingContainer semantics worked generically (serviced by subclasses) as long as the class
050       * is marked as implementing NamingContainer and we use the portletNamespace Id as
051       * (part of) the component's id.
052       */
053    
054      @Override
055      public String getContainerClientId(FacesContext context)
056      {
057        if (BridgeUtil.isPortletRequest() && ((Boolean) this.getAttributes().get(PORTLET_ENCODED_NAMESPACE_ID)).equals(Boolean.TRUE))
058        {
059          return super.getContainerClientId(context);
060        }
061        else
062        {
063          return null;
064        }
065    
066      }
067      
068      @Override
069      public void setId(String id)
070      {
071        if (BridgeUtil.isPortletRequest()) 
072        {
073          // Turns out some Faces impls (the RI) , on restoreView, manually sets the id from 
074          // the extracted state prior to telling the component to restore itself from this state.
075          // (At which point the self restore overwrites any id set prior.).  As this manual
076          // set is using the already encoded (saved) value we could end up with a doubly
077          // encoded id until the restore overwrites.  To avoid this -- first test if
078          // its already encoded and don't re-encode.
079          Boolean encoded = Boolean.FALSE;
080          ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
081          String namespace = ec.encodeNamespace("");
082          if (!id.startsWith(namespace))
083          {
084            id = namespace + "_" + id;
085          }
086            // now indicate that this id is encoded
087          encoded = Boolean.TRUE;
088          getAttributes().put(PORTLET_ENCODED_NAMESPACE_ID, encoded);
089        }
090        super.setId(id);
091      }
092      
093    }