Salome HOME
Refactoring: configuration files are moved into Siman web project.
[tools/siman.git] / Workspace / Siman-Common / src / org / splat / som / Study.java
1 package org.splat.som;
2 /**
3  * 
4  * @author    Daniel Brunier-Coulin
5  * @copyright OPEN CASCADE 2012
6  */
7
8 import java.util.Calendar;
9 import java.util.Collections;
10 import java.util.Date;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.LinkedList;
16 import java.util.Set;
17 import java.util.Vector;
18
19 import org.hibernate.Session;
20 import org.splat.kernel.Persistent;
21 import org.splat.kernel.Relation;
22 import org.splat.kernel.User;
23 import org.splat.kernel.MultiplyDefinedException;
24 import org.splat.kernel.InvalidPropertyException;
25 import org.splat.kernel.MissedPropertyException;
26 import org.splat.kernel.UserDirectory;
27
28
29 public class Study extends ProjectElement {
30
31 //  Persistent fields
32     private String         sid;                // External unique reference in a format conform to the configuration pattern
33     private int            docount;            // Total number of documents of this study, including versions
34     private ProgressState  state;
35     private Visibility     visibility;
36     private List<Scenario> scenarii;
37     private String         version;
38     private int            history;            // Number of studies versioning this one, if any
39
40 //  Transient fields
41     private List<User>                      contributor;  // Shortcut to contributors
42     private HashMap<String,ValidationCycle> validactor;   // Shortcut to validation cycles
43     private Set<User>                       actor;        // Summary of above actors
44
45 //  ==============================================================================================================================
46 //  Construction
47 //  ==============================================================================================================================
48
49 //  Fields initialization class
50     public static class Properties extends Persistent.Properties {
51 //  ------------------------------------------------------------
52       private String                  sid        = null;                             // Search criterion only
53       private String                  title      = null;
54       private String                  summary    = null;
55       private User                    manager    = null;
56       private User                    actor      = null;                             // Search criterion only
57       private Visibility              visibility = null;                             // Search criterion only
58       private ProgressState           state      = null;                             // Search criterion only
59       private Date                    date       = null;
60       private List<SimulationContext> context    = new Vector<SimulationContext>();  // Search criterion only
61
62 //  - Public services
63
64       public void clear () {
65         super.clear();
66         sid        = null;
67         title      = null;
68         summary    = null;
69         manager    = null;
70         actor      = null;
71         visibility = null;
72         state      = null;
73         date       = null;
74         context    = new Vector<SimulationContext>();      // as clear() may generate side effects
75       }
76       public Properties copy () {
77         Properties copy = new Properties();
78         copy.sid        = this.sid;
79         copy.title      = this.title;
80         copy.summary    = this.summary;
81         copy.manager    = this.manager;
82         copy.actor      = this.actor;
83         copy.visibility = this.visibility;
84         copy.state      = this.state;
85         copy.date       = this.date;
86         copy.context    = this.context;
87         return copy;
88       }
89 //  - Protected services
90
91       protected User getActor () {
92         return actor;
93       }
94           protected User getManager () {
95                 return manager;
96           }
97           protected ProgressState getProgressState () {
98         return state;
99           }
100       protected String getReference () {
101         return sid;
102       }
103       protected List<SimulationContext> getSimulationContexts () {
104             return context;
105           }
106       protected String getTitle () {
107         return title;
108       }
109       protected Visibility getVisibility () {
110         return visibility;
111       }
112 //  - Property setters
113
114 //    For building a search query
115       public Properties setActor (User actor)
116       {
117         this.actor = actor;
118         return this;
119       }
120       public Properties setDate (Date date)
121       {
122         this.date = date;
123         return this;
124       }
125       public Properties setDescription (String summary)
126       {
127         if (summary.length() > 0) this.summary = summary;
128         return this;
129       }
130       public Properties setManager (User user)
131       {
132         this.manager = user;
133         return this;
134       }
135 //    For building a search query
136       public Properties setReference (String sid) throws InvalidPropertyException
137       {
138         if (sid.length() == 0) throw new InvalidPropertyException("reference");
139         this.sid = sid;
140         return this;
141       }
142 //    For building a search query
143       public Properties setSimulationContexts (List<SimulationContext> context) {
144         this.context = context;
145         return this;
146       }
147 //    For building a search query
148       public Properties setState (ProgressState state)
149       {
150         this.state = state;
151         return this;
152       }
153       public Properties setTitle (String title) throws InvalidPropertyException
154       {
155         if (title.length() == 0) throw new InvalidPropertyException("title");
156         this.title = title;
157         return this;
158       }
159 //    For building a search query
160       public Properties setVisibility (Visibility area)
161       {
162         this.visibility = area;
163         return this;
164       }
165 //  - Global validity check
166       
167       public void checkValidity() throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException
168       {
169         if (title == null)   throw new MissedPropertyException("title");
170         if (manager == null) throw new MissedPropertyException("manager");
171       }
172     }
173 //  Database fetch constructor
174     protected Study () {
175 //  ------------------
176       contributor = null;
177       validactor  = null;
178       actor       = null;
179     }
180 //  Internal constructor
181     protected Study (Properties sprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException {
182 //  ----------------------------------
183       super(sprop);                 // Throws one of the above exception if not valid
184       sid        = ProjectSettings.getReferencePattern();   // Reset after save
185       title      = sprop.title;                             // Inherited attribute
186       manager    = sprop.manager;
187       docount    = 0;
188       history    = 0;
189       scenarii   = new LinkedList<Scenario>();
190       visibility = Visibility.PRIVATE;
191       state      = ProgressState.inWORK;
192
193       credate = sprop.date;                                 // Inherited attribute
194       if (credate == null) {
195         Calendar current = Calendar.getInstance();
196         credate = current.getTime();                        // Today
197       }
198       lasdate  = credate;                                   // Inherited attribute
199       version  = new Revision().incrementAs(state).toString();
200
201       if (sprop.summary != null) this.setAttribute( new DescriptionAttribute(this, sprop.summary) );
202
203       contributor = null;
204       validactor  = null;
205       actor       = null;
206     }
207
208 //  ==============================================================================================================================
209 //  Public member functions
210 //  ==============================================================================================================================
211
212     public boolean addContributor (User user) {
213 //  -----------------------------------------
214       if (contributor == null) this.setShortCuts();   // Initializes contributor
215       for (Iterator<User> i=contributor.iterator(); i.hasNext(); ) {
216         User present = i.next();
217         if ( present.equals(user) ) return false;
218       }
219       boolean  absent = actor.add(user);              // User may already be a reviewer or an approver
220
221       this.addRelation( new ContributorRelation(this, user) );
222       if (absent) updateMe();                         // Else, useless to re-index the study
223       contributor.add(user);
224       return true;
225     }
226
227     public SimulationContext addProjectContext (SimulationContext.Properties cprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException, RuntimeException {
228 //  -------------------------------------------------------------------------------
229       SimulationContext  added = this.getFirstStep().addSimulationContext(cprop);      
230       updateMe();
231       return  added;
232     }
233
234     public SimulationContext addProjectContext (SimulationContext context) {
235 //  ----------------------------------------------------------------------
236       SimulationContext  added = this.getFirstStep().addSimulationContext(context);      
237       updateMe();
238       return  added;
239     }
240
241     public Scenario addScenario (Scenario.Properties sprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException, RuntimeException {
242 //  -------------------------------------------------------
243       if (sprop.getManager() == null) sprop.setManager(this.manager);
244
245       Scenario  scenario = new Scenario(sprop.setOwnerStudy(this));
246       Scenario  previous = sprop.getInsertAfter();
247       Session   session  = Database.getSession();
248
249       if (previous == null) {
250         scenarii.add(scenario);
251       } else {
252         scenarii.add(scenarii.indexOf(previous)+1, scenario);
253       }
254       session.update(this);       // No need to update the Lucene index
255       session.save(scenario);     // Must be done after updating this study because of the back reference to the study
256       if (sprop.getBaseStep() != null) {
257 //      No need to update the Knowledge Element index as Knowledge Elements are not copied
258         scenario.refresh();       // Because saving the scenario changes the hashcode of copied Publications
259       }
260       KnowledgeElementType         ucase = KnowledgeElement.selectType("usecase");
261           KnowledgeElement.Properties  kprop = new KnowledgeElement.Properties();
262           User                         admin = UserDirectory.selectUser(1);   // First user created when creating the database
263       kprop.setType(ucase)
264            .setTitle(this.getTitle())
265            .setValue(scenario.getTitle())
266            .setAuthor(admin);                                             // Internal Knowledge Element required by the validation process of knowledges
267       scenario.addKnowledgeElement(kprop);
268       return scenario;
269     }
270
271 /**
272  * Returns all actors of this study other than the author, including contributors, reviewers and approvers.
273  * 
274  * @return the actors of this study
275  * @see    #hasActor(User)
276  */
277     public Set<User> getActors () {
278 //  -----------------------------
279       if (actor == null) setShortCuts();
280       return Collections.unmodifiableSet(actor);
281     }
282
283     public List<User> getContributors () {
284 //  ------------------------------------
285       if (contributor == null) setShortCuts();
286       return Collections.unmodifiableList(contributor);     // May be empty
287     }
288
289     public ProgressState getProgressState () {
290 //  ----------------------------------------
291       return state;
292     }
293
294 /**
295  * Returns the global unique reference of this study.
296  * The study reference is common to all versions of the study (versioning a study does not change its reference).
297  * The form of this study reference is defined in the configuration of the application server - see the SOM XML customization
298  * file.
299  */
300     public String getReference () {
301 //  -----------------------------
302       return sid;
303     }
304
305     public Scenario[] getScenarii () {
306 //  --------------------------------
307       return  scenarii.toArray(new Scenario[scenarii.size()]);
308     }
309
310 /**
311  * Returns the validation cycle of the given document type.
312  * 
313  * @param doc the document type being subject of validation
314  * @return     the validation cycle of the document, or null if not defined.
315  */
316     public ValidationCycle getValidationCycleOf (DocumentType type) {
317 //  ---------------------------------------------------------------
318       if (validactor == null)    setShortCuts();
319       ValidationCycle            result = validactor.get(type.getName());
320       if (result == null) {
321         if (type.isStepResult()) result = validactor.get("default");             // "default" validation cycle defined in the configuration, if exist
322         if (result == null)      result = validactor.get("built-in");
323       }
324       return result;
325     }
326
327     public String getVersion () {
328 //  ---------------------------
329       return version;
330     }
331
332     public Visibility getVisibility () {
333 //  ----------------------------------
334       return visibility;
335     }
336
337 /**
338  * Checks if the given user is actor of this study.
339  * Actors include contributors, reviewers and approvers.
340  * 
341  * @return true if the given user is actor of this study.
342  * @see    #getActors()
343  */
344     public boolean hasActor (User user) {
345 //  -----------------------------------
346       if (user == null) return false;
347       for (Iterator<User> i=this.getActors().iterator(); i.hasNext(); ) {
348         User involved = i.next();
349         if  (involved.equals(user))  return true;
350           }
351       return false;
352     }
353
354 /**
355  * Checks whether this study is in the Public or the Reference area of the repository.
356  * 
357  * @return true if the study is public.
358  * @see    #moveToPublic()
359  * @see    #moveToReference()
360  */
361     public boolean isPublic () {
362 //  --------------------------
363       return (visibility != Visibility.PRIVATE);
364     }
365 /**
366  * Checks if the given user participates to this study.
367  * The Study staff includes the author and contributors.
368  * 
369  * @return true if the given user is actor of this study.
370  * @see    #getContributors()
371  */
372     public boolean isStaffedBy (User user) {
373 //  --------------------------------------
374       if (user == null)         return false;
375       if (manager.equals(user)) return true;
376       for (Iterator<User> i=getContributors().iterator(); i.hasNext();) {
377         if (i.next().equals(user)) return true;
378       }
379       return false;
380     }
381
382     public boolean isVersioned () {
383 //  -----------------------------
384       return (history > 0);
385     }
386
387 /**
388  * Moves this study from the Private to the Public area of the repository.
389  * 
390  * @return true if the move succeeded.
391  * @see    #isPublic()
392  */
393     public boolean moveToPublic () {
394 //  ------------------------------
395       if  (visibility != Visibility.PRIVATE) return false;
396
397       this.visibility = Visibility.PUBLIC;
398       if ( updateMe() ) {
399         return updateKnowledgeElementsIndex();   // If fails, the database roll-back is under responsibility of the caller
400       }
401       return false;
402     }
403
404 /**
405  * Moves this study from the Public to the Reference area of the repository.
406  * For being moved to the Reference area, the study must previously be approved.
407  * 
408  * @return true if the move succeeded.
409  * @see    #moveToPublic()
410  * @see    #isPublic()
411  * @see    Publication#approve(Date)
412  */
413     public boolean moveToReference () {
414 //  ---------------------------------
415       if (state      != ProgressState.APPROVED) return false;
416       if (visibility != Visibility.PUBLIC) return false;
417
418       this.visibility = Visibility.REFERENCE;
419       if ( updateMe() ) {
420         return updateKnowledgeElementsIndex();   // If fails, the database roll-back is under responsibility of the caller
421       }
422       return false;
423     }
424
425     public boolean publishes (Document doc) {
426 //  ---------------------------------------
427       if (!super.publishes(doc)) {
428         Scenario[] scene = this.getScenarii();
429         for (int i=0; i<scene.length; i++) {
430           if (scene[i].publishes(doc)) return true;
431         }
432       }
433       return false;
434     }
435
436     public boolean removeContributor (User... users) {
437 //  ------------------------------------------------
438       if (contributor == null) this.setShortCuts();   // Initializes contributor
439       Boolean  done = false;
440       for (int i=0; i<users.length; i++) {
441         User user = users[i];
442         for (Iterator<User> j=contributor.iterator(); j.hasNext(); ) {
443           User present = j.next();
444           if (!present.equals(user)) continue;
445
446           this.removeRelation(ContributorRelation.class, user);
447           j.remove();                                 // Updates the contributor shortcut
448           done = true;
449           break;
450         }
451       }
452       if (done) updateMe();
453       return done;
454     }
455
456     public boolean removeProjectContext (SimulationContext context) {
457 //  ---------------------------------------------------------------
458       boolean  done = this.getFirstStep().removeSimulationContext(context);
459       updateMe();
460       return  done;
461     }
462
463     public void setValidationCycle (DocumentType type, ValidationCycle.Properties vprop) {
464 //  ------------------------------------------------------------------------------------
465       if (validactor == null) setShortCuts();             // Initializes validactor and actor
466
467       String          cname = type.getName();
468       ValidationCycle cycle = validactor.get(cname);
469
470       if (cycle != null && cycle.isAssigned()) {
471           cycle.resetActors(vprop);
472       } else
473       try {
474                   cycle = new ValidationCycle(this, vprop.setDocumentType(type));
475
476               ValidationCycleRelation link = cycle.getContext();
477                   this.addRelation(link);
478               validactor.put(cname, link.getTo());            // Replaces the cycle if exists as default, 
479           }
480       catch (Exception error) {
481               logger.error("Unable to re-index Knowledge Elements, reason:", error);
482               return;
483           }
484       resetActorsShortCut();
485       updateMe();                                         // Re-index the study, just in case
486     }
487
488     public boolean shares (Document doc) {
489 //  ------------------------------------
490       Scenario[] scene   = this.getScenarii();            // If shared from within the study, the document is shared by the scenarios
491       int        counter = 0;
492
493           for (int i=0; i<scene.length; i++) {
494         if (!scene[i].publishes(doc)) continue;
495         if (counter == 1)          return true;
496         counter += 1;
497           }
498       return false;
499     }
500
501     public boolean update (Properties sprop) throws InvalidPropertyException {
502 //  ----------------------------------------
503       if (sprop.title   != null) this.title   = sprop.title;
504       if (sprop.summary != null) this.setAttribute( new DescriptionAttribute(this, sprop.summary) );
505 //TODO: To be completed
506       return  updateMe();
507     }
508     
509 //  ==============================================================================================================================
510 //  Protected services
511 //  ==============================================================================================================================
512
513     protected boolean buildReference () {
514 //  -----------------------------------
515       String    pattern = getReference();   // The study being supposed just created, its reference is the reference pattern
516       IDBuilder tool    = Database.selectIDBuilder(credate);
517       if (tool == null) {
518         tool = new IDBuilder(credate);
519         Database.getSession().save(tool);
520       }
521       this.sid = tool.buildReference(pattern, this);
522       return true;
523     }
524
525 /**
526  * Demotes this study from In-Check to In-Draft then In-Work states.
527  * This function is called internally when demoting the final result document of the study.
528  * 
529  * @return true if the demotion succeeded.
530  */
531     protected boolean demote () {
532 //  ---------------------------
533       if      (state == ProgressState.inCHECK) state = ProgressState.inDRAFT;
534       else if (state == ProgressState.inDRAFT) state = ProgressState.inWORK;
535       else return false;
536       return  updateMe();
537     }
538
539     protected int generateLocalIndex () {
540 //  -----------------------------------
541       docount = docount + 1;
542       Database.getSession().update(this);
543       return  docount;
544     }
545
546     protected int getLastLocalIndex () {
547 //  ----------------------------------
548       return  docount;
549     }
550
551     protected void loadWorkflow () {
552 //  ------------------------------
553       setShortCuts();
554     }
555 /**
556  * Promotes this study from In-Work to In-Draft then In-Check and APPROVED states.
557  * This function is called internally when promoting the final result document of the study.
558  * 
559  * @return true if the demotion succeeded.
560  */
561     protected boolean promote () {
562 //  ----------------------------
563       if (state == ProgressState.inWORK) {
564         state = ProgressState.inDRAFT;
565       } else
566       if (state == ProgressState.inDRAFT) {
567         this.state      = ProgressState.inCHECK;
568             Revision myvers = new Revision(version);
569         if (myvers.isMinor()) {
570                   version = myvers.incrementAs(state).toString();
571         }
572       } else
573       if (state == ProgressState.inCHECK) {
574         state = ProgressState.APPROVED;
575       }
576       else return false;
577
578       return  updateMe();
579     }
580
581 //  ==============================================================================================================================
582 //  Private member functions
583 //  ==============================================================================================================================
584
585     private void resetActorsShortCut () {
586 //  -----------------------------------
587       actor.clear();
588 //    Get all actors involved in validation cycles
589       for (Iterator<ValidationCycle> i=validactor.values().iterator(); i.hasNext(); ) {
590         ValidationCycle cycle = i.next();
591         User[]          user  = cycle.getAllActors();
592         for (int j=0; j<user.length; j++) actor.add(user[j]);
593       }
594 //    Get all other actors
595       for (Iterator<Relation> i=this.getAllRelations().iterator(); i.hasNext(); ) {
596         Relation link   = i.next();
597         Class<?> kindof = link.getClass().getSuperclass();
598         if (!kindof.equals(ActorRelation.class)) continue;
599         actor.add( ((ActorRelation)link).getTo() );
600       }
601     }
602
603     private void setShortCuts () {
604 //  ----------------------------
605       contributor = new Vector<User>();
606       validactor  = new HashMap<String,ValidationCycle>();
607       actor       = new HashSet<User>();
608
609 //    Get the contributors
610       for (Iterator<Relation> i=getRelations(ContributorRelation.class).iterator(); i.hasNext(); ) {
611         ContributorRelation  link = (ContributorRelation)i.next();
612         contributor.add(link.getTo());
613       }
614 //    Get the validation cycles specific to this study
615       for (Iterator<Relation> i=getRelations(ValidationCycleRelation.class).iterator(); i.hasNext(); ) {
616         ValidationCycleRelation  link = (ValidationCycleRelation)i.next();
617         validactor.put(link.getDocumentType().getName(), link.getTo());   // The associated document type is necessarily not null in this context
618       }
619 //    Get the validation cycles coming from the configured workflow and not overridden in this study
620       for (Iterator<ProjectSettings.ValidationCycle> i=ProjectSettings.getAllValidationCycles().iterator(); i.hasNext(); ) {
621         ProjectSettings.ValidationCycle cycle = i.next();
622         String                          type  = cycle.getName();
623         if (!validactor.containsKey(type)) validactor.put(type, new ValidationCycle(this, cycle));
624       }
625 //    Get all corresponding actors
626       for (Iterator<ValidationCycle> i=validactor.values().iterator(); i.hasNext(); ) {
627         ValidationCycle cycle = i.next();
628         User[]          user  = cycle.getAllActors();
629         for (int j=0; j<user.length; j++) actor.add(user[j]);
630       }
631 //    Get all other actors
632       for (Iterator<Relation> i=this.getAllRelations().iterator(); i.hasNext(); ) {
633         Relation link   = i.next();
634         Class<?> kindof = link.getClass().getSuperclass();
635         if (!kindof.equals(ActorRelation.class)) continue;
636         actor.add( ((ActorRelation)link).getTo() );
637       }
638     }
639
640     private boolean updateKnowledgeElementsIndex() {
641 //  ----------------------------------------------
642       try {
643         Index lucin = Database.getIndex();
644
645         for (Iterator<Scenario> i=scenarii.iterator(); i.hasNext(); ) {
646           Scenario  scene = i.next();
647           for (Iterator<KnowledgeElement> j=scene.getAllKnowledgeElements().iterator(); j.hasNext(); ) {
648             KnowledgeElement kelm = j.next();
649             lucin.update(kelm);
650           }
651         }
652         return true;
653       }
654       catch (Exception error) {
655         logger.error("Unable to re-index Knowledge Elements, reason:", error);
656         return false;
657       }
658     }
659
660     private boolean updateMe () {
661 //  ---------------------------
662       try {      
663         Database.getSession().update(this);   // Update of relational base
664         Database.getIndex().update(this);     // Update of Lucene index
665         return true;
666       }
667       catch (Exception error) {
668         logger.error("Unable to re-index the study '" + getIndex() + "', reason:", error);
669         return false;
670       }
671     }
672 }