/*
* (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Nuxeo - initial API and implementation
*/
package org.nuxeo.ecm.platform.actions;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.nuxeo.common.xmap.annotation.XNode;
import org.nuxeo.common.xmap.annotation.XNodeList;
import org.nuxeo.common.xmap.annotation.XObject;
import org.nuxeo.runtime.api.Framework;
/**
* Descriptor for action.
*
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
@XObject("action")
public class Action implements Serializable, Cloneable, Comparable<Action> {
public static final String[] EMPTY_CATEGORIES = new String[0];
private static final long serialVersionUID = 1L;
@XNode("@id")
protected String id = "";
protected String link = null;
@XNode("@enabled")
protected Boolean enabled;
@XNode("@label")
protected String label;
@XNode("@icon")
protected String icon;
@XNode("@confirm")
protected String confirm;
@XNode("@help")
protected String help;
@XNode("@immediate")
protected Boolean immediate;
@XNode("@accessKey")
protected String accessKey;
/**
* @since 5.6
*/
@XNode("@type")
protected String type = null;
/**
* @since 5.6
*/
@XNode("properties")
protected ActionPropertiesDescriptor properties;
/**
* Extra set of properties to be used by API, when creating actions on the fly without contributions to the service.
*
* @since 5.6
*/
protected Map<String, Serializable> localProperties;
/**
* @since 5.6
*/
protected Map<String, Serializable> propertiesCache;
protected boolean available = true;
/**
* @since 8.2
*/
protected boolean filtered = false;
/**
* Attribute that provides a hint for action ordering.
* <p>
* :XXX: Action ordering remains a problem. We will continue to use the existing strategy of, by default, ordering
* actions by specificity of registration and order of definition.
*/
@XNode("@order")
protected int order = 0;
@XNodeList(value = "category", type = String[].class, componentType = String.class)
protected String[] categories = EMPTY_CATEGORIES;
// 'action -> filter(s)' association
@XNodeList(value = "filter-id", type = ArrayList.class, componentType = String.class)
protected List<String> filterIds;
@XNodeList(value = "filter", type = ActionFilter[].class, componentType = DefaultActionFilter.class)
protected ActionFilter[] filters;
public Action() {
}
public Action(String id, String[] categories) {
this.id = id;
this.categories = categories;
}
/**
* Returns true if the enabled element was set on the descriptor, useful for merging.
*
* @since 5.8
*/
public boolean isEnableSet() {
return enabled != null;
}
public boolean isEnabled() {
return enabled == null || Boolean.TRUE.equals(enabled);
}
public void setEnabled(boolean enabled) {
this.enabled = Boolean.valueOf(enabled);
}
protected String getStringProperty(String prop) {
Map<String, Serializable> props = getProperties();
if (props != null && props.containsKey(prop)) {
Object obj = props.get(prop);
if (obj instanceof String) {
return (String) obj;
}
}
return null;
}
public String getLabel() {
if (label == null) {
return getStringProperty("label");
}
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getIcon() {
if (icon == null) {
return getStringProperty("icon");
}
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
/**
* Returns the link for this action.
* <p>
* Since 5.7.3, fallbacks on properties when link is not set and retrieve it using key "link".
*/
public String getLink() {
if (link == null) {
return getStringProperty("link");
}
return link;
}
@XNode("@link")
public void setLink(String link) {
if (link != null) {
this.link = Framework.expandVars(link);
}
}
public String[] getCategories() {
return categories;
}
/**
* Returns the categories as a list.
*
* @since 7.2
*/
public List<String> getCategoryList() {
if (categories == null) {
return null;
}
return Arrays.asList(categories);
}
public String getId() {
return id;
}
@Override
public String toString() {
return id;
}
/**
* Returns the action order.
*
* @return the action order as an integer value
*/
public int getOrder() {
return order;
}
/**
* Sets the order of the action.
*
* @param order order of the action
*/
public void setOrder(int order) {
this.order = order;
}
public int compareTo(Action anotherAction) {
int cmp = order - anotherAction.order;
if (cmp == 0) {
// make sure we have a deterministic sort
cmp = id.compareTo(anotherAction.id);
}
return cmp;
}
public List<String> getFilterIds() {
return filterIds;
}
public void setFilterIds(List<String> filterIds) {
this.filterIds = filterIds;
}
public ActionFilter[] getFilters() {
return filters;
}
public void setFilters(ActionFilter[] filters) {
this.filters = filters;
}
public void setCategories(String[] categories) {
this.categories = categories;
}
/**
* Returns the confirm javascript for this element.
* <p>
* Since 5.7.3, fallbacks on properties when link is not set and retrieve it using key "confirm".
*/
public String getConfirm() {
if (confirm == null) {
String conf = getStringProperty("confirm");
if (conf == null) {
conf = "";
}
return conf;
} else {
return confirm;
}
}
public void setConfirm(String confirm) {
this.confirm = confirm;
}
public boolean getAvailable() {
return available;
}
public void setAvailable(boolean available) {
this.available = available;
}
/**
* @since 8.2
*/
public boolean isFiltered() {
return filtered;
}
/**
* @since 8.2
*/
public void setFiltered(boolean filtered) {
this.filtered = filtered;
}
public String getHelp() {
if (help == null) {
String conf = getStringProperty("help");
if (conf == null) {
conf = "";
}
return conf;
} else {
return help;
}
}
public void setHelp(String title) {
help = title;
}
public boolean isImmediate() {
if (immediate == null) {
Map<String, Serializable> props = getProperties();
if (props != null && props.containsKey("immediate")) {
Object obj = props.get("immediate");
if (obj instanceof String) {
return Boolean.valueOf((String) obj).booleanValue();
} else if (obj instanceof Boolean) {
return ((Boolean) obj).booleanValue();
}
}
return false;
}
return immediate.booleanValue();
}
public void setImmediate(boolean immediate) {
this.immediate = Boolean.valueOf(immediate);
}
/**
* @since 5.6
*/
public String getType() {
return type;
}
/**
* @since 5.6
*/
public void setType(String type) {
this.type = type;
}
/**
* @since 5.6
*/
public ActionPropertiesDescriptor getPropertiesDescriptor() {
return properties;
}
/**
* @since 5.6
*/
public void setPropertiesDescriptor(ActionPropertiesDescriptor properties) {
this.properties = properties;
this.propertiesCache = null;
}
/**
* Sets local properties programatically
*
* @since 5.6
*/
public void setProperties(Map<String, Serializable> localProperties) {
this.localProperties = localProperties;
this.propertiesCache = null;
}
/**
* Returns an aggregate of {@link #localProperties} and {@link #properties} set via descriptors.
*
* @since 5.6
*/
public Map<String, Serializable> getProperties() {
if (propertiesCache == null) {
propertiesCache = new HashMap<>();
if (properties != null) {
propertiesCache.putAll(properties.getAllProperties());
}
if (localProperties != null) {
propertiesCache.putAll(localProperties);
}
}
return propertiesCache;
}
/**
* @since 5.6
*/
public void setAccessKey(String accessKey) {
this.accessKey = accessKey;
}
/**
* @since 5.6
*/
public String getAccessKey() {
if (accessKey == null) {
return getStringProperty("accessKey");
}
return accessKey;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null) {
return false;
}
if (!(other instanceof Action)) {
return false;
}
Action otherAction = (Action) other;
return id == null ? otherAction.id == null : id.equals(otherAction.id);
}
@Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
@Override
public Action clone() {
Action clone = new Action();
clone.id = id;
clone.link = link;
clone.enabled = enabled;
clone.label = label;
clone.icon = icon;
clone.confirm = confirm;
clone.help = help;
clone.immediate = immediate;
clone.accessKey = accessKey;
clone.type = type;
if (properties != null) {
clone.properties = properties.clone();
}
clone.available = available;
clone.filtered = filtered;
clone.order = order;
if (categories != null) {
clone.categories = categories.clone();
}
if (filterIds != null) {
clone.filterIds = new ArrayList<>(filterIds);
}
if (filters != null) {
clone.filters = new ActionFilter[filters.length];
for (int i = 0; i < filters.length; i++) {
clone.filters[i] = filters[i].clone();
}
}
return clone;
}
}