1 package org.splat.dal.bo.som;
5 * @author Daniel Brunier-Coulin
6 * @copyright OPEN CASCADE 2012
9 import java.text.DecimalFormat;
10 import java.text.SimpleDateFormat;
11 import java.util.Arrays;
12 import java.util.Calendar;
13 import java.util.Date;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Vector;
18 import org.hibernate.Hibernate;
19 import org.hibernate.Session;
21 import org.splat.dal.bo.kernel.Persistent;
22 import org.splat.dal.bo.kernel.Relation;
23 import org.splat.dal.bo.kernel.User;
24 import org.splat.dal.bo.som.Timestamp.ComparatorByDate;
25 import org.splat.dal.dao.som.Database;
26 import org.splat.kernel.NotApplicableException;
27 import org.splat.kernel.InvalidPropertyException;
28 import org.splat.kernel.MissedPropertyException;
29 import org.splat.kernel.MultiplyDefinedException;
30 import org.splat.manox.Reader;
31 import org.splat.manox.Toolbox;
32 import org.splat.service.StudyService;
33 import org.splat.service.technical.ProjectSettingsService;
34 import org.splat.service.technical.ProjectSettingsServiceImpl;
35 import org.splat.service.technical.ProjectSettingsServiceImpl.FileNaming;
36 import org.splat.som.Revision;
37 import org.splat.som.Step;
39 public class Document extends Entity {
42 private DocumentType type; // User expendable types
46 private ProgressState state;
48 private String version;
53 private ProjectSettingsService _projectSettingsService;
54 private StudyService _studyService;
57 public static String suformat = "00"; // Format of the suffix number of document did and file name
59 // ==============================================================================================================================
61 // ==============================================================================================================================
63 // Fields initialization class
64 public static class Properties extends Persistent.Properties {
65 // ------------------------------------------------------------
66 private DocumentType type = null;
67 private String did = null; // Only for searching from a given reference
68 private ProjectElement owner = null; // Only for constructing a document
69 private ProjectSettingsService.Step step = null;
70 private ProgressState state = null;
71 private String name = null;
72 protected String format = null;
73 private String version = null;
74 private User author = null;
75 protected Date date = null;
76 private String summary = null; // Only for versioning a document
77 private String path = null; // Only for searching from a given path
97 public Properties copy() {
98 Properties copy = new Properties();
99 copy.type = this.type;
101 copy.owner = this.owner;
102 copy.step = this.step;
103 copy.state = this.state;
104 copy.name = this.name;
105 copy.format = this.format;
106 copy.version = this.version;
107 copy.author = this.author;
108 copy.date = this.date;
109 copy.summary = this.summary;
110 copy.path = this.path;
114 // - Protected services
116 public User getAuthor() {
120 public String getDescription() {
124 public String getLocalPath() {
128 public String getReference() {
132 public ProjectSettingsService.Step getStep() {
136 public DocumentType getType() {
140 // - Property setters
142 public Properties setAuthor(User user) {
147 public Properties setDate(Date date) {
152 public Properties setDescription(String summary)
153 throws InvalidPropertyException {
154 if (summary.length() == 0)
155 throw new InvalidPropertyException("description");
156 this.summary = summary;
160 public Properties setDocument(Document base) {
162 step = ProjectSettingsServiceImpl.getStep(base.step);
164 format = base.getFormat();
165 state = ProgressState.inWORK; // For incrementing the version number at save time
166 version = base.version;
170 public Properties setExternReference(String ref)
171 throws InvalidPropertyException {
172 if (ref.length() == 0)
173 throw new InvalidPropertyException("reference");
174 if (ref.equals(new Revision().toString()))
175 throw new InvalidPropertyException("reference"); // Internal version number
180 public Properties setFormat(String format)
181 throws InvalidPropertyException {
182 if (format.length() == 0)
183 throw new InvalidPropertyException("format");
184 this.format = format;
188 // Required only for passing search arguments
189 public Properties setLocalPath(String path)
190 throws InvalidPropertyException {
191 if (path.length() == 0)
192 throw new InvalidPropertyException("path");
197 public Properties setName(String name) throws InvalidPropertyException {
198 if (name.length() == 0)
199 throw new InvalidPropertyException("name");
204 public Properties setOwner(ProjectElement owner) {
209 // Required only for passing search arguments
210 public Properties setReference(String did)
211 throws InvalidPropertyException {
212 if (did.length() == 0)
213 throw new InvalidPropertyException("reference");
218 public Properties setState(ProgressState state)
219 throws InvalidPropertyException {
220 if (state == ProgressState.inPROGRESS
221 || state == ProgressState.TEMPLATE)
222 throw new InvalidPropertyException("state"); // Non document states
227 public Properties setStep(ProjectSettingsService.Step step) {
232 public Properties setType(DocumentType type) {
237 // - Global validity check
239 public void checkValidity() throws MissedPropertyException,
240 InvalidPropertyException, MultiplyDefinedException {
242 throw new MissedPropertyException("type");
244 throw new MissedPropertyException("owner");
246 throw new MissedPropertyException("step");
248 throw new MissedPropertyException("author");
250 throw new MissedPropertyException("format");
251 if (owner instanceof Study && !step.appliesTo(Study.class))
252 throw new InvalidPropertyException("step");
253 if (!type.isContentInto(step))
254 throw new InvalidPropertyException("step");
255 if (state != null && state != ProgressState.EXTERN) {
256 // inDRAFT, inCHECK or APPROVED + version = imposed version (future use)
257 // inWORK + version = base version incremented at save time (used for versioning)
259 throw new InvalidPropertyException("state");
261 if (version != null) {
263 state = ProgressState.EXTERN;
268 // Database fetch constructor
269 protected Document() {
272 // Internal constructor
273 public Document(Properties dprop) throws MissedPropertyException,
274 InvalidPropertyException, MultiplyDefinedException {
275 // -------------------------------------
276 super(dprop); // Throws one of the above exception if not valid
277 myfile = new File(null, dprop.format, dprop.date); // The path is initialized below
279 step = dprop.step.getNumber();
281 version = dprop.version;
282 author = dprop.author;
285 lasdate = myfile.getDate(); // Today if not defined in the properties
289 state = ProgressState.inWORK; // Promoted when saving this document
290 version = new Revision().toString();
293 if (dprop.owner instanceof Study)
294 owner = (Study) dprop.owner;
296 owner = ((Scenario) dprop.owner).getOwnerStudy();
298 ProjectSettingsService.Step step = ProjectSettingsServiceImpl
300 SimpleDateFormat tostring = new SimpleDateFormat("yyyy");
301 String year = tostring.format(owner.getDate());
302 if (name == null) { // Newed document
303 this.name = "%n"; // Named later at publication
304 this.history = -1; // Marks the document as undefined for future assignment
306 String filename = generateEncodedName(owner);
309 path = owner.getReference();
310 did = new StringBuffer(path).append(".%").append(suformat).toString(); // Document reference
311 path = new StringBuffer(year).append("/").append(path).append("/")
312 .append(step.getPath()) // File path relative to the repository vault
313 .append(filename).append(".").append(myfile.getFormat()) // File name and extension
315 myfile.changePath(path);
318 // ==============================================================================================================================
319 // Public member functions
320 // ==============================================================================================================================
322 public File getAttachedFile(String format) {
323 // -------------------------------------------
324 List<Relation> exports = getRelations(ConvertsRelation.class);
326 for (Iterator<Relation> i = exports.iterator(); i.hasNext();) {
327 File export = (File) i.next().getTo();
328 if (export.getFormat().equals(format))
334 public User getAuthor() {
335 // ------------------------
339 public Date getCreationDate() {
340 // ------------------------------
341 return myfile.getDate();
344 public Date getLastModificationDate() {
345 // --------------------------------------
349 public String getFormat() {
350 // --------------------------
351 return myfile.getFormat();
354 public Document getPreviousVersion() {
355 // -------------------------------------
356 Relation previous = getFirstRelation(VersionsRelation.class);
357 if (previous != null)
358 return (Document) previous.getTo();
363 public ProgressState getProgressState() {
364 // ----------------------------------------
369 * Returns the path where all physical files attached to this document are saved. This path is relative to the vault of the repository
370 * and include the file name, without extension, common to all physical files attached to this document.
372 * @return the path of the document
374 public String getRelativePath() {
375 // --------------------------------
376 String[] table = myfile.getRelativePath().split("\\x2E");
377 StringBuffer path = new StringBuffer(table[0]);
378 for (int i = 1; i < table.length - 1; i++)
379 path.append('.').append(table[i]);
380 return path.toString();
384 * Returns the global unique reference of this document lineage. The document reference is common to all versions of the document
385 * (versioning a document does not change its reference). It is made of the owner study reference suffixed by a document identifier
386 * unique in the scope of the study.
388 * @return the document reference
390 public String getReference() {
391 // -----------------------------
395 public java.io.File getSaveDirectory() {
396 // ---------------------------------------
397 String mypath = Database.getRepositoryVaultPath()
398 + myfile.getRelativePath();
399 String[] table = mypath.split("/");
401 // Cutting the filename
402 StringBuffer path = new StringBuffer(table[0]);
403 for (int i = 1; i < table.length - 1; i++)
404 path = path.append("/").append(table[i]);
405 return new java.io.File(path.append("/").toString());
408 public File getSourceFile() {
409 // ----------------------------
414 * Returns the stamps such as review and approval attached to this document, if exist. If several stamps exist, they are returned in
415 * ascending order of dates.
417 * @return the stamps of the document in ascending order of dates, or an empty array if no stamp exist.
419 public Timestamp[] getStamps() {
420 // -------------------------------
421 Vector<Timestamp> stamps = new Vector<Timestamp>();
423 for (Iterator<Relation> i = this.getAllRelations().iterator(); i
425 Relation link = i.next();
426 if (link instanceof StampRelation)
427 stamps.add(((StampRelation) link).getTo());
429 Timestamp[] result = stamps.toArray(new Timestamp[stamps.size()]);
430 ComparatorByDate bydate = new Timestamp.ComparatorByDate();
432 Arrays.sort(result, bydate);
437 * Returns the title of this document.
439 * @return the document title, or an empty string is this document is undefined.
440 * @see #isUndefined()
442 public String getTitle() {
443 // -------------------------
444 if (this.isUndefined())
450 public DocumentType getType() {
451 // ------------------------------
456 * Returns the version number of this document. The version number, when exists, is either of the internal form (m.n.s) usable for
457 * building a Revision object, or any string in case of external document (document with EXTERN state).<br/>
459 * Note: document slots have a version number equal to "0.0.0".
461 * @return the version number of this document, or null if this is EXTERN.
462 * @see #isUndefined()
464 public String getVersion() {
465 // ---------------------------
470 * Returns true if this document is undefined. An undefined document is a meta-document created for reserving the persistent reference
471 * of a new document before saving (or importing) this later into the repository. The working copy of a such document may include this
476 * @see #initialize(Properties)
478 public boolean isUndefined() {
479 // -----------------------------
480 return (history == -1);
483 public boolean isInto(Step container) {
484 // --------------------------------------
485 return (step == container.getNumber());
488 public boolean isPublished() {
489 // -----------------------------
490 return (countag > 0);
493 public boolean isShared() {
494 // --------------------------
495 return (countag + history > 1);
498 public boolean isVersioned() {
499 // -----------------------------
500 return (history > 0);
503 // ==============================================================================================================================
505 // ==============================================================================================================================
507 public static DocumentType createType(DocumentType.Properties tprop)
508 throws MissedPropertyException, InvalidPropertyException,
509 MultiplyDefinedException, RuntimeException {
510 // ---------------------------------------------------------------------
511 // TODO: Check for duplicate definition
512 DocumentType type = new DocumentType(tprop);
513 Session session = Database.getSession();
519 public static Properties extractProperties(java.io.File file) {
520 // --------------------------------------------------------------
521 Properties fprop = new Properties();
522 Reader tool = Toolbox.getReader(file);
526 value = tool.extractProperty("title");
528 fprop.setName(value);
530 value = tool.extractProperty("reference");
532 fprop.setReference(value);
533 } catch (Exception e) {
538 @SuppressWarnings("unchecked")
539 public static List<DocumentType> selectAllTypes() {
540 // --------------------------------------------------
541 String query = "from DocumentType";
543 List<DocumentType> types = Database.getSession().createQuery(query)
545 for (Iterator<DocumentType> i = types.iterator(); i.hasNext();) {
546 Hibernate.initialize(i.next()); // Supposed fetching document types
551 @SuppressWarnings("unchecked")
552 public static List<DocumentType> selectResultTypes() {
553 // -----------------------------------------------------
554 String query = "from DocumentType where result is not null order by result asc";
556 return Database.getSession().createQuery(query).list();
559 public static DocumentType selectType(String name) {
560 // ---------------------------------------------------
561 String query = new StringBuffer("from DocumentType where name='")
562 .append(name).append("'").toString();
564 return (DocumentType) Database.getSession().createQuery(query)
568 public static DocumentType selectType(int index) {
569 // -------------------------------------------------
570 String query = new StringBuffer("from DocumentType where rid='")
571 .append(index).append("'").toString();
573 return (DocumentType) Database.getSession().createQuery(query)
577 @SuppressWarnings("unchecked")
578 public static List<DocumentType> selectTypesOf(
579 ProjectSettingsService.Step step) {
580 // --------------------------------------------------------------------------
581 Integer number = step.getNumber();
582 String query = new StringBuffer("from DocumentType")
583 .append(" where step like '%-").append(number).append("-%'")
586 List<DocumentType> types = Database.getSession().createQuery(query)
588 for (Iterator<DocumentType> i = types.iterator(); i.hasNext();) {
589 Hibernate.initialize(i.next()); // For fetching document types
594 // ==============================================================================================================================
595 // Protected services
596 // ==============================================================================================================================
598 protected ConvertsRelation attach(String format) {
599 // -------------------------------------------------
600 return attach(format, null);
603 protected ConvertsRelation attach(String format, String description) {
604 // ---------------------------------------------------------------------
605 String path = this.getRelativePath();
606 File export = new File(path + "." + format);
607 ConvertsRelation attach = new ConvertsRelation(this, export,
609 Session session = Database.getSession();
611 session.save(export);
612 session.save(attach);
614 this.addRelation(attach); // Updates this
619 public boolean buildReferenceFrom(ProjectElement scope, Document lineage) {
620 // -----------------------------------------------------------------------------
621 if (state != ProgressState.inWORK)
624 Scenario context = null;
625 if (scope instanceof Study)
626 owner = (Study) scope;
628 context = ((Scenario) scope);
629 owner = context.getOwnerStudy();
632 if (context != null && (lineage.isVersioned() || owner.shares(lineage))) {
633 version = new Revision(version).setBranch(context.getReference())
639 public boolean buildReferenceFrom(Study scope) {
640 // --------------------------------------------------
641 if (state != ProgressState.inWORK && state != ProgressState.EXTERN)
643 DecimalFormat tostring = new DecimalFormat(suformat);
645 did = did.replace("%" + suformat,
646 tostring.format(scope.getLastLocalIndex()));
650 public boolean demote() {
651 // ---------------------------
652 ValidationStep torem;
654 if (state == ProgressState.inCHECK) {
655 state = ProgressState.inDRAFT;
656 torem = ValidationStep.REVIEW;
657 // This operation must not change the version number of documents.
658 // Consequently, inDRAFT documents may have a minor version number equal to zero.
659 } else if (state == ProgressState.inDRAFT) {
660 state = ProgressState.inWORK;
661 torem = ValidationStep.PROMOTION;
665 for (Iterator<Relation> i = this.getAllRelations().iterator(); i
667 Relation link = i.next();
668 if (!(link instanceof StampRelation))
670 if (((StampRelation) link).getStampType() != torem)
675 Database.getSession().update(this);
680 * Increments the reference count of this document following its publication into a Study step.
685 // ----------------------
688 Database.getSession().update(this);
692 * Defines this document.
695 * the properties of the document
697 * @see Step#createDocument(Properties)
698 * @see #isUndefined()
700 public void initialize(Properties dprop) throws MissedPropertyException,
701 InvalidPropertyException, NotApplicableException {
702 // --------------------------------------------
703 if (!this.isUndefined())
704 throw new NotApplicableException(
705 "Cannot initialize an existing Document");
706 if (dprop.name == null)
707 throw new MissedPropertyException("name");
708 if (dprop.name.length() == 0)
709 throw new InvalidPropertyException("name");
710 if (dprop.owner == null)
711 throw new MissedPropertyException("owner");
712 // if (dprop.owner instanceof Study && !ProjectSettings.getStep(step).appliesTo(Study.class)) {
713 // throw new InvalidPropertyException("step");
716 myfile.changePath(myfile.getRelativePath().replace("%n",
717 getEncodedRootName((Study) dprop.owner)));
720 if (dprop.date == null) {
721 Calendar current = Calendar.getInstance();
722 lasdate = current.getTime(); // Today
724 lasdate = dprop.date;
726 Database.getSession().update(this);
729 public boolean promote(Timestamp stamp) {
730 // -------------------------------------------
731 ProgressState newstate = null;
733 if (state == ProgressState.inWORK) {
734 newstate = ProgressState.inDRAFT; // Promotion to being reviewed
735 } else if (state == ProgressState.inDRAFT) {
736 newstate = ProgressState.inCHECK; // Promotion to approval
737 Revision myvers = new Revision(version);
738 if (myvers.isMinor()) {
739 version = myvers.incrementAs(newstate).toString();
740 // TODO: If my physical file is programatically editable, update its (property) version number
741 // ISSUE: What about attached files such as PDF if exist, should we remove them ?
743 } else if (state == ProgressState.inCHECK) {
744 newstate = ProgressState.APPROVED;
746 this.state = newstate;
748 this.addRelation(stamp.getContext());
749 Database.getSession().update(this);
754 * Decrements the reference count of this document following the removal of a Publication from a Study step.
758 public void release() {
759 // -------------------------
762 Database.getSession().update(this);
765 protected void rename(String title) throws InvalidPropertyException {
766 // ------------------------------------
767 if (title.length() == 0)
768 throw new InvalidPropertyException("name");
770 Calendar current = Calendar.getInstance();
772 this.lasdate = current.getTime(); // Today
773 Database.getSession().update(this);
776 public void updateAs(Revision newvers) {
777 // ------------------------------------------
778 version = newvers.setBranch(version).toString(); // Branch names are propagated by the versionning
779 ProgressState newstate = ProgressState.inCHECK;
780 if (newvers.isMinor())
781 newstate = ProgressState.inWORK;
782 state = null; // Just to tell updateAs(sate) to not increment the version number
786 public void updateAs(ProgressState state) {
787 // ---------------------------------------------
788 Document previous = null;
790 // Set of version number
791 if (state == ProgressState.EXTERN) {
792 if (this.state != ProgressState.EXTERN)
793 this.version = null; // Strange use-case...
795 Revision myvers = new Revision(version);
796 if (!myvers.isNull()) { // Versionning context
797 for (Iterator<Relation> i = getAllRelations().iterator(); i
799 Relation link = i.next();
800 if (!link.getClass().equals(VersionsRelation.class))
802 previous = (Document) link.getTo(); // Versioned document
806 if (this.state != null)
807 myvers.incrementAs(state); // Incrementation if the reversion number is not imposed
808 this.version = myvers.toString();
810 // Update this document and the previous version, if exit
811 Session session = Database.getSession();
812 if (previous != null) {
813 previous.history += 1;
814 session.update(previous);
817 session.update(this);
820 // protected void upgrade () {
821 // -------------------------
822 // if (this.state != ProgressState.inWORK) return;
824 // Calendar current = Calendar.getInstance();
825 // for (Iterator<Relation> i=getAllRelations().iterator(); i.hasNext();) {
826 // Relation link = i.next();
827 // if (!link.getClass().equals(UsesRelation.class)) continue;
829 // Document used = (Document)link.getTo();
830 // if (!used.isVersioned()) continue;
831 // TODO: Update the uses relation
834 // this.lasdate = current.getTime(); // Today
835 // Database.getSession().update(this);
837 // TODO: Promote documents using this one
840 // ==============================================================================================================================
842 // ==============================================================================================================================
844 private String generateEncodedName(Study scope) {
845 // ------------------------------------------------
846 StringBuffer encoding = new StringBuffer();
847 FileNaming scheme = getProjectSettingsService().getFileNamingScheme();
848 DecimalFormat tostring = new DecimalFormat(suformat);
850 int number = getStudyService().generateLocalIndex(scope);
852 if (scheme == FileNaming.encoded) {
853 encoding.append(scope.getReference()).append(".")
854 .append(tostring.format(number));
855 } else { // title and (temporarily) asis
856 encoding.append(name).append(".").append(tostring.format(number));
858 return encoding.toString();
861 private String getEncodedRootName(Study scope) {
862 // -----------------------------------------------
863 FileNaming scheme = getProjectSettingsService().getFileNamingScheme();
865 if (scheme == FileNaming.encoded)
866 return scope.getReference();
874 private ProjectSettingsService getProjectSettingsService() {
875 return _projectSettingsService;
878 public void setProjectSettingsService(
879 ProjectSettingsService projectSettingsService) {
880 _projectSettingsService = projectSettingsService;
886 public StudyService getStudyService() {
887 return _studyService;
890 public void setStudyService(StudyService studyService) {
891 _studyService = studyService;