001/* 002 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 018 * 019 * $Id: SubWidgetTagHandler.java 30553 2008-02-24 15:51:31Z atchertchian $ 020 */ 021 022package org.nuxeo.ecm.platform.forms.layout.facelets; 023 024import java.io.IOException; 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030 031import javax.el.ELException; 032import javax.el.ExpressionFactory; 033import javax.el.ValueExpression; 034import javax.el.VariableMapper; 035import javax.faces.FacesException; 036import javax.faces.component.UIComponent; 037import javax.faces.view.facelets.FaceletContext; 038import javax.faces.view.facelets.FaceletHandler; 039import javax.faces.view.facelets.TagAttribute; 040import javax.faces.view.facelets.TagConfig; 041import javax.faces.view.facelets.TagHandler; 042 043import org.apache.commons.logging.Log; 044import org.apache.commons.logging.LogFactory; 045import org.nuxeo.ecm.platform.forms.layout.api.Widget; 046import org.nuxeo.ecm.platform.ui.web.binding.BlockingVariableMapper; 047 048/** 049 * SubWidget tag handler. 050 * <p> 051 * Iterates over a widget subwidgets and apply next handlers as many times as needed. 052 * <p> 053 * Only works when used inside a tag using the {@link WidgetTagHandler}. 054 * 055 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 056 */ 057public class SubWidgetTagHandler extends TagHandler { 058 059 private static final Log log = LogFactory.getLog(SubWidgetTagHandler.class); 060 061 protected final TagConfig config; 062 063 /** 064 * @since 7.2 065 */ 066 protected final TagAttribute recomputeIds; 067 068 public SubWidgetTagHandler(TagConfig config) { 069 super(config); 070 this.config = config; 071 recomputeIds = getAttribute("recomputeIds"); 072 } 073 074 /** 075 * For each subwidget in current widget, exposes widget variables and applies next handler. 076 * <p> 077 * Needs widget to be exposed in context, so works in conjunction with {@link WidgetTagHandler}. 078 * <p> 079 * Widget variables exposed: {@link RenderVariables.widgetVariables#widget} , same variable suffixed with "_n" where 080 * n is the widget level, and {@link RenderVariables.widgetVariables#widgetIndex}. 081 */ 082 public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException { 083 // resolve subwidgets from widget in context 084 Widget widget = null; 085 String widgetVariableName = RenderVariables.widgetVariables.widget.name(); 086 FaceletHandlerHelper helper = new FaceletHandlerHelper(config); 087 TagAttribute widgetAttribute = helper.createAttribute(widgetVariableName, "#{" + widgetVariableName + "}"); 088 if (widgetAttribute != null) { 089 widget = (Widget) widgetAttribute.getObject(ctx, Widget.class); 090 } 091 if (widget == null) { 092 log.error("Could not resolve widget " + widgetAttribute); 093 return; 094 } 095 096 Widget[] subWidgets = widget.getSubWidgets(); 097 if (subWidgets == null || subWidgets.length == 0) { 098 return; 099 } 100 101 boolean recomputeIdsBool = false; 102 if (recomputeIds != null) { 103 recomputeIdsBool = recomputeIds.getBoolean(ctx); 104 } 105 106 if (FaceletHandlerHelper.isAliasOptimEnabled()) { 107 VariableMapper orig = ctx.getVariableMapper(); 108 try { 109 applyOptimized(ctx, parent, orig, subWidgets, helper, recomputeIdsBool); 110 } finally { 111 ctx.setVariableMapper(orig); 112 } 113 } else { 114 applyCompat(ctx, parent, subWidgets, helper, recomputeIdsBool); 115 } 116 } 117 118 protected void applyOptimized(FaceletContext ctx, UIComponent parent, VariableMapper orig, Widget[] subWidgets, 119 FaceletHandlerHelper helper, boolean recomputeIdsBool) throws IOException, FacesException, ELException { 120 int subWidgetCounter = 0; 121 for (Widget subWidget : subWidgets) { 122 BlockingVariableMapper vm = new BlockingVariableMapper(orig); 123 ctx.setVariableMapper(vm); 124 125 // set unique id on widget before exposing it to the context, but assumes iteration could be done 126 // several times => do not generate id again if already set, unless specified by attribute 127 // "recomputeIds" 128 if (subWidget != null && (subWidget.getId() == null || recomputeIdsBool)) { 129 WidgetTagHandler.generateWidgetId(ctx, helper, subWidget, false); 130 } 131 132 WidgetTagHandler.exposeWidgetVariables(ctx, vm, subWidget, subWidgetCounter, true); 133 134 nextHandler.apply(ctx, parent); 135 subWidgetCounter++; 136 } 137 } 138 139 protected void applyCompat(FaceletContext ctx, UIComponent parent, Widget[] subWidgets, FaceletHandlerHelper helper, 140 boolean recomputeIdsBool) throws IOException, FacesException, ELException { 141 int subWidgetCounter = 0; 142 for (Widget subWidget : subWidgets) { 143 // set unique id on widget before exposing it to the context, but assumes iteration could be done several 144 // times => do not generate id again if already set, unless specified by attribute "recomputeIds" 145 if (subWidget != null && (subWidget.getId() == null || recomputeIdsBool)) { 146 WidgetTagHandler.generateWidgetId(ctx, helper, subWidget, false); 147 } 148 149 // expose widget variables 150 Map<String, ValueExpression> variables = new HashMap<String, ValueExpression>(); 151 ExpressionFactory eFactory = ctx.getExpressionFactory(); 152 ValueExpression subWidgetVe = eFactory.createValueExpression(subWidget, Widget.class); 153 Integer level = null; 154 String tagConfigId = null; 155 if (subWidget != null) { 156 level = Integer.valueOf(subWidget.getLevel()); 157 tagConfigId = subWidget.getTagConfigId(); 158 } 159 160 variables.put(RenderVariables.widgetVariables.widget.name(), subWidgetVe); 161 // variables.put(String.format("%s_%s", 162 // RenderVariables.widgetVariables.widget.name(), level), 163 // subWidgetVe); 164 ValueExpression subWidgetIndexVe = eFactory.createValueExpression(Integer.valueOf(subWidgetCounter), 165 Integer.class); 166 variables.put(RenderVariables.widgetVariables.widgetIndex.name(), subWidgetIndexVe); 167 variables.put(RenderVariables.widgetVariables.widgetIndex.name() + "_" + level, subWidgetIndexVe); 168 169 // XXX: expose widget controls too, need to figure out 170 // why controls cannot be references to widget.controls like 171 // properties are in TemplateWidgetTypeHandler 172 if (subWidget != null) { 173 for (Map.Entry<String, Serializable> ctrl : subWidget.getControls().entrySet()) { 174 String key = ctrl.getKey(); 175 String name = RenderVariables.widgetVariables.widgetControl.name() + "_" + key; 176 Serializable value = ctrl.getValue(); 177 variables.put(name, eFactory.createValueExpression(value, Object.class)); 178 } 179 } 180 181 List<String> blockedPatterns = new ArrayList<String>(); 182 blockedPatterns.add(RenderVariables.widgetVariables.widget.name()); 183 blockedPatterns.add(RenderVariables.widgetVariables.widgetIndex.name() + "*"); 184 blockedPatterns.add(RenderVariables.widgetVariables.widgetControl.name() + "_*"); 185 186 FaceletHandler handlerWithVars = helper.getAliasFaceletHandler(tagConfigId, variables, blockedPatterns, 187 nextHandler); 188 189 // apply 190 handlerWithVars.apply(ctx, parent); 191 subWidgetCounter++; 192 } 193 } 194 195}