org.omnifaces.taghandler.TagAttribute Maven / Gradle / Ivy
/*
* Copyright OmniFaces
*
* Licensed 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
*
* https://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.omnifaces.taghandler;
import static org.omnifaces.util.Components.createValueExpression;
import java.io.IOException;
import jakarta.el.ValueExpression;
import jakarta.el.VariableMapper;
import jakarta.faces.component.UIComponent;
import jakarta.faces.view.facelets.FaceletContext;
import jakarta.faces.view.facelets.TagConfig;
import jakarta.faces.view.facelets.TagHandler;
import org.omnifaces.el.DelegatingVariableMapper;
/**
*
* The <o:tagAttribute>
is a tag handler that can be used to explicitly declare a tag attribute on
* a Facelets tag file. This makes sure that any tag attribute with the same name on a parent tag file is cleared out,
* which does not properly happen in Mojarra.
*
* Consider the following custom tag structure:
*
* <my:tag id="foo">
* <my:tag id="bar" />
* </my:tag>
*
*
* Inside the nested tag, the #{id}
will just evaluate to "bar"
. However, if this isn't
* declared on the nested tag like so,
*
* <my:tag id="foo">
* <my:tag />
* </my:tag>
*
*
* then #{id}
of the nested tag would evaluate to "foo"
instead of null
, even when you explicitly
* specify the attribute in the *.taglib.xml
file.
*
* This tag handler is designed to overcome this peculiar problem and unintuitive behavior of nested tagfiles in
* Mojarra.
*
*
Usage
*
* Just declare the attribute name in top of the tagfile as below.
*
* <o:tagAttribute name="id" />
*
*
* You can optionally provide a default value.
*
* <o:tagAttribute name="type" default="text" />
*
* Since OmniFaces 2.7/3.2 there is a special case for a <o:tagAttribute name="id">
without
* a default value: it will override any autogenerated ID into the form of j_ido[tagId]
where [tagId]
* is the <o:tagAttribute>
tag's own unique ID.
*
* @author Arjan Tijms.
* @since 2.1
*/
public class TagAttribute extends TagHandler {
private final String name;
private final jakarta.faces.view.facelets.TagAttribute defaultValue;
/**
* The tag constructor.
* @param config The tag config.
*/
public TagAttribute(TagConfig config) {
super(config);
name = getRequiredAttribute("name").getValue();
defaultValue = getAttribute("default");
}
@Override
public void apply(FaceletContext context, UIComponent parent) throws IOException {
DelegatingVariableMapper variableMapper = getDelegatingVariableMapper(context);
ValueExpression valueExpression = variableMapper.resolveWrappedVariable(name);
if (valueExpression == null) {
if (defaultValue != null) {
valueExpression = defaultValue.getValueExpression(context, Object.class);
}
else if ("id".equals(name)) {
valueExpression = createValueExpression("#{'j_ido" + context.generateUniqueId(this.tagId) + "'}", String.class);
variableMapper.setWrappedVariable(name, valueExpression);
return;
}
}
variableMapper.setVariable(name, valueExpression);
}
private DelegatingVariableMapper getDelegatingVariableMapper(FaceletContext context) {
VariableMapper variableMapper = context.getVariableMapper();
if (variableMapper instanceof DelegatingVariableMapper) {
return (DelegatingVariableMapper) variableMapper;
}
DelegatingVariableMapper delegatingVariableMapper = new DelegatingVariableMapper(variableMapper);
context.setVariableMapper(delegatingVariableMapper);
return delegatingVariableMapper;
}
}