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