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