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.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 document type DAO.
83 private DocumentTypeDAO _documentTypeDAO;
87 private FileDAO _fileDAO;
91 private StudyDAO _studyDAO;
96 * @see org.splat.service.DocumentService#selectDocument(long)
98 @Transactional(readOnly = true)
99 public Document selectDocument(final long index) {
100 return getDocumentDAO().get(index);
106 * @see org.splat.service.DocumentService#selectDocument(java.lang.String, java.lang.String)
108 @Transactional(readOnly = true)
109 public Document selectDocument(final String refid, final String version) {
110 Criterion aCondition = Restrictions.and(Restrictions.eq("did", refid),
111 Restrictions.eq("version", version));
112 return getDocumentDAO().findByCriteria(aCondition);
118 * @see org.splat.service.DocumentService#generateDocumentId(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
121 public void generateDocumentId(final Document aDoc, final Properties dprop) {
123 if (dprop.getOwner() instanceof Study) {
124 owner = (Study) dprop.getOwner();
126 owner = ((Scenario) dprop.getOwner()).getOwnerStudy();
129 // Synchronize the object with the current Hibernate session.
130 owner = getStudyDAO().get(owner.getIndex());
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 path = new StringBuffer(year).append("/").append(path).append("/")
141 .append(step.getPath())
142 // File path relative to the repository vault
143 .append(filename).append(".")
144 .append(aDoc.getFile().getFormat()) // File name and extension
146 aDoc.getFile().changePath(path);
150 * Generate encoded document file name according to the naming scheme from project settings.
156 * @return document reference name
158 private String generateEncodedName(final Document aDoc, final Study scope) {
159 StringBuffer encoding = new StringBuffer();
160 FileNaming scheme = getProjectSettings().getFileNamingScheme();
161 DecimalFormat tostring = new DecimalFormat(Document.suformat);
163 int number = getStudyService().generateLocalIndex(scope);
165 if (scheme == FileNaming.encoded) {
166 encoding.append(scope.getReference()).append(".").append(
167 tostring.format(number));
168 } else { // title and (temporarily) asis
169 encoding.append(aDoc.getTitle()).append(".").append(
170 tostring.format(number));
172 return encoding.toString();
176 * Get encoded root name for a document file.
184 private String getEncodedRootName(final Document aDoc, final Study scope) {
185 FileNaming scheme = getProjectSettings().getFileNamingScheme();
187 if (scheme == FileNaming.encoded) {
188 return scope.getReference();
190 return aDoc.getTitle();
197 * @see org.splat.service.DocumentService#initialize(org.splat.dal.bo.som.Document, org.splat.dal.bo.som.Document.Properties)
200 public void initialize(final Document aDoc, final Properties dprop)
201 throws MissedPropertyException, InvalidPropertyException,
202 NotApplicableException {
203 if (!aDoc.isUndefined()) {
204 throw new NotApplicableException(
205 "Cannot initialize an existing Document");
207 if (dprop.getName() == null) {
208 throw new MissedPropertyException("name");
210 if (dprop.getName().length() == 0) {
211 throw new InvalidPropertyException("name");
213 if (dprop.getOwner() == null) {
214 throw new MissedPropertyException("owner");
216 // if (dprop.owner instanceof Study && !ProjectSettings.getStep(step).appliesTo(Study.class)) {
217 // throw new InvalidPropertyException("step");
219 aDoc.setTitle(dprop.getName());
220 aDoc.getFile().changePath(
221 aDoc.getFile().getRelativePath().replace("%n",
222 getEncodedRootName(aDoc, (Study) dprop.getOwner())));
223 if (aDoc.getHistory() == -1) {
226 if (dprop.getDate() == null) {
227 Calendar current = Calendar.getInstance();
228 aDoc.setLastModificationDate(current.getTime()); // Today
230 aDoc.setLastModificationDate(dprop.getDate());
232 getDocumentDAO().update(aDoc);
238 * @see org.splat.service.DocumentService#getSaveDirectory(org.splat.dal.bo.som.Document)
240 public java.io.File getSaveDirectory(final Document aDoc) {
241 String mypath = getRepositoryService().getRepositoryVaultPath()
242 + aDoc.getSourceFile().getRelativePath();
243 String[] table = mypath.split("/");
245 // Cutting the filename
246 StringBuffer path = new StringBuffer(table[0]);
247 for (int i = 1; i < table.length - 1; i++) {
248 path = path.append("/").append(table[i]);
250 return new java.io.File(path.append("/").toString());
254 * Extract title and reference properties from the given file.
258 * @return the extracted properties
260 public Properties extractProperties(final java.io.File file) {
261 Properties fprop = new Properties();
262 Reader tool = Toolbox.getReader(file);
266 value = tool.extractProperty("title");
268 fprop.setName(value);
271 value = tool.extractProperty("reference");
273 fprop.setReference(value);
275 } catch (Exception e) {
276 LOG.debug(e.getMessage(), e);
283 * Create "Converts" relation for the given document and the given format.
289 * @return the created "Converts" relation
291 public ConvertsRelation attach(final Document aDoc, final String format) {
292 return attach(aDoc, format, null);
296 * Create "Converts" relation for the given document, format and description.
303 * the description of the relation
304 * @return the created "Converts" relation
307 public ConvertsRelation attach(final Document aDoc, final String format,
308 final String description) {
309 String path = aDoc.getRelativePath();
310 File export = new File(path + "." + format);
311 ConvertsRelation attach = new ConvertsRelation(aDoc, export,
314 // RKV session.save(export);
315 // RKV session.save(attach);
317 getFileDAO().create(export);
319 // RKV aDoc.addRelation(attach); // Updates this
320 aDoc.getAllRelations().add(attach); // Updates this //RKV
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 if (aDoc.getProgressState() != ProgressState.inWORK) {
342 Scenario context = null;
343 if (scope instanceof Study) {
344 owner = (Study) scope;
346 context = ((Scenario) scope);
347 owner = context.getOwnerStudy();
349 aDoc.setDid(lineage.getDid());
350 if (context != null && (lineage.isVersioned() || owner.shares(lineage))) {
351 aDoc.setVersion(new Revision(aDoc.getVersion()).setBranch(
352 context.getReference()).toString());
358 * Build a reference (document id) for the given document in the given study.
361 * the document to set set the new reference to
364 * @return true if the new reference is set
366 public boolean buildReferenceFrom(final Document aDoc, final Study scope) {
367 if (aDoc.getProgressState() != ProgressState.inWORK
368 && aDoc.getProgressState() != ProgressState.EXTERN) {
371 DecimalFormat tostring = new DecimalFormat(Document.suformat);
373 aDoc.setDid(aDoc.getDid().replace("%" + Document.suformat,
374 tostring.format(scope.getLastLocalIndex())));
382 * the document to demote
383 * @return true if demoting succeeded
386 public boolean demote(final Document aDoc) {
387 ValidationStep torem;
389 if (aDoc.getProgressState() == ProgressState.inCHECK) {
390 aDoc.setProgressState(ProgressState.inDRAFT);
391 torem = ValidationStep.REVIEW;
392 // This operation must not change the version number of documents.
393 // Consequently, inDRAFT documents may have a minor version number equal to zero.
394 } else if (aDoc.getProgressState() == ProgressState.inDRAFT) {
395 aDoc.setProgressState(ProgressState.inWORK);
396 torem = ValidationStep.PROMOTION;
400 for (Iterator<Relation> i = aDoc.getAllRelations().iterator(); i
402 Relation link = i.next();
403 if (!(link instanceof StampRelation)) {
406 if (((StampRelation) link).getStampType() != torem) {
412 getDocumentDAO().update(aDoc);
417 * Promote a document.
420 * the document to promote
422 * the timestamp of the promotion
423 * @return true if promotion succeeded
426 public boolean promote(final Document aDoc, final Timestamp stamp) {
427 ProgressState newstate = null;
429 if (aDoc.getProgressState() == ProgressState.inWORK) {
430 newstate = ProgressState.inDRAFT; // Promotion to being reviewed
431 } else if (aDoc.getProgressState() == ProgressState.inDRAFT) {
432 newstate = ProgressState.inCHECK; // Promotion to approval
433 Revision myvers = new Revision(aDoc.getVersion());
434 if (myvers.isMinor()) {
435 aDoc.setVersion(myvers.incrementAs(newstate).toString());
436 // TODO: If my physical file is programatically editable, update its (property) version number
437 // ISSUE: What about attached files such as PDF if exist, should we remove them ?
439 } else if (aDoc.getProgressState() == ProgressState.inCHECK) {
440 newstate = ProgressState.APPROVED;
442 aDoc.setProgressState(newstate);
444 // RKV: aDoc.addRelation(stamp.getContext());
445 aDoc.getAllRelations().add(stamp.getContext());
447 getDocumentDAO().update(aDoc);
452 * Increments the reference count of this document following its publication in a Study step.
455 * the document to hold
459 public void hold(final Document aDoc) {
460 aDoc.setCountag(aDoc.getCountag() + 1);
461 if (aDoc.isSaved()) {
462 getDocumentDAO().update(aDoc);
467 * Decrements the reference count of this document following the removal of a Publication from a Study step.
470 * the document to release
474 public void release(final Document aDoc) {
475 aDoc.setCountag(aDoc.getCountag() - 1);
476 if (aDoc.isSaved()) {
477 getDocumentDAO().merge(aDoc);
485 * the document to rename
487 * the new document title
488 * @throws InvalidPropertyException
489 * if the new title is empty
492 public void rename(final Document aDoc, final String title)
493 throws InvalidPropertyException {
494 if (title.length() == 0) {
495 throw new InvalidPropertyException("name");
498 Calendar current = Calendar.getInstance();
499 aDoc.setTitle(title);
500 aDoc.setLastModificationDate(current.getTime()); // Today
501 getDocumentDAO().update(aDoc);
505 * Update a version of the given document.
512 public void updateAs(final Document aDoc, final Revision newvers) {
513 aDoc.setVersion(newvers.setBranch(aDoc.getVersion()).toString()); // Branch names are propagated by the versionning
514 ProgressState newstate = ProgressState.inCHECK;
515 if (newvers.isMinor()) {
516 newstate = ProgressState.inWORK;
518 aDoc.setProgressState(null); // Just to tell updateAs(state) to not increment the version number
519 updateAs(aDoc, newstate);
523 * Update a state of the given document.
531 public void updateAs(final Document aDoc, final ProgressState state) {
532 Document previous = null;
534 // Set of version number
535 if (state == ProgressState.EXTERN) {
536 if (aDoc.getProgressState() != ProgressState.EXTERN) {
537 aDoc.setVersion(null); // Strange use-case...
540 Revision myvers = new Revision(aDoc.getVersion());
541 if (!myvers.isNull()) { // Versionning context
542 for (Iterator<Relation> i = aDoc.getAllRelations().iterator(); i
544 Relation link = i.next();
545 if (link.getClass().equals(VersionsRelation.class)) {
546 previous = (Document) link.getTo(); // Versioned document
551 if (aDoc.getProgressState() != null) {
552 myvers.incrementAs(state); // Incrementation if the reversion number is not imposed
554 aDoc.setVersion(myvers.toString());
556 // Update this document and the previous version, if exit
557 if (previous != null) {
558 previous.setHistory(previous.getHistory() + 1);
559 getDocumentDAO().merge(previous);
561 aDoc.setProgressState(state);
562 getDocumentDAO().update(aDoc);
565 // protected void upgrade () {
566 // -------------------------
567 // if (this.state != ProgressState.inWORK) return;
569 // Calendar current = Calendar.getInstance();
570 // for (Iterator<Relation> i=getAllRelations().iterator(); i.hasNext();) {
571 // Relation link = i.next();
572 // if (!link.getClass().equals(UsesRelation.class)) continue;
574 // Document used = (Document)link.getTo();
575 // if (!used.isVersioned()) continue;
576 // TODO: Update the uses relation
579 // this.lasdate = current.getTime(); // Today
580 // Database.getSession().update(this);
582 // TODO: Promote documents using this one
586 * 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
591 * @return true if documents of this type are result of a study.
592 * @see #isStepResult()
593 * @see #isResultOf(org.splat.service.technical.ProjectSettingsServiceImpl.Step)
595 public boolean isStudyResult(final DocumentType aType) {
596 List<ProjectSettingsService.Step> step = getProjectSettings()
598 ProjectSettingsService.Step lastep = step.get(step.size() - 1);
599 return (aType.isResultOf(lastep));
603 * Get document by its path.
607 * @return the document if found or null
609 @Transactional(readOnly = true)
610 public Document getDocumentByPath(String path) {
611 String[] parse = path.split("'");
614 for (int i = 1; i < parse.length; i++) {
615 path = path + "''" + parse[i];
617 Criterion aCondition = Restrictions.eq("path", path);
618 return getDocumentDAO().findByCriteria(aCondition);
622 * Get the studyService.
624 * @return the studyService
626 public StudyService getStudyService() {
627 return _studyService;
631 * Set the studyService.
633 * @param studyService
634 * the studyService to set
636 public void setStudyService(final StudyService studyService) {
637 _studyService = studyService;
641 * Get project settings.
643 * @return Project settings service
645 private ProjectSettingsService getProjectSettings() {
646 return _projectSettings;
650 * Set project settings service.
652 * @param projectSettingsService
653 * project settings service
655 public void setProjectSettings(
656 final ProjectSettingsService projectSettingsService) {
657 _projectSettings = projectSettingsService;
661 * Get the documentDAO.
663 * @return the documentDAO
665 public DocumentDAO getDocumentDAO() {
670 * Set the documentDAO.
673 * the documentDAO to set
675 public void setDocumentDAO(final DocumentDAO documentDAO) {
676 _documentDAO = documentDAO;
680 * Get the repositoryService.
682 * @return the repositoryService
684 public RepositoryService getRepositoryService() {
685 return _repositoryService;
689 * Set the repositoryService.
691 * @param repositoryService
692 * the repositoryService to set
694 public void setRepositoryService(final RepositoryService repositoryService) {
695 _repositoryService = repositoryService;
699 * Get the documentTypeDAO.
701 * @return the documentTypeDAO
703 public DocumentTypeDAO getDocumentTypeDAO() {
704 return _documentTypeDAO;
708 * Set the documentTypeDAO.
710 * @param documentTypeDAO
711 * the documentTypeDAO to set
713 public void setDocumentTypeDAO(final DocumentTypeDAO documentTypeDAO) {
714 _documentTypeDAO = documentTypeDAO;
720 * @return the fileDAO
722 public FileDAO getFileDAO() {
732 public void setFileDAO(final FileDAO fileDAO) {
739 * @return the studyDAO
741 public StudyDAO getStudyDAO() {
749 * the studyDAO to set
751 public void setStudyDAO(final StudyDAO studyDAO) {
752 _studyDAO = studyDAO;