Salome HOME
Modifications done to respect PMD rules. Versioning a document is fixed. Validation...
[tools/siman.git] / Workspace / Siman-Common / src / org / splat / service / StudyServiceImpl.java
1 /*****************************************************************************
2  * Company         OPEN CASCADE
3  * Application     SIMAN
4  * File            Id: 
5  * Creation date   02.10.2012
6  * @author         Author: Maria KRUCHININA
7  * @version        Revision: 
8  *****************************************************************************/
9
10 package org.splat.service;
11
12 import java.io.IOException;
13 import java.text.DecimalFormat;
14 import java.text.SimpleDateFormat;
15 import java.util.Calendar;
16 import java.util.Collections;
17 import java.util.Date;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22
23 import org.hibernate.criterion.Restrictions;
24 import org.splat.dal.bo.kernel.Relation;
25 import org.splat.dal.bo.kernel.User;
26 import org.splat.dal.bo.som.ActorRelation;
27 import org.splat.dal.bo.som.ContributorRelation;
28 import org.splat.dal.bo.som.DescriptionAttribute;
29 import org.splat.dal.bo.som.DocumentType;
30 import org.splat.dal.bo.som.IDBuilder;
31 import org.splat.dal.bo.som.KnowledgeElement;
32 import org.splat.dal.bo.som.ProgressState;
33 import org.splat.dal.bo.som.Publication;
34 import org.splat.dal.bo.som.Scenario;
35 import org.splat.dal.bo.som.SimulationContext;
36 import org.splat.dal.bo.som.Study;
37 import org.splat.dal.bo.som.ValidationCycle;
38 import org.splat.dal.bo.som.ValidationCycleRelation;
39 import org.splat.dal.bo.som.ValidationStep;
40 import org.splat.dal.bo.som.Visibility;
41 import org.splat.dal.bo.som.Study.Properties;
42 import org.splat.dal.bo.som.ValidationCycle.Actor;
43 import org.splat.dal.dao.som.IDBuilderDAO;
44 import org.splat.dal.dao.som.ScenarioDAO;
45 import org.splat.dal.dao.som.StudyDAO;
46 import org.splat.dal.dao.som.ValidationCycleDAO;
47 import org.splat.kernel.InvalidPropertyException;
48 import org.splat.kernel.MissedPropertyException;
49 import org.splat.kernel.MultiplyDefinedException;
50 import org.splat.log.AppLogger;
51 import org.splat.service.technical.IndexService;
52 import org.splat.service.technical.ProjectSettingsService;
53 import org.splat.service.technical.ProjectSettingsServiceImpl;
54 import org.splat.som.Revision;
55 import org.springframework.transaction.annotation.Transactional;
56
57 /**
58  * This class defines all methods for creation, modification the study.
59  * 
60  * @author Maria KRUCHININA
61  * 
62  */
63 public class StudyServiceImpl implements StudyService {
64
65         /**
66          * logger for the service.
67          */
68         public final static AppLogger LOG = AppLogger
69                         .getLogger(StudyServiceImpl.class);
70
71         /**
72          * Injected index service.
73          */
74         private IndexService _indexService;
75
76         /**
77          * Injected step service.
78          */
79         private StepService _stepService;
80
81         /**
82          * Injected project service.
83          */
84         private ProjectSettingsService _projectSettingsService;
85
86         /**
87          * Injected project element service.
88          */
89         private ProjectElementService _projectElementService;
90
91         /**
92          * Injected study DAO.
93          */
94         private StudyDAO _studyDAO;
95
96         /**
97          * Injected scenario DAO.
98          */
99         private ScenarioDAO _scenarioDAO;
100
101         /**
102          * Injected validation cycle DAO.
103          */
104         private ValidationCycleDAO _validationCycleDAO;
105
106         /**
107          * Injected IDBuilder DAO.
108          */
109         private IDBuilderDAO _iDBuilderDAO;
110
111         /**
112          * Injected document type service.
113          */
114         private DocumentTypeService _documentTypeService;
115
116         /**
117          * Injected user service.
118          */
119         private UserService _userService;
120
121         /**
122          * {@inheritDoc}
123          * 
124          * @see org.splat.service.StudyService#selectStudy(long)
125          */
126         @Transactional
127         public Study selectStudy(final long index) {
128                 Study result = getStudyDAO().get(index);
129                 loadWorkflow(result);
130                 return result;
131         }
132
133         /**
134          * Get study by its reference.
135          * 
136          * @param refid
137          *            the study reference
138          * @return found study or null
139          */
140         @Transactional(readOnly = true)
141         public Study selectStudy(final String refid) {
142                 Study result = getStudyDAO().findByCriteria(
143                                 Restrictions.eq("sid", refid));
144                 loadWorkflow(result);
145                 return result;
146         }
147
148         /**
149          * {@inheritDoc}
150          * 
151          * @see org.splat.service.StudyService#createStudy(org.splat.dal.bo.som.Study.Properties)
152          */
153         @Transactional
154         public Study createStudy(final Study.Properties sprop)
155                         throws MissedPropertyException, InvalidPropertyException,
156                         MultiplyDefinedException {
157                 sprop.setReference(getProjectSettings().getReferencePattern());
158                 Study study = new Study(sprop);
159
160                 buildReference(study);
161                 getStudyDAO().create(study);
162                 try {
163                         IndexService lucin = getIndex();
164                         lucin.add(study);
165                 } catch (IOException error) {
166                         LOG.error("Unable to index the study '" + study.getIndex()
167                                         + "', reason:", error);
168                         // Continue and try to index later
169                 }
170                 return study;
171         }
172
173         /**
174          * {@inheritDoc}
175          * 
176          * @see org.splat.service.StudyService#addProjectContext(org.splat.dal.bo.som.Study, org.splat.dal.bo.som.SimulationContext.Properties)
177          */
178         @Transactional
179         public SimulationContext addProjectContext(final Study aStudy,
180                         final SimulationContext.Properties cprop)
181                         throws MissedPropertyException, InvalidPropertyException,
182                         MultiplyDefinedException {
183                 SimulationContext added = getStepService().addSimulationContext(
184                                 getProjectElementService().getFirstStep(aStudy), cprop);
185                 update(aStudy);
186                 return added;
187         }
188
189         /**
190          * {@inheritDoc}
191          * 
192          * @see org.splat.service.StudyService#addProjectContext(org.splat.dal.bo.som.Study, org.splat.dal.bo.som.SimulationContext)
193          */
194         @Transactional
195         public SimulationContext addProjectContext(final Study aStudy,
196                         final SimulationContext context) {
197                 SimulationContext added = getStepService().addSimulationContext(
198                                 getProjectElementService().getFirstStep(aStudy), context);
199                 update(aStudy);
200                 return added;
201         }
202
203         /**
204          * {@inheritDoc}
205          * 
206          * @see org.splat.service.StudyService#addContributor(org.splat.dal.bo.som.Study, org.splat.dal.bo.kernel.User)
207          */
208         public boolean addContributor(final Study aStudy, final User user) {
209                 List<User> contributor = getModifiableContributors(aStudy); // Initializes contributor
210                 for (Iterator<User> i = contributor.iterator(); i.hasNext();) {
211                         User present = i.next();
212                         if (present.equals(user)) {
213                                 return false;
214                         }
215                 }
216                 boolean absent = getModifiableActors(aStudy).add(user); // User may already be a reviewer or an approver
217
218                 aStudy.addRelation(new ContributorRelation(aStudy, user));
219                 if (absent) {
220                         update(aStudy); // Else, useless to re-index the study
221                 }
222                 contributor.add(user);
223                 return true;
224         }
225
226         /**
227          * Moves this study from the Public to the Reference area of the repository. For being moved to the Reference area, the study must
228          * previously be approved.
229          * 
230          * @param aStudy
231          *            the study to move
232          * @return true if the move succeeded.
233          * @see #moveToPublic()
234          * @see #isPublic()
235          * @see Publication#approve(Date)
236          */
237         public boolean moveToReference(final Study aStudy) {
238                 if (aStudy.getProgressState() != ProgressState.APPROVED) {
239                         return false;
240                 }
241                 if (aStudy.getVisibility() != Visibility.PUBLIC) {
242                         return false;
243                 }
244
245                 aStudy.setVisibility(Visibility.REFERENCE);
246                 if (update(aStudy)) {
247                         return updateKnowledgeElementsIndex(aStudy); // If fails, the database roll-back is under responsibility of the caller
248                 }
249                 return false;
250         }
251
252         /**
253          * {@inheritDoc}
254          * 
255          * @see org.splat.service.StudyService#update(org.splat.dal.bo.som.Study, org.splat.dal.bo.som.Study.Properties)
256          */
257         public boolean update(final Study aStudy, final Properties sprop)
258                         throws InvalidPropertyException {
259                 if (sprop.getTitle() != null) {
260                         aStudy.setTitle(sprop.getTitle());
261                 }
262                 if (sprop.getSummary() != null) {
263                         aStudy.setAttribute(new DescriptionAttribute(aStudy, sprop
264                                         .getSummary()));
265                 }
266                 // TODO: To be completed
267                 return update(aStudy);
268         }
269
270         /**
271          * Check if the document is published in the study.
272          * 
273          * @param aStudy
274          *            the study
275          * @param doc
276          *            the document
277          * @return true if the document is published in the study
278          */
279 /*      private boolean publishes(final Study aStudy, final Document doc) {
280                 if (!aStudy.publishes(doc)) {
281                         Scenario[] scene = aStudy.getScenarii();
282                         for (int i = 0; i < scene.length; i++) {
283                                 if (scene[i].publishes(doc)) {
284                                         return true;
285                                 }
286                         }
287                 }
288                 return false;
289         }
290 */
291         /**
292          * {@inheritDoc}
293          * 
294          * @see org.splat.service.StudyService#removeContributor(org.splat.dal.bo.som.Study, org.splat.dal.bo.kernel.User[])
295          */
296         public boolean removeContributor(final Study aStudy, final User... users) {
297                 List<User> contributor = getModifiableContributors(aStudy); // Initializes contributor
298                 Boolean done = false;
299                 for (int i = 0; i < users.length; i++) {
300                         User user = users[i];
301                         for (Iterator<User> j = contributor.iterator(); j.hasNext();) {
302                                 User present = j.next();
303                                 if (!present.equals(user)) {
304                                         continue;
305                                 }
306
307                                 aStudy.removeRelation(ContributorRelation.class, user);
308                                 j.remove(); // Updates the contributor shortcut
309                                 done = true;
310                                 break;
311                         }
312                 }
313                 if (done) {
314                         update(aStudy);
315                 }
316                 return done;
317         }
318
319         /**
320          * {@inheritDoc}
321          * 
322          * @see org.splat.service.StudyService#removeProjectContext(org.splat.dal.bo.som.Study, org.splat.dal.bo.som.SimulationContext)
323          */
324         public boolean removeProjectContext(final Study aStudy,
325                         final SimulationContext context) {
326                 boolean done = getStepService().removeSimulationContext(
327                                 getProjectElementService().getFirstStep(aStudy), context);
328                 update(aStudy);
329                 return done;
330         }
331
332         /**
333          * {@inheritDoc}
334          * 
335          * @see org.splat.service.StudyService#setValidationCycle(org.splat.dal.bo.som.Study, org.splat.dal.bo.som.DocumentType,
336          *      org.splat.dal.bo.som.ValidationCycle.Properties)
337          */
338         @Transactional
339         public void setValidationCycle(final Study aStudyDTO, final DocumentType type,
340                         final ValidationCycle.Properties vprop) {
341                 Map<String, ValidationCycle> validactor = aStudyDTO.getValidationCycles();
342                 if (validactor == null) {
343                         setShortCuts(aStudyDTO); // Initializes validactor and actor
344                 }
345
346                 Study aStudy = selectStudy(aStudyDTO.getIndex());
347                 
348                 String cname = type.getName();
349                 ValidationCycle cycle = validactor.get(cname);
350
351                 if (cycle != null && cycle.isAssigned()) {
352                         resetActors(cycle, vprop);
353                 } else {
354                         try {
355                                 cycle = new ValidationCycle(aStudy, vprop.setDocumentType(type));
356
357                                 getValidationCycleDAO().create(cycle); // RKV
358
359                                 ValidationCycleRelation link = cycle.getContext();
360                                 // RKV: aStudy.addRelation(link);
361                                 aStudyDTO.getAllRelations().add(link); // RKV
362
363                                 validactor.put(cname, link.getTo()); // Replaces the cycle if exists as default,
364                         } catch (Exception error) {
365                                 LOG.error("Unable to re-index Knowledge Elements, reason:",
366                                                 error);
367                                 return;
368                         }
369                 }
370                 resetActorsShortCut(aStudyDTO);
371                 update(aStudy); // Re-index the study, just in case
372         }
373
374         /**
375          * Demotes this study from In-Check to In-Draft then In-Work states. This function is called internally when demoting the final result
376          * document of the study.
377          * 
378          * @param aStudy
379          *            a study to demote
380          * @return true if the demotion succeeded.
381          */
382         public boolean demote(final Study aStudy) {
383                 if (aStudy.getProgressState() == ProgressState.inCHECK) {
384                         aStudy.setProgressState(ProgressState.inDRAFT);
385                 } else if (aStudy.getProgressState() == ProgressState.inDRAFT) {
386                         aStudy.setProgressState(ProgressState.inWORK);
387                 } else {
388                         return false;
389                 }
390                 return update(aStudy);
391         }
392
393         /**
394          * {@inheritDoc}
395          * 
396          * @see org.splat.service.StudyService#generateLocalIndex(org.splat.dal.bo.som.Study)
397          */
398         @Transactional
399         public int generateLocalIndex(final Study aStudy) {
400                 aStudy.setLastLocalIndex(aStudy.getLastLocalIndex() + 1);
401                 getStudyDAO().update(aStudy);
402                 return aStudy.getLastLocalIndex();
403         }
404
405         /**
406          * Promotes this study from In-Work to In-Draft then In-Check and APPROVED states. This function is called internally when promoting the
407          * final result document of the study.
408          * 
409          * @param aStudy
410          *            a study to promote
411          * @return true if the demotion succeeded.
412          */
413         public boolean promote(final Study aStudy) {
414                 if (aStudy.getProgressState() == ProgressState.inWORK) {
415                         aStudy.setProgressState(ProgressState.inDRAFT);
416                 } else if (aStudy.getProgressState() == ProgressState.inDRAFT) {
417                         aStudy.setProgressState(ProgressState.inCHECK);
418                         Revision myvers = new Revision(aStudy.getVersion());
419                         if (myvers.isMinor()) {
420                                 aStudy.setVersion(myvers.incrementAs(aStudy.getProgressState())
421                                                 .toString());
422                         }
423                 } else if (aStudy.getProgressState() == ProgressState.inCHECK) {
424                         aStudy.setProgressState(ProgressState.APPROVED);
425                 } else {
426                         return false;
427                 }
428
429                 return update(aStudy);
430         }
431
432         /**
433          * Moves this study from the Private to the Public area of the repository.
434          * 
435          * @param aStudy
436          *            a study to move
437          * @return true if the move succeeded.
438          * @see #isPublic()
439          */
440         public boolean moveToPublic(final Study aStudy) {
441                 boolean isOk = false;
442                 if (aStudy.getVisibility() == Visibility.PRIVATE) {
443                         aStudy.setVisibility(Visibility.PUBLIC);
444                         if (update(aStudy)) {
445                                 isOk = updateKnowledgeElementsIndex(aStudy); // If fails, the database roll-back is under responsibility of the caller
446                         }
447                 }
448                 return isOk;
449         }
450
451         /**
452          * Update a study in the database.
453          * 
454          * @param aStudy
455          *            the study to update
456          * @return true if the study is updated successfully
457          */
458         @Transactional
459         private boolean update(final Study aStudy) {
460                 boolean isOk = false;
461                 try {
462                         getStudyDAO().update(aStudy); // Update of relational base
463                         setShortCuts(aStudy); // RKV: initialize transient actors set
464                         getIndex().update(aStudy); // Update of Lucene index
465                         isOk = true;
466                 } catch (Exception e) {
467                         LOG.error("STD-000001", e, aStudy.getIndex(), e.getMessage());
468                 }
469                 return isOk;
470         }
471
472         /**
473          * Build reference for the study. The reference of the study is stored as a new reference pattern (IDBuilder).
474          * 
475          * @param aStudy
476          *            the study
477          * @return true if reference building is succeded
478          */
479         @Transactional
480         private boolean buildReference(final Study aStudy) {
481                 String pattern = aStudy.getReference(); // The study being supposed just created, its reference is the reference pattern
482                 IDBuilder tool = selectIDBuilder(aStudy.getDate());
483                 if (tool == null) {
484                         tool = new IDBuilder(aStudy.getDate());
485                         getIDBuilderDAO().create(tool);
486                 }
487                 aStudy.setReference(buildReference(tool, pattern, aStudy));
488                 return true;
489         }
490
491         /**
492          * Build reference for the study. The reference of the study is stored as a new reference pattern (IDBuilder).
493          * 
494          * @param aBuilder
495          *            the id builder
496          * @param study
497          *            the study
498          * @param pattern
499          *            the reference pattern
500          * @return true if reference building is succeded
501          */
502         @Transactional
503         public String buildReference(final IDBuilder aBuilder,
504                         final String pattern, final Study study) {
505                 char[] format = pattern.toCharArray();
506                 char[] ref = new char[80]; // Better evaluate the length of the generated string
507                 int next = aBuilder.getBase() + 1;
508
509                 int count = 0;
510                 for (int i = 0; i < format.length; i++) {
511
512                         // Insertion of attribute values
513                         if (format[i] == '%') {
514                                 i += 1;
515
516                                 if (format[i] == 'y') { // Insertion of year in format 2 (e.g. 09) or 4 (e.g. 2009) digits
517                                         int n = i;
518                                         while (format[i] == 'y') {
519                                                 i += 1;
520                                                 if (i == format.length) {
521                                                         break;
522                                                 }
523                                         }
524                                         SimpleDateFormat tostring = new SimpleDateFormat("yyyy"); //RKV: NOPMD: TODO: Use locale here?
525                                         String year = tostring.format(study.getDate());
526                                         year = year.substring(4 - (i - n), 4); // 4-(i-n) must be equal to either 0 or 2
527                                         for (int j = 0; j < year.length(); j++) {
528                                                 ref[count] = year.charAt(j);
529                                                 count += 1;
530                                         }
531                                         i -= 1; // Back to the last 'y' character
532                                 } else if (format[i] == '0') { // Insertion of the index
533                                         int n = i;
534                                         while (format[i] == '0') {
535                                                 i += 1;
536                                                 if (i == format.length) {
537                                                         break;
538                                                 }
539                                         }
540                                         DecimalFormat tostring = new DecimalFormat(pattern
541                                                         .substring(n, i));
542                                         String number = tostring.format(next);
543                                         for (int j = 0; j < number.length(); j++) {
544                                                 ref[count] = number.charAt(j);
545                                                 count += 1;
546                                         }
547                                         i -= 1; // Back to the last '0' character
548                                 }
549                                 // Keep the character
550                         } else {
551                                 ref[count] = format[i];
552                                 count += 1;
553                         }
554                 }
555                 // Incrementation of the number of study
556                 aBuilder.setBase(next);
557                 getIDBuilderDAO().update(aBuilder);
558                 return String.copyValueOf(ref, 0, count);
559         }
560
561         /**
562          * Find an id builder by date.
563          * 
564          * @param date
565          *            the date
566          * @return found id builder
567          */
568         private IDBuilder selectIDBuilder(final Date date) {
569                 Calendar aDate = Calendar.getInstance();
570                 aDate.setTime(date);
571                 return getIDBuilderDAO().findByCriteria(
572                                 Restrictions.eq("cycle", aDate.get(Calendar.YEAR)));
573         }
574
575         /**
576          * Fill transient collection ModifiableActors of the study.
577          * 
578          * @param aStudy
579          *            the study
580          */
581         private void resetActorsShortCut(final Study aStudy) {
582                 getModifiableActors(aStudy).clear();
583                 // Get all actors involved in validation cycles
584                 for (Iterator<ValidationCycle> i = aStudy.getValidationCycles()
585                                 .values().iterator(); i.hasNext();) {
586                         ValidationCycle cycle = i.next();
587                         User[] user = cycle.getAllActors();
588                         for (int j = 0; j < user.length; j++) {
589                                 getModifiableActors(aStudy).add(user[j]);
590                         }
591                 }
592                 // Get all other actors
593                 for (Iterator<Relation> i = aStudy.getAllRelations().iterator(); i
594                                 .hasNext();) {
595                         Relation link = i.next();
596                         Class<?> kindof = link.getClass().getSuperclass();
597                         if (!kindof.equals(ActorRelation.class)) {
598                                 continue;
599                         }
600                         getModifiableActors(aStudy).add(((ActorRelation) link).getTo());
601                 }
602         }
603
604         /**
605          * Update lucene index for the study knowledge elements.
606          * 
607          * @param aStudy
608          *            the study
609          * @return true if reindexing succeeded
610          */
611         private boolean updateKnowledgeElementsIndex(final Study aStudy) {
612                 boolean isOk = false;
613                 try {
614                         IndexService lucin = getIndex();
615
616                         for (Iterator<Scenario> i = aStudy.getScenariiList().iterator(); i
617                                         .hasNext();) {
618                                 Scenario scene = i.next();
619                                 for (Iterator<KnowledgeElement> j = scene
620                                                 .getAllKnowledgeElements().iterator(); j.hasNext();) {
621                                         KnowledgeElement kelm = j.next();
622                                         lucin.update(kelm);
623                                 }
624                         }
625                         isOk = true;
626                 } catch (Exception error) {
627                         LOG.error("Unable to re-index Knowledge Elements, reason:",
628                                         error);
629                 }
630                 return isOk;
631         }
632
633         /**
634          * Get lucene index service. Create a lucene index if it does not exist.
635          * 
636          * @return index service
637          * @throws IOException
638          *             if error occurs during lucene index creation
639          */
640         private IndexService getIndex() throws IOException {
641                 IndexService lucin = getIndexService();
642                 if (!lucin.exists()) {
643                         lucin.create(); // Happens when re-indexing all studies
644                 }
645                 return lucin;
646         }
647
648         /**
649          * Create a new validation cycle for documents of the given study.
650          * 
651          * @param from
652          *            the study
653          * @param cycle
654          *            the cycle description
655          * @return the new validation cycle
656          */
657         protected ValidationCycle createValidationCycle(
658                         final Study from,
659                         final ProjectSettingsServiceImpl.ProjectSettingsValidationCycle cycle) {
660                 Actor[] actype = cycle.getActorTypes();
661                 User.Properties uprop = new User.Properties();
662
663                 ValidationCycle aValidationCycle = new ValidationCycle();
664                 aValidationCycle.setDocumentType(getDocumentTypeService().selectType(
665                                 cycle.getName())); // Null in case of default validation cycle
666                 // context = new ValidationCycleRelation(from, vprop);
667                 // RKV aValidationCycle.context = null; // Validation cycle defined in the workflow
668                 for (int i = 0; i < actype.length; i++) {
669                         User actor = null;
670                         if (actype[i] != null) {
671                                 try {
672                                         if (actype[i] == Actor.manager) {
673                                                 actor = from.getAuthor();
674                                         } else if (actype[i] == Actor.Nx1) {
675                                                 List<User> manager = getUserService().selectUsersWhere(
676                                                                 uprop.setOrganizationName("Nx1"));
677                                                 if (manager.size() == 1) {
678                                                         actor = manager.get(0);
679                                                 }
680                                         } else if (actype[i] == Actor.Nx2) {
681                                                 List<User> manager = getUserService().selectUsersWhere(
682                                                                 uprop.setOrganizationName("Nx2"));
683                                                 if (manager.size() == 1) {
684                                                         actor = manager.get(0);
685                                                 }
686                                         } else { /* Actor.customer */
687                                                 actor = from.getAuthor();
688                                                 // TODO: Get the customer of the study, if exists
689                                         }
690                                 } catch (Exception e) { // Should not happen
691                                         actor = null;
692                                 }
693                         }
694                         if (i == 0) {
695                                 aValidationCycle.setReviewer(actor);
696                         } else if (i == 1) {
697                                 aValidationCycle.setApprover(actor);
698                         } else if (i == 2) {
699                                 aValidationCycle.setSignatory(actor);
700                         }
701                 }
702                 return aValidationCycle;
703         }
704
705         /**
706          * Remove a validation step from the validation cycle.
707          * 
708          * @param aValidationCycle
709          *            the validation cycle
710          * @param step
711          *            the validation step to remove
712          */
713         @Transactional
714         protected void remove(final ValidationCycle aValidationCycle,
715                         final ValidationStep step) {
716                 if (step == ValidationStep.REVIEW) {
717                         aValidationCycle.setReviewer(null);
718                 } else if (step == ValidationStep.APPROVAL) {
719                         aValidationCycle.setApprover(null);
720                 } else if (step == ValidationStep.ACCEPTANCE
721                                 || step == ValidationStep.REFUSAL) {
722                         aValidationCycle.setSignatory(null);
723                 }
724                 if (aValidationCycle.isSaved()) {
725                         getValidationCycleDAO().update(aValidationCycle);
726                 }
727         }
728
729         /**
730          * Reset actors for the validation cycle.
731          * 
732          * @param aValidationCycle
733          *            the validation cycle to update
734          * @param vprop
735          *            new validation cycle properties containing new actors
736          */
737         @Transactional
738         public void resetActors(final ValidationCycle aValidationCycle,
739                         final ValidationCycle.Properties vprop) {
740                 aValidationCycle.setPublisher(vprop.getPublisher()); // May be null
741                 aValidationCycle.setReviewer(vprop.getReviewer()); // May be null
742                 aValidationCycle.setApprover(vprop.getApprover()); // May be null
743                 aValidationCycle.setSignatory(vprop.getSignatory()); // May be null
744                 if (aValidationCycle.isSaved()) {
745                         getValidationCycleDAO().update(aValidationCycle);
746                 }
747         }
748
749         /**
750          * Set actor for the given validation cycle and validation step.
751          * 
752          * @param aValidationCycle
753          *            the validation cycle
754          * @param step
755          *            the validation step
756          * @param actor
757          *            the actor to set
758          */
759         @Transactional
760         protected void setActor(final ValidationCycle aValidationCycle,
761                         final ValidationStep step, final User actor) {
762                 if (step == ValidationStep.PROMOTION) {
763                         aValidationCycle.setPublisher(actor);
764                 } else if (step == ValidationStep.REVIEW) {
765                         aValidationCycle.setReviewer(actor);
766                 } else if (step == ValidationStep.APPROVAL) {
767                         aValidationCycle.setApprover(actor);
768                 } else if (step == ValidationStep.ACCEPTANCE
769                                 || step == ValidationStep.REFUSAL) {
770                         aValidationCycle.setSignatory(actor);
771                 }
772                 if (aValidationCycle.isSaved()) {
773                         getValidationCycleDAO().update(aValidationCycle);
774                 }
775         }
776
777         /**
778          * Returns all actors of this study other than the author, including contributors, reviewers and approvers.
779          * 
780          * @param aStudy
781          *            the study
782          * @return the actors of this study
783          * @see #hasActor(User)
784          */
785         public Set<User> getActors(final Study aStudy) {
786                 if (aStudy.getActor() == null) {
787                         setShortCuts(aStudy);
788                 }
789                 return Collections.unmodifiableSet(aStudy.getActor());
790         }
791
792         /**
793          * Returns all actors of this study other than the author, including contributors, reviewers and approvers.
794          * 
795          * @param aStudy
796          *            the study
797          * @return the modifiable set of actors of this study
798          * @see #hasActor(User)
799          */
800         public Set<User> getModifiableActors(final Study aStudy) {
801                 if (aStudy.getActor() == null) {
802                         setShortCuts(aStudy);
803                 }
804                 return aStudy.getActor();
805         }
806
807         /**
808          * Returns unmodifiable initialized transient list of contributors of this study.
809          * 
810          * @param aStudy
811          *            the study
812          * @return the unmodifiable not null transient list of contributors of this study
813          */
814         public List<User> getContributors(final Study aStudy) {
815                 if (aStudy.getContributor() == null) {
816                         setShortCuts(aStudy);
817                 }
818                 return Collections.unmodifiableList(aStudy.getContributor()); // May be empty
819         }
820
821         /**
822          * Returns modifiable initialized transient list of contributors of this study.
823          * 
824          * @param aStudy
825          *            the study
826          * @return the modifiable not null transient list of contributors of this study
827          */
828         public List<User> getModifiableContributors(final Study aStudy) {
829                 if (aStudy.getContributor() == null) {
830                         setShortCuts(aStudy);
831                 }
832                 return aStudy.getContributor(); // May be empty
833         }
834
835         /**
836          * Returns the validation cycle of the given document type.
837          * 
838          * @param aStudy
839          *            the study
840          * @param type
841          *            the document type being subject of validation
842          * @return the validation cycle of the document, or null if not defined.
843          */
844         public ValidationCycle getValidationCycleOf(final Study aStudy,
845                         final DocumentType type) {
846                 if (aStudy.getValidationCycles() == null) {
847                         setShortCuts(aStudy);
848                 }
849                 ValidationCycle result = aStudy.getValidationCycles().get(
850                                 type.getName());
851                 if (result == null) {
852                         if (type.isStepResult()) {
853                                 result = aStudy.getValidationCycles().get("default"); // "default" validation cycle defined in the configuration, if exist
854                         }
855                         if (result == null) {
856                                 result = aStudy.getValidationCycles().get("built-in");
857                         }
858                 }
859                 return result;
860         }
861
862         /**
863          * Checks if the given user is actor of this study. Actors include contributors, reviewers and approvers.
864          * 
865          * @param aStudy
866          *            the study
867          * @param user
868          *            the user to look for
869          * @return true if the given user is actor of this study.
870          * @see #getActors()
871          */
872         public boolean hasActor(final Study aStudy, final User user) {
873                 if (user == null) {
874                         return false;
875                 }
876                 for (Iterator<User> i = getActors(aStudy).iterator(); i.hasNext();) {
877                         User involved = i.next();
878                         if (involved.equals(user)) {
879                                 return true;
880                         }
881                 }
882                 return false;
883         }
884
885         /**
886          * Checks if the given user participates to this study. The Study staff includes the author and contributors.
887          * 
888          * @param aStudy
889          *            the study
890          * @param user
891          *            the user to look for
892          * @return true if the given user is actor of this study.
893          * @see #getContributors()
894          */
895         public boolean isStaffedBy(final Study aStudy, final User user) {
896                 if (user == null) {
897                         return false;
898                 }
899                 if (aStudy == null) {
900                         return false;
901                 }
902                 if (aStudy.getAuthor() == null) {
903                         return false;
904                 }
905                 if (aStudy.getAuthor().equals(user)) {
906                         return true;
907                 }
908                 for (Iterator<User> i = getContributors(aStudy).iterator(); i.hasNext();) {
909                         if (i.next().equals(user)) {
910                                 return true;
911                         }
912                 }
913                 return false;
914         }
915
916         /**
917          * Initialize shortcuts of the study as its transient collections.
918          * 
919          * @param aStudy
920          *            the study
921          */
922         public void loadWorkflow(final Study aStudy) {
923                 setShortCuts(aStudy);
924         }
925
926         /**
927          * Initialize shortcuts of the study as its transient collections.
928          * 
929          * @param aStudy
930          *            the study
931          */
932         public void setShortCuts(final Study aStudy) {
933                 aStudy.getContributor().clear();
934                 aStudy.getValidationCycles().clear();
935                 aStudy.getActor().clear();
936
937                 // Get the contributors
938                 for (Iterator<Relation> i = aStudy.getRelations(
939                                 ContributorRelation.class).iterator(); i.hasNext();) {
940                         ContributorRelation link = (ContributorRelation) i.next();
941                         aStudy.getContributor().add(link.getTo());
942                 }
943                 // Get the validation cycles specific to this study
944                 for (Iterator<Relation> i = aStudy.getRelations(
945                                 ValidationCycleRelation.class).iterator(); i.hasNext();) {
946                         ValidationCycleRelation link = (ValidationCycleRelation) i.next();
947                         aStudy.getValidationCycles().put(link.getDocumentType().getName(),
948                                         link.getTo()); // The associated document type is necessarily not null in this
949                         // context
950                 }
951                 // Get the validation cycles coming from the configured workflow and not overridden in this study
952                 for (Iterator<ProjectSettingsServiceImpl.ProjectSettingsValidationCycle> i = getProjectSettings()
953                                 .getAllValidationCycles().iterator(); i.hasNext();) {
954                         ProjectSettingsServiceImpl.ProjectSettingsValidationCycle cycle = i
955                                         .next();
956                         String type = cycle.getName();
957                         if (!aStudy.getValidationCycles().containsKey(type)) {
958                                 aStudy.getValidationCycles().put(type,
959                                                 createValidationCycle(aStudy, cycle));
960                         }
961                 }
962                 // Get all corresponding actors
963                 for (Iterator<ValidationCycle> i = aStudy.getValidationCycles()
964                                 .values().iterator(); i.hasNext();) {
965                         ValidationCycle cycle = i.next();
966                         User[] user = cycle.getAllActors();
967                         for (int j = 0; j < user.length; j++) {
968                                 aStudy.getActor().add(user[j]);
969                         }
970                 }
971                 // Get all other actors
972                 for (Iterator<Relation> i = aStudy.getAllRelations().iterator(); i
973                                 .hasNext();) {
974                         Relation link = i.next();
975                         Class<?> kindof = link.getClass().getSuperclass();
976                         if (!kindof.equals(ActorRelation.class)) {
977                                 continue;
978                         }
979                         aStudy.getActor().add(((ActorRelation) link).getTo());
980                 }
981         }
982
983         /**
984          * Get project settings.
985          * 
986          * @return Project settings service
987          */
988         private ProjectSettingsService getProjectSettings() {
989                 return _projectSettingsService;
990         }
991
992         /**
993          * Set project settings service.
994          * 
995          * @param projectSettingsService
996          *            project settings service
997          */
998         public void setProjectSettings(
999                         final ProjectSettingsService projectSettingsService) {
1000                 _projectSettingsService = projectSettingsService;
1001         }
1002
1003         /**
1004          * Get the projectElementService.
1005          * 
1006          * @return the projectElementService
1007          */
1008         public ProjectElementService getProjectElementService() {
1009                 return _projectElementService;
1010         }
1011
1012         /**
1013          * Set the projectElementService.
1014          * 
1015          * @param projectElementService
1016          *            the projectElementService to set
1017          */
1018         public void setProjectElementService(
1019                         final ProjectElementService projectElementService) {
1020                 _projectElementService = projectElementService;
1021         }
1022
1023         /**
1024          * Get the stepService.
1025          * 
1026          * @return the stepService
1027          */
1028         public StepService getStepService() {
1029                 return _stepService;
1030         }
1031
1032         /**
1033          * Set the stepService.
1034          * 
1035          * @param stepService
1036          *            the stepService to set
1037          */
1038         public void setStepService(final StepService stepService) {
1039                 _stepService = stepService;
1040         }
1041
1042         /**
1043          * Get the indexService.
1044          * 
1045          * @return the indexService
1046          */
1047         public IndexService getIndexService() {
1048                 return _indexService;
1049         }
1050
1051         /**
1052          * Set the indexService.
1053          * 
1054          * @param indexService
1055          *            the indexService to set
1056          */
1057         public void setIndexService(final IndexService indexService) {
1058                 _indexService = indexService;
1059         }
1060
1061         /**
1062          * Get the studyDAO.
1063          * 
1064          * @return the studyDAO
1065          */
1066         public StudyDAO getStudyDAO() {
1067                 return _studyDAO;
1068         }
1069
1070         /**
1071          * Set the studyDAO.
1072          * 
1073          * @param studyDAO
1074          *            the studyDAO to set
1075          */
1076         public void setStudyDAO(final StudyDAO studyDAO) {
1077                 _studyDAO = studyDAO;
1078         }
1079
1080         /**
1081          * Get the iDBuilderDAO.
1082          * 
1083          * @return the iDBuilderDAO
1084          */
1085         public IDBuilderDAO getIDBuilderDAO() {
1086                 return _iDBuilderDAO;
1087         }
1088
1089         /**
1090          * Set the iDBuilderDAO.
1091          * 
1092          * @param builderDAO
1093          *            the iDBuilderDAO to set
1094          */
1095         public void setIDBuilderDAO(final IDBuilderDAO builderDAO) {
1096                 _iDBuilderDAO = builderDAO;
1097         }
1098
1099         /**
1100          * Get the scenarioDAO.
1101          * 
1102          * @return the scenarioDAO
1103          */
1104         public ScenarioDAO getScenarioDAO() {
1105                 return _scenarioDAO;
1106         }
1107
1108         /**
1109          * Set the scenarioDAO.
1110          * 
1111          * @param scenarioDAO
1112          *            the scenarioDAO to set
1113          */
1114         public void setScenarioDAO(final ScenarioDAO scenarioDAO) {
1115                 _scenarioDAO = scenarioDAO;
1116         }
1117
1118         /**
1119          * Get the validationCycleDAO.
1120          * 
1121          * @return the validationCycleDAO
1122          */
1123         public ValidationCycleDAO getValidationCycleDAO() {
1124                 return _validationCycleDAO;
1125         }
1126
1127         /**
1128          * Set the validationCycleDAO.
1129          * 
1130          * @param validationCycleDAO
1131          *            the validationCycleDAO to set
1132          */
1133         public void setValidationCycleDAO(
1134                         final ValidationCycleDAO validationCycleDAO) {
1135                 _validationCycleDAO = validationCycleDAO;
1136         }
1137
1138         /**
1139          * Get the documentTypeService.
1140          * 
1141          * @return the documentTypeService
1142          */
1143         public DocumentTypeService getDocumentTypeService() {
1144                 return _documentTypeService;
1145         }
1146
1147         /**
1148          * Set the documentTypeService.
1149          * 
1150          * @param documentTypeService
1151          *            the documentTypeService to set
1152          */
1153         public void setDocumentTypeService(
1154                         final DocumentTypeService documentTypeService) {
1155                 _documentTypeService = documentTypeService;
1156         }
1157
1158         /**
1159          * Get the userService.
1160          * 
1161          * @return the userService
1162          */
1163         public UserService getUserService() {
1164                 return _userService;
1165         }
1166
1167         /**
1168          * Set the userService.
1169          * 
1170          * @param userService
1171          *            the userService to set
1172          */
1173         public void setUserService(final UserService userService) {
1174                 _userService = userService;
1175         }
1176 }