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.som.DocumentDAO;
34 import org.splat.dal.dao.som.DocumentTypeDAO;
35 import org.splat.dal.dao.som.FileDAO;
36 import org.splat.dal.dao.som.StudyDAO;
37 import org.splat.kernel.InvalidPropertyException;
38 import org.splat.kernel.MissedPropertyException;
39 import org.splat.kernel.NotApplicableException;
40 import org.splat.log.AppLogger;
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 * The logger for the service.
60 public final static AppLogger LOG = AppLogger
61 .getLogger(DocumentServiceImpl.class);
64 * Injected study service.
66 private StudyService _studyService;
68 * Injected project settings service.
70 private ProjectSettingsService _projectSettings;
72 * Injected repository service.
74 private RepositoryService _repositoryService;
76 * Injected document DAO.
78 private DocumentDAO _documentDAO;
80 * Injected document type DAO.
82 private DocumentTypeDAO _documentTypeDAO;
86 private FileDAO _fileDAO;
90 private StudyDAO _studyDAO;
95 * @see org.splat.service.DocumentService#selectDocument(long)
97 @Transactional(readOnly = true)
98 public Document selectDocument(final long index) {
99 return getDocumentDAO().get(index);
105 * @see org.splat.service.DocumentService#selectDocument(java.lang.String, java.lang.String)
107 @Transactional(readOnly = true)
108 public Document selectDocument(final String refid, final String version) {
109 Criterion aCondition = Restrictions.and(Restrictions.eq("did", refid),
110 Restrictions.eq("version", version));
111 return getDocumentDAO().findByCriteria(aCondition);
117 * @see org.splat.service.DocumentService#generateDocumentId(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
120 public void generateDocumentId(final Document aDoc, final Properties dprop) {
121 Study owner = dprop.getOwner().getOwnerStudy();
123 // Synchronize the object with the current Hibernate session.
124 //owner = getStudyDAO().merge(owner);
126 SimpleDateFormat tostring = new SimpleDateFormat("yyyy"); // RKV: NOPMD: TODO: Use locale here?
127 String year = tostring.format(owner.getDate());
128 String filename = generateEncodedName(aDoc, owner);
129 String path = owner.getReference();
130 ProjectSettingsService.Step step = getProjectSettings().getStep(
132 aDoc.setDid(new StringBuffer(path).append(".%").append(
133 Document.suformat).toString()); // Document reference
134 path = new StringBuffer(year).append("/").append(path).append("/")
135 .append(step.getPath())
136 // File path relative to the repository vault
137 .append(filename).append(".")
138 .append(aDoc.getFile().getFormat()) // File name and extension
140 aDoc.getFile().changePath(path);
141 // owner = getStudyDAO().merge(owner);
142 // getStudyDAO().update(owner);
146 * Generate encoded document file name according to the naming scheme from project settings.
152 * @return document reference name
154 private String generateEncodedName(final Document aDoc, final Study scope) {
155 StringBuffer encoding = new StringBuffer();
156 FileNaming scheme = getProjectSettings().getFileNamingScheme();
157 DecimalFormat tostring = new DecimalFormat(Document.suformat);
159 int number = getStudyService().generateLocalIndex(scope);
161 if (scheme == FileNaming.encoded) {
162 encoding.append(scope.getReference()).append("_").append(
163 tostring.format(number));
164 } else { // title and (temporarily) asis
165 encoding.append(aDoc.getTitle()).append("_").append(
166 tostring.format(number));
168 return encoding.toString();
172 * Get encoded root name for a document file.
180 private String getEncodedRootName(final Document aDoc, final Study scope) {
181 FileNaming scheme = getProjectSettings().getFileNamingScheme();
183 if (scheme == FileNaming.encoded) {
184 return scope.getReference();
186 return aDoc.getTitle();
193 * @see org.splat.service.DocumentService#initialize(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
196 public void initialize(final Document aDoc, final Properties dprop)
197 throws MissedPropertyException, InvalidPropertyException,
198 NotApplicableException {
199 if (!aDoc.isUndefined()) {
200 throw new NotApplicableException(
201 "Cannot initialize an existing Document");
203 if (dprop.getName() == null) {
204 throw new MissedPropertyException("name");
206 if (dprop.getName().length() == 0) {
207 throw new InvalidPropertyException("name");
209 if (dprop.getOwner() == null) {
210 throw new MissedPropertyException("owner");
212 // if (dprop.owner instanceof Study && !ProjectSettings.getStep(step).appliesTo(Study.class)) {
213 // throw new InvalidPropertyException("step");
215 aDoc.setTitle(dprop.getName());
216 aDoc.getFile().changePath(
217 aDoc.getFile().getRelativePath().replace("%n",
218 getEncodedRootName(aDoc, (Study) dprop.getOwner())));
219 if (aDoc.getHistory() == -1) {
222 if (dprop.getDate() == null) {
223 Calendar current = Calendar.getInstance();
224 aDoc.setLastModificationDate(current.getTime()); // Today
226 aDoc.setLastModificationDate(dprop.getDate());
228 getDocumentDAO().update(aDoc);
234 * @see org.splat.service.DocumentService#getSaveDirectory(org.splat.dal.bo.som.Document)
236 public java.io.File getSaveDirectory(final Document aDoc) {
237 String mypath = getRepositoryService().getRepositoryVaultPath()
238 + aDoc.getSourceFile().getRelativePath();
239 String[] table = mypath.split("/");
241 // Cutting the filename
242 StringBuffer path = new StringBuffer(table[0]);
243 for (int i = 1; i < table.length - 1; i++) {
244 path = path.append("/").append(table[i]);
246 return new java.io.File(path.append("/").toString());
250 * Extract title and reference properties from the given file.
254 * @return the extracted properties
256 public Properties extractProperties(final java.io.File file) {
257 Properties fprop = new Properties();
258 Reader tool = Toolbox.getReader(file);
262 value = tool.extractProperty("title");
264 fprop.setName(value);
267 value = tool.extractProperty("reference");
269 fprop.setReference(value);
271 } catch (Exception e) {
272 LOG.debug(e.getMessage(), e);
279 * Create "Converts" relation for the given document and the given format.
285 * @return the created "Converts" relation
287 public ConvertsRelation attach(final Document aDoc, final String format) {
288 return attach(aDoc, format, null);
292 * Create "Converts" relation for the given document, format and description.
299 * the description of the relation
300 * @return the created "Converts" relation
303 public ConvertsRelation attach(final Document aDoc, final String format,
304 final String description) {
305 String path = aDoc.getRelativePath();
306 File export = new File(path + "." + format);
307 ConvertsRelation attach = new ConvertsRelation(aDoc, export,
310 // RKV session.save(export);
311 // RKV session.save(attach);
313 getFileDAO().create(export);
315 // RKV aDoc.addRelation(attach); // Updates this
316 aDoc.getAllRelations().add(attach); // Updates this //RKV
322 * Build a reference (document id) for the given document in the given study basing on the given original document.
325 * the document to set set the new reference to
329 * the original document
330 * @return true if the new reference is set
332 public boolean buildReferenceFrom(final Document aDoc,
333 final ProjectElement scope, final Document lineage) {
334 boolean res = (aDoc.getProgressState() == ProgressState.inWORK);
336 Study owner = scope.getOwnerStudy();
337 Scenario context = null;
338 if (scope instanceof Scenario) {
339 context = ((Scenario) scope);
341 aDoc.setDid(lineage.getDid());
343 && (lineage.isVersioned() || owner.shares(lineage))) {
344 aDoc.setVersion(new Revision(aDoc.getVersion()).setBranch(
345 context.getReference()).toString());
352 * Build a reference (document id) for the given document in the given study.
355 * the document to set set the new reference to
358 * @return true if the new reference is set
360 public boolean buildReferenceFrom(final Document aDoc, final Study scope) {
361 if (aDoc.getProgressState() != ProgressState.inWORK
362 && aDoc.getProgressState() != ProgressState.EXTERN) {
365 DecimalFormat tostring = new DecimalFormat(Document.suformat);
367 aDoc.setDid(aDoc.getDid().replace("%" + Document.suformat,
368 tostring.format(scope.getLastLocalIndex())));
376 * the document to demote
377 * @return true if demoting succeeded
380 public boolean demote(final Document aDoc) {
381 ValidationStep torem;
383 if (aDoc.getProgressState() == ProgressState.inCHECK) {
384 aDoc.setProgressState(ProgressState.inDRAFT);
385 torem = ValidationStep.REVIEW;
386 // This operation must not change the version number of documents.
387 // Consequently, inDRAFT documents may have a minor version number equal to zero.
388 } else if (aDoc.getProgressState() == ProgressState.inDRAFT) {
389 aDoc.setProgressState(ProgressState.inWORK);
390 torem = ValidationStep.PROMOTION;
394 for (Iterator<Relation> i = aDoc.getAllRelations().iterator(); i
396 Relation link = i.next();
397 if (!(link instanceof StampRelation)) {
400 if (((StampRelation) link).getStampType() != torem) {
406 getDocumentDAO().update(aDoc);
411 * Promote a document.
414 * the document to promote
416 * the timestamp of the promotion
417 * @return true if promotion succeeded
420 public boolean promote(final Document aDoc, final Timestamp stamp) {
421 ProgressState newstate = null;
423 if (aDoc.getProgressState() == ProgressState.inWORK) {
424 newstate = ProgressState.inDRAFT; // Promotion to being reviewed
425 } else if (aDoc.getProgressState() == ProgressState.inDRAFT) {
426 newstate = ProgressState.inCHECK; // Promotion to approval
427 Revision myvers = new Revision(aDoc.getVersion());
428 if (myvers.isMinor()) {
429 aDoc.setVersion(myvers.incrementAs(newstate).toString());
430 // TODO: If my physical file is programatically editable, update its (property) version number
431 // ISSUE: What about attached files such as PDF if exist, should we remove them ?
433 } else if (aDoc.getProgressState() == ProgressState.inCHECK) {
434 newstate = ProgressState.APPROVED;
436 aDoc.setProgressState(newstate);
438 // RKV: aDoc.addRelation(stamp.getContext());
439 aDoc.getAllRelations().add(stamp.getContext());
441 getDocumentDAO().update(aDoc);
446 * Increments the reference count of this document following its publication in a Study step.
449 * the document to hold
453 public void hold(final Document aDoc) {
454 aDoc.setCountag(aDoc.getCountag() + 1);
455 if (aDoc.isSaved()) {
456 getDocumentDAO().update(aDoc);
461 * Decrements the reference count of this document following the removal of a Publication from a Study step.
464 * the document to release
468 public void release(final Document aDoc) {
469 aDoc.setCountag(aDoc.getCountag() - 1);
470 if (aDoc.isSaved()) {
471 getDocumentDAO().merge(aDoc);
479 * the document to rename
481 * the new document title
482 * @throws InvalidPropertyException
483 * if the new title is empty
486 public void rename(final Document aDoc, final String title)
487 throws InvalidPropertyException {
488 if (title.length() == 0) {
489 throw new InvalidPropertyException("name");
492 Calendar current = Calendar.getInstance();
493 aDoc.setTitle(title);
494 aDoc.setLastModificationDate(current.getTime()); // Today
495 getDocumentDAO().update(aDoc);
499 * Update a version of the given document.
506 public void updateAs(final Document aDoc, final Revision newvers) {
507 aDoc.setVersion(newvers.setBranch(aDoc.getVersion()).toString()); // Branch names are propagated by the versionning
508 ProgressState newstate = ProgressState.inCHECK;
509 if (newvers.isMinor()) {
510 newstate = ProgressState.inWORK;
512 aDoc.setProgressState(null); // Just to tell updateAs(state) to not increment the version number
513 updateAs(aDoc, newstate);
517 * Update a state of the given document.
525 public void updateAs(final Document aDoc, final ProgressState state) {
526 Document previous = null;
528 // Set of version number
529 if (state == ProgressState.EXTERN) {
530 if (aDoc.getProgressState() != ProgressState.EXTERN) {
531 aDoc.setVersion(null); // Strange use-case...
534 Revision myvers = new Revision(aDoc.getVersion());
535 if (!myvers.isNull()) { // Versionning context
536 previous = aDoc.getPreviousVersion();
538 if (aDoc.getProgressState() != null) {
539 myvers.incrementAs(state); // Incrementation if the reversion number is not imposed
541 aDoc.setVersion(myvers.toString());
543 // Update this document and the previous version, if exist
544 if (previous != null) {
545 previous.setHistory(previous.getHistory() + 1);
546 getDocumentDAO().merge(previous);
548 aDoc.setProgressState(state);
549 // RKV: getDocumentDAO().update(aDoc);
552 // protected void upgrade () {
553 // -------------------------
554 // if (this.state != ProgressState.inWORK) return;
556 // Calendar current = Calendar.getInstance();
557 // for (Iterator<Relation> i=getAllRelations().iterator(); i.hasNext();) {
558 // Relation link = i.next();
559 // if (!link.getClass().equals(UsesRelation.class)) continue;
561 // Document used = (Document)link.getTo();
562 // if (!used.isVersioned()) continue;
563 // TODO: Update the uses relation
566 // this.lasdate = current.getTime(); // Today
567 // Database.getSession().update(this);
569 // TODO: Promote documents using this one
573 * 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 * @return true if documents of this type are result of a study.
579 * @see #isStepResult()
580 * @see #isResultOf(org.splat.service.technical.ProjectSettingsServiceImpl.Step)
582 public boolean isStudyResult(final DocumentType aType) {
583 List<ProjectSettingsService.Step> step = getProjectSettings()
585 ProjectSettingsService.Step lastep = step.get(step.size() - 1);
586 return (aType.isResultOf(lastep));
590 * Get document by its path.
594 * @return the document if found or null
596 @Transactional(readOnly = true)
597 public Document getDocumentByPath(String path) {
598 String[] parse = path.split("'");
601 for (int i = 1; i < parse.length; i++) {
602 path = path + "''" + parse[i];
604 Criterion aCondition = Restrictions.eq("path", path);
605 return getDocumentDAO().findByCriteria(aCondition);
609 * Get the studyService.
611 * @return the studyService
613 public StudyService getStudyService() {
614 return _studyService;
618 * Set the studyService.
620 * @param studyService
621 * the studyService to set
623 public void setStudyService(final StudyService studyService) {
624 _studyService = studyService;
628 * Get project settings.
630 * @return Project settings service
632 private ProjectSettingsService getProjectSettings() {
633 return _projectSettings;
637 * Set project settings service.
639 * @param projectSettingsService
640 * project settings service
642 public void setProjectSettings(
643 final ProjectSettingsService projectSettingsService) {
644 _projectSettings = projectSettingsService;
648 * Get the documentDAO.
650 * @return the documentDAO
652 public DocumentDAO getDocumentDAO() {
657 * Set the documentDAO.
660 * the documentDAO to set
662 public void setDocumentDAO(final DocumentDAO documentDAO) {
663 _documentDAO = documentDAO;
667 * Get the repositoryService.
669 * @return the repositoryService
671 public RepositoryService getRepositoryService() {
672 return _repositoryService;
676 * Set the repositoryService.
678 * @param repositoryService
679 * the repositoryService to set
681 public void setRepositoryService(final RepositoryService repositoryService) {
682 _repositoryService = repositoryService;
686 * Get the documentTypeDAO.
688 * @return the documentTypeDAO
690 public DocumentTypeDAO getDocumentTypeDAO() {
691 return _documentTypeDAO;
695 * Set the documentTypeDAO.
697 * @param documentTypeDAO
698 * the documentTypeDAO to set
700 public void setDocumentTypeDAO(final DocumentTypeDAO documentTypeDAO) {
701 _documentTypeDAO = documentTypeDAO;
707 * @return the fileDAO
709 public FileDAO getFileDAO() {
719 public void setFileDAO(final FileDAO fileDAO) {
726 * @return the studyDAO
728 public StudyDAO getStudyDAO() {
736 * the studyDAO to set
738 public void setStudyDAO(final StudyDAO studyDAO) {
739 _studyDAO = studyDAO;