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 File getSourceFile() {
395 // ----------------------------
400 * Returns the stamps such as review and approval attached to this document, if exist. If several stamps exist, they are returned in
401 * ascending order of dates.
403 * @return the stamps of the document in ascending order of dates, or an empty array if no stamp exist.
405 public Timestamp[] getStamps() {
406 // -------------------------------
407 Vector<Timestamp> stamps = new Vector<Timestamp>();
409 for (Iterator<Relation> i = this.getAllRelations().iterator(); i
411 Relation link = i.next();
412 if (link instanceof StampRelation)
413 stamps.add(((StampRelation) link).getTo());
415 Timestamp[] result = stamps.toArray(new Timestamp[stamps.size()]);
416 ComparatorByDate bydate = new Timestamp.ComparatorByDate();
418 Arrays.sort(result, bydate);
423 * Returns the title of this document.
425 * @return the document title, or an empty string is this document is undefined.
426 * @see #isUndefined()
428 public String getTitle() {
429 // -------------------------
430 if (this.isUndefined())
437 * Set document title.
438 * @param aTitle document title to set
440 public void setTitle(String aTitle) {
444 public DocumentType getType() {
445 // ------------------------------
450 * Returns the version number of this document. The version number, when exists, is either of the internal form (m.n.s) usable for
451 * building a Revision object, or any string in case of external document (document with EXTERN state).<br/> <br/> Note: document slots
452 * have a version number equal to "0.0.0".
454 * @return the version number of this document, or null if this is EXTERN.
455 * @see #isUndefined()
457 public String getVersion() {
458 // ---------------------------
463 * Returns true if this document is undefined. An undefined document is a meta-document created for reserving the persistent reference
464 * of a new document before saving (or importing) this later into the repository. The working copy of a such document may include this
469 * @see #initialize(Properties)
471 public boolean isUndefined() {
472 // -----------------------------
473 return (history == -1);
476 public boolean isInto(Step container) {
477 // --------------------------------------
478 return (step == container.getNumber());
481 public boolean isPublished() {
482 // -----------------------------
483 return (countag > 0);
486 public boolean isShared() {
487 // --------------------------
488 return (countag + history > 1);
491 public boolean isVersioned() {
492 // -----------------------------
493 return (history > 0);
496 // ==============================================================================================================================
498 // ==============================================================================================================================
500 public static DocumentType createType(DocumentType.Properties tprop)
501 throws MissedPropertyException, InvalidPropertyException,
502 MultiplyDefinedException, RuntimeException {
503 // ---------------------------------------------------------------------
504 // TODO: Check for duplicate definition
505 DocumentType type = new DocumentType(tprop);
506 Session session = Database.getSession();
512 public static Properties extractProperties(java.io.File file) {
513 // --------------------------------------------------------------
514 Properties fprop = new Properties();
515 Reader tool = Toolbox.getReader(file);
519 value = tool.extractProperty("title");
521 fprop.setName(value);
523 value = tool.extractProperty("reference");
525 fprop.setReference(value);
526 } catch (Exception e) {
531 @SuppressWarnings("unchecked")
532 public static List<DocumentType> selectAllTypes() {
533 // --------------------------------------------------
534 String query = "from DocumentType";
536 List<DocumentType> types = Database.getSession().createQuery(query)
538 for (Iterator<DocumentType> i = types.iterator(); i.hasNext();) {
539 Hibernate.initialize(i.next()); // Supposed fetching document types
544 @SuppressWarnings("unchecked")
545 public static List<DocumentType> selectResultTypes() {
546 // -----------------------------------------------------
547 String query = "from DocumentType where result is not null order by result asc";
549 return Database.getSession().createQuery(query).list();
552 public static DocumentType selectType(String name) {
553 // ---------------------------------------------------
554 String query = new StringBuffer("from DocumentType where name='")
555 .append(name).append("'").toString();
557 return (DocumentType) Database.getSession().createQuery(query)
561 public static DocumentType selectType(long index) {
562 // -------------------------------------------------
563 String query = new StringBuffer("from DocumentType where rid='")
564 .append(index).append("'").toString();
566 return (DocumentType) Database.getSession().createQuery(query)
570 @SuppressWarnings("unchecked")
571 public static List<DocumentType> selectTypesOf(
572 ProjectSettingsService.Step step) {
573 // --------------------------------------------------------------------------
574 Integer number = step.getNumber();
575 String query = new StringBuffer("from DocumentType").append(
576 " where step like '%-").append(number).append("-%'").toString();
578 List<DocumentType> types = Database.getSession().createQuery(query)
580 for (Iterator<DocumentType> i = types.iterator(); i.hasNext();) {
581 Hibernate.initialize(i.next()); // For fetching document types
586 // ==============================================================================================================================
587 // Protected services
588 // ==============================================================================================================================
590 protected ConvertsRelation attach(String format) {
591 // -------------------------------------------------
592 return attach(format, null);
595 protected ConvertsRelation attach(String format, String description) {
596 // ---------------------------------------------------------------------
597 String path = this.getRelativePath();
598 File export = new File(path + "." + format);
599 ConvertsRelation attach = new ConvertsRelation(this, export,
601 Session session = Database.getSession();
603 session.save(export);
604 session.save(attach);
606 this.addRelation(attach); // Updates this
611 public boolean buildReferenceFrom(ProjectElement scope, Document lineage) {
612 // -----------------------------------------------------------------------------
613 if (state != ProgressState.inWORK)
616 Scenario context = null;
617 if (scope instanceof Study)
618 owner = (Study) scope;
620 context = ((Scenario) scope);
621 owner = context.getOwnerStudy();
624 if (context != null && (lineage.isVersioned() || owner.shares(lineage))) {
625 version = new Revision(version).setBranch(context.getReference())
631 public boolean buildReferenceFrom(Study scope) {
632 // --------------------------------------------------
633 if (state != ProgressState.inWORK && state != ProgressState.EXTERN)
635 DecimalFormat tostring = new DecimalFormat(suformat);
637 did = did.replace("%" + suformat, tostring.format(scope
638 .getLastLocalIndex()));
642 public boolean demote() {
643 // ---------------------------
644 ValidationStep torem;
646 if (state == ProgressState.inCHECK) {
647 state = ProgressState.inDRAFT;
648 torem = ValidationStep.REVIEW;
649 // This operation must not change the version number of documents.
650 // Consequently, inDRAFT documents may have a minor version number equal to zero.
651 } else if (state == ProgressState.inDRAFT) {
652 state = ProgressState.inWORK;
653 torem = ValidationStep.PROMOTION;
657 for (Iterator<Relation> i = this.getAllRelations().iterator(); i
659 Relation link = i.next();
660 if (!(link instanceof StampRelation))
662 if (((StampRelation) link).getStampType() != torem)
667 Database.getSession().update(this);
672 * Increments the reference count of this document following its publication into a Study step.
677 // ----------------------
680 Database.getSession().update(this);
683 public boolean promote(Timestamp stamp) {
684 // -------------------------------------------
685 ProgressState newstate = null;
687 if (state == ProgressState.inWORK) {
688 newstate = ProgressState.inDRAFT; // Promotion to being reviewed
689 } else if (state == ProgressState.inDRAFT) {
690 newstate = ProgressState.inCHECK; // Promotion to approval
691 Revision myvers = new Revision(version);
692 if (myvers.isMinor()) {
693 version = myvers.incrementAs(newstate).toString();
694 // TODO: If my physical file is programatically editable, update its (property) version number
695 // ISSUE: What about attached files such as PDF if exist, should we remove them ?
697 } else if (state == ProgressState.inCHECK) {
698 newstate = ProgressState.APPROVED;
700 this.state = newstate;
702 this.addRelation(stamp.getContext());
703 Database.getSession().update(this);
708 * Decrements the reference count of this document following the removal of a Publication from a Study step.
712 public void release() {
713 // -------------------------
716 Database.getSession().update(this);
719 protected void rename(String title) throws InvalidPropertyException {
720 // ------------------------------------
721 if (title.length() == 0)
722 throw new InvalidPropertyException("name");
724 Calendar current = Calendar.getInstance();
726 this.lasdate = current.getTime(); // Today
727 Database.getSession().update(this);
730 public void updateAs(Revision newvers) {
731 // ------------------------------------------
732 version = newvers.setBranch(version).toString(); // Branch names are propagated by the versionning
733 ProgressState newstate = ProgressState.inCHECK;
734 if (newvers.isMinor())
735 newstate = ProgressState.inWORK;
736 state = null; // Just to tell updateAs(sate) to not increment the version number
740 public void updateAs(ProgressState state) {
741 // ---------------------------------------------
742 Document previous = null;
744 // Set of version number
745 if (state == ProgressState.EXTERN) {
746 if (this.state != ProgressState.EXTERN)
747 this.version = null; // Strange use-case...
749 Revision myvers = new Revision(version);
750 if (!myvers.isNull()) { // Versionning context
751 for (Iterator<Relation> i = getAllRelations().iterator(); i
753 Relation link = i.next();
754 if (!link.getClass().equals(VersionsRelation.class))
756 previous = (Document) link.getTo(); // Versioned document
760 if (this.state != null)
761 myvers.incrementAs(state); // Incrementation if the reversion number is not imposed
762 this.version = myvers.toString();
764 // Update this document and the previous version, if exit
765 Session session = Database.getSession();
766 if (previous != null) {
767 previous.history += 1;
768 session.update(previous);
771 session.update(this);
774 // protected void upgrade () {
775 // -------------------------
776 // if (this.state != ProgressState.inWORK) return;
778 // Calendar current = Calendar.getInstance();
779 // for (Iterator<Relation> i=getAllRelations().iterator(); i.hasNext();) {
780 // Relation link = i.next();
781 // if (!link.getClass().equals(UsesRelation.class)) continue;
783 // Document used = (Document)link.getTo();
784 // if (!used.isVersioned()) continue;
785 // TODO: Update the uses relation
788 // this.lasdate = current.getTime(); // Today
789 // Database.getSession().update(this);
791 // TODO: Promote documents using this one
797 private ProjectSettingsService getProjectSettingsService() {
798 return _projectSettingsService;
801 public void setProjectSettingsService(
802 ProjectSettingsService projectSettingsService) {
803 _projectSettingsService = projectSettingsService;
809 public StudyService getStudyService() {
810 return _studyService;
813 public void setStudyService(StudyService studyService) {
814 _studyService = studyService;
821 public int getStep() {
827 * @param step the step to set
829 public void setStep(int step) {
837 public String getDid() {
843 * @param did the did to set
845 public void setDid(String did) {
853 public File getFile() {
859 * @param myfile the myfile to set
861 public void setFile(File myfile) {
862 this.myfile = myfile;
867 * @return the history
869 public int getHistory() {
875 * @param history the history to set
877 public void setHistory(int history) {
878 this.history = history;