001/* 002 * (C) Copyright 2006-2012 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 * Nuxeo - initial API and implementation 016 * 017 * $Id: Action.java 28512 2008-01-06 11:52:28Z sfermigier $ 018 */ 019 020package org.nuxeo.ecm.platform.actions; 021 022import java.io.Serializable; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.nuxeo.common.xmap.annotation.XNode; 030import org.nuxeo.common.xmap.annotation.XNodeList; 031import org.nuxeo.common.xmap.annotation.XObject; 032import org.nuxeo.runtime.api.Framework; 033 034/** 035 * Descriptor for action. 036 * 037 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 038 */ 039@XObject("action") 040public class Action implements Serializable, Cloneable, Comparable<Action> { 041 042 public static final String[] EMPTY_CATEGORIES = new String[0]; 043 044 private static final long serialVersionUID = 1L; 045 046 @XNode("@id") 047 protected String id = ""; 048 049 protected String link = null; 050 051 @Deprecated 052 @XNodeList(value = "link-params/param", type = Class[].class, componentType = Class.class) 053 private Class<?>[] linkParams; 054 055 @XNode("@enabled") 056 protected Boolean enabled; 057 058 @XNode("@label") 059 protected String label; 060 061 @XNode("@icon") 062 protected String icon; 063 064 @XNode("@confirm") 065 protected String confirm; 066 067 @XNode("@help") 068 protected String help; 069 070 @XNode("@immediate") 071 protected Boolean immediate; 072 073 @XNode("@accessKey") 074 protected String accessKey; 075 076 /** 077 * @since 5.6 078 */ 079 @XNode("@type") 080 protected String type = null; 081 082 /** 083 * @since 5.6 084 */ 085 @XNode("properties") 086 protected ActionPropertiesDescriptor properties; 087 088 /** 089 * Extra set of properties to be used by API, when creating actions on the fly without contributions to the service. 090 * 091 * @since 5.6 092 */ 093 protected Map<String, Serializable> localProperties; 094 095 /** 096 * @since 5.6 097 */ 098 protected Map<String, Serializable> propertiesCache; 099 100 protected boolean available = true; 101 102 /** 103 * Attribute that provides a hint for action ordering. 104 * <p> 105 * :XXX: Action ordering remains a problem. We will continue to use the existing strategy of, by default, ordering 106 * actions by specificity of registration and order of definition. 107 */ 108 @XNode("@order") 109 protected int order = 0; 110 111 @XNodeList(value = "category", type = String[].class, componentType = String.class) 112 protected String[] categories = EMPTY_CATEGORIES; 113 114 // 'action -> filter(s)' association 115 116 @XNodeList(value = "filter-id", type = ArrayList.class, componentType = String.class) 117 protected List<String> filterIds; 118 119 @XNodeList(value = "filter", type = ActionFilter[].class, componentType = DefaultActionFilter.class) 120 protected ActionFilter[] filters; 121 122 public Action() { 123 } 124 125 public Action(String id, String[] categories) { 126 this.id = id; 127 this.categories = categories; 128 } 129 130 /** 131 * Returns true if the enabled element was set on the descriptor, useful for merging. 132 * 133 * @since 5.8 134 */ 135 public boolean isEnableSet() { 136 return enabled != null; 137 } 138 139 public boolean isEnabled() { 140 return enabled == null || Boolean.TRUE.equals(enabled); 141 } 142 143 public void setEnabled(boolean enabled) { 144 this.enabled = Boolean.valueOf(enabled); 145 } 146 147 protected String getStringProperty(String prop) { 148 Map<String, Serializable> props = getProperties(); 149 if (props != null && props.containsKey(prop)) { 150 Object obj = props.get(prop); 151 if (obj instanceof String) { 152 return (String) obj; 153 } 154 } 155 return null; 156 } 157 158 public String getLabel() { 159 if (label == null) { 160 return getStringProperty("label"); 161 } 162 return label; 163 } 164 165 public void setLabel(String label) { 166 this.label = label; 167 } 168 169 public String getIcon() { 170 if (icon == null) { 171 return getStringProperty("icon"); 172 } 173 return icon; 174 } 175 176 public void setIcon(String icon) { 177 this.icon = icon; 178 } 179 180 /** 181 * Returns the link for this action. 182 * <p> 183 * Since 5.7.3, fallbacks on properties when link is not set and retrieve it using key "link". 184 */ 185 public String getLink() { 186 if (link == null) { 187 return getStringProperty("link"); 188 } 189 return link; 190 } 191 192 @XNode("@link") 193 public void setLink(String link) { 194 if (link != null) { 195 this.link = Framework.expandVars(link); 196 } 197 } 198 199 public String[] getCategories() { 200 return categories; 201 } 202 203 /** 204 * Returns the categories as a list. 205 * 206 * @since 7.2 207 */ 208 public List<String> getCategoryList() { 209 if (categories == null) { 210 return null; 211 } 212 return Arrays.asList(categories); 213 } 214 215 public String getId() { 216 return id; 217 } 218 219 @Override 220 public String toString() { 221 return id; 222 } 223 224 /** 225 * Returns the action order. 226 * 227 * @return the action order as an integer value 228 */ 229 public int getOrder() { 230 return order; 231 } 232 233 /** 234 * Sets the order of the action. 235 * 236 * @param order order of the action 237 */ 238 public void setOrder(int order) { 239 this.order = order; 240 } 241 242 public int compareTo(Action anotherAction) { 243 int cmp = order - anotherAction.order; 244 if (cmp == 0) { 245 // make sure we have a deterministic sort 246 cmp = id.compareTo(anotherAction.id); 247 } 248 return cmp; 249 } 250 251 public List<String> getFilterIds() { 252 return filterIds; 253 } 254 255 public void setFilterIds(List<String> filterIds) { 256 this.filterIds = filterIds; 257 } 258 259 public ActionFilter[] getFilters() { 260 return filters; 261 } 262 263 public void setFilters(ActionFilter[] filters) { 264 this.filters = filters; 265 } 266 267 public void setCategories(String[] categories) { 268 this.categories = categories; 269 } 270 271 /** 272 * @deprecated since 5.6: useless now that EL expressions support parameters 273 */ 274 @Deprecated 275 @SuppressWarnings("rawtypes") 276 public Class[] getLinkParams() { 277 return linkParams; 278 } 279 280 /** 281 * @deprecated since 5.6: useless now that EL expressions support parameters 282 */ 283 @Deprecated 284 public void setLinkParams(Class<?>[] linkParams) { 285 this.linkParams = linkParams; 286 } 287 288 /** 289 * Returns the confirm javascript for this element. 290 * <p> 291 * Since 5.7.3, fallbacks on properties when link is not set and retrieve it using key "confirm". 292 */ 293 public String getConfirm() { 294 if (confirm == null) { 295 String conf = getStringProperty("confirm"); 296 if (conf == null) { 297 conf = ""; 298 } 299 return conf; 300 } else { 301 return confirm; 302 } 303 } 304 305 public void setConfirm(String confirm) { 306 this.confirm = confirm; 307 } 308 309 public boolean getAvailable() { 310 return available; 311 } 312 313 public void setAvailable(boolean available) { 314 this.available = available; 315 } 316 317 public String getHelp() { 318 if (help == null) { 319 String conf = getStringProperty("help"); 320 if (conf == null) { 321 conf = ""; 322 } 323 return conf; 324 } else { 325 return help; 326 } 327 } 328 329 public void setHelp(String title) { 330 help = title; 331 } 332 333 public boolean isImmediate() { 334 if (immediate == null) { 335 Map<String, Serializable> props = getProperties(); 336 if (props != null && props.containsKey("immediate")) { 337 Object obj = props.get("immediate"); 338 if (obj instanceof String) { 339 return Boolean.valueOf((String) obj).booleanValue(); 340 } else if (obj instanceof Boolean) { 341 return ((Boolean) obj).booleanValue(); 342 } 343 } 344 return false; 345 } 346 return immediate.booleanValue(); 347 } 348 349 public void setImmediate(boolean immediate) { 350 this.immediate = Boolean.valueOf(immediate); 351 } 352 353 /** 354 * @since 5.6 355 */ 356 public String getType() { 357 return type; 358 } 359 360 /** 361 * @since 5.6 362 */ 363 public void setType(String type) { 364 this.type = type; 365 } 366 367 /** 368 * @since 5.6 369 */ 370 public ActionPropertiesDescriptor getPropertiesDescriptor() { 371 return properties; 372 } 373 374 /** 375 * @since 5.6 376 */ 377 public void setPropertiesDescriptor(ActionPropertiesDescriptor properties) { 378 this.properties = properties; 379 this.propertiesCache = null; 380 } 381 382 /** 383 * Sets local properties programatically 384 * 385 * @since 5.6 386 */ 387 public void setProperties(Map<String, Serializable> localProperties) { 388 this.localProperties = localProperties; 389 this.propertiesCache = null; 390 } 391 392 /** 393 * Returns an aggregate of {@link #localProperties} and {@link #properties} set via descriptors. 394 * 395 * @since 5.6 396 */ 397 public Map<String, Serializable> getProperties() { 398 if (propertiesCache == null) { 399 propertiesCache = new HashMap<String, Serializable>(); 400 if (properties != null) { 401 propertiesCache.putAll(properties.getAllProperties()); 402 } 403 if (localProperties != null) { 404 propertiesCache.putAll(localProperties); 405 } 406 } 407 return propertiesCache; 408 } 409 410 /** 411 * @since 5.6 412 */ 413 public void setAccessKey(String accessKey) { 414 this.accessKey = accessKey; 415 } 416 417 /** 418 * @since 5.6 419 */ 420 public String getAccessKey() { 421 if (accessKey == null) { 422 return getStringProperty("accessKey"); 423 } 424 return accessKey; 425 } 426 427 @Override 428 public boolean equals(Object other) { 429 if (this == other) { 430 return true; 431 } 432 if (other == null) { 433 return false; 434 } 435 if (!(other instanceof Action)) { 436 return false; 437 } 438 Action otherAction = (Action) other; 439 return id == null ? otherAction.id == null : id.equals(otherAction.id); 440 } 441 442 @Override 443 public int hashCode() { 444 return id == null ? 0 : id.hashCode(); 445 } 446 447 @Override 448 public Action clone() { 449 Action clone = new Action(); 450 clone.id = id; 451 clone.link = link; 452 if (linkParams != null) { 453 clone.linkParams = linkParams.clone(); 454 } 455 clone.enabled = enabled; 456 clone.label = label; 457 clone.icon = icon; 458 clone.confirm = confirm; 459 clone.help = help; 460 clone.immediate = immediate; 461 clone.accessKey = accessKey; 462 clone.type = type; 463 if (properties != null) { 464 clone.properties = properties.clone(); 465 } 466 clone.available = available; 467 clone.order = order; 468 if (categories != null) { 469 clone.categories = categories.clone(); 470 } 471 if (filterIds != null) { 472 clone.filterIds = new ArrayList<String>(filterIds); 473 } 474 if (filters != null) { 475 clone.filters = new ActionFilter[filters.length]; 476 for (int i = 0; i < filters.length; i++) { 477 clone.filters[i] = filters[i].clone(); 478 } 479 } 480 return clone; 481 } 482 483}