Salome HOME
Mapping is fixed. Relation in the mapping is inherited from Persistent now. Versionin...
[tools/siman.git] / Workspace / Siman-Common / src / org / splat / service / DocumentServiceImpl.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.text.DecimalFormat;
13 import java.text.SimpleDateFormat;
14 import java.util.Calendar;
15 import java.util.Iterator;
16 import java.util.List;
17
18 import org.hibernate.criterion.Criterion;
19 import org.hibernate.criterion.Restrictions;
20 import org.splat.dal.bo.kernel.Relation;
21 import org.splat.dal.bo.som.ConvertsRelation;
22 import org.splat.dal.bo.som.Document;
23 import org.splat.dal.bo.som.DocumentType;
24 import org.splat.dal.bo.som.File;
25 import org.splat.dal.bo.som.ProgressState;
26 import org.splat.dal.bo.som.ProjectElement;
27 import org.splat.dal.bo.som.Scenario;
28 import org.splat.dal.bo.som.StampRelation;
29 import org.splat.dal.bo.som.Study;
30 import org.splat.dal.bo.som.Timestamp;
31 import org.splat.dal.bo.som.ValidationStep;
32 import org.splat.dal.bo.som.VersionsRelation;
33 import org.splat.dal.bo.som.Document.Properties;
34 import org.splat.dal.dao.som.DocumentDAO;
35 import org.splat.dal.dao.som.DocumentTypeDAO;
36 import org.splat.dal.dao.som.FileDAO;
37 import org.splat.dal.dao.som.StudyDAO;
38 import org.splat.kernel.InvalidPropertyException;
39 import org.splat.kernel.MissedPropertyException;
40 import org.splat.kernel.NotApplicableException;
41 import org.splat.manox.Reader;
42 import org.splat.manox.Toolbox;
43 import org.splat.service.technical.ProjectSettingsService;
44 import org.splat.service.technical.ProjectSettingsServiceImpl;
45 import org.splat.service.technical.RepositoryService;
46 import org.splat.service.technical.ProjectSettingsServiceImpl.FileNaming;
47 import org.splat.som.Revision;
48 import org.springframework.transaction.annotation.Transactional;
49
50 /**
51  * Document service implementation.
52  * 
53  * @author <a href="mailto:roman.kozlov@opencascade.com">Roman Kozlov (RKV)</a>
54  * 
55  */
56 public class DocumentServiceImpl implements DocumentService {
57
58         /**
59          * Injected study service.
60          */
61         private StudyService _studyService;
62         /**
63          * Injected project settings service.
64          */
65         private ProjectSettingsService _projectSettingsService;
66         /**
67          * Injected repository service.
68          */
69         private RepositoryService _repositoryService;
70         /**
71          * Injected document DAO.
72          */
73         private DocumentDAO _documentDAO;
74         /**
75          * Injected document type DAO.
76          */
77         private DocumentTypeDAO _documentTypeDAO;
78         /**
79          * Injected file DAO.
80          */
81         private FileDAO _fileDAO;
82         /**
83          * Injected study DAO.
84          */
85         private StudyDAO _studyDAO;
86
87         /**
88          * {@inheritDoc}
89          * 
90          * @see org.splat.service.DocumentService#selectDocument(long)
91          */
92         @Transactional(readOnly = true)
93         public Document selectDocument(long index) {
94                 return getDocumentDAO().get(index);
95         }
96
97         /**
98          * {@inheritDoc}
99          * 
100          * @see org.splat.service.DocumentService#selectDocument(java.lang.String, java.lang.String)
101          */
102         @Transactional(readOnly = true)
103         public Document selectDocument(String refid, String version) {
104                 Criterion aCondition = Restrictions.and(Restrictions.eq("did", refid),
105                                 Restrictions.eq("version", version));
106                 return getDocumentDAO().findByCriteria(aCondition);
107         }
108
109         /**
110          * {@inheritDoc}
111          * 
112          * @see org.splat.service.DocumentService#generateDocumentId(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
113          */
114         @Transactional
115         public void generateDocumentId(Document aDoc, Properties dprop) {
116                 Study owner = null;
117                 if (dprop.getOwner() instanceof Study)
118                         owner = (Study) dprop.getOwner();
119                 else
120                         owner = ((Scenario) dprop.getOwner()).getOwnerStudy();
121
122                 // Synchronize the object with the current Hibernate session.
123                 owner = getStudyDAO().get(owner.getIndex());
124                 
125                 SimpleDateFormat tostring = new SimpleDateFormat("yyyy");
126                 String year = tostring.format(owner.getDate());
127                 String filename = generateEncodedName(aDoc, owner);
128                 String path = owner.getReference();
129                 ProjectSettingsService.Step step = ProjectSettingsServiceImpl
130                                 .getStep(aDoc.getStep());
131                 aDoc.setDid(new StringBuffer(path).append(".%")
132                                 .append(Document.suformat).toString()); // Document reference
133                 path = new StringBuffer(year).append("/").append(path).append("/")
134                                 .append(step.getPath())
135                                 // File path relative to the repository vault
136                                 .append(filename).append(".")
137                                 .append(aDoc.getFile().getFormat()) // File name and extension
138                                 .toString();
139                 aDoc.getFile().changePath(path);
140         }
141
142         /**
143          * Generate encoded document file name according to the naming scheme from project settings.
144          * 
145          * @param aDoc
146          *            the document
147          * @param scope
148          *            the study
149          * @return document reference name
150          */
151         private String generateEncodedName(Document aDoc, Study scope) {
152                 StringBuffer encoding = new StringBuffer();
153                 FileNaming scheme = getProjectSettings().getFileNamingScheme();
154                 DecimalFormat tostring = new DecimalFormat(Document.suformat);
155
156                 int number = getStudyService().generateLocalIndex(scope);
157
158                 if (scheme == FileNaming.encoded) {
159                         encoding.append(scope.getReference()).append(".")
160                                         .append(tostring.format(number));
161                 } else { // title and (temporarily) asis
162                         encoding.append(aDoc.getTitle()).append(".")
163                                         .append(tostring.format(number));
164                 }
165                 return encoding.toString();
166         }
167
168         /**
169          * Get encoded root name for a document file.
170          * 
171          * @param aDoc
172          *            the document
173          * @param scope
174          *            the study
175          * @return file name
176          */
177         private String getEncodedRootName(Document aDoc, Study scope) {
178                 FileNaming scheme = getProjectSettings().getFileNamingScheme();
179
180                 if (scheme == FileNaming.encoded)
181                         return scope.getReference();
182                 else
183                         return aDoc.getTitle();
184         }
185
186         /**
187          * {@inheritDoc}
188          * 
189          * @see org.splat.service.DocumentService#initialize(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
190          */
191         @Transactional
192         public void initialize(Document aDoc, Properties dprop)
193                         throws MissedPropertyException, InvalidPropertyException,
194                         NotApplicableException {
195                 if (!aDoc.isUndefined())
196                         throw new NotApplicableException(
197                                         "Cannot initialize an existing Document");
198                 if (dprop.getName() == null)
199                         throw new MissedPropertyException("name");
200                 if (dprop.getName().length() == 0)
201                         throw new InvalidPropertyException("name");
202                 if (dprop.getOwner() == null)
203                         throw new MissedPropertyException("owner");
204                 // if (dprop.owner instanceof Study && !ProjectSettings.getStep(step).appliesTo(Study.class)) {
205                 // throw new InvalidPropertyException("step");
206                 // }
207                 aDoc.setTitle(dprop.getName());
208                 aDoc.getFile().changePath(
209                                 aDoc.getFile()
210                                                 .getRelativePath()
211                                                 .replace(
212                                                                 "%n",
213                                                                 getEncodedRootName(aDoc,
214                                                                                 (Study) dprop.getOwner())));
215                 if (aDoc.getHistory() == -1)
216                         aDoc.setHistory(0);
217                 if (dprop.getDate() == null) {
218                         Calendar current = Calendar.getInstance();
219                         aDoc.setLastModificationDate(current.getTime()); // Today
220                 } else {
221                         aDoc.setLastModificationDate(dprop.getDate());
222                 }
223                 getDocumentDAO().update(aDoc);
224         }
225
226         /**
227          * {@inheritDoc}
228          * 
229          * @see org.splat.service.DocumentService#getSaveDirectory(org.splat.dal.bo.som.Document)
230          */
231         public java.io.File getSaveDirectory(Document aDoc) {
232                 String mypath = getRepositoryService().getRepositoryVaultPath()
233                                 + aDoc.getSourceFile().getRelativePath();
234                 String[] table = mypath.split("/");
235
236                 // Cutting the filename
237                 StringBuffer path = new StringBuffer(table[0]);
238                 for (int i = 1; i < table.length - 1; i++)
239                         path = path.append("/").append(table[i]);
240                 return new java.io.File(path.append("/").toString());
241         }
242
243         /**
244          * Extract title and reference properties from the given file.
245          * 
246          * @param file
247          *            the file to parse
248          * @return the extracted properties
249          */
250         public Properties extractProperties(java.io.File file) {
251                 Properties fprop = new Properties();
252                 Reader tool = Toolbox.getReader(file);
253                 String value;
254                 if (tool != null)
255                         try {
256                                 value = tool.extractProperty("title");
257                                 if (value != null)
258                                         fprop.setName(value);
259
260                                 value = tool.extractProperty("reference");
261                                 if (value != null)
262                                         fprop.setReference(value);
263                         } catch (Exception e) {
264                         }
265                 return fprop;
266         }
267
268         /**
269          * Create "Converts" relation for the given document and the given format.
270          * 
271          * @param aDoc
272          *            the document
273          * @param format
274          *            the format
275          * @return the created "Converts" relation
276          */
277         public ConvertsRelation attach(Document aDoc, String format) {
278                 return attach(aDoc, format, null);
279         }
280
281         /**
282          * Create "Converts" relation for the given document, format and description.
283          * 
284          * @param aDoc
285          *            the document
286          * @param format
287          *            the format
288          * @param description
289          *            the description of the relation
290          * @return the created "Converts" relation
291          */
292         @Transactional
293         public ConvertsRelation attach(Document aDoc, String format,
294                         String description) {
295                 String path = aDoc.getRelativePath();
296                 File export = new File(path + "." + format);
297                 ConvertsRelation attach = new ConvertsRelation(aDoc, export,
298                                 description);
299
300                 // RKV Session session = Database.getSession();
301                 // RKV session.save(export);
302                 // RKV session.save(attach);
303
304                 getFileDAO().create(export);
305
306                 // RKV aDoc.addRelation(attach); // Updates this
307                 aDoc.getAllRelations().add(attach); // Updates this //RKV
308
309                 return attach;
310         }
311
312         /**
313          * Build a reference (document id) for the given document in the given study basing on the given original document.
314          * 
315          * @param aDoc
316          *            the document to set set the new reference to
317          * @param scope
318          *            the study
319          * @param lineage
320          *            the original document
321          * @return true if the new reference is set
322          */
323         public boolean buildReferenceFrom(Document aDoc, ProjectElement scope,
324                         Document lineage) {
325                 if (aDoc.getProgressState() != ProgressState.inWORK)
326                         return false;
327                 Study owner = null;
328                 Scenario context = null;
329                 if (scope instanceof Study)
330                         owner = (Study) scope;
331                 else {
332                         context = ((Scenario) scope);
333                         owner = context.getOwnerStudy();
334                 }
335                 aDoc.setDid(lineage.getDid());
336                 if (context != null && (lineage.isVersioned() || owner.shares(lineage))) {
337                         aDoc.setVersion(new Revision(aDoc.getVersion()).setBranch(
338                                         context.getReference()).toString());
339                 }
340                 return true;
341         }
342
343         /**
344          * Build a reference (document id) for the given document in the given study.
345          * 
346          * @param aDoc
347          *            the document to set set the new reference to
348          * @param scope
349          *            the study
350          * @return true if the new reference is set
351          */
352         public boolean buildReferenceFrom(Document aDoc, Study scope) {
353                 if (aDoc.getProgressState() != ProgressState.inWORK
354                                 && aDoc.getProgressState() != ProgressState.EXTERN)
355                         return false;
356                 DecimalFormat tostring = new DecimalFormat(Document.suformat);
357
358                 aDoc.setDid(aDoc.getDid().replace("%" + Document.suformat,
359                                 tostring.format(scope.getLastLocalIndex())));
360                 return true;
361         }
362
363         /**
364          * Demote a document.
365          * 
366          * @param aDoc
367          *            the document to demote
368          * @return true if demoting succeeded
369          */
370         @Transactional
371         public boolean demote(Document aDoc) {
372                 ValidationStep torem;
373
374                 if (aDoc.getProgressState() == ProgressState.inCHECK) {
375                         aDoc.setProgressState(ProgressState.inDRAFT);
376                         torem = ValidationStep.REVIEW;
377                         // This operation must not change the version number of documents.
378                         // Consequently, inDRAFT documents may have a minor version number equal to zero.
379                 } else if (aDoc.getProgressState() == ProgressState.inDRAFT) {
380                         aDoc.setProgressState(ProgressState.inWORK);
381                         torem = ValidationStep.PROMOTION;
382                 } else {
383                         return false;
384                 }
385                 for (Iterator<Relation> i = aDoc.getAllRelations().iterator(); i
386                                 .hasNext();) {
387                         Relation link = i.next();
388                         if (!(link instanceof StampRelation))
389                                 continue;
390                         if (((StampRelation) link).getStampType() != torem)
391                                 continue;
392                         i.remove();
393                         break;
394                 }
395                 getDocumentDAO().update(aDoc);
396                 return true;
397         }
398
399         /**
400          * Promote a document.
401          * 
402          * @param aDoc
403          *            the document to promote
404          * @param stamp
405          *            the timestamp of the promotion
406          * @return true if promotion succeeded
407          */
408         @Transactional
409         public boolean promote(Document aDoc, Timestamp stamp) {
410                 ProgressState newstate = null;
411
412                 if (aDoc.getProgressState() == ProgressState.inWORK) {
413                         newstate = ProgressState.inDRAFT; // Promotion to being reviewed
414                 } else if (aDoc.getProgressState() == ProgressState.inDRAFT) {
415                         newstate = ProgressState.inCHECK; // Promotion to approval
416                         Revision myvers = new Revision(aDoc.getVersion());
417                         if (myvers.isMinor()) {
418                                 aDoc.setVersion(myvers.incrementAs(newstate).toString());
419                                 // TODO: If my physical file is programatically editable, update its (property) version number
420                                 // ISSUE: What about attached files such as PDF if exist, should we remove them ?
421                         }
422                 } else if (aDoc.getProgressState() == ProgressState.inCHECK) {
423                         newstate = ProgressState.APPROVED;
424                 }
425                 aDoc.setProgressState(newstate);
426                 if (stamp != null) {
427                         // RKV: aDoc.addRelation(stamp.getContext());
428                         aDoc.getAllRelations().add(stamp.getContext());
429                 }
430                 getDocumentDAO().update(aDoc);
431                 return true;
432         }
433
434         /**
435          * Increments the reference count of this document following its publication in a Study step.
436          * 
437          * @param aDoc
438          *            the document to hold
439          * @see #release()
440          */
441         @Transactional
442         public void hold(Document aDoc) {
443                 aDoc.setCountag(aDoc.getCountag() + 1);
444                 if (aDoc.isSaved()) {
445                         getDocumentDAO().update(aDoc);
446                 }
447         }
448
449         /**
450          * Decrements the reference count of this document following the removal of a Publication from a Study step.
451          * 
452          * @param aDoc
453          *            the document to release
454          * @see #hold()
455          */
456         @Transactional
457         public void release(Document aDoc) {
458                 aDoc.setCountag(aDoc.getCountag() - 1);
459                 if (aDoc.isSaved()) {
460                         getDocumentDAO().merge(aDoc);
461                 }
462         }
463
464         /**
465          * Rename a document.
466          * 
467          * @param aDoc
468          *            the document to rename
469          * @param title
470          *            the new document title
471          * @throws InvalidPropertyException
472          *             if the new title is empty
473          */
474         @Transactional
475         public void rename(Document aDoc, String title)
476                         throws InvalidPropertyException {
477                 if (title.length() == 0)
478                         throw new InvalidPropertyException("name");
479
480                 Calendar current = Calendar.getInstance();
481                 aDoc.setTitle(title);
482                 aDoc.setLastModificationDate(current.getTime()); // Today
483                 getDocumentDAO().update(aDoc);
484         }
485
486         /**
487          * Update a version of the given document.
488          * 
489          * @param aDoc
490          *            the document
491          * @param newvers
492          *            the new version
493          */
494         public void updateAs(Document aDoc, Revision newvers) {
495                 aDoc.setVersion(newvers.setBranch(aDoc.getVersion()).toString()); // Branch names are propagated by the versionning
496                 ProgressState newstate = ProgressState.inCHECK;
497                 if (newvers.isMinor())
498                         newstate = ProgressState.inWORK;
499                 aDoc.setProgressState(null); // Just to tell updateAs(state) to not increment the version number
500                 updateAs(aDoc, newstate);
501         }
502
503         /**
504          * Update a state of the given document.
505          * 
506          * @param aDoc
507          *            the document
508          * @param state
509          *            the new state
510          */
511         @Transactional
512         public void updateAs(Document aDoc, ProgressState state) {
513                 Document previous = null;
514
515                 // Set of version number
516                 if (state == ProgressState.EXTERN) {
517                         if (aDoc.getProgressState() != ProgressState.EXTERN)
518                                 aDoc.setVersion(null); // Strange use-case...
519                 } else {
520                         Revision myvers = new Revision(aDoc.getVersion());
521                         if (!myvers.isNull()) { // Versionning context
522                                 for (Iterator<Relation> i = aDoc.getAllRelations().iterator(); i
523                                                 .hasNext();) {
524                                         Relation link = i.next();
525                                         if (!link.getClass().equals(VersionsRelation.class))
526                                                 continue;
527                                         previous = (Document) link.getTo(); // Versioned document
528                                         break;
529                                 }
530                         }
531                         if (aDoc.getProgressState() != null)
532                                 myvers.incrementAs(state); // Incrementation if the reversion number is not imposed
533                         aDoc.setVersion(myvers.toString());
534                 }
535                 // Update this document and the previous version, if exit
536                 if (previous != null) {
537                         previous.setHistory(previous.getHistory() + 1);
538                         getDocumentDAO().merge(previous);
539                 }
540                 aDoc.setProgressState(state);
541                 getDocumentDAO().update(aDoc);
542         }
543
544         // protected void upgrade () {
545         // -------------------------
546         // if (this.state != ProgressState.inWORK) return;
547         //
548         // Calendar current = Calendar.getInstance();
549         // for (Iterator<Relation> i=getAllRelations().iterator(); i.hasNext();) {
550         // Relation link = i.next();
551         // if (!link.getClass().equals(UsesRelation.class)) continue;
552         //
553         // Document used = (Document)link.getTo();
554         // if (!used.isVersioned()) continue;
555         // TODO: Update the uses relation
556         // }
557         // this.promote();
558         // this.lasdate = current.getTime(); // Today
559         // Database.getSession().update(this);
560         //
561         // TODO: Promote documents using this one
562         // }
563
564         /**
565          * Checks if documents of this type are result of a study. A document is the result of a study when it is the result of the last step of
566          * the study.
567          * 
568          * @param aType
569          *            the document type
570          * @return true if documents of this type are result of a study.
571          * @see #isStepResult()
572          * @see #isResultOf(org.splat.service.technical.ProjectSettingsServiceImpl.Step)
573          */
574         public boolean isStudyResult(DocumentType aType) {
575                 // -------------------------------
576                 List<ProjectSettingsService.Step> step = getProjectSettings()
577                                 .getAllSteps();
578                 ProjectSettingsService.Step lastep = step.get(step.size() - 1);
579                 return (aType.isResultOf(lastep));
580         }
581
582         /**
583          * Get document by its path.
584          * @param path the document path
585          * @return the document if found or null
586          */
587         @Transactional(readOnly=true)
588         public Document getDocumentByPath(String path) {
589                 String[] parse = path.split("'");
590
591                 path = parse[0];
592                 for (int i = 1; i < parse.length; i++) {
593                         path = path + "''" + parse[i];
594                 }
595                 Criterion aCondition = Restrictions.eq("path", path);
596                 return getDocumentDAO().findByCriteria(aCondition);
597         }
598
599         /**
600          * Get the studyService.
601          * 
602          * @return the studyService
603          */
604         public StudyService getStudyService() {
605                 return _studyService;
606         }
607
608         /**
609          * Set the studyService.
610          * 
611          * @param studyService
612          *            the studyService to set
613          */
614         public void setStudyService(StudyService studyService) {
615                 _studyService = studyService;
616         }
617
618         /**
619          * Get project settings.
620          * 
621          * @return Project settings service
622          */
623         private ProjectSettingsService getProjectSettings() {
624                 return _projectSettingsService;
625         }
626
627         /**
628          * Set project settings service.
629          * 
630          * @param projectSettingsService
631          *            project settings service
632          */
633         public void setProjectSettings(ProjectSettingsService projectSettingsService) {
634                 _projectSettingsService = projectSettingsService;
635         }
636
637         /**
638          * Get the documentDAO.
639          * 
640          * @return the documentDAO
641          */
642         public DocumentDAO getDocumentDAO() {
643                 return _documentDAO;
644         }
645
646         /**
647          * Set the documentDAO.
648          * 
649          * @param documentDAO
650          *            the documentDAO to set
651          */
652         public void setDocumentDAO(DocumentDAO documentDAO) {
653                 _documentDAO = documentDAO;
654         }
655
656         /**
657          * Get the repositoryService.
658          * 
659          * @return the repositoryService
660          */
661         public RepositoryService getRepositoryService() {
662                 return _repositoryService;
663         }
664
665         /**
666          * Set the repositoryService.
667          * 
668          * @param repositoryService
669          *            the repositoryService to set
670          */
671         public void setRepositoryService(RepositoryService repositoryService) {
672                 _repositoryService = repositoryService;
673         }
674
675         /**
676          * Get the documentTypeDAO.
677          * 
678          * @return the documentTypeDAO
679          */
680         public DocumentTypeDAO getDocumentTypeDAO() {
681                 return _documentTypeDAO;
682         }
683
684         /**
685          * Set the documentTypeDAO.
686          * 
687          * @param documentTypeDAO
688          *            the documentTypeDAO to set
689          */
690         public void setDocumentTypeDAO(DocumentTypeDAO documentTypeDAO) {
691                 _documentTypeDAO = documentTypeDAO;
692         }
693
694         /**
695          * Get the fileDAO.
696          * 
697          * @return the fileDAO
698          */
699         public FileDAO getFileDAO() {
700                 return _fileDAO;
701         }
702
703         /**
704          * Set the fileDAO.
705          * 
706          * @param fileDAO
707          *            the fileDAO to set
708          */
709         public void setFileDAO(FileDAO fileDAO) {
710                 _fileDAO = fileDAO;
711         }
712
713         /**
714          * Get the studyDAO.
715          * @return the studyDAO
716          */
717         public StudyDAO getStudyDAO() {
718                 return _studyDAO;
719         }
720
721         /**
722          * Set the studyDAO.
723          * @param studyDAO the studyDAO to set
724          */
725         public void setStudyDAO(StudyDAO studyDAO) {
726                 _studyDAO = studyDAO;
727         }
728
729 }