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.readers; 017 018import java.util.HashSet; 019import java.util.List; 020import java.util.Set; 021 022import org.apache.cocoon.environment.Request; 023 024import org.ametys.plugins.workflow.dao.WorkflowStepDAO; 025import org.ametys.runtime.i18n.I18nizableText; 026 027import com.opensymphony.workflow.loader.ActionDescriptor; 028import com.opensymphony.workflow.loader.ResultDescriptor; 029import com.opensymphony.workflow.loader.StepDescriptor; 030import com.opensymphony.workflow.loader.WorkflowDescriptor; 031 032/** 033 * Read step mindMap from plantUML 034 */ 035public class PlantUmlMindMapStepSVGReader extends AbstractPlantUMLMindmapSVGReader 036{ 037 @Override 038 protected String _getPlantUMLStyle() 039 { 040 StringBuilder style = new StringBuilder(); 041 style.append("<style> \n"); 042 style.append("mindmapDiagram { \n"); 043 style.append("boxless { \n"); 044 style.append("HyperLinkColor #0a7fb2 \n"); 045 style.append("hyperlinkUnderlineThickness 0 \n"); //workaround until HyperlinkUnderline false is fixed 046 style.append("} \n"); 047 style.append("} \n"); 048 style.append("</style> \n"); 049 return style.toString(); 050 } 051 052 @Override 053 protected String _getPlantUMLGraphContent(Request request, WorkflowDescriptor workflowDescriptor) 054 { 055 StringBuilder content = new StringBuilder(); 056 String workflowName = workflowDescriptor.getName(); 057 int stepId = Integer.valueOf((String) request.get("stepId")); 058 String stepIdAsString = stepId != 0 ? String.valueOf(stepId) : WorkflowStepDAO.INITIAL_STEP_ID; 059 060 // Graph for central step node 061 String nodeContent = _getMindMapNodeContent( 062 "+", 063 _getStepNodeLabel(workflowDescriptor, stepId), 064 _workflowStepDAO.getStepIconPathAsBase64(workflowDescriptor, stepId), 065 __MAIN_STEP_NODE_COLOR, 066 null, 067 null, 068 false 069 ); 070 071 content.append(nodeContent); 072 073 // Graph for incoming actions 074 Set<ActionDescriptor> incomingActions = _getIncomingActions(stepId, workflowDescriptor); 075 for (ActionDescriptor action : incomingActions) 076 { 077 String firstParentStepId = _getFirstParentStepId(stepId, workflowDescriptor.getSteps(), action.getId()); 078 String actionNode = _getMindMapNodeContent( 079 "--", 080 _getActionLabel(action), 081 _workflowStepDAO.getActionIconPathAsBase64(action), 082 null, 083 _getJsFunction(workflowName, firstParentStepId, String.valueOf(action.getId())), 084 _i18nUtils.translate(new I18nizableText("plugin.workflow", "PLUGIN_WORKFLOW_LINK_SEE_TRANSITION")), 085 true 086 ); 087 content.append(actionNode); 088 } 089 090 // Graph for outgoing action 091 List<ActionDescriptor> outgoingActions = _getOutgoingActions(stepId, workflowDescriptor); 092 for (ActionDescriptor action : outgoingActions) 093 { 094 String actionNode = _getMindMapNodeContent( 095 "++", 096 _getActionLabel(action), 097 _workflowStepDAO.getActionIconPathAsBase64(action), 098 null, 099 _getJsFunction(workflowName, stepIdAsString, String.valueOf(action.getId())), 100 _i18nUtils.translate(new I18nizableText("plugin.workflow", "PLUGIN_WORKFLOW_LINK_SEE_TRANSITION")), 101 true 102 ); 103 content.append(actionNode); 104 } 105 return content.toString(); 106 } 107 108 /** 109 * Get a set of actions incoming to current step 110 * @param stepId id of current step 111 * @param workflow current workflow 112 * @return the set of outgoing actions 113 */ 114 @SuppressWarnings("unchecked") 115 protected Set<ActionDescriptor> _getIncomingActions(int stepId , WorkflowDescriptor workflow) 116 { 117 Set<ActionDescriptor> incomingActions = new HashSet<>(); 118 if (stepId != 0) 119 { 120 incomingActions.addAll(_getIncomingActionsFromList(stepId, workflow.getInitialActions())); 121 List<StepDescriptor> steps = workflow.getSteps(); 122 for (StepDescriptor otherSteps : steps) 123 { 124 if (otherSteps.getId() != stepId) 125 { 126 List<ActionDescriptor> actions = otherSteps.getActions(); 127 incomingActions.addAll(_getIncomingActionsFromList(stepId, actions)); 128 } 129 } 130 } 131 return incomingActions; 132 } 133 134 /** 135 * Get a list of actions outgoing from current step 136 * @param stepId id of current step 137 * @param workflow current workflow 138 * @return the list of outgoing actions 139 */ 140 protected List<ActionDescriptor> _getOutgoingActions(int stepId, WorkflowDescriptor workflow) 141 { 142 return stepId != 0 143 ? workflow.getStep(stepId).getActions() 144 : workflow.getInitialActions(); 145 } 146 147 /** 148 * Get id of the first step having current action, INITIAL_STEP_ID if current action is an initial action 149 * @param stepId id of current step 150 * @param steps list of all the steps in current workflow 151 * @param actionId id of current action 152 * @return the id of the first found step having current action 153 */ 154 protected String _getFirstParentStepId(int stepId, List<StepDescriptor> steps, Integer actionId) 155 { 156 String firstParentStepId = ""; 157 int i = 0; 158 do 159 { 160 StepDescriptor otherStep = steps.get(i); 161 if (otherStep.getId() != stepId && otherStep.getAction(actionId) != null) 162 { 163 firstParentStepId = String.valueOf(otherStep.getId()); 164 } 165 i++; 166 } while (firstParentStepId.isEmpty() && i < steps.size()); 167 return firstParentStepId.isBlank() ? WorkflowStepDAO.INITIAL_STEP_ID : firstParentStepId; 168 } 169 170 /** 171 * Get a set of incoming actions if present in actions list 172 * @param stepId id of current step 173 * @param actions list of other step's actions 174 * @return a list containing other step's outgoing actions that are incoming to current step 175 */ 176 protected Set<ActionDescriptor> _getIncomingActionsFromList(int stepId, List<ActionDescriptor> actions) 177 { 178 Set<ActionDescriptor> incoming = new HashSet<>(); 179 for (ActionDescriptor action : actions) 180 { 181 ResultDescriptor unconditionalResult = action.getUnconditionalResult(); 182 if (unconditionalResult.getStep() == stepId) 183 { 184 incoming.add(action); 185 } 186 else 187 { 188 boolean leadToStep = false; 189 List<ResultDescriptor> conditionalResults = action.getConditionalResults(); 190 int indexResult = 0; 191 while (!leadToStep && indexResult < conditionalResults.size()) 192 { 193 if (conditionalResults.get(indexResult).getStep() == stepId) 194 { 195 incoming.add(action); 196 leadToStep = true; 197 } 198 indexResult++; 199 } 200 } 201 } 202 return incoming; 203 } 204}