/*
* 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 javax.portlet.faces.component;
import java.io.Serializable;
import javax.faces.context.FacesContext;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.portlet.faces.Bridge;
import javax.portlet.faces.BridgeUtil;
import javax.portlet.faces.annotation.PortletNamingContainer;
/**
* <code>UIViewRoot</code> that implements portlet specific <code>NamingContainer</code>
* that ensures the consumer's unique portlet Id is encoded in all tree components.
* The class is annotated by <code>javax.portlet.faces.annotation.PortletNamingContainer</code>
* allowing the bridge to recognize that this specific <code>UIViewRoot</code>
* implements the behavior.
*/
@PortletNamingContainer
public class PortletNamingContainerUIViewRoot extends UIViewRoot implements Serializable, NamingContainer
{
//TODO: This should be regenerated each time this is modified. Can this be added to maven?
private static final long serialVersionUID = -4524288011655837711L;
private static final String PORTLET_NAMESPACE_ID_PREFIX = "_jpfcpncuivr_";
// Assumes that first use of the UIViewRoot always occurs in a render as portlet namespace
// is only available during a render. If this isn't the case we disable use of the
// NamingContainer
public PortletNamingContainerUIViewRoot()
{
super();
}
/**
* NamingContainer semantics worked generically (serviced by subclasses) as long as the class
* is marked as implementing NamingContainer and we use the portletNamespace Id as
* (part of) the component's id.
*/
@Override
public String getContainerClientId(FacesContext context)
{
if (BridgeUtil.isPortletRequest())
{
// Some impls (Facelets don't set an id on the UIViewRoot) -- Also handles the action case
if ((this.getId() == null || !this.getId().startsWith(PORTLET_NAMESPACE_ID_PREFIX)))
{
setId(this.getId()); // setId can handle the null
}
return super.getContainerClientId(context);
}
else
{
return null;
}
}
@Override
public void setId(String id)
{
if (BridgeUtil.isPortletRequest())
{
// Turns out that in Facelets the UIViewRoot doesn't seem to have its id set -- i.e. its null
// So recognize null or the temp one we set and change to ""
if (id == null)
{
id = createUniqueId();
}
/* Can only calculate the namespace during a render (in portlet 1.0) */
if (!id.startsWith(PORTLET_NAMESPACE_ID_PREFIX) && BridgeUtil.getPortletRequestPhase() == Bridge.PortletPhase.RENDER_PHASE)
{
// Turns out some Faces impls (the RI) , on restoreView, manually sets the id from
// the extracted state prior to telling the component to restore itself from this state.
// (At which point the self restore overwrites any id set prior.). As this manual
// set is using the already encoded (saved) value we could end up with a doubly
// encoded id until the restore overwrites. To avoid this -- first test if
// its already encoded and don't re-encode.
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
id = PORTLET_NAMESPACE_ID_PREFIX + ec.encodeNamespace("") + "_" + id;
}
}
super.setId(id);
}
}