001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 016 * 017 * $Id: WidgetDescriptor.java 28478 2008-01-04 12:53:58Z sfermigier $ 018 */ 019 020package org.nuxeo.ecm.platform.forms.layout.descriptors; 021 022import java.io.Serializable; 023import java.util.ArrayList; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030import org.nuxeo.common.xmap.XMap; 031import org.nuxeo.common.xmap.annotation.XContent; 032import org.nuxeo.common.xmap.annotation.XNode; 033import org.nuxeo.common.xmap.annotation.XNodeList; 034import org.nuxeo.common.xmap.annotation.XNodeMap; 035import org.nuxeo.common.xmap.annotation.XObject; 036import org.nuxeo.ecm.platform.forms.layout.api.BuiltinModes; 037import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition; 038import org.nuxeo.ecm.platform.forms.layout.api.RenderingInfo; 039import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition; 040import org.nuxeo.ecm.platform.forms.layout.api.WidgetReference; 041import org.nuxeo.ecm.platform.forms.layout.api.WidgetSelectOption; 042import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetDefinitionImpl; 043import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetReferenceImpl; 044import org.w3c.dom.DocumentFragment; 045import org.w3c.dom.Element; 046import org.w3c.dom.Node; 047 048/** 049 * Widget definition descriptor. 050 * 051 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 052 */ 053@XObject("widget") 054public class WidgetDescriptor { 055 056 private static final Log log = LogFactory.getLog(WidgetDescriptor.class); 057 058 @XNode("@name") 059 String name; 060 061 @XNode("@type") 062 String type; 063 064 /** 065 * @since 5.7.3 066 */ 067 @XNode("@typeCategory") 068 String typeCategory; 069 070 @XNodeList(value = "fields/field", type = FieldDescriptor[].class, componentType = FieldDescriptor.class) 071 FieldDescriptor[] fields = new FieldDescriptor[0]; 072 073 @XNodeMap(value = "widgetModes/mode", key = "@value", type = HashMap.class, componentType = String.class) 074 Map<String, String> modes = new HashMap<String, String>(); 075 076 @XNodeMap(value = "labels/label", key = "@mode", type = HashMap.class, componentType = String.class) 077 Map<String, String> labels = new HashMap<String, String>(); 078 079 @XNodeMap(value = "helpLabels/label", key = "@mode", type = HashMap.class, componentType = String.class) 080 Map<String, String> helpLabels = new HashMap<String, String>(); 081 082 /** 083 * Defaults to true, contrary to {@link WidgetDefinition} interface, but kept as is for compatibility. 084 */ 085 @XNode("translated") 086 boolean translated = true; 087 088 /** 089 * @since 5.6 090 * @deprecated since 5.7: use {@link #controls} instead, with name "handleLabels". 091 */ 092 @Deprecated 093 @XNode("handlingLabels") 094 boolean handlingLabels = false; 095 096 @XNodeMap(value = "properties", key = "@mode", type = HashMap.class, componentType = PropertiesDescriptor.class) 097 Map<String, PropertiesDescriptor> properties = new HashMap<String, PropertiesDescriptor>(); 098 099 @XNodeMap(value = "controls", key = "@mode", type = HashMap.class, componentType = ControlsDescriptor.class) 100 Map<String, ControlsDescriptor> controls = new HashMap<String, ControlsDescriptor>(); 101 102 @XNodeMap(value = "properties", key = "@widgetMode", type = HashMap.class, componentType = PropertiesDescriptor.class) 103 Map<String, PropertiesDescriptor> widgetModeProperties = new HashMap<String, PropertiesDescriptor>(); 104 105 @XNodeList(value = "subWidgets/widget", type = WidgetDescriptor[].class, componentType = WidgetDescriptor.class) 106 WidgetDescriptor[] subWidgets = new WidgetDescriptor[0]; 107 108 /** 109 * @since 5.6 110 */ 111 @XNodeList(value = "subWidgetRefs/widget", type = WidgetReferenceDescriptor[].class, componentType = WidgetReferenceDescriptor.class) 112 WidgetReferenceDescriptor[] subWidgetRefs = new WidgetReferenceDescriptor[0]; 113 114 // set in method to mix single and multiple options 115 WidgetSelectOption[] selectOptions = new WidgetSelectOption[0]; 116 117 @XNodeMap(value = "renderingInfos", key = "@mode", type = HashMap.class, componentType = RenderingInfosDescriptor.class) 118 Map<String, RenderingInfosDescriptor> renderingInfos = new HashMap<String, RenderingInfosDescriptor>(); 119 120 @XNodeList(value = "categories/category", type = String[].class, componentType = String.class) 121 String[] categories = new String[0]; 122 123 /** 124 * @since 6.0 125 */ 126 @XNodeList(value = "aliases/alias", type = ArrayList.class, componentType = String.class) 127 List<String> aliases; 128 129 public String getName() { 130 return name; 131 } 132 133 public String getType() { 134 return type; 135 } 136 137 public FieldDefinition[] getFieldDefinitions() { 138 if (fields == null) { 139 return null; 140 } 141 FieldDefinition[] res = new FieldDefinition[fields.length]; 142 for (int i = 0; i < fields.length; i++) { 143 res[i] = fields[i].getFieldDefinition(); 144 } 145 return res; 146 } 147 148 public String getMode(String layoutMode) { 149 String mode = modes.get(layoutMode); 150 if (mode == null) { 151 mode = modes.get(BuiltinModes.ANY); 152 } 153 return mode; 154 } 155 156 public Map<String, String> getModes() { 157 return modes; 158 } 159 160 public String getRequired(String layoutMode, String mode) { 161 String res = "false"; 162 Map<String, Serializable> props = getProperties(layoutMode, mode); 163 if (props != null && props.containsKey(WidgetDefinition.REQUIRED_PROPERTY_NAME)) { 164 Object value = props.get(WidgetDefinition.REQUIRED_PROPERTY_NAME); 165 if (value instanceof String) { 166 res = (String) value; 167 } else { 168 log.error(String.format("Invalid property \"%s\" on widget %s: %s", 169 WidgetDefinition.REQUIRED_PROPERTY_NAME, value, name)); 170 } 171 } 172 return res; 173 } 174 175 public String getLabel(String mode) { 176 String label = labels.get(mode); 177 if (label == null) { 178 label = labels.get(BuiltinModes.ANY); 179 } 180 return label; 181 } 182 183 public Map<String, String> getLabels() { 184 return labels; 185 } 186 187 public String getHelpLabel(String mode) { 188 String label = helpLabels.get(mode); 189 if (label == null) { 190 label = helpLabels.get(BuiltinModes.ANY); 191 } 192 return label; 193 } 194 195 public Map<String, String> getHelpLabels() { 196 return helpLabels; 197 } 198 199 public boolean isTranslated() { 200 return translated; 201 } 202 203 public Map<String, Serializable> getProperties(String layoutMode, String mode) { 204 Map<String, Serializable> modeProps = getProperties(properties, layoutMode); 205 Map<String, Serializable> widgetModeProps = getProperties(widgetModeProperties, mode); 206 if (modeProps == null && widgetModeProps == null) { 207 return null; 208 } else if (widgetModeProps == null) { 209 return modeProps; 210 } else if (modeProps == null) { 211 return widgetModeProps; 212 } else { 213 // take mode values, and override with widget mode values 214 Map<String, Serializable> res = new HashMap<String, Serializable>(modeProps); 215 res.putAll(widgetModeProps); 216 return res; 217 } 218 } 219 220 public Map<String, Map<String, Serializable>> getProperties() { 221 return getProperties(properties); 222 } 223 224 public Map<String, Map<String, Serializable>> getWidgetModeProperties() { 225 return getProperties(widgetModeProperties); 226 } 227 228 /** 229 * @since 5.7 230 * @see WidgetDefinition#getControls() 231 */ 232 public Map<String, Map<String, Serializable>> getControls() { 233 if (controls == null) { 234 return null; 235 } 236 Map<String, Map<String, Serializable>> res = new HashMap<String, Map<String, Serializable>>(); 237 for (Map.Entry<String, ControlsDescriptor> item : controls.entrySet()) { 238 Map<String, Serializable> props = new HashMap<String, Serializable>(); 239 props.putAll(item.getValue().getControls()); 240 res.put(item.getKey(), props); 241 } 242 return res; 243 } 244 245 public WidgetDefinition[] getSubWidgetDefinitions() { 246 WidgetDefinition[] csubWidgets = null; 247 if (subWidgets != null) { 248 csubWidgets = new WidgetDefinition[subWidgets.length]; 249 for (int i = 0; i < subWidgets.length; i++) { 250 csubWidgets[i] = subWidgets[i].getWidgetDefinition(); 251 } 252 } 253 return csubWidgets; 254 } 255 256 public WidgetReference[] getSubWidgetReferences() { 257 WidgetReference[] csubWidgets = null; 258 if (subWidgetRefs != null) { 259 csubWidgets = new WidgetReference[subWidgetRefs.length]; 260 for (int i = 0; i < subWidgetRefs.length; i++) { 261 csubWidgets[i] = new WidgetReferenceImpl(subWidgetRefs[i].getCategory(), subWidgetRefs[i].getName()); 262 } 263 } 264 return csubWidgets; 265 } 266 267 public static Map<String, Serializable> getProperties(Map<String, PropertiesDescriptor> map, String mode) { 268 if (map == null) { 269 return null; 270 } 271 PropertiesDescriptor defaultProps = map.get(BuiltinModes.ANY); 272 PropertiesDescriptor props = map.get(mode); 273 274 if (defaultProps == null && props == null) { 275 return null; 276 } else if (defaultProps == null) { 277 return props.getProperties(); 278 } else if (props == null) { 279 return defaultProps.getProperties(); 280 } else { 281 // take any mode values, and override with given mode values 282 Map<String, Serializable> res = new HashMap<String, Serializable>(defaultProps.getProperties()); 283 res.putAll(props.getProperties()); 284 return res; 285 } 286 } 287 288 public static Map<String, Map<String, Serializable>> getProperties(Map<String, PropertiesDescriptor> map) { 289 if (map == null) { 290 return null; 291 } 292 Map<String, Map<String, Serializable>> res = new HashMap<String, Map<String, Serializable>>(); 293 for (Map.Entry<String, PropertiesDescriptor> item : map.entrySet()) { 294 Map<String, Serializable> props = new HashMap<String, Serializable>(); 295 props.putAll(item.getValue().getProperties()); 296 res.put(item.getKey(), props); 297 } 298 return res; 299 } 300 301 public WidgetSelectOption[] getSelectOptions() { 302 return selectOptions; 303 } 304 305 @XContent("selectOptions") 306 public void setSelectOptions(DocumentFragment selectOptionsDOM) { 307 XMap xmap = new XMap(); 308 xmap.register(WidgetSelectOptionDescriptor.class); 309 xmap.register(WidgetSelectOptionsDescriptor.class); 310 Node p = selectOptionsDOM.getFirstChild(); 311 List<WidgetSelectOption> options = new ArrayList<WidgetSelectOption>(); 312 while (p != null) { 313 if (p.getNodeType() == Node.ELEMENT_NODE) { 314 Object desc = xmap.load((Element) p); 315 if (desc instanceof WidgetSelectOptionDescriptor) { 316 options.add(((WidgetSelectOptionDescriptor) desc).getWidgetSelectOption()); 317 } else if (desc instanceof WidgetSelectOptionsDescriptor) { 318 options.add(((WidgetSelectOptionsDescriptor) desc).getWidgetSelectOption()); 319 } else { 320 log.error("Unknown resolution of select option"); 321 } 322 } 323 p = p.getNextSibling(); 324 } 325 selectOptions = options.toArray(new WidgetSelectOption[0]); 326 } 327 328 /** 329 * Returns the categories for this widget type, so that it can be stored in the corresponding registry. 330 * 331 * @since 5.5 332 */ 333 public String[] getCategories() { 334 return categories; 335 } 336 337 /** 338 * @since 6.0 339 */ 340 public List<String> getAliases() { 341 return aliases; 342 } 343 344 public WidgetDefinition getWidgetDefinition() { 345 Map<String, String> clabels = null; 346 if (labels != null) { 347 clabels = new HashMap<String, String>(); 348 clabels.putAll(labels); 349 } 350 Map<String, String> chelpLabels = null; 351 if (helpLabels != null) { 352 chelpLabels = new HashMap<String, String>(); 353 chelpLabels.putAll(helpLabels); 354 } 355 Map<String, String> cmodes = null; 356 if (modes != null) { 357 cmodes = new HashMap<String, String>(); 358 cmodes.putAll(modes); 359 } 360 FieldDefinition[] cfieldDefinitions = getFieldDefinitions(); 361 WidgetDefinition[] csubWidgets = getSubWidgetDefinitions(); 362 WidgetReference[] csubwidgetRefs = getSubWidgetReferences(); 363 WidgetSelectOption[] cselectOptions = null; 364 if (selectOptions != null) { 365 cselectOptions = new WidgetSelectOption[selectOptions.length]; 366 for (int i = 0; i < selectOptions.length; i++) { 367 cselectOptions[i] = selectOptions[i].clone(); 368 } 369 } 370 Map<String, List<RenderingInfo>> crenderingInfos = null; 371 if (renderingInfos != null) { 372 crenderingInfos = new HashMap<String, List<RenderingInfo>>(); 373 for (Map.Entry<String, RenderingInfosDescriptor> item : renderingInfos.entrySet()) { 374 RenderingInfosDescriptor infos = item.getValue(); 375 List<RenderingInfo> clonedInfos = null; 376 if (infos != null) { 377 clonedInfos = new ArrayList<RenderingInfo>(); 378 for (RenderingInfoDescriptor info : infos.getRenderingInfos()) { 379 clonedInfos.add(info.getRenderingInfo()); 380 } 381 } 382 crenderingInfos.put(item.getKey(), clonedInfos); 383 } 384 } 385 WidgetDefinitionImpl clone = new WidgetDefinitionImpl(name, type, clabels, chelpLabels, translated, cmodes, 386 cfieldDefinitions, getProperties(), getWidgetModeProperties(), csubWidgets, cselectOptions); 387 clone.setRenderingInfos(crenderingInfos); 388 clone.setSubWidgetReferences(csubwidgetRefs); 389 clone.setHandlingLabels(handlingLabels); 390 clone.setControls(getControls()); 391 clone.setTypeCategory(typeCategory); 392 if (aliases != null) { 393 clone.setAliases(new ArrayList<String>(aliases)); 394 } 395 return clone; 396 } 397}