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.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;
51 * Document service implementation.
53 * @author <a href="mailto:roman.kozlov@opencascade.com">Roman Kozlov (RKV)</a>
56 public class DocumentServiceImpl implements DocumentService {
59 * The logger for the service.
61 public final static AppLogger LOG = AppLogger
62 .getLogger(DocumentServiceImpl.class);
65 * Injected study service.
67 private StudyService _studyService;
69 * Injected project settings service.
71 private ProjectSettingsService _projectSettings;
73 * Injected repository service.
75 private RepositoryService _repositoryService;
77 * Injected document DAO.
79 private DocumentDAO _documentDAO;
81 * Injected relation DAO.
83 private RelationDAO _relationDAO;
85 * Injected document type DAO.
87 private DocumentTypeDAO _documentTypeDAO;
91 private FileDAO _fileDAO;
95 private StudyDAO _studyDAO;
100 * @see org.splat.service.DocumentService#selectDocument(long)
102 @Transactional(readOnly = true)
103 public Document selectDocument(final long index) {
104 return getDocumentDAO().get(index);
110 * @see org.splat.service.DocumentService#selectDocument(java.lang.String, java.lang.String)
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);
122 * @see org.splat.service.DocumentService#generateDocumentId(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
125 public void generateDocumentId(final Document aDoc, final Properties dprop) {
126 Study owner = dprop.getOwner().getOwnerStudy();
128 // Synchronize the object with the current Hibernate session.
129 // owner = getStudyDAO().merge(owner);
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(
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
145 aDoc.getFile().changePath(path);
146 owner = getStudyDAO().merge(owner);
147 // getStudyDAO().update(owner);
151 * Generate encoded document file name according to the naming scheme from project settings.
157 * @return document reference name
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);
164 int number = getStudyService().generateLocalIndex(scope);
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("_").append(tostring.format(number)).append(
171 aDoc.getFile().getName());
173 return encoding.toString();
177 * Get encoded root name for a document file.
185 private String getEncodedRootName(final Document aDoc, final Study scope) {
186 FileNaming scheme = getProjectSettings().getFileNamingScheme();
188 if (scheme == FileNaming.encoded) {
189 return scope.getReference();
191 return aDoc.getTitle();
198 * @see org.splat.service.DocumentService#initialize(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
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");
208 if (dprop.getName() == null) {
209 throw new MissedPropertyException("name");
211 if (dprop.getName().length() == 0) {
212 throw new InvalidPropertyException("name");
214 if (dprop.getOwner() == null) {
215 throw new MissedPropertyException("owner");
217 // if (dprop.owner instanceof Study && !ProjectSettings.getStep(step).appliesTo(Study.class)) {
218 // throw new InvalidPropertyException("step");
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) {
227 if (dprop.getDate() == null) {
228 Calendar current = Calendar.getInstance();
229 aDoc.setLastModificationDate(current.getTime()); // Today
231 aDoc.setLastModificationDate(dprop.getDate());
233 getDocumentDAO().update(aDoc);
239 * @see org.splat.service.DocumentService#getSaveDirectory(org.splat.dal.bo.som.Document)
241 public java.io.File getSaveDirectory(final Document aDoc) {
242 String mypath = getRepositoryService().getRepositoryVaultPath()
243 + aDoc.getSourceFile().getRelativePath();
244 String[] table = mypath.split("/");
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]);
251 return new java.io.File(path.append("/").toString());
255 * Extract title and reference properties from the given file.
259 * @return the extracted properties
261 public Properties extractProperties(final java.io.File file) {
262 Properties fprop = new Properties();
263 Reader tool = Toolbox.getReader(file);
267 value = tool.extractProperty("title");
269 fprop.setName(value);
272 value = tool.extractProperty("reference");
274 fprop.setReference(value);
276 } catch (Exception e) {
277 LOG.debug(e.getMessage(), e);
284 * Create "Converts" relation for the given document and the given format.
290 * @return the created "Converts" relation
293 public ConvertsRelation attach(final Document aDoc, final String format) {
294 return attach(aDoc, format, null);
298 * Create "Converts" relation for the given document, format and description.
305 * the description of the relation
306 * @return the created "Converts" relation
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,
316 getFileDAO().create(export);
317 getRelationDAO().create(attach);
319 aDoc.getAllRelations().add(attach);
320 getDocumentDAO().merge(aDoc);
326 * Build a reference (document id) for the given document in the given study basing on the given original document.
329 * the document to set set the new reference to
333 * the original document
334 * @return true if the new reference is set
336 public boolean buildReferenceFrom(final Document aDoc,
337 final ProjectElement scope, final Document lineage) {
338 boolean res = (aDoc.getProgressState() == ProgressState.inWORK);
340 Study owner = scope.getOwnerStudy();
341 Scenario context = null;
342 if (scope instanceof Scenario) {
343 context = ((Scenario) scope);
345 aDoc.setDid(lineage.getDid());
347 && (lineage.isVersioned() || owner.shares(lineage))) {
348 aDoc.setVersion(new Revision(aDoc.getVersion()).setBranch(
349 context.getReference()).toString());
356 * Build a reference (document id) for the given document in the given study.
359 * the document to set set the new reference to
362 * @return true if the new reference is set
364 public boolean buildReferenceFrom(final Document aDoc, final Study scope) {
365 if (aDoc.getProgressState() != ProgressState.inWORK
366 && aDoc.getProgressState() != ProgressState.EXTERN) {
369 DecimalFormat tostring = new DecimalFormat(Document.suformat);
371 aDoc.setDid(aDoc.getDid().replace("%" + Document.suformat,
372 tostring.format(scope.getLastLocalIndex())));
380 * the document to demote
381 * @return true if demoting succeeded
384 public boolean demote(final Document aDoc) {
385 ValidationStep torem;
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;
398 for (Iterator<Relation> i = aDoc.getAllRelations().iterator(); i
400 Relation link = i.next();
401 if (!(link instanceof StampRelation)) {
404 if (((StampRelation) link).getStampType() != torem) {
410 getDocumentDAO().update(aDoc);
415 * Promote a document.
418 * the document to promote
420 * the timestamp of the promotion
421 * @return true if promotion succeeded
424 public boolean promote(final Document aDoc, final Timestamp stamp) {
425 ProgressState newstate = null;
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 ?
437 } else if (aDoc.getProgressState() == ProgressState.inCHECK) {
438 newstate = ProgressState.APPROVED;
440 aDoc.setProgressState(newstate);
442 // RKV: aDoc.addRelation(stamp.getContext());
443 aDoc.getAllRelations().add(stamp.getContext());
445 getDocumentDAO().update(aDoc);
450 * Increments the reference count of this document following its publication in a Study step.
453 * the document to hold
457 public void hold(final Document aDoc) {
458 aDoc.setCountag(aDoc.getCountag() + 1);
459 if (aDoc.isSaved()) {
460 getDocumentDAO().update(aDoc);
465 * Decrements the reference count of this document following the removal of a Publication from a Study step.
468 * the document to release
472 public void release(final Document aDoc) {
473 aDoc.setCountag(aDoc.getCountag() - 1);
474 if (aDoc.isSaved()) {
475 getDocumentDAO().merge(aDoc);
483 * the document to rename
485 * the new document title
486 * @throws InvalidPropertyException
487 * if the new title is empty
490 public void rename(final Document aDoc, final String title)
491 throws InvalidPropertyException {
492 if (title.length() == 0) {
493 throw new InvalidPropertyException("name");
496 Calendar current = Calendar.getInstance();
497 aDoc.setTitle(title);
498 aDoc.setLastModificationDate(current.getTime()); // Today
499 getDocumentDAO().update(aDoc);
503 * Update a version of the given document.
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;
516 aDoc.setProgressState(null); // Just to tell updateAs(state) to not increment the version number
517 updateAs(aDoc, newstate);
521 * Update a state of the given document.
529 public void updateAs(final Document aDoc, final ProgressState state) {
530 Document previous = null;
532 // Set of version number
533 if (state == ProgressState.EXTERN) {
534 if (aDoc.getProgressState() != ProgressState.EXTERN) {
535 aDoc.setVersion(null); // Strange use-case...
538 Revision myvers = new Revision(aDoc.getVersion());
539 if (!myvers.isNull()) { // Versionning context
540 previous = aDoc.getPreviousVersion();
542 if (aDoc.getProgressState() != null) {
543 myvers.incrementAs(state); // Incrementation if the reversion number is not imposed
545 aDoc.setVersion(myvers.toString());
547 // Update this document and the previous version, if exist
548 if (previous != null) {
549 previous.setHistory(previous.getHistory() + 1);
550 getDocumentDAO().merge(previous);
552 aDoc.setProgressState(state);
553 // RKV: getDocumentDAO().update(aDoc);
556 // protected void upgrade () {
557 // -------------------------
558 // if (this.state != ProgressState.inWORK) return;
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;
565 // Document used = (Document)link.getTo();
566 // if (!used.isVersioned()) continue;
567 // TODO: Update the uses relation
570 // this.lasdate = current.getTime(); // Today
571 // Database.getSession().update(this);
573 // TODO: Promote documents using this one
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
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)
586 public boolean isStudyResult(final DocumentType aType) {
587 List<ProjectSettingsService.Step> step = getProjectSettings()
589 ProjectSettingsService.Step lastep = step.get(step.size() - 1);
590 return (aType.isResultOf(lastep));
594 * Get document by its path.
598 * @return the document if found or null
600 @Transactional(readOnly = true)
601 public Document getDocumentByPath(String path) {
602 String[] parse = path.split("'");
605 for (int i = 1; i < parse.length; i++) {
606 path = path + "''" + parse[i];
608 Criterion aCondition = Restrictions.eq("path", path);
609 return getDocumentDAO().findByCriteria(aCondition);
613 * Get the studyService.
615 * @return the studyService
617 public StudyService getStudyService() {
618 return _studyService;
622 * Set the studyService.
624 * @param studyService
625 * the studyService to set
627 public void setStudyService(final StudyService studyService) {
628 _studyService = studyService;
632 * Get project settings.
634 * @return Project settings service
636 private ProjectSettingsService getProjectSettings() {
637 return _projectSettings;
641 * Set project settings service.
643 * @param projectSettingsService
644 * project settings service
646 public void setProjectSettings(
647 final ProjectSettingsService projectSettingsService) {
648 _projectSettings = projectSettingsService;
652 * Get the documentDAO.
654 * @return the documentDAO
656 public DocumentDAO getDocumentDAO() {
661 * Set the documentDAO.
664 * the documentDAO to set
666 public void setDocumentDAO(final DocumentDAO documentDAO) {
667 _documentDAO = documentDAO;
671 * Get the repositoryService.
673 * @return the repositoryService
675 public RepositoryService getRepositoryService() {
676 return _repositoryService;
680 * Set the repositoryService.
682 * @param repositoryService
683 * the repositoryService to set
685 public void setRepositoryService(final RepositoryService repositoryService) {
686 _repositoryService = repositoryService;
690 * Get the documentTypeDAO.
692 * @return the documentTypeDAO
694 public DocumentTypeDAO getDocumentTypeDAO() {
695 return _documentTypeDAO;
699 * Set the documentTypeDAO.
701 * @param documentTypeDAO
702 * the documentTypeDAO to set
704 public void setDocumentTypeDAO(final DocumentTypeDAO documentTypeDAO) {
705 _documentTypeDAO = documentTypeDAO;
711 * @return the fileDAO
713 public FileDAO getFileDAO() {
723 public void setFileDAO(final FileDAO fileDAO) {
730 * @return the studyDAO
732 public StudyDAO getStudyDAO() {
740 * the studyDAO to set
742 public void setStudyDAO(final StudyDAO studyDAO) {
743 _studyDAO = studyDAO;
747 * Get the relationDAO.
749 * @return the relationDAO
751 public RelationDAO getRelationDAO() {
756 * Set the relationDAO.
759 * the relationDAO to set
761 public void setRelationDAO(final RelationDAO relationDAO) {
762 _relationDAO = relationDAO;