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 }