Salome HOME
Compare method signature and DocToCompareDTO are added.
[tools/siman.git] / Workspace / Siman-Common / src / org / splat / service / ScenarioServiceImpl.java
1 /*****************************************************************************
2  * Company         OPEN CASCADE
3  * Application     SIMAN
4  * File            $Id$ 
5  * Creation date   06.10.2012
6  * @author         $Author$
7  * @version        $Revision$
8  *****************************************************************************/
9
10 package org.splat.service;
11
12 import java.io.IOException;
13 import java.util.ArrayList;
14 import java.util.Calendar;
15 import java.util.Date;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21
22 import org.hibernate.criterion.Order;
23 import org.hibernate.criterion.Restrictions;
24 import org.splat.common.properties.MessageKeyEnum;
25 import org.splat.dal.bo.kernel.Relation;
26 import org.splat.dal.bo.kernel.Role;
27 import org.splat.dal.bo.kernel.User;
28 import org.splat.dal.bo.som.ConvertsRelation;
29 import org.splat.dal.bo.som.Document;
30 import org.splat.dal.bo.som.DocumentType;
31 import org.splat.dal.bo.som.File;
32 import org.splat.dal.bo.som.KnowledgeElement;
33 import org.splat.dal.bo.som.KnowledgeElementType;
34 import org.splat.dal.bo.som.ProgressState;
35 import org.splat.dal.bo.som.Publication;
36 import org.splat.dal.bo.som.Scenario;
37 import org.splat.dal.bo.som.SimulationContext;
38 import org.splat.dal.bo.som.Study;
39 import org.splat.dal.bo.som.UsedByRelation;
40 import org.splat.dal.bo.som.UsesRelation;
41 import org.splat.dal.bo.som.Document.Properties;
42 import org.splat.dal.dao.kernel.RoleDAO;
43 import org.splat.dal.dao.kernel.UserDAO;
44 import org.splat.dal.dao.som.KnowledgeElementDAO;
45 import org.splat.dal.dao.som.KnowledgeElementTypeDAO;
46 import org.splat.dal.dao.som.ScenarioDAO;
47 import org.splat.dal.dao.som.StudyDAO;
48 import org.splat.kernel.InvalidPropertyException;
49 import org.splat.kernel.MismatchException;
50 import org.splat.kernel.MissedPropertyException;
51 import org.splat.kernel.MultiplyDefinedException;
52 import org.splat.kernel.NotApplicableException;
53 import org.splat.log.AppLogger;
54 import org.splat.service.dto.DocumentDTO;
55 import org.splat.service.dto.FileDTO;
56 import org.splat.service.dto.StepDTO;
57 import org.splat.service.technical.IndexService;
58 import org.splat.service.technical.ProjectSettingsService;
59 import org.splat.som.Step;
60 import org.splat.util.BeanHelper;
61 import org.springframework.transaction.annotation.Transactional;
62
63 /**
64  * Scenario service implementation.
65  * 
66  * @author <a href="mailto:roman.kozlov@opencascade.com">Roman Kozlov (RKV)</a>
67  */
68 public class ScenarioServiceImpl implements ScenarioService {
69
70         /**
71          * The logger for the service.
72          */
73         public final static AppLogger LOG = AppLogger
74                         .getLogger(ScenarioServiceImpl.class);
75
76         /**
77          * Injected index service.
78          */
79         private IndexService _indexService;
80         /**
81          * Injected step service.
82          */
83         private StepService _stepService;
84         /**
85          * Injected study service.
86          */
87         private StudyService _studyService;
88         /**
89          * Injected publication service.
90          */
91         private PublicationService _publicationService;
92         /**
93          * Injected project element service.
94          */
95         private ProjectElementService _projectElementService;
96         /**
97          * Injected knowledge element DAO.
98          */
99         private KnowledgeElementDAO _knowledgeElementDAO;
100         /**
101          * Injected scenario DAO.
102          */
103         private ScenarioDAO _scenarioDAO;
104
105         /**
106          * Injected study DAO.
107          */
108         private StudyDAO _studyDAO;
109
110         /**
111          * Injected knowledge element service.
112          */
113         private KnowledgeElementTypeService _knowledgeElementTypeService;
114
115         /**
116          * Injected user service.
117          */
118         private UserService _userService;
119
120         /**
121          * Injected user DAO.
122          */
123         private UserDAO _userDAO;
124
125         /**
126          * Injected role DAO.
127          */
128         private RoleDAO _roleDAO;
129
130         /**
131          * Injected knowledge element type DAO.
132          */
133         private KnowledgeElementTypeDAO _knowledgeElementTypeDAO;
134
135         /**
136          * Injected simulation context service.
137          */
138         private SimulationContextService _simulationContextService;
139
140         /**
141          * Injected project service.
142          */
143         private ProjectSettingsService _projectSettings;
144
145         /**
146          * Injected document type service.
147          */
148         private DocumentTypeService _documentTypeService;
149
150         /**
151          * Get the projectElementService.
152          * 
153          * @return the projectElementService
154          */
155         public ProjectElementService getProjectElementService() {
156                 return _projectElementService;
157         }
158
159         /**
160          * Set the projectElementService.
161          * 
162          * @param projectElementService
163          *            the projectElementService to set
164          */
165         public void setProjectElementService(
166                         final ProjectElementService projectElementService) {
167                 _projectElementService = projectElementService;
168         }
169
170         /**
171          * Get the publicationService.
172          * 
173          * @return the publicationService
174          */
175         public PublicationService getPublicationService() {
176                 return _publicationService;
177         }
178
179         /**
180          * Set the publicationService.
181          * 
182          * @param publicationService
183          *            the publicationService to set
184          */
185         public void setPublicationService(
186                         final PublicationService publicationService) {
187                 _publicationService = publicationService;
188         }
189
190         /**
191          * Get the stepService.
192          * 
193          * @return the stepService
194          */
195         public StepService getStepService() {
196                 return _stepService;
197         }
198
199         /**
200          * Set the stepService.
201          * 
202          * @param stepService
203          *            the stepService to set
204          */
205         public void setStepService(final StepService stepService) {
206                 _stepService = stepService;
207         }
208
209         /**
210          * {@inheritDoc}
211          * 
212          * @see org.splat.service.ScenarioService#getScenarioInfo(long)
213          */
214         @Transactional(readOnly = true)
215         public List<StepDTO> getScenarioInfo(final long scenarioId) {
216                 List<StepDTO> res = new ArrayList<StepDTO>();
217                 // Get the scenario from the database by id
218                 Scenario scen = getScenarioDAO().get(scenarioId);
219                 if (LOG.isDebugEnabled()) {
220                         LOG.debug("Scenario[" + scenarioId + "]: Number of publications: "
221                                         + scen.getDocums().size());
222                 }
223                 // Get activities of the scenario
224                 Step[] steps = getProjectElementService().getSteps(scen);
225                 StepDTO stepDTO;
226                 DocumentDTO docDTO;
227                 String docType, fileFormat;
228                 String processing;
229                 boolean doImport;
230                 // For each activity create a step DTO and add it to the result list
231                 for (Step step : steps) {
232                         stepDTO = BeanHelper.copyBean(step.getStep(), StepDTO.class);
233                         res.add(stepDTO);
234                         if (LOG.isDebugEnabled()) {
235                                 LOG.debug("Step[" + stepDTO.getNumber()
236                                                 + "]: Number of documents: "
237                                                 + step.getDocuments().size());
238                         }
239                         // For each publication of the activity create a document DTO.
240                         // Each file is considered as a source file.
241                         for (Publication tag : step.getDocuments()) {
242                                 docDTO = stepDTO.addDoc(tag.value().getIndex(), tag.value()
243                                                 .getTitle());
244                                 char aState = tag.getIsnew();
245                                 docType = tag.value().getType().getName();
246                                 // For each file of the document create a file DTO
247                                 // Process source file of the document
248                                 fileFormat = tag.value().getFile().getFormat();
249                                 doImport = getProjectSettings().doImport(docType, fileFormat);
250                                 if (doImport && (!tag.isOutdated())) {
251                                         processing = "file-import";
252                                 } else {
253                                         processing = "file-download";
254                                 }
255                                 File aFile = tag.value().getFile();
256                                 docDTO.addFile(aFile.getIndex(), aFile.getRelativePath(),
257                                                 aState, processing, false);
258                                 // Process all exported files
259                                 for (Relation rel : tag.value().getRelations(
260                                                 ConvertsRelation.class)) {
261                                         aFile = ((ConvertsRelation) rel).getTo();
262                                         fileFormat = aFile.getFormat();
263                                         doImport = getProjectSettings().doImport(docType,
264                                                         fileFormat);
265                                         if (doImport && (!tag.isOutdated())) {
266                                                 processing = "file-import";
267                                         } else {
268                                                 processing = "file-download";
269                                         }
270                                         docDTO.addFile(aFile.getIndex(), aFile.getRelativePath(),
271                                                         aState, processing, false);
272                                 }
273                         }
274                 }
275                 return res;
276         }
277
278         /**
279          * Create a new study with one scenario and "product" simulation context.
280          * 
281          * @param sprop
282          *            the study properties
283          * @param oprop
284          *            the scenario properties
285          * @param cprop
286          *            the "product" simulation context properties
287          * @return the created study
288          * @throws MissedPropertyException
289          *             if a mandatory property is missed
290          * @throws InvalidPropertyException
291          *             if a property is invalid
292          * @throws MultiplyDefinedException
293          *             if some property occurs several times
294          */
295         @Transactional
296         public Study createStudy(final Study.Properties sprop,
297                         final Scenario.Properties oprop,
298                         final SimulationContext.Properties cprop)
299                         throws MissedPropertyException, InvalidPropertyException,
300                         MultiplyDefinedException {
301                 Study study = getStudyService().createStudy(sprop);
302                 addScenario(study, oprop);
303                 if (cprop.getIndex() == 0) { // Input of new project context
304                         cprop.setType(getSimulationContextService().selectType("product"))
305                                         .setValue(cprop.getValue());
306                         getStudyService().addProjectContext(study, cprop);
307                 } else { // Selection of existing project context
308                         SimulationContext context = getSimulationContextService()
309                                         .selectSimulationContext(cprop.getIndex());
310                         getStudyService().addProjectContext(study, context);
311                 }
312                 return study;
313         }
314
315         /**
316          * {@inheritDoc}
317          * 
318          * @see org.splat.service.ScenarioService#addKnowledgeElement(org.splat.dal.bo.som.Scenario,
319          *      org.splat.dal.bo.som.KnowledgeElement.Properties)
320          */
321         @Transactional
322         public KnowledgeElement addKnowledgeElement(final Scenario aScenarioDTO,
323                         final KnowledgeElement.Properties kprop)
324                         throws MissedPropertyException, InvalidPropertyException,
325                         MultiplyDefinedException {
326                 KnowledgeElement kelm = null;
327                 try {
328                         long aScenarioId = aScenarioDTO.getIndex();
329                         if (LOG.isDebugEnabled()) {
330                                 LOG.debug("Add a knowledge element to the scenario #"
331                                                 + aScenarioId);
332                         }
333                         // Get the persistent scenario.
334                         Scenario aScenario = getScenarioDAO().get(aScenarioId);
335                         // Get persistent objects for creating a new knowledge.
336                         // TODO: Actions must use DTO instead of persistent objects.
337                         getUserDAO().merge(kprop.getAuthor());
338                         getKnowledgeElementTypeDAO().merge(kprop.getType());
339                         // Create a transient knowledge element related to the given scenario.
340                         kelm = new KnowledgeElement(kprop.setOwnerScenario(aScenario));
341                         // Save the new knowledge in the database.
342                         getKnowledgeElementDAO().create(kelm);
343                         // Update scenario transient data.
344                         if (kelm.getType().equals("usecase")) {
345                                 aScenarioDTO.setUcase(kelm);
346                         } else if (aScenarioDTO.getKnowledgeElementsList() != null) { // If null, knowl will be initialized when needed
347                                 aScenarioDTO.getKnowledgeElementsList().add(kelm);
348                         }
349
350                         // Load the workflow for the parent study to take into account
351                         // all study actors durng reindexing.
352                         getStudyService().loadWorkflow(aScenario.getOwnerStudy());
353
354                         // Update the lucene index of knowledge elements.
355                         getIndexService().add(kelm);
356                         if (LOG.isDebugEnabled()) {
357                                 LOG.debug("A knowledge element #" + kelm.getIndex()
358                                                 + " is added to the scenario #" + aScenario.getIndex());
359                         }
360                 } catch (IOException error) {
361                         LOG.error("Unable to index the knowedge element '"
362                                         + kelm.getIndex() + "', reason:", error);
363                         kelm = null;
364                 }
365
366                 return kelm;
367         }
368
369         /**
370          * Update the scenario in the database.
371          * 
372          * @param aScenario
373          *            the scenario to update
374          * @return true if updating succeeded
375          */
376         @Transactional
377         private boolean update(final Scenario aScenario) {
378                 boolean isOk = false;
379                 try {
380                         getScenarioDAO().update(aScenario); // Update of relational base
381                         isOk = true;
382                 } catch (Exception error) {
383                         LOG.error("Unable to re-index the knowledge element '"
384                                         + aScenario.getIndex() + "', reason:", error);
385                 }
386                 return isOk;
387         }
388
389         /**
390          * {@inheritDoc}
391          * 
392          * @see org.splat.service.ScenarioService#checkin(long, long, java.util.List)
393          */
394         @Transactional
395         public void checkin(final long scenId, final long userId,
396                         final List<StepDTO> scInfo) throws InvalidPropertyException,
397                         MissedPropertyException, MultiplyDefinedException,
398                         MismatchException, IOException, NotApplicableException {
399                 // Get the scenario from the database by id
400                 Scenario aScenario = getScenarioDAO().get(scenId);
401                 // Get the user who perform this check-in operation
402                 User aUser = getUserService().selectUser(userId);
403                 // Get activities of the scenario
404                 Step[] steps = getProjectElementService().getSteps(aScenario);
405                 // Find result document types
406                 List<DocumentType> resTypes = getDocumentTypeService()
407                                 .selectResultTypes();
408
409                 // Keep newly created documents to create uses relations to results of a previous step.
410                 // For each processed existing document keep its new version
411                 Map<Document, Document> newVersion = new HashMap<Document, Document>();
412                 // Created publications of new created versions of existing documents
413                 List<Publication> newVers = new ArrayList<Publication>();
414                 // The list of publications of new created documents not existing before the checkin
415                 List<Publication> newDocs = new ArrayList<Publication>();
416                 // For each step DTO
417                 DocumentType resType;
418                 Date aDate = new Date(); // The timestamp of the checkin operation
419                 for (StepDTO stepDTO : scInfo) {
420                         if (LOG.isDebugEnabled()) {
421                                 LOG.debug("Checkin the step:\n" + stepDTO);
422                         }
423                         // Find a result document type of the step
424                         int i = 0;
425                         resType = null;
426                         do {
427                                 if (resTypes.get(i).isResultOf(
428                                                 getProjectSettings().getStep(stepDTO.getNumber()))) {
429                                         resType = resTypes.get(i);
430                                 }
431                                 i++;
432                         } while ((resType == null) && (i < resTypes.size()));
433
434                         // Find the appropriate scenario step
435                         Step step = findStep(stepDTO, steps);
436
437                         // Process each document of the step
438                         for (DocumentDTO doc : stepDTO.getDocs()) {
439                                 checkinDoc(step, doc, aUser, resType, aDate, newVersion,
440                                                 newVers, newDocs);
441                         }
442                 }
443
444                 // Set uses/used relations
445                 updateRelationsAfterCheckin(aScenario, newVersion, newVers, newDocs);
446
447                 // Mark the scenario as checked in
448                 checkin(aScenario);
449         }
450
451         /**
452          * Updated uses/used relations after checkin operation:<BR>
453          * <ul>
454          * <li>For each new version copy uses relations from the previous version.</li>
455          * <li>Outdate documents which depend from the previous version and were not checked in during this operation.</li>
456          * <li>For each new document create uses relation to the last versions of results of the previous step.</li>
457          * </ul>
458          * 
459          * @param aScenario
460          *            the checked in scenario
461          * @param newVersion
462          *            the mapping of documents existed before the checkin to their new created versions
463          * @param newVers
464          *            the list of publications of new created versions of documents existed before the checkin
465          * @param newDocs
466          *            the list of publications of new created documents not existed before the checkin
467          */
468         private void updateRelationsAfterCheckin(final Scenario aScenario,
469                         final Map<Document, Document> newVersion,
470                         final List<Publication> newVers, final List<Publication> newDocs) {
471                 // For each new version copy uses relations from the previous version.
472                 for (Publication newVer : newVers) {
473                         // For each Uses relation of the previous version
474                         Document prevDoc = newVer.value().getPreviousVersion();// prevVersion.get(newVer);
475                         if (LOG.isDebugEnabled()) {
476                                 LOG.debug("Previous version for publication #"
477                                                 + newVer.getIndex() + " is found: " + prevDoc);
478                         }
479                         List<Relation> usesRelations = prevDoc
480                                         .getRelations(UsesRelation.class);
481                         for (Relation rel : usesRelations) {
482                                 // If used document has been also versioned then refer to its new version.
483                                 Document usedDoc = ((UsesRelation) rel).getTo();
484                                 if (newVersion.containsKey(usedDoc)) {
485                                         usedDoc = newVersion.get(usedDoc);
486                                 }
487                                 // Build the appropriate relation for the new version.
488                                 newVer.addDependency(usedDoc);
489                         }
490                         // Outdate documents which depend from the previous version and
491                         // were not checked in during this operation.
492                         // 1. Get all usedBy relations of the previous document version
493                         for (Relation rel : prevDoc.getRelations(UsedByRelation.class)) {
494                                 Document using = ((UsedByRelation) rel).getTo();
495                                 // Check that not checked in dependent documents became outdated
496                                 Publication usingPub = aScenario.getPublication(using);
497                                 if (usingPub != null) { // if the document using the old version is still published
498                                         usingPub.setIsnew('O');
499                                 }
500                         }
501                 }
502
503                 // For each new document create uses relation to the last versions of
504                 // results of the previous step.
505                 for (Publication newPub : newDocs) {
506                         // Find used document type according to the configuration.
507                         Set<DocumentType> usedTypes = newPub.value().getType()
508                                         .getDefaultUses();
509                         // Find documents of used type in the previous study step.
510                         for (Publication pub : aScenario.getDocums()) {
511                                 if ((pub.getStep().getNumber() <= newPub.getStep().getNumber())
512                                                 && (!pub.isOutdated())
513                                                 && usedTypes.contains(pub.value().getType())) {
514                                         // Create uses relation from the new document
515                                         // to the found document in the previous step.
516                                         newPub.addDependency(pub);
517                                 }
518                         }
519                 }
520         }
521
522         /**
523          * Pure checkin of the document without creation of uses/usedBy relations. For an existing document a new version is created. New
524          * documents become published in the given step of the appropriate scenario. The appropriate uploaded file is attached to the created
525          * document and the document is published in the scenario. The publication of the old version is removed from the scenario.
526          * 
527          * @param step
528          *            the destination scenario step
529          * @param doc
530          *            the DTO of the document to checkin
531          * @param aUser
532          *            the user who performs checkin
533          * @param resType
534          *            the result document type of the given step
535          * @param aDate
536          *            timestamp of the checkin operation
537          * @param newVersion
538          *            the mapping of existing documents to their new created versions
539          * @param newVers
540          *            the list of publications of new created versions of existing documents
541          * @param newDocs
542          *            the list of publications of new created documents not existing before the checkin
543          * @throws InvalidPropertyException
544          *             if the scenario hasn't some of given steps or documents
545          * @throws IOException
546          *             if a file can't be moved into the vault
547          * @throws MismatchException
548          *             if version creation in some of steps is failed
549          * @throws MissedPropertyException
550          *             if some mandatory property is missed when new document or new document version is created
551          * @throws MultiplyDefinedException
552          *             if some property is defined several times when new document or new document version is created
553          * @throws NotApplicableException
554          *             if failed saving of a new publication with a given state
555          */
556         private void checkinDoc(final Step step, final DocumentDTO doc,
557                         final User aUser, final DocumentType resType, final Date aDate,
558                         final Map<Document, Document> newVersion,
559                         final List<Publication> newVers, final List<Publication> newDocs)
560                         throws InvalidPropertyException, MismatchException,
561                         MissedPropertyException, MultiplyDefinedException, IOException,
562                         NotApplicableException {
563                 if (doc.getFiles().size() > 0) {
564                         Document.Properties dprop = new Document.Properties();
565                         // NOTE: Process only the first attached file for each document
566                         FileDTO file = doc.getFiles().get(0);
567                         dprop.setLocalPath(file.getPath());
568
569                         // Get document title as the file name
570                         java.io.File upfile = new java.io.File(file.getPath());
571                         String fileFormat = upfile.getName().substring(
572                                         upfile.getName().lastIndexOf('.') + 1);
573
574                         // Attach the file via ConvertsRelation, create a new document or
575                         // create a new version of the document
576                         dprop.setAuthor(aUser).setDate(aDate).setFormat(fileFormat);
577
578                         if (doc.getId() > 0) {
579                                 checkinExistingDoc(step, doc, dprop, fileFormat, upfile,
580                                                 newVersion, newVers);
581                         } else {
582
583                                 // Otherwise create a new document of the result type
584                                 // If result type is not found try to get type by file extension
585                                 if (resType == null) {
586                                         dprop.setType(getProjectSettings().getDefaultDocumentType(
587                                                         step.getStep(), fileFormat));
588                                 } else {
589                                         dprop.setType(resType);
590                                 }
591                                 // New document title generation as <document type name>_N
592                                 String docname = dprop.getType().getName();
593                                 int i = 1;
594                                 for (Publication scenPub : step.getOwner().getDocums()) {
595                                         if (scenPub.value().getTitle().startsWith(docname)) {
596                                                 i++;
597                                         }
598                                 }
599                                 docname += "_" + i; // The generated new document title
600
601                                 dprop.setDescription("Checked in").setName(docname);
602                                 Publication newPub = getStepService().createDocument(step,
603                                                 dprop);
604
605                                 // Remeber the new document
606                                 newDocs.add(newPub);
607
608                                 saveFile(newPub, step, upfile);
609                         }
610                 }
611         }
612
613         /**
614          * Check in existing document.
615          * 
616          * @param step
617          *            study step to check in
618          * @param doc
619          *            document DTO to check in
620          * @param dprop
621          *            document properties
622          * @param fileFormat
623          *            checked in file format
624          * @param upfile
625          *            the file to check in
626          * @param newVersion
627          *            the map of created versions during this check in
628          * @param newVers
629          *            the list of new versions created during this check in
630          * @throws InvalidPropertyException
631          *             if publication of the document is not found in the step
632          * @throws MismatchException
633          *             if the found publication does not point to a document
634          * @throws IOException
635          *             if can not move the file into the vault
636          * @throws MultiplyDefinedException
637          *             thrown by versionDocument
638          * @throws MissedPropertyException
639          *             thrown by versionDocument
640          * @throws NotApplicableException
641          *             if failed saving of a new publication with a given state
642          */
643         private void checkinExistingDoc(final Step step, final DocumentDTO doc,
644                         final Properties dprop, final String fileFormat,
645                         final java.io.File upfile,
646                         final Map<Document, Document> newVersion,
647                         final List<Publication> newVers) throws InvalidPropertyException,
648                         MismatchException, MissedPropertyException,
649                         MultiplyDefinedException, IOException, NotApplicableException {
650                 // If the document already exists then
651                 // Attach the file via ConvertsRelation if the extension of the
652                 // new file differs from the old one.
653                 // If file format (i.e. extension) is the same then create a new
654                 // version of the document.
655                 // Find the document publication
656                 Publication pub = step.getDocument(doc.getId());
657                 if (pub == null) {
658                         throw new InvalidPropertyException(MessageKeyEnum.SCN_000002
659                                         .toString(), doc.getId());
660                 }
661                 if (pub.value() == null) {
662                         throw new MismatchException(MessageKeyEnum.SCN_000002.toString(),
663                                         doc.getId());
664                 }
665                 if (LOG.isDebugEnabled()) {
666                         LOG.debug("Old format: " + pub.value().getFormat()
667                                         + " => New format: " + fileFormat);
668                 }
669                 // If formats are same then create a new document version
670                 if (pub.value().getFormat() != null
671                                 && pub.value().getFormat().equals(fileFormat)) {
672                         Publication newPub = getStepService().versionDocument(step, pub,
673                                         dprop);
674                         if (LOG.isDebugEnabled()) {
675                                 LOG.debug("Created document type: "
676                                                 + newPub.value().getType().getName() + ", format: "
677                                                 + newPub.value().getFormat());
678                         }
679                         // Remeber the link from the old document to the new document version
680                         newVersion.put(pub.value(), newPub.value());
681                         // Remember the new version publication
682                         newVers.add(newPub);
683
684                         saveFile(newPub, step, upfile);
685
686                 } else { // If formats are different then attach the new file via ConvertsRelation
687                         File attach = pub.value().getAttachedFile(fileFormat);
688                         if (attach == null) {
689                                 // If there is no attachment with this extension then attach the new one
690                                 ConvertsRelation export = getPublicationService().attach(pub,
691                                                 fileFormat);
692                                 if (LOG.isDebugEnabled()) {
693                                         LOG.debug("Moving " + upfile.getName() + " to "
694                                                         + export.getTo().asFile().getPath());
695                                 }
696                                 upfile.renameTo(export.getTo().asFile());
697                         } else {
698                                 // If an attachment with this extension already exists then
699                                 // replace it by the new one
700                                 upfile.renameTo(attach.asFile());
701                                 // Update attached file modification date
702                                 attach.setDate(new Date());
703                         }
704                 }
705         }
706
707         /**
708          * Save the file in the vault and create its publication in the step.
709          * 
710          * @param newPub
711          *            the new publication to save
712          * @param step
713          *            the study step to publish the document
714          * @param upfile
715          *            the downloaded file
716          * @throws IOException
717          *             if a file can't be moved into the vault
718          * @throws NotApplicableException
719          *             if failed saving of a new publication with a given state
720          */
721         private void saveFile(final Publication newPub, final Step step,
722                         final java.io.File upfile) throws IOException,
723                         NotApplicableException {
724                 // Attach the file to the created document
725                 java.io.File updir = newPub.getSourceFile().asFile();
726                 if (LOG.isDebugEnabled()) {
727                         LOG.debug("Moving \"" + upfile.getName() + "\" to \""
728                                         + updir.getPath() + "\".");
729                 }
730                 if (updir.exists()) {
731                         if (updir.delete()) {
732                                 LOG.info(MessageKeyEnum.SCN_000003.toString(), updir
733                                                 .getAbsoluteFile(), step.getOwner().getIndex());
734                         } else {
735                                 throw new IOException(
736                                                 "Can't delete the existing destination file to move file from "
737                                                                 + upfile.getAbsolutePath() + " to "
738                                                                 + updir.getAbsolutePath());
739                         }
740                 }
741                 if (upfile.renameTo(updir)) {
742                         // Save the new publication in the scenario.
743                         // The old publication is removed from the scenario here.
744                         getPublicationService().saveAs(newPub, ProgressState.inWORK); // May throw FileNotFound if rename was not done
745                 } else {
746                         throw new IOException("Can't move file from "
747                                         + upfile.getAbsolutePath() + " to "
748                                         + updir.getAbsolutePath());
749                 }
750         }
751
752         /**
753          * Find appropriate step in the array of scenario steps according to the given step DTO.
754          * 
755          * @param stepDTO
756          *            the stepDTO
757          * @param steps
758          *            scenario steps
759          * @return appropriate scenario step
760          * @throws InvalidPropertyException
761          *             if appropriate step is not found
762          */
763         private Step findStep(final StepDTO stepDTO, final Step[] steps)
764                         throws InvalidPropertyException {
765                 int i = 0;
766                 Step step = null;
767                 do {
768                         if (steps[i].getNumber() == stepDTO.getNumber()) {
769                                 step = steps[i];
770                         }
771                         i++;
772                 } while ((step == null) && (i < steps.length));
773
774                 if (step == null) {
775                         throw new InvalidPropertyException(MessageKeyEnum.SCN_000001
776                                         .toString(), stepDTO.getNumber());
777                 }
778                 return step;
779         }
780
781         /**
782          * {@inheritDoc}
783          * 
784          * @see org.splat.service.ScenarioService#checkin(long)
785          */
786         @Transactional
787         public void checkin(final long scenarioId) throws InvalidPropertyException {
788                 Scenario aScenario = getScenarioDAO().get(scenarioId);
789                 if (aScenario == null) {
790                         // Scenario not found
791                         throw new InvalidPropertyException(MessageKeyEnum.SCN_000006
792                                         .toString(), scenarioId);
793                 }
794                 checkin(aScenario);
795         }
796
797         /**
798          * Mark the scenario as checked in.
799          * 
800          * @param aScenario
801          *            the scenario to check in.
802          */
803         private void checkin(final Scenario aScenario) {
804                 aScenario.setUser(null);
805                 aScenario.setLastModificationDate(Calendar.getInstance().getTime());
806                 // getScenarioDAO().update(aScenario);
807         }
808
809         /**
810          * {@inheritDoc}
811          * 
812          * @see org.splat.service.ScenarioService#checkout(org.splat.dal.bo.som.Scenario, org.splat.dal.bo.kernel.User)
813          */
814         public boolean checkout(final Scenario aScenario, final User user) {
815                 boolean res = getStudyService().isStaffedBy(aScenario.getOwnerStudy(),
816                                 user);
817                 if (res) {
818                         aScenario.setUser(user);
819                         aScenario.setLastModificationDate(Calendar.getInstance().getTime());
820                         // RKV: getScenarioDAO().update(aScenario);
821                 }
822                 return res;
823         }
824
825         /**
826          * Mark the given scenario as checked out by the given user.
827          * 
828          * @param scenarioId
829          *            the scenario id
830          * @param userId
831          *            the id of the user performing the check out
832          * @throws InvalidPropertyException
833          *             if the user or the scenario is not found in the database
834          * @throws NotApplicableException
835          *             if the given user can not check out the scenario
836          */
837         @Transactional
838         public void checkout(final long scenarioId, final long userId)
839                         throws InvalidPropertyException, NotApplicableException {
840                 User aUser = getUserService().selectUser(userId);
841                 if (aUser == null) {
842                         // User not found
843                         throw new InvalidPropertyException(MessageKeyEnum.USR_000001
844                                         .toString(), userId);
845                 }
846                 Scenario aScenario = getScenarioDAO().get(scenarioId);
847                 if (aScenario == null) {
848                         // Scenario not found
849                         throw new InvalidPropertyException(MessageKeyEnum.SCN_000006
850                                         .toString(), scenarioId);
851                 }
852                 boolean res = getStudyService().isStaffedBy(aScenario.getOwnerStudy(),
853                                 aUser);
854                 if (res) {
855                         if (aScenario.isCheckedout()
856                                         && (!aScenario.getUser().getUsername().equals(
857                                                         aUser.getUsername()))) {
858                                 throw new NotApplicableException(MessageKeyEnum.SCN_000008
859                                                 .toString(), scenarioId, aScenario.getUser()
860                                                 .getUsername());
861                         }
862                         aScenario.setUser(aUser);
863                         aScenario.setLastModificationDate(Calendar.getInstance().getTime());
864                 } else {
865                         // User doesn't participate in the scenario
866                         throw new NotApplicableException(MessageKeyEnum.SCN_000007
867                                         .toString(), aUser.getUsername(), scenarioId);
868                 }
869         }
870
871         /**
872          * {@inheritDoc}
873          * 
874          * @see org.splat.service.ScenarioService#copyContentsUpTo(org.splat.dal.bo.som.Scenario, org.splat.som.Step)
875          */
876         public void copyContentsUpTo(final Scenario scenario, final Step lastep) {
877                 Scenario base = (Scenario) lastep.getOwner();
878                 Step[] from = getProjectElementService().getSteps(base);
879                 Step[] to = getProjectElementService().getSteps(scenario);
880                 for (int i = 0; i < from.length; i++) {
881                         Step step = from[i];
882                         if (step.getNumber() > lastep.getNumber()) {
883                                 break;
884                         }
885
886                         List<Publication> docs = step.getAllDocuments();
887                         for (Iterator<Publication> j = docs.iterator(); j.hasNext();) {
888                                 Publication doc = getPublicationService().copy(j.next(),
889                                                 scenario); // Creation of a new reference to the document
890                                 // Database.getSession().save(doc); Publications MUST be saved later through cascading when saving the scenario
891                                 getStepService().add(to[i], doc);
892                         }
893                         List<SimulationContext> ctex = step.getAllSimulationContexts();
894                         for (Iterator<SimulationContext> j = ctex.iterator(); j.hasNext();) {
895                                 getStepService().addSimulationContext(to[i], j.next());
896                         }
897                 }
898         }
899
900         /**
901          * {@inheritDoc}
902          * 
903          * @see org.splat.service.ScenarioService#isEmpty(org.splat.dal.bo.som.Scenario)
904          */
905         public boolean isEmpty(final Scenario scenario) {
906                 Step[] mystep = getProjectElementService().getSteps(scenario);
907                 boolean isEmp = true;
908                 for (int i = 0; i < mystep.length; i++) {
909                         if (mystep[i].isStarted()) {
910                                 isEmp = false;
911                                 break;
912                         }
913                 }
914                 return isEmp;
915         }
916
917         /**
918          * @param scenario
919          * @return
920          */
921         public boolean isFinished(final Scenario scenario) {
922                 Step[] mystep = getProjectElementService().getSteps(scenario);
923                 boolean notempty = false; // If this is empty, this is not finished
924                 for (int i = 0; i < mystep.length; i++) {
925                         if (!mystep[i].isStarted()) {
926                                 continue;
927                         }
928                         if (!mystep[i].isFinished()) {
929                                 return false;
930                         }
931                         notempty = true;
932                 }
933                 return notempty;
934         }
935
936         /**
937          * {@inheritDoc}
938          * 
939          * @see org.splat.service.StudyService#addScenario(org.splat.dal.bo.som.Study, org.splat.dal.bo.som.Scenario.Properties)
940          */
941         @Transactional
942         public Scenario addScenario(final Study aStudy,
943                         final Scenario.Properties sprop) throws MissedPropertyException,
944                         InvalidPropertyException, MultiplyDefinedException {
945                 if (sprop.getManager() == null) {
946                         sprop.setManager(aStudy.getAuthor());
947                 }
948
949                 Scenario scenario = new Scenario(sprop.setOwnerStudy(aStudy));
950                 if (sprop.getBaseStep() != null) {
951                         copyContentsUpTo(scenario, sprop.getBaseStep());
952                 }
953                 Scenario previous = sprop.getInsertAfter();
954
955                 if (previous == null) {
956                         aStudy.getScenariiList().add(scenario);
957                 } else {
958                         aStudy.getScenariiList().add(
959                                         aStudy.getScenariiList().indexOf(previous) + 1, scenario);
960                 }
961                 getStudyDAO().update(aStudy); // No need to update the Lucene index
962                 getScenarioDAO().create(scenario); // Must be done after updating this study because of the back reference to the study
963                 if (sprop.getBaseStep() != null) {
964                         // No need to update the Knowledge Element index as Knowledge Elements are not copied
965                         getProjectElementService().refresh(scenario); // Because saving the scenario changes the hashcode of copied Publications
966                 }
967                 KnowledgeElementType ucase = getKnowledgeElementTypeService()
968                                 .selectType("usecase");
969                 KnowledgeElement.Properties kprop = new KnowledgeElement.Properties();
970                 // TODO: Get appropriate user by its role: UserService.getAdmin();
971                 // User admin = getUserService().selectUser(1); // First user created when creating the database
972                 Role adminRole = getRoleDAO().getFilteredList(
973                                 Restrictions.like("role", "%sysadmin%")).get(0);
974                 User admin = getUserDAO().getFilteredList(
975                                 Restrictions.eq("role", adminRole), Order.asc("rid")).get(0); // First sysadmin in the database
976
977                 kprop.setType(ucase).setTitle(aStudy.getTitle()).setValue(
978                                 scenario.getTitle()).setAuthor(admin); // Internal Knowledge Element required by the validation process of
979                 // knowledges
980                 addKnowledgeElement(scenario, kprop);
981                 return scenario;
982         }
983
984         /**
985          * Remove a knowledge element from a scenario.
986          * 
987          * @param scenario
988          *            the scenario
989          * @param kelm
990          *            the knowledge element to remove
991          * @return true if removal succeeded
992          */
993         @Transactional
994         public boolean removeKnowledgeElement(final Scenario scenario,
995                         final KnowledgeElement kelm) {
996                 KnowledgeElement torem = scenario.getKnowledgeElement(kelm.getIndex());
997                 boolean isOk = (torem != null);
998                 if (isOk) {
999                         isOk = scenario.getKnowledgeElements().remove(torem);
1000                         if (isOk) {
1001                                 getScenarioDAO().merge(scenario);
1002                                 // Update of my transient data
1003                                 // RKV: These transient data are not used indeed.
1004                                 // RKV: List<KnowledgeElement> kelms = scenario.getKnowledgeByType().get(
1005                                 // RKV: kelm.getType().getIndex());
1006                                 // RKV: kelms.remove(torem);
1007                                 if (scenario.getKnowledgeElementsList() != null) {
1008                                         scenario.getKnowledgeElementsList().remove(torem);
1009                                 }
1010                                 // TODO: If the owner study is not private, remove the knowledge from the Lucene index
1011                         }
1012                 }
1013                 return isOk;
1014         }
1015
1016         /**
1017          * 
1018          * {@inheritDoc}
1019          * 
1020          * @see org.splat.service.ScenarioService#renameScenario(java.lang.String)
1021          */
1022         @Transactional
1023         public void renameScenario(final Scenario scenario) {
1024                 getScenarioDAO().merge(scenario);
1025         }
1026
1027         /**
1028          * Get the knowledgeElementDAO.
1029          * 
1030          * @return the knowledgeElementDAO
1031          */
1032         public KnowledgeElementDAO getKnowledgeElementDAO() {
1033                 return _knowledgeElementDAO;
1034         }
1035
1036         /**
1037          * Set the knowledgeElementDAO.
1038          * 
1039          * @param knowledgeElementDAO
1040          *            the knowledgeElementDAO to set
1041          */
1042         public void setKnowledgeElementDAO(
1043                         final KnowledgeElementDAO knowledgeElementDAO) {
1044                 _knowledgeElementDAO = knowledgeElementDAO;
1045         }
1046
1047         /**
1048          * Get the indexService.
1049          * 
1050          * @return the indexService
1051          */
1052         public IndexService getIndexService() {
1053                 return _indexService;
1054         }
1055
1056         /**
1057          * Set the indexService.
1058          * 
1059          * @param indexService
1060          *            the indexService to set
1061          */
1062         public void setIndexService(final IndexService indexService) {
1063                 _indexService = indexService;
1064         }
1065
1066         /**
1067          * Get the scenarioDAO.
1068          * 
1069          * @return the scenarioDAO
1070          */
1071         public ScenarioDAO getScenarioDAO() {
1072                 return _scenarioDAO;
1073         }
1074
1075         /**
1076          * Set the scenarioDAO.
1077          * 
1078          * @param scenarioDAO
1079          *            the scenarioDAO to set
1080          */
1081         public void setScenarioDAO(final ScenarioDAO scenarioDAO) {
1082                 _scenarioDAO = scenarioDAO;
1083         }
1084
1085         /**
1086          * Get the studyDAO.
1087          * 
1088          * @return the studyDAO
1089          */
1090         public StudyDAO getStudyDAO() {
1091                 return _studyDAO;
1092         }
1093
1094         /**
1095          * Set the studyDAO.
1096          * 
1097          * @param studyDAO
1098          *            the studyDAO to set
1099          */
1100         public void setStudyDAO(final StudyDAO studyDAO) {
1101                 _studyDAO = studyDAO;
1102         }
1103
1104         /**
1105          * Get the knowledgeElementTypeService.
1106          * 
1107          * @return the knowledgeElementTypeService
1108          */
1109         public KnowledgeElementTypeService getKnowledgeElementTypeService() {
1110                 return _knowledgeElementTypeService;
1111         }
1112
1113         /**
1114          * Set the knowledgeElementTypeService.
1115          * 
1116          * @param knowledgeElementTypeService
1117          *            the knowledgeElementTypeService to set
1118          */
1119         public void setKnowledgeElementTypeService(
1120                         final KnowledgeElementTypeService knowledgeElementTypeService) {
1121                 _knowledgeElementTypeService = knowledgeElementTypeService;
1122         }
1123
1124         /**
1125          * Get the studyService.
1126          * 
1127          * @return the studyService
1128          */
1129         public StudyService getStudyService() {
1130                 return _studyService;
1131         }
1132
1133         /**
1134          * Set the studyService.
1135          * 
1136          * @param studyService
1137          *            the studyService to set
1138          */
1139         public void setStudyService(final StudyService studyService) {
1140                 _studyService = studyService;
1141         }
1142
1143         /**
1144          * Get the userService.
1145          * 
1146          * @return the userService
1147          */
1148         public UserService getUserService() {
1149                 return _userService;
1150         }
1151
1152         /**
1153          * Set the userService.
1154          * 
1155          * @param userService
1156          *            the userService to set
1157          */
1158         public void setUserService(final UserService userService) {
1159                 _userService = userService;
1160         }
1161
1162         /**
1163          * Get the userDAO.
1164          * 
1165          * @return the userDAO
1166          */
1167         public UserDAO getUserDAO() {
1168                 return _userDAO;
1169         }
1170
1171         /**
1172          * Set the userDAO.
1173          * 
1174          * @param userDAO
1175          *            the userDAO to set
1176          */
1177         public void setUserDAO(final UserDAO userDAO) {
1178                 _userDAO = userDAO;
1179         }
1180
1181         /**
1182          * Get the knowledgeElementTypeDAO.
1183          * 
1184          * @return the knowledgeElementTypeDAO
1185          */
1186         public KnowledgeElementTypeDAO getKnowledgeElementTypeDAO() {
1187                 return _knowledgeElementTypeDAO;
1188         }
1189
1190         /**
1191          * Set the knowledgeElementTypeDAO.
1192          * 
1193          * @param knowledgeElementTypeDAO
1194          *            the knowledgeElementTypeDAO to set
1195          */
1196         public void setKnowledgeElementTypeDAO(
1197                         final KnowledgeElementTypeDAO knowledgeElementTypeDAO) {
1198                 _knowledgeElementTypeDAO = knowledgeElementTypeDAO;
1199         }
1200
1201         /**
1202          * Get the simulationContextService.
1203          * 
1204          * @return the simulationContextService
1205          */
1206         public SimulationContextService getSimulationContextService() {
1207                 return _simulationContextService;
1208         }
1209
1210         /**
1211          * Set the simulationContextService.
1212          * 
1213          * @param simulationContextService
1214          *            the simulationContextService to set
1215          */
1216         public void setSimulationContextService(
1217                         final SimulationContextService simulationContextService) {
1218                 _simulationContextService = simulationContextService;
1219         }
1220
1221         /**
1222          * Get project settings.
1223          * 
1224          * @return Project settings service
1225          */
1226         private ProjectSettingsService getProjectSettings() {
1227                 return _projectSettings;
1228         }
1229
1230         /**
1231          * Set project settings service.
1232          * 
1233          * @param projectSettingsService
1234          *            project settings service
1235          */
1236         public void setProjectSettings(
1237                         final ProjectSettingsService projectSettingsService) {
1238                 _projectSettings = projectSettingsService;
1239         }
1240
1241         /**
1242          * Get the documentTypeService.
1243          * 
1244          * @return the documentTypeService
1245          */
1246         public DocumentTypeService getDocumentTypeService() {
1247                 return _documentTypeService;
1248         }
1249
1250         /**
1251          * Set the documentTypeService.
1252          * 
1253          * @param documentTypeService
1254          *            the documentTypeService to set
1255          */
1256         public void setDocumentTypeService(
1257                         final DocumentTypeService documentTypeService) {
1258                 _documentTypeService = documentTypeService;
1259         }
1260
1261         /**
1262          * Get the roleDAO.
1263          * 
1264          * @return the roleDAO
1265          */
1266         public RoleDAO getRoleDAO() {
1267                 return _roleDAO;
1268         }
1269
1270         /**
1271          * Set the roleDAO.
1272          * 
1273          * @param roleDAO
1274          *            the roleDAO to set
1275          */
1276         public void setRoleDAO(final RoleDAO roleDAO) {
1277                 _roleDAO = roleDAO;
1278         }
1279
1280 }