4 * @author Daniel Brunier-Coulin
5 * @copyright OPEN CASCADE 2012
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;
17 import java.util.Vector;
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;
29 public class Study extends ProjectElement {
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
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
45 // ==============================================================================================================================
47 // ==============================================================================================================================
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
64 public void clear () {
74 context = new Vector<SimulationContext>(); // as clear() may generate side effects
76 public Properties copy () {
77 Properties copy = new Properties();
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;
89 // - Protected services
91 protected User getActor () {
94 protected User getManager () {
97 protected ProgressState getProgressState () {
100 protected String getReference () {
103 protected List<SimulationContext> getSimulationContexts () {
106 protected String getTitle () {
109 protected Visibility getVisibility () {
112 // - Property setters
114 // For building a search query
115 public Properties setActor (User actor)
120 public Properties setDate (Date date)
125 public Properties setDescription (String summary)
127 if (summary.length() > 0) this.summary = summary;
130 public Properties setManager (User user)
135 // For building a search query
136 public Properties setReference (String sid) throws InvalidPropertyException
138 if (sid.length() == 0) throw new InvalidPropertyException("reference");
142 // For building a search query
143 public Properties setSimulationContexts (List<SimulationContext> context) {
144 this.context = context;
147 // For building a search query
148 public Properties setState (ProgressState state)
153 public Properties setTitle (String title) throws InvalidPropertyException
155 if (title.length() == 0) throw new InvalidPropertyException("title");
159 // For building a search query
160 public Properties setVisibility (Visibility area)
162 this.visibility = area;
165 // - Global validity check
167 public void checkValidity() throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException
169 if (title == null) throw new MissedPropertyException("title");
170 if (manager == null) throw new MissedPropertyException("manager");
173 // Database fetch constructor
175 // ------------------
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;
189 scenarii = new LinkedList<Scenario>();
190 visibility = Visibility.PRIVATE;
191 state = ProgressState.inWORK;
193 credate = sprop.date; // Inherited attribute
194 if (credate == null) {
195 Calendar current = Calendar.getInstance();
196 credate = current.getTime(); // Today
198 lasdate = credate; // Inherited attribute
199 version = new Revision().incrementAs(state).toString();
201 if (sprop.summary != null) this.setAttribute( new DescriptionAttribute(this, sprop.summary) );
208 // ==============================================================================================================================
209 // Public member functions
210 // ==============================================================================================================================
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;
219 boolean absent = actor.add(user); // User may already be a reviewer or an approver
221 this.addRelation( new ContributorRelation(this, user) );
222 if (absent) updateMe(); // Else, useless to re-index the study
223 contributor.add(user);
227 public SimulationContext addProjectContext (SimulationContext.Properties cprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException, RuntimeException {
228 // -------------------------------------------------------------------------------
229 SimulationContext added = this.getFirstStep().addSimulationContext(cprop);
234 public SimulationContext addProjectContext (SimulationContext context) {
235 // ----------------------------------------------------------------------
236 SimulationContext added = this.getFirstStep().addSimulationContext(context);
241 public Scenario addScenario (Scenario.Properties sprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException, RuntimeException {
242 // -------------------------------------------------------
243 if (sprop.getManager() == null) sprop.setManager(this.manager);
245 Scenario scenario = new Scenario(sprop.setOwnerStudy(this));
246 Scenario previous = sprop.getInsertAfter();
247 Session session = Database.getSession();
249 if (previous == null) {
250 scenarii.add(scenario);
252 scenarii.add(scenarii.indexOf(previous)+1, scenario);
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
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
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);
272 * Returns all actors of this study other than the author, including contributors, reviewers and approvers.
274 * @return the actors of this study
275 * @see #hasActor(User)
277 public Set<User> getActors () {
278 // -----------------------------
279 if (actor == null) setShortCuts();
280 return Collections.unmodifiableSet(actor);
283 public List<User> getContributors () {
284 // ------------------------------------
285 if (contributor == null) setShortCuts();
286 return Collections.unmodifiableList(contributor); // May be empty
289 public ProgressState getProgressState () {
290 // ----------------------------------------
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
300 public String getReference () {
301 // -----------------------------
305 public Scenario[] getScenarii () {
306 // --------------------------------
307 return scenarii.toArray(new Scenario[scenarii.size()]);
311 * Returns the validation cycle of the given document type.
313 * @param doc the document type being subject of validation
314 * @return the validation cycle of the document, or null if not defined.
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");
327 public String getVersion () {
328 // ---------------------------
332 public Visibility getVisibility () {
333 // ----------------------------------
338 * Checks if the given user is actor of this study.
339 * Actors include contributors, reviewers and approvers.
341 * @return true if the given user is actor of this study.
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;
355 * Checks whether this study is in the Public or the Reference area of the repository.
357 * @return true if the study is public.
358 * @see #moveToPublic()
359 * @see #moveToReference()
361 public boolean isPublic () {
362 // --------------------------
363 return (visibility != Visibility.PRIVATE);
366 * Checks if the given user participates to this study.
367 * The Study staff includes the author and contributors.
369 * @return true if the given user is actor of this study.
370 * @see #getContributors()
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;
382 public boolean isVersioned () {
383 // -----------------------------
384 return (history > 0);
388 * Moves this study from the Private to the Public area of the repository.
390 * @return true if the move succeeded.
393 public boolean moveToPublic () {
394 // ------------------------------
395 if (visibility != Visibility.PRIVATE) return false;
397 this.visibility = Visibility.PUBLIC;
399 return updateKnowledgeElementsIndex(); // If fails, the database roll-back is under responsibility of the caller
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.
408 * @return true if the move succeeded.
409 * @see #moveToPublic()
411 * @see Publication#approve(Date)
413 public boolean moveToReference () {
414 // ---------------------------------
415 if (state != ProgressState.APPROVED) return false;
416 if (visibility != Visibility.PUBLIC) return false;
418 this.visibility = Visibility.REFERENCE;
420 return updateKnowledgeElementsIndex(); // If fails, the database roll-back is under responsibility of the caller
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;
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;
446 this.removeRelation(ContributorRelation.class, user);
447 j.remove(); // Updates the contributor shortcut
452 if (done) updateMe();
456 public boolean removeProjectContext (SimulationContext context) {
457 // ---------------------------------------------------------------
458 boolean done = this.getFirstStep().removeSimulationContext(context);
463 public void setValidationCycle (DocumentType type, ValidationCycle.Properties vprop) {
464 // ------------------------------------------------------------------------------------
465 if (validactor == null) setShortCuts(); // Initializes validactor and actor
467 String cname = type.getName();
468 ValidationCycle cycle = validactor.get(cname);
470 if (cycle != null && cycle.isAssigned()) {
471 cycle.resetActors(vprop);
474 cycle = new ValidationCycle(this, vprop.setDocumentType(type));
476 ValidationCycleRelation link = cycle.getContext();
477 this.addRelation(link);
478 validactor.put(cname, link.getTo()); // Replaces the cycle if exists as default,
480 catch (Exception error) {
481 logger.error("Unable to re-index Knowledge Elements, reason:", error);
484 resetActorsShortCut();
485 updateMe(); // Re-index the study, just in case
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
493 for (int i=0; i<scene.length; i++) {
494 if (!scene[i].publishes(doc)) continue;
495 if (counter == 1) return true;
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
509 // ==============================================================================================================================
510 // Protected services
511 // ==============================================================================================================================
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);
518 tool = new IDBuilder(credate);
519 Database.getSession().save(tool);
521 this.sid = tool.buildReference(pattern, this);
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.
529 * @return true if the demotion succeeded.
531 protected boolean demote () {
532 // ---------------------------
533 if (state == ProgressState.inCHECK) state = ProgressState.inDRAFT;
534 else if (state == ProgressState.inDRAFT) state = ProgressState.inWORK;
539 protected int generateLocalIndex () {
540 // -----------------------------------
541 docount = docount + 1;
542 Database.getSession().update(this);
546 protected int getLastLocalIndex () {
547 // ----------------------------------
551 protected void loadWorkflow () {
552 // ------------------------------
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.
559 * @return true if the demotion succeeded.
561 protected boolean promote () {
562 // ----------------------------
563 if (state == ProgressState.inWORK) {
564 state = ProgressState.inDRAFT;
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();
573 if (state == ProgressState.inCHECK) {
574 state = ProgressState.APPROVED;
581 // ==============================================================================================================================
582 // Private member functions
583 // ==============================================================================================================================
585 private void resetActorsShortCut () {
586 // -----------------------------------
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]);
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() );
603 private void setShortCuts () {
604 // ----------------------------
605 contributor = new Vector<User>();
606 validactor = new HashMap<String,ValidationCycle>();
607 actor = new HashSet<User>();
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());
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
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));
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]);
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() );
640 private boolean updateKnowledgeElementsIndex() {
641 // ----------------------------------------------
643 Index lucin = Database.getIndex();
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();
654 catch (Exception error) {
655 logger.error("Unable to re-index Knowledge Elements, reason:", error);
660 private boolean updateMe () {
661 // ---------------------------
663 Database.getSession().update(this); // Update of relational base
664 Database.getIndex().update(this); // Update of Lucene index
667 catch (Exception error) {
668 logger.error("Unable to re-index the study '" + getIndex() + "', reason:", error);