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