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.setLastLocalIndex(getStudyDAO().get(owner.getIndex())
130 .getLastLocalIndex());
132 SimpleDateFormat tostring = new SimpleDateFormat("yyyy"); // RKV: NOPMD: TODO: Use locale here?
133 String year = tostring.format(owner.getDate());
134 String filename = generateEncodedName(aDoc, owner);
135 String path = owner.getReference();
136 ProjectSettingsService.Step step = getProjectSettings().getStep(
138 aDoc.setDid(new StringBuffer(path).append(".%").append(
139 Document.suformat).toString()); // Document reference
140 StringBuffer pathBuf = new StringBuffer(year).append("/").append(path)
141 .append("/").append(step.getPath())
142 // File path relative to the repository vault
144 if (!FileNaming.asis.equals(getProjectSettings().getFileNamingScheme())) {
145 // Use format as extension if file naming strategy is not "asis".
146 pathBuf.append(".").append(aDoc.getFile().getFormat()); // File name and extension
148 aDoc.getFile().changePath(pathBuf.toString());
149 owner = getStudyDAO().merge(owner);
153 * Generate encoded document file name according to the naming scheme from project settings.
159 * @return document reference name
161 private String generateEncodedName(final Document aDoc, final Study scope) {
162 StringBuffer encoding = new StringBuffer();
163 FileNaming scheme = getProjectSettings().getFileNamingScheme();
164 DecimalFormat tostring = new DecimalFormat(Document.suformat);
166 int number = getStudyService().generateLocalIndex(scope);
168 if (scheme == FileNaming.encoded) {
169 encoding.append(scope.getReference()).append("_").append(
170 tostring.format(number));
171 } else { // title and (temporarily) asis
172 encoding.append(tostring.format(number)).append("_").append(
173 aDoc.getFile().getName());
175 return encoding.toString();
179 * Get encoded root name for a document file.
187 private String getEncodedRootName(final Document aDoc, final Study scope) {
188 FileNaming scheme = getProjectSettings().getFileNamingScheme();
190 if (scheme == FileNaming.encoded) {
191 res = scope.getReference();
192 } else if (scheme == FileNaming.asis) {
193 res = aDoc.getFile().getName();
195 res = aDoc.getTitle();
203 * @see org.splat.service.DocumentService#initialize(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
206 public void initialize(final Document aDoc, final Properties dprop)
207 throws MissedPropertyException, InvalidPropertyException,
208 NotApplicableException {
209 if (!aDoc.isUndefined()) {
210 throw new NotApplicableException(
211 "Cannot initialize an existing Document");
213 if (dprop.getName() == null) {
214 throw new MissedPropertyException("name");
216 if (dprop.getName().length() == 0) {
217 throw new InvalidPropertyException("name");
219 if (dprop.getOwner() == null) {
220 throw new MissedPropertyException("owner");
222 // if (dprop.owner instanceof Study && !ProjectSettings.getStep(step).appliesTo(Study.class)) {
223 // throw new InvalidPropertyException("step");
225 aDoc.setTitle(dprop.getName());
226 aDoc.getFile().changePath(
227 aDoc.getFile().getRelativePath().replace("%n",
228 getEncodedRootName(aDoc, (Study) dprop.getOwner())));
229 if (aDoc.getHistory() == -1) {
232 if (dprop.getDate() == null) {
233 Calendar current = Calendar.getInstance();
234 aDoc.setLastModificationDate(current.getTime()); // Today
236 aDoc.setLastModificationDate(dprop.getDate());
238 getDocumentDAO().update(aDoc);
244 * @see org.splat.service.DocumentService#getSaveDirectory(org.splat.dal.bo.som.Document)
246 public java.io.File getSaveDirectory(final Document aDoc) {
247 String mypath = getRepositoryService().getRepositoryVaultPath()
248 + aDoc.getSourceFile().getRelativePath();
249 String[] table = mypath.split("/");
251 // Cutting the filename
252 StringBuffer path = new StringBuffer(table[0]);
253 for (int i = 1; i < table.length - 1; i++) {
254 path = path.append("/").append(table[i]);
256 return new java.io.File(path.append("/").toString());
260 * Extract title and reference properties from the given file.
264 * @return the extracted properties
266 public Properties extractProperties(final java.io.File file) {
267 Properties fprop = new Properties();
268 Reader tool = Toolbox.getReader(file);
272 value = tool.extractProperty("title");
274 fprop.setName(value);
277 value = tool.extractProperty("reference");
279 fprop.setReference(value);
281 } catch (Exception e) {
282 LOG.debug(e.getMessage(), e);
289 * Create "Converts" relation for the given document and the given format.
295 * @return the created "Converts" relation
298 public ConvertsRelation attach(final Document aDoc, final String format) {
299 return attach(aDoc, format, null);
303 * Create "Converts" relation for the given document, format and description.
310 * the description of the relation
311 * @return the created "Converts" relation
314 public ConvertsRelation attach(final Document aDoc, final String format,
315 final String description) {
316 String path = aDoc.getRelativePath();
317 File export = new File(path + "." + format);
318 ConvertsRelation attach = new ConvertsRelation(aDoc, export,
321 getFileDAO().create(export);
322 getRelationDAO().create(attach);
324 aDoc.getAllRelations().add(attach);
325 getDocumentDAO().merge(aDoc);
331 * Build a reference (document id) for the given document in the given study basing on the given original document.
334 * the document to set set the new reference to
338 * the original document
339 * @return true if the new reference is set
341 public boolean buildReferenceFrom(final Document aDoc,
342 final ProjectElement scope, final Document lineage) {
343 boolean res = (aDoc.getProgressState() == ProgressState.inWORK);
345 Study owner = scope.getOwnerStudy();
346 Scenario context = null;
347 if (scope instanceof Scenario) {
348 context = ((Scenario) scope);
350 aDoc.setDid(lineage.getDid());
352 && (lineage.isVersioned() || owner.shares(lineage))) {
353 aDoc.setVersion(new Revision(aDoc.getVersion()).setBranch(
354 context.getReference()).toString());
361 * Build a reference (document id) for the given document in the given study.
364 * the document to set set the new reference to
367 * @return true if the new reference is set
369 public boolean buildReferenceFrom(final Document aDoc, final Study scope) {
370 if (aDoc.getProgressState() != ProgressState.inWORK
371 && aDoc.getProgressState() != ProgressState.EXTERN) {
374 DecimalFormat tostring = new DecimalFormat(Document.suformat);
376 aDoc.setDid(aDoc.getDid().replace("%" + Document.suformat,
377 tostring.format(scope.getLastLocalIndex())));
385 * the document to demote
386 * @return true if demoting succeeded
389 public boolean demote(final Document aDoc) {
390 ValidationStep torem;
392 if (aDoc.getProgressState() == ProgressState.inCHECK) {
393 aDoc.setProgressState(ProgressState.inDRAFT);
394 torem = ValidationStep.REVIEW;
395 // This operation must not change the version number of documents.
396 // Consequently, inDRAFT documents may have a minor version number equal to zero.
397 } else if (aDoc.getProgressState() == ProgressState.inDRAFT) {
398 aDoc.setProgressState(ProgressState.inWORK);
399 torem = ValidationStep.PROMOTION;
403 for (Iterator<Relation> i = aDoc.getAllRelations().iterator(); i
405 Relation link = i.next();
406 if (!(link instanceof StampRelation)) {
409 if (((StampRelation) link).getStampType() != torem) {
415 getDocumentDAO().update(aDoc);
420 * Promote a document.
423 * the document to promote
425 * the timestamp of the promotion
426 * @return true if promotion succeeded
429 public boolean promote(final Document aDoc, final Timestamp stamp) {
430 ProgressState newstate = null;
432 if (aDoc.getProgressState() == ProgressState.inWORK) {
433 newstate = ProgressState.inDRAFT; // Promotion to being reviewed
434 } else if (aDoc.getProgressState() == ProgressState.inDRAFT) {
435 newstate = ProgressState.inCHECK; // Promotion to approval
436 Revision myvers = new Revision(aDoc.getVersion());
437 if (myvers.isMinor()) {
438 aDoc.setVersion(myvers.incrementAs(newstate).toString());
439 // TODO: If my physical file is programatically editable, update its (property) version number
440 // ISSUE: What about attached files such as PDF if exist, should we remove them ?
442 } else if (aDoc.getProgressState() == ProgressState.inCHECK) {
443 newstate = ProgressState.APPROVED;
445 aDoc.setProgressState(newstate);
447 // RKV: aDoc.addRelation(stamp.getContext());
448 aDoc.getAllRelations().add(stamp.getContext());
450 getDocumentDAO().update(aDoc);
455 * Increments the reference count of this document following its publication in a Study step.
458 * the document to hold
462 public void hold(final Document aDoc) {
463 aDoc.setCountag(aDoc.getCountag() + 1);
464 if (aDoc.isSaved()) {
465 getDocumentDAO().update(aDoc);
470 * Decrements the reference count of this document following the removal of a Publication from a Study step.
473 * the document to release
477 public void release(final Document aDoc) {
478 aDoc.setCountag(aDoc.getCountag() - 1);
479 if (aDoc.isSaved()) {
480 getDocumentDAO().merge(aDoc);
488 * the document to rename
490 * the new document title
491 * @throws InvalidPropertyException
492 * if the new title is empty
495 public void rename(final Document aDoc, final String title)
496 throws InvalidPropertyException {
497 if (title.length() == 0) {
498 throw new InvalidPropertyException("name");
501 Calendar current = Calendar.getInstance();
502 aDoc.setTitle(title);
503 aDoc.setLastModificationDate(current.getTime()); // Today
504 getDocumentDAO().update(aDoc);
508 * Update a version of the given document.
515 public void updateAs(final Document aDoc, final Revision newvers) {
516 aDoc.setVersion(newvers.setBranch(aDoc.getVersion()).toString()); // Branch names are propagated by the versionning
517 ProgressState newstate = ProgressState.inCHECK;
518 if (newvers.isMinor()) {
519 newstate = ProgressState.inWORK;
521 aDoc.setProgressState(null); // Just to tell updateAs(state) to not increment the version number
522 updateAs(aDoc, newstate);
526 * Update a state of the given document.
534 public void updateAs(final Document aDoc, final ProgressState state) {
535 Document previous = null;
537 // Set of version number
538 if (state == ProgressState.EXTERN) {
539 if (aDoc.getProgressState() != ProgressState.EXTERN) {
540 aDoc.setVersion(null); // Strange use-case...
543 Revision myvers = new Revision(aDoc.getVersion());
544 if (!myvers.isNull()) { // Versionning context
545 previous = aDoc.getPreviousVersion();
547 if (aDoc.getProgressState() != null) {
548 myvers.incrementAs(state); // Incrementation if the reversion number is not imposed
550 aDoc.setVersion(myvers.toString());
552 // Update this document and the previous version, if exist
553 if (previous != null) {
554 previous.setHistory(previous.getHistory() + 1);
555 getDocumentDAO().merge(previous);
557 aDoc.setProgressState(state);
558 // RKV: getDocumentDAO().update(aDoc);
561 // protected void upgrade () {
562 // -------------------------
563 // if (this.state != ProgressState.inWORK) return;
565 // Calendar current = Calendar.getInstance();
566 // for (Iterator<Relation> i=getAllRelations().iterator(); i.hasNext();) {
567 // Relation link = i.next();
568 // if (!link.getClass().equals(UsesRelation.class)) continue;
570 // Document used = (Document)link.getTo();
571 // if (!used.isVersioned()) continue;
572 // TODO: Update the uses relation
575 // this.lasdate = current.getTime(); // Today
576 // Database.getSession().update(this);
578 // TODO: Promote documents using this one
582 * 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
587 * @return true if documents of this type are result of a study.
588 * @see #isStepResult()
589 * @see #isResultOf(org.splat.service.technical.ProjectSettingsServiceImpl.Step)
591 public boolean isStudyResult(final DocumentType aType) {
592 List<ProjectSettingsService.Step> step = getProjectSettings()
594 ProjectSettingsService.Step lastep = step.get(step.size() - 1);
595 return (aType.isResultOf(lastep));
599 * Get document by its path.
603 * @return the document if found or null
605 @Transactional(readOnly = true)
606 public Document getDocumentByPath(String path) {
607 String[] parse = path.split("'");
610 for (int i = 1; i < parse.length; i++) {
611 path = path + "''" + parse[i];
613 Criterion aCondition = Restrictions.eq("path", path);
614 return getDocumentDAO().findByCriteria(aCondition);
618 * Get the studyService.
620 * @return the studyService
622 public StudyService getStudyService() {
623 return _studyService;
627 * Set the studyService.
629 * @param studyService
630 * the studyService to set
632 public void setStudyService(final StudyService studyService) {
633 _studyService = studyService;
637 * Get project settings.
639 * @return Project settings service
641 private ProjectSettingsService getProjectSettings() {
642 return _projectSettings;
646 * Set project settings service.
648 * @param projectSettingsService
649 * project settings service
651 public void setProjectSettings(
652 final ProjectSettingsService projectSettingsService) {
653 _projectSettings = projectSettingsService;
657 * Get the documentDAO.
659 * @return the documentDAO
661 public DocumentDAO getDocumentDAO() {
666 * Set the documentDAO.
669 * the documentDAO to set
671 public void setDocumentDAO(final DocumentDAO documentDAO) {
672 _documentDAO = documentDAO;
676 * Get the repositoryService.
678 * @return the repositoryService
680 public RepositoryService getRepositoryService() {
681 return _repositoryService;
685 * Set the repositoryService.
687 * @param repositoryService
688 * the repositoryService to set
690 public void setRepositoryService(final RepositoryService repositoryService) {
691 _repositoryService = repositoryService;
695 * Get the documentTypeDAO.
697 * @return the documentTypeDAO
699 public DocumentTypeDAO getDocumentTypeDAO() {
700 return _documentTypeDAO;
704 * Set the documentTypeDAO.
706 * @param documentTypeDAO
707 * the documentTypeDAO to set
709 public void setDocumentTypeDAO(final DocumentTypeDAO documentTypeDAO) {
710 _documentTypeDAO = documentTypeDAO;
716 * @return the fileDAO
718 public FileDAO getFileDAO() {
728 public void setFileDAO(final FileDAO fileDAO) {
735 * @return the studyDAO
737 public StudyDAO getStudyDAO() {
745 * the studyDAO to set
747 public void setStudyDAO(final StudyDAO studyDAO) {
748 _studyDAO = studyDAO;
752 * Get the relationDAO.
754 * @return the relationDAO
756 public RelationDAO getRelationDAO() {
761 * Set the relationDAO.
764 * the relationDAO to set
766 public void setRelationDAO(final RelationDAO relationDAO) {
767 _relationDAO = relationDAO;