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 * @since 8.2 106 */ 107 protected boolean filtered = false; 108 109 /** 110 * Attribute that provides a hint for action ordering. 111 * <p> 112 * :XXX: Action ordering remains a problem. We will continue to use the existing strategy of, by default, ordering 113 * actions by specificity of registration and order of definition. 114 */ 115 @XNode("@order") 116 protected int order = 0; 117 118 @XNodeList(value = "category", type = String[].class, componentType = String.class) 119 protected String[] categories = EMPTY_CATEGORIES; 120 121 // 'action -> filter(s)' association 122 123 @XNodeList(value = "filter-id", type = ArrayList.class, componentType = String.class) 124 protected List<String> filterIds; 125 126 @XNodeList(value = "filter", type = ActionFilter[].class, componentType = DefaultActionFilter.class) 127 protected ActionFilter[] filters; 128 129 public Action() { 130 } 131 132 public Action(String id, String[] categories) { 133 this.id = id; 134 this.categories = categories; 135 } 136 137 /** 138 * Returns true if the enabled element was set on the descriptor, useful for merging. 139 * 140 * @since 5.8 141 */ 142 public boolean isEnableSet() { 143 return enabled != null; 144 } 145 146 public boolean isEnabled() { 147 return enabled == null || Boolean.TRUE.equals(enabled); 148 } 149 150 public void setEnabled(boolean enabled) { 151 this.enabled = Boolean.valueOf(enabled); 152 } 153 154 protected String getStringProperty(String prop) { 155 Map<String, Serializable> props = getProperties(); 156 if (props != null && props.containsKey(prop)) { 157 Object obj = props.get(prop); 158 if (obj instanceof String) { 159 return (String) obj; 160 } 161 } 162 return null; 163 } 164 165 public String getLabel() { 166 if (label == null) { 167 return getStringProperty("label"); 168 } 169 return label; 170 } 171 172 public void setLabel(String label) { 173 this.label = label; 174 } 175 176 public String getIcon() { 177 if (icon == null) { 178 return getStringProperty("icon"); 179 } 180 return icon; 181 } 182 183 public void setIcon(String icon) { 184 this.icon = icon; 185 } 186 187 /** 188 * Returns the link for this action. 189 * <p> 190 * Since 5.7.3, fallbacks on properties when link is not set and retrieve it using key "link". 191 */ 192 public String getLink() { 193 if (link == null) { 194 return getStringProperty("link"); 195 } 196 return link; 197 } 198 199 @XNode("@link") 200 public void setLink(String link) { 201 if (link != null) { 202 this.link = Framework.expandVars(link); 203 } 204 } 205 206 public String[] getCategories() { 207 return categories; 208 } 209 210 /** 211 * Returns the categories as a list. 212 * 213 * @since 7.2 214 */ 215 public List<String> getCategoryList() { 216 if (categories == null) { 217 return null; 218 } 219 return Arrays.asList(categories); 220 } 221 222 public String getId() { 223 return id; 224 } 225 226 @Override 227 public String toString() { 228 return id; 229 } 230 231 /** 232 * Returns the action order. 233 * 234 * @return the action order as an integer value 235 */ 236 public int getOrder() { 237 return order; 238 } 239 240 /** 241 * Sets the order of the action. 242 * 243 * @param order order of the action 244 */ 245 public void setOrder(int order) { 246 this.order = order; 247 } 248 249 public int compareTo(Action anotherAction) { 250 int cmp = order - anotherAction.order; 251 if (cmp == 0) { 252 // make sure we have a deterministic sort 253 cmp = id.compareTo(anotherAction.id); 254 } 255 return cmp; 256 } 257 258 public List<String> getFilterIds() { 259 return filterIds; 260 } 261 262 public void setFilterIds(List<String> filterIds) { 263 this.filterIds = filterIds; 264 } 265 266 public ActionFilter[] getFilters() { 267 return filters; 268 } 269 270 public void setFilters(ActionFilter[] filters) { 271 this.filters = filters; 272 } 273 274 public void setCategories(String[] categories) { 275 this.categories = categories; 276 } 277 278 /** 279 * @deprecated since 5.6: useless now that EL expressions support parameters 280 */ 281 @Deprecated 282 @SuppressWarnings("rawtypes") 283 public Class[] getLinkParams() { 284 return linkParams; 285 } 286 287 /** 288 * @deprecated since 5.6: useless now that EL expressions support parameters 289 */ 290 @Deprecated 291 public void setLinkParams(Class<?>[] linkParams) { 292 this.linkParams = linkParams; 293 } 294 295 /** 296 * Returns the confirm javascript for this element. 297 * <p> 298 * Since 5.7.3, fallbacks on properties when link is not set and retrieve it using key "confirm". 299 */ 300 public String getConfirm() { 301 if (confirm == null) { 302 String conf = getStringProperty("confirm"); 303 if (conf == null) { 304 conf = ""; 305 } 306 return conf; 307 } else { 308 return confirm; 309 } 310 } 311 312 public void setConfirm(String confirm) { 313 this.confirm = confirm; 314 } 315 316 public boolean getAvailable() { 317 return available; 318 } 319 320 public void setAvailable(boolean available) { 321 this.available = available; 322 } 323 324 /** 325 * @since 8.2 326 */ 327 public boolean isFiltered() { 328 return filtered; 329 } 330 331 /** 332 * @since 8.2 333 */ 334 public void setFiltered(boolean filtered) { 335 this.filtered = filtered; 336 } 337 338 public String getHelp() { 339 if (help == null) { 340 String conf = getStringProperty("help"); 341 if (conf == null) { 342 conf = ""; 343 } 344 return conf; 345 } else { 346 return help; 347 } 348 } 349 350 public void setHelp(String title) { 351 help = title; 352 } 353 354 public boolean isImmediate() { 355 if (immediate == null) { 356 Map<String, Serializable> props = getProperties(); 357 if (props != null && props.containsKey("immediate")) { 358 Object obj = props.get("immediate"); 359 if (obj instanceof String) { 360 return Boolean.valueOf((String) obj).booleanValue(); 361 } else if (obj instanceof Boolean) { 362 return ((Boolean) obj).booleanValue(); 363 } 364 } 365 return false; 366 } 367 return immediate.booleanValue(); 368 } 369 370 public void setImmediate(boolean immediate) { 371 this.immediate = Boolean.valueOf(immediate); 372 } 373 374 /** 375 * @since 5.6 376 */ 377 public String getType() { 378 return type; 379 } 380 381 /** 382 * @since 5.6 383 */ 384 public void setType(String type) { 385 this.type = type; 386 } 387 388 /** 389 * @since 5.6 390 */ 391 public ActionPropertiesDescriptor getPropertiesDescriptor() { 392 return properties; 393 } 394 395 /** 396 * @since 5.6 397 */ 398 public void setPropertiesDescriptor(ActionPropertiesDescriptor properties) { 399 this.properties = properties; 400 this.propertiesCache = null; 401 } 402 403 /** 404 * Sets local properties programatically 405 * 406 * @since 5.6 407 */ 408 public void setProperties(Map<String, Serializable> localProperties) { 409 this.localProperties = localProperties; 410 this.propertiesCache = null; 411 } 412 413 /** 414 * Returns an aggregate of {@link #localProperties} and {@link #properties} set via descriptors. 415 * 416 * @since 5.6 417 */ 418 public Map<String, Serializable> getProperties() { 419 if (propertiesCache == null) { 420 propertiesCache = new HashMap<String, Serializable>(); 421 if (properties != null) { 422 propertiesCache.putAll(properties.getAllProperties()); 423 } 424 if (localProperties != null) { 425 propertiesCache.putAll(localProperties); 426 } 427 } 428 return propertiesCache; 429 } 430 431 /** 432 * @since 5.6 433 */ 434 public void setAccessKey(String accessKey) { 435 this.accessKey = accessKey; 436 } 437 438 /** 439 * @since 5.6 440 */ 441 public String getAccessKey() { 442 if (accessKey == null) { 443 return getStringProperty("accessKey"); 444 } 445 return accessKey; 446 } 447 448 @Override 449 public boolean equals(Object other) { 450 if (this == other) { 451 return true; 452 } 453 if (other == null) { 454 return false; 455 } 456 if (!(other instanceof Action)) { 457 return false; 458 } 459 Action otherAction = (Action) other; 460 return id == null ? otherAction.id == null : id.equals(otherAction.id); 461 } 462 463 @Override 464 public int hashCode() { 465 return id == null ? 0 : id.hashCode(); 466 } 467 468 @Override 469 public Action clone() { 470 Action clone = new Action(); 471 clone.id = id; 472 clone.link = link; 473 if (linkParams != null) { 474 clone.linkParams = linkParams.clone(); 475 } 476 clone.enabled = enabled; 477 clone.label = label; 478 clone.icon = icon; 479 clone.confirm = confirm; 480 clone.help = help; 481 clone.immediate = immediate; 482 clone.accessKey = accessKey; 483 clone.type = type; 484 if (properties != null) { 485 clone.properties = properties.clone(); 486 } 487 clone.available = available; 488 clone.filtered = filtered; 489 clone.order = order; 490 if (categories != null) { 491 clone.categories = categories.clone(); 492 } 493 if (filterIds != null) { 494 clone.filterIds = new ArrayList<String>(filterIds); 495 } 496 if (filters != null) { 497 clone.filters = new ActionFilter[filters.length]; 498 for (int i = 0; i < filters.length; i++) { 499 clone.filters[i] = filters[i].clone(); 500 } 501 } 502 return clone; 503 } 504 505}