1 /*****************************************************************************
5 * Creation date 06.10.2012
8 *****************************************************************************/
10 package org.splat.service;
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;
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.RepositoryService;
45 import org.splat.service.technical.ProjectSettingsServiceImpl.FileNaming;
46 import org.splat.som.Revision;
47 import org.springframework.transaction.annotation.Transactional;
50 * Document service implementation.
52 * @author <a href="mailto:roman.kozlov@opencascade.com">Roman Kozlov (RKV)</a>
55 public class DocumentServiceImpl implements DocumentService {
58 * Injected study service.
60 private StudyService _studyService;
62 * Injected project settings service.
64 private ProjectSettingsService _projectSettingsService;
66 * Injected repository service.
68 private RepositoryService _repositoryService;
70 * Injected document DAO.
72 private DocumentDAO _documentDAO;
74 * Injected document type DAO.
76 private DocumentTypeDAO _documentTypeDAO;
80 private FileDAO _fileDAO;
84 private StudyDAO _studyDAO;
89 * @see org.splat.service.DocumentService#selectDocument(long)
91 @Transactional(readOnly = true)
92 public Document selectDocument(final long index) {
93 return getDocumentDAO().get(index);
99 * @see org.splat.service.DocumentService#selectDocument(java.lang.String, java.lang.String)
101 @Transactional(readOnly = true)
102 public Document selectDocument(final String refid, final String version) {
103 Criterion aCondition = Restrictions.and(Restrictions.eq("did", refid),
104 Restrictions.eq("version", version));
105 return getDocumentDAO().findByCriteria(aCondition);
111 * @see org.splat.service.DocumentService#generateDocumentId(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
114 public void generateDocumentId(final Document aDoc, final Properties dprop) {
116 if (dprop.getOwner() instanceof Study) {
117 owner = (Study) dprop.getOwner();
119 owner = ((Scenario) dprop.getOwner()).getOwnerStudy();
122 // Synchronize the object with the current Hibernate session.
123 owner = getStudyDAO().get(owner.getIndex());
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 = getProjectSettings().getStep(
131 aDoc.setDid(new StringBuffer(path).append(".%").append(
132 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
139 aDoc.getFile().changePath(path);
143 * Generate encoded document file name according to the naming scheme from project settings.
149 * @return document reference name
151 private String generateEncodedName(final Document aDoc, final Study scope) {
152 StringBuffer encoding = new StringBuffer();
153 FileNaming scheme = getProjectSettings().getFileNamingScheme();
154 DecimalFormat tostring = new DecimalFormat(Document.suformat);
156 int number = getStudyService().generateLocalIndex(scope);
158 if (scheme == FileNaming.encoded) {
159 encoding.append(scope.getReference()).append(".").append(
160 tostring.format(number));
161 } else { // title and (temporarily) asis
162 encoding.append(aDoc.getTitle()).append(".").append(
163 tostring.format(number));
165 return encoding.toString();
169 * Get encoded root name for a document file.
177 private String getEncodedRootName(final Document aDoc, final Study scope) {
178 FileNaming scheme = getProjectSettings().getFileNamingScheme();
180 if (scheme == FileNaming.encoded) {
181 return scope.getReference();
183 return aDoc.getTitle();
190 * @see org.splat.service.DocumentService#initialize(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
193 public void initialize(final Document aDoc, final Properties dprop)
194 throws MissedPropertyException, InvalidPropertyException,
195 NotApplicableException {
196 if (!aDoc.isUndefined()) {
197 throw new NotApplicableException(
198 "Cannot initialize an existing Document");
200 if (dprop.getName() == null) {
201 throw new MissedPropertyException("name");
203 if (dprop.getName().length() == 0) {
204 throw new InvalidPropertyException("name");
206 if (dprop.getOwner() == null) {
207 throw new MissedPropertyException("owner");
209 // if (dprop.owner instanceof Study && !ProjectSettings.getStep(step).appliesTo(Study.class)) {
210 // throw new InvalidPropertyException("step");
212 aDoc.setTitle(dprop.getName());
213 aDoc.getFile().changePath(
214 aDoc.getFile().getRelativePath().replace("%n",
215 getEncodedRootName(aDoc, (Study) dprop.getOwner())));
216 if (aDoc.getHistory() == -1) {
219 if (dprop.getDate() == null) {
220 Calendar current = Calendar.getInstance();
221 aDoc.setLastModificationDate(current.getTime()); // Today
223 aDoc.setLastModificationDate(dprop.getDate());
225 getDocumentDAO().update(aDoc);
231 * @see org.splat.service.DocumentService#getSaveDirectory(org.splat.dal.bo.som.Document)
233 public java.io.File getSaveDirectory(final Document aDoc) {
234 String mypath = getRepositoryService().getRepositoryVaultPath()
235 + aDoc.getSourceFile().getRelativePath();
236 String[] table = mypath.split("/");
238 // Cutting the filename
239 StringBuffer path = new StringBuffer(table[0]);
240 for (int i = 1; i < table.length - 1; i++) {
241 path = path.append("/").append(table[i]);
243 return new java.io.File(path.append("/").toString());
247 * Extract title and reference properties from the given file.
251 * @return the extracted properties
253 public Properties extractProperties(final java.io.File file) {
254 Properties fprop = new Properties();
255 Reader tool = Toolbox.getReader(file);
259 value = tool.extractProperty("title");
261 fprop.setName(value);
264 value = tool.extractProperty("reference");
266 fprop.setReference(value);
268 } catch (Exception e) {
275 * Create "Converts" relation for the given document and the given format.
281 * @return the created "Converts" relation
283 public ConvertsRelation attach(final Document aDoc, final String format) {
284 return attach(aDoc, format, null);
288 * Create "Converts" relation for the given document, format and description.
295 * the description of the relation
296 * @return the created "Converts" relation
299 public ConvertsRelation attach(final Document aDoc, final String format,
300 final String description) {
301 String path = aDoc.getRelativePath();
302 File export = new File(path + "." + format);
303 ConvertsRelation attach = new ConvertsRelation(aDoc, export,
306 // RKV session.save(export);
307 // RKV session.save(attach);
309 getFileDAO().create(export);
311 // RKV aDoc.addRelation(attach); // Updates this
312 aDoc.getAllRelations().add(attach); // Updates this //RKV
318 * Build a reference (document id) for the given document in the given study basing on the given original document.
321 * the document to set set the new reference to
325 * the original document
326 * @return true if the new reference is set
328 public boolean buildReferenceFrom(final Document aDoc, final ProjectElement scope,
329 final Document lineage) {
330 if (aDoc.getProgressState() != ProgressState.inWORK) {
334 Scenario context = null;
335 if (scope instanceof Study) {
336 owner = (Study) scope;
338 context = ((Scenario) scope);
339 owner = context.getOwnerStudy();
341 aDoc.setDid(lineage.getDid());
342 if (context != null && (lineage.isVersioned() || owner.shares(lineage))) {
343 aDoc.setVersion(new Revision(aDoc.getVersion()).setBranch(
344 context.getReference()).toString());
350 * Build a reference (document id) for the given document in the given study.
353 * the document to set set the new reference to
356 * @return true if the new reference is set
358 public boolean buildReferenceFrom(final Document aDoc, final Study scope) {
359 if (aDoc.getProgressState() != ProgressState.inWORK
360 && aDoc.getProgressState() != ProgressState.EXTERN) {
363 DecimalFormat tostring = new DecimalFormat(Document.suformat);
365 aDoc.setDid(aDoc.getDid().replace("%" + Document.suformat,
366 tostring.format(scope.getLastLocalIndex())));
374 * the document to demote
375 * @return true if demoting succeeded
378 public boolean demote(final Document aDoc) {
379 ValidationStep torem;
381 if (aDoc.getProgressState() == ProgressState.inCHECK) {
382 aDoc.setProgressState(ProgressState.inDRAFT);
383 torem = ValidationStep.REVIEW;
384 // This operation must not change the version number of documents.
385 // Consequently, inDRAFT documents may have a minor version number equal to zero.
386 } else if (aDoc.getProgressState() == ProgressState.inDRAFT) {
387 aDoc.setProgressState(ProgressState.inWORK);
388 torem = ValidationStep.PROMOTION;
392 for (Iterator<Relation> i = aDoc.getAllRelations().iterator(); i
394 Relation link = i.next();
395 if (!(link instanceof StampRelation)) {
398 if (((StampRelation) link).getStampType() != torem) {
404 getDocumentDAO().update(aDoc);
409 * Promote a document.
412 * the document to promote
414 * the timestamp of the promotion
415 * @return true if promotion succeeded
418 public boolean promote(final Document aDoc, final Timestamp stamp) {
419 ProgressState newstate = null;
421 if (aDoc.getProgressState() == ProgressState.inWORK) {
422 newstate = ProgressState.inDRAFT; // Promotion to being reviewed
423 } else if (aDoc.getProgressState() == ProgressState.inDRAFT) {
424 newstate = ProgressState.inCHECK; // Promotion to approval
425 Revision myvers = new Revision(aDoc.getVersion());
426 if (myvers.isMinor()) {
427 aDoc.setVersion(myvers.incrementAs(newstate).toString());
428 // TODO: If my physical file is programatically editable, update its (property) version number
429 // ISSUE: What about attached files such as PDF if exist, should we remove them ?
431 } else if (aDoc.getProgressState() == ProgressState.inCHECK) {
432 newstate = ProgressState.APPROVED;
434 aDoc.setProgressState(newstate);
436 // RKV: aDoc.addRelation(stamp.getContext());
437 aDoc.getAllRelations().add(stamp.getContext());
439 getDocumentDAO().update(aDoc);
444 * Increments the reference count of this document following its publication in a Study step.
447 * the document to hold
451 public void hold(final Document aDoc) {
452 aDoc.setCountag(aDoc.getCountag() + 1);
453 if (aDoc.isSaved()) {
454 getDocumentDAO().update(aDoc);
459 * Decrements the reference count of this document following the removal of a Publication from a Study step.
462 * the document to release
466 public void release(final Document aDoc) {
467 aDoc.setCountag(aDoc.getCountag() - 1);
468 if (aDoc.isSaved()) {
469 getDocumentDAO().merge(aDoc);
477 * the document to rename
479 * the new document title
480 * @throws InvalidPropertyException
481 * if the new title is empty
484 public void rename(final Document aDoc, final String title)
485 throws InvalidPropertyException {
486 if (title.length() == 0) {
487 throw new InvalidPropertyException("name");
490 Calendar current = Calendar.getInstance();
491 aDoc.setTitle(title);
492 aDoc.setLastModificationDate(current.getTime()); // Today
493 getDocumentDAO().update(aDoc);
497 * Update a version of the given document.
504 public void updateAs(final Document aDoc, final Revision newvers) {
505 aDoc.setVersion(newvers.setBranch(aDoc.getVersion()).toString()); // Branch names are propagated by the versionning
506 ProgressState newstate = ProgressState.inCHECK;
507 if (newvers.isMinor()) {
508 newstate = ProgressState.inWORK;
510 aDoc.setProgressState(null); // Just to tell updateAs(state) to not increment the version number
511 updateAs(aDoc, newstate);
515 * Update a state of the given document.
523 public void updateAs(final Document aDoc, final ProgressState state) {
524 Document previous = null;
526 // Set of version number
527 if (state == ProgressState.EXTERN) {
528 if (aDoc.getProgressState() != ProgressState.EXTERN) {
529 aDoc.setVersion(null); // Strange use-case...
532 Revision myvers = new Revision(aDoc.getVersion());
533 if (!myvers.isNull()) { // Versionning context
534 for (Iterator<Relation> i = aDoc.getAllRelations().iterator(); i
536 Relation link = i.next();
537 if (!link.getClass().equals(VersionsRelation.class)) {
540 previous = (Document) link.getTo(); // Versioned document
544 if (aDoc.getProgressState() != null) {
545 myvers.incrementAs(state); // Incrementation if the reversion number is not imposed
547 aDoc.setVersion(myvers.toString());
549 // Update this document and the previous version, if exit
550 if (previous != null) {
551 previous.setHistory(previous.getHistory() + 1);
552 getDocumentDAO().merge(previous);
554 aDoc.setProgressState(state);
555 getDocumentDAO().update(aDoc);
558 // protected void upgrade () {
559 // -------------------------
560 // if (this.state != ProgressState.inWORK) return;
562 // Calendar current = Calendar.getInstance();
563 // for (Iterator<Relation> i=getAllRelations().iterator(); i.hasNext();) {
564 // Relation link = i.next();
565 // if (!link.getClass().equals(UsesRelation.class)) continue;
567 // Document used = (Document)link.getTo();
568 // if (!used.isVersioned()) continue;
569 // TODO: Update the uses relation
572 // this.lasdate = current.getTime(); // Today
573 // Database.getSession().update(this);
575 // TODO: Promote documents using this one
579 * 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
584 * @return true if documents of this type are result of a study.
585 * @see #isStepResult()
586 * @see #isResultOf(org.splat.service.technical.ProjectSettingsServiceImpl.Step)
588 public boolean isStudyResult(final DocumentType aType) {
589 // -------------------------------
590 List<ProjectSettingsService.Step> step = getProjectSettings()
592 ProjectSettingsService.Step lastep = step.get(step.size() - 1);
593 return (aType.isResultOf(lastep));
597 * Get document by its path.
601 * @return the document if found or null
603 @Transactional(readOnly = true)
604 public Document getDocumentByPath(String path) {
605 String[] parse = path.split("'");
608 for (int i = 1; i < parse.length; i++) {
609 path = path + "''" + parse[i];
611 Criterion aCondition = Restrictions.eq("path", path);
612 return getDocumentDAO().findByCriteria(aCondition);
616 * Get the studyService.
618 * @return the studyService
620 public StudyService getStudyService() {
621 return _studyService;
625 * Set the studyService.
627 * @param studyService
628 * the studyService to set
630 public void setStudyService(final StudyService studyService) {
631 _studyService = studyService;
635 * Get project settings.
637 * @return Project settings service
639 private ProjectSettingsService getProjectSettings() {
640 return _projectSettingsService;
644 * Set project settings service.
646 * @param projectSettingsService
647 * project settings service
649 public void setProjectSettings(final ProjectSettingsService projectSettingsService) {
650 _projectSettingsService = projectSettingsService;
654 * Get the documentDAO.
656 * @return the documentDAO
658 public DocumentDAO getDocumentDAO() {
663 * Set the documentDAO.
666 * the documentDAO to set
668 public void setDocumentDAO(final DocumentDAO documentDAO) {
669 _documentDAO = documentDAO;
673 * Get the repositoryService.
675 * @return the repositoryService
677 public RepositoryService getRepositoryService() {
678 return _repositoryService;
682 * Set the repositoryService.
684 * @param repositoryService
685 * the repositoryService to set
687 public void setRepositoryService(final RepositoryService repositoryService) {
688 _repositoryService = repositoryService;
692 * Get the documentTypeDAO.
694 * @return the documentTypeDAO
696 public DocumentTypeDAO getDocumentTypeDAO() {
697 return _documentTypeDAO;
701 * Set the documentTypeDAO.
703 * @param documentTypeDAO
704 * the documentTypeDAO to set
706 public void setDocumentTypeDAO(final DocumentTypeDAO documentTypeDAO) {
707 _documentTypeDAO = documentTypeDAO;
713 * @return the fileDAO
715 public FileDAO getFileDAO() {
725 public void setFileDAO(final FileDAO fileDAO) {
732 * @return the studyDAO
734 public StudyDAO getStudyDAO() {
742 * the studyDAO to set
744 public void setStudyDAO(final StudyDAO studyDAO) {
745 _studyDAO = studyDAO;