001/*
002 *  Copyright 2016 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.support;
017
018import java.util.ArrayList;
019import java.util.Date;
020import java.util.HashSet;
021import java.util.Iterator;
022import java.util.List;
023import java.util.Set;
024
025import org.apache.avalon.framework.component.Component;
026import org.apache.avalon.framework.logger.AbstractLogEnabled;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030
031import com.opensymphony.workflow.AbstractWorkflow;
032import com.opensymphony.workflow.FactoryException;
033import com.opensymphony.workflow.Workflow;
034import com.opensymphony.workflow.WorkflowException;
035import com.opensymphony.workflow.loader.ActionDescriptor;
036import com.opensymphony.workflow.loader.StepDescriptor;
037import com.opensymphony.workflow.loader.WorkflowDescriptor;
038import com.opensymphony.workflow.spi.Step;
039import com.opensymphony.workflow.spi.WorkflowStore;
040
041/**
042 * Helper to get information on the workflow structures
043 */
044public class WorkflowHelper extends AbstractLogEnabled implements Component, Serviceable
045{
046    /** The Avalon role */
047    public static final String ROLE = WorkflowHelper.class.getName();
048
049    /** The content types extension point */
050    protected WorkflowProvider _workflowProvider;
051
052    @Override
053    public void service(ServiceManager smanager) throws ServiceException
054    {
055        _workflowProvider = (WorkflowProvider) smanager.lookup(WorkflowProvider.ROLE);
056    }
057    
058    /**
059     * Get a list of workflow names available
060     * @return String[] an array of workflow names.
061     */
062    public String[] getWorkflowNames()
063    {
064        try
065        {
066            return _workflowProvider.getWorkflowFactory().getWorkflowNames();
067        }
068        catch (FactoryException e)
069        {
070            getLogger().error("Error getting workflow names", e);
071        }
072        
073        return new String[0];
074    }
075    
076    /**
077     * Returns a workflow definition object associated with the given name.
078     * @param workflowName the name of the workflow
079     * @return the object graph that represents a workflow definition
080     */
081    public WorkflowDescriptor getWorkflowDescriptor(String workflowName)
082    {
083        try
084        {
085            return _workflowProvider.getWorkflowFactory().getWorkflow(workflowName);
086        }
087        catch (FactoryException e)
088        {
089            getLogger().error("Error loading workflow " + workflowName, e);
090        }
091        
092        return null;
093    }
094    
095    /**
096     * Retrieves all actions of the workflow of a particular type of workflow
097     * except initial actions.
098     * @param workflowName the name of the workflow.
099     * @return all actions ids.
100     * @throws IllegalArgumentException If the workflow name is not valid.
101     */
102    public int[] getAllActions(String workflowName) throws IllegalArgumentException
103    {
104        WorkflowDescriptor workflowDesc = getWorkflowDescriptor(workflowName);
105        
106        if (workflowDesc == null)
107        {
108            throw new IllegalArgumentException("Bad workflow name \"" + workflowName + "\"");
109        }
110        
111        Set<Integer> actions = new HashSet<>();
112        
113        // Récupérer des actions globales
114        Iterator iterGlobalActions = workflowDesc.getGlobalActions().iterator();
115        
116        // Parcourir la liste des actions globales
117        while (iterGlobalActions.hasNext())
118        {
119            // Récupérer la description de l'action globale courante
120            ActionDescriptor actionDesc = (ActionDescriptor) iterGlobalActions.next();
121            
122            // Ajouter l'action globale courante
123            actions.add(Integer.valueOf(actionDesc.getId()));
124        }
125        
126        // Récupérer la liste des steps
127        Iterator iterSteps = workflowDesc.getSteps().iterator();
128        
129        // Parcourir la liste des steps
130        while (iterSteps.hasNext())
131        {
132            // Récupérer le step courant
133            StepDescriptor stepDesc = (StepDescriptor) iterSteps.next();
134            
135            // Récupérer la liste des actions associés au step courant
136            Iterator iterActions = stepDesc.getActions().iterator();
137            
138            // Parcourir la liste des actions
139            while (iterActions.hasNext())
140            {
141                // Récupérer la description de chaque action
142                ActionDescriptor actionDesc = (ActionDescriptor) iterActions.next();
143                
144                // Ajouter l'action courante
145                actions.add(Integer.valueOf(actionDesc.getId()));
146            }
147        }
148        
149        // Convertir la liste en tableau d'entier
150        int[] array = new int[actions.size()];
151        Iterator<Integer> itAction = actions.iterator();
152        
153        // Remplir le tableau
154        for (int i = 0; itAction.hasNext(); i++)
155        {
156            array[i] = itAction.next().intValue();
157        }
158        
159        return array;
160    }
161    
162    /**
163     * Retrieves the name of an action.
164     * @param workflowName The name of the workflow.
165     * @param actionID The id of the action.
166     * @return The name of the action or an empty string.
167     */
168    public String getActionName(String workflowName, int actionID)
169    {
170        // Récupérer la description du workflow
171        WorkflowDescriptor workflowDesc = getWorkflowDescriptor(workflowName);
172        
173        if (workflowDesc == null)
174        {
175            return "";
176        }
177        
178        // Récupérer la description de l'action
179        ActionDescriptor actionDesc = workflowDesc.getAction(actionID);
180        
181        if (actionDesc == null)
182        {
183            return "";
184        }
185        
186        return actionDesc.getName();
187    }
188    
189    /**
190     * Retrieves the name of a step.
191     * @param workflowName the name of the workflow.
192     * @param stepId the id of the step.
193     * @return the name of the step or an empty string.
194     */
195    public String getStepName(String workflowName, int stepId)
196    {
197        // Récupérer la description du workflow
198        WorkflowDescriptor workflowDesc = getWorkflowDescriptor(workflowName);
199        
200        if (workflowDesc == null)
201        {
202            return "";
203        }
204        
205        // Récupérer la description de l'action
206        StepDescriptor stepDesc = workflowDesc.getStep(stepId);
207        
208        if (stepDesc == null)
209        {
210            return "";
211        }
212        
213        return stepDesc.getName();
214    }
215    
216    /**
217     * Retrieves the initial action id of a workflow.
218     * @param workflowName the name of the workflow.
219     * @return the first initial action id or <code>-1</code>
220     *         if the workflow does not exist.
221     */
222    public int getInitialAction(String workflowName)
223    {
224        // Récupérer la description du workflow
225        WorkflowDescriptor workflowDesc = getWorkflowDescriptor(workflowName);
226        
227        if (workflowDesc == null)
228        {
229            return -1;
230        }
231        
232        // Retourner la première action initiale
233        return ((ActionDescriptor) workflowDesc.getInitialActions().get(0)).getId();
234    }
235    
236    /**
237     * Retrieves the action ids from a particular step.
238     * @param workflowName the name of the workflow.
239     * @param stepId the id of the step.
240     * @return the ids of the actions of the step.
241     */
242    public int[] getAllActionsFromStep(String workflowName, int stepId)
243    {
244        // Récupérer la description du workflow
245        WorkflowDescriptor workflowDesc = getWorkflowDescriptor(workflowName);
246        
247        if (workflowDesc == null)
248        {
249            return new int[0];
250        }
251        
252        List<Integer> actions = new ArrayList<>();
253        
254        // Vérifier que l'identifiant est valide
255        StepDescriptor stepDesc = workflowDesc.getStep(stepId);
256        
257        if (stepDesc == null)
258        {
259            return new int[0];
260        }
261        
262        // Récupérer la liste des actions associés au step courant
263        Iterator iterator = stepDesc.getActions().iterator();
264        
265        // Parcourir la liste des actions
266        while (iterator.hasNext())
267        {
268            // Récupérer la description de chaque action
269            ActionDescriptor actionDesc = (ActionDescriptor) iterator.next();
270            
271            // Ajouter l'action courante
272            actions.add(Integer.valueOf(actionDesc.getId()));
273        }
274        
275        // Récupérer les actions globales
276        iterator = workflowDesc.getGlobalActions().iterator();
277        
278        // Parcourir la liste des actions globales
279        while (iterator.hasNext())
280        {
281            // Récupérer la description de l'action globale courante
282            ActionDescriptor actionDesc = (ActionDescriptor) iterator.next();
283            
284            // Ajouter l'action globale courant
285            actions.add(Integer.valueOf(actionDesc.getId()));
286        }
287        
288        // Convertir la liste en tableau d'entier
289        int[] array = new int[actions.size()];
290        
291        // Remplir le tableau
292        for (int i = 0; i < array.length; i++)
293        {
294            array[i] = actions.get(i).intValue();
295        }
296        
297        return array;
298    }
299    
300    /**
301     * Get the steps the workflow was "in" at a given date.
302     * @param workflow workflow
303     * @param entryId the workflow entry ID.
304     * @param timestamp the date.
305     * @return the list of steps the workflow was in.
306     * @throws WorkflowException if an error occurs.
307     */
308    public List<Step> getStepAt(Workflow workflow, long entryId, Date timestamp) throws WorkflowException
309    {
310        return getStepsBetween(workflow, entryId, timestamp, timestamp);
311    }
312    
313    /**
314     * Get the steps the workflow was "in" between two dates.
315     * @param workflow workflow
316     * @param entryId the workflow entry ID.
317     * @param start the start date.
318     * @param end the end date.
319     * @return the list of steps the workflow was in between the two dates.
320     * @throws WorkflowException if an error occurs.
321     */
322    public List<Step> getStepsBetween(Workflow workflow, long entryId, Date start, Date end) throws WorkflowException
323    {
324        WorkflowStore store = ((AbstractWorkflow) workflow).getConfiguration().getWorkflowStore();
325        
326        List<Step> steps = new ArrayList<>();
327        
328        List<Step> allSteps = new ArrayList<>();
329        
330        allSteps.addAll(store.findCurrentSteps(entryId));
331        allSteps.addAll(store.findHistorySteps(entryId));
332        
333        for (Step step : allSteps)
334        {
335            Date stepStartDate = step.getStartDate();
336            Date stepFinishDate = step.getFinishDate();
337            
338            if (stepStartDate != null)
339            {
340                if (end.after(stepStartDate))
341                {
342                    if (stepFinishDate == null || start.before(stepFinishDate))
343                    {
344                        steps.add(step);
345                    }
346                }
347            }
348            else
349            {
350                if (stepFinishDate == null || start.before(stepFinishDate))
351                {
352                    steps.add(step);
353                }
354            }
355        }
356        
357        return steps;
358    }
359}
360