001/* 002 * Copyright 2023 Anyware Services 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 */ 016package org.ametys.plugins.workflow.dao; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022 023import org.apache.avalon.framework.component.Component; 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.avalon.framework.service.Serviceable; 027 028import org.ametys.core.ui.Callable; 029import org.ametys.plugins.workflow.EnhancedCondition; 030import org.ametys.plugins.workflow.EnhancedCondition.ConditionArgument; 031import org.ametys.plugins.workflow.support.AvalonTypeResolver; 032import org.ametys.plugins.workflow.support.WorkflowHelper; 033import org.ametys.runtime.i18n.I18nizableText; 034import org.ametys.runtime.plugin.component.AbstractLogEnabled; 035 036import com.opensymphony.workflow.Condition; 037import com.opensymphony.workflow.TypeResolver; 038import com.opensymphony.workflow.WorkflowException; 039import com.opensymphony.workflow.loader.AbstractDescriptor; 040import com.opensymphony.workflow.loader.ActionDescriptor; 041import com.opensymphony.workflow.loader.ConditionDescriptor; 042import com.opensymphony.workflow.loader.ConditionsDescriptor; 043import com.opensymphony.workflow.loader.RestrictionDescriptor; 044import com.opensymphony.workflow.loader.WorkflowDescriptor; 045 046/** 047 * DAO for workflow conditions 048 */ 049public class WorkflowConditionDAO extends AbstractLogEnabled implements Component, Serviceable 050{ 051 /** The component role */ 052 public static final String ROLE = WorkflowConditionDAO.class.getName(); 053 054 /** The "and" type of condition */ 055 protected static final String __AND = "AND"; 056 057 /** The "or" type of condition */ 058 protected static final String __OR = "OR"; 059 060 /** Key for "and" label in tree */ 061 protected static final String __ANDI18N = "PLUGIN_WORKFLOW_TRANSITION_CONDITIONS_TYPE_AND"; 062 063 /** Key for "or" label in tree */ 064 protected static final String __ORI18N = "PLUGIN_WORKFLOW_TRANSITION_CONDITIONS_TYPE_OR"; 065 066 /** The workflow helper */ 067 protected WorkflowHelper _workflowHelper; 068 069 /** The service manager */ 070 protected ServiceManager _manager; 071 072 public void service(ServiceManager smanager) throws ServiceException 073 { 074 _workflowHelper = (WorkflowHelper) smanager.lookup(WorkflowHelper.ROLE); 075 _manager = smanager; 076 } 077 078 /** 079 * Get the tree's condition nodes 080 * @param currentNode id of the current node 081 * @param workflowName unique name of current workflow 082 * @param actionId id of current action 083 * @return a map of current node's children 084 */ 085 @Callable(right = "Workflow_Right_Read") 086 public Map<String, Object> getConditionNodes(String currentNode, String workflowName, String actionId) 087 { 088 WorkflowDescriptor workflowDescriptor = _workflowHelper.getWorkflowDescriptor(workflowName); 089 ActionDescriptor action = workflowDescriptor.getAction(Integer.valueOf(actionId)); 090 091 List<Map<String, Object>> nodes = new ArrayList<>(); 092 093 List<AbstractDescriptor> conditions = _getConditions(currentNode, action); 094 for (int i = 0; i < conditions.size(); i++) 095 { 096 nodes.add(conditionToJSON(conditions.get(i), currentNode, i)); 097 } 098 099 return Map.of("conditions", nodes); 100 } 101 102 /** 103 * Get conditions below current node 104 * @param currentNode id of the current node 105 * @param action current action 106 * @return a list of childnodes condition 107 */ 108 protected List<AbstractDescriptor> _getConditions(String currentNode, ActionDescriptor action) 109 { 110 RestrictionDescriptor restriction = action.getRestriction(); 111 if (restriction != null) 112 { 113 ConditionsDescriptor rootConditionsDescriptor = restriction.getConditionsDescriptor(); 114 115 String[] path = currentNode.split("-"); 116 // The current node is root and it's a OR node, so display it ... 117 if ("root".equals(currentNode) && rootConditionsDescriptor.getType().equals(__OR)) 118 { 119 return List.of(rootConditionsDescriptor); 120 } 121 // ... the current node is a AND node, display child conditions ... 122 else if (path.length == 1) 123 { 124 return rootConditionsDescriptor.getConditions(); 125 } 126 // ... the selected node is not a condition, so it has children 127 // we need to search the condition and get child condition of current node 128 else if (!path[path.length - 1].startsWith("condition")) 129 { 130 List<AbstractDescriptor> conditions = rootConditionsDescriptor.getConditions(); 131 // get conditions for current node 132 int i = 1; 133 do 134 { 135 String currentOrAndConditionId = path[i]; 136 int currentOrAndConditionIndex = (currentOrAndConditionId.startsWith("and")) 137 ? Integer.valueOf(currentOrAndConditionId.substring(3)) 138 : Integer.valueOf(currentOrAndConditionId.substring(2)); 139 140 ConditionsDescriptor currentOrAndCondition = (ConditionsDescriptor) conditions.get(currentOrAndConditionIndex); 141 conditions = currentOrAndCondition.getConditions(); 142 i++; 143 } 144 while (i < path.length); 145 146 return conditions; 147 } 148 } 149 150 return List.of(); 151 } 152 153 /** 154 * Get condition or condition types properties 155 * @param condition current condition, can be ConditionsDescriptor or ConditionDescriptor 156 * @param currentNodeId the id of the current node in the ConditionTreePanel 157 * @param index index of current condition in node's condition list 158 * @return a map of the condition infos 159 */ 160 public Map<String, Object> conditionToJSON(AbstractDescriptor condition, String currentNodeId, int index) 161 { 162 Map<String, Object> infosConditions = new HashMap<>(); 163 164 // if it's a 'and' or a 'or' 165 if (condition instanceof ConditionsDescriptor) 166 { 167 String type = ((ConditionsDescriptor) condition).getType(); 168 if (type.equals(__AND)) 169 { 170 String id = "root".equals(currentNodeId) ? "and0-and" + index : currentNodeId + "-and" + index; 171 infosConditions.put("id", id); 172 I18nizableText i18nLabel = new I18nizableText("plugin.workflow", __ANDI18N); 173 infosConditions.put("label", i18nLabel); 174 } 175 else 176 { 177 String id = "root".equals(currentNodeId) ? "or0" : currentNodeId + "-or" + index; 178 infosConditions.put("id", id); 179 I18nizableText i18nLabel = new I18nizableText("plugin.workflow", __ORI18N); 180 infosConditions.put("label", i18nLabel); 181 } 182 infosConditions.put("hasChildren", true); 183 } 184 else //it's a condition 185 { 186 String id = "root".equals(currentNodeId) ? "condition" + index : currentNodeId + "-condition" + index; 187 infosConditions.put("id", id); 188 infosConditions.put("label", _getConditionLabel((ConditionDescriptor) condition)); 189 infosConditions.put("hasChildren", false); 190 } 191 192 return infosConditions; 193 } 194 195 /** 196 * Get condition's description or role 197 * @param condition the current condition 198 * @return the condition description as I18nizableText if exist, or its role as String if not 199 */ 200 protected Object _getConditionLabel(ConditionDescriptor condition) 201 { 202 String role = (String) condition.getArgs().get("role"); 203 TypeResolver typeResolver = new AvalonTypeResolver(_manager); 204 try 205 { 206 Condition function = typeResolver.getCondition(condition.getType(), condition.getArgs()); 207 if (function instanceof EnhancedCondition) 208 { 209 List<ConditionArgument> arguments = ((EnhancedCondition) function).getArguments(); 210 Map<String, String> values = new HashMap<>(); 211 for (ConditionArgument arg : arguments) 212 { 213 values.put(arg.name(), (String) condition.getArgs().get(arg.name())); 214 } 215 I18nizableText description = ((EnhancedCondition) function).getDescription(values); 216 217 return description != null ? description : role; 218 } 219 } 220 catch (WorkflowException e) 221 { 222 getLogger().error("An error occured while resolving condition with role {}", role, e); 223 } 224 return role; 225 } 226}