/*
* (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Contributors:
* <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
*
* $Id: WidgetTagHandler.java 30553 2008-02-24 15:51:31Z atchertchian $
*/
package org.nuxeo.ecm.platform.forms.layout.facelets;
import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.el.ELException;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.el.VariableMapper;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.FaceletHandler;
import javax.faces.view.facelets.MetaRuleset;
import javax.faces.view.facelets.MetaTagHandler;
import javax.faces.view.facelets.TagAttribute;
import javax.faces.view.facelets.TagConfig;
import javax.faces.view.facelets.TagException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.el.ValueExpressionLiteral;
import org.nuxeo.ecm.platform.forms.layout.api.Widget;
import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition;
import org.nuxeo.ecm.platform.forms.layout.service.WebLayoutManager;
import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils;
import org.nuxeo.runtime.api.Framework;
import com.sun.faces.facelets.el.VariableMapperWrapper;
/**
* Widget tag handler.
* <p>
* Applies {@link WidgetTypeHandler} found for given widget, in given mode and
* for given value.
*
* @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
*/
public class WidgetTagHandler extends MetaTagHandler {
@SuppressWarnings("unused")
private static final Log log = LogFactory.getLog(WidgetTagHandler.class);
protected final TagConfig config;
protected final TagAttribute widget;
/**
* @since 5.6
*/
protected final TagAttribute name;
/**
* @since 5.6
*/
protected final TagAttribute category;
/**
* @since 5.6
*/
protected final TagAttribute definition;
/**
* @since 5.6
*/
protected final TagAttribute mode;
/**
* @since 5.6
*/
protected final TagAttribute layoutName;
/**
* @since 5.7
*/
protected final TagAttribute resolveOnly;
protected final TagAttribute value;
protected final TagAttribute[] vars;
protected final String[] reservedVarsArray = { "id", "widget", "name",
"category", "definition", "mode", "layoutName", "value",
"resolveOnly" };
public WidgetTagHandler(TagConfig config) {
super(config);
this.config = config;
widget = getAttribute("widget");
name = getAttribute("name");
definition = getAttribute("definition");
category = getAttribute("category");
mode = getAttribute("mode");
layoutName = getAttribute("layoutName");
resolveOnly = getAttribute("resolveOnly");
value = getAttribute("value");
vars = tag.getAttributes().getAll();
// additional checks
if (name == null && widget == null && definition == null) {
throw new TagException(this.tag,
"At least one of attributes 'name', 'widget' "
+ "or 'definition' is required");
}
if (widget == null && (name != null || definition != null)) {
if (mode == null) {
throw new TagException(this.tag,
"Attribute 'mode' is required when using attribute"
+ " 'name' or 'definition' so that the "
+ "widget instance " + "can be resolved");
}
}
}
/**
* Renders given widget resolving its {@link FaceletHandler} from
* {@link WebLayoutManager} configuration.
* <p>
* Variables exposed: {@link RenderVariables.globalVariables#value}, same
* variable suffixed with "_n" where n is the widget level, and
* {@link RenderVariables.globalVariables#document}.
*/
public void apply(FaceletContext ctx, UIComponent parent)
throws IOException, FacesException, ELException {
// compute value name to set on widget instance in case it's changed
// from first computation
String valueName = null;
if (value != null) {
valueName = value.getValue();
}
if (ComponentTagUtils.isStrictValueReference(valueName)) {
valueName = ComponentTagUtils.getBareValueName(valueName);
}
// build handler
boolean widgetInstanceBuilt = false;
Widget widgetInstance = null;
if (widget != null) {
widgetInstance = (Widget) widget.getObject(ctx, Widget.class);
if (widgetInstance != null && valueName != null) {
widgetInstance.setValueName(valueName);
}
} else {
// resolve widget according to name and mode (and optional
// category)
WebLayoutManager layoutService;
try {
layoutService = Framework.getService(WebLayoutManager.class);
} catch (Exception e) {
throw new FacesException(e);
}
if (layoutService == null) {
throw new FacesException("Layout service not found");
}
String modeValue = mode.getValue(ctx);
String layoutNameValue = null;
if (layoutName != null) {
layoutNameValue = layoutName.getValue(ctx);
}
if (name != null) {
String nameValue = name.getValue(ctx);
String catValue = null;
if (category != null) {
catValue = category.getValue(ctx);
}
widgetInstance = layoutService.getWidget(ctx, nameValue,
catValue, modeValue, valueName, layoutNameValue);
widgetInstanceBuilt = true;
} else if (definition != null) {
WidgetDefinition widgetDef = (WidgetDefinition) definition.getObject(
ctx, WidgetDefinition.class);
if (widgetDef != null) {
widgetInstance = layoutService.getWidget(ctx, widgetDef,
modeValue, valueName, layoutNameValue);
widgetInstanceBuilt = true;
}
}
}
if (widgetInstance != null) {
// add additional properties put on tag
String widgetPropertyMarker = RenderVariables.widgetVariables.widgetProperty.name()
+ "_";
List<String> reservedVars = Arrays.asList(reservedVarsArray);
for (TagAttribute var : vars) {
String localName = var.getLocalName();
if (!reservedVars.contains(localName)) {
if (localName != null
&& localName.startsWith(widgetPropertyMarker)) {
localName = localName.substring(widgetPropertyMarker.length());
}
widgetInstance.setProperty(localName, var.getValue());
}
}
VariableMapper orig = ctx.getVariableMapper();
if (widgetInstanceBuilt) {
// expose widget variable to the context as layout row has not
// done it already, and set unique id on widget and sub widgets
// before exposing them to the context
FaceletHandlerHelper helper = new FaceletHandlerHelper(ctx,
config);
WidgetTagHandler.generateWidgetIdsRecursive(helper,
widgetInstance);
VariableMapper vm = new VariableMapperWrapper(orig);
ctx.setVariableMapper(vm);
ExpressionFactory eFactory = ctx.getExpressionFactory();
ValueExpression widgetVe = eFactory.createValueExpression(
widgetInstance, Widget.class);
vm.setVariable(RenderVariables.widgetVariables.widget.name(),
widgetVe);
// expose widget controls too
for (Map.Entry<String, Serializable> ctrl : widgetInstance.getControls().entrySet()) {
String key = ctrl.getKey();
String name = String.format(
"%s_%s",
RenderVariables.widgetVariables.widgetControl.name(),
key);
String value = String.format("#{%s.controls.%s}",
RenderVariables.widgetVariables.widget.name(), key);
vm.setVariable(name, eFactory.createValueExpression(ctx,
value, Object.class));
}
}
try {
boolean resolveOnlyBool = false;
if (resolveOnly != null) {
resolveOnlyBool = resolveOnly.getBoolean(ctx);
}
if (resolveOnlyBool) {
nextHandler.apply(ctx, parent);
} else {
applyWidgetHandler(ctx, parent, config, widgetInstance,
value, true, nextHandler);
}
} finally {
ctx.setVariableMapper(orig);
}
}
}
public static void generateWidgetIdsRecursive(FaceletHandlerHelper helper,
Widget widget) {
if (widget == null) {
return;
}
widget.setId(helper.generateWidgetId(widget.getName()));
Widget[] subWidgets = widget.getSubWidgets();
if (subWidgets != null) {
for (Widget subWidget : subWidgets) {
generateWidgetIdsRecursive(helper, subWidget);
}
}
}
public static void applyWidgetHandler(FaceletContext ctx,
UIComponent parent, TagConfig config, Widget widget,
TagAttribute value, boolean fillVariables,
FaceletHandler nextHandler) throws IOException {
if (widget == null) {
return;
}
WebLayoutManager layoutService;
try {
layoutService = Framework.getService(WebLayoutManager.class);
} catch (Exception e) {
throw new FacesException(e);
}
FaceletHandlerHelper helper = new FaceletHandlerHelper(ctx, config);
FaceletHandler handler = layoutService.getFaceletHandler(ctx, config,
widget, nextHandler);
if (handler == null) {
return;
}
if (fillVariables) {
// expose widget variables
Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>();
ValueExpression valueExpr;
if (value == null) {
valueExpr = new ValueExpressionLiteral(null, Object.class);
} else {
valueExpr = value.getValueExpression(ctx, Object.class);
}
variables.put(RenderVariables.globalVariables.value.name(),
valueExpr);
variables.put(String.format("%s_%s",
RenderVariables.globalVariables.value.name(),
Integer.valueOf(widget.getLevel())), valueExpr);
// document as alias to value
// variables.put(RenderVariables.globalVariables.document.name(),
// valueExpr);
FaceletHandler handlerWithVars = helper.getAliasTagHandler(
widget.getTagConfigId(), variables, null, handler);
// apply
handlerWithVars.apply(ctx, parent);
} else {
// just apply
handler.apply(ctx, parent);
}
}
@Override
@SuppressWarnings("rawtypes")
protected MetaRuleset createMetaRuleset(Class type) {
return null;
}
}