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