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) {
156 public Date getDate() {
160 public Properties setDescription(String summary)
161 throws InvalidPropertyException {
162 if (summary.length() == 0)
163 throw new InvalidPropertyException("description");
164 this.summary = summary;
168 public Properties setDocument(Document base) {
170 step = ProjectSettingsServiceImpl.getStep(base.step);
172 format = base.getFormat();
173 state = ProgressState.inWORK; // For incrementing the version number at save time
174 version = base.version;
178 public Properties setExternReference(String ref)
179 throws InvalidPropertyException {
180 if (ref.length() == 0)
181 throw new InvalidPropertyException("reference");
182 if (ref.equals(new Revision().toString()))
183 throw new InvalidPropertyException("reference"); // Internal version number
188 public Properties setFormat(String format)
189 throws InvalidPropertyException {
190 if (format.length() == 0)
191 throw new InvalidPropertyException("format");
192 this.format = format;
196 // Required only for passing search arguments
197 public Properties setLocalPath(String path)
198 throws InvalidPropertyException {
199 if (path.length() == 0)
200 throw new InvalidPropertyException("path");
205 public Properties setName(String name) throws InvalidPropertyException {
206 if (name.length() == 0)
207 throw new InvalidPropertyException("name");
212 public String getName() {
216 public Properties setOwner(ProjectElement owner) {
221 public ProjectElement getOwner() {
224 // Required only for passing search arguments
225 public Properties setReference(String did)
226 throws InvalidPropertyException {
227 if (did.length() == 0)
228 throw new InvalidPropertyException("reference");
233 public Properties setState(ProgressState state)
234 throws InvalidPropertyException {
235 if (state == ProgressState.inPROGRESS
236 || state == ProgressState.TEMPLATE)
237 throw new InvalidPropertyException("state"); // Non document states
242 public Properties setStep(ProjectSettingsService.Step step) {
247 public Properties setType(DocumentType type) {
252 // - Global validity check
254 public void checkValidity() throws MissedPropertyException,
255 InvalidPropertyException, MultiplyDefinedException {
257 throw new MissedPropertyException("type");
259 throw new MissedPropertyException("owner");
261 throw new MissedPropertyException("step");
263 throw new MissedPropertyException("author");
265 throw new MissedPropertyException("format");
266 if (owner instanceof Study && !step.appliesTo(Study.class))
267 throw new InvalidPropertyException("step");
268 if (!type.isContentInto(step))
269 throw new InvalidPropertyException("step");
270 if (state != null && state != ProgressState.EXTERN) {
271 // inDRAFT, inCHECK or APPROVED + version = imposed version (future use)
272 // inWORK + version = base version incremented at save time (used for versioning)
274 throw new InvalidPropertyException("state");
276 if (version != null) {
278 state = ProgressState.EXTERN;
283 // Database fetch constructor
284 protected Document() {
287 // Internal constructor
288 public Document(Properties dprop) throws MissedPropertyException,
289 InvalidPropertyException, MultiplyDefinedException {
290 // -------------------------------------
291 super(dprop); // Throws one of the above exception if not valid
292 myfile = new File(null, dprop.format, dprop.date); // The path is initialized below
294 step = dprop.step.getNumber();
296 version = dprop.version;
297 author = dprop.author;
300 lasdate = myfile.getDate(); // Today if not defined in the properties
304 state = ProgressState.inWORK; // Promoted when saving this document
305 version = new Revision().toString();
307 if (name == null) { // Newed document
308 this.name = "%n"; // Named later at publication
309 this.history = -1; // Marks the document as undefined for future assignment
313 // ==============================================================================================================================
314 // Public member functions
315 // ==============================================================================================================================
317 public File getAttachedFile(String format) {
318 // -------------------------------------------
319 List<Relation> exports = getRelations(ConvertsRelation.class);
321 for (Iterator<Relation> i = exports.iterator(); i.hasNext();) {
322 File export = (File) i.next().getTo();
323 if (export.getFormat().equals(format))
329 public User getAuthor() {
330 // ------------------------
334 public Date getCreationDate() {
335 // ------------------------------
336 return myfile.getDate();
339 public Date getLastModificationDate() {
340 // --------------------------------------
344 public void setLastModificationDate(Date aDate) {
348 public String getFormat() {
349 // --------------------------
350 return myfile.getFormat();
353 public Document getPreviousVersion() {
354 // -------------------------------------
355 Relation previous = getFirstRelation(VersionsRelation.class);
356 if (previous != null)
357 return (Document) previous.getTo();
362 public ProgressState getProgressState() {
363 // ----------------------------------------
368 * Returns the path where all physical files attached to this document are saved. This path is relative to the vault of the repository
369 * and include the file name, without extension, common to all physical files attached to this document.
371 * @return the path of the document
373 public String getRelativePath() {
374 // --------------------------------
375 String[] table = myfile.getRelativePath().split("\\x2E");
376 StringBuffer path = new StringBuffer(table[0]);
377 for (int i = 1; i < table.length - 1; i++)
378 path.append('.').append(table[i]);
379 return path.toString();
383 * Returns the global unique reference of this document lineage. The document reference is common to all versions of the document
384 * (versioning a document does not change its reference). It is made of the owner study reference suffixed by a document identifier
385 * unique in the scope of the study.
387 * @return the document reference
389 public String getReference() {
390 // -----------------------------
394 public java.io.File getSaveDirectory() {
395 // ---------------------------------------
396 String mypath = Database.getRepositoryVaultPath()
397 + myfile.getRelativePath();
398 String[] table = mypath.split("/");
400 // Cutting the filename
401 StringBuffer path = new StringBuffer(table[0]);
402 for (int i = 1; i < table.length - 1; i++)
403 path = path.append("/").append(table[i]);
404 return new java.io.File(path.append("/").toString());
407 public File getSourceFile() {
408 // ----------------------------
413 * Returns the stamps such as review and approval attached to this document, if exist. If several stamps exist, they are returned in
414 * ascending order of dates.
416 * @return the stamps of the document in ascending order of dates, or an empty array if no stamp exist.
418 public Timestamp[] getStamps() {
419 // -------------------------------
420 Vector<Timestamp> stamps = new Vector<Timestamp>();
422 for (Iterator<Relation> i = this.getAllRelations().iterator(); i
424 Relation link = i.next();
425 if (link instanceof StampRelation)
426 stamps.add(((StampRelation) link).getTo());
428 Timestamp[] result = stamps.toArray(new Timestamp[stamps.size()]);
429 ComparatorByDate bydate = new Timestamp.ComparatorByDate();
431 Arrays.sort(result, bydate);
436 * Returns the title of this document.
438 * @return the document title, or an empty string is this document is undefined.
439 * @see #isUndefined()
441 public String getTitle() {
442 // -------------------------
443 if (this.isUndefined())
450 * Set document title.
451 * @param aTitle document title to set
453 public void setTitle(String aTitle) {
457 public DocumentType getType() {
458 // ------------------------------
463 * Returns the version number of this document. The version number, when exists, is either of the internal form (m.n.s) usable for
464 * building a Revision object, or any string in case of external document (document with EXTERN state).<br/> <br/> Note: document slots
465 * have a version number equal to "0.0.0".
467 * @return the version number of this document, or null if this is EXTERN.
468 * @see #isUndefined()
470 public String getVersion() {
471 // ---------------------------
476 * Returns true if this document is undefined. An undefined document is a meta-document created for reserving the persistent reference
477 * of a new document before saving (or importing) this later into the repository. The working copy of a such document may include this
482 * @see #initialize(Properties)
484 public boolean isUndefined() {
485 // -----------------------------
486 return (history == -1);
489 public boolean isInto(Step container) {
490 // --------------------------------------
491 return (step == container.getNumber());
494 public boolean isPublished() {
495 // -----------------------------
496 return (countag > 0);
499 public boolean isShared() {
500 // --------------------------
501 return (countag + history > 1);
504 public boolean isVersioned() {
505 // -----------------------------
506 return (history > 0);
509 // ==============================================================================================================================
511 // ==============================================================================================================================
513 public static DocumentType createType(DocumentType.Properties tprop)
514 throws MissedPropertyException, InvalidPropertyException,
515 MultiplyDefinedException, RuntimeException {
516 // ---------------------------------------------------------------------
517 // TODO: Check for duplicate definition
518 DocumentType type = new DocumentType(tprop);
519 Session session = Database.getSession();
525 public static Properties extractProperties(java.io.File file) {
526 // --------------------------------------------------------------
527 Properties fprop = new Properties();
528 Reader tool = Toolbox.getReader(file);
532 value = tool.extractProperty("title");
534 fprop.setName(value);
536 value = tool.extractProperty("reference");
538 fprop.setReference(value);
539 } catch (Exception e) {
544 @SuppressWarnings("unchecked")
545 public static List<DocumentType> selectAllTypes() {
546 // --------------------------------------------------
547 String query = "from DocumentType";
549 List<DocumentType> types = Database.getSession().createQuery(query)
551 for (Iterator<DocumentType> i = types.iterator(); i.hasNext();) {
552 Hibernate.initialize(i.next()); // Supposed fetching document types
557 @SuppressWarnings("unchecked")
558 public static List<DocumentType> selectResultTypes() {
559 // -----------------------------------------------------
560 String query = "from DocumentType where result is not null order by result asc";
562 return Database.getSession().createQuery(query).list();
565 public static DocumentType selectType(String name) {
566 // ---------------------------------------------------
567 String query = new StringBuffer("from DocumentType where name='")
568 .append(name).append("'").toString();
570 return (DocumentType) Database.getSession().createQuery(query)
574 public static DocumentType selectType(long index) {
575 // -------------------------------------------------
576 String query = new StringBuffer("from DocumentType where rid='")
577 .append(index).append("'").toString();
579 return (DocumentType) Database.getSession().createQuery(query)
583 @SuppressWarnings("unchecked")
584 public static List<DocumentType> selectTypesOf(
585 ProjectSettingsService.Step step) {
586 // --------------------------------------------------------------------------
587 Integer number = step.getNumber();
588 String query = new StringBuffer("from DocumentType").append(
589 " where step like '%-").append(number).append("-%'").toString();
591 List<DocumentType> types = Database.getSession().createQuery(query)
593 for (Iterator<DocumentType> i = types.iterator(); i.hasNext();) {
594 Hibernate.initialize(i.next()); // For fetching document types
599 // ==============================================================================================================================
600 // Protected services
601 // ==============================================================================================================================
603 protected ConvertsRelation attach(String format) {
604 // -------------------------------------------------
605 return attach(format, null);
608 protected ConvertsRelation attach(String format, String description) {
609 // ---------------------------------------------------------------------
610 String path = this.getRelativePath();
611 File export = new File(path + "." + format);
612 ConvertsRelation attach = new ConvertsRelation(this, export,
614 Session session = Database.getSession();
616 session.save(export);
617 session.save(attach);
619 this.addRelation(attach); // Updates this
624 public boolean buildReferenceFrom(ProjectElement scope, Document lineage) {
625 // -----------------------------------------------------------------------------
626 if (state != ProgressState.inWORK)
629 Scenario context = null;
630 if (scope instanceof Study)
631 owner = (Study) scope;
633 context = ((Scenario) scope);
634 owner = context.getOwnerStudy();
637 if (context != null && (lineage.isVersioned() || owner.shares(lineage))) {
638 version = new Revision(version).setBranch(context.getReference())
644 public boolean buildReferenceFrom(Study scope) {
645 // --------------------------------------------------
646 if (state != ProgressState.inWORK && state != ProgressState.EXTERN)
648 DecimalFormat tostring = new DecimalFormat(suformat);
650 did = did.replace("%" + suformat, tostring.format(scope
651 .getLastLocalIndex()));
655 public boolean demote() {
656 // ---------------------------
657 ValidationStep torem;
659 if (state == ProgressState.inCHECK) {
660 state = ProgressState.inDRAFT;
661 torem = ValidationStep.REVIEW;
662 // This operation must not change the version number of documents.
663 // Consequently, inDRAFT documents may have a minor version number equal to zero.
664 } else if (state == ProgressState.inDRAFT) {
665 state = ProgressState.inWORK;
666 torem = ValidationStep.PROMOTION;
670 for (Iterator<Relation> i = this.getAllRelations().iterator(); i
672 Relation link = i.next();
673 if (!(link instanceof StampRelation))
675 if (((StampRelation) link).getStampType() != torem)
680 Database.getSession().update(this);
685 * Increments the reference count of this document following its publication into a Study step.
690 // ----------------------
693 Database.getSession().update(this);
696 public boolean promote(Timestamp stamp) {
697 // -------------------------------------------
698 ProgressState newstate = null;
700 if (state == ProgressState.inWORK) {
701 newstate = ProgressState.inDRAFT; // Promotion to being reviewed
702 } else if (state == ProgressState.inDRAFT) {
703 newstate = ProgressState.inCHECK; // Promotion to approval
704 Revision myvers = new Revision(version);
705 if (myvers.isMinor()) {
706 version = myvers.incrementAs(newstate).toString();
707 // TODO: If my physical file is programatically editable, update its (property) version number
708 // ISSUE: What about attached files such as PDF if exist, should we remove them ?
710 } else if (state == ProgressState.inCHECK) {
711 newstate = ProgressState.APPROVED;
713 this.state = newstate;
715 this.addRelation(stamp.getContext());
716 Database.getSession().update(this);
721 * Decrements the reference count of this document following the removal of a Publication from a Study step.
725 public void release() {
726 // -------------------------
729 Database.getSession().update(this);
732 protected void rename(String title) throws InvalidPropertyException {
733 // ------------------------------------
734 if (title.length() == 0)
735 throw new InvalidPropertyException("name");
737 Calendar current = Calendar.getInstance();
739 this.lasdate = current.getTime(); // Today
740 Database.getSession().update(this);
743 public void updateAs(Revision newvers) {
744 // ------------------------------------------
745 version = newvers.setBranch(version).toString(); // Branch names are propagated by the versionning
746 ProgressState newstate = ProgressState.inCHECK;
747 if (newvers.isMinor())
748 newstate = ProgressState.inWORK;
749 state = null; // Just to tell updateAs(sate) to not increment the version number
753 public void updateAs(ProgressState state) {
754 // ---------------------------------------------
755 Document previous = null;
757 // Set of version number
758 if (state == ProgressState.EXTERN) {
759 if (this.state != ProgressState.EXTERN)
760 this.version = null; // Strange use-case...
762 Revision myvers = new Revision(version);
763 if (!myvers.isNull()) { // Versionning context
764 for (Iterator<Relation> i = getAllRelations().iterator(); i
766 Relation link = i.next();
767 if (!link.getClass().equals(VersionsRelation.class))
769 previous = (Document) link.getTo(); // Versioned document
773 if (this.state != null)
774 myvers.incrementAs(state); // Incrementation if the reversion number is not imposed
775 this.version = myvers.toString();
777 // Update this document and the previous version, if exit
778 Session session = Database.getSession();
779 if (previous != null) {
780 previous.history += 1;
781 session.update(previous);
784 session.update(this);
787 // protected void upgrade () {
788 // -------------------------
789 // if (this.state != ProgressState.inWORK) return;
791 // Calendar current = Calendar.getInstance();
792 // for (Iterator<Relation> i=getAllRelations().iterator(); i.hasNext();) {
793 // Relation link = i.next();
794 // if (!link.getClass().equals(UsesRelation.class)) continue;
796 // Document used = (Document)link.getTo();
797 // if (!used.isVersioned()) continue;
798 // TODO: Update the uses relation
801 // this.lasdate = current.getTime(); // Today
802 // Database.getSession().update(this);
804 // TODO: Promote documents using this one
810 private ProjectSettingsService getProjectSettingsService() {
811 return _projectSettingsService;
814 public void setProjectSettingsService(
815 ProjectSettingsService projectSettingsService) {
816 _projectSettingsService = projectSettingsService;
822 public StudyService getStudyService() {
823 return _studyService;
826 public void setStudyService(StudyService studyService) {
827 _studyService = studyService;
834 public int getStep() {
840 * @param step the step to set
842 public void setStep(int step) {
850 public String getDid() {
856 * @param did the did to set
858 public void setDid(String did) {
866 public File getFile() {
872 * @param myfile the myfile to set
874 public void setFile(File myfile) {
875 this.myfile = myfile;
880 * @return the history
882 public int getHistory() {
888 * @param history the history to set
890 public void setHistory(int history) {
891 this.history = history;