blob: bd4c8f6524bd7ae7267efc172c35ea97a95a2c67 [file] [log] [blame]
/*
* 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 org.apache.myfaces.view.facelets.tag.composite;
import java.io.IOException;
import java.util.Map;
import javax.faces.component.StateHolder;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.ComponentSystemEventListener;
import javax.faces.view.facelets.Facelet;
import org.apache.myfaces.config.RuntimeConfig;
import org.apache.myfaces.view.facelets.AbstractFacelet;
import org.apache.myfaces.view.facelets.DynamicComponentRefreshTransientBuildEvent;
import org.apache.myfaces.view.facelets.FaceletCompositionContext;
import org.apache.myfaces.view.facelets.FaceletDynamicComponentRefreshTransientBuildEvent;
import org.apache.myfaces.view.facelets.FaceletFactory;
import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage;
import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguageBase;
import org.apache.myfaces.view.facelets.LocationAwareFacesException;
import org.apache.myfaces.view.facelets.compiler.RefreshDynamicComponentListener;
import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
/**
* This listener must be attached to PostAddToViewEvent, so when composite component is
* added to the view, the algorithm that create the composite component content is executed.
*
* @author lu4242
*/
public class CreateDynamicCompositeComponentListener
implements ComponentSystemEventListener, StateHolder
{
private String taglibURI;
private String tagName;
private Map<String,Object> attributes;
private String baseKey;
public CreateDynamicCompositeComponentListener(String taglibURI, String tagName,
Map<String, Object> attributes, String baseKey)
{
this.taglibURI = taglibURI;
this.tagName = tagName;
this.attributes = attributes;
this.baseKey = baseKey;
}
public CreateDynamicCompositeComponentListener()
{
}
@Override
public void processEvent(ComponentSystemEvent event)
{
FacesContext facesContext = FacesContext.getCurrentInstance();
FaceletViewDeclarationLanguage vdl = (FaceletViewDeclarationLanguage)
facesContext.getApplication().getViewHandler().getViewDeclarationLanguage(
facesContext, facesContext.getViewRoot().getViewId());
Facelet componentFacelet;
FaceletFactory faceletFactory = vdl.getFaceletFactory();
FaceletFactory.setInstance(faceletFactory);
try
{
componentFacelet
= faceletFactory.compileComponentFacelet(taglibURI, tagName, attributes);
}
finally
{
FaceletFactory.setInstance(null);
}
UIComponent component = event.getComponent();
// The execution of this listener activated another call to PostAddToViewEvent, because
// ComponentTagHandlerDelegate removes and add the component again. This is necessary because
// the inner components also require the propagation of PostAddToViewEvent to refresh themselves.
// but this check avoids the duplicate call to the facelet, even if the duplicate call does not
// have any side effect (counts as a refresh).
Integer step = (Integer) component.getAttributes().get(
CompositeComponentResourceTagHandler.CREATE_CC_ON_POST_ADD_TO_VIEW);
if (step != null && step == 0)
{
component.getAttributes().put(CompositeComponentResourceTagHandler.CREATE_CC_ON_POST_ADD_TO_VIEW, 1);
}
else
{
return;
}
try
{
facesContext.getAttributes().put(FaceletViewDeclarationLanguage.REFRESHING_TRANSIENT_BUILD,
Boolean.TRUE);
// Detect the relationship between parent and child, to ensure the component is properly created
// and refreshed. In facelets this is usually done by core.FacetHandler, but since it is a
// dynamic component, we need to do it here before apply the handler
UIComponent parent = component.getParent();
String facetName = null;
if (parent.getFacetCount() > 0 && !parent.getChildren().contains(component))
{
facetName = ComponentSupport.findFacetNameByComponentInstance(parent, component);
}
try
{
if (facetName != null)
{
parent.getAttributes().put(org.apache.myfaces.view.facelets.tag.jsf.core.FacetHandler.KEY,
facetName);
}
// The trick here is restore MARK_CREATED, just to allow ComponentTagHandlerDelegate to
// find the component. Then we reset it to exclude it from facelets refresh algorithm.
String markId = (String) component.getAttributes().get(FaceletViewDeclarationLanguage.GEN_MARK_ID);
if (markId == null)
{
((AbstractFacelet) componentFacelet).applyDynamicComponentHandler(
facesContext, component, baseKey);
}
else
{
try
{
component.getAttributes().put(ComponentSupport.MARK_CREATED, markId);
((AbstractFacelet) componentFacelet).applyDynamicComponentHandler(
facesContext, component.getParent(), baseKey);
}
finally
{
component.getAttributes().put(ComponentSupport.MARK_CREATED, null);
}
}
if (FaceletViewDeclarationLanguageBase.isDynamicComponentNeedsRefresh(facesContext))
{
FaceletCompositionContext fcc = FaceletCompositionContext.getCurrentInstance(facesContext);
if (fcc == null)
{
FaceletViewDeclarationLanguageBase.activateDynamicComponentRefreshTransientBuild(facesContext);
FaceletViewDeclarationLanguageBase.resetDynamicComponentNeedsRefreshFlag(facesContext);
component.subscribeToEvent(DynamicComponentRefreshTransientBuildEvent.class, new
RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey));
component.getAttributes().put(
DynamicComponentRefreshTransientBuildEvent.DYN_COMP_REFRESH_FLAG, Boolean.TRUE);
}
else
{
component.subscribeToEvent(FaceletDynamicComponentRefreshTransientBuildEvent.class, new
RefreshDynamicComponentListener(taglibURI, tagName, attributes, baseKey));
}
}
}
finally
{
if (facetName != null)
{
parent.getAttributes().remove(org.apache.myfaces.view.facelets.tag.jsf.core.FacetHandler.KEY);
}
}
}
catch (IOException e)
{
throw new LocationAwareFacesException(e, component);
}
finally
{
facesContext.getAttributes().remove(FaceletViewDeclarationLanguage.REFRESHING_TRANSIENT_BUILD);
}
}
@Override
public Object saveState(FacesContext context)
{
RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(context.getExternalContext());
Object[] values = new Object[4];
Integer tagId = runtimeConfig.getIdByNamespace().get(taglibURI);
if (tagId != null)
{
values[0] = tagId;
}
else if (taglibURI.startsWith(CompositeResourceLibrary.NAMESPACE_PREFIX))
{
values[0] = new Object[]{0, taglibURI.substring(35)};
}
else if(taglibURI.startsWith(CompositeResourceLibrary.ALIAS_NAMESPACE_PREFIX))
{
values[0] = new Object[]{1, taglibURI.substring(34)};
}
else
{
values[0] = taglibURI;
}
values[1] = tagName;
values[2] = attributes;
values[3] = baseKey;
return values;
}
@Override
public void restoreState(FacesContext context, Object state)
{
Object[] values = (Object[]) state;
if (values[0] instanceof String)
{
taglibURI = (String) values[0];
}
else if (values[0] instanceof Integer)
{
RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(context.getExternalContext());
taglibURI = runtimeConfig.getNamespaceById().get((Integer)values[0]);
}
else if (values[0] instanceof Object[])
{
Object[] def = (Object[])values[0];
String ns = (((Integer) def[0]) == 0) ?
CompositeResourceLibrary.NAMESPACE_PREFIX :
CompositeResourceLibrary.ALIAS_NAMESPACE_PREFIX;
taglibURI = ns + (String) ((Object[]) values[0])[1];
}
tagName = (String)values[1];
attributes = (Map<String,Object>) values[2];
baseKey = (String)values[3];
}
@Override
public boolean isTransient()
{
return false;
}
@Override
public void setTransient(boolean newTransientValue)
{
}
}