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