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 }