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