From e2ce0069b9e8edf8719ff26b2b1a68498d108fbc Mon Sep 17 00:00:00 2001 From: rkv Date: Tue, 9 Oct 2012 11:46:46 +0000 Subject: [PATCH] Siman codebase is refactored. Spring beans are introduced in the context. --- .../com.bea.workshop.common.upgrade.log.xml | 5 + Workspace/SPlat/src/META-INF/MANIFEST.MF | 3 + Workspace/Siman-Common/.classpath | 4 +- Workspace/Siman-Common/.springBeans | 14 + Workspace/Siman-Common/lib/aopalliance.jar | Bin 0 -> 4467 bytes Workspace/Siman-Common/lib/p6spy.jar | Bin 0 -> 106577 bytes .../org/splat/{ => dal/bo}/kernel/Any.hbm.xml | 17 +- .../org/splat/{ => dal/bo}/kernel/Any.java | 10 +- .../{ => dal/bo}/kernel/Attribute.hbm.xml | 4 +- .../splat/{ => dal/bo}/kernel/Attribute.java | 2 +- .../splat/{ => dal/bo}/kernel/Entity.hbm.xml | 20 +- .../org/splat/{ => dal/bo}/kernel/Entity.java | 7 +- .../{ => dal/bo}/kernel/GenericEnumType.java | 2 +- .../splat/dal/bo/kernel/Persistent.hbm.xml | 28 + .../splat/{ => dal/bo}/kernel/Persistent.java | 8 +- .../{ => dal/bo}/kernel/Relation.hbm.xml | 21 +- .../splat/{ => dal/bo}/kernel/Relation.java | 3 +- .../org/splat/{ => dal/bo}/kernel/Role.java | 2 +- .../{ => dal/bo}/kernel/TextAttribute.hbm.xml | 8 +- .../{ => dal/bo}/kernel/TextAttribute.java | 5 +- .../splat/{ => dal/bo}/kernel/User.hbm.xml | 13 +- .../org/splat/{ => dal/bo}/kernel/User.java | 8 +- .../splat/{ => dal/bo}/som/ActorRelation.java | 8 +- .../splat/{ => dal/bo}/som/Attributes.hbm.xml | 4 +- .../{ => dal/bo}/som/CommentAttribute.java | 4 +- .../{ => dal/bo}/som/ContributorRelation.java | 6 +- .../{ => dal/bo}/som/ConvertsRelation.java | 6 +- .../bo}/som/DescriptionAttribute.java | 6 +- .../splat/{ => dal/bo}/som/Document.hbm.xml | 23 +- .../src/org/splat/dal/bo/som/Document.java | 893 ++++++++++++++++++ .../splat/{ => dal/bo}/som/DocumentType.java | 36 +- .../org/splat/{ => dal/bo}/som/Entity.java | 14 +- .../org/splat/{ => dal/bo}/som/File.hbm.xml | 10 +- .../src/org/splat/{ => dal/bo}/som/File.java | 5 +- .../splat/{ => dal/bo}/som/IDBuilder.hbm.xml | 2 +- .../org/splat/{ => dal/bo}/som/IDBuilder.java | 8 +- .../{ => dal/bo}/som/KnowledgeElement.hbm.xml | 18 +- .../{ => dal/bo}/som/KnowledgeElement.java | 83 +- .../bo}/som/KnowledgeElementType.java | 7 +- .../splat/{ => dal/bo}/som/ProgressState.java | 2 +- .../{ => dal/bo}/som/ProjectElement.hbm.xml | 19 +- .../{ => dal/bo}/som/ProjectElement.java | 65 +- .../{ => dal/bo}/som/Publication.hbm.xml | 10 +- .../src/org/splat/dal/bo/som/Publication.java | 251 +++++ .../org/splat/dal/bo/som/Relations.hbm.xml | 57 ++ .../splat/{ => dal/bo}/som/Scenario.hbm.xml | 4 +- .../org/splat/{ => dal/bo}/som/Scenario.java | 158 +--- .../bo}/som/SimulationContext.hbm.xml | 6 +- .../{ => dal/bo}/som/SimulationContext.java | 25 +- .../bo}/som/SimulationContextType.java | 22 +- .../splat/{ => dal/bo}/som/StampRelation.java | 6 +- .../org/splat/{ => dal/bo}/som/Study.hbm.xml | 12 +- .../src/org/splat/dal/bo/som/Study.java | 448 +++++++++ .../splat/{ => dal/bo}/som/Timestamp.hbm.xml | 19 +- .../org/splat/{ => dal/bo}/som/Timestamp.java | 10 +- .../{ => dal/bo}/som/UsedByRelation.java | 6 +- .../splat/{ => dal/bo}/som/UsesRelation.java | 8 +- .../{ => dal/bo}/som/ValidationCycle.hbm.xml | 10 +- .../{ => dal/bo}/som/ValidationCycle.java | 20 +- .../bo}/som/ValidationCycleRelation.java | 6 +- .../{ => dal/bo}/som/ValidationStep.java | 2 +- .../{ => dal/bo}/som/VersionsRelation.java | 10 +- .../splat/{ => dal/bo}/som/Visibility.java | 2 +- .../splat/{ => dal/dao}/kernel/Database.java | 62 +- .../org/splat/dal/dao/kernel/GenericDAO.java | 33 + .../splat/dal/dao/kernel/GenericDAOImpl.java | 64 ++ .../src/org/splat/dal/dao/som/Database.java | 489 ++++++++++ .../dal/dao/som/KnowledgeElementDAO.java | 20 + .../dal/dao/som/KnowledgeElementDAOImpl.java | 32 + .../Siman-Common/src/org/splat/kernel/Do.java | 1 + .../src/org/splat/kernel/IDGenerator.java | 24 - .../src/org/splat/kernel/IDPool.hbm.xml | 23 - .../src/org/splat/kernel/IDPool.java | 115 --- .../org/splat/kernel/ObjectProperties.java | 3 + .../src/org/splat/kernel/Persistent.hbm.xml | 23 - .../org/splat/kernel/RealmLoginModule.java | 2 + .../src/org/splat/kernel/UserDirectory.java | 3 +- .../org/splat/service/DocumentService.java | 18 + .../splat/service/DocumentServiceImpl.java | 18 + .../splat/service/DocumentTypeService.java | 29 + .../service/DocumentTypeServiceImpl.java | 52 + .../service/KnowledgeElementService.java | 29 + .../service/KnowledgeElementServiceImpl.java | 92 ++ .../splat/service/ProjectElementService.java | 24 + .../service/ProjectElementServiceImpl.java | 72 ++ .../org/splat/service/PublicationService.java | 145 +++ .../splat/service/PublicationServiceImpl.java | 445 +++++++++ .../org/splat/service/ScenarioService.java | 34 + .../splat/service/ScenarioServiceImpl.java | 228 +++++ .../src/org/splat/service/SearchService.java | 27 + .../org/splat/service/SearchServiceImpl.java | 328 +++++++ .../src/org/splat/service/StepService.java | 38 + .../org/splat/service/StepServiceImpl.java | 125 +++ .../src/org/splat/service/StudyService.java | 94 ++ .../org/splat/service/StudyServiceImpl.java | 476 ++++++++++ .../org/splat/{som => service/dto}/Proxy.java | 17 +- .../service/technical/DatabaseService.java | 18 + .../technical/DatabaseServiceImpl.java | 18 + .../splat/service/technical/IndexService.java | 36 + .../technical/IndexServiceImpl.java} | 110 ++- .../technical/ProjectSettingsService.java | 82 ++ .../ProjectSettingsServiceImpl.java} | 160 ++-- .../service/technical/RepositoryService.java | 33 + .../technical/RepositoryServiceImpl.java | 57 ++ .../src/org/splat/som/ApplicationRights.java | 4 +- .../src/org/splat/som/Database.java | 712 -------------- .../src/org/splat/som/Document.java | 776 --------------- .../src/org/splat/som/DocumentRights.java | 12 +- .../src/org/splat/som/Publication.java | 506 ---------- .../src/org/splat/som/Relations.hbm.xml | 57 -- .../src/org/splat/som/Revision.java | 2 + .../Siman-Common/src/org/splat/som/Step.java | 113 +-- .../src/org/splat/som/StepRights.java | 5 +- .../Siman-Common/src/org/splat/som/Study.java | 672 ------------- .../src/org/splat/som/StudyRights.java | 4 +- .../src/spring/businessServiceContext.xml | 69 ++ .../src/spring/daoServiceContext.xml | 21 + .../Siman-Common/src/spring/globalContext.xml | 87 ++ .../src/spring/technicalServiceContext.xml | 28 + Workspace/Siman-Common/src/test/Test.java | 34 +- Workspace/Siman/.project | 6 + .../org.eclipse.wst.common.component | 8 +- Workspace/Siman/.springBeans | 23 + .../WEB-INF/lib/commons-dbcp-1.4.jar | Bin 0 -> 160519 bytes Workspace/Siman/WebContent/jsp/menubar.jsp | 6 +- .../Siman/WebContent/jsp/newDocument.jsp | 2 +- Workspace/Siman/WebContent/sgeom/index.jsp | 12 +- Workspace/Siman/WebContent/smesh/index.jsp | 12 +- .../WebContent/study/editStudyProperties.jsp | 2 +- .../WebContent/study/searchKnowledge.jsp | 2 +- .../Siman/WebContent/study/searchStudy.jsp | 2 +- Workspace/Siman/conf/debug.properties | 4 +- Workspace/Siman/conf/release.properties | 1 + .../Siman/conf/templates/hibernate.cfg.xml | 60 -- .../org/splat/module/SaveDocumentAction.java | 565 ++++++----- .../Siman/src/org/splat/simer/Action.java | 10 +- .../org/splat/simer/ApplicationSettings.java | 48 +- .../src/org/splat/simer/ConnectionAction.java | 4 +- .../Siman/src/org/splat/simer/Converter.java | 4 +- .../splat/simer/DisplayKnowledgeAction.java | 4 +- .../splat/simer/DisplayStudyStepAction.java | 8 +- .../src/org/splat/simer/DocumentFacade.java | 64 +- .../org/splat/simer/EditDocumentAction.java | 38 +- .../simer/EditKnowledgeElementAction.java | 50 +- .../simer/EditScenarioPropertiesAction.java | 205 ++-- .../simer/EditSimulationContextAction.java | 549 ++++++----- .../src/org/splat/simer/EditStudyAction.java | 29 +- .../Siman/src/org/splat/simer/FileFacade.java | 2 +- .../org/splat/simer/ImportDocumentAction.java | 596 +++++++----- .../splat/simer/KnowledgeElementFacade.java | 2 +- .../org/splat/simer/NewScenarioAction.java | 59 +- .../src/org/splat/simer/NewScenarioMenu.java | 312 +++--- .../src/org/splat/simer/NewStudyAction.java | 269 +++--- .../src/org/splat/simer/OpenKnowledge.java | 325 ++++--- .../Siman/src/org/splat/simer/OpenObject.java | 18 +- .../Siman/src/org/splat/simer/OpenStudy.java | 85 +- .../org/splat/simer/OpenStudyServices.java | 4 +- .../src/org/splat/simer/SearchBaseAction.java | 14 +- .../splat/simer/SearchKnowledgeAction.java | 39 +- .../org/splat/simer/SearchStudyAction.java | 58 +- .../splat/simer/SimulationContextFacade.java | 29 +- .../src/org/splat/simer/StampFacade.java | 4 +- .../src/org/splat/simer/StartAction.java | 43 +- .../Siman/src/org/splat/simer/StudyMenu.java | 68 +- .../splat/simer/StudyPropertiesAction.java | 42 +- .../src/org/splat/simer/UploadAction.java | 2 +- .../org/splat/simer/UploadBaseNextAction.java | 8 +- .../src/org/splat/simer/ValidationFacade.java | 6 +- .../splat/simer/VersionDocumentAction.java | 543 ++++++----- .../simer/admin/DatabaseIndexingAction.java | 44 +- .../splat/simer/admin/ImportUserAction.java | 4 +- .../org/splat/simer/admin/ImportedStudy.java | 41 +- .../simer/admin/ProjectElementFacade.java | 12 +- .../simer/admin/SimulationContextAction.java | 58 +- .../simer/admin/SimulationContextFacade.java | 31 +- .../Siman/src/spring/applicationContext.xml | 145 ++- Workspace/Siman/src/spy.properties | 246 +++++ Workspace/Siman/src/{ => test}/Test.java | 1 + 178 files changed, 8773 insertions(+), 5351 deletions(-) create mode 100644 Workspace/SPlat/.settings/com.bea.workshop.common.upgrade.log.xml create mode 100644 Workspace/SPlat/src/META-INF/MANIFEST.MF create mode 100644 Workspace/Siman-Common/lib/aopalliance.jar create mode 100644 Workspace/Siman-Common/lib/p6spy.jar rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/Any.hbm.xml (56%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/Any.java (91%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/Attribute.hbm.xml (83%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/Attribute.java (96%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/Entity.hbm.xml (50%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/Entity.java (93%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/GenericEnumType.java (99%) create mode 100644 Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.hbm.xml rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/Persistent.java (95%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/Relation.hbm.xml (50%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/Relation.java (98%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/Role.java (98%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/TextAttribute.hbm.xml (53%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/TextAttribute.java (95%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/User.hbm.xml (82%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/kernel/User.java (96%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/ActorRelation.java (92%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/Attributes.hbm.xml (58%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/CommentAttribute.java (92%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/ContributorRelation.java (83%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/ConvertsRelation.java (95%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/DescriptionAttribute.java (91%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/Document.hbm.xml (79%) create mode 100644 Workspace/Siman-Common/src/org/splat/dal/bo/som/Document.java rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/DocumentType.java (84%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/Entity.java (81%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/File.hbm.xml (72%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/File.java (97%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/IDBuilder.hbm.xml (86%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/IDBuilder.java (94%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/KnowledgeElement.hbm.xml (74%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/KnowledgeElement.java (84%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/KnowledgeElementType.java (95%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/ProgressState.java (93%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/ProjectElement.hbm.xml (67%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/ProjectElement.java (82%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/Publication.hbm.xml (72%) create mode 100644 Workspace/Siman-Common/src/org/splat/dal/bo/som/Publication.java create mode 100644 Workspace/Siman-Common/src/org/splat/dal/bo/som/Relations.hbm.xml rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/Scenario.hbm.xml (80%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/Scenario.java (66%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/SimulationContext.hbm.xml (87%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/SimulationContext.java (92%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/SimulationContextType.java (81%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/StampRelation.java (92%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/Study.hbm.xml (73%) create mode 100644 Workspace/Siman-Common/src/org/splat/dal/bo/som/Study.java rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/Timestamp.hbm.xml (63%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/Timestamp.java (92%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/UsedByRelation.java (93%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/UsesRelation.java (90%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/ValidationCycle.hbm.xml (79%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/ValidationCycle.java (94%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/ValidationCycleRelation.java (93%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/ValidationStep.java (89%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/VersionsRelation.java (90%) rename Workspace/Siman-Common/src/org/splat/{ => dal/bo}/som/Visibility.java (92%) rename Workspace/Siman-Common/src/org/splat/{ => dal/dao}/kernel/Database.java (76%) create mode 100644 Workspace/Siman-Common/src/org/splat/dal/dao/kernel/GenericDAO.java create mode 100644 Workspace/Siman-Common/src/org/splat/dal/dao/kernel/GenericDAOImpl.java create mode 100644 Workspace/Siman-Common/src/org/splat/dal/dao/som/Database.java create mode 100644 Workspace/Siman-Common/src/org/splat/dal/dao/som/KnowledgeElementDAO.java create mode 100644 Workspace/Siman-Common/src/org/splat/dal/dao/som/KnowledgeElementDAOImpl.java delete mode 100644 Workspace/Siman-Common/src/org/splat/kernel/IDGenerator.java delete mode 100644 Workspace/Siman-Common/src/org/splat/kernel/IDPool.hbm.xml delete mode 100644 Workspace/Siman-Common/src/org/splat/kernel/IDPool.java delete mode 100644 Workspace/Siman-Common/src/org/splat/kernel/Persistent.hbm.xml create mode 100644 Workspace/Siman-Common/src/org/splat/service/DocumentService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/DocumentServiceImpl.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/DocumentTypeService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/DocumentTypeServiceImpl.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/KnowledgeElementService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/KnowledgeElementServiceImpl.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/ProjectElementService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/ProjectElementServiceImpl.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/PublicationService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/PublicationServiceImpl.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/ScenarioService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/ScenarioServiceImpl.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/SearchService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/SearchServiceImpl.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/StepService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/StepServiceImpl.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/StudyService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/StudyServiceImpl.java rename Workspace/Siman-Common/src/org/splat/{som => service/dto}/Proxy.java (82%) create mode 100644 Workspace/Siman-Common/src/org/splat/service/technical/DatabaseService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/technical/DatabaseServiceImpl.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/technical/IndexService.java rename Workspace/Siman-Common/src/org/splat/{som/Index.java => service/technical/IndexServiceImpl.java} (71%) create mode 100644 Workspace/Siman-Common/src/org/splat/service/technical/ProjectSettingsService.java rename Workspace/Siman-Common/src/org/splat/{som/ProjectSettings.java => service/technical/ProjectSettingsServiceImpl.java} (78%) create mode 100644 Workspace/Siman-Common/src/org/splat/service/technical/RepositoryService.java create mode 100644 Workspace/Siman-Common/src/org/splat/service/technical/RepositoryServiceImpl.java delete mode 100644 Workspace/Siman-Common/src/org/splat/som/Database.java delete mode 100644 Workspace/Siman-Common/src/org/splat/som/Document.java delete mode 100644 Workspace/Siman-Common/src/org/splat/som/Publication.java delete mode 100644 Workspace/Siman-Common/src/org/splat/som/Relations.hbm.xml delete mode 100644 Workspace/Siman-Common/src/org/splat/som/Study.java create mode 100644 Workspace/Siman-Common/src/spring/businessServiceContext.xml create mode 100644 Workspace/Siman-Common/src/spring/daoServiceContext.xml create mode 100644 Workspace/Siman-Common/src/spring/technicalServiceContext.xml create mode 100644 Workspace/Siman/.springBeans create mode 100644 Workspace/Siman/WebContent/WEB-INF/lib/commons-dbcp-1.4.jar delete mode 100644 Workspace/Siman/conf/templates/hibernate.cfg.xml create mode 100644 Workspace/Siman/src/spy.properties rename Workspace/Siman/src/{ => test}/Test.java (98%) diff --git a/Workspace/SPlat/.settings/com.bea.workshop.common.upgrade.log.xml b/Workspace/SPlat/.settings/com.bea.workshop.common.upgrade.log.xml new file mode 100644 index 0000000..7c458f2 --- /dev/null +++ b/Workspace/SPlat/.settings/com.bea.workshop.common.upgrade.log.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/Workspace/SPlat/src/META-INF/MANIFEST.MF b/Workspace/SPlat/src/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/Workspace/SPlat/src/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/Workspace/Siman-Common/.classpath b/Workspace/Siman-Common/.classpath index c00fad4..063fd5a 100644 --- a/Workspace/Siman-Common/.classpath +++ b/Workspace/Siman-Common/.classpath @@ -16,7 +16,7 @@ - + @@ -25,5 +25,7 @@ + + diff --git a/Workspace/Siman-Common/.springBeans b/Workspace/Siman-Common/.springBeans index 2ef260c..c18ad0a 100644 --- a/Workspace/Siman-Common/.springBeans +++ b/Workspace/Siman-Common/.springBeans @@ -5,7 +5,21 @@ src/spring/globalContext.xml + src/spring/businessServiceContext.xml + src/spring/daoServiceContext.xml + src/spring/technicalServiceContext.xml + + + true + false + + src/spring/globalContext.xml + src/spring/businessServiceContext.xml + src/spring/daoServiceContext.xml + src/spring/technicalServiceContext.xml + + diff --git a/Workspace/Siman-Common/lib/aopalliance.jar b/Workspace/Siman-Common/lib/aopalliance.jar new file mode 100644 index 0000000000000000000000000000000000000000..578b1a0c359ef88a84461bdb91d9d0041afd54de GIT binary patch literal 4467 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 P$p&@`QUih61jPRUGcw%K literal 0 HcmV?d00001 diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Any.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Any.hbm.xml similarity index 56% rename from Workspace/Siman-Common/src/org/splat/kernel/Any.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Any.hbm.xml index 6c32c8f..5bc0fd6 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Any.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Any.hbm.xml @@ -10,20 +10,23 @@ - - + + - - - + + - + - + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Any.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Any.java similarity index 91% rename from Workspace/Siman-Common/src/org/splat/kernel/Any.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Any.java index 84ba87c..1992ed5 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Any.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Any.java @@ -1,4 +1,4 @@ -package org.splat.kernel; +package org.splat.dal.bo.kernel; /** * Abstract root class of persistent objects supporting dynamic attributes.
* Dynamic attributes are instances of concrete subclasses of Attribute that are assigned to Any objects at run time. @@ -14,7 +14,11 @@ import java.util.Iterator; import java.util.Set; import org.hibernate.Session; -import org.splat.som.Database; +import org.splat.dal.dao.som.Database; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.kernel.ObjectProperties; public abstract class Any extends Persistent { @@ -71,7 +75,7 @@ public abstract class Any extends Persistent { return false; } - protected boolean setAttribute (Attribute field) { + public boolean setAttribute (Attribute field) { // ------------------------------------------------ Class type = field.getClass(); Session session = Database.getSession(); diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Attribute.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Attribute.hbm.xml similarity index 83% rename from Workspace/Siman-Common/src/org/splat/kernel/Attribute.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Attribute.hbm.xml index 2a25e08..79c5a85 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Attribute.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Attribute.hbm.xml @@ -10,11 +10,11 @@ - + - + diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Attribute.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Attribute.java similarity index 96% rename from Workspace/Siman-Common/src/org/splat/kernel/Attribute.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Attribute.java index 2b14ad0..c3096c7 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Attribute.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Attribute.java @@ -1,4 +1,4 @@ -package org.splat.kernel; +package org.splat.dal.bo.kernel; /** * * @see Any diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Entity.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Entity.hbm.xml similarity index 50% rename from Workspace/Siman-Common/src/org/splat/kernel/Entity.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Entity.hbm.xml index 4b63419..e024f44 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Entity.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Entity.hbm.xml @@ -10,26 +10,26 @@ - + + - - - - + - + - + - + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Entity.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Entity.java similarity index 93% rename from Workspace/Siman-Common/src/org/splat/kernel/Entity.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Entity.java index 76acc28..dd190ca 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Entity.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Entity.java @@ -1,4 +1,4 @@ -package org.splat.kernel; +package org.splat.dal.bo.kernel; /** * Abstract root class of persistent objects supporting relations to other persistent objects.
* Relations are instances of concrete subclasses of Relation that are assigned to Entity objects at run time.
@@ -17,6 +17,11 @@ import java.util.Set; import java.util.Vector; import org.hibernate.Session; +import org.splat.dal.dao.kernel.Database; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.kernel.ObjectProperties; public abstract class Entity extends Any { diff --git a/Workspace/Siman-Common/src/org/splat/kernel/GenericEnumType.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/GenericEnumType.java similarity index 99% rename from Workspace/Siman-Common/src/org/splat/kernel/GenericEnumType.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/GenericEnumType.java index 365a39d..3f3ba07 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/GenericEnumType.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/GenericEnumType.java @@ -1,4 +1,4 @@ -package org.splat.kernel; +package org.splat.dal.bo.kernel; /** * * @author Daniel Brunier-Coulin diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.hbm.xml new file mode 100644 index 0000000..998846b --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.hbm.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + persistent_id + 1000 + + + + + + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Persistent.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.java similarity index 95% rename from Workspace/Siman-Common/src/org/splat/kernel/Persistent.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.java index 454bb0d..8ceda82 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Persistent.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.java @@ -1,4 +1,10 @@ -package org.splat.kernel; +package org.splat.dal.bo.kernel; + +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.kernel.ObjectProperties; + /** * Base implementation of the API Design Pattern of objects which persistence is based on Hibernate.
* This Design Pattern supports the following features: diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Relation.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.hbm.xml similarity index 50% rename from Workspace/Siman-Common/src/org/splat/kernel/Relation.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.hbm.xml index 48012c6..47b7ab8 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Relation.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.hbm.xml @@ -10,23 +10,30 @@ - - + + + - - - - + + + + relation_id + + + - + + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Relation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.java similarity index 98% rename from Workspace/Siman-Common/src/org/splat/kernel/Relation.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.java index a4c017b..9d8d22d 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Relation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.java @@ -1,4 +1,4 @@ -package org.splat.kernel; +package org.splat.dal.bo.kernel; /** * Base implementation of relations between entities.
* A relation makes a typed link from an entity to any kind persistent object. The relations are typed by subclasses of this @@ -19,6 +19,7 @@ package org.splat.kernel; import java.util.Iterator; import org.hibernate.Session; +import org.splat.dal.dao.kernel.Database; public abstract class Relation extends Any { diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Role.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Role.java similarity index 98% rename from Workspace/Siman-Common/src/org/splat/kernel/Role.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Role.java index a6c3ba1..512e59a 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Role.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Role.java @@ -1,4 +1,4 @@ -package org.splat.kernel; +package org.splat.dal.bo.kernel; /** * Class of objects representing the role of users. * A role is named by an application-dependent string (reason why role names are not defined by an enumeration). diff --git a/Workspace/Siman-Common/src/org/splat/kernel/TextAttribute.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/TextAttribute.hbm.xml similarity index 53% rename from Workspace/Siman-Common/src/org/splat/kernel/TextAttribute.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/TextAttribute.hbm.xml index d5d0682..7b5fe9b 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/TextAttribute.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/TextAttribute.hbm.xml @@ -7,14 +7,8 @@ --> - - - - + - - - \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/kernel/TextAttribute.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/TextAttribute.java similarity index 95% rename from Workspace/Siman-Common/src/org/splat/kernel/TextAttribute.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/TextAttribute.java index fb8d677..4e797eb 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/TextAttribute.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/TextAttribute.java @@ -1,4 +1,7 @@ -package org.splat.kernel; +package org.splat.dal.bo.kernel; + +import org.splat.dal.dao.kernel.Database; + /** * * @author Daniel Brunier-Coulin diff --git a/Workspace/Siman-Common/src/org/splat/kernel/User.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/User.hbm.xml similarity index 82% rename from Workspace/Siman-Common/src/org/splat/kernel/User.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/User.hbm.xml index 885767e..5ab556b 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/User.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/User.hbm.xml @@ -12,25 +12,22 @@ - - - - - + + - + - + - + diff --git a/Workspace/Siman-Common/src/org/splat/kernel/User.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/User.java similarity index 96% rename from Workspace/Siman-Common/src/org/splat/kernel/User.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/kernel/User.java index 1ec7e46..1494c37 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/User.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/User.java @@ -1,4 +1,4 @@ -package org.splat.kernel; +package org.splat.dal.bo.kernel; /** * * @author Daniel Brunier-Coulin @@ -7,7 +7,11 @@ package org.splat.kernel; import java.security.Principal; -import org.splat.kernel.Persistent; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.kernel.Name; public class User extends Persistent implements Principal, Name { diff --git a/Workspace/Siman-Common/src/org/splat/som/ActorRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ActorRelation.java similarity index 92% rename from Workspace/Siman-Common/src/org/splat/som/ActorRelation.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/ActorRelation.java index c8bca9a..98fdf8a 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ActorRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ActorRelation.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * Base implementation of actor relations such as Contributor, Reviewer and Approver. * ActorRelation objects are attached to Documents for defining those who HAVE CONTRIBUTED to these documents, @@ -14,9 +14,9 @@ package org.splat.som; * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.Persistent; -import org.splat.kernel.Relation; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.Relation; +import org.splat.dal.bo.kernel.User; public abstract class ActorRelation extends Relation { diff --git a/Workspace/Siman-Common/src/org/splat/som/Attributes.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Attributes.hbm.xml similarity index 58% rename from Workspace/Siman-Common/src/org/splat/som/Attributes.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/Attributes.hbm.xml index 282fed5..7aefad5 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Attributes.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Attributes.hbm.xml @@ -11,12 +11,12 @@ - + - +
\ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/CommentAttribute.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/CommentAttribute.java similarity index 92% rename from Workspace/Siman-Common/src/org/splat/som/CommentAttribute.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/CommentAttribute.java index 5dcc580..e6cc415 100644 --- a/Workspace/Siman-Common/src/org/splat/som/CommentAttribute.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/CommentAttribute.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * Attribute class of type Comment.
* A comment is made of any text up to 65 thousand characters. @@ -7,7 +7,7 @@ package org.splat.som; * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.TextAttribute; +import org.splat.dal.bo.kernel.TextAttribute; public class CommentAttribute extends TextAttribute { diff --git a/Workspace/Siman-Common/src/org/splat/som/ContributorRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ContributorRelation.java similarity index 83% rename from Workspace/Siman-Common/src/org/splat/som/ContributorRelation.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/ContributorRelation.java index 2f3f3dd..d2ea3d4 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ContributorRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ContributorRelation.java @@ -1,11 +1,11 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; public class ContributorRelation extends ActorRelation { @@ -18,7 +18,7 @@ public class ContributorRelation extends ActorRelation { protected ContributorRelation () { } // Initialization constructor - protected ContributorRelation (Study from, User to) { + public ContributorRelation (Study from, User to) { // -------------------------------------------------- super(from, to); } diff --git a/Workspace/Siman-Common/src/org/splat/som/ConvertsRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ConvertsRelation.java similarity index 95% rename from Workspace/Siman-Common/src/org/splat/som/ConvertsRelation.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/ConvertsRelation.java index 5d77c75..ff2997a 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ConvertsRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ConvertsRelation.java @@ -1,12 +1,12 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.Persistent; -import org.splat.kernel.Relation; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.Relation; public class ConvertsRelation extends Relation { diff --git a/Workspace/Siman-Common/src/org/splat/som/DescriptionAttribute.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/DescriptionAttribute.java similarity index 91% rename from Workspace/Siman-Common/src/org/splat/som/DescriptionAttribute.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/DescriptionAttribute.java index 9369f2c..4213156 100644 --- a/Workspace/Siman-Common/src/org/splat/som/DescriptionAttribute.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/DescriptionAttribute.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * Attribute class of type Description.
* A description is made of any text up to 65 thousand characters. @@ -7,7 +7,7 @@ package org.splat.som; * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.TextAttribute; +import org.splat.dal.bo.kernel.TextAttribute; public class DescriptionAttribute extends TextAttribute { @@ -25,7 +25,7 @@ public class DescriptionAttribute extends TextAttribute { * @param from the study or the scenario to which this description is attached. * @param value the text of this description */ - protected DescriptionAttribute (ProjectElement from, String value) { + public DescriptionAttribute (ProjectElement from, String value) { // ------------------------------------------------------------------ super(from, value); } diff --git a/Workspace/Siman-Common/src/org/splat/som/Document.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Document.hbm.xml similarity index 79% rename from Workspace/Siman-Common/src/org/splat/som/Document.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/Document.hbm.xml index f42acac..ca6e184 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Document.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Document.hbm.xml @@ -9,21 +9,22 @@ - - + + - - + - + - + @@ -73,7 +74,7 @@ - + diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Document.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Document.java new file mode 100644 index 0000000..9669171 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Document.java @@ -0,0 +1,893 @@ +package org.splat.dal.bo.som; + +/** + * + * @author Daniel Brunier-Coulin + * @copyright OPEN CASCADE 2012 + */ + +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +import org.hibernate.Hibernate; +import org.hibernate.Session; + +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.Relation; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.Timestamp.ComparatorByDate; +import org.splat.dal.dao.som.Database; +import org.splat.kernel.NotApplicableException; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.manox.Reader; +import org.splat.manox.Toolbox; +import org.splat.service.StudyService; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.service.technical.ProjectSettingsServiceImpl; +import org.splat.service.technical.ProjectSettingsServiceImpl.FileNaming; +import org.splat.som.Revision; +import org.splat.som.Step; + +public class Document extends Entity { + + // Persistent fields + private DocumentType type; // User expendable types + private File myfile; + private String did; + private int step; + private ProgressState state; + private String name; + private String version; + private int countag; + private int history; + private User author; + private Date lasdate; + private ProjectSettingsService _projectSettingsService; + private StudyService _studyService; + + // Transient fields + public static String suformat = "00"; // Format of the suffix number of document did and file name + + // ============================================================================================================================== + // Construction + // ============================================================================================================================== + + // Fields initialization class + public static class Properties extends Persistent.Properties { + // ------------------------------------------------------------ + private DocumentType type = null; + private String did = null; // Only for searching from a given reference + private ProjectElement owner = null; // Only for constructing a document + private ProjectSettingsService.Step step = null; + private ProgressState state = null; + private String name = null; + protected String format = null; + private String version = null; + private User author = null; + protected Date date = null; + private String summary = null; // Only for versioning a document + private String path = null; // Only for searching from a given path + + // - Public services + + public void clear() { + super.clear(); + type = null; + did = null; + owner = null; + step = null; + state = null; + name = null; + format = null; + version = null; + author = null; + date = null; + summary = null; + path = null; + } + + public Properties copy() { + Properties copy = new Properties(); + copy.type = this.type; + copy.did = this.did; + copy.owner = this.owner; + copy.step = this.step; + copy.state = this.state; + copy.name = this.name; + copy.format = this.format; + copy.version = this.version; + copy.author = this.author; + copy.date = this.date; + copy.summary = this.summary; + copy.path = this.path; + return copy; + } + + // - Protected services + + public User getAuthor() { + return author; + } + + public String getDescription() { + return summary; + } + + public String getLocalPath() { + return path; + } + + public String getReference() { + return did; + } + + public ProjectSettingsService.Step getStep() { + return step; + } + + public DocumentType getType() { + return type; + } + + // - Property setters + + public Properties setAuthor(User user) { + this.author = user; + return this; + } + + public Properties setDate(Date date) { + this.date = date; + return this; + } + + public Properties setDescription(String summary) + throws InvalidPropertyException { + if (summary.length() == 0) + throw new InvalidPropertyException("description"); + this.summary = summary; + return this; + } + + public Properties setDocument(Document base) { + type = base.type; + step = ProjectSettingsServiceImpl.getStep(base.step); + name = base.name; + format = base.getFormat(); + state = ProgressState.inWORK; // For incrementing the version number at save time + version = base.version; + return this; + } + + public Properties setExternReference(String ref) + throws InvalidPropertyException { + if (ref.length() == 0) + throw new InvalidPropertyException("reference"); + if (ref.equals(new Revision().toString())) + throw new InvalidPropertyException("reference"); // Internal version number + this.version = ref; + return this; + } + + public Properties setFormat(String format) + throws InvalidPropertyException { + if (format.length() == 0) + throw new InvalidPropertyException("format"); + this.format = format; + return this; + } + + // Required only for passing search arguments + public Properties setLocalPath(String path) + throws InvalidPropertyException { + if (path.length() == 0) + throw new InvalidPropertyException("path"); + this.path = path; + return this; + } + + public Properties setName(String name) throws InvalidPropertyException { + if (name.length() == 0) + throw new InvalidPropertyException("name"); + this.name = name; + return this; + } + + public Properties setOwner(ProjectElement owner) { + this.owner = owner; + return this; + } + + // Required only for passing search arguments + public Properties setReference(String did) + throws InvalidPropertyException { + if (did.length() == 0) + throw new InvalidPropertyException("reference"); + this.did = did; + return this; + } + + public Properties setState(ProgressState state) + throws InvalidPropertyException { + if (state == ProgressState.inPROGRESS + || state == ProgressState.TEMPLATE) + throw new InvalidPropertyException("state"); // Non document states + this.state = state; + return this; + } + + public Properties setStep(ProjectSettingsService.Step step) { + this.step = step; + return this; + } + + public Properties setType(DocumentType type) { + this.type = type; + return this; + } + + // - Global validity check + + public void checkValidity() throws MissedPropertyException, + InvalidPropertyException, MultiplyDefinedException { + if (type == null) + throw new MissedPropertyException("type"); + if (owner == null) + throw new MissedPropertyException("owner"); + if (step == null) + throw new MissedPropertyException("step"); + if (author == null) + throw new MissedPropertyException("author"); + if (format == null) + throw new MissedPropertyException("format"); + if (owner instanceof Study && !step.appliesTo(Study.class)) + throw new InvalidPropertyException("step"); + if (!type.isContentInto(step)) + throw new InvalidPropertyException("step"); + if (state != null && state != ProgressState.EXTERN) { + // inDRAFT, inCHECK or APPROVED + version = imposed version (future use) + // inWORK + version = base version incremented at save time (used for versioning) + if (version == null) + throw new InvalidPropertyException("state"); + } + if (version != null) { + if (state == null) + state = ProgressState.EXTERN; + } + } + } + + // Database fetch constructor + protected Document() { + } + + // Internal constructor + public Document(Properties dprop) throws MissedPropertyException, + InvalidPropertyException, MultiplyDefinedException { + // ------------------------------------- + super(dprop); // Throws one of the above exception if not valid + myfile = new File(null, dprop.format, dprop.date); // The path is initialized below + type = dprop.type; + step = dprop.step.getNumber(); + name = dprop.name; + version = dprop.version; + author = dprop.author; + countag = 0; + history = 0; + lasdate = myfile.getDate(); // Today if not defined in the properties + + state = dprop.state; + if (state == null) { + state = ProgressState.inWORK; // Promoted when saving this document + version = new Revision().toString(); + } + Study owner = null; + if (dprop.owner instanceof Study) + owner = (Study) dprop.owner; + else + owner = ((Scenario) dprop.owner).getOwnerStudy(); + + ProjectSettingsService.Step step = ProjectSettingsServiceImpl + .getStep(this.step); + SimpleDateFormat tostring = new SimpleDateFormat("yyyy"); + String year = tostring.format(owner.getDate()); + if (name == null) { // Newed document + this.name = "%n"; // Named later at publication + this.history = -1; // Marks the document as undefined for future assignment + } + String filename = generateEncodedName(owner); + String path; + + path = owner.getReference(); + did = new StringBuffer(path).append(".%").append(suformat).toString(); // Document reference + path = new StringBuffer(year).append("/").append(path).append("/") + .append(step.getPath()) // File path relative to the repository vault + .append(filename).append(".").append(myfile.getFormat()) // File name and extension + .toString(); + myfile.changePath(path); + } + + // ============================================================================================================================== + // Public member functions + // ============================================================================================================================== + + public File getAttachedFile(String format) { + // ------------------------------------------- + List exports = getRelations(ConvertsRelation.class); + + for (Iterator i = exports.iterator(); i.hasNext();) { + File export = (File) i.next().getTo(); + if (export.getFormat().equals(format)) + return export; + } + return null; + } + + public User getAuthor() { + // ------------------------ + return author; + } + + public Date getCreationDate() { + // ------------------------------ + return myfile.getDate(); + } + + public Date getLastModificationDate() { + // -------------------------------------- + return lasdate; + } + + public String getFormat() { + // -------------------------- + return myfile.getFormat(); + } + + public Document getPreviousVersion() { + // ------------------------------------- + Relation previous = getFirstRelation(VersionsRelation.class); + if (previous != null) + return (Document) previous.getTo(); + else + return null; + } + + public ProgressState getProgressState() { + // ---------------------------------------- + return state; + } + + /** + * Returns the path where all physical files attached to this document are saved. This path is relative to the vault of the repository + * and include the file name, without extension, common to all physical files attached to this document. + * + * @return the path of the document + */ + public String getRelativePath() { + // -------------------------------- + String[] table = myfile.getRelativePath().split("\\x2E"); + StringBuffer path = new StringBuffer(table[0]); + for (int i = 1; i < table.length - 1; i++) + path.append('.').append(table[i]); + return path.toString(); + } + + /** + * Returns the global unique reference of this document lineage. The document reference is common to all versions of the document + * (versioning a document does not change its reference). It is made of the owner study reference suffixed by a document identifier + * unique in the scope of the study. + * + * @return the document reference + */ + public String getReference() { + // ----------------------------- + return did; + } + + public java.io.File getSaveDirectory() { + // --------------------------------------- + String mypath = Database.getRepositoryVaultPath() + + myfile.getRelativePath(); + String[] table = mypath.split("/"); + + // Cutting the filename + StringBuffer path = new StringBuffer(table[0]); + for (int i = 1; i < table.length - 1; i++) + path = path.append("/").append(table[i]); + return new java.io.File(path.append("/").toString()); + } + + public File getSourceFile() { + // ---------------------------- + return myfile; + } + + /** + * Returns the stamps such as review and approval attached to this document, if exist. If several stamps exist, they are returned in + * ascending order of dates. + * + * @return the stamps of the document in ascending order of dates, or an empty array if no stamp exist. + */ + public Timestamp[] getStamps() { + // ------------------------------- + Vector stamps = new Vector(); + + for (Iterator i = this.getAllRelations().iterator(); i + .hasNext();) { + Relation link = i.next(); + if (link instanceof StampRelation) + stamps.add(((StampRelation) link).getTo()); + } + Timestamp[] result = stamps.toArray(new Timestamp[stamps.size()]); + ComparatorByDate bydate = new Timestamp.ComparatorByDate(); + + Arrays.sort(result, bydate); + return result; + } + + /** + * Returns the title of this document. + * + * @return the document title, or an empty string is this document is undefined. + * @see #isUndefined() + */ + public String getTitle() { + // ------------------------- + if (this.isUndefined()) + return ""; + else + return name; + } + + public DocumentType getType() { + // ------------------------------ + return type; + } + + /** + * Returns the version number of this document. The version number, when exists, is either of the internal form (m.n.s) usable for + * building a Revision object, or any string in case of external document (document with EXTERN state).
+ *
+ * Note: document slots have a version number equal to "0.0.0". + * + * @return the version number of this document, or null if this is EXTERN. + * @see #isUndefined() + */ + public String getVersion() { + // --------------------------- + return version; + } + + /** + * Returns true if this document is undefined. An undefined document is a meta-document created for reserving the persistent reference + * of a new document before saving (or importing) this later into the repository. The working copy of a such document may include this + * reference. + * + * @see #getTitle() + * @see #getVersion() + * @see #initialize(Properties) + */ + public boolean isUndefined() { + // ----------------------------- + return (history == -1); + } + + public boolean isInto(Step container) { + // -------------------------------------- + return (step == container.getNumber()); + } + + public boolean isPublished() { + // ----------------------------- + return (countag > 0); + } + + public boolean isShared() { + // -------------------------- + return (countag + history > 1); + } + + public boolean isVersioned() { + // ----------------------------- + return (history > 0); + } + + // ============================================================================================================================== + // Public services + // ============================================================================================================================== + + public static DocumentType createType(DocumentType.Properties tprop) + throws MissedPropertyException, InvalidPropertyException, + MultiplyDefinedException, RuntimeException { + // --------------------------------------------------------------------- + // TODO: Check for duplicate definition + DocumentType type = new DocumentType(tprop); + Session session = Database.getSession(); + session.save(type); + + return type; + } + + public static Properties extractProperties(java.io.File file) { + // -------------------------------------------------------------- + Properties fprop = new Properties(); + Reader tool = Toolbox.getReader(file); + String value; + if (tool != null) + try { + value = tool.extractProperty("title"); + if (value != null) + fprop.setName(value); + + value = tool.extractProperty("reference"); + if (value != null) + fprop.setReference(value); + } catch (Exception e) { + } + return fprop; + } + + @SuppressWarnings("unchecked") + public static List selectAllTypes() { + // -------------------------------------------------- + String query = "from DocumentType"; + + List types = Database.getSession().createQuery(query) + .list(); + for (Iterator i = types.iterator(); i.hasNext();) { + Hibernate.initialize(i.next()); // Supposed fetching document types + } + return types; + } + + @SuppressWarnings("unchecked") + public static List selectResultTypes() { + // ----------------------------------------------------- + String query = "from DocumentType where result is not null order by result asc"; + + return Database.getSession().createQuery(query).list(); + } + + public static DocumentType selectType(String name) { + // --------------------------------------------------- + String query = new StringBuffer("from DocumentType where name='") + .append(name).append("'").toString(); + + return (DocumentType) Database.getSession().createQuery(query) + .uniqueResult(); + } + + public static DocumentType selectType(int index) { + // ------------------------------------------------- + String query = new StringBuffer("from DocumentType where rid='") + .append(index).append("'").toString(); + + return (DocumentType) Database.getSession().createQuery(query) + .uniqueResult(); + } + + @SuppressWarnings("unchecked") + public static List selectTypesOf( + ProjectSettingsService.Step step) { + // -------------------------------------------------------------------------- + Integer number = step.getNumber(); + String query = new StringBuffer("from DocumentType") + .append(" where step like '%-").append(number).append("-%'") + .toString(); + + List types = Database.getSession().createQuery(query) + .list(); + for (Iterator i = types.iterator(); i.hasNext();) { + Hibernate.initialize(i.next()); // For fetching document types + } + return types; + } + + // ============================================================================================================================== + // Protected services + // ============================================================================================================================== + + protected ConvertsRelation attach(String format) { + // ------------------------------------------------- + return attach(format, null); + } + + protected ConvertsRelation attach(String format, String description) { + // --------------------------------------------------------------------- + String path = this.getRelativePath(); + File export = new File(path + "." + format); + ConvertsRelation attach = new ConvertsRelation(this, export, + description); + Session session = Database.getSession(); + + session.save(export); + session.save(attach); + + this.addRelation(attach); // Updates this + + return attach; + } + + public boolean buildReferenceFrom(ProjectElement scope, Document lineage) { + // ----------------------------------------------------------------------------- + if (state != ProgressState.inWORK) + return false; + Study owner = null; + Scenario context = null; + if (scope instanceof Study) + owner = (Study) scope; + else { + context = ((Scenario) scope); + owner = context.getOwnerStudy(); + } + did = lineage.did; + if (context != null && (lineage.isVersioned() || owner.shares(lineage))) { + version = new Revision(version).setBranch(context.getReference()) + .toString(); + } + return true; + } + + public boolean buildReferenceFrom(Study scope) { + // -------------------------------------------------- + if (state != ProgressState.inWORK && state != ProgressState.EXTERN) + return false; + DecimalFormat tostring = new DecimalFormat(suformat); + + did = did.replace("%" + suformat, + tostring.format(scope.getLastLocalIndex())); + return true; + } + + public boolean demote() { + // --------------------------- + ValidationStep torem; + + if (state == ProgressState.inCHECK) { + state = ProgressState.inDRAFT; + torem = ValidationStep.REVIEW; + // This operation must not change the version number of documents. + // Consequently, inDRAFT documents may have a minor version number equal to zero. + } else if (state == ProgressState.inDRAFT) { + state = ProgressState.inWORK; + torem = ValidationStep.PROMOTION; + } else { + return false; + } + for (Iterator i = this.getAllRelations().iterator(); i + .hasNext();) { + Relation link = i.next(); + if (!(link instanceof StampRelation)) + continue; + if (((StampRelation) link).getStampType() != torem) + continue; + i.remove(); + break; + } + Database.getSession().update(this); + return true; + } + + /** + * Increments the reference count of this document following its publication into a Study step. + * + * @see #release() + */ + public void hold() { + // ---------------------- + countag += 1; + if (this.isSaved()) + Database.getSession().update(this); + } + + /** + * Defines this document. + * + * @param dprop + * the properties of the document + * + * @see Step#createDocument(Properties) + * @see #isUndefined() + */ + public void initialize(Properties dprop) throws MissedPropertyException, + InvalidPropertyException, NotApplicableException { + // -------------------------------------------- + if (!this.isUndefined()) + throw new NotApplicableException( + "Cannot initialize an existing Document"); + if (dprop.name == null) + throw new MissedPropertyException("name"); + if (dprop.name.length() == 0) + throw new InvalidPropertyException("name"); + if (dprop.owner == null) + throw new MissedPropertyException("owner"); + // if (dprop.owner instanceof Study && !ProjectSettings.getStep(step).appliesTo(Study.class)) { + // throw new InvalidPropertyException("step"); + // } + name = dprop.name; + myfile.changePath(myfile.getRelativePath().replace("%n", + getEncodedRootName((Study) dprop.owner))); + if (history == -1) + history = 0; + if (dprop.date == null) { + Calendar current = Calendar.getInstance(); + lasdate = current.getTime(); // Today + } else { + lasdate = dprop.date; + } + Database.getSession().update(this); + } + + public boolean promote(Timestamp stamp) { + // ------------------------------------------- + ProgressState newstate = null; + + if (state == ProgressState.inWORK) { + newstate = ProgressState.inDRAFT; // Promotion to being reviewed + } else if (state == ProgressState.inDRAFT) { + newstate = ProgressState.inCHECK; // Promotion to approval + Revision myvers = new Revision(version); + if (myvers.isMinor()) { + version = myvers.incrementAs(newstate).toString(); + // TODO: If my physical file is programatically editable, update its (property) version number + // ISSUE: What about attached files such as PDF if exist, should we remove them ? + } + } else if (state == ProgressState.inCHECK) { + newstate = ProgressState.APPROVED; + } + this.state = newstate; + if (stamp != null) + this.addRelation(stamp.getContext()); + Database.getSession().update(this); + return true; + } + + /** + * Decrements the reference count of this document following the removal of a Publication from a Study step. + * + * @see #hold() + */ + public void release() { + // ------------------------- + countag -= 1; + if (this.isSaved()) + Database.getSession().update(this); + } + + protected void rename(String title) throws InvalidPropertyException { + // ------------------------------------ + if (title.length() == 0) + throw new InvalidPropertyException("name"); + + Calendar current = Calendar.getInstance(); + this.name = title; + this.lasdate = current.getTime(); // Today + Database.getSession().update(this); + } + + public void updateAs(Revision newvers) { + // ------------------------------------------ + version = newvers.setBranch(version).toString(); // Branch names are propagated by the versionning + ProgressState newstate = ProgressState.inCHECK; + if (newvers.isMinor()) + newstate = ProgressState.inWORK; + state = null; // Just to tell updateAs(sate) to not increment the version number + updateAs(newstate); + } + + public void updateAs(ProgressState state) { + // --------------------------------------------- + Document previous = null; + + // Set of version number + if (state == ProgressState.EXTERN) { + if (this.state != ProgressState.EXTERN) + this.version = null; // Strange use-case... + } else { + Revision myvers = new Revision(version); + if (!myvers.isNull()) { // Versionning context + for (Iterator i = getAllRelations().iterator(); i + .hasNext();) { + Relation link = i.next(); + if (!link.getClass().equals(VersionsRelation.class)) + continue; + previous = (Document) link.getTo(); // Versioned document + break; + } + } + if (this.state != null) + myvers.incrementAs(state); // Incrementation if the reversion number is not imposed + this.version = myvers.toString(); + } + // Update this document and the previous version, if exit + Session session = Database.getSession(); + if (previous != null) { + previous.history += 1; + session.update(previous); + } + this.state = state; + session.update(this); + } + + // protected void upgrade () { + // ------------------------- + // if (this.state != ProgressState.inWORK) return; + // + // Calendar current = Calendar.getInstance(); + // for (Iterator i=getAllRelations().iterator(); i.hasNext();) { + // Relation link = i.next(); + // if (!link.getClass().equals(UsesRelation.class)) continue; + // + // Document used = (Document)link.getTo(); + // if (!used.isVersioned()) continue; + // TODO: Update the uses relation + // } + // this.promote(); + // this.lasdate = current.getTime(); // Today + // Database.getSession().update(this); + // + // TODO: Promote documents using this one + // } + + // ============================================================================================================================== + // Private services + // ============================================================================================================================== + + private String generateEncodedName(Study scope) { + // ------------------------------------------------ + StringBuffer encoding = new StringBuffer(); + FileNaming scheme = getProjectSettingsService().getFileNamingScheme(); + DecimalFormat tostring = new DecimalFormat(suformat); + + int number = getStudyService().generateLocalIndex(scope); + + if (scheme == FileNaming.encoded) { + encoding.append(scope.getReference()).append(".") + .append(tostring.format(number)); + } else { // title and (temporarily) asis + encoding.append(name).append(".").append(tostring.format(number)); + } + return encoding.toString(); + } + + private String getEncodedRootName(Study scope) { + // ----------------------------------------------- + FileNaming scheme = getProjectSettingsService().getFileNamingScheme(); + + if (scheme == FileNaming.encoded) + return scope.getReference(); + else + return name; + } + + /** + * @return + */ + private ProjectSettingsService getProjectSettingsService() { + return _projectSettingsService; + } + + public void setProjectSettingsService( + ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } + + /** + * @return + */ + public StudyService getStudyService() { + return _studyService; + } + + public void setStudyService(StudyService studyService) { + _studyService = studyService; + } +} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/DocumentType.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/DocumentType.java similarity index 84% rename from Workspace/Siman-Common/src/org/splat/som/DocumentType.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/DocumentType.java index 2e0f169..29288c5 100644 --- a/Workspace/Siman-Common/src/org/splat/som/DocumentType.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/DocumentType.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin @@ -9,10 +9,13 @@ import java.util.List; import java.util.Set; import java.util.HashSet; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.dao.som.Database; import org.splat.kernel.InvalidPropertyException; import org.splat.kernel.MissedPropertyException; import org.splat.kernel.MultiplyDefinedException; -import org.splat.kernel.Persistent; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.service.technical.ProjectSettingsService.Step; public class DocumentType extends Persistent { @@ -53,12 +56,12 @@ public class DocumentType extends Persistent { this.name = name; return this; } - public Properties setResult (ProjectSettings.Step step) + public Properties setResult (ProjectSettingsService.Step step) { this.result = String.valueOf(step.getNumber()); return this; } - public Properties setStep (ProjectSettings.Step... step) + public Properties setStep (ProjectSettingsService.Step... step) { this.step = "-"; for (int i=0; i step = ProjectSettings.getAllSteps(); - ProjectSettings.Step lastep = step.get( step.size()-1 ); - return (this.isResultOf(lastep)); - } - /** * Checks if documents of this type are result of the given study step. * * @param step the involved study step * @return true if documents of this type are result of the given step. - * @see #isContentInto(org.splat.som.ProjectSettings.Step) + * @see #isContentInto(org.splat.service.technical.ProjectSettingsServiceImpl.Step) * @see #isStepResult() * @see #isStudyResult() */ - public boolean isResultOf (ProjectSettings.Step step) { + public boolean isResultOf (ProjectSettingsService.Step step) { // ----------------------------------------------------- if (result == null) return false; return (Integer.valueOf(result) == step.getNumber()); diff --git a/Workspace/Siman-Common/src/org/splat/som/Entity.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Entity.java similarity index 81% rename from Workspace/Siman-Common/src/org/splat/som/Entity.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/Entity.java index 16b87ce..13b9b50 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Entity.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Entity.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * Class whose only purpose is to represent the kernel's Entity class for propagating to this package the visibility of relations * and attributes editing functions. @@ -9,15 +9,15 @@ package org.splat.som; import java.util.Set; -import org.splat.kernel.Persistent; -import org.splat.kernel.Relation; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.Relation; import org.splat.kernel.ObjectProperties; import org.splat.kernel.InvalidPropertyException; import org.splat.kernel.MissedPropertyException; import org.splat.kernel.MultiplyDefinedException; -public abstract class Entity extends org.splat.kernel.Entity { +public abstract class Entity extends org.splat.dal.bo.kernel.Entity { // ============================================================================================================================== // Constructors @@ -36,17 +36,17 @@ public abstract class Entity extends org.splat.kernel.Entity { // Protected services // ============================================================================================================================== - protected Relation addRelation (Relation link) { + public Relation addRelation (Relation link) { // ---------------------------------------------- return super.addRelation(link); } - protected Set getAllRelations () { + public Set getAllRelations () { // ------------------------------------------ return super.getAllRelations(); } - protected void removeRelation (Class type, Persistent to) { + public void removeRelation (Class type, Persistent to) { // ----------------------------------------------------------------------------- super.removeRelation(type, to); } diff --git a/Workspace/Siman-Common/src/org/splat/som/File.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/File.hbm.xml similarity index 72% rename from Workspace/Siman-Common/src/org/splat/som/File.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/File.hbm.xml index 86371b2..b586829 100644 --- a/Workspace/Siman-Common/src/org/splat/som/File.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/File.hbm.xml @@ -8,13 +8,13 @@ - + - - - + @@ -27,6 +27,6 @@ - + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/File.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/File.java similarity index 97% rename from Workspace/Siman-Common/src/org/splat/som/File.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/File.java index 1a1d37d..9da39a7 100644 --- a/Workspace/Siman-Common/src/org/splat/som/File.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/File.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * Class of meta files representing physical files under the control of Study Manager. * Typically, the files represented by this class are source files of Documents and exports in different formats. @@ -13,7 +13,8 @@ package org.splat.som; import java.util.Calendar; import java.util.Date; -import org.splat.kernel.Persistent; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.dao.som.Database; public class File extends Persistent { diff --git a/Workspace/Siman-Common/src/org/splat/som/IDBuilder.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/IDBuilder.hbm.xml similarity index 86% rename from Workspace/Siman-Common/src/org/splat/som/IDBuilder.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/IDBuilder.hbm.xml index 90520be..1ba9b8f 100644 --- a/Workspace/Siman-Common/src/org/splat/som/IDBuilder.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/IDBuilder.hbm.xml @@ -8,7 +8,7 @@ - + diff --git a/Workspace/Siman-Common/src/org/splat/som/IDBuilder.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/IDBuilder.java similarity index 94% rename from Workspace/Siman-Common/src/org/splat/som/IDBuilder.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/IDBuilder.java index 5bc18f9..294ae8e 100644 --- a/Workspace/Siman-Common/src/org/splat/som/IDBuilder.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/IDBuilder.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin @@ -9,6 +9,8 @@ import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Date; +import org.splat.dal.dao.som.Database; + public class IDBuilder { @@ -22,7 +24,7 @@ public class IDBuilder { protected IDBuilder () { } - protected IDBuilder (Date date) { + public IDBuilder (Date date) { // ------------------------------- SimpleDateFormat get = new SimpleDateFormat("yyyy"); String year = get.format(date); @@ -34,7 +36,7 @@ public class IDBuilder { // Public member functions // ============================================================================================================================== - protected String buildReference (String pattern, Study study) { + public String buildReference (String pattern, Study study) { // ------------------------------------------------------------- char[] format = pattern.toCharArray(); char[] ref = new char[80]; // Better evaluate the length of the generated string diff --git a/Workspace/Siman-Common/src/org/splat/som/KnowledgeElement.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/KnowledgeElement.hbm.xml similarity index 74% rename from Workspace/Siman-Common/src/org/splat/som/KnowledgeElement.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/KnowledgeElement.hbm.xml index 4b63058..15a2c12 100644 --- a/Workspace/Siman-Common/src/org/splat/som/KnowledgeElement.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/KnowledgeElement.hbm.xml @@ -8,13 +8,13 @@ - + - - - + @@ -39,16 +39,16 @@ - + - - + + - + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/KnowledgeElement.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/KnowledgeElement.java similarity index 84% rename from Workspace/Siman-Common/src/org/splat/som/KnowledgeElement.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/KnowledgeElement.java index 2df8aad..0923f9f 100644 --- a/Workspace/Siman-Common/src/org/splat/som/KnowledgeElement.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/KnowledgeElement.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin @@ -13,8 +13,9 @@ import java.util.Vector; import org.hibernate.Session; -import org.splat.kernel.Persistent; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.dao.som.Database; import org.splat.kernel.InvalidPropertyException; import org.splat.kernel.MissedPropertyException; import org.splat.kernel.MultiplyDefinedException; @@ -82,28 +83,28 @@ public class KnowledgeElement extends Persistent { } // - Protected services - protected User getActor () { + public User getActor () { return actor; } - protected User getAuthor () { + public User getAuthor () { return author; } - protected ProgressState getProgressState () { + public ProgressState getProgressState () { return state; } - protected String getReference () { + public String getReference () { return kid; } - protected List getSimulationContexts () { + public List getSimulationContexts () { return context; } - protected String getTitle () { + public String getTitle () { return title; } - protected KnowledgeElementType getType () { + public KnowledgeElementType getType () { return type; } - protected Visibility getVisibility () { + public Visibility getVisibility () { return visibility; } // - Property setters @@ -124,7 +125,7 @@ public class KnowledgeElement extends Persistent { this.date = date; return this; } - protected Properties setOwnerScenario (Scenario owner) + public Properties setOwnerScenario (Scenario owner) { this.owner = owner; return this; @@ -187,7 +188,7 @@ public class KnowledgeElement extends Persistent { protected KnowledgeElement () { } // Internal constructor - protected KnowledgeElement (Properties kprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { + public KnowledgeElement (Properties kprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { super(kprop); // Throws one of the above exception if not valid type = kprop.type; owner = kprop.owner; @@ -220,20 +221,6 @@ public class KnowledgeElement extends Persistent { // Public member functions // ============================================================================================================================== - public boolean approve () { -// ------------------------- - if (state != ProgressState.inCHECK) return false; - state = ProgressState.APPROVED; - return updateMe(); - } - - public boolean demote () { -// ------------------------ - if (state != ProgressState.APPROVED && state != ProgressState.inCHECK) return false; - state = ProgressState.inDRAFT; - return updateMe(); - } - public boolean equals (KnowledgeElement given) { // ---------------------------------------------- if (isSaved()) return (this.getIndex() == given.getIndex()); @@ -288,20 +275,6 @@ public class KnowledgeElement extends Persistent { return getOwnerScenario().getOwnerStudy().getVisibility(); } - public boolean promote () { -// ------------------------- - if (state != ProgressState.inDRAFT) return false; - state = ProgressState.inCHECK; - return updateMe(); - } - - public void rename (String title) throws InvalidPropertyException { -// --------------------------------- - if (title.length() == 0) throw new InvalidPropertyException("name"); - this.title = title; - updateMe(); - } - public void update (String description) { // --------------------------------------- value = description.trim(); @@ -359,20 +332,16 @@ public class KnowledgeElement extends Persistent { return (KnowledgeElementType)Database.getSession().createQuery(query.toString()).uniqueResult(); } -// ============================================================================================================================== -// Protected services -// ============================================================================================================================== - - protected boolean updateMe () { -// ----------------------------- - try { - Database.getSession().update(this); - Database.getIndex().update(this); - return true; - } - catch (Exception error) { -// logger.error("Unable to re-index the knowledge '" + getIndex() + "', reason:", error); - return false; - } - } + /** + * @param aState knowledge element progress state to set + */ + public void setProgressState(ProgressState aState) { + state = aState; + } + /** + * @param aTitle a title to set + */ + public void setTitle(String aTitle) { + title = aTitle; + } } \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/KnowledgeElementType.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/KnowledgeElementType.java similarity index 95% rename from Workspace/Siman-Common/src/org/splat/som/KnowledgeElementType.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/KnowledgeElementType.java index 0b181cd..887ec92 100644 --- a/Workspace/Siman-Common/src/org/splat/som/KnowledgeElementType.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/KnowledgeElementType.java @@ -1,11 +1,12 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.Persistent; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.dao.som.Database; public class KnowledgeElementType extends Persistent { @@ -80,7 +81,7 @@ public class KnowledgeElementType extends Persistent { * For being able to get the studies in which simulation contexts are used, all study scenarios are indexed through this * knowledge element type, whether they include knowledge elements or not. */ - protected boolean reserve () { + public boolean reserve () { // ---------------------------- if (state != ProgressState.inCHECK) return false; this.state = ProgressState.inWORK; diff --git a/Workspace/Siman-Common/src/org/splat/som/ProgressState.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ProgressState.java similarity index 93% rename from Workspace/Siman-Common/src/org/splat/som/ProgressState.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/ProgressState.java index 338f14c..a33ad5d 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ProgressState.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ProgressState.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin diff --git a/Workspace/Siman-Common/src/org/splat/som/ProjectElement.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ProjectElement.hbm.xml similarity index 67% rename from Workspace/Siman-Common/src/org/splat/som/ProjectElement.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/ProjectElement.hbm.xml index 3f04647..d98a3cc 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ProjectElement.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ProjectElement.hbm.xml @@ -9,21 +9,22 @@ - + + - - + @@ -34,13 +35,13 @@ - + - + - + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/ProjectElement.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ProjectElement.java similarity index 82% rename from Workspace/Siman-Common/src/org/splat/som/ProjectElement.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/ProjectElement.java index ad56870..d728901 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ProjectElement.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ProjectElement.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin @@ -15,11 +15,13 @@ import java.util.Vector; import org.apache.log4j.Logger; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.dao.som.Database; import org.splat.kernel.ObjectProperties; -import org.splat.kernel.User; import org.splat.kernel.InvalidPropertyException; import org.splat.kernel.MissedPropertyException; import org.splat.kernel.MultiplyDefinedException; +import org.splat.som.Step; public abstract class ProjectElement extends Entity { @@ -35,7 +37,22 @@ public abstract class ProjectElement extends Entity { // Transient field private Step[] folders; - protected final static Logger logger = Logger.getLogger(ProjectElement.class); + /** + * Set the folders. + * @param folders the folders to set + */ + public void setFolders(Step[] folders) { + this.folders = folders; + } + /** + * Get the folders. + * @return the folders + */ + public Step[] getFolders() { + return folders; + } + + protected final static Logger logger = Logger.getLogger(ProjectElement.class); // ============================================================================================================================== // Constructors @@ -84,23 +101,15 @@ public abstract class ProjectElement extends Entity { return summary; // May be null } - public Step getFirstStep () { -// --------------------------- - return this.getSteps()[0]; - } - public Date getLastModificationDate () { -// -------------------------------------- return lasdate; } - public Step getLastStep () { -// -------------------------- - Step[] mystep = this.getSteps(); // For getting the folders length, if null - return mystep[mystep.length-1]; + public void setLastModificationDate (Date aDate) { + lasdate = aDate; } -/** + /** * Returns the publication into this Project Element of the given document version, if exists. * If exists, a document publication id unique in a given ProjectElement. * @@ -117,25 +126,15 @@ public abstract class ProjectElement extends Entity { return null; } - public Step[] getSteps () { -// ------------------------- - if (folders == null) { - List steps = ProjectSettings.getStepsOf(this.getClass()); - Iterator nstep = steps.iterator(); - - folders = new Step[steps.size()]; - for (int i=0; i - + - - - + @@ -27,6 +27,6 @@ - + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Publication.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Publication.java new file mode 100644 index 0000000..0ce5c59 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Publication.java @@ -0,0 +1,251 @@ +package org.splat.dal.bo.som; +/** + * Publication objects are the way to reference document versions from a Project Element. + * As such, a Document version is added (or published) to a Project Element through a Publication object. + * This publication is done by saving the Publication object produced when creating and versioning a Document from a given + * Project Element Step (call of the saveAs() function).
+ *
+ * A Publication object is homogeneous to a reference to a Document version and belongs to one Project Element, this latter + * being either a Study Scenario or a Study itself, depending on the Study Step to which the document is published.
+ *
+ * The document version referenced by a Publication object is the Value of the publication. + * + * @see Document + * @see ProjectElement + * @see Step + * @author Daniel Brunier-Coulin + * @copyright OPEN CASCADE 2012 + */ + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.hibernate.Session; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.Relation; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.dao.som.Database; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.NotApplicableException; +import org.splat.manox.Reader; +import org.splat.manox.Toolbox; +import org.splat.som.DocumentRights; +import org.splat.som.Revision; +import org.splat.som.Step; + + +public class Publication extends Persistent { + +// Persistent fields + private Document mydoc; + private ProjectElement owner; // Either Study or Scenario, depending on the step involved by the publication + private char isnew; // True if this references a document version new for the owner project element + +// Transient fields + private Step mystep; + +// ============================================================================================================================== +// Construction +// ============================================================================================================================== + +/** + * Get the mystep. + * @return the mystep + */ + public Step getStep() { + return mystep; + } + /** + * Set the mystep. + * @param aStep the mystep to set + */ + public void setStep(Step aStep) { + this.mystep = aStep; + } + // Database fetch constructor + public Publication () { +// ------------------------ + mystep = null; + } +// Internal constructors + public Publication (Document doc, ProjectElement publisher) { +// -------------------------------------------------------------- + mydoc = doc; + mystep = null; + owner = publisher; + isnew = 'Y'; + } +// ============================================================================================================================== +// Member functions +// ============================================================================================================================== + + public Relation addDependency (Publication to) { +// ---------------------------------------------- + return this.addDependency(to.value()); + } + + public Relation addDependency (Document to) { +// ------------------------------------------- + if (to == null) return null; + else return mydoc.addRelation( new UsesRelation(mydoc, to) ); + } + +/** + * Undo the out-date operation. + * + * @return true if the acceptance succeeds + * @see #outdate() + * @see DocumentRights#canAccept() + */ + public boolean actualize () { +// --------------------------- + if (!this.isOutdated()) return false; + isnew = 'Y'; + Database.getSession().update(this); + return true; + } + + public ConvertsRelation attach (String format) { +// ---------------------------------------------- + return mydoc.attach(format); + } + + public ConvertsRelation attach (String format, String description) { +// ------------------------------------------------------------------ + return mydoc.attach(format, description); + } + +/** + * Returns either the Study Scenario or the Study itself to which this publication belongs, depending on the Study Step into + * which the referenced document has been published.
+ * If this publication belongs to a Study, the Project Element returned is the Study returned by getOwnerStudy(). + * + * @return the Study Scenario or the Study to which this publication belongs to + * @see #getOwnerStudy() + */ + public ProjectElement getOwner () { +// --------------------------------- + return owner; + } + + /** + * Set the owner. + * @param owner the owner to set + */ + public void setOwner(ProjectElement owner) { + this.owner = owner; + } + + public Study getOwnerStudy () { +// ----------------------------- + if (owner instanceof Study) return (Study)owner; + else return ((Scenario)owner).getOwnerStudy(); + } + +/** + * Returns the state of this published document. + * It is the same than the state of the referenced document, unless this publication is out-of-date, in which case it is + * In-Work state. + * + * @see #outdate() + * @see #isOutdated() + */ + public ProgressState getProgressState () { +// ---------------------------------------- + if (this.isOutdated()) return ProgressState.inWORK; // Overrides the document state + else return mydoc.getProgressState(); + } + + public List getRelations (Class type) { +// ---------------------------------------------------------------------- + if (type == null) return null; + + List result = new ArrayList(); + List relist = mydoc.getRelations(type); + for (Iterator i=relist.iterator(); i.hasNext();) { + Relation relation = i.next(); + Document relatedoc = (Document)relation.getTo(); + Publication related = owner.getPublication(relatedoc); + if (related != null) { + result.add(related); + } else + if (owner instanceof Scenario) { // The relation may cross steps belonging to a scenario and its owner study + related = ((Scenario)owner).getOwnerStudy().getPublication(relatedoc); + if (related != null) result.add(related); + } + } + return result; + } + + public File getSourceFile () { +// ---------------------------- + return mydoc.getSourceFile(); + } + + public boolean isNewForOwner () { +// ------------------------------- + return (isnew == 'Y'); + } + + public boolean isOutdated () { +// ---------------------------- + return (isnew == 'O'); + } + + /** + * Get the isnew. + * @return the isnew + */ + public char getIsnew() { + return isnew; + } + /** + * Set the isnew. + * @param isnew the isnew to set + */ + public void setIsnew(char isnew) { + this.isnew = isnew; + } + public void rename (String title) throws InvalidPropertyException { +// --------------------------------- + mydoc.rename(title); + } + +/** + * Out-dates this publication and recursively all publications using this one. + * Typically, a publication is out-dated when modifying a document to which it depends. + * + * @see #isOutdated() + * @see #getProgressState() + * @see #actualize() + */ + public void outdate () { +// ---------------------- + if (this.isOutdated()) return; + + List relist = this.getRelations(UsedByRelation.class); + for (Iterator i = relist.iterator(); i.hasNext(); ) { + i.next().outdate(); + } + isnew = 'O'; + Database.getSession().update(this); + } + +/** + * Returns the document version referenced by this Publication. + */ + public Document value () { +// ------------------------ + return mydoc; + } + /** + * Set the mydoc. + * @param mydoc the mydoc to set + */ + public void setValue (Document aDoc) { + this.mydoc = aDoc; + } +} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Relations.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Relations.hbm.xml new file mode 100644 index 0000000..6ec782e --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Relations.hbm.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/Scenario.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Scenario.hbm.xml similarity index 80% rename from Workspace/Siman-Common/src/org/splat/som/Scenario.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/Scenario.hbm.xml index d2cf503..efad6f2 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Scenario.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Scenario.hbm.xml @@ -8,7 +8,7 @@ - + @@ -22,7 +22,7 @@ - + diff --git a/Workspace/Siman-Common/src/org/splat/som/Scenario.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Scenario.java similarity index 66% rename from Workspace/Siman-Common/src/org/splat/som/Scenario.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/Scenario.java index 845fb6d..7a822e1 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Scenario.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Scenario.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin @@ -19,11 +19,15 @@ import java.util.Vector; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; -import org.splat.kernel.Persistent; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.dao.som.Database; import org.splat.kernel.InvalidPropertyException; import org.splat.kernel.MissedPropertyException; import org.splat.kernel.MultiplyDefinedException; -import org.splat.kernel.User; +import org.splat.service.StepService; +import org.splat.service.technical.IndexService; +import org.splat.som.Step; public class Scenario extends ProjectElement { @@ -69,16 +73,16 @@ public class Scenario extends ProjectElement { } // - Protected services - protected Step getBaseStep () { + public Step getBaseStep () { return base; // May be null } - protected Scenario getInsertAfter () { + public Scenario getInsertAfter () { return previous; // May be null } - protected User getManager () { + public User getManager () { return manager; } - protected Properties setOwnerStudy (Study owner) + public Properties setOwnerStudy (Study owner) { this.owner = owner; return this; @@ -134,7 +138,7 @@ public class Scenario extends ProjectElement { ucase = null; } // Internal constructor - protected Scenario (Properties sprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { + public Scenario (Properties sprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { // ------------------------------------- super(sprop); // Throws one of the above exception if not valid owner = sprop.owner; @@ -161,72 +165,12 @@ public class Scenario extends ProjectElement { Scenario[] scene = owner.getScenarii(); for (int i=0; i this.sid) this.sid = scene[i].sid; sid += 1; - if (sprop.base != null) copyContentsUpTo(sprop.base); } // ============================================================================================================================== // Public member functions // ============================================================================================================================== - public KnowledgeElement addKnowledgeElement (KnowledgeElement.Properties kprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { -// ------------------------------------------------------------------------------- - KnowledgeElement kelm = new KnowledgeElement(kprop.setOwnerScenario(this)); - Session session = Database.getSession(); - Transaction transax = session.getTransaction(); - try { - session.save(kelm); -// Update of my persistent data - kelms.add(kelm); -// Update of my transient data - List known = getKnowledgeElementsOf(kelm.getType()); // Initializes this.known, if not yet done - known.add(kelm); - if (kelm.getType().equals("usecase")) { - ucase = kelm; - } else - if (knowl != null) { // If null, knowl will be initialized when needed - knowl.add(kelm); - } -// Update of the index of Knowledge Elements - Database.getIndex().add(kelm); - updateMe(); - return kelm; - } - catch (RuntimeException e) { - if (transax != null && transax.isActive()) { -// Second try-catch as the rollback could fail as well - try { - transax.rollback(); - } catch (HibernateException error) { - Study.logger.debug("Error rolling back transaction", error); - } -// Throw again the first exception - throw e; - } - return null; - } - catch (IOException error) { - logger.error("Unable to index the knowedge element '" + kelm.getIndex() + "', reason:", error); - return null; - } - } - - public void checkin () { -// ---------------------- - cuser = null; - lasdate = Calendar.getInstance().getTime(); - Database.getSession().update(this); - } - - public boolean checkout (User user) { -// ----------------------------------- - if (!owner.isStaffedBy(user)) return false; - - cuser = user; - lasdate = Calendar.getInstance().getTime(); - Database.getSession().update(this); - return true; - } - public List getAllKnowledgeElements () { // -------------------------------------------------------- if (knowl == null) { @@ -280,10 +224,13 @@ public class Scenario extends ProjectElement { } public User getUser () { -// ---------------------- return cuser; // Null if the scenario has not been checked-out } + public void setUser (User aUser) { + cuser = aUser; // Null if the scenario has not been checked-out + } + public boolean removeKnowledgeElement (KnowledgeElement kelm) { // ------------------------------------------------------------- KnowledgeElement torem = getKnowledgeElement(kelm.getIndex()); @@ -307,30 +254,13 @@ public class Scenario extends ProjectElement { return (cuser != null); } - public boolean isEmpty () { -// ------------------------- - Step[] mystep = this.getSteps(); - for (int i=0; i i=kelms.iterator(); i.hasNext(); ) { KnowledgeElement kelm = i.next(); @@ -344,38 +274,22 @@ public class Scenario extends ProjectElement { // ============================================================================================================================== // Private services // ============================================================================================================================== - - private void copyContentsUpTo (Step lastep) { -// ------------------------------------------- - Scenario base = (Scenario)lastep.getOwner(); - Step[] from = base.getSteps(); - Step[] to = this.getSteps(); - for (int i=0; i lastep.getNumber()) break; - - List docs = step.getAllDocuments(); - for (Iterator j=docs.iterator(); j.hasNext(); ) { - Publication doc = j.next().copy(this); // Creation of a new reference to the document -// Database.getSession().save(doc); Publications MUST be saved later through cascading when saving the scenario - to[i].add(doc); - } - List ctex = step.getAllSimulationContexts(); - for (Iterator j=ctex.iterator(); j.hasNext(); ) { - to[i].addSimulationContext(j.next()); - } - } - } - - private boolean updateMe () { -// --------------------------- - try { - Database.getSession().update(this); // Update of relational base - return true; - } - catch (Exception error) { - logger.error("Unable to re-index the knowledge element '" + getIndex() + "', reason:", error); - return false; - } - } + /** + * @return + */ + public Set getKnowledgeElements() { + return kelms; + } + /** + * @param kelm + */ + public void setUcase(KnowledgeElement kelm) { + ucase = kelm; + } + /** + * @return + */ + public List getKnowledgeElementsList() { + return knowl; + } } \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/SimulationContext.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/SimulationContext.hbm.xml similarity index 87% rename from Workspace/Siman-Common/src/org/splat/som/SimulationContext.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/SimulationContext.hbm.xml index adae01e..48fab32 100644 --- a/Workspace/Siman-Common/src/org/splat/som/SimulationContext.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/SimulationContext.hbm.xml @@ -8,12 +8,12 @@ - + - + - + diff --git a/Workspace/Siman-Common/src/org/splat/som/SimulationContext.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/SimulationContext.java similarity index 92% rename from Workspace/Siman-Common/src/org/splat/som/SimulationContext.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/SimulationContext.java index 03c159e..a91d0af 100644 --- a/Workspace/Siman-Common/src/org/splat/som/SimulationContext.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/SimulationContext.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin @@ -10,10 +10,13 @@ import java.util.List; import org.hibernate.Session; -import org.splat.kernel.Persistent; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.dao.som.Database; import org.splat.kernel.InvalidPropertyException; import org.splat.kernel.MissedPropertyException; import org.splat.kernel.MultiplyDefinedException; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.som.Step; public class SimulationContext extends Persistent implements Serializable { @@ -34,7 +37,7 @@ public class SimulationContext extends Persistent implements Serializable { public static class Properties extends Persistent.Properties { // ------------------------------------------------------------ private SimulationContextType type = null; - private ProjectSettings.Step step = null; + private ProjectSettingsService.Step step = null; private ProgressState state = null; private String value = null; @@ -47,7 +50,7 @@ public class SimulationContext extends Persistent implements Serializable { state = null; value = null; } - protected ProgressState getProgressState () { + public ProgressState getProgressState () { return state; } public SimulationContextType getType () { @@ -67,7 +70,7 @@ public class SimulationContext extends Persistent implements Serializable { this.state = state; return this; } - protected Properties setStep (ProjectSettings.Step step) throws InvalidPropertyException + public Properties setStep (ProjectSettingsService.Step step) throws InvalidPropertyException { this.step = step; return this; @@ -97,7 +100,7 @@ public class SimulationContext extends Persistent implements Serializable { protected SimulationContext () { } // Internal constructor - protected SimulationContext (Properties kprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { + public SimulationContext (Properties kprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { super(kprop); // Throws one of the above exception if not valid type = kprop.type; step = kprop.step.getNumber(); @@ -156,7 +159,7 @@ public class SimulationContext extends Persistent implements Serializable { // Public services // ============================================================================================================================== - public static SimulationContextType createType (String name, ProjectSettings.Step step) throws InvalidPropertyException, RuntimeException { + public static SimulationContextType createType (String name, ProjectSettingsService.Step step) throws InvalidPropertyException, RuntimeException { // --------------------------------------------------------------------------------------- //TODO: Check for duplicate definition SimulationContextType type = new SimulationContextType(name, step); @@ -175,7 +178,7 @@ public class SimulationContext extends Persistent implements Serializable { } @SuppressWarnings("unchecked") - public static List selectTypesOf (ProjectSettings.Step... step) { + public static List selectTypesOf (ProjectSettingsService.Step... step) { // -------------------------------------------------------------------------------------- StringBuffer query = new StringBuffer("from SimulationContextType where step='").append(step[0].getNumber()).append("'"); for (int i=1; i - - org.splat.som.ProgressState + + org.splat.dal.bo.som.ProgressState - - org.splat.som.Visibility + + org.splat.dal.bo.som.Visibility - + @@ -34,7 +34,7 @@ - + diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Study.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Study.java new file mode 100644 index 0000000..7fdb682 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Study.java @@ -0,0 +1,448 @@ +package org.splat.dal.bo.som; +/** + * + * @author Daniel Brunier-Coulin + * @copyright OPEN CASCADE 2012 + */ + +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.LinkedList; +import java.util.Set; +import java.util.Vector; + +import org.hibernate.Session; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.Relation; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.dao.som.Database; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.UserDirectory; +import org.splat.service.StepService; +import org.splat.service.technical.IndexServiceImpl; +import org.splat.service.technical.ProjectSettingsServiceImpl; +import org.splat.service.technical.ProjectSettingsServiceImpl.ProjectSettingsValidationCycle; +import org.splat.som.Revision; + + +public class Study extends ProjectElement { + +// Persistent fields + private String sid; // External unique reference in a format conform to the configuration pattern + private int docount; // Total number of documents of this study, including versions + private ProgressState state; + private Visibility visibility; + private List scenarii; + private String version; + private int history; // Number of studies versioning this one, if any + +// Transient fields + private List contributor; // Shortcut to contributors + private HashMap validactor; // Shortcut to validation cycles + private Set actor; // Summary of above actors + private StepService _stepService; + +// ============================================================================================================================== +// Construction +// ============================================================================================================================== + +// Fields initialization class + public static class Properties extends Persistent.Properties { +// ------------------------------------------------------------ + private String sid = null; // Search criterion only + private String title = null; + private String summary = null; + private User manager = null; + private User actor = null; // Search criterion only + private Visibility visibility = null; // Search criterion only + private ProgressState state = null; // Search criterion only + private Date date = null; + private List context = new Vector(); // Search criterion only + +// - Public services + + public void clear () { + super.clear(); + sid = null; + title = null; + summary = null; + manager = null; + actor = null; + visibility = null; + state = null; + date = null; + context = new Vector(); // as clear() may generate side effects + } + public Properties copy () { + Properties copy = new Properties(); + copy.sid = this.sid; + copy.title = this.title; + copy.summary = this.summary; + copy.manager = this.manager; + copy.actor = this.actor; + copy.visibility = this.visibility; + copy.state = this.state; + copy.date = this.date; + copy.context = this.context; + return copy; + } +// - Protected services + + public User getActor () { + return actor; + } + public User getManager () { + return manager; + } + public ProgressState getProgressState () { + return state; + } + public String getReference () { + return sid; + } + public List getSimulationContexts () { + return context; + } + public String getTitle () { + return title; + } + public String getSummary () { + return summary; + } + public Visibility getVisibility () { + return visibility; + } +// - Property setters + +// For building a search query + public Properties setActor (User actor) + { + this.actor = actor; + return this; + } + public Properties setDate (Date date) + { + this.date = date; + return this; + } + public Properties setDescription (String summary) + { + if (summary.length() > 0) this.summary = summary; + return this; + } + public Properties setManager (User user) + { + this.manager = user; + return this; + } +// For building a search query + public Properties setReference (String sid) throws InvalidPropertyException + { + if (sid.length() == 0) throw new InvalidPropertyException("reference"); + this.sid = sid; + return this; + } +// For building a search query + public Properties setSimulationContexts (List context) { + this.context = context; + return this; + } +// For building a search query + public Properties setState (ProgressState state) + { + this.state = state; + return this; + } + public Properties setTitle (String title) throws InvalidPropertyException + { + if (title.length() == 0) throw new InvalidPropertyException("title"); + this.title = title; + return this; + } +// For building a search query + public Properties setVisibility (Visibility area) + { + this.visibility = area; + return this; + } +// - Global validity check + + public void checkValidity() throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException + { + if (title == null) throw new MissedPropertyException("title"); + if (manager == null) throw new MissedPropertyException("manager"); + } + } +// Database fetch constructor + protected Study () { +// ------------------ + contributor = null; + validactor = null; + actor = null; + } +// Internal constructor + public Study (Properties sprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { +// ---------------------------------- + super(sprop); // Throws one of the above exception if not valid + sid = sprop.sid; // Reset after save + title = sprop.title; // Inherited attribute + manager = sprop.manager; + docount = 0; + history = 0; + scenarii = new LinkedList(); + visibility = Visibility.PRIVATE; + state = ProgressState.inWORK; + + credate = sprop.date; // Inherited attribute + if (credate == null) { + Calendar current = Calendar.getInstance(); + credate = current.getTime(); // Today + } + lasdate = credate; // Inherited attribute + version = new Revision().incrementAs(state).toString(); + + if (sprop.summary != null) this.setAttribute( new DescriptionAttribute(this, sprop.summary) ); + + contributor = null; + validactor = null; + actor = null; + } + + /** + * Returns all actors of this study other than the author, including contributors, reviewers and approvers. + * + * @return the actors of this study + * @see #hasActor(User) + */ + public Set getActors () { + // ----------------------------- + if (actor == null) setShortCuts(); + return Collections.unmodifiableSet(actor); + } + + /** + * Returns all actors of this study other than the author, including contributors, reviewers and approvers. + * + * @return the actors of this study + * @see #hasActor(User) + */ + public Set getModifiableActors () { + // ----------------------------- + if (actor == null) setShortCuts(); + return actor; + } + + public List getContributors () { + // ------------------------------------ + if (contributor == null) setShortCuts(); + return Collections.unmodifiableList(contributor); // May be empty + } + + public List getModifiableContributors () { + if (contributor == null) setShortCuts(); + return contributor; // May be empty + } + + public ProgressState getProgressState () { +// ---------------------------------------- + return state; + } + +/** + * Returns the global unique reference of this study. + * The study reference is common to all versions of the study (versioning a study does not change its reference). + * The form of this study reference is defined in the configuration of the application server - see the SOM XML customization + * file. + */ + public String getReference () { + return sid; + } + + public void setReference (String aReference) { + sid = aReference; + } + + public Scenario[] getScenarii () { +// -------------------------------- + return scenarii.toArray(new Scenario[scenarii.size()]); + } + + public List getScenariiList () { +// -------------------------------- + return scenarii; + } + +/** + * Returns the validation cycle of the given document type. + * + * @param doc the document type being subject of validation + * @return the validation cycle of the document, or null if not defined. + */ + public ValidationCycle getValidationCycleOf (DocumentType type) { +// --------------------------------------------------------------- + if (validactor == null) setShortCuts(); + ValidationCycle result = validactor.get(type.getName()); + if (result == null) { + if (type.isStepResult()) result = validactor.get("default"); // "default" validation cycle defined in the configuration, if exist + if (result == null) result = validactor.get("built-in"); + } + return result; + } + + public String getVersion () { +// --------------------------- + return version; + } + + public Visibility getVisibility () { +// ---------------------------------- + return visibility; + } + +/** + * Checks if the given user is actor of this study. + * Actors include contributors, reviewers and approvers. + * + * @return true if the given user is actor of this study. + * @see #getActors() + */ + public boolean hasActor (User user) { +// ----------------------------------- + if (user == null) return false; + for (Iterator i=this.getActors().iterator(); i.hasNext(); ) { + User involved = i.next(); + if (involved.equals(user)) return true; + } + return false; + } + +/** + * Checks whether this study is in the Public or the Reference area of the repository. + * + * @return true if the study is public. + * @see #moveToPublic() + * @see #moveToReference() + */ + public boolean isPublic () { +// -------------------------- + return (visibility != Visibility.PRIVATE); + } +/** + * Checks if the given user participates to this study. + * The Study staff includes the author and contributors. + * + * @return true if the given user is actor of this study. + * @see #getContributors() + */ + public boolean isStaffedBy (User user) { +// -------------------------------------- + if (user == null) return false; + if (manager.equals(user)) return true; + for (Iterator i=getContributors().iterator(); i.hasNext();) { + if (i.next().equals(user)) return true; + } + return false; + } + + public boolean isVersioned () { +// ----------------------------- + return (history > 0); + } + + public boolean shares (Document doc) { +// ------------------------------------ + Scenario[] scene = this.getScenarii(); // If shared from within the study, the document is shared by the scenarios + int counter = 0; + + for (int i=0; i(); + validactor = new HashMap(); + actor = new HashSet(); + +// Get the contributors + for (Iterator i=getRelations(ContributorRelation.class).iterator(); i.hasNext(); ) { + ContributorRelation link = (ContributorRelation)i.next(); + contributor.add(link.getTo()); + } +// Get the validation cycles specific to this study + for (Iterator i=getRelations(ValidationCycleRelation.class).iterator(); i.hasNext(); ) { + ValidationCycleRelation link = (ValidationCycleRelation)i.next(); + validactor.put(link.getDocumentType().getName(), link.getTo()); // The associated document type is necessarily not null in this context + } +// Get the validation cycles coming from the configured workflow and not overridden in this study + for (Iterator i=ProjectSettingsServiceImpl.getAllValidationCycles().iterator(); i.hasNext(); ) { + ProjectSettingsServiceImpl.ProjectSettingsValidationCycle cycle = i.next(); + String type = cycle.getName(); + if (!validactor.containsKey(type)) validactor.put(type, new ValidationCycle(this, cycle)); + } +// Get all corresponding actors + for (Iterator i=validactor.values().iterator(); i.hasNext(); ) { + ValidationCycle cycle = i.next(); + User[] user = cycle.getAllActors(); + for (int j=0; j i=this.getAllRelations().iterator(); i.hasNext(); ) { + Relation link = i.next(); + Class kindof = link.getClass().getSuperclass(); + if (!kindof.equals(ActorRelation.class)) continue; + actor.add( ((ActorRelation)link).getTo() ); + } + } + /** + * @param aVisibility a study visibility to set + */ + public void setVisibility(Visibility aVisibility) { + visibility = aVisibility; + } + /** + * @param aState a study progress state to set + */ + public void setProgressState(ProgressState aState) { + state = aState; + } + /** + * @param string + */ + public void setVersion(String aVersion) { + version = aVersion; + } + /** + * @param i + */ + public void setLastLocalIndex(int anIndex) { + docount = anIndex; + } + /** + * @return + */ + public HashMap getValidationCycles() { + return validactor; + } +} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/Timestamp.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Timestamp.hbm.xml similarity index 63% rename from Workspace/Siman-Common/src/org/splat/som/Timestamp.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/Timestamp.hbm.xml index ea90b78..8a8f537 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Timestamp.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Timestamp.hbm.xml @@ -8,26 +8,27 @@ - - org.splat.som.ValidationStep + + org.splat.dal.bo.som.ValidationStep - + + - - + - + @@ -38,5 +39,5 @@ - + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/Timestamp.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Timestamp.java similarity index 92% rename from Workspace/Siman-Common/src/org/splat/som/Timestamp.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/Timestamp.java index 6884a4b..6593c54 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Timestamp.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Timestamp.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin @@ -8,9 +8,9 @@ package org.splat.som; import java.util.Comparator; import java.util.Date; -import org.splat.kernel.Any; -import org.splat.kernel.Attribute; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.Any; +import org.splat.dal.bo.kernel.Attribute; +import org.splat.dal.bo.kernel.User; public class Timestamp extends Any { @@ -36,7 +36,7 @@ public class Timestamp extends Any { protected Timestamp () { } // Internal constructors - protected Timestamp (ValidationStep type, Document from, User to, Date sdate) { + public Timestamp (ValidationStep type, Document from, User to, Date sdate) { // ----------------------------------------------------------------------------- super((Attribute)null); // For building the collection of attributes this.mytype = type; diff --git a/Workspace/Siman-Common/src/org/splat/som/UsedByRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsedByRelation.java similarity index 93% rename from Workspace/Siman-Common/src/org/splat/som/UsedByRelation.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/UsedByRelation.java index a498bdf..b5dedd1 100644 --- a/Workspace/Siman-Common/src/org/splat/som/UsedByRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsedByRelation.java @@ -1,12 +1,12 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.Persistent; -import org.splat.kernel.Relation; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.Relation; public class UsedByRelation extends Relation { diff --git a/Workspace/Siman-Common/src/org/splat/som/UsesRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsesRelation.java similarity index 90% rename from Workspace/Siman-Common/src/org/splat/som/UsesRelation.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/UsesRelation.java index 0c815c3..e153992 100644 --- a/Workspace/Siman-Common/src/org/splat/som/UsesRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsesRelation.java @@ -1,12 +1,12 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.Persistent; -import org.splat.kernel.Relation; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.Relation; public class UsesRelation extends Relation { @@ -21,7 +21,7 @@ public class UsesRelation extends Relation { protected UsesRelation () { } // Initialization constructors - protected UsesRelation (Document from, Document to) { + public UsesRelation (Document from, Document to) { // --------------------------------------------------- super(from); this.refer = to; diff --git a/Workspace/Siman-Common/src/org/splat/som/ValidationCycle.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycle.hbm.xml similarity index 79% rename from Workspace/Siman-Common/src/org/splat/som/ValidationCycle.hbm.xml rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycle.hbm.xml index eaa8978..0a60a67 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ValidationCycle.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycle.hbm.xml @@ -8,13 +8,13 @@ - + - - - + @@ -36,5 +36,5 @@ - + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/ValidationCycle.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycle.java similarity index 94% rename from Workspace/Siman-Common/src/org/splat/som/ValidationCycle.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycle.java index 42a569c..8803d52 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ValidationCycle.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycle.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * Class defining the validation cycle applicable to documents of a given type.
* A validation cycle specifies the validation steps of documents complying with by this cycle, as well as the actors @@ -29,12 +29,14 @@ package org.splat.som; import java.util.List; import java.util.Vector; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.dao.som.Database; import org.splat.kernel.InvalidPropertyException; import org.splat.kernel.MissedPropertyException; import org.splat.kernel.MultiplyDefinedException; -import org.splat.kernel.Persistent; -import org.splat.kernel.User; import org.splat.kernel.UserDirectory; +import org.splat.service.technical.ProjectSettingsServiceImpl; public class ValidationCycle extends Persistent { @@ -77,7 +79,7 @@ public class ValidationCycle extends Persistent { } // - Protected services - protected Properties setDocumentType (DocumentType type) + public Properties setDocumentType (DocumentType type) { doctype = type; return this; @@ -103,7 +105,7 @@ public class ValidationCycle extends Persistent { protected ValidationCycle () { } // Internal constructors - protected ValidationCycle (Study from, ProjectSettings.ValidationCycle cycle) { + protected ValidationCycle (Study from, ProjectSettingsServiceImpl.ProjectSettingsValidationCycle cycle) { // ----------------------------------------------------------------------------- Actor[] actype = cycle.getActorTypes(); User.Properties uprop = new User.Properties(); @@ -137,7 +139,7 @@ public class ValidationCycle extends Persistent { else if (i == 2) signatory = actor; } } - protected ValidationCycle (Study from, Properties vprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { + public ValidationCycle (Study from, Properties vprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { // -------------------------------------------------------- super(vprop); // Throws one of the above exception if not valid mytype = vprop.doctype; @@ -235,7 +237,7 @@ public class ValidationCycle extends Persistent { * * @return true if this validation cycle is a default one. * @see #getDocumentType() - * @see ProjectSettings#getNewValidationCycle() + * @see ProjectSettingsServiceImpl#getNewValidationCycle() */ public boolean isDefault () { // --------------------------- @@ -246,7 +248,7 @@ public class ValidationCycle extends Persistent { // Protected services // ============================================================================================================================== - protected ValidationCycleRelation getContext () { + public ValidationCycleRelation getContext () { // ----------------------------------------------- return context; } @@ -259,7 +261,7 @@ public class ValidationCycle extends Persistent { if (this.isSaved()) Database.getSession().update(this); } - protected void resetActors (Properties vprop) { + public void resetActors (Properties vprop) { // --------------------------------------------- publisher = vprop.publisher; // May be null reviewer = vprop.reviewer; // May be null diff --git a/Workspace/Siman-Common/src/org/splat/som/ValidationCycleRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycleRelation.java similarity index 93% rename from Workspace/Siman-Common/src/org/splat/som/ValidationCycleRelation.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycleRelation.java index dea6b5e..e3c4f15 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ValidationCycleRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycleRelation.java @@ -1,12 +1,12 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.Persistent; -import org.splat.kernel.Relation; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.Relation; public class ValidationCycleRelation extends Relation { diff --git a/Workspace/Siman-Common/src/org/splat/som/ValidationStep.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationStep.java similarity index 89% rename from Workspace/Siman-Common/src/org/splat/som/ValidationStep.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationStep.java index 95bfdee..4bcb16f 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ValidationStep.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationStep.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin diff --git a/Workspace/Siman-Common/src/org/splat/som/VersionsRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/VersionsRelation.java similarity index 90% rename from Workspace/Siman-Common/src/org/splat/som/VersionsRelation.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/VersionsRelation.java index d65cee2..c4e92cf 100644 --- a/Workspace/Siman-Common/src/org/splat/som/VersionsRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/VersionsRelation.java @@ -1,12 +1,12 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.Persistent; -import org.splat.kernel.Relation; +import org.splat.dal.bo.kernel.Persistent; +import org.splat.dal.bo.kernel.Relation; public class VersionsRelation extends Relation { @@ -29,14 +29,14 @@ public class VersionsRelation extends Relation { description = null; } // Initialization constructors - protected VersionsRelation (Document from, Document to) { + public VersionsRelation (Document from, Document to) { // ------------------------------------------------------- super(from); this.refer = to; this.got = true; this.description = null; // Conversion not described } - protected VersionsRelation (Document from, Document to, String description) { + public VersionsRelation (Document from, Document to, String description) { // --------------------------------------------------------------------------- super(from); this.refer = to; diff --git a/Workspace/Siman-Common/src/org/splat/som/Visibility.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Visibility.java similarity index 92% rename from Workspace/Siman-Common/src/org/splat/som/Visibility.java rename to Workspace/Siman-Common/src/org/splat/dal/bo/som/Visibility.java index e66d85c..6cfeeea 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Visibility.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Visibility.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.dal.bo.som; /** * * @author Daniel Brunier-Coulin diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Database.java b/Workspace/Siman-Common/src/org/splat/dal/dao/kernel/Database.java similarity index 76% rename from Workspace/Siman-Common/src/org/splat/kernel/Database.java rename to Workspace/Siman-Common/src/org/splat/dal/dao/kernel/Database.java index 88ef38f..ece7223 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Database.java +++ b/Workspace/Siman-Common/src/org/splat/dal/dao/kernel/Database.java @@ -1,4 +1,4 @@ -package org.splat.kernel; +package org.splat.dal.dao.kernel; /** * * @author Daniel Brunier-Coulin @@ -11,14 +11,38 @@ import java.sql.Statement; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.jdbc.Work; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.apache.log4j.Logger; -public abstract class Database { +public abstract class Database implements ApplicationContextAware { + /** + * The ApplicationContext. + */ + private static ApplicationContext _context = null; + + /** + * Spring will call this method for initialize the applicationContext. + * @param ctx the application context + * @throws BeansException the BeanException + */ + public void setApplicationContext(final ApplicationContext ctx) throws BeansException { + _context = ctx; + } + + /** + * Static for getting the context. + * @return ApplicationContext the application context + */ + public static ApplicationContext getContext() { + return _context; + } + // private static String CONFIG_FILE = "/hibernate.cfg.xml"; private static SessionFactory mySessionFactory = null; - protected static IDPool myIDpool = null; protected class CreateTables implements Work { // ----------------------------------------------- @@ -98,36 +122,19 @@ public abstract class Database { return getInstance().getCurrentSession(); } - public static void close () { -// --------------------------- - if (mySessionFactory != null) mySessionFactory.close(); - mySessionFactory = null; - } - - public static IDPool getIDPool () { -// --------------------------------- - if (myIDpool == null) myIDpool = new IDPool(getSession()); - return myIDpool; - } - // ============================================================================================================================== // Protected services // ============================================================================================================================== protected String getSchemaVersion () { // ------------------------------------ - return getIDPool().getSchemaVersion(); + return null;//TODO: Get schema version into specific object/table: getIDPool().getSchemaVersion(); } - protected void setIDPoolSize (int size) { -// --------------------------------------- - IDPool.setPoolSize(size); - } - protected void setSchemaVersion (String version) { // ------------------------------------------------ - myIDpool = new IDPool(version); - getSession().save(myIDpool); +//TODO: Set schema version into specific object/table: myIDpool = new IDPool(version); +// getSession().save(myIDpool); } // ============================================================================================================================== @@ -137,10 +144,11 @@ public abstract class Database { private static SessionFactory getInstance () { // -------------------------------------------- if (mySessionFactory == null) { - org.hibernate.cfg.Configuration cfg = new org.hibernate.cfg.Configuration(); - try { - cfg.configure(); // The configuration file (hibernate.cfg.xml)) is supposed to be on the classpath - mySessionFactory = cfg.buildSessionFactory(); +// org.hibernate.cfg.Configuration cfg = new org.hibernate.cfg.Configuration(); + try { + mySessionFactory = getContext().getBean(SessionFactory.class); +// cfg.configure(); // The configuration file (hibernate.cfg.xml)) is supposed to be on the classpath +// mySessionFactory = cfg.buildSessionFactory(); } catch (Exception error) { logger.fatal("Could not initialize the Hibernate configuration, reason:", error); diff --git a/Workspace/Siman-Common/src/org/splat/dal/dao/kernel/GenericDAO.java b/Workspace/Siman-Common/src/org/splat/dal/dao/kernel/GenericDAO.java new file mode 100644 index 0000000..5ea89df --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/dal/dao/kernel/GenericDAO.java @@ -0,0 +1,33 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 08.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.dal.dao.kernel; + +import java.io.Serializable; + +/** + * @author rkv + * + */ +public interface GenericDAO { + + /** Persist the newInstance object into database */ + PK create(T newInstance); + + /** + * Retrieve an object that was previously persisted to the database using the indicated id as primary key + */ + T read(PK id); + + /** Save changes made to a persistent object. */ + void update(T transientObject); + + /** Remove an object from persistent storage in the database */ + void delete(T persistentObject); +} diff --git a/Workspace/Siman-Common/src/org/splat/dal/dao/kernel/GenericDAOImpl.java b/Workspace/Siman-Common/src/org/splat/dal/dao/kernel/GenericDAOImpl.java new file mode 100644 index 0000000..c19d1cf --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/dal/dao/kernel/GenericDAOImpl.java @@ -0,0 +1,64 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 08.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.dal.dao.kernel; + +import java.io.Serializable; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; + +/** + * @author rkv + * + */ +public abstract class GenericDAOImpl implements + GenericDAO { + private SessionFactory _sessionFactory; + + public PK create(T o) { + return (PK) getSession().save(o); + } + + public T read(PK id) { + return (T) getSession().get(getType(), id); + } + + public void update(T o) { + getSession().update(o); + } + + public void delete(T o) { + getSession().delete(o); + } + + abstract protected Class getType(); + /** + * @return hibernate session + */ + private Session getSession() { + return getSessionFactory().getCurrentSession(); + } + + /** + * Get the sessionFactory. + * @return the sessionFactory + */ + public SessionFactory getSessionFactory() { + return _sessionFactory; + } + + /** + * Set the sessionFactory. + * @param sessionFactory the sessionFactory to set + */ + public void setSessionFactory(SessionFactory sessionFactory) { + _sessionFactory = sessionFactory; + } +} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/dal/dao/som/Database.java b/Workspace/Siman-Common/src/org/splat/dal/dao/som/Database.java new file mode 100644 index 0000000..3764a3d --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/dal/dao/som/Database.java @@ -0,0 +1,489 @@ +package org.splat.dal.dao.som; + +/** + * + * @author Daniel Brunier-Coulin + * @copyright OPEN CASCADE 2012 + */ + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Properties; +import java.io.File; +import java.io.IOException; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.jdbc.Work; +import org.apache.log4j.Logger; + +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.IDBuilder; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; +import org.splat.dal.bo.som.Study; +import org.splat.kernel.UserDirectory; +import org.splat.kernel.InvalidPropertyException; +import org.splat.service.technical.IndexService; +import org.splat.service.technical.RepositoryService; + +public class Database extends org.splat.dal.dao.kernel.Database { + + private int uplevel = 0; // Level of database upgrade + private String basepath = null; // Path of the root directory of repository + private RepositoryService _repositoryService; + private IndexService _indexService; + private SessionFactory _sessionFactory; + + private static Database my = null; // Singleton instance + + protected class CreateTables extends + org.splat.dal.dao.kernel.Database.CreateTables { + // --------------------------------------------------------------------------- + public void execute(Connection connex) throws SQLException { + super.execute(connex); + + // Study Entity + String create = "CREATE TABLE `study` (" + + "`rid` int(10) UNSIGNED NOT NULL," + + "`sid` tinytext NOT NULL," + + "`title` tinytext NOT NULL," + + "`state` enum('inWORK','inDRAFT','inCHECK','APPROVED', 'TEMPLATE') NOT NULL default 'inWORK'," + + "`area` enum('PRIVATE','PUBLIC','REFERENCE') NOT NULL default 'PRIVATE'," + + "`manager` int(10) NOT NULL," + + "`version` tinytext NOT NULL," + + "`docount` int(10) UNSIGNED NOT NULL," + + "`history` int(10) UNSIGNED NOT NULL," + + "`credate` date NOT NULL," + + "`lasdate` date NOT NULL," + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + + // Scenario Entity + create = "CREATE TABLE `scenario` (" + + "`rid` int(10) UNSIGNED NOT NULL," + + "`sid` int(10) UNSIGNED NOT NULL," + + "`owner` int(10) NOT NULL," + + "`scendex` int(3) NOT NULL," + + "`title` tinytext NOT NULL," + + "`manager` int(10) NOT NULL," + "`cuser` int(10)," + + "`credate` date NOT NULL," + + "`lasdate` date NOT NULL," + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + + // Document Entity and document tag (Publication) + create = "CREATE TABLE `document` (" + + "`rid` int(10) UNSIGNED NOT NULL," + + "`did` tinytext NOT NULL," + + "`type` int(10) NOT NULL," + + "`step` int(10) NOT NULL," + + "`state` enum('inWORK','inDRAFT','inCHECK','APPROVED','EXTERN') NOT NULL default 'inWORK'," + + "`name` tinytext NOT NULL," + + "`author` int(10) NOT NULL," + "`version` tinytext," + + "`countag` int(10) UNSIGNED NOT NULL," + + "`history` int(10) NOT NULL," + + "`myfile` int(10) NOT NULL," + + "`lasdate` date NOT NULL," + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + create = "CREATE TABLE `doctag` (" + + "`rid` int(10) UNSIGNED NOT NULL auto_increment," + + "`doc` int(10) NOT NULL," + + "`owner` int(10) NOT NULL," + + "`isnew` char(1) NOT NULL," + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + // Document types + create = "CREATE TABLE `doctype` (" + + "`rid` int(10) UNSIGNED NOT NULL auto_increment," + + "`name` tinytext NOT NULL," + + "`state` enum('inCHECK','APPROVED') NOT NULL default 'inCHECK'," + + "`step` tinytext NOT NULL," + "`result` tinytext," + + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + // Document types dependencies + create = "CREATE TABLE `docuse` (" + "`owner` int(10) NOT NULL," + + "`rid` int(10) NOT NULL" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + + // ValidationCycle related object + create = "CREATE TABLE `cycle` (" + + "`rid` int(10) UNSIGNED NOT NULL auto_increment," + + "`type` int(10) NOT NULL," + "`publisher` int(10)," + + "`reviewer` int(10)," + "`approver` int(10)," + + "`signatory` int(10)," + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + + // Timestamp related object + create = "CREATE TABLE `stamp` (" + + "`rid` int(10) UNSIGNED NOT NULL auto_increment," + + "`type` enum('PROMOTION','REVIEW','APPROVAL','ACCEPTANCE','DISTRIBUTION','REFUSAL') NOT NULL," + + "`author` int(10) NOT NULL," + + "`date` datetime NOT NULL," + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + + // KnowledgeElements objects + create = "CREATE TABLE `knowelm` (" + + "`rid` int(10) UNSIGNED NOT NULL auto_increment," + + "`type` int(10) NOT NULL," + + "`owner` int(10) NOT NULL," + + "`state` enum('inWORK','inDRAFT','inCHECK','APPROVED') NOT NULL default 'inDRAFT'," + + "`title` tinytext NOT NULL," + + "`value` text NOT NULL," + + "`author` int(10) NOT NULL," + + "`date` date NOT NULL," + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + // KnowledgeElement types + create = "CREATE TABLE `knowtype` (" + + "`rid` int(10) UNSIGNED NOT NULL auto_increment," + + "`name` tinytext NOT NULL," + + "`state` enum('inWORK','inCHECK','APPROVED') NOT NULL default 'inCHECK'," + + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + + // SimulationContext objects + create = "CREATE TABLE `contelm` (" + + "`rid` int(10) UNSIGNED NOT NULL auto_increment," + + "`type` int(10) NOT NULL," + + "`step` int(10) NOT NULL," + + "`state` enum('inCHECK','APPROVED') NOT NULL default 'inCHECK'," + + "`value` text NOT NULL," + + "`counter` int(10) UNSIGNED NOT NULL," + + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + // SimulationContext types + create = "CREATE TABLE `contype` (" + + "`rid` int(10) UNSIGNED NOT NULL auto_increment," + + "`name` tinytext NOT NULL," + + "`state` enum('inCHECK','APPROVED') NOT NULL default 'inCHECK'," + + "`step` int(10) NOT NULL," + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + + // Many-to-many association between ProjectElement (Study and Scenario) and SimulationContext + create = "CREATE TABLE `projext` (" + "`owner` int(10) NOT NULL," + + "`ordex` int(10) NOT NULL," + "`rid` int(10) NOT NULL" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + + // File objects + create = "CREATE TABLE `file` (" + + "`rid` int(10) UNSIGNED NOT NULL," + + "`format` tinytext NOT NULL," + + "`path` tinytext NOT NULL," + + "`date` date NOT NULL," + "PRIMARY KEY (`rid`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + + // Reference objects + create = "CREATE TABLE `refid` (" + "`cycle` int(10) NOT NULL," + + "`base` int(10) NOT NULL," + "PRIMARY KEY (`cycle`)" + + ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; + request.execute(create); + } + } + + protected class CheckVersion implements Work { + // -------------------------------------------- + public void execute(Connection connex) throws SQLException { + DatabaseMetaData dbmdata = connex.getMetaData(); + String dbname = connex.getCatalog(); + ResultSet table; + + table = dbmdata.getTables(dbname, null, "study", null); + if (table.next()) + return; + uplevel = -1; // Database not initialized + } + } + + public final static Logger logger = org.splat.dal.dao.kernel.Database.logger; + + // ============================================================================================================================== + // Construction + // ============================================================================================================================== + + public Database getMe() { + // ------------------------------- + if (my == null) + try { + my = this; + my.checkVersion(); + } catch (Exception error) { + logger.fatal("Could not access the database, reason:", error); + } + return my; + } + + private Database() { + } + + private void checkVersion() { + getSessionFactory().getCurrentSession().doWork(new CheckVersion()); + } + + // ============================================================================================================================== + // Public member functions + // ============================================================================================================================== + + public boolean isInitialized() { + // ------------------------------- + return (uplevel >= 0); + } + + public void initialize() throws IOException, SQLException { + // ------------------------- + logger.info("Creation of the database."); + + // Creation of the Lucene index + getIndexService().create(); // May throw IOException if the index repository is improperly configured + + // Creation of the SIMER SQL tables + Session session = Database.getSession(); + session.doWork(new CreateTables()); // May throw SQLException if the SIMER database does not exist + session.flush(); + + // Population of the database with customized data + this.populate(); + + session.flush(); + uplevel = 0; // The database is now up-to-date + } + + // ============================================================================================================================== + // Protected member functions + // ============================================================================================================================== + + public void configure(Properties reprop) throws IOException { + // -------------------------------------------- + basepath = reprop.getProperty("repository"); + getRepositoryService().setBasepath(basepath); + getIndexService().configure(); + } + + protected void populate() { + // -------------------------- + try { + // Initialization of the schema version + this.setSchemaVersion("D0.3"); // TODO: Get the version name from the configuration file + + // Creation of the default system administrator + // TODO: Get the username password from the Hibernate configuration + User.Properties uprop = new User.Properties(); + uprop.setUsername("simer").setPassword("admin").setName( + "Simulation").setFirstName("Manager").setDisplayName( + "label.sysadmin").addRole("sysadmin").setMailAddress( + "noreply@salome-platform.org"); + uprop.disableCheck(); + UserDirectory.createUser(uprop); + } catch (Exception e) { + // Let's continue, hoping the best... + } + } + + // ============================================================================================================================== + // Public services + // ============================================================================================================================== + + public static File getDownloadDirectory(User user) { + return my.getRepositoryService().getDownloadDirectory(user); + } + + public static String getTemplatePath() { + return my.getRepositoryService().getTemplatePath(); + } + + public static String getRepositoryVaultPath() { + return my.getRepositoryService().getRepositoryVaultPath(); + } + + public static Document selectDocument(int index) { + // ------------------------------------------------- + StringBuffer query = new StringBuffer("from Document where rid='") + .append(index).append("'"); + return (Document) Database.getSession().createQuery(query.toString()) + .uniqueResult(); + } + + public static Document selectDocument(String refid, String version) { + // -------------------------------------------------------------------- + StringBuffer query = new StringBuffer("from Document where did='") + .append(refid).append("' and version='").append(version) + .append("'"); + return (Document) Database.getSession().createQuery(query.toString()) + .uniqueResult(); + } + + public static KnowledgeElement selectKnowledgeElement(int index) { + // ----------------------------------------------------------------- + StringBuffer query = new StringBuffer( + "from KnowledgeElement where rid='").append(index).append("'"); + KnowledgeElement result = (KnowledgeElement) Database.getSession() + .createQuery(query.toString()).uniqueResult(); + + result.getOwnerScenario().getOwnerStudy().loadWorkflow(); + return result; + } + + public static SimulationContext selectSimulationContext(int index) { + // ------------------------------------------------------------------- + StringBuffer query = new StringBuffer( + "from SimulationContext where rid='").append(index).append("'"); + return (SimulationContext) Database.getSession().createQuery( + query.toString()).uniqueResult(); + } + + public static SimulationContext selectSimulationContext( + SimulationContextType celt, String value) { + // -------------------------------------------------------------------------------------------------- + SimulationContext result = null; + try { + SimulationContext.Properties cprop = new SimulationContext.Properties(); + List clist = selectSimulationContextsWhere(cprop + .setType(celt).setValue(value)); + if (!clist.isEmpty()) + result = clist.get(0); // Supposed being the most used one if many exist + } catch (InvalidPropertyException error) { + logger.info("Attempt to select a simulation context \"" + + celt.getName() + "\" with an invalid value."); + } + return result; + } + + @SuppressWarnings("unchecked") + public static List selectSimulationContextsWhere( + SimulationContext.Properties cprop) { + // -------------------------------------------------------------------------------------------------------- + StringBuffer query = new StringBuffer("from SimulationContext"); + String separator = " where"; + SimulationContextType celt = cprop.getType(); + String value = cprop.getValue(); + ProgressState state = cprop.getProgressState(); + String order = ""; + + if (celt != null) { + query = query.append(separator).append(" type='").append( + celt.getIndex()).append("'"); + separator = " and"; + order = " order by value asc"; + } + if (value != null) { + query = query.append(separator).append(" value='").append(value) + .append("'"); + separator = " and"; + } + if (state != null) { + query = query.append(separator).append(" state='").append(state) + .append("'"); + if (celt == null) + order = " order by type asc"; + } + query.append(order); + return (List) Database.getSession().createQuery( + query.toString()).list(); + } + + public static Study selectStudy(int index) { + // ------------------------------------------- + StringBuffer query = new StringBuffer("from Study where rid='").append( + index).append("'"); + Study result = (Study) Database.getSession().createQuery( + query.toString()).uniqueResult(); + + result.loadWorkflow(); + return result; + } + + public static Study selectStudy(String refid) { + // ---------------------------------------------- + StringBuffer query = new StringBuffer("from Study where sid='").append( + refid).append("'"); + Study result = (Study) Database.getSession().createQuery( + query.toString()).uniqueResult(); + + result.loadWorkflow(); + return result; + } + + protected static IDBuilder selectIDBuilder(int cycle) { + // ------------------------------------------------------ + StringBuffer buffer = new StringBuffer("from IDBuilder where cycle='") + .append(cycle).append("'"); + String qstring = buffer.toString(); + Query query = Database.getSession().createQuery(qstring); + IDBuilder result = (IDBuilder) query.uniqueResult(); + + return result; + } + + public static IDBuilder selectIDBuilder(Date date) { + // ------------------------------------------------------ + SimpleDateFormat year = new SimpleDateFormat("yyyy"); + String cycle = year.format(date); + StringBuffer buffer = new StringBuffer("from IDBuilder where cycle='") + .append(cycle).append("'"); + String qstring = buffer.toString(); + Query query = Database.getSession().createQuery(qstring); + IDBuilder result = (IDBuilder) query.uniqueResult(); + + return result; + } + + /** + * @return + */ + public IndexService getIndexService() { + return _indexService; + } + + /** + * @return + */ + public RepositoryService getRepositoryService() { + return _repositoryService; + } + + public void setRepositoryService(RepositoryService repositoryService) { + _repositoryService = repositoryService; + } + + public void setIndexService(IndexService indexService) { + _indexService = indexService; + } + + /** + * Get the sessionFactory. + * + * @return the sessionFactory + */ + public SessionFactory getSessionFactory() { + return _sessionFactory; + } + + /** + * Set the sessionFactory. + * + * @param sessionFactory + * the sessionFactory to set + */ + public void setSessionFactory(SessionFactory sessionFactory) { + _sessionFactory = sessionFactory; + } +} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/dal/dao/som/KnowledgeElementDAO.java b/Workspace/Siman-Common/src/org/splat/dal/dao/som/KnowledgeElementDAO.java new file mode 100644 index 0000000..4652efb --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/dal/dao/som/KnowledgeElementDAO.java @@ -0,0 +1,20 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.dal.dao.som; + +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.dao.kernel.GenericDAO; + +/** + * @author RKV + * + */ +public interface KnowledgeElementDAO extends GenericDAO { +} diff --git a/Workspace/Siman-Common/src/org/splat/dal/dao/som/KnowledgeElementDAOImpl.java b/Workspace/Siman-Common/src/org/splat/dal/dao/som/KnowledgeElementDAOImpl.java new file mode 100644 index 0000000..f19b5ed --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/dal/dao/som/KnowledgeElementDAOImpl.java @@ -0,0 +1,32 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.dal.dao.som; + +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.dao.kernel.GenericDAOImpl; + +/** + * Knowledge element DAO. + * @author RKV + * + */ +public class KnowledgeElementDAOImpl extends + GenericDAOImpl implements KnowledgeElementDAO { + + /** + * {@inheritDoc} + * @see org.splat.dal.dao.kernel.GenericDAOImpl#getType() + */ + @Override + protected Class getType() { + return KnowledgeElement.class; + } + +} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Do.java b/Workspace/Siman-Common/src/org/splat/kernel/Do.java index a929540..c770290 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/Do.java +++ b/Workspace/Siman-Common/src/org/splat/kernel/Do.java @@ -26,6 +26,7 @@ import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import org.apache.log4j.Logger; +import org.splat.dal.bo.kernel.User; public class Do { diff --git a/Workspace/Siman-Common/src/org/splat/kernel/IDGenerator.java b/Workspace/Siman-Common/src/org/splat/kernel/IDGenerator.java deleted file mode 100644 index e5dd545..0000000 --- a/Workspace/Siman-Common/src/org/splat/kernel/IDGenerator.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.splat.kernel; -/** - * Class generating the persistent identifier of all objects instance of a subclass of Any.
- * The implementation of this generator is optimized basing on IDPool. - * - * @see IDPool - * @author Daniel Brunier-Coulin - * @copyright OPEN CASCADE 2012 - */ - -import java.io.Serializable; - -import org.hibernate.HibernateException; -import org.hibernate.engine.SessionImplementor; -import org.hibernate.id.IdentifierGenerator; - - -public class IDGenerator implements IdentifierGenerator { - - public Serializable generate (SessionImplementor session, Object any) throws HibernateException { -// --------------------------------------------------------------------- - return Database.getIDPool().getNextID(); - } -} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/kernel/IDPool.hbm.xml b/Workspace/Siman-Common/src/org/splat/kernel/IDPool.hbm.xml deleted file mode 100644 index 1c2cdd4..0000000 --- a/Workspace/Siman-Common/src/org/splat/kernel/IDPool.hbm.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/kernel/IDPool.java b/Workspace/Siman-Common/src/org/splat/kernel/IDPool.java deleted file mode 100644 index 6e6b0af..0000000 --- a/Workspace/Siman-Common/src/org/splat/kernel/IDPool.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.splat.kernel; -/** - * Optimized generator of persistent identifiers.
- * This generator minimizes the database hits by grouping a bunch of identifiers in memory and only hitting the database - * when the in-memory value group is exhausted. - * Only one IDPool object held by the Database class is created during a session. - * - * @author Daniel Brunier-Coulin - * @copyright OPEN CASCADE 2012 - */ - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import org.hibernate.Session; -import org.hibernate.jdbc.Work; - - -public class IDPool { - -// Persistent fields - private Integer lastid; // Last generated ID - private String version; // Version of the database schema - -// Transient fields - private int remaining; // Remaining available ID held in this pool - private static int poolsize = 1; // No pool by default - - private class LoadNewIDs implements Work { -// ----------------------------------------- - public void execute(Connection connex) throws SQLException - { - Statement request = connex.createStatement(); - ResultSet result = request.executeQuery("SELECT MAX(rid) AS lastid FROM any"); - StringBuffer command = new StringBuffer("UPDATE any SET rid="); - - result.first(); - lastid = result.getInt("lastid"); - - command.append(lastid + poolsize).append(" WHERE rid=").append(lastid); - request.execute(command.toString()); - - remaining = poolsize; - } - } - private class LoadLastID implements Work { -// ----------------------------------------- - public void execute(Connection connex) throws SQLException - { - Statement request = connex.createStatement(); - ResultSet result = request.executeQuery("SELECT MAX(rid) AS lastid FROM any"); - StringBuffer command = new StringBuffer("SELECT version FROM any WHERE rid="); - - result.first(); - lastid = result.getInt("lastid"); - - command.append(lastid); - result = request.executeQuery(command.toString()); - result.first(); - version = result.getString("version"); - remaining = 0; - } - } - -// ============================================================================================================================== -// Constructors -// ============================================================================================================================== - - protected IDPool () { - } -/** - * Constructor called only once, when initializing the database. - * - * @param version the version of the constructed database schema. - */ - protected IDPool (String version) { -// --------------------------------- - this.lastid = 0; //TODO: Get the current last ID if a previous version exists - this.version = version; - this.remaining = 0; - } -/** - * Constructor called at start of every database session. - * - * @param base the started database session - */ - protected IDPool (Session base) { -// ------------------------------- - base.doWork( new LoadLastID() ); - } - -// ============================================================================================================================== -// Protected member functions -// ============================================================================================================================== - - protected Integer getNextID () { -// ------------------------------ - if (remaining <= 0) Database.getSession().doWork( new LoadNewIDs() ); - lastid += 1; - remaining -= 1; - return lastid; - } - - protected String getSchemaVersion () { -// ------------------------------------ - return version; - } - - protected static void setPoolSize (int size) { -// -------------------------------------------- - if (size > 1) poolsize = size; - } -} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/kernel/ObjectProperties.java b/Workspace/Siman-Common/src/org/splat/kernel/ObjectProperties.java index 5f4c15a..a37725e 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/ObjectProperties.java +++ b/Workspace/Siman-Common/src/org/splat/kernel/ObjectProperties.java @@ -1,4 +1,7 @@ package org.splat.kernel; + +import org.splat.dal.bo.kernel.Persistent; + /** * Common interface of intermediate properties objects used for constructing persistent objects supporting the API Design Pattern * provided by this package. diff --git a/Workspace/Siman-Common/src/org/splat/kernel/Persistent.hbm.xml b/Workspace/Siman-Common/src/org/splat/kernel/Persistent.hbm.xml deleted file mode 100644 index 019827e..0000000 --- a/Workspace/Siman-Common/src/org/splat/kernel/Persistent.hbm.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/kernel/RealmLoginModule.java b/Workspace/Siman-Common/src/org/splat/kernel/RealmLoginModule.java index fcc0b28..a0234f4 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/RealmLoginModule.java +++ b/Workspace/Siman-Common/src/org/splat/kernel/RealmLoginModule.java @@ -15,6 +15,8 @@ import javax.security.auth.login.*; import javax.security.auth.spi.*; import org.apache.log4j.Logger; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.dao.kernel.Database; public class RealmLoginModule implements LoginModule { diff --git a/Workspace/Siman-Common/src/org/splat/kernel/UserDirectory.java b/Workspace/Siman-Common/src/org/splat/kernel/UserDirectory.java index d196344..e33de80 100644 --- a/Workspace/Siman-Common/src/org/splat/kernel/UserDirectory.java +++ b/Workspace/Siman-Common/src/org/splat/kernel/UserDirectory.java @@ -28,9 +28,10 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.hibernate.Session; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.dao.som.Database; import org.splat.manox.XDOM; import org.splat.manox.XMLException; -import org.splat.som.Database; import org.w3c.dom.Node; import org.apache.log4j.Logger; diff --git a/Workspace/Siman-Common/src/org/splat/service/DocumentService.java b/Workspace/Siman-Common/src/org/splat/service/DocumentService.java new file mode 100644 index 0000000..d2b2218 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/DocumentService.java @@ -0,0 +1,18 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +/** + * @author RKV + * + */ +public interface DocumentService { + +} diff --git a/Workspace/Siman-Common/src/org/splat/service/DocumentServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/DocumentServiceImpl.java new file mode 100644 index 0000000..cd86e08 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/DocumentServiceImpl.java @@ -0,0 +1,18 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +/** + * @author RKV + * + */ +public class DocumentServiceImpl implements DocumentService { + +} diff --git a/Workspace/Siman-Common/src/org/splat/service/DocumentTypeService.java b/Workspace/Siman-Common/src/org/splat/service/DocumentTypeService.java new file mode 100644 index 0000000..84d82cc --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/DocumentTypeService.java @@ -0,0 +1,29 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import org.splat.dal.bo.som.DocumentType; + +/** + * @author RKV + * + */ +public interface DocumentTypeService { + + /** + * Checks if documents of this type are result of a study. + * A document is the result of a study when it is the result of the last step of the study. + * + * @return true if documents of this type are result of a study. + * @see #isStepResult() + * @see #isResultOf(org.splat.service.technical.ProjectSettingsServiceImpl.Step) + */ + public boolean isStudyResult (DocumentType aType); +} diff --git a/Workspace/Siman-Common/src/org/splat/service/DocumentTypeServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/DocumentTypeServiceImpl.java new file mode 100644 index 0000000..5a717a1 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/DocumentTypeServiceImpl.java @@ -0,0 +1,52 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import java.util.List; + +import org.splat.dal.bo.som.DocumentType; +import org.splat.service.technical.ProjectSettingsService; + +/** + * @author RKV + * + */ +public class DocumentTypeServiceImpl implements DocumentTypeService { + + private ProjectSettingsService _projectSettingsService; + + /** + * Checks if documents of this type are result of a study. + * A document is the result of a study when it is the result of the last step of the study. + * + * @return true if documents of this type are result of a study. + * @see #isStepResult() + * @see #isResultOf(org.splat.service.technical.ProjectSettingsServiceImpl.Step) + */ + public boolean isStudyResult (DocumentType aType) { + // ------------------------------- + List step = getProjectSettingsService().getAllSteps(); + ProjectSettingsService.Step lastep = step.get( step.size()-1 ); + return (aType.isResultOf(lastep)); + } + + /** + * @return + */ + public ProjectSettingsService getProjectSettingsService() { + return _projectSettingsService; + } + + public void setProjectSettingsService( + ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } + +} diff --git a/Workspace/Siman-Common/src/org/splat/service/KnowledgeElementService.java b/Workspace/Siman-Common/src/org/splat/service/KnowledgeElementService.java new file mode 100644 index 0000000..5ccb94c --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/KnowledgeElementService.java @@ -0,0 +1,29 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.kernel.InvalidPropertyException; + +/** + * @author RKV + * + */ +public interface KnowledgeElementService { + + public boolean approve(KnowledgeElement knowledgeElement); + + public boolean demote(KnowledgeElement knowledgeElement); + + public boolean promote(KnowledgeElement knowledgeElement); + + public void rename(KnowledgeElement knowledgeElement, String title) + throws InvalidPropertyException; +} diff --git a/Workspace/Siman-Common/src/org/splat/service/KnowledgeElementServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/KnowledgeElementServiceImpl.java new file mode 100644 index 0000000..1c107d5 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/KnowledgeElementServiceImpl.java @@ -0,0 +1,92 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.dao.kernel.GenericDAO; +import org.splat.dal.dao.som.Database; +import org.splat.dal.dao.som.KnowledgeElementDAO; +import org.splat.kernel.InvalidPropertyException; +import org.splat.service.technical.IndexService; + +/** + * @author RKV + * + */ +public class KnowledgeElementServiceImpl implements KnowledgeElementService { + + private IndexService _indexService; + private KnowledgeElementDAO _knowledgeElementDAO; + + public boolean approve(KnowledgeElement knowledgeElement) { + // ------------------------- + if (knowledgeElement.getProgressState() != ProgressState.inCHECK) + return false; + knowledgeElement.setProgressState(ProgressState.APPROVED); + return update(knowledgeElement); + } + + public boolean demote(KnowledgeElement knowledgeElement) { + // ------------------------ + if (knowledgeElement.getProgressState() != ProgressState.APPROVED + && knowledgeElement.getProgressState() != ProgressState.inCHECK) + return false; + knowledgeElement.setProgressState(ProgressState.inDRAFT); + return update(knowledgeElement); + } + + protected boolean update(KnowledgeElement knowledgeElement) { + // ----------------------------- + try { + getKnowledgeElementDAO().update(knowledgeElement); + getIndexService().update(knowledgeElement); + return true; + } catch (Exception error) { + // logger.error("Unable to re-index the knowledge '" + getIndex() + "', reason:", error); + return false; + } + } + + public boolean promote (KnowledgeElement knowledgeElement) { +// ------------------------- + if (knowledgeElement.getProgressState() != ProgressState.inDRAFT) return false; + knowledgeElement.setProgressState(ProgressState.inCHECK); + return update(knowledgeElement); + } + + public void rename (KnowledgeElement knowledgeElement, String title) throws InvalidPropertyException { + if (title.length() == 0) throw new InvalidPropertyException("name"); + knowledgeElement.setTitle(title); + update(knowledgeElement); + } + + /** + * @return + */ + public IndexService getIndexService() { + return _indexService; + } + + public void setIndexService(IndexService indexService) { + _indexService = indexService; + } + + /** + * @return + */ + public KnowledgeElementDAO getKnowledgeElementDAO() { + return _knowledgeElementDAO; + } + + public void setKnowledgeElementDAO(KnowledgeElementDAO knowledgeElementDAO) { + _knowledgeElementDAO = knowledgeElementDAO; + } +} diff --git a/Workspace/Siman-Common/src/org/splat/service/ProjectElementService.java b/Workspace/Siman-Common/src/org/splat/service/ProjectElementService.java new file mode 100644 index 0000000..b9fd9d5 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/ProjectElementService.java @@ -0,0 +1,24 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 07.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import org.splat.dal.bo.som.ProjectElement; +import org.splat.som.Step; + +/** + * @author RKV + * + */ +public interface ProjectElementService { + + public Step getFirstStep(ProjectElement elem); + + public Step[] getSteps(ProjectElement elem); +} diff --git a/Workspace/Siman-Common/src/org/splat/service/ProjectElementServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/ProjectElementServiceImpl.java new file mode 100644 index 0000000..031b2ee --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/ProjectElementServiceImpl.java @@ -0,0 +1,72 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 07.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import java.util.Iterator; +import java.util.List; + +import org.splat.dal.bo.som.ProjectElement; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.service.technical.ProjectSettingsServiceImpl; +import org.splat.som.Step; + +/** + * @author RKV + * + */ +public class ProjectElementServiceImpl implements ProjectElementService { + + private ProjectSettingsService _projectSettingsService; + + public Step getFirstStep(ProjectElement elem) { + // --------------------------- + return getSteps(elem)[0]; + } + + public Step getLastStep(ProjectElement elem) { + // -------------------------- + Step[] mystep = getSteps(elem); // For getting the folders length, if null + return mystep[mystep.length - 1]; + } + + public Step[] getSteps(ProjectElement elem) { + // ------------------------- + if (elem.getFolders() == null) { + List steps = getProjectSettings() + .getStepsOf(elem.getClass()); + Iterator nstep = steps.iterator(); + + elem.setFolders(new Step[steps.size()]); + for (int i = 0; i < elem.getFolders().length; i++) { + elem.getFolders()[i] = new Step(nstep.next(), elem); + } + } + return elem.getFolders(); // No protection against this object corruption as it would not corrupt the database + } + + /** + * Get project settings. + * + * @return Project settings service + */ + private ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + + /** + * Set project settings service. + * + * @param projectSettingsService + * project settings service + */ + public void setProjectSettings(ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } +} diff --git a/Workspace/Siman-Common/src/org/splat/service/PublicationService.java b/Workspace/Siman-Common/src/org/splat/service/PublicationService.java new file mode 100644 index 0000000..2b629c0 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/PublicationService.java @@ -0,0 +1,145 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import java.io.FileNotFoundException; +import java.util.Date; + +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.ProjectElement; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.Timestamp; +import org.splat.kernel.NotApplicableException; +import org.splat.som.DocumentRights; +import org.splat.som.Revision; +import org.splat.som.Step; + +/** + * @author RKV + * + */ +public interface PublicationService { + + public Publication copy(Publication aPublication, ProjectElement publisher); + + /** + * Returns the study Step into which the document version referenced by this publication has been published. + */ + public Step getInvolvedStep(Publication aPublication); + + /** + * Promotes the document referenced by this publication from In-Check to Approved state, if not out-dated, and attaches the + * corresponding time-stamp to the document.
If the promoted document is the final result of the owner study, the study is itself + * is promoted as well.

Limitation: the way this promotion is propagated to the study supposes that the study has only ONE + * final result document. + * + * @param adate + * the date of approval + * @return true if the approval succeeded + * @see #getProgressState() + * @see DocumentRights#canApprove() + * @see DocumentType#isStudyResult() + * @see Study#getApproverOf(Publication) + */ + public Timestamp approve(Publication aPublication, Date adate); + + /** + * Demotes the document referenced by this publication to In-Work state, and removes the Promoter of the document, if exist.
The + * In-Draft state is skipped (direct demotion to In-Work) if the validation cycle of the document does not include the review step.
+ * If the demoted document is the final result of the owner study, the study is itself is demoted as well.

Limitation: the + * way this demotion is propagated to the study supposes that the study has only ONE final result document. + * + * @return true if the demotion succeeded + * @see #getProgressState() + * @see DocumentRights#canDemote() + * @see DocumentType#isStudyResult() + */ + public boolean demote(Publication aPublication); + + /** + * Undo the review operation by demoting the document referenced by this publication from In-Check to In-Draft state and removing the + * Reviewer.
If the demoted document is the final result of the owner study, the study is itself is demoted as well.

+ * Limitation: the way this demotion is propagated to the study supposes that the study has only ONE final result document. + * + * @return true if the demotion succeeded + * @see #getProgressState() + * @see #review() + * @see DocumentRights#canInvalidate() + * @see DocumentType#isStudyResult() + */ + public boolean invalidate(Publication aPublication); + + /** + * Promotes the document referenced by this publication from In-Work to In-Draft or In-Check state, if not out-dated, and attaches the + * corresponding time-stamp to the document.
The In-Draft state is skipped (direct promotion to In-Check) if the validation cycle + * of the document does not include the review step.
Also, if the promoted document is the final result of the owner study, the + * study is itself promoted as well.
This operation can be undo-ed by demote().

Limitation: the way this promotion is + * propagated to the study supposes that the study has only ONE final result document. + * + * @return true if the promotion succeeded + * @see #getProgressState() + * @see #demote() + * @see DocumentRights#canPromote() + * @see DocumentType#isStudyResult() + */ + public Timestamp promote(Publication aPublication, Date pdate); + + /** + * Promotes the document referenced by this publication from In-Draft to In-Check state, if not out-dated, and attaches the + * corresponding time-stamp to the document.
If the promoted document is the final result of the owner study, the study is itself + * is promoted as well.
This operation can be undo-ed by invalidate().

Limitation: the way this promotion is propagated + * to the study supposes that the study has only ONE final result document. + * + * @param rdate + * the date of review + * @return true if the review succeeded + * @see #getProgressState() + * @see #invalidate() + * @see DocumentRights#canReview() + * @see DocumentType#isStudyResult() + * @see Study#getReviewerOf(Publication) + */ + public Timestamp review(Publication aPublication, Date rdate); + + /** + * Publishes the document referenced by this publication into the owner Project Element under the given state, the revision number of + * the document being automatically set accordingly. If the given state is In-Draft and the document is final result of the owner study, + * this automatically promotes the study to In-Draft. + * + * @param state + * the required progress state + * @throws FileNotFoundException + * If the referenced document is empty + * @throws NotApplicableException + * If the referenced document is undefined + */ + public void saveAs(Publication aPublication, ProgressState state) + throws FileNotFoundException, NotApplicableException; + + /** + * Publishes the document referenced by this publication into the owner Project Element under the given revision number.
+ * The state of the referenced document is supposed being automatically set according to the given revision number, but, due to the + * versioning scheme, as it is not possible to differentiate In-Work and In-Draft states, this function has been deprecated (it is + * currently used only for the need of integration of Microsoft Office which anyway has to be redesigned).
+ * Note: in the context of branch versioning, the given revision may be modified by an update of the branch name. + * + * @param newvers + * the required revision number + * @throws FileNotFoundException + * If the referenced document is empty + * @throws NotApplicableException + * If the referenced document is undefined + * @deprecated + */ + public void saveAs(Publication aPublication, Revision newvers) + throws FileNotFoundException, NotApplicableException; +} diff --git a/Workspace/Siman-Common/src/org/splat/service/PublicationServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/PublicationServiceImpl.java new file mode 100644 index 0000000..8f64387 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/PublicationServiceImpl.java @@ -0,0 +1,445 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.hibernate.Session; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.ProjectElement; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.Timestamp; +import org.splat.dal.bo.som.ValidationCycle; +import org.splat.dal.bo.som.ValidationStep; +import org.splat.dal.dao.som.Database; +import org.splat.kernel.NotApplicableException; +import org.splat.manox.Reader; +import org.splat.manox.Toolbox; +import org.splat.som.DocumentRights; +import org.splat.som.Revision; +import org.splat.som.Step; + +/** + * @author RKV + * + */ +public class PublicationServiceImpl implements PublicationService { + + private StudyService _studyService; + private StepService _stepService; + private DocumentTypeService _documentTypeService; + private ProjectElementService _projectElementService; + + public Publication copy(Publication aPublication, ProjectElement publisher) { + // ----------------------------------------------------- + Publication copy = new Publication(); + copy.setValue(aPublication.value()); + copy.setStep(aPublication.getStep()); // May not be initialized yet + copy.setOwner(publisher); + copy.setIsnew(aPublication.getIsnew()); + if (!copy.getOwnerStudy().equals(aPublication.getOwnerStudy())) { + copy.setIsnew('N'); // The referenced document is not new for the given study + } + return copy; + } + + /** + * Promotes the document referenced by this publication from In-Check to Approved state, if not out-dated, and attaches the + * corresponding time-stamp to the document.
If the promoted document is the final result of the owner study, the study is itself + * is promoted as well.

Limitation: the way this promotion is propagated to the study supposes that the study has only ONE + * final result document. + * + * @param adate + * the date of approval + * @return true if the approval succeeded + * @see #getProgressState() + * @see DocumentRights#canApprove() + * @see DocumentType#isStudyResult() + * @see Study#getApproverOf(Publication) + */ + public Timestamp approve(Publication aPublication, Date adate) { + // ------------------------------------- + if (aPublication.isOutdated()) + return null; + else if (aPublication.value().getProgressState() != ProgressState.inCHECK) + return null; // This statement must conform to the corresponding right + + DocumentType type = aPublication.value().getType(); + Study owner = aPublication.getOwnerStudy(); + ValidationCycle cycle = owner.getValidationCycleOf(type); + User approver = cycle.getActor(ValidationStep.APPROVAL); + Timestamp stamp = new Timestamp(ValidationStep.APPROVAL, + aPublication.value(), approver, adate); + if (!aPublication.value().promote(stamp)) + return null; + if (getDocumentTypeService().isStudyResult(type) + && owner.getProgressState() == ProgressState.inCHECK) + getStudyService().promote(owner); + return stamp; // Hoping that promotion of the study succeeded + } + + /** + * Demotes the document referenced by this publication to In-Work state, and removes the Promoter of the document, if exist.
The + * In-Draft state is skipped (direct demotion to In-Work) if the validation cycle of the document does not include the review step.
+ * If the demoted document is the final result of the owner study, the study is itself is demoted as well.

Limitation: the + * way this demotion is propagated to the study supposes that the study has only ONE final result document. + * + * @return true if the demotion succeeded + * @see #getProgressState() + * @see DocumentRights#canDemote() + * @see DocumentType#isStudyResult() + */ + public boolean demote(Publication aPublication) { + // ------------------------ + DocumentType type = aPublication.value().getType(); + Study owner = aPublication.getOwnerStudy(); + + if (aPublication.value().getProgressState() == ProgressState.inCHECK) { + ValidationCycle cycle = owner.getValidationCycleOf(type); + if (cycle.enables(ValidationStep.REVIEW)) { + if (!aPublication.value().demote()) + return false; + } else { + if (!aPublication.value().demote()) + return false; + aPublication.value().demote(); + } + } else if (aPublication.value().getProgressState() == ProgressState.inDRAFT) { + if (!aPublication.value().demote()) + return false; + } else { + return false; + } + if (getDocumentTypeService().isStudyResult(type) + && owner.getProgressState() != ProgressState.inWORK) + getStudyService().demote(owner); + return true; + } + + /** + * Undo the review operation by demoting the document referenced by this publication from In-Check to In-Draft state and removing the + * Reviewer.
If the demoted document is the final result of the owner study, the study is itself is demoted as well.

+ * Limitation: the way this demotion is propagated to the study supposes that the study has only ONE final result document. + * + * @return true if the demotion succeeded + * @see #getProgressState() + * @see #review() + * @see DocumentRights#canInvalidate() + * @see DocumentType#isStudyResult() + */ + public boolean invalidate(Publication aPublication) { + // ---------------------------- + if (aPublication.value().getProgressState() != ProgressState.inCHECK) + return false; + if (!aPublication.value().demote()) // Removes the reviewer if this document is In-Check + return false; + DocumentType type = aPublication.value().getType(); + Study owner = aPublication.getOwnerStudy(); + if (getDocumentTypeService().isStudyResult(type) + && owner.getProgressState() == ProgressState.inCHECK) + getStudyService().demote(owner); + return true; + } + + /** + * Promotes the document referenced by this publication from In-Work to In-Draft or In-Check state, if not out-dated, and attaches the + * corresponding time-stamp to the document.
The In-Draft state is skipped (direct promotion to In-Check) if the validation cycle + * of the document does not include the review step.
Also, if the promoted document is the final result of the owner study, the + * study is itself promoted as well.
This operation can be undo-ed by demote().

Limitation: the way this promotion is + * propagated to the study supposes that the study has only ONE final result document. + * + * @return true if the promotion succeeded + * @see #getProgressState() + * @see #demote() + * @see DocumentRights#canPromote() + * @see DocumentType#isStudyResult() + */ + public Timestamp promote(Publication aPublication, Date pdate) { + if (aPublication.isOutdated()) + return null; + else if (aPublication.value().getProgressState() != ProgressState.inWORK) + return null; // This statement must conform to the corresponding right + else { + DocumentType type = aPublication.value().getType(); + Study owner = aPublication.getOwnerStudy(); + ValidationCycle cycle = owner.getValidationCycleOf(type); + User promoter = cycle.getActor(ValidationStep.PROMOTION); + if (promoter == null) + promoter = getInvolvedStep(aPublication).getActor(); + if (promoter == null) + promoter = owner.getAuthor(); + Timestamp stamp = new Timestamp(ValidationStep.PROMOTION, + aPublication.value(), promoter, pdate); + + if (!aPublication.value().promote(stamp)) // Promotion to being reviewed + return null; + if (!cycle.enables(ValidationStep.REVIEW)) { + aPublication.value().promote(null); + } + if (getDocumentTypeService().isStudyResult(type) + && owner.getProgressState() == ProgressState.inWORK) + getStudyService().promote(owner); + return stamp; // Hoping that promotion of the study succeeded + } + } + + /** + * Promotes the document referenced by this publication from In-Draft to In-Check state, if not out-dated, and attaches the + * corresponding time-stamp to the document.
If the promoted document is the final result of the owner study, the study is itself + * is promoted as well.
This operation can be undo-ed by invalidate().

Limitation: the way this promotion is propagated + * to the study supposes that the study has only ONE final result document. + * + * @param rdate + * the date of review + * @return true if the review succeeded + * @see #getProgressState() + * @see #invalidate() + * @see DocumentRights#canReview() + * @see DocumentType#isStudyResult() + * @see Study#getReviewerOf(Publication) + */ + public Timestamp review(Publication aPublication, Date rdate) { + if (aPublication.isOutdated()) + return null; + else if (aPublication.value().getProgressState() != ProgressState.inDRAFT) + return null; // This statement must conform to the corresponding right + + DocumentType type = aPublication.value().getType(); + Study owner = aPublication.getOwnerStudy(); + ValidationCycle cycle = owner.getValidationCycleOf(type); + User reviewer = cycle.getActor(ValidationStep.REVIEW); + Timestamp stamp = new Timestamp(ValidationStep.REVIEW, + aPublication.value(), reviewer, rdate); + if (!aPublication.value().promote(stamp)) + return null; + if (getDocumentTypeService().isStudyResult(type) + && owner.getProgressState() == ProgressState.inDRAFT) + getStudyService().promote(owner); + return stamp; // Hoping that promotion of the study succeeded + } + + /** + * Publishes the document referenced by this publication into the owner Project Element under the given revision number.
+ * The state of the referenced document is supposed being automatically set according to the given revision number, but, due to the + * versioning scheme, as it is not possible to differentiate In-Work and In-Draft states, this function has been deprecated (it is + * currently used only for the need of integration of Microsoft Office which anyway has to be redesigned).
+ * Note: in the context of branch versioning, the given revision may be modified by an update of the branch name. + * + * @param newvers + * the required revision number + * @throws FileNotFoundException + * If the referenced document is empty + * @throws NotApplicableException + * If the referenced document is undefined + * @deprecated + */ + public void saveAs(Publication aPublication, Revision newvers) + throws FileNotFoundException, NotApplicableException { + // ------------------------------------- + if (aPublication.value().isUndefined()) + throw new NotApplicableException( + "Cannot save a Publication object refering an undefined Document"); + if (!aPublication.value().getSourceFile().exists()) + throw new FileNotFoundException(); + + Database.getSession().save(aPublication); // Must be done before updating the study in order to fix this final (rid-based) hascode + aPublication.value().updateAs(newvers); // May change the branch name of given revision + updateOwner(aPublication); + } + + /** + * Publishes the document referenced by this publication into the owner Project Element under the given state, the revision number of + * the document being automatically set accordingly. If the given state is In-Draft and the document is final result of the owner study, + * this automatically promotes the study to In-Draft. + * + * @param state + * the required progress state + * @throws FileNotFoundException + * If the referenced document is empty + * @throws NotApplicableException + * If the referenced document is undefined + */ + public void saveAs(Publication aPublication, ProgressState state) + throws FileNotFoundException, NotApplicableException { + // ---------------------------------------- + if (aPublication.value().isUndefined()) + throw new NotApplicableException( + "Cannot save a Publication object refering an undefined Document"); + if (!aPublication.value().getSourceFile().exists()) + throw new FileNotFoundException(); + + if (state == ProgressState.inWORK || state == ProgressState.EXTERN) { + Database.getSession().save(this); // Must be done before updating the study in order to fix this final (rid-based) hascode + aPublication.value().updateAs(state); + } else { + DocumentType mytype = aPublication.value().getType(); + Study owner = aPublication.getOwnerStudy(); + ValidationCycle cycle = owner.getValidationCycleOf(mytype); + boolean review = cycle.enables(ValidationStep.REVIEW); + if (!(state == ProgressState.inDRAFT && review) + && !(state == ProgressState.inCHECK && !review)) { + throw new NotApplicableException( + "Cannot save a result document in " + state.toString() + + " state"); + } + Database.getSession().save(aPublication); // Must be done before updating the study in order to fix this final (rid-based) + // hascode + aPublication.value().updateAs(ProgressState.inWORK); + + promote(aPublication, aPublication.value() + .getLastModificationDate()); // Promotes to the appropriate state in accordance to the validation cycle + } + updateOwner(aPublication); + } + + public void updateOwner(Publication aPublication) { + // --------------------------- + Session session = Database.getSession(); + Step step = getInvolvedStep(aPublication); + + // Update of involved step + Document previous = aPublication.value().getPreviousVersion(); + if (previous != null) { + Publication oldoc = step.getDocument(previous.getIndex()); + boolean done = step.remove(oldoc); // Decrements the configuration tag count of document + if (done) + session.delete(oldoc); // WARNING: Potential problem because it's not automatically done as orphan object + } + step.add(aPublication); // Increments the configuration tag count of document + + // Import the document properties and update of the study + forwardProperties(aPublication, aPublication.value().getSourceFile() + .asFile(), step); + session.update(aPublication.getOwner()); + } + + private void forwardProperties(Publication aPublication, java.io.File from, + Step to) { + // ----------------------------------------------------------- + Reader tool = Toolbox.getReader(from); + if (tool == null) + return; // No properties extractor available for this type of document + + SimulationContextType.Properties sprop = new SimulationContextType.Properties() + .setStep(to.getStep()).setState(ProgressState.APPROVED); + List contype = SimulationContext + .selectTypesWhere(sprop); + if (contype.isEmpty()) + return; // No approved property type configured at this step + + SimulationContext.Properties cprop = new SimulationContext.Properties(); + List context = to.getAllSimulationContexts(); + + context = new ArrayList(context.size()); + context.addAll(to.getAllSimulationContexts()); + cprop.disableCheck(); + for (Iterator i = contype.iterator(); i + .hasNext();) { + SimulationContextType property = i.next(); + for (Iterator j = context.iterator(); j + .hasNext();) { + SimulationContext existing = j.next(); + if (!existing.getType().equals(property)) + continue; + property = null; // Forget this property as it is already set + break; + } + if (property != null) + try { + String value = tool.extractProperty(property.getName()); + if (value == null) + continue; // Property not defined into the document + + cprop.setType(property).setValue(value); + if (aPublication.getOwner() instanceof Study) + getStudyService().addProjectContext( + (Study) aPublication.getOwner(), cprop); // Re-indexes knowledges and the study + else + getStepService().addSimulationContext(to, cprop); // Re-indexes knowledges only + } catch (Exception e) { + break; + } + } + } + + /** + * Returns the study Step into which the document version referenced by this publication has been published. + */ + public Step getInvolvedStep(Publication aPublication) { + if (aPublication.getStep() == null) { + Step[] step = getProjectElementService().getSteps(aPublication.getOwner()); + for (int i = 0; i < step.length; i++) { + aPublication.setStep(step[i]); // The involved step necessarily exists + if (aPublication.value().isInto(aPublication.getStep())) + break; + } + } + return aPublication.getStep(); + } + + /** + * @return + */ + private StepService getStepService() { + return _stepService; + } + + public void setStepService(StepService stepService) { + _stepService = stepService; + } + + /** + * @return + */ + public StudyService getStudyService() { + return _studyService; + } + + public void setStudyService(StudyService studyService) { + _studyService = studyService; + } + + /** + * @return + */ + public DocumentTypeService getDocumentTypeService() { + return _documentTypeService; + } + + public void setDocumentTypeService(DocumentTypeService documentTypeService) { + _documentTypeService = documentTypeService; + } + + /** + * Get the projectElementService. + * @return the projectElementService + */ + public ProjectElementService getProjectElementService() { + return _projectElementService; + } + + /** + * Set the projectElementService. + * @param projectElementService the projectElementService to set + */ + public void setProjectElementService(ProjectElementService projectElementService) { + _projectElementService = projectElementService; + } +} diff --git a/Workspace/Siman-Common/src/org/splat/service/ScenarioService.java b/Workspace/Siman-Common/src/org/splat/service/ScenarioService.java new file mode 100644 index 0000000..44bdc43 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/ScenarioService.java @@ -0,0 +1,34 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.Scenario; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.som.Step; + +/** + * @author RKV + * + */ +public interface ScenarioService { + + public KnowledgeElement addKnowledgeElement(Scenario aScenario, + KnowledgeElement.Properties kprop) throws MissedPropertyException, + InvalidPropertyException, MultiplyDefinedException; + + public void checkin(Scenario aScenario); + + public void copyContentsUpTo(Scenario scenario, Step lastep); + + public boolean isEmpty(Scenario scenario); +} diff --git a/Workspace/Siman-Common/src/org/splat/service/ScenarioServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/ScenarioServiceImpl.java new file mode 100644 index 0000000..003c4db --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/ScenarioServiceImpl.java @@ -0,0 +1,228 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import java.io.IOException; +import java.util.Calendar; +import java.util.Iterator; +import java.util.List; + +import org.apache.log4j.Logger; +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.ProjectElement; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.Study; +import org.splat.dal.dao.som.Database; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.service.technical.IndexService; +import org.splat.som.Step; + +/** + * @author RKV + * + */ +public class ScenarioServiceImpl implements ScenarioService { + + protected final static Logger logger = Logger + .getLogger(ScenarioServiceImpl.class); + + private IndexService _indexService; + private StepService _stepService; + private PublicationService _publicationService; + private ProjectElementService _projectElementService; + + /** + * Get the projectElementService. + * @return the projectElementService + */ + public ProjectElementService getProjectElementService() { + return _projectElementService; + } + + /** + * Set the projectElementService. + * @param projectElementService the projectElementService to set + */ + public void setProjectElementService(ProjectElementService projectElementService) { + _projectElementService = projectElementService; + } + + /** + * Get the publicationService. + * @return the publicationService + */ + public PublicationService getPublicationService() { + return _publicationService; + } + + /** + * Set the publicationService. + * @param publicationService the publicationService to set + */ + public void setPublicationService(PublicationService publicationService) { + _publicationService = publicationService; + } + + /** + * Get the stepService. + * @return the stepService + */ + public StepService getStepService() { + return _stepService; + } + + /** + * Set the stepService. + * @param stepService the stepService to set + */ + public void setStepService(StepService stepService) { + _stepService = stepService; + } + + public KnowledgeElement addKnowledgeElement(Scenario aScenario, + KnowledgeElement.Properties kprop) throws MissedPropertyException, + InvalidPropertyException, MultiplyDefinedException { + // ------------------------------------------------------------------------------- + KnowledgeElement kelm = new KnowledgeElement( + kprop.setOwnerScenario(aScenario)); + Session session = Database.getSession(); + Transaction transax = session.getTransaction(); + try { + session.save(kelm); + // Update of my persistent data + aScenario.getKnowledgeElements().add(kelm); + // Update of my transient data + List known = aScenario + .getKnowledgeElementsOf(kelm.getType()); // Initializes this.known, if not yet done + known.add(kelm); + if (kelm.getType().equals("usecase")) { + aScenario.setUcase(kelm); + } else if (aScenario.getKnowledgeElementsList() != null) { // If null, knowl will be initialized when needed + aScenario.getKnowledgeElementsList().add(kelm); + } + // Update of the index of Knowledge Elements + getIndexService().add(kelm); + update(aScenario); + return kelm; + } catch (RuntimeException e) { + if (transax != null && transax.isActive()) { + // Second try-catch as the rollback could fail as well + try { + transax.rollback(); + } catch (HibernateException error) { + logger.debug("Error rolling back transaction", error); + } + // Throw again the first exception + throw e; + } + return null; + } catch (IOException error) { + logger.error( + "Unable to index the knowedge element '" + kelm.getIndex() + + "', reason:", error); + return null; + } + } + + private boolean update(Scenario aScenario) { + // --------------------------- + try { + Database.getSession().update(aScenario); // Update of relational base + return true; + } catch (Exception error) { + logger.error("Unable to re-index the knowledge element '" + + aScenario.getIndex() + "', reason:", error); + return false; + } + } + + /** + * @return + */ + public IndexService getIndexService() { + return _indexService; + } + + public void setIndexService(IndexService indexService) { + _indexService = indexService; + } + + public void checkin(Scenario aScenario) { + // ---------------------- + aScenario.setUser(null); + aScenario.setLastModificationDate(Calendar.getInstance().getTime()); + Database.getSession().update(aScenario); + } + + public boolean checkout(Scenario aScenario, User user) { + // ----------------------------------- + if (!aScenario.getOwnerStudy().isStaffedBy(user)) + return false; + + aScenario.setUser(user); + aScenario.setLastModificationDate(Calendar.getInstance().getTime()); + Database.getSession().update(this); + return true; + } + + // ============================================================================================================================== + // Private services + // ============================================================================================================================== + + public void copyContentsUpTo (Scenario scenario, Step lastep) { + // ------------------------------------------- + Scenario base = (Scenario)lastep.getOwner(); + Step[] from = getProjectElementService().getSteps(base); + Step[] to = getProjectElementService().getSteps(scenario); + for (int i=0; i lastep.getNumber()) break; + + List docs = step.getAllDocuments(); + for (Iterator j=docs.iterator(); j.hasNext(); ) { + Publication doc = getPublicationService().copy(j.next(), scenario); // Creation of a new reference to the document + // Database.getSession().save(doc); Publications MUST be saved later through cascading when saving the scenario + to[i].add(doc); + } + List ctex = step.getAllSimulationContexts(); + for (Iterator j=ctex.iterator(); j.hasNext(); ) { + getStepService().addSimulationContext(to[i], j.next()); + } + } + } + + public boolean isEmpty (Scenario scenario) { + // ------------------------- + Step[] mystep = getProjectElementService().getSteps(scenario); + for (int i=0; i selectKnowledgeElementsWhere (KnowledgeElement.Properties... kprop); + public List selectStudiesWhere (Study.Properties... sprop); + public void indexStudy (Study study); +} diff --git a/Workspace/Siman-Common/src/org/splat/service/SearchServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/SearchServiceImpl.java new file mode 100644 index 0000000..4cd5ab6 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/SearchServiceImpl.java @@ -0,0 +1,328 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 05.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.log4j.Logger; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanFilter; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.FilterClause; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TermsFilter; +import org.apache.lucene.search.TopFieldDocs; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.FSDirectory; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.KnowledgeElementType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.Visibility; +import org.splat.dal.bo.som.Study.Properties; +import org.splat.dal.dao.som.Database; +import org.splat.service.dto.Proxy; +import org.splat.service.technical.IndexService; +import org.splat.service.technical.IndexServiceImpl; +import org.splat.service.technical.RepositoryService; + +/** + * @author rkv + * + */ +public class SearchServiceImpl implements SearchService { + + public final static Logger logger = org.splat.service.SearchServiceImpl.logger; + + private RepositoryService _repositoryService; + + private IndexService _indexService; + + public List selectKnowledgeElementsWhere (KnowledgeElement.Properties... kprop) { + // --------------------------------------------------------------------------------------------- + List result = new ArrayList(); + int hitsize = 20; + try { + + // Creation of the Lucene query + File indir = getRepositoryService().getRepositoryIndexDirectory(); + Directory index = FSDirectory.open(indir); + IndexSearcher searcher = new IndexSearcher(index, true); + BooleanQuery fulquery = new BooleanQuery(); + + for (int i=0; i context = kprop[i].getSimulationContexts(); + if (context != null && context.size() > 0) { + BooleanQuery critext = new BooleanQuery(); + for (Iterator j=context.iterator(); j.hasNext();) { + SimulationContext seltext = j.next(); + input = new Term(String.valueOf(seltext.getType().getIndex())); + critext.add(new TermQuery(input.createTerm(seltext.getValue())), BooleanClause.Occur.MUST); + } + query.add(critext, BooleanClause.Occur.MUST); + } + fulquery.add(query, BooleanClause.Occur.SHOULD); + } + if (logger.isInfoEnabled()) { + logger.info("Searching knowledges by Lucene query \"" + fulquery.toString() + "\"."); + } + // Creation of the knowledge filter + BooleanFilter filter = new BooleanFilter(); + TermsFilter select = new TermsFilter(); + Term mytype = new Term("class"); + select.addTerm( mytype.createTerm("KnowledgeElement") ); + filter.add(new FilterClause(select, BooleanClause.Occur.SHOULD)); + + // Creation of the sort criteria + Sort sort = new Sort(new SortField("title", SortField.STRING)); + + // Search + TopFieldDocs found = searcher.search(fulquery, filter, hitsize, sort); + + if (found.totalHits < 1) return result; // No study found + + // Construction of the result list + ScoreDoc[] hits = found.scoreDocs; + for (int i=0; i selectStudiesWhere (Study.Properties... sprop) { + // ------------------------------------------------------------------------ + List result = new ArrayList(); + int hitsize = 20; + try { + + // Creation of the Lucene query + File indir = getRepositoryService().getRepositoryIndexDirectory(); + Directory index = FSDirectory.open(indir); + IndexSearcher searcher = new IndexSearcher(index, true); + BooleanQuery fulquery = new BooleanQuery(); + + for (int i=0; i context = sprop[i].getSimulationContexts(); + if (context != null && context.size() > 0) { + BooleanQuery critext = new BooleanQuery(); + for (Iterator j=context.iterator(); j.hasNext();) { + SimulationContext seltext = j.next(); + input = new Term(String.valueOf(seltext.getType().getIndex())); + critext.add(new TermQuery(input.createTerm(seltext.getValue())), BooleanClause.Occur.MUST); + } + query.add(critext, BooleanClause.Occur.MUST); + } + fulquery.add(query, BooleanClause.Occur.SHOULD); + } + if (logger.isInfoEnabled()) { + logger.info("Searching studies by Lucene query \"" + fulquery.toString() + "\"."); + } + // Creation of the studies filter + BooleanFilter filter = new BooleanFilter(); + TermsFilter select = new TermsFilter(); + Term mytype = new Term("class"); + select.addTerm( mytype.createTerm("Study") ); + filter.add(new FilterClause(select, BooleanClause.Occur.SHOULD)); + + // Creation of the sort criteria + Sort sort = new Sort(new SortField("title", SortField.STRING)); + + // Search + TopFieldDocs found = searcher.search(fulquery, filter, hitsize, sort); + + if (found.totalHits < 1) return result; // No study found + + // Construction of the result list + ScoreDoc[] hits = found.scoreDocs; + for (int i=0; i index = selectStudiesWhere(sprop.setReference(study.getReference())); + + if (index.size() != 0) return; // The given study is already indexed + + IndexService lucin = getIndex(); + Scenario[] scenes = study.getScenarii(); + + lucin.add(study); + if (study.getProgressState() != ProgressState.inWORK) for (int i=0; i list = scenes[i].getAllKnowledgeElements(); + for (Iterator j=list.iterator(); j.hasNext(); ) { + lucin.add(j.next()); + } + } + } + catch (Exception error) { + Database.logger.error("Unable to index the study '" + study.getIndex() + "', reason:", error); + } + } + + public IndexService getIndex () throws IOException { + IndexService lucin = getIndexService(); + if ( !lucin.exists() ) lucin.create(); // Happens when re-indexing all studies + return lucin; + } + + /** + * Get the repositoryService. + * @return the repositoryService + */ + public RepositoryService getRepositoryService() { + return _repositoryService; + } + + /** + * Set the repositoryService. + * @param repositoryService the repositoryService to set + */ + public void setRepositoryService(RepositoryService repositoryService) { + _repositoryService = repositoryService; + } + + /** + * Get the indexService. + * @return the indexService + */ + public IndexService getIndexService() { + return _indexService; + } + + /** + * Set the indexService. + * @param indexService the indexService to set + */ + public void setIndexService(IndexService indexService) { + _indexService = indexService; + } +} diff --git a/Workspace/Siman-Common/src/org/splat/service/StepService.java b/Workspace/Siman-Common/src/org/splat/service/StepService.java new file mode 100644 index 0000000..9a0457a --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/StepService.java @@ -0,0 +1,38 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import org.splat.dal.bo.som.SimulationContext; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.som.Step; + +/** + * @author RKV + * + */ +public interface StepService { + + public SimulationContext addSimulationContext(Step aStep, + SimulationContext.Properties dprop) throws MissedPropertyException, + InvalidPropertyException, MultiplyDefinedException, + RuntimeException; + + /** + * @param firstStep + * @param context + * @return + */ + public SimulationContext addSimulationContext(Step firstStep, + SimulationContext context); + + public boolean removeSimulationContext(Step aStep, SimulationContext context); +} diff --git a/Workspace/Siman-Common/src/org/splat/service/StepServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/StepServiceImpl.java new file mode 100644 index 0000000..dda84d6 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/StepServiceImpl.java @@ -0,0 +1,125 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import java.util.Iterator; +import java.util.List; + +import org.hibernate.Session; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.dao.som.Database; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.service.technical.IndexService; +import org.splat.service.technical.IndexServiceImpl; +import org.splat.som.Step; + +/** + * @author RKV + * + */ +public class StepServiceImpl implements StepService { + + private IndexService _indexService; + + public SimulationContext addSimulationContext(Step aStep, + SimulationContext.Properties dprop) throws MissedPropertyException, + InvalidPropertyException, MultiplyDefinedException, + RuntimeException { + // ---------------------------------------------------------------------------------- + SimulationContext context = new SimulationContext(dprop.setStep(aStep + .getStep())); + return addSimulationContext(aStep, context); + } + + public SimulationContext addSimulationContext(Step aStep, + SimulationContext context) { + // ------------------------------------------------------------------------- + context.hold(); // Increments the reference count of simulation context + if (aStep.getOwner().isSaved()) + try { + Session session = Database.getSession(); + IndexService lucin = getIndexService(); + + if (!context.isSaved()) + session.save(context); + aStep.getOwner().add(context); + aStep.getContex().add(context); // The context is also referenced from this (transient) Step + session.update(aStep.getOwner()); + updateKnowledgeElementsIndex(aStep, lucin); + } catch (Exception error) { + return null; + } + else { // Happens when copying a scenario + aStep.getOwner().add(context); + aStep.getContex().add(context); // The context is also referenced from this (transient) Step + // In case of owner scenario, the Knowledge Element index will be updated later, when saving the scenario + } + return context; + } + + private void updateKnowledgeElementsIndex(Step aStep, IndexService lucin) { + // ------------------------------------------------------ + Scenario[] scenarii; + if (aStep.getOwner() instanceof Scenario) { + scenarii = new Scenario[1]; + scenarii[0] = (Scenario) aStep.getOwner(); + } else { + scenarii = aStep.getOwnerStudy().getScenarii(); + } + try { + for (int i = 0; i < scenarii.length; i++) { + Scenario scene = scenarii[i]; + List knelm = scene.getAllKnowledgeElements(); + for (Iterator j = knelm.iterator(); j + .hasNext();) { + KnowledgeElement kelm = j.next(); + lucin.update(kelm); + } + scene.updateMyIndex(lucin); + } + } catch (Exception error) { + // logger.error("Unable to re-index Knowledge Elements, reason:", error); + } + } + + public boolean removeSimulationContext (Step aStep, SimulationContext context) { +// ------------------------------------------------------------------ + SimulationContext torem = aStep.getSimulationContext(context.getIndex()); + Session session = Database.getSession(); + + if (torem == null) return false; + if (!aStep.getOwner().remove(torem)) return false; + + aStep.getContex().remove(torem); + session.update(aStep.getOwner()); + if (torem.isShared()) { + torem.release(); + session.update(torem); + } else { + session.delete(torem); + } + return true; + } + + /** + * @return + */ + public IndexService getIndexService() { + return _indexService; + } + + public void setIndexService(IndexService indexService) { + _indexService = indexService; + } +} diff --git a/Workspace/Siman-Common/src/org/splat/service/StudyService.java b/Workspace/Siman-Common/src/org/splat/service/StudyService.java new file mode 100644 index 0000000..e8d8c6f --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/StudyService.java @@ -0,0 +1,94 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.ValidationCycle; +import org.splat.dal.bo.som.Study.Properties; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; + +/** + * @author RKV + * + */ +public interface StudyService { + + public int generateLocalIndex(Study aStudy); + + public Study createStudy(Study.Properties sprop) + throws MissedPropertyException, InvalidPropertyException, + MultiplyDefinedException, RuntimeException; + + public SimulationContext addProjectContext(Study aStudy, + SimulationContext.Properties cprop) throws MissedPropertyException, + InvalidPropertyException, MultiplyDefinedException, + RuntimeException; + + public SimulationContext addProjectContext(Study aStudy, + SimulationContext context); + + public Scenario addScenario(Study aStudy, Scenario.Properties sprop) + throws MissedPropertyException, InvalidPropertyException, + MultiplyDefinedException, RuntimeException; + + public boolean removeProjectContext(Study aStudy, SimulationContext context); + + /** + * Demotes this study from In-Check to In-Draft then In-Work states. This function is called internally when demoting the final result + * document of the study. + * + * @return true if the demotion succeeded. + */ + public boolean demote(Study aStudy); + + /** + * Promotes this study from In-Work to In-Draft then In-Check and APPROVED states. This function is called internally when promoting the + * final result document of the study. + * + * @return true if the demotion succeeded. + */ + public boolean promote(Study aStudy); + + public boolean addContributor(Study aStudy, User user); + + public boolean removeContributor(Study aStudy, User... users); + + public void setValidationCycle(Study aStudy, DocumentType type, + ValidationCycle.Properties vprop); + + /** + * Moves this study from the Private to the Public area of the repository. + * + * @return true if the move succeeded. + * @see #isPublic() + */ + public boolean moveToPublic(Study aStudy); + + /** + * Moves this study from the Public to the Reference area of the repository. For being moved to the Reference area, the study must + * previously be approved. + * + * @return true if the move succeeded. + * @see #moveToPublic() + * @see #isPublic() + * @see Publication#approve(Date) + */ + public boolean moveToReference(Study aStudy); + + public boolean update(Study aStudy, Properties sprop) + throws InvalidPropertyException; +} diff --git a/Workspace/Siman-Common/src/org/splat/service/StudyServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/StudyServiceImpl.java new file mode 100644 index 0000000..ab57263 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/StudyServiceImpl.java @@ -0,0 +1,476 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service; + +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import org.apache.log4j.Logger; +import org.hibernate.Session; +import org.splat.dal.bo.kernel.Relation; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.ActorRelation; +import org.splat.dal.bo.som.ContributorRelation; +import org.splat.dal.bo.som.DescriptionAttribute; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.IDBuilder; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.KnowledgeElementType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.ValidationCycle; +import org.splat.dal.bo.som.ValidationCycleRelation; +import org.splat.dal.bo.som.Visibility; +import org.splat.dal.bo.som.Study.Properties; +import org.splat.dal.dao.som.Database; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.kernel.UserDirectory; +import org.splat.service.technical.IndexService; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.som.Revision; + +/** + * @author RKV + * + */ +public class StudyServiceImpl implements StudyService { + + public final static Logger logger = org.splat.service.StudyServiceImpl.logger; + + private IndexService _indexService; + + private StepService _stepService; + + private ScenarioService _scenarioService; + + private ProjectSettingsService _projectSettingsService; + + private ProjectElementService _projectElementService; + + public Study createStudy(Study.Properties sprop) + throws MissedPropertyException, InvalidPropertyException, + MultiplyDefinedException, RuntimeException { + sprop.setReference(getProjectSettings().getReferencePattern()); + Study study = new Study(sprop); + + buildReference(study); + Database.getSession().save(study); // TODO: use StudyDAO and throw out Database. + try { + IndexService lucin = getIndex(); + lucin.add(study); + } catch (IOException error) { + logger.error("Unable to index the study '" + study.getIndex() + + "', reason:", error); + // Continue and try to index later + } + return study; + } + + public IndexService getIndex() throws IOException { + IndexService lucin = getIndexService(); + if (!lucin.exists()) + lucin.create(); // Happens when re-indexing all studies + return lucin; + } + + public SimulationContext addProjectContext(Study aStudy, + SimulationContext.Properties cprop) throws MissedPropertyException, + InvalidPropertyException, MultiplyDefinedException, + RuntimeException { + // ------------------------------------------------------------------------------- + SimulationContext added = getStepService().addSimulationContext( + getProjectElementService().getFirstStep(aStudy), cprop); + update(aStudy); + return added; + } + + /** + * @return + */ + public StepService getStepService() { + return _stepService; + } + + public void setStepService(StepService stepService) { + _stepService = stepService; + } + + public SimulationContext addProjectContext(Study aStudy, + SimulationContext context) { + // ---------------------------------------------------------------------- + SimulationContext added = getStepService().addSimulationContext( + getProjectElementService().getFirstStep(aStudy), context); + update(aStudy); + return added; + } + + public boolean addContributor(Study aStudy, User user) { + // ----------------------------------------- + List contributor = aStudy.getModifiableContributors(); // Initializes contributor + for (Iterator i = contributor.iterator(); i.hasNext();) { + User present = i.next(); + if (present.equals(user)) + return false; + } + boolean absent = aStudy.getModifiableActors().add(user); // User may already be a reviewer or an approver + + aStudy.addRelation(new ContributorRelation(aStudy, user)); + if (absent) + update(aStudy); // Else, useless to re-index the study + contributor.add(user); + return true; + } + + /** + * Moves this study from the Public to the Reference area of the repository. For being moved to the Reference area, the study must + * previously be approved. + * + * @return true if the move succeeded. + * @see #moveToPublic() + * @see #isPublic() + * @see Publication#approve(Date) + */ + public boolean moveToReference(Study aStudy) { + // --------------------------------- + if (aStudy.getProgressState() != ProgressState.APPROVED) + return false; + if (aStudy.getVisibility() != Visibility.PUBLIC) + return false; + + aStudy.setVisibility(Visibility.REFERENCE); + if (update(aStudy)) { + return updateKnowledgeElementsIndex(aStudy); // If fails, the database roll-back is under responsibility of the caller + } + return false; + } + + public boolean update(Study aStudy, Properties sprop) + throws InvalidPropertyException { + if (sprop.getTitle() != null) + aStudy.setTitle(sprop.getTitle()); + if (sprop.getSummary() != null) + aStudy.setAttribute(new DescriptionAttribute(aStudy, sprop + .getSummary())); + // TODO: To be completed + return update(aStudy); + } + + public boolean buildReference(Study aStudy) { + String pattern = aStudy.getReference(); // The study being supposed just created, its reference is the reference pattern + IDBuilder tool = Database.selectIDBuilder(aStudy.getDate()); + if (tool == null) { + tool = new IDBuilder(aStudy.getDate()); + Database.getSession().save(tool); + } + aStudy.setReference(tool.buildReference(pattern, aStudy)); + return true; + } + + public boolean publishes(Study aStudy, Document doc) { + // --------------------------------------- + if (!aStudy.publishes(doc)) { + Scenario[] scene = aStudy.getScenarii(); + for (int i = 0; i < scene.length; i++) { + if (scene[i].publishes(doc)) + return true; + } + } + return false; + } + + public boolean removeContributor(Study aStudy, User... users) { + // ------------------------------------------------ + List contributor = aStudy.getModifiableContributors(); // Initializes contributor + Boolean done = false; + for (int i = 0; i < users.length; i++) { + User user = users[i]; + for (Iterator j = contributor.iterator(); j.hasNext();) { + User present = j.next(); + if (!present.equals(user)) + continue; + + aStudy.removeRelation(ContributorRelation.class, user); + j.remove(); // Updates the contributor shortcut + done = true; + break; + } + } + if (done) + update(aStudy); + return done; + } + + public boolean removeProjectContext(Study aStudy, SimulationContext context) { + // --------------------------------------------------------------- + boolean done = getStepService().removeSimulationContext( + getProjectElementService().getFirstStep(aStudy), context); + update(aStudy); + return done; + } + + public void setValidationCycle(Study aStudy, DocumentType type, + ValidationCycle.Properties vprop) { + HashMap validactor = aStudy + .getValidationCycles(); + if (validactor == null) + aStudy.setShortCuts(); // Initializes validactor and actor + + String cname = type.getName(); + ValidationCycle cycle = validactor.get(cname); + + if (cycle != null && cycle.isAssigned()) { + cycle.resetActors(vprop); + } else + try { + cycle = new ValidationCycle(aStudy, vprop.setDocumentType(type)); + + ValidationCycleRelation link = cycle.getContext(); + aStudy.addRelation(link); + validactor.put(cname, link.getTo()); // Replaces the cycle if exists as default, + } catch (Exception error) { + logger.error("Unable to re-index Knowledge Elements, reason:", + error); + return; + } + resetActorsShortCut(aStudy); + update(aStudy); // Re-index the study, just in case + } + + private void resetActorsShortCut(Study aStudy) { + aStudy.getModifiableActors().clear(); + // Get all actors involved in validation cycles + for (Iterator i = aStudy.getValidationCycles() + .values().iterator(); i.hasNext();) { + ValidationCycle cycle = i.next(); + User[] user = cycle.getAllActors(); + for (int j = 0; j < user.length; j++) + aStudy.getModifiableActors().add(user[j]); + } + // Get all other actors + for (Iterator i = aStudy.getAllRelations().iterator(); i + .hasNext();) { + Relation link = i.next(); + Class kindof = link.getClass().getSuperclass(); + if (!kindof.equals(ActorRelation.class)) + continue; + aStudy.getModifiableActors().add(((ActorRelation) link).getTo()); + } + } + + /** + * Demotes this study from In-Check to In-Draft then In-Work states. This function is called internally when demoting the final result + * document of the study. + * + * @return true if the demotion succeeded. + */ + public boolean demote(Study aStudy) { + // --------------------------- + if (aStudy.getProgressState() == ProgressState.inCHECK) + aStudy.setProgressState(ProgressState.inDRAFT); + else if (aStudy.getProgressState() == ProgressState.inDRAFT) + aStudy.setProgressState(ProgressState.inWORK); + else + return false; + return update(aStudy); + } + + public int generateLocalIndex(Study aStudy) { + aStudy.setLastLocalIndex(aStudy.getLastLocalIndex() + 1); + Database.getSession().update(this); + return aStudy.getLastLocalIndex(); + } + + // ============================================================================================================================== + // Public member functions + // ============================================================================================================================== + + public Scenario addScenario(Study aStudy, Scenario.Properties sprop) + throws MissedPropertyException, InvalidPropertyException, + MultiplyDefinedException, RuntimeException { + // ------------------------------------------------------- + if (sprop.getManager() == null) + sprop.setManager(aStudy.getAuthor()); + + Scenario scenario = new Scenario(sprop.setOwnerStudy(aStudy)); + if (sprop.getBaseStep() != null) + getScenarioService() + .copyContentsUpTo(scenario, sprop.getBaseStep()); + Scenario previous = sprop.getInsertAfter(); + Session session = Database.getSession(); + + if (previous == null) { + aStudy.getScenariiList().add(scenario); + } else { + aStudy.getScenariiList().add( + aStudy.getScenariiList().indexOf(previous) + 1, scenario); + } + session.update(this); // No need to update the Lucene index + session.save(scenario); // Must be done after updating this study because of the back reference to the study + if (sprop.getBaseStep() != null) { + // No need to update the Knowledge Element index as Knowledge Elements are not copied + scenario.refresh(); // Because saving the scenario changes the hashcode of copied Publications + } + KnowledgeElementType ucase = KnowledgeElement.selectType("usecase"); + KnowledgeElement.Properties kprop = new KnowledgeElement.Properties(); + User admin = UserDirectory.selectUser(1); // First user created when creating the database + kprop.setType(ucase).setTitle(aStudy.getTitle()) + .setValue(scenario.getTitle()).setAuthor(admin); // Internal Knowledge Element required by the validation process of + // knowledges + getScenarioService().addKnowledgeElement(scenario, kprop); + return scenario; + } + + /** + * @return + */ + private ScenarioService getScenarioService() { + return _scenarioService; + } + + public void setScenarioService(ScenarioService scenarioService) { + _scenarioService = scenarioService; + } + + /** + * Promotes this study from In-Work to In-Draft then In-Check and APPROVED states. This function is called internally when promoting the + * final result document of the study. + * + * @return true if the demotion succeeded. + */ + public boolean promote(Study aStudy) { + // ---------------------------- + if (aStudy.getProgressState() == ProgressState.inWORK) { + aStudy.setProgressState(ProgressState.inDRAFT); + } else if (aStudy.getProgressState() == ProgressState.inDRAFT) { + aStudy.setProgressState(ProgressState.inCHECK); + Revision myvers = new Revision(aStudy.getVersion()); + if (myvers.isMinor()) { + aStudy.setVersion(myvers.incrementAs(aStudy.getProgressState()) + .toString()); + } + } else if (aStudy.getProgressState() == ProgressState.inCHECK) { + aStudy.setProgressState(ProgressState.APPROVED); + } else + return false; + + return update(aStudy); + } + + /** + * Moves this study from the Private to the Public area of the repository. + * + * @return true if the move succeeded. + * @see #isPublic() + */ + public boolean moveToPublic(Study aStudy) { + // ------------------------------ + if (aStudy.getVisibility() != Visibility.PRIVATE) + return false; + + aStudy.setVisibility(Visibility.PUBLIC); + if (update(aStudy)) { + return updateKnowledgeElementsIndex(aStudy); // If fails, the database roll-back is under responsibility of the caller + } + return false; + } + + private boolean update(Study aStudy) { + try { + Database.getSession().update(aStudy); // Update of relational base + getIndex().update(aStudy); // Update of Lucene index + return true; + } catch (Exception error) { + logger.error("Unable to re-index the study '" + aStudy.getIndex() + + "', reason:", error); + return false; + } + } + + private boolean updateKnowledgeElementsIndex(Study aStudy) { + // ---------------------------------------------- + try { + IndexService lucin = getIndex(); + + for (Iterator i = aStudy.getScenariiList().iterator(); i + .hasNext();) { + Scenario scene = i.next(); + for (Iterator j = scene + .getAllKnowledgeElements().iterator(); j.hasNext();) { + KnowledgeElement kelm = j.next(); + lucin.update(kelm); + } + } + return true; + } catch (Exception error) { + logger.error("Unable to re-index Knowledge Elements, reason:", + error); + return false; + } + } + + /** + * @return + */ + public IndexService getIndexService() { + return _indexService; + } + + public void setIndexService(IndexService indexService) { + _indexService = indexService; + } + + /** + * Get project settings. + * + * @return Project settings service + */ + private ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + + /** + * Set project settings service. + * + * @param projectSettingsService + * project settings service + */ + public void setProjectSettings(ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } + + /** + * Get the projectElementService. + * + * @return the projectElementService + */ + public ProjectElementService getProjectElementService() { + return _projectElementService; + } + + /** + * Set the projectElementService. + * + * @param projectElementService + * the projectElementService to set + */ + public void setProjectElementService( + ProjectElementService projectElementService) { + _projectElementService = projectElementService; + } +} diff --git a/Workspace/Siman-Common/src/org/splat/som/Proxy.java b/Workspace/Siman-Common/src/org/splat/service/dto/Proxy.java similarity index 82% rename from Workspace/Siman-Common/src/org/splat/som/Proxy.java rename to Workspace/Siman-Common/src/org/splat/service/dto/Proxy.java index c316ede..8b2c6e6 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Proxy.java +++ b/Workspace/Siman-Common/src/org/splat/service/dto/Proxy.java @@ -1,4 +1,15 @@ -package org.splat.som; +package org.splat.service.dto; + +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.dao.som.Database; + + +import org.splat.service.technical.IndexServiceImpl; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.Study.Properties; + /** * Stand proxy for entities such as Study and Knowledge Element returned by Lucene-based searches. * This interface provides access to properties of searched entities which can be presented in a result search list @@ -11,8 +22,8 @@ package org.splat.som; * @see Database#selectKnowledgeElementsWhere(KnowledgeElement.Properties) * @see Database#selectStudy(int) * @see Database#selectKnowledgeElement(int) - * @see Index - * @see Index.ObjectProxy + * @see IndexServiceImpl + * @see IndexServiceImpl.ObjectProxy * @author Daniel Brunier-Coulin * @copyright OPEN CASCADE 2012 */ diff --git a/Workspace/Siman-Common/src/org/splat/service/technical/DatabaseService.java b/Workspace/Siman-Common/src/org/splat/service/technical/DatabaseService.java new file mode 100644 index 0000000..54d99dc --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/technical/DatabaseService.java @@ -0,0 +1,18 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 05.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service.technical; + +/** + * @author rkv + * + */ +public interface DatabaseService { + +} diff --git a/Workspace/Siman-Common/src/org/splat/service/technical/DatabaseServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/technical/DatabaseServiceImpl.java new file mode 100644 index 0000000..a219f01 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/technical/DatabaseServiceImpl.java @@ -0,0 +1,18 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 05.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service.technical; + +/** + * @author rkv + * + */ +public class DatabaseServiceImpl implements DatabaseService { + +} diff --git a/Workspace/Siman-Common/src/org/splat/service/technical/IndexService.java b/Workspace/Siman-Common/src/org/splat/service/technical/IndexService.java new file mode 100644 index 0000000..91c57c4 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/technical/IndexService.java @@ -0,0 +1,36 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 05.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service.technical; + +import java.io.IOException; + +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.Study; + +/** + * @author rkv + * + */ +public interface IndexService { + + public void configure() throws IOException; + + public void create() throws IOException; + + public boolean exists(); + + public void add(KnowledgeElement kelm) throws IOException; + + public void add(Study study) throws IOException; + + public void update(Study study) throws IOException; + + public void update(KnowledgeElement kelm) throws IOException; +} diff --git a/Workspace/Siman-Common/src/org/splat/som/Index.java b/Workspace/Siman-Common/src/org/splat/service/technical/IndexServiceImpl.java similarity index 71% rename from Workspace/Siman-Common/src/org/splat/som/Index.java rename to Workspace/Siman-Common/src/org/splat/service/technical/IndexServiceImpl.java index 2f5194b..3caed85 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Index.java +++ b/Workspace/Siman-Common/src/org/splat/service/technical/IndexServiceImpl.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.service.technical; /** * * @author Daniel Brunier-Coulin @@ -24,16 +24,27 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.util.Version; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.Study; +import org.splat.dal.dao.som.Database; +import org.splat.service.ProjectElementService; +import org.splat.service.dto.Proxy; +import org.splat.som.Step; -class Index { +public class IndexServiceImpl implements IndexService { private Directory index; private org.apache.lucene.document.Document body; + private ProjectElementService _projectElementService; + private RepositoryService _repositoryService; protected static StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_29); - private static final Logger logger = Logger.getLogger(Index.class); + private static final Logger logger = Logger.getLogger(IndexServiceImpl.class); private class Entry extends IndexWriter { // --------------------------------------- @@ -76,7 +87,7 @@ class Index { // Addition of optional fields setActorsOf(study); - setContextAt(study.getSteps()); + setContextAt(getProjectElementService().getSteps(study)); } private Entry (KnowledgeElement kelm) throws CorruptIndexException, LockObtainFailedException, IOException { @@ -117,8 +128,8 @@ class Index { Scenario scene = kelm.getOwnerScenario(); Study study = scene.getOwnerStudy(); setActorsOf(study); // For restricting the visibility of knowledges attached to private studies - setContextAt(study.getSteps()); - setContextAt(scene.getSteps()); + setContextAt(getProjectElementService().getSteps(study)); + setContextAt(getProjectElementService().getSteps(scene)); } private void add () throws CorruptIndexException, IOException { @@ -198,52 +209,52 @@ class Index { // Construction // ============================================================================================================================== - protected static void create () throws IOException { + public void configure () throws IOException { + File indir = getRepositoryService().getRepositoryIndexDirectory(); + index = FSDirectory.open(indir); + body = new org.apache.lucene.document.Document(); + body.add( new Field("index", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); + body.add( new Field("class", "", Field.Store.NO, Field.Index.NOT_ANALYZED) ); + body.add( new Field("type", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); + body.add( new Field("ref", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); + body.add( new Field("area", "", Field.Store.NO, Field.Index.NOT_ANALYZED) ); + body.add( new Field("state", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); + body.add( new Field("author", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); + body.add( new Field("title", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); + body.add( new Field("contents","", Field.Store.NO, Field.Index.ANALYZED) ); + if ( !this.exists() ) this.create(); // Happens when re-indexing all studies + } + + public void create () throws IOException { // ------------------------------- - Directory index = FSDirectory.open(Database.getRepositoryIndexDirectory()); + Directory index = FSDirectory.open(getRepositoryService().getRepositoryIndexDirectory()); IndexWriter writer = new IndexWriter(index, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED); writer.close(); // ==== Creates an empty index } - protected Index () throws IOException { -// ------------------ - File indir = Database.getRepositoryIndexDirectory(); - index = FSDirectory.open(indir); - body = new org.apache.lucene.document.Document(); - body.add( new Field("index", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); - body.add( new Field("class", "", Field.Store.NO, Field.Index.NOT_ANALYZED) ); - body.add( new Field("type", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); - body.add( new Field("ref", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); - body.add( new Field("area", "", Field.Store.NO, Field.Index.NOT_ANALYZED) ); - body.add( new Field("state", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); - body.add( new Field("author", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); - body.add( new Field("title", "", Field.Store.YES, Field.Index.NOT_ANALYZED) ); - body.add( new Field("contents","", Field.Store.NO, Field.Index.ANALYZED) ); - } - // ============================================================================================================================== // Member functions // ============================================================================================================================== - protected void add (Study study) throws IOException { + public void add (Study study) throws IOException { // -------------------------------- - Index.Entry entry = new Entry(study); + IndexServiceImpl.Entry entry = new Entry(study); entry.add(); if (logger.isInfoEnabled()) { logger.info("Study \"" + study.getIndex() + "\" indexed."); } } - protected void add (KnowledgeElement kelm) throws IOException { + public void add (KnowledgeElement kelm) throws IOException { // ------------------------------------------ - Index.Entry entry = new Entry(kelm); + IndexServiceImpl.Entry entry = new Entry(kelm); entry.add(); if (logger.isInfoEnabled()) { logger.info("Knowledge \"" + kelm.getIndex() + "\" indexed."); } } - protected boolean exists () { + public boolean exists () { // --------------------------- try { return IndexReader.indexExists(index); @@ -254,21 +265,52 @@ class Index { } } - protected void update (Study study) throws IOException { + public void update (Study study) throws IOException { // ----------------------------------- - Index.Entry entry = new Entry(study); + IndexServiceImpl.Entry entry = new Entry(study); entry.update(); if (logger.isInfoEnabled()) { logger.info("Study \"" + study.getIndex() + "\" re-indexed."); } } - protected void update (KnowledgeElement kelm) throws IOException { + public void update (KnowledgeElement kelm) throws IOException { // --------------------------------------------- - Index.Entry entry = new Entry(kelm); + IndexServiceImpl.Entry entry = new Entry(kelm); entry.update(); if (logger.isInfoEnabled()) { logger.info("Knowledge \"" + kelm.getIndex() + "\" re-indexed."); } } + /** + * Get the projectElementService. + * @return the projectElementService + */ + public ProjectElementService getProjectElementService() { + return _projectElementService; + } + + /** + * Set the projectElementService. + * @param projectElementService the projectElementService to set + */ + public void setProjectElementService(ProjectElementService projectElementService) { + _projectElementService = projectElementService; + } + + /** + * Get the repositoryService. + * @return the repositoryService + */ + public RepositoryService getRepositoryService() { + return _repositoryService; + } + + /** + * Set the repositoryService. + * @param repositoryService the repositoryService to set + */ + public void setRepositoryService(RepositoryService repositoryService) { + _repositoryService = repositoryService; + } } \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/service/technical/ProjectSettingsService.java b/Workspace/Siman-Common/src/org/splat/service/technical/ProjectSettingsService.java new file mode 100644 index 0000000..856126f --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/technical/ProjectSettingsService.java @@ -0,0 +1,82 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 05.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service.technical; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.splat.dal.bo.som.ProjectElement; +import org.splat.service.technical.ProjectSettingsService.Step; +import org.splat.service.technical.ProjectSettingsServiceImpl.FileNaming; + +/** + * @author rkv + * + */ +public interface ProjectSettingsService { + + public static class Step { + // ------------------------ + int number; + private Class level; // Study or Scenario + Set> contents; // Set of Document and/or Knowledge + private String path; + + Step(int number, Class level, String path) { + this.initialize(number, level, path); + } + + private Step(int number, Class level, + Class contents, String path) { + this.initialize(number, level, path); + this.contents.add(contents); + } + + private void initialize(int number, + Class level, String path) { + this.number = number; + this.level = level; + this.path = path + "/"; + this.contents = new HashSet>(); + } + + public boolean appliesTo(Class level) { + return (level == this.level); + } + + public boolean mayContain(Class type) { + return contents.contains(type); + } + + public int getNumber() { + return number; + } + + public String getPath() { + return path; + } + } + + public FileNaming getFileNamingScheme(); + + public List getAllSteps(); + + public String getReferencePattern(); + + public String getRevisionPattern(); + + public void configure(String filename) throws IOException, SQLException; + + public List getStepsOf( + Class level); +} diff --git a/Workspace/Siman-Common/src/org/splat/som/ProjectSettings.java b/Workspace/Siman-Common/src/org/splat/service/technical/ProjectSettingsServiceImpl.java similarity index 78% rename from Workspace/Siman-Common/src/org/splat/som/ProjectSettings.java rename to Workspace/Siman-Common/src/org/splat/service/technical/ProjectSettingsServiceImpl.java index 413c33f..9d6cfec 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ProjectSettings.java +++ b/Workspace/Siman-Common/src/org/splat/service/technical/ProjectSettingsServiceImpl.java @@ -1,4 +1,4 @@ -package org.splat.som; +package org.splat.service.technical; /** * * @author Daniel Brunier-Coulin @@ -10,7 +10,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.sql.SQLException; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -26,19 +25,29 @@ import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.KnowledgeElementType; +import org.splat.dal.bo.som.ProjectElement; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.ValidationCycle.Actor; +import org.splat.dal.dao.som.Database; import org.splat.manox.XDOM; -import org.splat.som.ValidationCycle.Actor; -public class ProjectSettings { +public class ProjectSettingsServiceImpl implements ProjectSettingsService { // Non persistent configuration information private Properties reprop; // Repository settings private String pattern; // Pattern of study references private FileNaming naming; // Scheme of file names stored into the repository private String versioning; // Pattern of the presentation of version numbers - private Vector steps; // Ordered list of (transient) study steps - private Vector concycles; // Configuration document validation cycles + private Vector steps; // Ordered list of (transient) study steps + private Vector concycles; // Configuration document validation cycles // Temporary attributes initialized from the configuration file for populating the database with object types private LinkedHashMap mapuse; // Document type names and uses mapping @@ -48,53 +57,21 @@ public class ProjectSettings { private Vector sclass; // Study classifications // Other resources - private static ProjectSettings my = null; // Singleton instance - protected final static Logger logger = Logger.getLogger(ProjectSettings.class); - - protected enum FileNaming { title, encoded, asis } - public static class Step { -// ------------------------ - private int number; - private Class level; // Study or Scenario - private Set> contents; // Set of Document and/or Knowledge - private String path; - - private Step (int number, Class level, String path) { - this.initialize(number, level, path); - } - private Step (int number, Class level, Class contents, String path) { - this.initialize(number, level, path); - this.contents.add(contents); - } - private void initialize (int number, Class level, String path) { - this.number = number; - this.level = level; - this.path = path + "/"; - this.contents = new HashSet>(); - } - public boolean appliesTo (Class level) { - return (level == this.level); - } - public boolean mayContain (Class type) { - return contents.contains(type); - } - public int getNumber () { - return number; - } - public String getPath () { - return path; - } - } - public static class ValidationCycle { + private static ProjectSettingsServiceImpl my = null; // Singleton instance + private Database _database; + protected final static Logger logger = Logger.getLogger(ProjectSettingsServiceImpl.class); + + public enum FileNaming { title, encoded, asis } + public static class ProjectSettingsValidationCycle { // ----------------------------------- private String name; private Actor[] actor; - private ValidationCycle () { + private ProjectSettingsValidationCycle () { this.name = "built-in"; this.actor = new Actor[] { null, null, null }; } - private ValidationCycle (String name, Actor[] actor) { + private ProjectSettingsValidationCycle (String name, Actor[] actor) { this.name = name; this.actor = actor; } @@ -110,15 +87,15 @@ public class ProjectSettings { // Construction // ============================================================================================================================== - public static ProjectSettings getMe () { + public static ProjectSettingsServiceImpl getMe () { // -------------------------------------- - if (my == null) my = new ProjectSettings(); + if (my == null) my = new ProjectSettingsServiceImpl(); return my; } - protected ProjectSettings () { + protected ProjectSettingsServiceImpl () { // ---------------------------- reprop = new Properties(); - steps = new Vector(); + steps = new Vector(); } // ============================================================================================================================== @@ -129,7 +106,7 @@ public class ProjectSettings { // --------------------------------------- if (!steps.isEmpty()) return; // Project already configured - Database base = Database.getMe(); + Database base = getDatabase().getMe(); File config = new File(filename); if (config.exists()) { loadCustomization(config); @@ -140,13 +117,13 @@ public class ProjectSettings { base.configure(reprop); if (!base.isInitialized()) { base.initialize(); -//TODO: Move the second part of loadCustomization here + initialize(); // Populates the database with all necessary stuff } } - public static List getAllSteps () { + public List getAllSteps () { // --------------------------------------- - return my.steps; + return steps; } /** @@ -155,46 +132,45 @@ public class ProjectSettings { * * @return the validation cycles of the workflow */ - public static List getAllValidationCycles () { + public static List getAllValidationCycles () { // ------------------------------------------------------------- return my.concycles; } - public static FileNaming getFileNamingScheme () { + public FileNaming getFileNamingScheme () { // ----------------------------------------------- - return my.naming; + return naming; } - public static ValidationCycle getNewValidationCycle () { + public static ProjectSettingsValidationCycle getNewValidationCycle () { // ------------------------------------------------------ - return new ValidationCycle(); + return new ProjectSettingsValidationCycle(); } - public static String getReferencePattern () { -// ------------------------------------------- - return my.pattern; + public String getReferencePattern () { + return pattern; } - public static String getRevisionPattern () { + public String getRevisionPattern () { // ------------------------------------------ - return my.versioning; + return versioning; } - public static Step getStep (int number) { + public static ProjectSettingsService.Step getStep (int number) { // --------------------------------------- for (int i=0; i getStepsOf (Class level) { + public List getStepsOf (Class level) { // --------------------------------------------------------------------------- - Vector result = new Vector(); + Vector result = new Vector(); - for (int i=0; i tags = XDOM.getNamedChildNodes(child); natr = tags.get("storage").getAttributes(); - Step step = new Step(snum, Scenario.class, natr.getNamedItem("path").getNodeValue()); + ProjectSettingsService.Step step = new ProjectSettingsService.Step(snum, Scenario.class, natr.getNamedItem("path").getNodeValue()); // Keeping flow and classification information for eventual later use natr = tags.get("flow").getAttributes(); @@ -290,7 +266,7 @@ public class ProjectSettings { HashMap tags = XDOM.getNamedChildNodes(child); natr = tags.get("storage").getAttributes(); // Mandatory information - Step step = new Step(snum, Study.class, natr.getNamedItem("path").getNodeValue()); + ProjectSettingsService.Step step = new ProjectSettingsService.Step(snum, Study.class, natr.getNamedItem("path").getNodeValue()); // Keeping flow and classification information for eventual later use natr = tags.get("flow").getAttributes(); @@ -313,7 +289,7 @@ public class ProjectSettings { } // Validations tag child = children.get("validations"); - concycles = new Vector(); + concycles = new Vector(); datag = XDOM.getNamedChildNodes(child); String[] step = { "review", "approval", "acceptance" }; @@ -329,11 +305,11 @@ public class ProjectSettings { if (child == null) continue; // Validation step not required actor[j] = Actor.valueOf(child.getNodeValue()); } - concycles.add( new ValidationCycle(name, actor) ); + concycles.add( new ProjectSettingsValidationCycle(name, actor) ); } - concycles.add( new ValidationCycle() ); // Adds the built-in validation cycle + concycles.add( new ProjectSettingsValidationCycle() ); // Adds the built-in validation cycle - if (Database.getMe().isInitialized()) return; // No need to load object type definitions as they are already stored + if (getDatabase().getMe().isInitialized()) return; // No need to load object type definitions as they are already stored // Documents tag child = children.get("documents"); @@ -384,17 +360,17 @@ public class ProjectSettings { private void createDocumentTypes () { // ----------------------------------- DocumentType.Properties tprop = new DocumentType.Properties(); - HashMap> mapsteps = new HashMap>(); - HashMap mapresult = new HashMap(); + HashMap> mapsteps = new HashMap>(); + HashMap mapresult = new HashMap(); HashMap maptype = new HashMap(); - Vector slist = null; // List of Steps to which each document type is valid + Vector slist = null; // List of Steps to which each document type is valid int snum = 0; // Step number String type = null; String uses = null; for (Iterator i=flows.iterator(); i.hasNext(); snum++) { NamedNodeMap flow = i.next(); - Step step = steps.get(snum); + ProjectSettingsService.Step step = steps.get(snum); String[] contents = flow.getNamedItem("contents").getNodeValue().split(","); for (int j=0; j(); + if (slist == null) slist = new Vector(); slist.add(step); mapsteps.put(type, slist); } @@ -412,7 +388,7 @@ public class ProjectSettings { try { DocumentType tdoc = null; Set tset = mapuse.keySet(); - Step step; + ProjectSettingsService.Step step; for (Iterator i=tset.iterator(); i.hasNext(); ) { type = i.next(); slist = mapsteps.get(type); @@ -420,7 +396,7 @@ public class ProjectSettings { step = mapresult.get(type); tprop.clear(); - tprop.setName(type).setStep(slist.toArray(new Step[slist.size()])); + tprop.setName(type).setStep(slist.toArray(new ProjectSettingsService.Step[slist.size()])); if (uses != null) { tdoc = maptype.get(uses); if (tdoc == null) logger.warn("Undefined \"" + uses + "\" document type."); @@ -456,7 +432,7 @@ public class ProjectSettings { private void createSimulationContextTypes () { // -------------------------------------------- - HashMap mapstep = new HashMap(); + HashMap mapstep = new HashMap(); int snum = 0; for (Iterator i=sclass.iterator(); i.hasNext(); snum++) { NamedNodeMap clatr = i.next(); @@ -482,4 +458,18 @@ public class ProjectSettings { logger.warn("Error creating context types, reason:", error); // Should not happen } } + /** + * Get the database. + * @return the database + */ + public Database getDatabase() { + return _database; + } + /** + * Set the database. + * @param database the database to set + */ + public void setDatabase(Database database) { + _database = database; + } } \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/service/technical/RepositoryService.java b/Workspace/Siman-Common/src/org/splat/service/technical/RepositoryService.java new file mode 100644 index 0000000..51e83f5 --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/technical/RepositoryService.java @@ -0,0 +1,33 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service.technical; + +import java.io.File; + +import org.splat.dal.bo.kernel.User; + +/** + * @author RKV + * + */ +public interface RepositoryService { + + public File getRepositoryIndexDirectory(); + + public String getRepositoryVaultPath(); + + public String getBasepath(); + + public void setBasepath(String basepath); + + public File getDownloadDirectory(User user); + + public String getTemplatePath(); +} diff --git a/Workspace/Siman-Common/src/org/splat/service/technical/RepositoryServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/technical/RepositoryServiceImpl.java new file mode 100644 index 0000000..bc1dd1d --- /dev/null +++ b/Workspace/Siman-Common/src/org/splat/service/technical/RepositoryServiceImpl.java @@ -0,0 +1,57 @@ +/***************************************************************************** + * Company EURIWARE + * Application SIMAN + * File $Id$ + * Creation date 06.10.2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ + +package org.splat.service.technical; + +import java.io.File; + +import org.splat.dal.bo.kernel.User; + +/** + * @author RKV + * + */ +public class RepositoryServiceImpl implements RepositoryService { + + private String _basepath; + + public File getRepositoryIndexDirectory() { + return new File(getBasepath() + "lucin/"); + } + + public String getRepositoryVaultPath() { + return (getBasepath() + "vault/"); + } + + public File getDownloadDirectory(User user) { + StringBuffer path = new StringBuffer(_basepath).append("downloads/") + .append(user.getUsername()).append("/"); + return new File(path.toString()); + } + + public String getTemplatePath() { + return (_basepath + "templates/"); + } + + /** + * Get the basepath. + * @return the basepath + */ + public String getBasepath() { + return _basepath; + } + + /** + * Set the basepath. + * @param basepath the basepath to set + */ + public void setBasepath(String basepath) { + _basepath = basepath; + } +} diff --git a/Workspace/Siman-Common/src/org/splat/som/ApplicationRights.java b/Workspace/Siman-Common/src/org/splat/som/ApplicationRights.java index 2bde8cd..4a4da8a 100644 --- a/Workspace/Siman-Common/src/org/splat/som/ApplicationRights.java +++ b/Workspace/Siman-Common/src/org/splat/som/ApplicationRights.java @@ -8,8 +8,8 @@ package org.splat.som; import java.util.HashSet; import java.util.Set; -import org.splat.kernel.Role; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.Role; +import org.splat.dal.bo.kernel.User; public class ApplicationRights { diff --git a/Workspace/Siman-Common/src/org/splat/som/Database.java b/Workspace/Siman-Common/src/org/splat/som/Database.java deleted file mode 100644 index 4cca8a2..0000000 --- a/Workspace/Siman-Common/src/org/splat/som/Database.java +++ /dev/null @@ -1,712 +0,0 @@ -package org.splat.som; -/** - * - * @author Daniel Brunier-Coulin - * @copyright OPEN CASCADE 2012 - */ - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Properties; -import java.io.File; -import java.io.IOException; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.hibernate.Query; -import org.hibernate.Session; -import org.hibernate.jdbc.Work; -import org.apache.log4j.Logger; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanFilter; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.FilterClause; -import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.ScoreDoc; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.SortField; -import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.TermsFilter; -import org.apache.lucene.search.TopFieldDocs; -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.FSDirectory; - -import org.splat.kernel.User; -import org.splat.kernel.UserDirectory; -import org.splat.kernel.MultiplyDefinedException; -import org.splat.kernel.InvalidPropertyException; -import org.splat.kernel.MissedPropertyException; - - -public class Database extends org.splat.kernel.Database { - - private int uplevel = 0; // Level of database upgrade - private String basepath = null; // Path of the root directory of repository - - private static Database my = null; // Singleton instance - - protected class CreateTables extends org.splat.kernel.Database.CreateTables { -// --------------------------------------------------------------------------- - public void execute(Connection connex) throws SQLException - { - super.execute(connex); - -// Study Entity - String create = "CREATE TABLE `study` (" + - "`rid` int(10) UNSIGNED NOT NULL," + - "`sid` tinytext NOT NULL," + - "`title` tinytext NOT NULL," + - "`state` enum('inWORK','inDRAFT','inCHECK','APPROVED', 'TEMPLATE') NOT NULL default 'inWORK'," + - "`area` enum('PRIVATE','PUBLIC','REFERENCE') NOT NULL default 'PRIVATE'," + - "`manager` int(10) NOT NULL," + - "`version` tinytext NOT NULL," + - "`docount` int(10) UNSIGNED NOT NULL," + - "`history` int(10) UNSIGNED NOT NULL," + - "`credate` date NOT NULL," + - "`lasdate` date NOT NULL," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - -// Scenario Entity - create = "CREATE TABLE `scenario` (" + - "`rid` int(10) UNSIGNED NOT NULL," + - "`sid` int(10) UNSIGNED NOT NULL," + - "`owner` int(10) NOT NULL," + - "`scendex` int(3) NOT NULL," + - "`title` tinytext NOT NULL," + - "`manager` int(10) NOT NULL," + - "`cuser` int(10)," + - "`credate` date NOT NULL," + - "`lasdate` date NOT NULL," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - -// Document Entity and document tag (Publication) - create = "CREATE TABLE `document` (" + - "`rid` int(10) UNSIGNED NOT NULL," + - "`did` tinytext NOT NULL," + - "`type` int(10) NOT NULL," + - "`step` int(10) NOT NULL," + - "`state` enum('inWORK','inDRAFT','inCHECK','APPROVED','EXTERN') NOT NULL default 'inWORK'," + - "`name` tinytext NOT NULL," + - "`author` int(10) NOT NULL," + - "`version` tinytext," + - "`countag` int(10) UNSIGNED NOT NULL," + - "`history` int(10) NOT NULL," + - "`myfile` int(10) NOT NULL," + - "`lasdate` date NOT NULL," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - create = "CREATE TABLE `doctag` (" + - "`rid` int(10) UNSIGNED NOT NULL auto_increment," + - "`doc` int(10) NOT NULL," + - "`owner` int(10) NOT NULL," + - "`isnew` char(1) NOT NULL," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); -// Document types - create = "CREATE TABLE `doctype` (" + - "`rid` int(10) UNSIGNED NOT NULL auto_increment," + - "`name` tinytext NOT NULL," + - "`state` enum('inCHECK','APPROVED') NOT NULL default 'inCHECK'," + - "`step` tinytext NOT NULL," + - "`result` tinytext," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); -// Document types dependencies - create = "CREATE TABLE `docuse` (" + - "`owner` int(10) NOT NULL," + - "`rid` int(10) NOT NULL" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - -// ValidationCycle related object - create = "CREATE TABLE `cycle` (" + - "`rid` int(10) UNSIGNED NOT NULL auto_increment," + - "`type` int(10) NOT NULL," + - "`publisher` int(10)," + - "`reviewer` int(10)," + - "`approver` int(10)," + - "`signatory` int(10)," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - -// Timestamp related object - create = "CREATE TABLE `stamp` (" + - "`rid` int(10) UNSIGNED NOT NULL auto_increment," + - "`type` enum('PROMOTION','REVIEW','APPROVAL','ACCEPTANCE','DISTRIBUTION','REFUSAL') NOT NULL," + - "`author` int(10) NOT NULL," + - "`date` datetime NOT NULL," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - -// KnowledgeElements objects - create = "CREATE TABLE `knowelm` (" + - "`rid` int(10) UNSIGNED NOT NULL auto_increment," + - "`type` int(10) NOT NULL," + - "`owner` int(10) NOT NULL," + - "`state` enum('inWORK','inDRAFT','inCHECK','APPROVED') NOT NULL default 'inDRAFT'," + - "`title` tinytext NOT NULL," + - "`value` text NOT NULL," + - "`author` int(10) NOT NULL," + - "`date` date NOT NULL," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); -// KnowledgeElement types - create = "CREATE TABLE `knowtype` (" + - "`rid` int(10) UNSIGNED NOT NULL auto_increment," + - "`name` tinytext NOT NULL," + - "`state` enum('inWORK','inCHECK','APPROVED') NOT NULL default 'inCHECK'," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - -// SimulationContext objects - create = "CREATE TABLE `contelm` (" + - "`rid` int(10) UNSIGNED NOT NULL auto_increment," + - "`type` int(10) NOT NULL," + - "`step` int(10) NOT NULL," + - "`state` enum('inCHECK','APPROVED') NOT NULL default 'inCHECK'," + - "`value` text NOT NULL," + - "`counter` int(10) UNSIGNED NOT NULL," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); -// SimulationContext types - create = "CREATE TABLE `contype` (" + - "`rid` int(10) UNSIGNED NOT NULL auto_increment," + - "`name` tinytext NOT NULL," + - "`state` enum('inCHECK','APPROVED') NOT NULL default 'inCHECK'," + - "`step` int(10) NOT NULL," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - -// Many-to-many association between ProjectElement (Study and Scenario) and SimulationContext - create = "CREATE TABLE `projext` (" + - "`owner` int(10) NOT NULL," + - "`ordex` int(10) NOT NULL," + - "`rid` int(10) NOT NULL" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - -// File objects - create = "CREATE TABLE `file` (" + - "`rid` int(10) UNSIGNED NOT NULL," + - "`format` tinytext NOT NULL," + - "`path` tinytext NOT NULL," + - "`date` date NOT NULL," + - "PRIMARY KEY (`rid`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - -// Reference objects - create = "CREATE TABLE `refid` (" + - "`cycle` int(10) NOT NULL," + - "`base` int(10) NOT NULL," + - "PRIMARY KEY (`cycle`)" + - ") ENGINE=MyISAM DEFAULT CHARSET=latin1"; - request.execute(create); - } - } - protected class CheckVersion implements Work { -// -------------------------------------------- - public void execute(Connection connex) throws SQLException - { - DatabaseMetaData dbmdata = connex.getMetaData(); - String dbname = "simer"; //TODO: Get the name from meta-data - ResultSet table; - - table = dbmdata.getTables(dbname, null, "study", null); - if (table.next()) return; - uplevel = -1; // Database not initialized - } - } - - protected final static Logger logger = org.splat.kernel.Database.logger; - -// ============================================================================================================================== -// Construction -// ============================================================================================================================== - - public static Database getMe () { -// ------------------------------- - if (my == null) try { - my = new Database(); - } - catch (Exception error) { - logger.fatal("Could not access the database, reason:", error); - } - return my; - } - private Database () { -// ------------------- - Database.getSession().doWork(new CheckVersion()); - this.setIDPoolSize(4); // Average number of generated IDs when creating a study and versioning a document - } - -// ============================================================================================================================== -// Public member functions -// ============================================================================================================================== - - public boolean isInitialized () { -// ------------------------------- - return (uplevel >= 0); - } - - public void initialize () throws IOException, SQLException { -// ------------------------- - logger.info("Creation of the database."); - -// Creation of the Lucene index - Index.create(); // May throw IOException if the index repository is improperly configured - -// Creation of the SIMER SQL tables - Session session = Database.getSession(); - session.doWork(new CreateTables()); // May throw SQLException if the SIMER database does not exist - session.flush(); - -// Population of the database with customized data - this.populate(); - - session.flush(); - uplevel = 0; // The database is now up-to-date - } - -// ============================================================================================================================== -// Protected member functions -// ============================================================================================================================== - - protected void configure (Properties reprop) { -// -------------------------------------------- - basepath = reprop.getProperty("repository"); - } - - protected void populate () { -// -------------------------- - try { -// Initialization of the schema version - this.setSchemaVersion("D0.3"); //TODO: Get the version name from the configuration file - -// Creation of the default system administrator -//TODO: Get the username password from the Hibernate configuration - User.Properties uprop = new User.Properties(); - uprop.setUsername("simer") - .setPassword("admin") - .setName("Simulation") - .setFirstName("Manager") - .setDisplayName("label.sysadmin") - .addRole("sysadmin") - .setMailAddress("noreply@salome-platform.org"); - uprop.disableCheck(); - UserDirectory.createUser(uprop); - } - catch (Exception e) { -// Let's continue, hoping the best... - } - ProjectSettings.getMe().initialize(); // Populates the database with all necessary stuff - } - -// ============================================================================================================================== -// Public services -// ============================================================================================================================== - - public static Study createStudy (Study.Properties sprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException, RuntimeException { -// -------------------------------------------------------- - Study study = new Study(sprop); - - study.buildReference(); - Database.getSession().save(study); - try { - Index lucin = getIndex(); - lucin.add(study); - } - catch (IOException error) { - logger.error("Unable to index the study '" + study.getIndex() + "', reason:", error); -// Continue and try to index later - } - return study; - } - - public static void indexStudy (Study study) { -// ------------------------------------------- - try { - Study.Properties sprop = new Study.Properties(); - List index = Database.selectStudiesWhere(sprop.setReference(study.getReference())); - - if (index.size() != 0) return; // The given study is already indexed - - Index lucin = getIndex(); - Scenario[] scenes = study.getScenarii(); - - lucin.add(study); - if (study.getProgressState() != ProgressState.inWORK) for (int i=0; i list = scenes[i].getAllKnowledgeElements(); - for (Iterator j=list.iterator(); j.hasNext(); ) { - lucin.add(j.next()); - } - } - } - catch (Exception error) { - logger.error("Unable to index the study '" + study.getIndex() + "', reason:", error); - } - } - - public static Index getIndex () throws IOException { -// ------------------------------- - Index lucin = new Index(); - if ( !lucin.exists() ) Index.create(); // Happens when re-indexing all studies - return lucin; - } - - public static File getDownloadDirectory (User user) { -// --------------------------------------------------- - StringBuffer path = new StringBuffer(my.basepath).append("downloads/").append(user.getUsername()).append("/"); - return new File(path.toString()); - } - - public static File getRepositoryIndexDirectory () { -// ------------------------------------------------- - return new File(my.basepath + "lucin/"); - } - - public static String getRepositoryVaultPath () { -// -------------------------------------------- - return (my.basepath + "vault/"); - } - - public static String getTemplatePath () { -// --------------------------------------- - return (my.basepath + "templates/"); - } - - public static Document selectDocument (int index) { -// ------------------------------------------------- - StringBuffer query = new StringBuffer("from Document where rid='").append(index).append("'"); - return (Document)Database.getSession().createQuery(query.toString()).uniqueResult(); - } - - public static Document selectDocument (String refid, String version) { -// -------------------------------------------------------------------- - StringBuffer query = new StringBuffer("from Document where did='").append(refid).append("' and version='").append(version).append("'"); - return (Document)Database.getSession().createQuery(query.toString()).uniqueResult(); - } - - public static KnowledgeElement selectKnowledgeElement (int index) { -// ----------------------------------------------------------------- - StringBuffer query = new StringBuffer("from KnowledgeElement where rid='").append(index).append("'"); - KnowledgeElement result = (KnowledgeElement)Database.getSession().createQuery(query.toString()).uniqueResult(); - - result.getOwnerScenario().getOwnerStudy().loadWorkflow(); - return result; - } - - public static List selectKnowledgeElementsWhere (KnowledgeElement.Properties... kprop) { -// --------------------------------------------------------------------------------------------- - List result = new ArrayList(); - int hitsize = 20; - try { - -// Creation of the Lucene query - File indir = Database.getRepositoryIndexDirectory(); - Directory index = FSDirectory.open(indir); - IndexSearcher searcher = new IndexSearcher(index, true); - BooleanQuery fulquery = new BooleanQuery(); - - for (int i=0; i context = kprop[i].getSimulationContexts(); - if (context != null && context.size() > 0) { - BooleanQuery critext = new BooleanQuery(); - for (Iterator j=context.iterator(); j.hasNext();) { - SimulationContext seltext = j.next(); - input = new Term(String.valueOf(seltext.getType().getIndex())); - critext.add(new TermQuery(input.createTerm(seltext.getValue())), BooleanClause.Occur.MUST); - } - query.add(critext, BooleanClause.Occur.MUST); - } - fulquery.add(query, BooleanClause.Occur.SHOULD); - } - if (logger.isInfoEnabled()) { - logger.info("Searching knowledges by Lucene query \"" + fulquery.toString() + "\"."); - } -// Creation of the knowledge filter - BooleanFilter filter = new BooleanFilter(); - TermsFilter select = new TermsFilter(); - Term mytype = new Term("class"); - select.addTerm( mytype.createTerm("KnowledgeElement") ); - filter.add(new FilterClause(select, BooleanClause.Occur.SHOULD)); - -// Creation of the sort criteria - Sort sort = new Sort(new SortField("title", SortField.STRING)); - -// Search - TopFieldDocs found = searcher.search(fulquery, filter, hitsize, sort); - - if (found.totalHits < 1) return result; // No study found - -// Construction of the result list - ScoreDoc[] hits = found.scoreDocs; - for (int i=0; i clist = selectSimulationContextsWhere(cprop.setType(celt).setValue(value)); - if (!clist.isEmpty()) result = clist.get(0); // Supposed being the most used one if many exist - } - catch (InvalidPropertyException error) { - logger.info("Attempt to select a simulation context \"" + celt.getName() + "\" with an invalid value."); - } - return result; - } - - @SuppressWarnings("unchecked") - public static List selectSimulationContextsWhere (SimulationContext.Properties cprop) { -// -------------------------------------------------------------------------------------------------------- - StringBuffer query = new StringBuffer("from SimulationContext"); - String separator = " where"; - SimulationContextType celt = cprop.getType(); - String value = cprop.getValue(); - ProgressState state = cprop.getProgressState(); - String order = ""; - - if (celt != null) { query = query.append(separator).append(" type='").append(celt.getIndex()).append("'"); - separator = " and"; - order = " order by value asc"; - } - if (value != null ) { query = query.append(separator).append(" value='").append(value).append("'"); - separator = " and"; - } - if (state != null ) { query = query.append(separator).append(" state='").append(state).append("'"); - if (celt == null) order = " order by type asc"; - } - query.append(order); - return (List)Database.getSession().createQuery(query.toString()).list(); - } - - public static Study selectStudy (int index) { -// ------------------------------------------- - StringBuffer query = new StringBuffer("from Study where rid='").append(index).append("'"); - Study result = (Study)Database.getSession().createQuery(query.toString()).uniqueResult(); - - result.loadWorkflow(); - return result; - } - - public static Study selectStudy (String refid) { -// ---------------------------------------------- - StringBuffer query = new StringBuffer("from Study where sid='").append(refid).append("'"); - Study result = (Study)Database.getSession().createQuery(query.toString()).uniqueResult(); - - result.loadWorkflow(); - return result; - } - - public static List selectStudiesWhere (Study.Properties... sprop) { -// ------------------------------------------------------------------------ - List result = new ArrayList(); - int hitsize = 20; - try { - -// Creation of the Lucene query - File indir = Database.getRepositoryIndexDirectory(); - Directory index = FSDirectory.open(indir); - IndexSearcher searcher = new IndexSearcher(index, true); - BooleanQuery fulquery = new BooleanQuery(); - - for (int i=0; i context = sprop[i].getSimulationContexts(); - if (context != null && context.size() > 0) { - BooleanQuery critext = new BooleanQuery(); - for (Iterator j=context.iterator(); j.hasNext();) { - SimulationContext seltext = j.next(); - input = new Term(String.valueOf(seltext.getType().getIndex())); - critext.add(new TermQuery(input.createTerm(seltext.getValue())), BooleanClause.Occur.MUST); - } - query.add(critext, BooleanClause.Occur.MUST); - } - fulquery.add(query, BooleanClause.Occur.SHOULD); - } - if (logger.isInfoEnabled()) { - logger.info("Searching studies by Lucene query \"" + fulquery.toString() + "\"."); - } -// Creation of the studies filter - BooleanFilter filter = new BooleanFilter(); - TermsFilter select = new TermsFilter(); - Term mytype = new Term("class"); - select.addTerm( mytype.createTerm("Study") ); - filter.add(new FilterClause(select, BooleanClause.Occur.SHOULD)); - -// Creation of the sort criteria - Sort sort = new Sort(new SortField("title", SortField.STRING)); - -// Search - TopFieldDocs found = searcher.search(fulquery, filter, hitsize, sort); - - if (found.totalHits < 1) return result; // No study found - -// Construction of the result list - ScoreDoc[] hits = found.scoreDocs; - for (int i=0; i exports = getRelations(ConvertsRelation.class); - - for (Iterator i=exports.iterator(); i.hasNext(); ) { - File export = (File)i.next().getTo(); - if (export.getFormat().equals(format)) return export; - } - return null; - } - - public User getAuthor () { -// ------------------------ - return author; - } - - public Date getCreationDate () { -// ------------------------------ - return myfile.getDate(); - } - - public Date getLastModificationDate () { -// -------------------------------------- - return lasdate; - } - - public String getFormat () { -// -------------------------- - return myfile.getFormat(); - } - - public Document getPreviousVersion () { -// ------------------------------------- - Relation previous = getFirstRelation(VersionsRelation.class); - if (previous != null) return (Document)previous.getTo(); - else return null; - } - - public ProgressState getProgressState () { -// ---------------------------------------- - return state; - } - -/** - * Returns the path where all physical files attached to this document are saved. - * This path is relative to the vault of the repository and include the file name, without extension, common - * to all physical files attached to this document. - * - * @return the path of the document - */ - public String getRelativePath () { -// -------------------------------- - String[] table = myfile.getRelativePath().split("\\x2E"); - StringBuffer path = new StringBuffer(table[0]); - for (int i=1; i stamps = new Vector(); - - for (Iterator i=this.getAllRelations().iterator(); i.hasNext(); ) { - Relation link = i.next(); - if (link instanceof StampRelation) stamps.add( ((StampRelation)link).getTo() ); - } - Timestamp[] result = stamps.toArray( new Timestamp[stamps.size()] ); - ComparatorByDate bydate = new Timestamp.ComparatorByDate(); - - Arrays.sort(result, bydate); - return result; - } - -/** - * Returns the title of this document. - * - * @return the document title, or an empty string is this document is undefined. - * @see #isUndefined() - */ - public String getTitle () { -// ------------------------- - if (this.isUndefined()) return ""; - else return name; - } - - public DocumentType getType () { -// ------------------------------ - return type; - } - -/** - * Returns the version number of this document. - * The version number, when exists, is either of the internal form (m.n.s) usable for building a Revision object, or any string - * in case of external document (document with EXTERN state).
- *
- * Note: document slots have a version number equal to "0.0.0". - * - * @return the version number of this document, or null if this is EXTERN. - * @see #isUndefined() - */ - public String getVersion () { -// --------------------------- - return version; - } - -/** - * Returns true if this document is undefined. - * An undefined document is a meta-document created for reserving the persistent reference of a new document before saving - * (or importing) this later into the repository. - * The working copy of a such document may include this reference. - * - * @see #getTitle() - * @see #getVersion() - * @see #initialize(Properties) - */ - public boolean isUndefined () { -// ----------------------------- - return (history == -1); - } - - public boolean isInto (Step container) { -// -------------------------------------- - return (step == container.getNumber()); - } - - public boolean isPublished () { -// ----------------------------- - return (countag > 0); - } - - public boolean isShared () { -// -------------------------- - return (countag + history > 1); - } - - public boolean isVersioned () { -// ----------------------------- - return (history > 0); - } - -// ============================================================================================================================== -// Public services -// ============================================================================================================================== - - public static DocumentType createType (DocumentType.Properties tprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException, RuntimeException { -// --------------------------------------------------------------------- -//TODO: Check for duplicate definition - DocumentType type = new DocumentType(tprop); - Session session = Database.getSession(); - session.save(type); - - return type; - } - - public static Properties extractProperties (java.io.File file) { -// -------------------------------------------------------------- - Properties fprop = new Properties(); - Reader tool = Toolbox.getReader(file); - String value; - if (tool != null) try { - value = tool.extractProperty("title"); - if (value != null) fprop.setName(value); - - value = tool.extractProperty("reference"); - if (value != null) fprop.setReference(value); - } - catch (Exception e) { - } - return fprop; - } - - @SuppressWarnings("unchecked") - public static List selectAllTypes () { -// -------------------------------------------------- - String query = "from DocumentType"; - - List types = Database.getSession().createQuery(query).list(); - for (Iterator i=types.iterator(); i.hasNext();) { - Hibernate.initialize(i.next()); // Supposed fetching document types - } - return types; - } - - @SuppressWarnings("unchecked") - public static List selectResultTypes () { -// ----------------------------------------------------- - String query = "from DocumentType where result is not null order by result asc"; - - return Database.getSession().createQuery(query).list(); - } - - public static DocumentType selectType (String name) { -// --------------------------------------------------- - String query = new StringBuffer("from DocumentType where name='").append(name).append("'").toString(); - - return (DocumentType)Database.getSession().createQuery(query).uniqueResult(); - } - - public static DocumentType selectType (int index) { -// ------------------------------------------------- - String query = new StringBuffer("from DocumentType where rid='").append(index).append("'").toString(); - - return (DocumentType)Database.getSession().createQuery(query).uniqueResult(); - } - - @SuppressWarnings("unchecked") - public static List selectTypesOf (ProjectSettings.Step step) { -// -------------------------------------------------------------------------- - Integer number = step.getNumber(); - String query = new StringBuffer("from DocumentType").append(" where step like '%-").append(number).append("-%'").toString(); - - List types = Database.getSession().createQuery(query).list(); - for (Iterator i=types.iterator(); i.hasNext();) { - Hibernate.initialize(i.next()); // For fetching document types - } - return types; - } - -// ============================================================================================================================== -// Protected services -// ============================================================================================================================== - - protected ConvertsRelation attach (String format) { -// ------------------------------------------------- - return attach(format, null); - } - - protected ConvertsRelation attach (String format, String description) { -// --------------------------------------------------------------------- - String path = this.getRelativePath(); - File export = new File(path + "." + format); - ConvertsRelation attach = new ConvertsRelation(this, export, description); - Session session = Database.getSession(); - - session.save(export); - session.save(attach); - - this.addRelation(attach); // Updates this - - return attach; - } - - protected boolean buildReferenceFrom (ProjectElement scope, Document lineage) { -// ----------------------------------------------------------------------------- - if (state != ProgressState.inWORK) return false; - Study owner = null; - Scenario context = null; - if (scope instanceof Study) owner = (Study)scope; - else { - context = ((Scenario)scope); - owner = context.getOwnerStudy(); - } - did = lineage.did; - if (context != null && (lineage.isVersioned() || owner.shares(lineage))) { - version = new Revision(version).setBranch(context.getReference()).toString(); - } - return true; - } - - protected boolean buildReferenceFrom (Study scope) { -// -------------------------------------------------- - if (state != ProgressState.inWORK && state != ProgressState.EXTERN) return false; - DecimalFormat tostring = new DecimalFormat(suformat); - - did = did.replace ("%" + suformat, tostring.format(scope.getLastLocalIndex())); - return true; - } - - protected boolean demote () { -// --------------------------- - ValidationStep torem; - - if (state == ProgressState.inCHECK) { - state = ProgressState.inDRAFT; - torem = ValidationStep.REVIEW; -// This operation must not change the version number of documents. -// Consequently, inDRAFT documents may have a minor version number equal to zero. - } else - if (state == ProgressState.inDRAFT) { - state = ProgressState.inWORK; - torem = ValidationStep.PROMOTION; - } else { - return false; - } - for (Iterator i=this.getAllRelations().iterator(); i.hasNext(); ) { - Relation link = i.next(); - if (!(link instanceof StampRelation)) continue; - if (((StampRelation)link).getStampType() != torem) continue; - i.remove(); - break; - } - Database.getSession().update(this); - return true; - } - -/** - * Increments the reference count of this document following its publication into a Study step. - * - * @see #release() - */ - protected void hold () { -// ---------------------- - countag += 1; - if (this.isSaved()) Database.getSession().update(this); - } - -/** - * Defines this document. - * - * @param dprop the properties of the document - * - * @see Step#createDocument(Properties) - * @see #isUndefined() - */ - protected void initialize (Properties dprop) throws MissedPropertyException, InvalidPropertyException, NotApplicableException { -// -------------------------------------------- - if (!this.isUndefined()) throw new NotApplicableException("Cannot initialize an existing Document"); - if (dprop.name == null) throw new MissedPropertyException("name"); - if (dprop.name.length() == 0) throw new InvalidPropertyException("name"); - if (dprop.owner == null) throw new MissedPropertyException("owner"); -// if (dprop.owner instanceof Study && !ProjectSettings.getStep(step).appliesTo(Study.class)) { -// throw new InvalidPropertyException("step"); -// } - name = dprop.name; - myfile.changePath( myfile.getRelativePath().replace("%n", getEncodedRootName((Study)dprop.owner)) ); - if (history == -1) history = 0; - if (dprop.date == null) { - Calendar current = Calendar.getInstance(); - lasdate = current.getTime(); // Today - } else { - lasdate = dprop.date; - } - Database.getSession().update(this); - } - - protected boolean promote (Timestamp stamp) { -// ------------------------------------------- - ProgressState newstate = null; - - if (state == ProgressState.inWORK) { - newstate = ProgressState.inDRAFT; // Promotion to being reviewed - } else - if (state == ProgressState.inDRAFT) { - newstate = ProgressState.inCHECK; // Promotion to approval - Revision myvers = new Revision(version); - if (myvers.isMinor()) { - version = myvers.incrementAs(newstate).toString(); -//TODO: If my physical file is programatically editable, update its (property) version number -//ISSUE: What about attached files such as PDF if exist, should we remove them ? - } - } else - if (state == ProgressState.inCHECK) { - newstate = ProgressState.APPROVED; - } - this.state = newstate; - if (stamp != null) this.addRelation( stamp.getContext() ); - Database.getSession().update(this); - return true; - } - -/** - * Decrements the reference count of this document following the removal of a Publication from a Study step. - * - * @see #hold() - */ - protected void release () { -// ------------------------- - countag -= 1; - if (this.isSaved()) Database.getSession().update(this); - } - - protected void rename (String title) throws InvalidPropertyException { -// ------------------------------------ - if (title.length() == 0) throw new InvalidPropertyException("name"); - - Calendar current = Calendar.getInstance(); - this.name = title; - this.lasdate = current.getTime(); // Today - Database.getSession().update(this); - } - - protected void updateAs (Revision newvers) { -// ------------------------------------------ - version = newvers.setBranch(version).toString(); // Branch names are propagated by the versionning - ProgressState newstate = ProgressState.inCHECK; - if (newvers.isMinor()) newstate = ProgressState.inWORK; - state = null; // Just to tell updateAs(sate) to not increment the version number - updateAs(newstate); - } - - protected void updateAs (ProgressState state) { -// --------------------------------------------- - Document previous = null; - -// Set of version number - if (state == ProgressState.EXTERN) { - if (this.state != ProgressState.EXTERN) this.version = null; // Strange use-case... - } else { - Revision myvers = new Revision(version); - if (!myvers.isNull()) { // Versionning context - for (Iterator i=getAllRelations().iterator(); i.hasNext();) { - Relation link = i.next(); - if (!link.getClass().equals(VersionsRelation.class)) continue; - previous = (Document)link.getTo(); // Versioned document - break; - } - } - if (this.state != null) myvers.incrementAs(state); // Incrementation if the reversion number is not imposed - this.version = myvers.toString(); - } -// Update this document and the previous version, if exit - Session session = Database.getSession(); - if (previous != null) { - previous.history += 1; - session.update(previous); - } - this.state = state; - session.update(this); - } - -// protected void upgrade () { -// ------------------------- -// if (this.state != ProgressState.inWORK) return; -// -// Calendar current = Calendar.getInstance(); -// for (Iterator i=getAllRelations().iterator(); i.hasNext();) { -// Relation link = i.next(); -// if (!link.getClass().equals(UsesRelation.class)) continue; -// -// Document used = (Document)link.getTo(); -// if (!used.isVersioned()) continue; -//TODO: Update the uses relation -// } -// this.promote(); -// this.lasdate = current.getTime(); // Today -// Database.getSession().update(this); -// -//TODO: Promote documents using this one -// } - -// ============================================================================================================================== -// Private services -// ============================================================================================================================== - - private String generateEncodedName (Study scope) { -// ------------------------------------------------ - StringBuffer encoding = new StringBuffer(); - FileNaming scheme = ProjectSettings.getFileNamingScheme(); - DecimalFormat tostring = new DecimalFormat(suformat); - - int number = scope.generateLocalIndex(); - - if (scheme == FileNaming.encoded) { - encoding.append(scope.getReference()).append(".").append(tostring.format(number)); - } else { // title and (temporarily) asis - encoding.append(name).append(".").append(tostring.format(number)); - } - return encoding.toString(); - } - - private String getEncodedRootName (Study scope) { -// ----------------------------------------------- - FileNaming scheme = ProjectSettings.getFileNamingScheme(); - - if (scheme == FileNaming.encoded) return scope.getReference(); - else return name; - } -} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/DocumentRights.java b/Workspace/Siman-Common/src/org/splat/som/DocumentRights.java index 4eb9845..6934941 100644 --- a/Workspace/Siman-Common/src/org/splat/som/DocumentRights.java +++ b/Workspace/Siman-Common/src/org/splat/som/DocumentRights.java @@ -14,8 +14,16 @@ package org.splat.som; import java.util.Iterator; import java.util.List; -import org.splat.kernel.Relation; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.Relation; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.UsedByRelation; +import org.splat.dal.bo.som.UsesRelation; +import org.splat.dal.bo.som.ValidationCycle; +import org.splat.dal.bo.som.ValidationStep; +import org.splat.dal.bo.som.VersionsRelation; public class DocumentRights { diff --git a/Workspace/Siman-Common/src/org/splat/som/Publication.java b/Workspace/Siman-Common/src/org/splat/som/Publication.java deleted file mode 100644 index d7969ee..0000000 --- a/Workspace/Siman-Common/src/org/splat/som/Publication.java +++ /dev/null @@ -1,506 +0,0 @@ -package org.splat.som; -/** - * Publication objects are the way to reference document versions from a Project Element. - * As such, a Document version is added (or published) to a Project Element through a Publication object. - * This publication is done by saving the Publication object produced when creating and versioning a Document from a given - * Project Element Step (call of the saveAs() function).
- *
- * A Publication object is homogeneous to a reference to a Document version and belongs to one Project Element, this latter - * being either a Study Scenario or a Study itself, depending on the Study Step to which the document is published.
- *
- * The document version referenced by a Publication object is the Value of the publication. - * - * @see Document - * @see ProjectElement - * @see Step - * @author Daniel Brunier-Coulin - * @copyright OPEN CASCADE 2012 - */ - -import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.List; - -import org.hibernate.Session; -import org.splat.kernel.InvalidPropertyException; -import org.splat.kernel.NotApplicableException; -import org.splat.kernel.Persistent; -import org.splat.kernel.Relation; -import org.splat.kernel.User; -import org.splat.manox.Reader; -import org.splat.manox.Toolbox; - - -public class Publication extends Persistent { - -// Persistent fields - private Document mydoc; - private ProjectElement owner; // Either Study or Scenario, depending on the step involved by the publication - private char isnew; // True if this references a document version new for the owner project element - -// Transient fields - private Step mystep; - -// ============================================================================================================================== -// Construction -// ============================================================================================================================== - -// Database fetch constructor - protected Publication () { -// ------------------------ - mystep = null; - } -// Internal constructors - protected Publication (Document doc, ProjectElement publisher) { -// -------------------------------------------------------------- - mydoc = doc; - mystep = null; - owner = publisher; - isnew = 'Y'; - } - protected Publication copy (ProjectElement publisher) { -// ----------------------------------------------------- - Publication copy = new Publication(); - copy.mydoc = this.mydoc; - copy.mystep = this.mystep; // May not be initialized yet - copy.owner = publisher; - copy.isnew = this.isnew; - if (!copy.getOwnerStudy().equals(this.getOwnerStudy())) { - copy.isnew = 'N'; // The referenced document is not new for the given study - } - return copy; - } - -// ============================================================================================================================== -// Member functions -// ============================================================================================================================== - - public Relation addDependency (Publication to) { -// ---------------------------------------------- - return this.addDependency(to.value()); - } - - public Relation addDependency (Document to) { -// ------------------------------------------- - if (to == null) return null; - else return mydoc.addRelation( new UsesRelation(mydoc, to) ); - } - -/** - * Undo the out-date operation. - * - * @return true if the acceptance succeeds - * @see #outdate() - * @see DocumentRights#canAccept() - */ - public boolean actualize () { -// --------------------------- - if (!this.isOutdated()) return false; - isnew = 'Y'; - Database.getSession().update(this); - return true; - } - -/** - * Promotes the document referenced by this publication from In-Check to Approved state, if not out-dated, and attaches the corresponding - * time-stamp to the document.
- * If the promoted document is the final result of the owner study, the study is itself is promoted as well.
- *
- * Limitation: the way this promotion is propagated to the study supposes that the study has only ONE final result document. - * - * @param adate the date of approval - * @return true if the approval succeeded - * @see #getProgressState() - * @see DocumentRights#canApprove() - * @see DocumentType#isStudyResult() - * @see Study#getApproverOf(Publication) - */ - public Timestamp approve (Date adate) { -// ------------------------------------- - if (this.isOutdated()) return null; - else if (mydoc.getProgressState() != ProgressState.inCHECK) return null; // This statement must conform to the corresponding right - - DocumentType type = mydoc.getType(); - Study owner = this.getOwnerStudy(); - ValidationCycle cycle = owner.getValidationCycleOf(type); - User approver = cycle.getActor(ValidationStep.APPROVAL); - Timestamp stamp = new Timestamp(ValidationStep.APPROVAL, mydoc, approver, adate); - if (!mydoc.promote(stamp)) return null; - if (type.isStudyResult() && owner.getProgressState() == ProgressState.inCHECK) owner.promote(); - return stamp; // Hoping that promotion of the study succeeded - } - - public ConvertsRelation attach (String format) { -// ---------------------------------------------- - return mydoc.attach(format); - } - - public ConvertsRelation attach (String format, String description) { -// ------------------------------------------------------------------ - return mydoc.attach(format, description); - } - -/** - * Demotes the document referenced by this publication to In-Work state, and removes the Promoter of the document, if exist.
- * The In-Draft state is skipped (direct demotion to In-Work) if the validation cycle of the document does not include the review step.
- * If the demoted document is the final result of the owner study, the study is itself is demoted as well.
- *
- * Limitation: the way this demotion is propagated to the study supposes that the study has only ONE final result document. - * - * @return true if the demotion succeeded - * @see #getProgressState() - * @see DocumentRights#canDemote() - * @see DocumentType#isStudyResult() - */ - public boolean demote () { -// ------------------------ - DocumentType type = mydoc.getType(); - Study owner = this.getOwnerStudy(); - - if (mydoc.getProgressState() == ProgressState.inCHECK) { - ValidationCycle cycle = owner.getValidationCycleOf(type); - if (cycle.enables(ValidationStep.REVIEW)) { - if (!mydoc.demote()) return false; - } else { - if (!mydoc.demote()) return false; - mydoc.demote(); - } - } else - if (mydoc.getProgressState() == ProgressState.inDRAFT) { - if (!mydoc.demote()) return false; - } else { - return false; - } - if (type.isStudyResult() && owner.getProgressState() != ProgressState.inWORK) owner.demote(); - return true; - } - -/** - * Returns the study Step into which the document version referenced by this publication has been published. - */ - public Step getInvolvedStep () { -// ------------------------------ - if (mystep == null) { - Step[] step = owner.getSteps(); - for (int i=0; i - * If this publication belongs to a Study, the Project Element returned is the Study returned by getOwnerStudy(). - * - * @return the Study Scenario or the Study to which this publication belongs to - * @see #getOwnerStudy() - */ - public ProjectElement getOwner () { -// --------------------------------- - return owner; - } - - public Study getOwnerStudy () { -// ----------------------------- - if (owner instanceof Study) return (Study)owner; - else return ((Scenario)owner).getOwnerStudy(); - } - -/** - * Returns the state of this published document. - * It is the same than the state of the referenced document, unless this publication is out-of-date, in which case it is - * In-Work state. - * - * @see #outdate() - * @see #isOutdated() - */ - public ProgressState getProgressState () { -// ---------------------------------------- - if (this.isOutdated()) return ProgressState.inWORK; // Overrides the document state - else return mydoc.getProgressState(); - } - - public List getRelations (Class type) { -// ---------------------------------------------------------------------- - if (type == null) return null; - - List result = new ArrayList(); - List relist = mydoc.getRelations(type); - for (Iterator i=relist.iterator(); i.hasNext();) { - Relation relation = i.next(); - Document relatedoc = (Document)relation.getTo(); - Publication related = owner.getPublication(relatedoc); - if (related != null) { - result.add(related); - } else - if (owner instanceof Scenario) { // The relation may cross steps belonging to a scenario and its owner study - related = ((Scenario)owner).getOwnerStudy().getPublication(relatedoc); - if (related != null) result.add(related); - } - } - return result; - } - - public File getSourceFile () { -// ---------------------------- - return mydoc.getSourceFile(); - } - -/** - * Undo the review operation by demoting the document referenced by this publication from In-Check to In-Draft state and - * removing the Reviewer.
- * If the demoted document is the final result of the owner study, the study is itself is demoted as well.
- *
- * Limitation: the way this demotion is propagated to the study supposes that the study has only ONE final result document. - * - * @return true if the demotion succeeded - * @see #getProgressState() - * @see #review() - * @see DocumentRights#canInvalidate() - * @see DocumentType#isStudyResult() - */ - public boolean invalidate () { -// ---------------------------- - if ( mydoc.getProgressState() != ProgressState.inCHECK ) return false; - if (!mydoc.demote()) // Removes the reviewer if this document is In-Check - return false; - DocumentType type = this.value().getType(); - Study owner = this.getOwnerStudy(); - if (type.isStudyResult() && owner.getProgressState() == ProgressState.inCHECK) owner.demote(); - return true; - } - - public boolean isNewForOwner () { -// ------------------------------- - return (isnew == 'Y'); - } - - public boolean isOutdated () { -// ---------------------------- - return (isnew == 'O'); - } - -/** - * Promotes the document referenced by this publication from In-Work to In-Draft or In-Check state, if not out-dated, and attaches the corresponding - * time-stamp to the document.
- * The In-Draft state is skipped (direct promotion to In-Check) if the validation cycle of the document does not include the review step.
- * Also, if the promoted document is the final result of the owner study, the study is itself promoted as well.
- * This operation can be undo-ed by demote().
- *
- * Limitation: the way this promotion is propagated to the study supposes that the study has only ONE final result document. - * - * @return true if the promotion succeeded - * @see #getProgressState() - * @see #demote() - * @see DocumentRights#canPromote() - * @see DocumentType#isStudyResult() - */ - public Timestamp promote (Date pdate) { -// ------------------------------------- - if (this.isOutdated()) return null; - else if (mydoc.getProgressState() != ProgressState.inWORK) return null; // This statement must conform to the corresponding right - else { - DocumentType type = mydoc.getType(); - Study owner = this.getOwnerStudy(); - ValidationCycle cycle = owner.getValidationCycleOf(type); - User promoter = cycle.getActor(ValidationStep.PROMOTION); - if (promoter == null) promoter = getInvolvedStep().getActor(); - if (promoter == null) promoter = owner.getAuthor(); - Timestamp stamp = new Timestamp(ValidationStep.PROMOTION, mydoc, promoter, pdate); - - if (!mydoc.promote(stamp) ) // Promotion to being reviewed - return null; - if (!cycle.enables(ValidationStep.REVIEW)) { - mydoc.promote(null); - } - if (type.isStudyResult() && owner.getProgressState() == ProgressState.inWORK) owner.promote(); - return stamp; // Hoping that promotion of the study succeeded - } - } - - public void rename (String title) throws InvalidPropertyException { -// --------------------------------- - mydoc.rename(title); - } - -/** - * Promotes the document referenced by this publication from In-Draft to In-Check state, if not out-dated, and attaches the corresponding - * time-stamp to the document.
- * If the promoted document is the final result of the owner study, the study is itself is promoted as well.
- * This operation can be undo-ed by invalidate().
- *
- * Limitation: the way this promotion is propagated to the study supposes that the study has only ONE final result document. - * - * @param rdate the date of review - * @return true if the review succeeded - * @see #getProgressState() - * @see #invalidate() - * @see DocumentRights#canReview() - * @see DocumentType#isStudyResult() - * @see Study#getReviewerOf(Publication) - */ - public Timestamp review (Date rdate) { -// ------------------------------------ - if (this.isOutdated()) return null; - else if (mydoc.getProgressState() != ProgressState.inDRAFT) return null; // This statement must conform to the corresponding right - - DocumentType type = mydoc.getType(); - Study owner = this.getOwnerStudy(); - ValidationCycle cycle = owner.getValidationCycleOf(type); - User reviewer = cycle.getActor(ValidationStep.REVIEW); - Timestamp stamp = new Timestamp(ValidationStep.REVIEW, mydoc, reviewer, rdate); - if (!mydoc.promote(stamp)) return null; - if (type.isStudyResult() && owner.getProgressState() == ProgressState.inDRAFT) owner.promote(); - return stamp; // Hoping that promotion of the study succeeded - } - -/** - * Publishes the document referenced by this publication into the owner Project Element under the given revision number.
- * The state of the referenced document is supposed being automatically set according to the given revision number, but, due to the - * versioning scheme, as it is not possible to differentiate In-Work and In-Draft states, this function has been deprecated (it is - * currently used only for the need of integration of Microsoft Office which anyway has to be redesigned). - *
- * Note: in the context of branch versioning, the given revision may be modified by an update of the branch name. - * - * @param newvers the required revision number - * @throws FileNotFoundException If the referenced document is empty - * @throws NotApplicableException If the referenced document is undefined - * @deprecated - */ - public void saveAs (Revision newvers) throws FileNotFoundException, NotApplicableException { -// ------------------------------------- - if ( mydoc.isUndefined() ) throw new NotApplicableException("Cannot save a Publication object refering an undefined Document"); - if (!mydoc.getSourceFile().exists()) throw new FileNotFoundException(); - - Database.getSession().save(this); // Must be done before updating the study in order to fix this final (rid-based) hascode - mydoc.updateAs(newvers); // May change the branch name of given revision - updateOwner(); - } - -/** - * Publishes the document referenced by this publication into the owner Project Element under the given state, - * the revision number of the document being automatically set accordingly. - * If the given state is In-Draft and the document is final result of the owner study, this automatically promotes the study to In-Draft. - * - * @param state the required progress state - * @throws FileNotFoundException If the referenced document is empty - * @throws NotApplicableException If the referenced document is undefined - */ - public void saveAs (ProgressState state) throws FileNotFoundException, NotApplicableException { -// ---------------------------------------- - if ( mydoc.isUndefined() ) throw new NotApplicableException("Cannot save a Publication object refering an undefined Document"); - if (!mydoc.getSourceFile().exists()) throw new FileNotFoundException(); - - if (state == ProgressState.inWORK || state == ProgressState.EXTERN) { - Database.getSession().save(this); // Must be done before updating the study in order to fix this final (rid-based) hascode - mydoc.updateAs(state); - } else { - DocumentType mytype = mydoc.getType(); - Study owner = this.getOwnerStudy(); - ValidationCycle cycle = owner.getValidationCycleOf(mytype); - boolean review = cycle.enables(ValidationStep.REVIEW); - if (!(state == ProgressState.inDRAFT && review) && !(state == ProgressState.inCHECK && !review)) { - throw new NotApplicableException("Cannot save a result document in " + state.toString() + " state"); - } - Database.getSession().save(this); // Must be done before updating the study in order to fix this final (rid-based) hascode - mydoc.updateAs(ProgressState.inWORK); - - this.promote(mydoc.getLastModificationDate()); // Promotes to the appropriate state in accordance to the validation cycle - } - updateOwner(); - } - -/** - * Out-dates this publication and recursively all publications using this one. - * Typically, a publication is out-dated when modifying a document to which it depends. - * - * @see #isOutdated() - * @see #getProgressState() - * @see #actualize() - */ - public void outdate () { -// ---------------------- - if (this.isOutdated()) return; - - List relist = this.getRelations(UsedByRelation.class); - for (Iterator i = relist.iterator(); i.hasNext(); ) { - i.next().outdate(); - } - isnew = 'O'; - Database.getSession().update(this); - } - -/** - * Returns the document version referenced by this Publication. - */ - public Document value () { -// ------------------------ - return mydoc; - } - -// ============================================================================================================================== -// Private services -// ============================================================================================================================== - - private void updateOwner () { -// --------------------------- - Session session = Database.getSession(); - Step step = this.getInvolvedStep(); - -// Update of involved step - Document previous = mydoc.getPreviousVersion(); - if (previous != null) { - Publication oldoc = step.getDocument(previous.getIndex()); - boolean done = step.remove(oldoc); // Decrements the configuration tag count of document - if (done) session.delete(oldoc); //WARNING: Potential problem because it's not automatically done as orphan object - } - step.add(this); // Increments the configuration tag count of document - -// Import the document properties and update of the study - forwardProperties(mydoc.getSourceFile().asFile(), step); - session.update(getOwner()); - } - - private void forwardProperties (java.io.File from, Step to) { -// ----------------------------------------------------------- - Reader tool = Toolbox.getReader(from); - if (tool == null) return; // No properties extractor available for this type of document - - SimulationContextType.Properties sprop = new SimulationContextType.Properties() - .setStep(to.getStep()) - .setState(ProgressState.APPROVED); - List contype = SimulationContext.selectTypesWhere(sprop); - if (contype.isEmpty()) return; // No approved property type configured at this step - - SimulationContext.Properties cprop = new SimulationContext.Properties(); - List context = to.getAllSimulationContexts(); - - context = new ArrayList(context.size()); - context.addAll(to.getAllSimulationContexts()); - cprop.disableCheck(); - for (Iterator i=contype.iterator(); i.hasNext(); ) { - SimulationContextType property = i.next(); - for (Iterator j=context.iterator(); j.hasNext(); ) { - SimulationContext existing = j.next(); - if (!existing.getType().equals(property)) continue; - property = null; // Forget this property as it is already set - break; - } - if (property != null) try { - String value = tool.extractProperty(property.getName()); - if (value == null) continue; // Property not defined into the document - - cprop.setType(property).setValue(value); - if (owner instanceof Study) ((Study)owner).addProjectContext(cprop); // Re-indexes knowledges and the study - else to.addSimulationContext(cprop); // Re-indexes knowledges only - } catch (Exception e) { - break; - } - } - } -} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/Relations.hbm.xml b/Workspace/Siman-Common/src/org/splat/som/Relations.hbm.xml deleted file mode 100644 index 7e7cd6a..0000000 --- a/Workspace/Siman-Common/src/org/splat/som/Relations.hbm.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/Revision.java b/Workspace/Siman-Common/src/org/splat/som/Revision.java index ea06e7f..bc00b66 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Revision.java +++ b/Workspace/Siman-Common/src/org/splat/som/Revision.java @@ -13,6 +13,8 @@ package org.splat.som; import java.nio.CharBuffer; import java.text.ParseException; +import org.splat.dal.bo.som.ProgressState; + public class Revision { diff --git a/Workspace/Siman-Common/src/org/splat/som/Step.java b/Workspace/Siman-Common/src/org/splat/som/Step.java index e22975e..d7a8dfb 100644 --- a/Workspace/Siman-Common/src/org/splat/som/Step.java +++ b/Workspace/Siman-Common/src/org/splat/som/Step.java @@ -14,29 +14,45 @@ import java.util.Vector; import java.util.Iterator; import org.hibernate.Session; +import org.splat.dal.bo.kernel.Relation; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.ConvertsRelation; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.ProjectElement; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.UsedByRelation; +import org.splat.dal.bo.som.UsesRelation; +import org.splat.dal.bo.som.VersionsRelation; +import org.splat.dal.dao.som.Database; import org.splat.kernel.InvalidPropertyException; import org.splat.kernel.MismatchException; import org.splat.kernel.MissedPropertyException; import org.splat.kernel.MultiplyDefinedException; import org.splat.kernel.NotApplicableException; -import org.splat.kernel.Relation; -import org.splat.kernel.User; -import org.splat.som.Database; +import org.splat.service.technical.IndexServiceImpl; +import org.splat.service.technical.ProjectSettingsService; public class Step { - private ProjectSettings.Step step; + private ProjectSettingsService.Step step; private ProjectElement owner; private List contex; - private List docums; + private List docums; private User actor; // Actor involved in operations on published documents and requiring a time-stamp // ============================================================================================================================== // Constructor // ============================================================================================================================== - protected Step (ProjectSettings.Step step, ProjectElement owner) { + public Step (ProjectSettingsService.Step step, ProjectElement owner) { // ---------------------------------------------------------------- this.step = step; this.owner = owner; @@ -131,36 +147,6 @@ public class Step { return new Publication(newdoc, owner); } - public SimulationContext addSimulationContext (SimulationContext.Properties dprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException, RuntimeException { -// ---------------------------------------------------------------------------------- - SimulationContext context = new SimulationContext(dprop.setStep(step)); - return addSimulationContext(context); - } - - public SimulationContext addSimulationContext (SimulationContext context) { -// ------------------------------------------------------------------------- - context.hold(); // Increments the reference count of simulation context - if (owner.isSaved()) try { - Session session = Database.getSession(); - Index lucin = Database.getIndex(); - - if (!context.isSaved()) session.save(context); - owner.add(context); - contex.add(context); // The context is also referenced from this (transient) Step - session.update(owner); - updateKnowledgeElementsIndex(lucin); - } - catch (Exception error) { - return null; - } - else { // Happens when copying a scenario - owner.add(context); - contex.add(context); // The context is also referenced from this (transient) Step -// In case of owner scenario, the Knowledge Element index will be updated later, when saving the scenario - } - return context; - } - public User getActor () { // ----------------------- return actor; @@ -219,7 +205,7 @@ public class Step { return result; } - public ProjectSettings.Step getStep () { + public ProjectSettingsService.Step getStep () { // -------------------------------------- return step; } @@ -319,25 +305,6 @@ public class Step { return true; } - public boolean removeSimulationContext (SimulationContext context) { -// ------------------------------------------------------------------ - SimulationContext torem = getSimulationContext(context.getIndex()); - Session session = Database.getSession(); - - if (torem == null) return false; - if (!owner.remove(torem)) return false; - - contex.remove(torem); - session.update(owner); - if (torem.isShared()) { - torem.release(); - session.update(torem); - } else { - session.delete(torem); - } - return true; - } - public void setActor (User user) { // -------------------------------- actor = user; @@ -346,7 +313,7 @@ public class Step { // Protected member functions // ============================================================================================================================== - protected boolean add (Publication newdoc) { + public boolean add (Publication newdoc) { // ------------------------------------------ if (!owner.add(newdoc)) return false; // Updates the study in memory docums.add(0, newdoc); // Updates this step @@ -356,7 +323,7 @@ public class Step { return true; } - protected boolean remove (Publication oldoc) { + public boolean remove (Publication oldoc) { // -------------------------------------------- if (!owner.remove(oldoc)) return false; // Updates the study in memory docums.remove(oldoc); // Updates this step @@ -365,32 +332,8 @@ public class Step { return true; } -// ============================================================================================================================== -// Private services -// ============================================================================================================================== + public List getContex() { + return contex; + } - private void updateKnowledgeElementsIndex(Index lucin) { -// ------------------------------------------------------ - Scenario[] scenarii; - if (owner instanceof Scenario) { - scenarii = new Scenario[1]; - scenarii[0] = (Scenario)owner; - } else { - scenarii = getOwnerStudy().getScenarii(); - } - try { - for (int i=0; i knelm = scene.getAllKnowledgeElements(); - for (Iterator j=knelm.iterator(); j.hasNext(); ) { - KnowledgeElement kelm = j.next(); - lucin.update(kelm); - } - scene.updateMyIndex(lucin); - } - } - catch (Exception error) { -// logger.error("Unable to re-index Knowledge Elements, reason:", error); - } - } } \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/StepRights.java b/Workspace/Siman-Common/src/org/splat/som/StepRights.java index af15d5c..6a07bc8 100644 --- a/Workspace/Siman-Common/src/org/splat/som/StepRights.java +++ b/Workspace/Siman-Common/src/org/splat/som/StepRights.java @@ -7,7 +7,10 @@ package org.splat.som; */ //TODO: Review this rights according to the state of the owner study. -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.ProjectElement; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.Study; public class StepRights { diff --git a/Workspace/Siman-Common/src/org/splat/som/Study.java b/Workspace/Siman-Common/src/org/splat/som/Study.java deleted file mode 100644 index ec5430a..0000000 --- a/Workspace/Siman-Common/src/org/splat/som/Study.java +++ /dev/null @@ -1,672 +0,0 @@ -package org.splat.som; -/** - * - * @author Daniel Brunier-Coulin - * @copyright OPEN CASCADE 2012 - */ - -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.LinkedList; -import java.util.Set; -import java.util.Vector; - -import org.hibernate.Session; -import org.splat.kernel.Persistent; -import org.splat.kernel.Relation; -import org.splat.kernel.User; -import org.splat.kernel.MultiplyDefinedException; -import org.splat.kernel.InvalidPropertyException; -import org.splat.kernel.MissedPropertyException; -import org.splat.kernel.UserDirectory; - - -public class Study extends ProjectElement { - -// Persistent fields - private String sid; // External unique reference in a format conform to the configuration pattern - private int docount; // Total number of documents of this study, including versions - private ProgressState state; - private Visibility visibility; - private List scenarii; - private String version; - private int history; // Number of studies versioning this one, if any - -// Transient fields - private List contributor; // Shortcut to contributors - private HashMap validactor; // Shortcut to validation cycles - private Set actor; // Summary of above actors - -// ============================================================================================================================== -// Construction -// ============================================================================================================================== - -// Fields initialization class - public static class Properties extends Persistent.Properties { -// ------------------------------------------------------------ - private String sid = null; // Search criterion only - private String title = null; - private String summary = null; - private User manager = null; - private User actor = null; // Search criterion only - private Visibility visibility = null; // Search criterion only - private ProgressState state = null; // Search criterion only - private Date date = null; - private List context = new Vector(); // Search criterion only - -// - Public services - - public void clear () { - super.clear(); - sid = null; - title = null; - summary = null; - manager = null; - actor = null; - visibility = null; - state = null; - date = null; - context = new Vector(); // as clear() may generate side effects - } - public Properties copy () { - Properties copy = new Properties(); - copy.sid = this.sid; - copy.title = this.title; - copy.summary = this.summary; - copy.manager = this.manager; - copy.actor = this.actor; - copy.visibility = this.visibility; - copy.state = this.state; - copy.date = this.date; - copy.context = this.context; - return copy; - } -// - Protected services - - protected User getActor () { - return actor; - } - protected User getManager () { - return manager; - } - protected ProgressState getProgressState () { - return state; - } - protected String getReference () { - return sid; - } - protected List getSimulationContexts () { - return context; - } - protected String getTitle () { - return title; - } - protected Visibility getVisibility () { - return visibility; - } -// - Property setters - -// For building a search query - public Properties setActor (User actor) - { - this.actor = actor; - return this; - } - public Properties setDate (Date date) - { - this.date = date; - return this; - } - public Properties setDescription (String summary) - { - if (summary.length() > 0) this.summary = summary; - return this; - } - public Properties setManager (User user) - { - this.manager = user; - return this; - } -// For building a search query - public Properties setReference (String sid) throws InvalidPropertyException - { - if (sid.length() == 0) throw new InvalidPropertyException("reference"); - this.sid = sid; - return this; - } -// For building a search query - public Properties setSimulationContexts (List context) { - this.context = context; - return this; - } -// For building a search query - public Properties setState (ProgressState state) - { - this.state = state; - return this; - } - public Properties setTitle (String title) throws InvalidPropertyException - { - if (title.length() == 0) throw new InvalidPropertyException("title"); - this.title = title; - return this; - } -// For building a search query - public Properties setVisibility (Visibility area) - { - this.visibility = area; - return this; - } -// - Global validity check - - public void checkValidity() throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException - { - if (title == null) throw new MissedPropertyException("title"); - if (manager == null) throw new MissedPropertyException("manager"); - } - } -// Database fetch constructor - protected Study () { -// ------------------ - contributor = null; - validactor = null; - actor = null; - } -// Internal constructor - protected Study (Properties sprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { -// ---------------------------------- - super(sprop); // Throws one of the above exception if not valid - sid = ProjectSettings.getReferencePattern(); // Reset after save - title = sprop.title; // Inherited attribute - manager = sprop.manager; - docount = 0; - history = 0; - scenarii = new LinkedList(); - visibility = Visibility.PRIVATE; - state = ProgressState.inWORK; - - credate = sprop.date; // Inherited attribute - if (credate == null) { - Calendar current = Calendar.getInstance(); - credate = current.getTime(); // Today - } - lasdate = credate; // Inherited attribute - version = new Revision().incrementAs(state).toString(); - - if (sprop.summary != null) this.setAttribute( new DescriptionAttribute(this, sprop.summary) ); - - contributor = null; - validactor = null; - actor = null; - } - -// ============================================================================================================================== -// Public member functions -// ============================================================================================================================== - - public boolean addContributor (User user) { -// ----------------------------------------- - if (contributor == null) this.setShortCuts(); // Initializes contributor - for (Iterator i=contributor.iterator(); i.hasNext(); ) { - User present = i.next(); - if ( present.equals(user) ) return false; - } - boolean absent = actor.add(user); // User may already be a reviewer or an approver - - this.addRelation( new ContributorRelation(this, user) ); - if (absent) updateMe(); // Else, useless to re-index the study - contributor.add(user); - return true; - } - - public SimulationContext addProjectContext (SimulationContext.Properties cprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException, RuntimeException { -// ------------------------------------------------------------------------------- - SimulationContext added = this.getFirstStep().addSimulationContext(cprop); - updateMe(); - return added; - } - - public SimulationContext addProjectContext (SimulationContext context) { -// ---------------------------------------------------------------------- - SimulationContext added = this.getFirstStep().addSimulationContext(context); - updateMe(); - return added; - } - - public Scenario addScenario (Scenario.Properties sprop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException, RuntimeException { -// ------------------------------------------------------- - if (sprop.getManager() == null) sprop.setManager(this.manager); - - Scenario scenario = new Scenario(sprop.setOwnerStudy(this)); - Scenario previous = sprop.getInsertAfter(); - Session session = Database.getSession(); - - if (previous == null) { - scenarii.add(scenario); - } else { - scenarii.add(scenarii.indexOf(previous)+1, scenario); - } - session.update(this); // No need to update the Lucene index - session.save(scenario); // Must be done after updating this study because of the back reference to the study - if (sprop.getBaseStep() != null) { -// No need to update the Knowledge Element index as Knowledge Elements are not copied - scenario.refresh(); // Because saving the scenario changes the hashcode of copied Publications - } - KnowledgeElementType ucase = KnowledgeElement.selectType("usecase"); - KnowledgeElement.Properties kprop = new KnowledgeElement.Properties(); - User admin = UserDirectory.selectUser(1); // First user created when creating the database - kprop.setType(ucase) - .setTitle(this.getTitle()) - .setValue(scenario.getTitle()) - .setAuthor(admin); // Internal Knowledge Element required by the validation process of knowledges - scenario.addKnowledgeElement(kprop); - return scenario; - } - -/** - * Returns all actors of this study other than the author, including contributors, reviewers and approvers. - * - * @return the actors of this study - * @see #hasActor(User) - */ - public Set getActors () { -// ----------------------------- - if (actor == null) setShortCuts(); - return Collections.unmodifiableSet(actor); - } - - public List getContributors () { -// ------------------------------------ - if (contributor == null) setShortCuts(); - return Collections.unmodifiableList(contributor); // May be empty - } - - public ProgressState getProgressState () { -// ---------------------------------------- - return state; - } - -/** - * Returns the global unique reference of this study. - * The study reference is common to all versions of the study (versioning a study does not change its reference). - * The form of this study reference is defined in the configuration of the application server - see the SOM XML customization - * file. - */ - public String getReference () { -// ----------------------------- - return sid; - } - - public Scenario[] getScenarii () { -// -------------------------------- - return scenarii.toArray(new Scenario[scenarii.size()]); - } - -/** - * Returns the validation cycle of the given document type. - * - * @param doc the document type being subject of validation - * @return the validation cycle of the document, or null if not defined. - */ - public ValidationCycle getValidationCycleOf (DocumentType type) { -// --------------------------------------------------------------- - if (validactor == null) setShortCuts(); - ValidationCycle result = validactor.get(type.getName()); - if (result == null) { - if (type.isStepResult()) result = validactor.get("default"); // "default" validation cycle defined in the configuration, if exist - if (result == null) result = validactor.get("built-in"); - } - return result; - } - - public String getVersion () { -// --------------------------- - return version; - } - - public Visibility getVisibility () { -// ---------------------------------- - return visibility; - } - -/** - * Checks if the given user is actor of this study. - * Actors include contributors, reviewers and approvers. - * - * @return true if the given user is actor of this study. - * @see #getActors() - */ - public boolean hasActor (User user) { -// ----------------------------------- - if (user == null) return false; - for (Iterator i=this.getActors().iterator(); i.hasNext(); ) { - User involved = i.next(); - if (involved.equals(user)) return true; - } - return false; - } - -/** - * Checks whether this study is in the Public or the Reference area of the repository. - * - * @return true if the study is public. - * @see #moveToPublic() - * @see #moveToReference() - */ - public boolean isPublic () { -// -------------------------- - return (visibility != Visibility.PRIVATE); - } -/** - * Checks if the given user participates to this study. - * The Study staff includes the author and contributors. - * - * @return true if the given user is actor of this study. - * @see #getContributors() - */ - public boolean isStaffedBy (User user) { -// -------------------------------------- - if (user == null) return false; - if (manager.equals(user)) return true; - for (Iterator i=getContributors().iterator(); i.hasNext();) { - if (i.next().equals(user)) return true; - } - return false; - } - - public boolean isVersioned () { -// ----------------------------- - return (history > 0); - } - -/** - * Moves this study from the Private to the Public area of the repository. - * - * @return true if the move succeeded. - * @see #isPublic() - */ - public boolean moveToPublic () { -// ------------------------------ - if (visibility != Visibility.PRIVATE) return false; - - this.visibility = Visibility.PUBLIC; - if ( updateMe() ) { - return updateKnowledgeElementsIndex(); // If fails, the database roll-back is under responsibility of the caller - } - return false; - } - -/** - * Moves this study from the Public to the Reference area of the repository. - * For being moved to the Reference area, the study must previously be approved. - * - * @return true if the move succeeded. - * @see #moveToPublic() - * @see #isPublic() - * @see Publication#approve(Date) - */ - public boolean moveToReference () { -// --------------------------------- - if (state != ProgressState.APPROVED) return false; - if (visibility != Visibility.PUBLIC) return false; - - this.visibility = Visibility.REFERENCE; - if ( updateMe() ) { - return updateKnowledgeElementsIndex(); // If fails, the database roll-back is under responsibility of the caller - } - return false; - } - - public boolean publishes (Document doc) { -// --------------------------------------- - if (!super.publishes(doc)) { - Scenario[] scene = this.getScenarii(); - for (int i=0; i j=contributor.iterator(); j.hasNext(); ) { - User present = j.next(); - if (!present.equals(user)) continue; - - this.removeRelation(ContributorRelation.class, user); - j.remove(); // Updates the contributor shortcut - done = true; - break; - } - } - if (done) updateMe(); - return done; - } - - public boolean removeProjectContext (SimulationContext context) { -// --------------------------------------------------------------- - boolean done = this.getFirstStep().removeSimulationContext(context); - updateMe(); - return done; - } - - public void setValidationCycle (DocumentType type, ValidationCycle.Properties vprop) { -// ------------------------------------------------------------------------------------ - if (validactor == null) setShortCuts(); // Initializes validactor and actor - - String cname = type.getName(); - ValidationCycle cycle = validactor.get(cname); - - if (cycle != null && cycle.isAssigned()) { - cycle.resetActors(vprop); - } else - try { - cycle = new ValidationCycle(this, vprop.setDocumentType(type)); - - ValidationCycleRelation link = cycle.getContext(); - this.addRelation(link); - validactor.put(cname, link.getTo()); // Replaces the cycle if exists as default, - } - catch (Exception error) { - logger.error("Unable to re-index Knowledge Elements, reason:", error); - return; - } - resetActorsShortCut(); - updateMe(); // Re-index the study, just in case - } - - public boolean shares (Document doc) { -// ------------------------------------ - Scenario[] scene = this.getScenarii(); // If shared from within the study, the document is shared by the scenarios - int counter = 0; - - for (int i=0; i i=validactor.values().iterator(); i.hasNext(); ) { - ValidationCycle cycle = i.next(); - User[] user = cycle.getAllActors(); - for (int j=0; j i=this.getAllRelations().iterator(); i.hasNext(); ) { - Relation link = i.next(); - Class kindof = link.getClass().getSuperclass(); - if (!kindof.equals(ActorRelation.class)) continue; - actor.add( ((ActorRelation)link).getTo() ); - } - } - - private void setShortCuts () { -// ---------------------------- - contributor = new Vector(); - validactor = new HashMap(); - actor = new HashSet(); - -// Get the contributors - for (Iterator i=getRelations(ContributorRelation.class).iterator(); i.hasNext(); ) { - ContributorRelation link = (ContributorRelation)i.next(); - contributor.add(link.getTo()); - } -// Get the validation cycles specific to this study - for (Iterator i=getRelations(ValidationCycleRelation.class).iterator(); i.hasNext(); ) { - ValidationCycleRelation link = (ValidationCycleRelation)i.next(); - validactor.put(link.getDocumentType().getName(), link.getTo()); // The associated document type is necessarily not null in this context - } -// Get the validation cycles coming from the configured workflow and not overridden in this study - for (Iterator i=ProjectSettings.getAllValidationCycles().iterator(); i.hasNext(); ) { - ProjectSettings.ValidationCycle cycle = i.next(); - String type = cycle.getName(); - if (!validactor.containsKey(type)) validactor.put(type, new ValidationCycle(this, cycle)); - } -// Get all corresponding actors - for (Iterator i=validactor.values().iterator(); i.hasNext(); ) { - ValidationCycle cycle = i.next(); - User[] user = cycle.getAllActors(); - for (int j=0; j i=this.getAllRelations().iterator(); i.hasNext(); ) { - Relation link = i.next(); - Class kindof = link.getClass().getSuperclass(); - if (!kindof.equals(ActorRelation.class)) continue; - actor.add( ((ActorRelation)link).getTo() ); - } - } - - private boolean updateKnowledgeElementsIndex() { -// ---------------------------------------------- - try { - Index lucin = Database.getIndex(); - - for (Iterator i=scenarii.iterator(); i.hasNext(); ) { - Scenario scene = i.next(); - for (Iterator j=scene.getAllKnowledgeElements().iterator(); j.hasNext(); ) { - KnowledgeElement kelm = j.next(); - lucin.update(kelm); - } - } - return true; - } - catch (Exception error) { - logger.error("Unable to re-index Knowledge Elements, reason:", error); - return false; - } - } - - private boolean updateMe () { -// --------------------------- - try { - Database.getSession().update(this); // Update of relational base - Database.getIndex().update(this); // Update of Lucene index - return true; - } - catch (Exception error) { - logger.error("Unable to re-index the study '" + getIndex() + "', reason:", error); - return false; - } - } -} \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/som/StudyRights.java b/Workspace/Siman-Common/src/org/splat/som/StudyRights.java index 4b23afc..2d98eca 100644 --- a/Workspace/Siman-Common/src/org/splat/som/StudyRights.java +++ b/Workspace/Siman-Common/src/org/splat/som/StudyRights.java @@ -8,7 +8,9 @@ package org.splat.som; * @copyright OPEN CASCADE 2012 */ -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.Study; public class StudyRights { diff --git a/Workspace/Siman-Common/src/spring/businessServiceContext.xml b/Workspace/Siman-Common/src/spring/businessServiceContext.xml new file mode 100644 index 0000000..01da7c5 --- /dev/null +++ b/Workspace/Siman-Common/src/spring/businessServiceContext.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/spring/daoServiceContext.xml b/Workspace/Siman-Common/src/spring/daoServiceContext.xml new file mode 100644 index 0000000..ec7b3a4 --- /dev/null +++ b/Workspace/Siman-Common/src/spring/daoServiceContext.xml @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/spring/globalContext.xml b/Workspace/Siman-Common/src/spring/globalContext.xml index aaffeaf..4ee39ca 100644 --- a/Workspace/Siman-Common/src/spring/globalContext.xml +++ b/Workspace/Siman-Common/src/spring/globalContext.xml @@ -11,5 +11,92 @@ http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> + + + + + + + + + + + + + + + + + + + + + + + org/splat/dal/bo/kernel/Persistent.hbm.xml + + org/splat/dal/bo/kernel/Any.hbm.xml + org/splat/dal/bo/kernel/Entity.hbm.xml + org/splat/dal/bo/kernel/Attribute.hbm.xml + org/splat/dal/bo/kernel/Relation.hbm.xml + + org/splat/dal/bo/kernel/TextAttribute.hbm.xml + + org/splat/dal/bo/kernel/User.hbm.xml + + + org/splat/dal/bo/som/ProjectElement.hbm.xml + + org/splat/dal/bo/som/Study.hbm.xml + org/splat/dal/bo/som/Scenario.hbm.xml + org/splat/dal/bo/som/Attributes.hbm.xml + org/splat/dal/bo/som/Relations.hbm.xml + org/splat/dal/bo/som/File.hbm.xml + org/splat/dal/bo/som/Document.hbm.xml + org/splat/dal/bo/som/Publication.hbm.xml + + org/splat/dal/bo/som/ValidationCycle.hbm.xml + + org/splat/dal/bo/som/Timestamp.hbm.xml + + org/splat/dal/bo/som/SimulationContext.hbm.xml + + + org/splat/dal/bo/som/KnowledgeElement.hbm.xml + + org/splat/dal/bo/som/IDBuilder.hbm.xml + + + + + hibernate.dialect=org.hibernate.dialect.HSQLDialect + hibernate.show_sql=true hbm2ddl.auto=update + hibernate.current_session_context_class=thread + + + + false + + + + + + + + + + + + + + + diff --git a/Workspace/Siman-Common/src/spring/technicalServiceContext.xml b/Workspace/Siman-Common/src/spring/technicalServiceContext.xml new file mode 100644 index 0000000..2f8d9b5 --- /dev/null +++ b/Workspace/Siman-Common/src/spring/technicalServiceContext.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/test/Test.java b/Workspace/Siman-Common/src/test/Test.java index e0e7083..10d81df 100644 --- a/Workspace/Siman-Common/src/test/Test.java +++ b/Workspace/Siman-Common/src/test/Test.java @@ -7,11 +7,27 @@ import java.util.Iterator; import java.util.List; import java.util.Vector; +import org.splat.service.SearchServiceImpl; +import org.splat.service.dto.Proxy; +import org.splat.service.technical.ProjectSettingsServiceImpl; import org.splat.som.*; +import org.splat.dal.bo.kernel.Role; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.ConvertsRelation; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.KnowledgeElementType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.ValidationCycle; +import org.splat.dal.dao.som.Database; import org.splat.kernel.Do; import org.splat.kernel.UserDirectory; -import org.splat.kernel.User; -import org.splat.kernel.Role; import org.splat.manox.Reader; import org.splat.manox.Toolbox; import org.splat.manox.Writer; @@ -29,12 +45,12 @@ public class Test { // Main // ============================================================================================================================== - public static void main(String[] args) { + public static void main(String[] args) {/* TODO: Create unit tests // -------------------------------------- Session session = Database.getSession(); // Single session for multiple operations Transaction transax = session.beginTransaction(); - ProjectSettings project = ProjectSettings.getMe(); + ProjectSettingsServiceImpl project = ProjectSettingsServiceImpl.getMe(); String path = System.getProperty("user.dir"); try { project.configure(path + "/src/som.xml"); @@ -563,7 +579,7 @@ public class Test { try { Study.Properties criter1 = new Study.Properties().setState(ProgressState.inPROGRESS); Study.Properties criter2 = new Study.Properties().setState(ProgressState.inWORK).setManager(user); - List result = Database.selectStudiesWhere(criter1, criter2); + List result = SearchServiceImpl.selectStudiesWhere(criter1, criter2); if (result.size() == 0) { logger.info("No study found."); } else { @@ -584,7 +600,7 @@ public class Test { String words = "sercoter"; try { Study.Properties criteria = new Study.Properties(); - List result = Database.selectStudiesWhere(criteria.setTitle(words)); + List result = SearchServiceImpl.selectStudiesWhere(criteria.setTitle(words)); if (result.size() == 0) { logger.info("No study found with a title including \"" + words + "\"."); } else { @@ -632,7 +648,7 @@ public class Test { context.add(reactor); Study.Properties sprop = new Study.Properties(); - List result = Database.selectStudiesWhere(sprop.setSimulationContexts(context) + List result = SearchServiceImpl.selectStudiesWhere(sprop.setSimulationContexts(context) .setState(ProgressState.inPROGRESS)); if (result.size() == 0) { logger.info("Study on Réacteur RAPSODIE not found."); @@ -676,7 +692,7 @@ public class Test { KnowledgeElementType ktype = KnowledgeElement.selectType("Bonne pratique"); KnowledgeElement.Properties sprop = new KnowledgeElement.Properties(); - List result = Database.selectKnowledgeElementsWhere(sprop.setSimulationContexts(context) + List result = SearchServiceImpl.selectKnowledgeElementsWhere(sprop.setSimulationContexts(context) .setType(ktype)); if (result.size() == 0) { logger.info("Study on Réacteur RAPSODIE not found."); @@ -831,6 +847,6 @@ public class Test { } catch (Exception e) { return false; - } + }*/ } } \ No newline at end of file diff --git a/Workspace/Siman/.project b/Workspace/Siman/.project index 9bce07d..cdf9f8d 100644 --- a/Workspace/Siman/.project +++ b/Workspace/Siman/.project @@ -52,8 +52,14 @@ + + org.springframework.ide.eclipse.core.springbuilder + + + + org.springframework.ide.eclipse.core.springnature org.eclipse.wst.common.project.facet.core.nature org.eclipse.jdt.core.javanature org.eclipse.wst.common.modulecore.ModuleCoreNature diff --git a/Workspace/Siman/.settings/org.eclipse.wst.common.component b/Workspace/Siman/.settings/org.eclipse.wst.common.component index 849ba45..477d6d7 100644 --- a/Workspace/Siman/.settings/org.eclipse.wst.common.component +++ b/Workspace/Siman/.settings/org.eclipse.wst.common.component @@ -127,16 +127,16 @@ uses - + uses - + uses - + uses - + uses diff --git a/Workspace/Siman/.springBeans b/Workspace/Siman/.springBeans new file mode 100644 index 0000000..ef7a1b3 --- /dev/null +++ b/Workspace/Siman/.springBeans @@ -0,0 +1,23 @@ + + + + xml + + + src/spring/applicationContext.xml + + + + + true + false + + /Siman-Common/src/spring/businessServiceContext.xml + /Siman-Common/src/spring/daoServiceContext.xml + /Siman-Common/src/spring/globalContext.xml + /Siman-Common/src/spring/technicalServiceContext.xml + src/spring/applicationContext.xml + + + + diff --git a/Workspace/Siman/WebContent/WEB-INF/lib/commons-dbcp-1.4.jar b/Workspace/Siman/WebContent/WEB-INF/lib/commons-dbcp-1.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..c4c1c4f286a464227a55bcd3577d01d107e7b192 GIT binary patch literal 160519 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 literal 0 HcmV?d00001 diff --git a/Workspace/Siman/WebContent/jsp/menubar.jsp b/Workspace/Siman/WebContent/jsp/menubar.jsp index cf7129b..c9f862e 100644 --- a/Workspace/Siman/WebContent/jsp/menubar.jsp +++ b/Workspace/Siman/WebContent/jsp/menubar.jsp @@ -1,9 +1,9 @@ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> -<%@ page import="org.splat.kernel.User"%> -<%@ page import="org.splat.som.Study"%> -<%@ page import="org.splat.som.KnowledgeElement"%> +<%@ page import="org.splat.dal.bo.kernel.User"%> +<%@ page import="org.splat.dal.bo.som.Study"%> +<%@ page import="org.splat.dal.bo.som.KnowledgeElement"%> <%@ page import="org.splat.som.ApplicationRights"%> <%@ page import="org.splat.simer.ApplicationSettings"%> <%@ diff --git a/Workspace/Siman/WebContent/jsp/newDocument.jsp b/Workspace/Siman/WebContent/jsp/newDocument.jsp index aa15212..dbd2c20 100644 --- a/Workspace/Siman/WebContent/jsp/newDocument.jsp +++ b/Workspace/Siman/WebContent/jsp/newDocument.jsp @@ -4,7 +4,7 @@ <%@ page import="java.io.ObjectOutputStream"%> <%@ page import="java.io.IOException"%> <%@ page import="java.net.URL"%> -<%@ page import="org.splat.kernel.User"%> +<%@ page import="org.splat.dal.bo.kernel.User"%> <%@ page import="org.splat.som.ApplicationRights"%> <%@ page import="org.splat.simer.OpenStudyServices"%> <% diff --git a/Workspace/Siman/WebContent/sgeom/index.jsp b/Workspace/Siman/WebContent/sgeom/index.jsp index ccb4844..2f08300 100644 --- a/Workspace/Siman/WebContent/sgeom/index.jsp +++ b/Workspace/Siman/WebContent/sgeom/index.jsp @@ -4,10 +4,10 @@ <%@ page import="java.util.ResourceBundle"%> <%@ page import="org.hibernate.Session"%> <%@ page import="org.hibernate.Transaction"%> -<%@ page import="org.splat.som.Database"%> -<%@ page import="org.splat.som.Scenario"%> -<%@ page import="org.splat.som.Publication"%> -<%@ page import="org.splat.som.Document"%> +<%@ page import="org.splat.dal.dao.som.Database"%> +<%@ page import="org.splat.dal.bo.som.Scenario"%> +<%@ page import="org.splat.dal.bo.som.Publication"%> +<%@ page import="org.splat.dal.bo.som.Document"%> <%@ page import="org.splat.som.ApplicationRights"%> <%@ page import="org.splat.simer.OpenStudy"%> <%@ page import="org.splat.simer.StudyMenu"%> @@ -38,7 +38,7 @@ for (i=0; i <%@ page import="org.hibernate.Session"%> <%@ page import="org.hibernate.Transaction"%> -<%@ page import="org.splat.som.Database"%> -<%@ page import="org.splat.som.Scenario"%> -<%@ page import="org.splat.som.Publication"%> -<%@ page import="org.splat.som.Document"%> +<%@ page import="org.splat.dal.dao.som.Database"%> +<%@ page import="org.splat.dal.bo.som.Scenario"%> +<%@ page import="org.splat.dal.bo.som.Publication"%> +<%@ page import="org.splat.dal.bo.som.Document"%> <%@ page import="org.splat.som.ApplicationRights"%> <%@ page import="org.splat.simer.OpenStudy"%> <%@ page import="org.splat.simer.StudyMenu"%> @@ -38,7 +38,7 @@ for (i=0; i <%@ page import="com.opensymphony.xwork2.ActionContext"%> <%@ page import="org.splat.simer.StudyPropertiesAction"%> -<%@ page import="org.splat.kernel.User"%> +<%@ page import="org.splat.dal.bo.kernel.User"%> <%@ taglib prefix="s" uri="/struts-tags" %> diff --git a/Workspace/Siman/WebContent/study/searchKnowledge.jsp b/Workspace/Siman/WebContent/study/searchKnowledge.jsp index c832584..26f947f 100644 --- a/Workspace/Siman/WebContent/study/searchKnowledge.jsp +++ b/Workspace/Siman/WebContent/study/searchKnowledge.jsp @@ -1,7 +1,7 @@ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> -<%@ page import="org.splat.som.KnowledgeElement"%> +<%@ page import="org.splat.dal.bo.som.KnowledgeElement"%> <%@ page import="org.splat.som.ApplicationRights"%> <%@ page import="org.splat.simer.ApplicationSettings"%> <%@ page import="java.util.ResourceBundle"%> diff --git a/Workspace/Siman/WebContent/study/searchStudy.jsp b/Workspace/Siman/WebContent/study/searchStudy.jsp index 62285d5..e651945 100644 --- a/Workspace/Siman/WebContent/study/searchStudy.jsp +++ b/Workspace/Siman/WebContent/study/searchStudy.jsp @@ -1,7 +1,7 @@ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" %> -<%@ page import="org.splat.som.Study"%> +<%@ page import="org.splat.dal.bo.som.Study"%> <%@ page import="org.splat.som.ApplicationRights"%> <%@ page import="org.splat.simer.ApplicationSettings"%> <%@ page import="java.util.ResourceBundle"%> diff --git a/Workspace/Siman/conf/debug.properties b/Workspace/Siman/conf/debug.properties index d6e03a8..0ac18cd 100644 --- a/Workspace/Siman/conf/debug.properties +++ b/Workspace/Siman/conf/debug.properties @@ -2,6 +2,8 @@ connection.url=jdbc:mysql://localhost/simer connection.username=simer connection.password=admin +#connection.driver_class=com.mysql.jdbc.Driver +connection.driver_class=com.p6spy.engine.spy.P6SpyDriver # Hibernate config hbm2ddl.auto=update @@ -10,7 +12,7 @@ hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect # Log properties hibernate.show_sql=true root.logger.appender= -root.logger.level=INFO +root.logger.level=DEBUG com.opensymphony.logger.level=DEBUG org.apache.struts2.logger.level=DEBUG org.springframework.logger.level=DEBUG diff --git a/Workspace/Siman/conf/release.properties b/Workspace/Siman/conf/release.properties index 3bf7eb0..d4894ed 100644 --- a/Workspace/Siman/conf/release.properties +++ b/Workspace/Siman/conf/release.properties @@ -2,6 +2,7 @@ connection.url=jdbc:mysql://localhost/simer connection.username=simer connection.password=admin +connection.driver_class=com.p6spy.engine.spy.P6SpyDriver # Hibernate config hbm2ddl.auto=validate diff --git a/Workspace/Siman/conf/templates/hibernate.cfg.xml b/Workspace/Siman/conf/templates/hibernate.cfg.xml deleted file mode 100644 index 297cc7e..0000000 --- a/Workspace/Siman/conf/templates/hibernate.cfg.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - com.mysql.jdbc.Driver - @connection.url@ - @connection.username@ - @connection.password@ - - - 1 - org.hibernate.transaction.JDBCTransactionFactory - - - @hibernate.dialect@ - - - thread - - - @hibernate.show_sql@ - - - @hbm2ddl.auto@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/module/SaveDocumentAction.java b/Workspace/Siman/src/org/splat/module/SaveDocumentAction.java index 7d04e46..4ff2559 100644 --- a/Workspace/Siman/src/org/splat/module/SaveDocumentAction.java +++ b/Workspace/Siman/src/org/splat/module/SaveDocumentAction.java @@ -10,257 +10,332 @@ import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.splat.kernel.Do; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; +import org.splat.service.PublicationService; +import org.splat.service.ScenarioService; +import org.splat.service.StepService; import org.splat.simer.Action; import org.splat.simer.OpenStudy; -import org.splat.som.ConvertsRelation; -import org.splat.som.Database; -import org.splat.som.Document; -import org.splat.som.DocumentType; -import org.splat.som.ProgressState; -import org.splat.som.Publication; -import org.splat.som.Scenario; -import org.splat.som.SimulationContext; -import org.splat.som.SimulationContextType; +import org.splat.dal.bo.som.ConvertsRelation; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; import org.splat.som.Step; +public class SaveDocumentAction extends Action { -public class SaveDocumentAction extends Action { - - private OpenStudy mystudy = null; - private int doctype = 0; - private String filename = null; - private String docname = null; - private ProgressState state = null; - private List defuses = null; - private String summary = null; // Summary of changes in the new version + private OpenStudy mystudy = null; + private int doctype = 0; + private String filename = null; + private String docname = null; + private ProgressState state = null; + private List defuses = null; + private String summary = null; // Summary of changes in the new version + private ScenarioService _scenarioService; + private PublicationService _publicationService; + private StepService _stepService; private static final long serialVersionUID = -3364960833373200115L; -// ============================================================================================================================== -// Action methods -// ============================================================================================================================== - - public String doSave () { -// ----------------------- - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - try { -// Getting user inputs - mystudy = getOpenStudy(); - User user = getConnectedUser(); - Step step = mystudy.getSelectedStep(); - DocumentType type = Document.selectType(doctype); -// File updir = Database.getDownloadDirectory(user); -// File upfile = new File(updir.getPath() + "/" + filename); - String upath = Database.getTemplatePath(); // Instead of DownloadDirectory for sharing the "uploaded" file between users - File upfile = new File(upath + filename); - String[] table = filename.split("\\x2E"); - String format = table[table.length-1]; - -// Creation of the document - ((Scenario)step.getOwner()).checkin(); // Modules necessarily save their data in a scenario step - connex.flush(); - - Document.Properties dprop = new Document.Properties(); - Publication credoc = step.createDocument(dprop.setName(docname) - .setType(type) - .setFormat(format) - .setAuthor(user)); -// Writing the uploaded file into the created document - File target = credoc.getSourceFile().asFile(); - if (target.exists()) target.delete(); - Do.copy(upfile, target); // Instead of rename for keeping the "uploaded" file for further use -// upfile.renameTo(target); - -// Saving the document in given state - credoc.saveAs(state); - -// Creation of default uses relations - defuses = new Vector(); - setupDefaultUses(type); // Recursive function - for (Iterator i=defuses.iterator(); i.hasNext(); ) { - credoc.addDependency(i.next()); - } - -// Execution of module specific operations - -// 1. Conversion of the document to internal format, if required -//TODO: The following code is temporary, waiting for the support of converters - if (format.equals("part")) { - ConvertsRelation export = credoc.attach("brep"); - - target = export.getTo().asFile(); - if (target.exists()) target.delete(); - Do.copy(upfile, target); // Instead of rename for keeping the "uploaded" file for further use - } -// 2. Addition of simulation contexts - if (type.equals("model")) { // Set the characteristics of the mesh - SimulationContext.Properties cprop = new SimulationContext.Properties(); - SimulationContextType ctype = SimulationContext.selectType("model"); - SimulationContext context = Database.selectSimulationContext(ctype, "Éléments finis"); - if (context == null) { - step.addSimulationContext(cprop.setType(ctype).setValue("Éléments finis")); - } else { - step.addSimulationContext(context); - } - ctype = SimulationContext.selectType("element"); - context = Database.selectSimulationContext(ctype, "Surfacique"); - if (context == null) { - step.addSimulationContext(cprop.setType(ctype).setValue("Surfacique")); - } else { - step.addSimulationContext(context); - } - ctype = SimulationContext.selectType("shape"); - context = Database.selectSimulationContext(ctype, "Triangles"); - if (context == null) { - step.addSimulationContext(cprop.setType(ctype).setValue("Triangles")); - } else { - step.addSimulationContext(context); - } - } -// Update of the open study -// mystudy.add(credoc); // Useless while the SIMER page need to be refreshed manually - getMenu("study").selects(mystudy.getSelection()); // Updates the menu icon, in case of first added document - - transax.commit(); - return SUCCESS; - } - catch (Exception saverror) { - logger.error("Reason:", saverror); - if (transax != null && transax.isActive()) { -// Second try-catch as the rollback could fail as well - try { - transax.rollback(); - } catch (HibernateException backerror) { - logger.debug("Error rolling back transaction", backerror); - } - } - return ERROR; - } - } - - public String doUpdate () { -// ------------------------- - return SUCCESS; - } - - public String doVersion () { -// -------------------------- - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - try { -// Getting user inputs - mystudy = getOpenStudy(); - User user = getConnectedUser(); - Step step = mystudy.getSelectedStep(); -// File updir = Database.getDownloadDirectory(user); -// File upfile = new File(updir.getPath() + "/" + filename); - String upath = Database.getTemplatePath(); // Instead of DownloadDirectory for sharing the "uploaded" file between users - File upfile = new File(upath + filename); - String[] table = filename.split("\\x2E"); - String format = table[table.length-1]; - -// Versioning of the document - Publication current = mystudy.getSelectedDocument(); - Document.Properties dprop = new Document.Properties(); - dprop.setAuthor(user); - if (summary.length() > 0) dprop.setDescription(summary); - - Publication next = step.versionDocument(current, dprop); - -// Writing the uploaded file into the created document - File target = next.getSourceFile().asFile(); - if (target.exists()) target.delete(); - Do.copy(upfile, target); // Instead of rename for keeping the "uploaded" file for further use -// upfile.renameTo(target); - -// Saving the document in given state - next.saveAs(state); - -// Creation of default uses relations - defuses = new Vector(); - setupDefaultUses(next.value().getType()); // Recursive function - for (Iterator i=defuses.iterator(); i.hasNext(); ) { - next.addDependency(i.next()); - } -//TODO: Outdating impacted document - -// Execution of module specific operations - -// 1. Conversion of the document to internal format, if required -//TODO: The following code is temporary, waiting for the support of converters - if (format.equals("part")) { - ConvertsRelation export = next.attach("brep"); - String fname = table[0]; - - for (int i=1; i uses = type.getDefaultUses(); - - for (Iterator i=uses.iterator(); i.hasNext();) { - DocumentType usetype = i.next(); - List usedoc = mystudy.collectInvolvedDocuments(usetype); - if (usedoc.isEmpty()) setupDefaultUses(usetype); - else defuses.addAll(usedoc); - } - } + // ============================================================================================================================== + // Action methods + // ============================================================================================================================== + + public String doSave() { + // ----------------------- + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + try { + // Getting user inputs + mystudy = getOpenStudy(); + User user = getConnectedUser(); + Step step = mystudy.getSelectedStep(); + DocumentType type = Document.selectType(doctype); + // File updir = Database.getDownloadDirectory(user); + // File upfile = new File(updir.getPath() + "/" + filename); + String upath = Database.getTemplatePath(); // Instead of DownloadDirectory for sharing the "uploaded" file between users + File upfile = new File(upath + filename); + String[] table = filename.split("\\x2E"); + String format = table[table.length - 1]; + + // Creation of the document + getScenarioService().checkin((Scenario) step.getOwner()); // Modules necessarily save their data in a scenario step + connex.flush(); + + Document.Properties dprop = new Document.Properties(); + Publication credoc = step.createDocument(dprop.setName(docname) + .setType(type).setFormat(format).setAuthor(user)); + // Writing the uploaded file into the created document + File target = credoc.getSourceFile().asFile(); + if (target.exists()) + target.delete(); + Do.copy(upfile, target); // Instead of rename for keeping the "uploaded" file for further use + // upfile.renameTo(target); + + // Saving the document in given state + getPublicationService().saveAs(credoc, state); + + // Creation of default uses relations + defuses = new Vector(); + setupDefaultUses(type); // Recursive function + for (Iterator i = defuses.iterator(); i.hasNext();) { + credoc.addDependency(i.next()); + } + + // Execution of module specific operations + + // 1. Conversion of the document to internal format, if required + // TODO: The following code is temporary, waiting for the support of converters + if (format.equals("part")) { + ConvertsRelation export = credoc.attach("brep"); + + target = export.getTo().asFile(); + if (target.exists()) + target.delete(); + Do.copy(upfile, target); // Instead of rename for keeping the "uploaded" file for further use + } + // 2. Addition of simulation contexts + if (type.equals("model")) { // Set the characteristics of the mesh + SimulationContext.Properties cprop = new SimulationContext.Properties(); + SimulationContextType ctype = SimulationContext + .selectType("model"); + SimulationContext context = Database.selectSimulationContext( + ctype, "Éléments finis"); + if (context == null) { + getStepService().addSimulationContext(step, + cprop.setType(ctype).setValue("Éléments finis")); + } else { + getStepService().addSimulationContext(step, context); + } + ctype = SimulationContext.selectType("element"); + context = Database.selectSimulationContext(ctype, "Surfacique"); + if (context == null) { + getStepService().addSimulationContext(step, + cprop.setType(ctype).setValue("Surfacique")); + } else { + getStepService().addSimulationContext(step, context); + } + ctype = SimulationContext.selectType("shape"); + context = Database.selectSimulationContext(ctype, "Triangles"); + if (context == null) { + getStepService().addSimulationContext(step, + cprop.setType(ctype).setValue("Triangles")); + } else { + getStepService().addSimulationContext(step, context); + } + } + // Update of the open study + // mystudy.add(credoc); // Useless while the SIMER page need to be refreshed manually + getMenu("study").selects(mystudy.getSelection()); // Updates the menu icon, in case of first added document + + transax.commit(); + return SUCCESS; + } catch (Exception saverror) { + logger.error("Reason:", saverror); + if (transax != null && transax.isActive()) { + // Second try-catch as the rollback could fail as well + try { + transax.rollback(); + } catch (HibernateException backerror) { + logger.debug("Error rolling back transaction", backerror); + } + } + return ERROR; + } + } + + /** + * Get the publicationService. + * + * @return publicationService + */ + private PublicationService getPublicationService() { + return _publicationService; + } + + /** + * Set the publicationService. + * + * @param publicationService + * the publicationService to set + */ + public void setPublicationService(PublicationService publicationService) { + _publicationService = publicationService; + } + + /** + * Get the scenarioService. + * + * @return scenarioService + */ + public ScenarioService getScenarioService() { + return _scenarioService; + } + + /** + * Set the scenarioService. + * + * @param scenarioService + * the scenarioService to set + */ + public void setScenarioService(ScenarioService scenarioService) { + _scenarioService = scenarioService; + } + + /** + * Get the stepService. + * + * @return the stepService + */ + public StepService getStepService() { + return _stepService; + } + + /** + * Set the stepService. + * + * @param stepService + * the stepService to set + */ + public void setStepService(StepService stepService) { + _stepService = stepService; + } + + public String doUpdate() { + // ------------------------- + return SUCCESS; + } + + public String doVersion() { + // -------------------------- + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + try { + // Getting user inputs + mystudy = getOpenStudy(); + User user = getConnectedUser(); + Step step = mystudy.getSelectedStep(); + // File updir = Database.getDownloadDirectory(user); + // File upfile = new File(updir.getPath() + "/" + filename); + String upath = Database.getTemplatePath(); // Instead of DownloadDirectory for sharing the "uploaded" file between users + File upfile = new File(upath + filename); + String[] table = filename.split("\\x2E"); + String format = table[table.length - 1]; + + // Versioning of the document + Publication current = mystudy.getSelectedDocument(); + Document.Properties dprop = new Document.Properties(); + dprop.setAuthor(user); + if (summary.length() > 0) + dprop.setDescription(summary); + + Publication next = step.versionDocument(current, dprop); + + // Writing the uploaded file into the created document + File target = next.getSourceFile().asFile(); + if (target.exists()) + target.delete(); + Do.copy(upfile, target); // Instead of rename for keeping the "uploaded" file for further use + // upfile.renameTo(target); + + // Saving the document in given state + getPublicationService().saveAs(next, state); + + // Creation of default uses relations + defuses = new Vector(); + setupDefaultUses(next.value().getType()); // Recursive function + for (Iterator i = defuses.iterator(); i.hasNext();) { + next.addDependency(i.next()); + } + // TODO: Outdating impacted document + + // Execution of module specific operations + + // 1. Conversion of the document to internal format, if required + // TODO: The following code is temporary, waiting for the support of converters + if (format.equals("part")) { + ConvertsRelation export = next.attach("brep"); + String fname = table[0]; + + for (int i = 1; i < table.length - 1; i++) + fname = fname + table[i]; + upfile = new File(upath + fname + ".brep"); + upfile.renameTo(export.getTo().asFile()); + } + + // Update of the open study + // mystudy.setSelection(mystudy.getSelection()); // Rebuild the presentation + + transax.commit(); + return SUCCESS; + } catch (Exception saverror) { + logger.error("Reason:", saverror); + if (transax != null && transax.isActive()) { + // Second try-catch as the rollback could fail as well + try { + transax.rollback(); + } catch (HibernateException backerror) { + logger.debug("Error rolling back transaction", backerror); + } + } + return ERROR; + } + } + + // ============================================================================================================================== + // Getters and setters + // ============================================================================================================================== + + public String getDescription() { + // ------------------------------- + return summary; + } + + public void setDescription(String summary) { + // ------------------------------------------- + this.summary = summary; + } + + public void setDocumentName(String name) { + // ----------------------------------------- + this.docname = name; + } + + public void setDocumentState(String state) { + // ------------------------------------------- + this.state = ProgressState.valueOf(state); + } + + public void setDocumentType(String value) { + // ------------------------------------------ + this.doctype = Integer.valueOf(value); + } + + public void setFileName(String name) { + // ------------------------------------- + this.filename = name; + } + + // ============================================================================================================================== + // Private service + // ============================================================================================================================== + + private void setupDefaultUses(DocumentType type) { + // ------------------------------------------------- + Set uses = type.getDefaultUses(); + + for (Iterator i = uses.iterator(); i.hasNext();) { + DocumentType usetype = i.next(); + List usedoc = mystudy.collectInvolvedDocuments(usetype); + if (usedoc.isEmpty()) + setupDefaultUses(usetype); + else + defuses.addAll(usedoc); + } + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/Action.java b/Workspace/Siman/src/org/splat/simer/Action.java index 8c7fc18..9766ed3 100644 --- a/Workspace/Siman/src/org/splat/simer/Action.java +++ b/Workspace/Siman/src/org/splat/simer/Action.java @@ -10,12 +10,12 @@ import com.opensymphony.xwork2.ActionSupport; import org.apache.struts2.interceptor.SessionAware; import org.apache.log4j.Logger; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; import org.splat.som.ApplicationRights; -import org.splat.som.KnowledgeElement; -import org.splat.som.SimulationContextType; -import org.splat.som.Study; -import org.splat.som.DocumentType; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.SimulationContextType; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.DocumentType; import org.splat.wapp.Menu; diff --git a/Workspace/Siman/src/org/splat/simer/ApplicationSettings.java b/Workspace/Siman/src/org/splat/simer/ApplicationSettings.java index a6cb601..27ec0ac 100644 --- a/Workspace/Siman/src/org/splat/simer/ApplicationSettings.java +++ b/Workspace/Siman/src/org/splat/simer/ApplicationSettings.java @@ -20,15 +20,15 @@ import org.w3c.dom.NodeList; import org.w3c.dom.NamedNodeMap; import org.apache.log4j.Logger; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; import org.splat.manox.XDOM; -import org.splat.som.Document; +import org.splat.dal.bo.som.Document; import org.splat.som.DocumentRights; -import org.splat.som.DocumentType; -import org.splat.som.KnowledgeElement; -import org.splat.som.ProgressState; -import org.splat.som.ProjectSettings; -import org.splat.som.SimulationContext; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.ProgressState; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.dal.bo.som.SimulationContext; import org.splat.som.Step; import org.splat.som.StudyRights; import org.splat.wapp.MenuItem; @@ -57,6 +57,23 @@ public class ApplicationSettings { private static ApplicationSettings my = null; // Singleton instance protected final static Logger logger = Logger.getLogger(ApplicationSettings.class); + private ProjectSettingsService _projectSettingsService; + + /** + * Get the projectSettingsService. + * @return the projectSettingsService + */ + public ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + /** + * Set the projectSettingsService. + * @param projectSettingsService the projectSettingsService to set + */ + public void setProjectSettings( + ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } private static class NewMenu extends SimpleMenu { // ----------------------------------------------------------------- @@ -389,8 +406,12 @@ public class ApplicationSettings { // ------------------------------------------ return my; // The application is supposed being previously created below } - protected ApplicationSettings (String wappurl, Locale lang) throws IOException { -// ----------------------------------------------------------- + /** + * @param wappurl + * @param lang + * @return + */ + public ApplicationSettings init(String wappurl, Locale lang) throws IOException { ClassLoader cloader = getClass().getClassLoader(); String[] wurl = wappurl.split("/"); // [0]="http:", [1]="", [2]="{server}:{port}", [3]="name" @@ -404,6 +425,7 @@ public class ApplicationSettings { logger.info("Application root set to " + wapprops.getProperty("wapp.root")); my = this; + return this; } // ============================================================================================================================== @@ -475,9 +497,9 @@ public class ApplicationSettings { // Settings based on the customization bars = new HashMap(); // May be empty if no module installed - List steps = ProjectSettings.getAllSteps(); - for (Iterator i=steps.iterator(); i.hasNext();) { - ProjectSettings.Step step = i.next(); + List steps = getProjectSettings().getAllSteps(); + for (Iterator i=steps.iterator(); i.hasNext();) { + ProjectSettingsService.Step step = i.next(); List formats = getDefaultFormats(step); if (formats.size() == 0) continue; @@ -618,7 +640,7 @@ public class ApplicationSettings { // Private services // ============================================================================================================================== - private List getDefaultFormats (ProjectSettings.Step step) { + private List getDefaultFormats (ProjectSettingsService.Step step) { // ------------------------------------------------------------------ Set keys = defdoctype.keySet(); int number = step.getNumber(); diff --git a/Workspace/Siman/src/org/splat/simer/ConnectionAction.java b/Workspace/Siman/src/org/splat/simer/ConnectionAction.java index f427826..1e90a55 100644 --- a/Workspace/Siman/src/org/splat/simer/ConnectionAction.java +++ b/Workspace/Siman/src/org/splat/simer/ConnectionAction.java @@ -9,9 +9,9 @@ import javax.security.auth.callback.*; import org.hibernate.Session; import org.hibernate.Transaction; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; import org.splat.som.ApplicationRights; -import org.splat.som.Database; +import org.splat.dal.dao.som.Database; import java.io.IOException; import javax.security.auth.login.FailedLoginException; diff --git a/Workspace/Siman/src/org/splat/simer/Converter.java b/Workspace/Siman/src/org/splat/simer/Converter.java index 91048c0..5721022 100644 --- a/Workspace/Siman/src/org/splat/simer/Converter.java +++ b/Workspace/Siman/src/org/splat/simer/Converter.java @@ -13,8 +13,8 @@ import javax.naming.Context; import javax.naming.InitialContext; import org.splat.kernel.MismatchException; -import org.splat.som.Document; -import org.splat.som.Publication; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.Publication; import org.apache.log4j.Logger; diff --git a/Workspace/Siman/src/org/splat/simer/DisplayKnowledgeAction.java b/Workspace/Siman/src/org/splat/simer/DisplayKnowledgeAction.java index 6b44dd0..988d6d5 100644 --- a/Workspace/Siman/src/org/splat/simer/DisplayKnowledgeAction.java +++ b/Workspace/Siman/src/org/splat/simer/DisplayKnowledgeAction.java @@ -4,8 +4,8 @@ import java.util.List; import org.hibernate.Session; import org.hibernate.Transaction; -import org.splat.som.Database; -import org.splat.som.KnowledgeElement; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.KnowledgeElement; import org.splat.som.Step; diff --git a/Workspace/Siman/src/org/splat/simer/DisplayStudyStepAction.java b/Workspace/Siman/src/org/splat/simer/DisplayStudyStepAction.java index 1e96c24..0c7d45f 100644 --- a/Workspace/Siman/src/org/splat/simer/DisplayStudyStepAction.java +++ b/Workspace/Siman/src/org/splat/simer/DisplayStudyStepAction.java @@ -4,11 +4,11 @@ import java.util.List; import org.hibernate.Session; import org.hibernate.Transaction; -import org.splat.som.Database; -import org.splat.som.ProjectElement; -import org.splat.som.Scenario; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.ProjectElement; +import org.splat.dal.bo.som.Scenario; import org.splat.som.StepRights; -import org.splat.som.Study; +import org.splat.dal.bo.som.Study; import org.splat.wapp.PopupMenu; import org.splat.wapp.SimpleMenu; diff --git a/Workspace/Siman/src/org/splat/simer/DocumentFacade.java b/Workspace/Siman/src/org/splat/simer/DocumentFacade.java index 28f0b2c..9c113ca 100644 --- a/Workspace/Siman/src/org/splat/simer/DocumentFacade.java +++ b/Workspace/Siman/src/org/splat/simer/DocumentFacade.java @@ -14,19 +14,20 @@ import java.util.List; import java.util.ResourceBundle; import org.splat.manox.XMLDocument; -import org.splat.kernel.Relation; -import org.splat.som.ConvertsRelation; -import org.splat.som.Document; +import org.splat.dal.bo.kernel.Relation; +import org.splat.dal.bo.som.ConvertsRelation; +import org.splat.dal.bo.som.Document; import org.splat.som.DocumentRights; -import org.splat.som.DocumentType; -import org.splat.som.ProgressState; -import org.splat.som.ProjectSettings; -import org.splat.som.Publication; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.service.PublicationService; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.dal.bo.som.Publication; import org.splat.som.Revision; import org.splat.som.Step; -import org.splat.som.Timestamp; -import org.splat.som.UsesRelation; -import org.splat.som.VersionsRelation; +import org.splat.dal.bo.som.Timestamp; +import org.splat.dal.bo.som.UsesRelation; +import org.splat.dal.bo.som.VersionsRelation; import org.splat.wapp.PopupMenu; @@ -50,6 +51,8 @@ public class DocumentFacade implements HistoryFacade { private List exports; private List history; private PopupMenu popup; + private ProjectSettingsService _projectSettingsService; + private PublicationService _publicationService; private enum State { closed, open, deepopen } @@ -234,12 +237,12 @@ public class DocumentFacade implements HistoryFacade { ResourceBundle custom = ResourceBundle.getBundle("som", ApplicationSettings.getCurrentLocale()); DecimalFormat sizstring = new DecimalFormat(custom.getString("size.format")); // Locale size display format SimpleDateFormat datstring = new SimpleDateFormat(custom.getString("date.format")); // Locale date display format - Revision.Format verstring = new Revision.Format(ProjectSettings.getRevisionPattern()); + Revision.Format verstring = new Revision.Format(getProjectSettings().getRevisionPattern()); String path = my.getSourceFile().getRelativePath(); String[] mapping = ApplicationSettings.getViewersMapping(); for (int i=0; i contype = null; - private List contelm = null; - private String selectype = null; // Context type, if selected - private String newtype = null; // Context type, if newed - private SimulationContextType type = null; // Corresponding context type object - private String value = null; // Context value - - private static final long serialVersionUID = -641719644024601042L; - -// ============================================================================================================================== -// Action methods -// ============================================================================================================================== - - public String doInitialize () { -// ----------------------------- - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - - mystudy = getOpenStudy(); - contype = getInvolvedContexts(); - - transax.commit(); - if (contype.isEmpty()) return "create"; - else return "select"; - } - - public String doSelectContext () { -// -------------------------------- - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - try { - mystudy = getOpenStudy(); - int typid = Integer.valueOf(selectype); - if (typid == 0) return "create"; - - SimulationContext.Properties cprop = new SimulationContext.Properties(); - type = SimulationContext.selectType(typid); - newtype = type.getName(); - contype = getInvolvedContexts(); - contelm = Database.selectSimulationContextsWhere(cprop.setType(type)); - - return "set"; - } - finally { - transax.commit(); - } - } - - public String doCreateContext () { -// -------------------------------- - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - try { - mystudy = getOpenStudy(); - if (newtype.length() == 0 || value.length() == 0) return INPUT; - - Step step = mystudy.getSelectedStep(); - ProjectElement owner = step.getOwner(); - - SimulationContext.Properties cprop = new SimulationContext.Properties(); - SimulationContext contex = null; - type = SimulationContext.createType(newtype, step.getStep()); - cprop.setType(type).setValue(value); - if (owner instanceof Study) contex = ((Study)owner).addProjectContext(cprop); // Re-indexes knowledges and the study - else contex = step.addSimulationContext(cprop); // Re-indexes knowledges only - - mystudy.add(contex); - transax.commit(); - return SUCCESS; - } - catch (RuntimeException saverror) { - logger.error("Reason:", saverror); - if (transax != null && transax.isActive()) { -// Second try-catch as the rollback could fail as well - try { - transax.rollback(); - } catch (HibernateException backerror) { - logger.debug("Error rolling back transaction", backerror); - } - } - return ERROR; - } - catch (Exception error) { - transax.commit(); - return INPUT; - } - } - - public String doDeleteContext () { -// -------------------------------- - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - try { - mystudy = getOpenStudy(); - - Step step = mystudy.getSelectedStep(); - ProjectElement owner = step.getOwner(); - SimulationContext context = step.getSimulationContext(Integer.valueOf(myindex)); - if (owner instanceof Study) ((Study)owner).removeProjectContext(context); // Re-indexes knowledges and the study - else step.removeSimulationContext(context); // Re-indexes knowledges only - - mystudy.remove(context); - transax.commit(); - return SUCCESS; - } - catch (RuntimeException saverror) { - logger.error("Reason:", saverror); - if (transax != null && transax.isActive()) { -// Second try-catch as the rollback could fail as well - try { - transax.rollback(); - } catch (HibernateException backerror) { - logger.debug("Error rolling back transaction", backerror); - } - } - return ERROR; - } - } - - public String doSetContext () { -// ----------------------------- - String[] input = value.split(","); - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - try { + private List contype = null; + private List contelm = null; + private String selectype = null; // Context type, if selected + private String newtype = null; // Context type, if newed + private SimulationContextType type = null; // Corresponding context type object + private String value = null; // Context value + private StudyService _studyService; + private StepService _stepService; + + /** + * Get the stepService. + * + * @return the stepService + */ + public StepService getStepService() { + return _stepService; + } + + /** + * Set the stepService. + * + * @param stepService + * the stepService to set + */ + public void setStepService(StepService stepService) { + _stepService = stepService; + } + + private static final long serialVersionUID = -641719644024601042L; + + // ============================================================================================================================== + // Action methods + // ============================================================================================================================== + + public String doInitialize() { + // ----------------------------- + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + mystudy = getOpenStudy(); + contype = getInvolvedContexts(); + + transax.commit(); + if (contype.isEmpty()) + return "create"; + else + return "select"; + } + + public String doSelectContext() { + // -------------------------------- + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + try { + mystudy = getOpenStudy(); + int typid = Integer.valueOf(selectype); + if (typid == 0) + return "create"; + + SimulationContext.Properties cprop = new SimulationContext.Properties(); + type = SimulationContext.selectType(typid); + newtype = type.getName(); + contype = getInvolvedContexts(); + contelm = Database.selectSimulationContextsWhere(cprop + .setType(type)); + + return "set"; + } finally { + transax.commit(); + } + } + + public String doCreateContext() { + // -------------------------------- + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + try { + mystudy = getOpenStudy(); + if (newtype.length() == 0 || value.length() == 0) + return INPUT; + + Step step = mystudy.getSelectedStep(); + ProjectElement owner = step.getOwner(); + + SimulationContext.Properties cprop = new SimulationContext.Properties(); + SimulationContext contex = null; + type = SimulationContext.createType(newtype, step.getStep()); + cprop.setType(type).setValue(value); + if (owner instanceof Study) + contex = getStudyService().addProjectContext(((Study) owner), + cprop); // Re-indexes knowledges and the study + else + contex = getStepService().addSimulationContext(step, cprop); // Re-indexes knowledges only + + mystudy.add(contex); + transax.commit(); + return SUCCESS; + } catch (RuntimeException saverror) { + logger.error("Reason:", saverror); + if (transax != null && transax.isActive()) { + // Second try-catch as the rollback could fail as well + try { + transax.rollback(); + } catch (HibernateException backerror) { + logger.debug("Error rolling back transaction", backerror); + } + } + return ERROR; + } catch (Exception error) { + transax.commit(); + return INPUT; + } + } + + public String doDeleteContext() { + // -------------------------------- + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + try { + mystudy = getOpenStudy(); + + Step step = mystudy.getSelectedStep(); + ProjectElement owner = step.getOwner(); + SimulationContext context = step.getSimulationContext(Integer + .valueOf(myindex)); + if (owner instanceof Study) + getStudyService() + .removeProjectContext(((Study) owner), context); // Re-indexes knowledges and the study + else + getStepService().removeSimulationContext(step, context); // Re-indexes knowledges only + + mystudy.remove(context); + transax.commit(); + return SUCCESS; + } catch (RuntimeException saverror) { + logger.error("Reason:", saverror); + if (transax != null && transax.isActive()) { + // Second try-catch as the rollback could fail as well + try { + transax.rollback(); + } catch (HibernateException backerror) { + logger.debug("Error rolling back transaction", backerror); + } + } + return ERROR; + } + } + + public String doSetContext() { + // ----------------------------- + String[] input = value.split(","); + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + try { + mystudy = getOpenStudy(); + + Step step = mystudy.getSelectedStep(); + ProjectElement owner = step.getOwner(); + SimulationContext contex = null; + + if (input.length == 1 + || (input.length == 2 && input[1].equals(" "))) { + // Setting an existing simulation context identified by value (input = rid," ") + int valid = Integer.valueOf(input[0]); + contex = Database.selectSimulationContext(valid); + if (owner instanceof Study) + getStudyService() + .addProjectContext(((Study) owner), contex); + else + getStepService().addSimulationContext(step, contex); + } else { + // Setting a new simulation context value (input = 0,"new context value") + int typid = Integer.valueOf(selectype); + SimulationContext.Properties cprop = new SimulationContext.Properties(); + cprop.setType(SimulationContext.selectType(typid)).setValue( + input[1].trim()); + if (owner instanceof Study) + contex = getStudyService().addProjectContext( + ((Study) owner), cprop); // Re-indexes knowledges and the study + else + contex = getStepService().addSimulationContext(step, cprop); // Re-indexes knowledges only + } + mystudy.add(contex); + contype = getInvolvedContexts(); + + transax.commit(); + return SUCCESS; + } catch (RuntimeException saverror) { + logger.error("Reason:", saverror); + if (transax != null && transax.isActive()) { + // Second try-catch as the rollback could fail as well + try { + transax.rollback(); + } catch (HibernateException backerror) { + logger.debug("Error rolling back transaction", backerror); + } + } + return ERROR; + } catch (Exception error) { + value = input[0]; + transax.commit(); + return INPUT; + } + } + + // ============================================================================================================================== + // Getters and setters + // ============================================================================================================================== + + public SimulationContextType getContextType() { + // ---------------------------------------------- + return type; + } + + public String getContextName() { + // ------------------------------- + return newtype; + } + + // public List getSimulationContexts () { + // ------------------------------------------------------- + // return mystudy.getSelectedStep().getAllSimulationContexts(); + // } + public List getSimulationContextTypes() { + // --------------------------------------------------------------- + return contype; + } + + public List getSimulationContextValues() { + // ------------------------------------------------------------ + return contelm; + } + + public void setContextType(String type) { + // ---------------------------------------- + this.selectype = type; + } + + public void setContextValue(String value) { + // ----------------------------------------- + this.value = value; + } + + public void setNewType(String name) { + // ------------------------------------ + this.newtype = name; + } + + // ============================================================================================================================== + // Private service + // ============================================================================================================================== + + private List getInvolvedContexts() { + // ---------------------------------------------------------- + SimulationContextType.Properties sprop = new SimulationContextType.Properties() + .setStep(mystudy.getSelectedStep().getStep()); + List contype = SimulationContext + .selectTypesWhere(sprop); + + if (!contype.isEmpty()) { + // Ordering by alphabetical order of localized context types + SimulationContextType[] types = contype + .toArray(new SimulationContextType[contype.size()]); + ContextTypeComparator compare = new ContextTypeComparator(); + boolean state = types[0].isApproved(); + int from = 0; + int to = 0; + while (to < types.length - 1) { + to += 1; + if (types[to].isApproved() == state) + continue; + + if (to > from + 1) + Arrays.sort(types, from, to, compare); + state = !state; + from = to; + } + if (to > from) + Arrays.sort(types, from, to + 1, compare); + contype = Arrays.asList(types); + } + return contype; + } + + /** + * Get the studyService. + * + * @return the studyService + */ + public StudyService getStudyService() { + return _studyService; + } - Step step = mystudy.getSelectedStep(); - ProjectElement owner = step.getOwner(); - SimulationContext contex = null; - - if (input.length == 1 || (input.length == 2 && input[1].equals(" "))) { -// Setting an existing simulation context identified by value (input = rid," ") - int valid = Integer.valueOf(input[0]); - contex = Database.selectSimulationContext(valid); - if (owner instanceof Study) ((Study)owner).addProjectContext(contex); - else step.addSimulationContext(contex); - } - else { -// Setting a new simulation context value (input = 0,"new context value") - int typid = Integer.valueOf(selectype); - SimulationContext.Properties cprop = new SimulationContext.Properties(); - cprop.setType(SimulationContext.selectType(typid)) - .setValue(input[1].trim()); - if (owner instanceof Study) contex = ((Study)owner).addProjectContext(cprop); // Re-indexes knowledges and the study - else contex = step.addSimulationContext(cprop); // Re-indexes knowledges only - } - mystudy.add(contex); - contype = getInvolvedContexts(); - - transax.commit(); - return SUCCESS; - } - catch (RuntimeException saverror) { - logger.error("Reason:", saverror); - if (transax != null && transax.isActive()) { -// Second try-catch as the rollback could fail as well - try { - transax.rollback(); - } catch (HibernateException backerror) { - logger.debug("Error rolling back transaction", backerror); - } - } - return ERROR; - } - catch (Exception error) { - value = input[0]; - transax.commit(); - return INPUT; - } - } - -// ============================================================================================================================== -// Getters and setters -// ============================================================================================================================== - - public SimulationContextType getContextType () { -// ---------------------------------------------- - return type; - } - public String getContextName () { -// ------------------------------- - return newtype; - } -// public List getSimulationContexts () { -// ------------------------------------------------------- -// return mystudy.getSelectedStep().getAllSimulationContexts(); -// } - public List getSimulationContextTypes () { -// --------------------------------------------------------------- - return contype; - } - public List getSimulationContextValues () { -// ------------------------------------------------------------ - return contelm; - } - - public void setContextType (String type) { -// ---------------------------------------- - this.selectype = type; - } - public void setContextValue (String value) { -// ----------------------------------------- - this.value = value; - } - public void setNewType (String name) { -// ------------------------------------ - this.newtype = name; - } - -// ============================================================================================================================== -// Private service -// ============================================================================================================================== - - private List getInvolvedContexts () { -// ---------------------------------------------------------- - SimulationContextType.Properties sprop = new SimulationContextType.Properties() - .setStep(mystudy.getSelectedStep().getStep()); - List contype = SimulationContext.selectTypesWhere(sprop); - - if (!contype.isEmpty()) { -// Ordering by alphabetical order of localized context types - SimulationContextType[] types = contype.toArray( new SimulationContextType[contype.size()] ); - ContextTypeComparator compare = new ContextTypeComparator(); - boolean state = types[0].isApproved(); - int from = 0; - int to = 0; - while (to < types.length-1) { - to += 1; - if (types[to].isApproved() == state) continue; - - if (to > from+1) Arrays.sort(types, from, to, compare); - state = !state; - from = to; - } - if (to > from) Arrays.sort(types, from, to+1, compare); - contype = Arrays.asList(types); - } - return contype; + /** + * Set the studyService. + * + * @param studyService + * the studyService to set + */ + public void setStudyService(StudyService studyService) { + _studyService = studyService; } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/EditStudyAction.java b/Workspace/Siman/src/org/splat/simer/EditStudyAction.java index 0280138..79c4842 100644 --- a/Workspace/Siman/src/org/splat/simer/EditStudyAction.java +++ b/Workspace/Siman/src/org/splat/simer/EditStudyAction.java @@ -3,8 +3,9 @@ package org.splat.simer; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; -import org.splat.som.Database; -import org.splat.som.Study; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.Study; +import org.splat.service.StudyService; public class EditStudyAction extends DisplayStudyStepAction { @@ -13,6 +14,8 @@ public class EditStudyAction extends DisplayStudyStepAction { private enum Execute { publish, promote } + private StudyService _studyService; + // ============================================================================================================================== // Action methods // ============================================================================================================================== @@ -27,8 +30,8 @@ public class EditStudyAction extends DisplayStudyStepAction { Execute todo = Execute.valueOf(action); Study study = mystudy.getStudyObject(); - if (todo == Execute.publish) study.moveToPublic(); - else if (todo == Execute.promote) study.moveToReference(); + if (todo == Execute.publish) getStudyService().moveToPublic(study); + else if (todo == Execute.promote) getStudyService().moveToReference(study); mystudy.getPopup().setContext("study", mystudy.getStudyRights()); // The context has changed // Useless to update the open study @@ -48,4 +51,22 @@ public class EditStudyAction extends DisplayStudyStepAction { return ERROR; } } + /** + * Get the studyService. + * + * @return the studyService + */ + public StudyService getStudyService() { + return _studyService; + } + + /** + * Set the studyService. + * + * @param studyService + * the studyService to set + */ + public void setStudyService(StudyService studyService) { + _studyService = studyService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/FileFacade.java b/Workspace/Siman/src/org/splat/simer/FileFacade.java index 41daac8..740a2a7 100644 --- a/Workspace/Siman/src/org/splat/simer/FileFacade.java +++ b/Workspace/Siman/src/org/splat/simer/FileFacade.java @@ -5,7 +5,7 @@ import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ResourceBundle; -import org.splat.som.ConvertsRelation; +import org.splat.dal.bo.som.ConvertsRelation; public class FileFacade { diff --git a/Workspace/Siman/src/org/splat/simer/ImportDocumentAction.java b/Workspace/Siman/src/org/splat/simer/ImportDocumentAction.java index e792383..3de8369 100644 --- a/Workspace/Siman/src/org/splat/simer/ImportDocumentAction.java +++ b/Workspace/Siman/src/org/splat/simer/ImportDocumentAction.java @@ -13,270 +13,348 @@ import java.util.Vector; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; import org.splat.manox.Reader; import org.splat.manox.Toolbox; -import org.splat.som.Database; -import org.splat.som.Document; -import org.splat.som.ProgressState; -import org.splat.som.ProjectSettings; -import org.splat.som.Publication; -import org.splat.som.DocumentType; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.ProgressState; +import org.splat.service.PublicationService; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.DocumentType; import org.splat.som.Revision; import org.splat.som.Step; - public class ImportDocumentAction extends UploadBaseNextAction { - private List doctypes = null; - private int doctype = 0; - private String docref = null; // Reference extracted from the imported file, if exist - private String docver = ""; // Version number extracted from the imported file, if exist - private String date = ""; // Date extracted from the imported file, if exist - - private static final long serialVersionUID = 2587822564883588556L; - -// ============================================================================================================================== -// Action methods -// ============================================================================================================================== - - public String doInitialize () { -// ----------------------------- - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - User user = getConnectedUser(); - File updir = Database.getDownloadDirectory(user); - File upfile = new File(updir.getPath() + "/" + filename); - String[] table = filename.split("\\x2E"); - String filext = table[table.length-1].toLowerCase(); - - mystudy = getOpenStudy(); - Step step = mystudy.getSelectedStep(); - doctypes = step.getValidDocumentTypes(); - deftype = ApplicationSettings.getDefaultDocumentType(step, filext); - defuses = new Vector(); - state = ProgressState.inWORK; - - Reader tool = Toolbox.getReader(upfile); - if (tool != null) { - String fileref = tool.extractProperty("reference"); - String filever = tool.extractProperty("version"); // Property kept even if the file is not referenced - String filetype = tool.extractProperty("type"); // Property kept even if the file is not referenced - for (Iterator i=doctypes.iterator(); i.hasNext(); ) { - DocumentType type = i.next(); - if (!type.getName().equals(filetype)) continue; - deftype = type; - doctype = type.getIndex(); // Disables the document type field - break; - } - if (fileref != null) { - Document slot = Database.selectDocument(fileref, new Revision().toString()); - if (slot == null) { - setErrorCode("reference.undefined"); - return ERROR; - } - else { - if (!slot.isUndefined()) { - setErrorCode("reference.duplicate"); - return ERROR; - } - docref = fileref; // Disables document name and state fields - deftype = slot.getType(); // Just in case - doctype = deftype.getIndex(); // Disables the document type field - } - } - if (filever != null) try { - Revision.Format get = new Revision.Format(ProjectSettings.getRevisionPattern()); - Revision version = get.parse(filever); - if ( version.isNull() ) throw new ParseException(filever, filever.length()-1); - if (!version.isMinor()) state = ProgressState.inCHECK; - docver = version.toString(); - } catch (ParseException e) { - setErrorCode("format.version"); - return ERROR; - } - docname = tool.extractProperty("title"); // Property kept even if the file is not referenced - date = tool.extractProperty("date"); - if (date != null) { - ResourceBundle locale = ResourceBundle.getBundle("som", ApplicationSettings.getCurrentLocale()); - SimpleDateFormat check = new SimpleDateFormat(locale.getString("date.format")); - try { - check.parse(date); - } catch (ParseException e) { - setErrorCode("format.date"); - return ERROR; - } - } else date = ""; - } else - if (filext.equals("pdf")) state = ProgressState.EXTERN; //TODO: Should external extensions be configurable ? - if (docname == null) { - docname = table[0]; - for (int i=1; i 0) { - ResourceBundle locale = ResourceBundle.getBundle("som", ApplicationSettings.getCurrentLocale()); - SimpleDateFormat get = new SimpleDateFormat(locale.getString("date.format")); - dprop.setDate(get.parse(date)); - } - addoc = step.assignDocument(dprop.setReference(docref).setName(docname)); - updir = addoc.getSourceFile().asFile(); - if (logger.isInfoEnabled()) logger.info("Moving \"" + upfile.getName() + "\" to \"" + updir.getPath() + "\"."); - upfile.renameTo(updir); - try { - if (docver.length() > 0) addoc.saveAs(new Revision(docver)); - else addoc.saveAs(state); - } catch (FileNotFoundException saverror) { - Thread.sleep(1000); - logger.info("Waiting for the file."); - upfile.renameTo(updir); - if (docver.length() > 0) addoc.saveAs(new Revision(docver)); - else addoc.saveAs(state); - } - mystudy.updateSimulationContexts(); // In case of simulation contexts extracted from the imported document - } -// Creation of uses relations - if (docuses != null) { - String[] list = docuses.split(","); - for (int i=0; i getDocumentTypes () { -// --------------------------------------------- - return doctypes; - } - public int getDocumentType () { -// ----------------------------- - return doctype; - } - public String getReference () { -// ----------------------------- - return docref; - } - public String getVersion () { -// --------------------------- - return docver; - } - - public void setDocumentDate (String date) { -// ----------------------------------------- - this.date = date; - } - public void setDocumentName (String name) { -// ----------------------------------------- - this.docname = name; // Name entered by the user if enabled - } - public void setDocumentTitle (String name) { // Called even if DocumentName is enabled -// ----------------------------------------- - if (this.docname == null) this.docname = name; - } - public void setDocumentType (String value) { -// ------------------------------------------ - this.doctype = Integer.valueOf(value); - } - public void setDefaultDocumentState (String state) { // Called even if DocumentState is enabled -// -------------------------------------------------- - if (this.state == null) this.state = ProgressState.valueOf(state); - } - public void setDefaultDocumentType (String value) { // Called even if DocumentType is enabled -// -------------------------------------------------- - if (this.doctype == 0) this.doctype = Integer.valueOf(value); - } - public void setReference (String value) { -// --------------------------------------- - this.docref = value; - } - public void setVersion (String value) { -// ------------------------------------- - this.docver = value; - } + private List doctypes = null; + private int doctype = 0; + private String docref = null; // Reference extracted from the imported file, if exist + private String docver = ""; // Version number extracted from the imported file, if exist + private String date = ""; // Date extracted from the imported file, if exist + private ProjectSettingsService _projectSettingsService; + private PublicationService _publicationService; + + private static final long serialVersionUID = 2587822564883588556L; + + // ============================================================================================================================== + // Action methods + // ============================================================================================================================== + + public String doInitialize() { + // ----------------------------- + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + User user = getConnectedUser(); + File updir = Database.getDownloadDirectory(user); + File upfile = new File(updir.getPath() + "/" + filename); + String[] table = filename.split("\\x2E"); + String filext = table[table.length - 1].toLowerCase(); + + mystudy = getOpenStudy(); + Step step = mystudy.getSelectedStep(); + doctypes = step.getValidDocumentTypes(); + deftype = ApplicationSettings.getDefaultDocumentType(step, filext); + defuses = new Vector(); + state = ProgressState.inWORK; + + Reader tool = Toolbox.getReader(upfile); + if (tool != null) { + String fileref = tool.extractProperty("reference"); + String filever = tool.extractProperty("version"); // Property kept even if the file is not referenced + String filetype = tool.extractProperty("type"); // Property kept even if the file is not referenced + for (Iterator i = doctypes.iterator(); i.hasNext();) { + DocumentType type = i.next(); + if (!type.getName().equals(filetype)) + continue; + deftype = type; + doctype = type.getIndex(); // Disables the document type field + break; + } + if (fileref != null) { + Document slot = Database.selectDocument(fileref, + new Revision().toString()); + if (slot == null) { + setErrorCode("reference.undefined"); + return ERROR; + } else { + if (!slot.isUndefined()) { + setErrorCode("reference.duplicate"); + return ERROR; + } + docref = fileref; // Disables document name and state fields + deftype = slot.getType(); // Just in case + doctype = deftype.getIndex(); // Disables the document type field + } + } + if (filever != null) + try { + Revision.Format get = new Revision.Format( + getProjectSettings().getRevisionPattern()); + Revision version = get.parse(filever); + if (version.isNull()) + throw new ParseException(filever, filever.length() - 1); + if (!version.isMinor()) + state = ProgressState.inCHECK; + docver = version.toString(); + } catch (ParseException e) { + setErrorCode("format.version"); + return ERROR; + } + docname = tool.extractProperty("title"); // Property kept even if the file is not referenced + date = tool.extractProperty("date"); + if (date != null) { + ResourceBundle locale = ResourceBundle.getBundle("som", + ApplicationSettings.getCurrentLocale()); + SimpleDateFormat check = new SimpleDateFormat( + locale.getString("date.format")); + try { + check.parse(date); + } catch (ParseException e) { + setErrorCode("format.date"); + return ERROR; + } + } else + date = ""; + } else if (filext.equals("pdf")) + state = ProgressState.EXTERN; // TODO: Should external extensions be configurable ? + if (docname == null) { + docname = table[0]; + for (int i = 1; i < table.length - 1; i++) + docname = docname + "." + table[i]; + } + if (deftype != null) + setupDefaultUses(deftype); + + DocumentType[] types = doctypes.toArray(new DocumentType[doctypes + .size()]); + DocumentTypeComparator compare = new DocumentTypeComparator(); + Arrays.sort(types, compare); + doctypes = Arrays.asList(types); + + transax.commit(); + return SUCCESS; + } + + public String doImport() { + // ------------------------- + if (action == ToDo.cancel) + return "cancel"; + if (doctype == 0) { + setErrorCode("import.type"); + return ERROR; + } + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + try { + // Getting user inputs + mystudy = getOpenStudy(); + User user = getConnectedUser(); + Step step = mystudy.getSelectedStep(); + DocumentType type = Document.selectType(doctype); + File updir = Database.getDownloadDirectory(user); + File upfile = new File(updir.getPath() + "/" + filename); + String[] table = filename.split("\\x2E"); + + // Creation of the document + Document.Properties dprop = new Document.Properties(); + Publication addoc; + + if (docref.length() == 0) { // Importation of a foreign document + // TODO: Extract property of supported documents (DOCX, ODT...) + addoc = step.createDocument(dprop.setName(docname) + .setType(type).setFormat(table[table.length - 1]) + .setAuthor(user)); + updir = addoc.getSourceFile().asFile(); + if (logger.isInfoEnabled()) + logger.info("Moving \"" + upfile.getName() + "\" to \"" + + updir.getPath() + "\"."); + upfile.renameTo(updir); + try { + getPublicationService().saveAs(addoc, state); // May throw FileNotFound if rename was not done + } catch (FileNotFoundException saverror) { + Thread.sleep(1000); + logger.info("Waiting for the file."); + upfile.renameTo(updir); + getPublicationService().saveAs(addoc, state); // Forget it if throw again FileNotFound + } + } else { // Importation of a previously created template-based document + if (date.length() > 0) { + ResourceBundle locale = ResourceBundle.getBundle("som", + ApplicationSettings.getCurrentLocale()); + SimpleDateFormat get = new SimpleDateFormat( + locale.getString("date.format")); + dprop.setDate(get.parse(date)); + } + addoc = step.assignDocument(dprop.setReference(docref).setName( + docname)); + updir = addoc.getSourceFile().asFile(); + if (logger.isInfoEnabled()) + logger.info("Moving \"" + upfile.getName() + "\" to \"" + + updir.getPath() + "\"."); + upfile.renameTo(updir); + try { + if (docver.length() > 0) + getPublicationService().saveAs(addoc, + new Revision(docver)); + else + getPublicationService().saveAs(addoc, state); + } catch (FileNotFoundException saverror) { + Thread.sleep(1000); + logger.info("Waiting for the file."); + upfile.renameTo(updir); + if (docver.length() > 0) + getPublicationService().saveAs(addoc, + new Revision(docver)); + else + getPublicationService().saveAs(addoc, state); + } + mystudy.updateSimulationContexts(); // In case of simulation contexts extracted from the imported document + } + // Creation of uses relations + if (docuses != null) { + String[] list = docuses.split(","); + for (int i = 0; i < list.length; i++) { + Integer index = Integer.valueOf(list[i].trim()); + Publication used = getPublication(index); + addoc.addDependency(used); + } + } + // Creation of derived the document formats + // Document ndoc = addoc.value(); + // Converter send = ApplicationSettings.getConverter(ndoc.getType(), ndoc.getFormat()); + // + // if (send != null) send.converts(addoc); // Asynchronous process + transax.commit(); + + mystudy.add(addoc); // Updates the presentation + return SUCCESS; + } catch (FileNotFoundException error) { + logger.error("Reason:", error); + setErrorCode("import.file"); + } catch (Exception error) { + logger.error("Reason:", error); + setErrorCode("internal"); + } + if (transax != null && transax.isActive()) { // Probably useless test + // Second try-catch as the rollback could fail as well + try { + transax.rollback(); + } catch (HibernateException backerror) { + logger.debug("Error rolling back transaction", backerror); + } + } + return ERROR; + } + + // ============================================================================================================================== + // Getters and setters + // ============================================================================================================================== + + public String getDocumentDate() { + // -------------------------------- + return date; + } + + public List getDocumentTypes() { + // --------------------------------------------- + return doctypes; + } + + public int getDocumentType() { + // ----------------------------- + return doctype; + } + + public String getReference() { + // ----------------------------- + return docref; + } + + public String getVersion() { + // --------------------------- + return docver; + } + + public void setDocumentDate(String date) { + // ----------------------------------------- + this.date = date; + } + + public void setDocumentName(String name) { + // ----------------------------------------- + this.docname = name; // Name entered by the user if enabled + } + + public void setDocumentTitle(String name) { // Called even if DocumentName is enabled + // ----------------------------------------- + if (this.docname == null) + this.docname = name; + } + + public void setDocumentType(String value) { + // ------------------------------------------ + this.doctype = Integer.valueOf(value); + } + + public void setDefaultDocumentState(String state) { // Called even if DocumentState is enabled + // -------------------------------------------------- + if (this.state == null) + this.state = ProgressState.valueOf(state); + } + + public void setDefaultDocumentType(String value) { // Called even if DocumentType is enabled + // -------------------------------------------------- + if (this.doctype == 0) + this.doctype = Integer.valueOf(value); + } + + public void setReference(String value) { + // --------------------------------------- + this.docref = value; + } + + public void setVersion(String value) { + // ------------------------------------- + this.docver = value; + } + + /** + * Get project settings. + * + * @return Project settings service + */ + private ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + + /** + * Set project settings service. + * + * @param projectSettingsService + * project settings service + */ + public void setProjectSettings(ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } + + /** + * Get the publicationService. + * + * @return the publicationService + */ + public PublicationService getPublicationService() { + return _publicationService; + } + + /** + * Set the publicationService. + * + * @param publicationService + * the publicationService to set + */ + public void setPublicationService(PublicationService publicationService) { + _publicationService = publicationService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/KnowledgeElementFacade.java b/Workspace/Siman/src/org/splat/simer/KnowledgeElementFacade.java index 1b72325..3d4c9ae 100644 --- a/Workspace/Siman/src/org/splat/simer/KnowledgeElementFacade.java +++ b/Workspace/Siman/src/org/splat/simer/KnowledgeElementFacade.java @@ -1,6 +1,6 @@ package org.splat.simer; -import org.splat.som.KnowledgeElement; +import org.splat.dal.bo.som.KnowledgeElement; import org.splat.wapp.PopupMenu; diff --git a/Workspace/Siman/src/org/splat/simer/NewScenarioAction.java b/Workspace/Siman/src/org/splat/simer/NewScenarioAction.java index c873e60..03106d2 100644 --- a/Workspace/Siman/src/org/splat/simer/NewScenarioAction.java +++ b/Workspace/Siman/src/org/splat/simer/NewScenarioAction.java @@ -8,11 +8,13 @@ import java.util.ResourceBundle; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; -import org.splat.som.Database; -import org.splat.som.Publication; -import org.splat.som.Scenario; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.Scenario; +import org.splat.service.ProjectElementService; +import org.splat.service.StudyService; import org.splat.som.Step; -import org.splat.som.Study; +import org.splat.dal.bo.som.Study; import org.splat.wapp.Menu; @@ -25,6 +27,8 @@ public class NewScenarioAction extends Action { private int scindex; private int bastep; private ToDo action; + private StudyService _studyService; + private ProjectElementService _projectElementService; private static final long serialVersionUID = -5586724442986956861L; @@ -45,7 +49,7 @@ public class NewScenarioAction extends Action { myscene = Arrays.asList(scene); scindex = base.getIndex(); mytitle = locale.getString("label.scenario") + " " + String.valueOf(scene.length+1); - bastep = base.getFirstStep().getNumber(); // Better use the last current step ? + bastep = getProjectElementService().getFirstStep(base).getNumber(); // Better use the last current step ? selection = scindex + "." + bastep; action = null; @@ -62,7 +66,7 @@ public class NewScenarioAction extends Action { Scenario[] scene = study.getScenarii(); myscene = Arrays.asList(scene); - bastep = scene[0].getFirstStep().getNumber(); // All scenarios have the same first step number + bastep = getProjectElementService().getFirstStep(scene[0]).getNumber(); // All scenarios have the same first step number action = null; getMenu("scenario").selects(selection); @@ -91,12 +95,12 @@ public class NewScenarioAction extends Action { Step[] step = null; Scenario.Properties sprop = new Scenario.Properties().setManager(getConnectedUser()).setTitle(mytitle).setInsertAfter(bascene); - bastep = bascene.getFirstStep().getNumber(); + bastep = getProjectElementService().getFirstStep(bascene).getNumber(); if (this.sharesStep()) { - step = bascene.getSteps(); + step = getProjectElementService().getSteps(bascene); sprop.setBaseStep(step[number-bastep]); } - bascene = study.addScenario(sprop); + bascene = getStudyService().addScenario(study, sprop); transax.commit(); // Update of the display @@ -175,4 +179,41 @@ public class NewScenarioAction extends Action { // ---------------------------- return (Integer.valueOf(getSharedStep()) > bastep); } + /** + * Get the studyService. + * + * @return the studyService + */ + public StudyService getStudyService() { + return _studyService; + } + + /** + * Set the studyService. + * + * @param studyService + * the studyService to set + */ + public void setStudyService(StudyService studyService) { + _studyService = studyService; + } + /** + * Get the projectElementService. + * + * @return the projectElementService + */ + public ProjectElementService getProjectElementService() { + return _projectElementService; + } + + /** + * Set the projectElementService. + * + * @param projectElementService + * the projectElementService to set + */ + public void setProjectElementService( + ProjectElementService projectElementService) { + _projectElementService = projectElementService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/NewScenarioMenu.java b/Workspace/Siman/src/org/splat/simer/NewScenarioMenu.java index 1577846..f5914a6 100644 --- a/Workspace/Siman/src/org/splat/simer/NewScenarioMenu.java +++ b/Workspace/Siman/src/org/splat/simer/NewScenarioMenu.java @@ -3,134 +3,198 @@ package org.splat.simer; import java.util.Iterator; import java.util.Vector; -import org.splat.som.Scenario; +import org.splat.dal.bo.som.Scenario; +import org.splat.service.ProjectElementService; +import org.splat.service.ScenarioService; import org.splat.som.Step; -import org.splat.som.Study; +import org.splat.dal.bo.som.Study; import org.splat.wapp.MenuItem; import org.splat.wapp.SlidMenu; - public class NewScenarioMenu extends SlidMenu { - private Study study; - private Scenario scopen; // Currently "open" scenario - private Step stopen; // Currently selected step - -// ============================================================================================================================== -// Constructor -// ============================================================================================================================== - - public NewScenarioMenu (Study context) { -// ----------------------------------- - super("scenarii", "study"); - study = context; - scopen = null; - } - -// ============================================================================================================================== -// Member functions -// ============================================================================================================================== - - public void selects (String name) { -// --------------------------------- - String[] parse = name.split("\\x2E"); - Scenario[] scenes = study.getScenarii(); - Scenario scenew = scopen; - int askid = 0; - -// Initialization - if (scenew == null && scenes.length == 1) scenew = scenes[0]; - try { - int askdex = Integer.valueOf(parse[0]); - if (askdex > 0) { - while (askid < scenes.length) { - if (scenes[askid].getIndex() == askdex) break; - askid += 1; - } - scenew = scenes[askid]; // Throws an exception if the scenario does not exist (that is, if name is not correct) - } - } catch (Exception error) { - return; - } - if (scenew == null) { - -// Study with several scenarii, non of them open - -// Collection of steps to be displayed - Vector steps = new Vector(); - Step[] newstep = scenes[0].getSteps(); // All scenarii have the same steps - - for (int i=0; i i=steps.iterator(); i.hasNext(); ) { - Step step = i.next(); - int number = step.getNumber(); - Scenario group = (Scenario)step.getOwner(); // The menu includes first scenario steps only - int index = group.getIndex(); - String value = index + "." + number; - String icon; - if (group.isEmpty()) icon = "icon.empty.png"; -// else if (group.isFinished()) icon = "icon.checked.png"; - else icon = "icon.done.png"; - addGroup(value, group.getTitle(), icon, "select-step?selection=" + value + "&title=%{title}"); - } - } else - if (scopen == null || !scenew.equals(scopen)) { - -// Opening a scenario - this.clear(); -// Collection of steps to be displayed - Vector steps = new Vector(); - Step[] newstep = scenew.getSteps(); - - for (int i=0; i-1; i--) steps.add(0, scenes[i].getFirstStep()); - newstep = study.getSteps(); - for (int i=askid+1; i i=steps.iterator(); i.hasNext(); ) { - Step step = i.next(); - int number = step.getNumber(); - String icon; - if (!step.isStarted()) icon = "icon.empty.png"; - else if (step.isFinished()) icon = "icon.checked.png"; - else icon = "icon.done.png"; - if (number == askdex) stopen = step; - Scenario group = (Scenario)step.getOwner(); - int index = group.getIndex(); - String value = index + "." + number; - if (index != scenew.getIndex()) { - if (group.isEmpty()) icon = "icon.empty.png"; -// else if (group.isFinished()) icon = "icon.checked.png"; - else icon = "icon.done.png"; - addGroup(value, group.getTitle(), icon, "select-step?selection=" + value + "&title=%{title}"); - } else if (first) { - addGroup(value, scenew.getTitle(), icon, "select-step?selection=" + value + "&title=%{title}"); - first = false; - } else { - addSubItem(value, "menu.step." + number, icon, "select-step?selection=" + value + "&title=%{title}"); - } - } - scopen = scenew; - } - super.selects(name); - } - - public void refreshSelectedItem () { -// ---------------------------------- - MenuItem item = this.getSelectedItem(); - String icon; - if (!stopen.isStarted()) icon = "icon.empty.png"; - else if (stopen.isFinished()) icon = "icon.checked.png"; - else icon = "icon.done.png"; - item.icon(icon); - } + private Study study; + private Scenario scopen; // Currently "open" scenario + private Step stopen; // Currently selected step + private ProjectElementService _projectElementService; + private ScenarioService _scenarioService; + + // ============================================================================================================================== + // Constructor + // ============================================================================================================================== + + public NewScenarioMenu(Study context) { + // ----------------------------------- + super("scenarii", "study"); + study = context; + scopen = null; + } + + // ============================================================================================================================== + // Member functions + // ============================================================================================================================== + + public void selects(String name) { + // --------------------------------- + String[] parse = name.split("\\x2E"); + Scenario[] scenes = study.getScenarii(); + Scenario scenew = scopen; + int askid = 0; + + // Initialization + if (scenew == null && scenes.length == 1) + scenew = scenes[0]; + try { + int askdex = Integer.valueOf(parse[0]); + if (askdex > 0) { + while (askid < scenes.length) { + if (scenes[askid].getIndex() == askdex) + break; + askid += 1; + } + scenew = scenes[askid]; // Throws an exception if the scenario does not exist (that is, if name is not correct) + } + } catch (Exception error) { + return; + } + if (scenew == null) { + + // Study with several scenarii, non of them open + + // Collection of steps to be displayed + Vector steps = new Vector(); + Step[] newstep = getProjectElementService().getSteps(scenes[0]); // All scenarii have the same steps + + for (int i = 0; i < scenes.length; i++) + steps.add(getProjectElementService().getFirstStep(scenes[i])); + newstep = getProjectElementService().getSteps(study); + stopen = newstep[0]; // Default selected step + + // Creation of the menu + for (Iterator i = steps.iterator(); i.hasNext();) { + Step step = i.next(); + int number = step.getNumber(); + Scenario group = (Scenario) step.getOwner(); // The menu includes first scenario steps only + int index = group.getIndex(); + String value = index + "." + number; + String icon; + if (getScenarioService().isEmpty(group)) + icon = "icon.empty.png"; + // else if (group.isFinished()) icon = "icon.checked.png"; + else + icon = "icon.done.png"; + addGroup(value, group.getTitle(), icon, + "select-step?selection=" + value + "&title=%{title}"); + } + } else if (scopen == null || !scenew.equals(scopen)) { + + // Opening a scenario + this.clear(); + // Collection of steps to be displayed + Vector steps = new Vector(); + Step[] newstep = getProjectElementService().getSteps(scenew); + + for (int i = 0; i < newstep.length; i++) { + steps.add(newstep[i]); + } + for (int i = askid - 1; i > -1; i--) + steps.add(0, getProjectElementService().getFirstStep(scenes[i])); + newstep = getProjectElementService().getSteps(study); + for (int i = askid + 1; i < scenes.length; i++) + steps.add(getProjectElementService().getFirstStep(scenes[i])); + + // Creation of the menu + boolean first = true; // For differentiating the first scenario step + int askdex = Integer.valueOf(parse[1]); + for (Iterator i = steps.iterator(); i.hasNext();) { + Step step = i.next(); + int number = step.getNumber(); + String icon; + if (!step.isStarted()) + icon = "icon.empty.png"; + else if (step.isFinished()) + icon = "icon.checked.png"; + else + icon = "icon.done.png"; + if (number == askdex) + stopen = step; + Scenario group = (Scenario) step.getOwner(); + int index = group.getIndex(); + String value = index + "." + number; + if (index != scenew.getIndex()) { + if (getScenarioService().isEmpty(group)) + icon = "icon.empty.png"; + // else if (group.isFinished()) icon = "icon.checked.png"; + else + icon = "icon.done.png"; + addGroup(value, group.getTitle(), icon, + "select-step?selection=" + value + + "&title=%{title}"); + } else if (first) { + addGroup(value, scenew.getTitle(), icon, + "select-step?selection=" + value + + "&title=%{title}"); + first = false; + } else { + addSubItem(value, "menu.step." + number, icon, + "select-step?selection=" + value + + "&title=%{title}"); + } + } + scopen = scenew; + } + super.selects(name); + } + + public void refreshSelectedItem() { + // ---------------------------------- + MenuItem item = this.getSelectedItem(); + String icon; + if (!stopen.isStarted()) + icon = "icon.empty.png"; + else if (stopen.isFinished()) + icon = "icon.checked.png"; + else + icon = "icon.done.png"; + item.icon(icon); + } + + /** + * Get the scenarioService. + * + * @return the scenarioService + */ + public ScenarioService getScenarioService() { + return _scenarioService; + } + + /** + * Set the scenarioService. + * + * @param scenarioService + * the scenarioService to set + */ + public void setScenarioService(ScenarioService scenarioService) { + _scenarioService = scenarioService; + } + + /** + * Get the projectElementService. + * + * @return the projectElementService + */ + public ProjectElementService getProjectElementService() { + return _projectElementService; + } + + /** + * Set the projectElementService. + * + * @param projectElementService + * the projectElementService to set + */ + public void setProjectElementService( + ProjectElementService projectElementService) { + _projectElementService = projectElementService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/NewStudyAction.java b/Workspace/Siman/src/org/splat/simer/NewStudyAction.java index ca76f5e..22f4a0b 100644 --- a/Workspace/Siman/src/org/splat/simer/NewStudyAction.java +++ b/Workspace/Siman/src/org/splat/simer/NewStudyAction.java @@ -6,131 +6,162 @@ import java.util.ResourceBundle; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; -import org.splat.som.Database; -import org.splat.som.Scenario; -import org.splat.som.SimulationContext; -import org.splat.som.SimulationContextType; -import org.splat.som.Study; - +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; +import org.splat.dal.bo.som.Study; +import org.splat.service.StudyService; public class NewStudyAction extends Action { - private String title = null; + private String title = null; private List contelm = null; - private String context = null; - - private static int number = 0; - private static final long serialVersionUID = 693943641800113782L; - -// ============================================================================================================================== -// Action methods -// ============================================================================================================================== - - public String doInitialize () { -// ----------------------------- - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - - SimulationContext.Properties cprop = new SimulationContext.Properties(); - SimulationContextType product = SimulationContext.selectType("product"); - ResourceBundle locale = ResourceBundle.getBundle("labels", ApplicationSettings.getCurrentLocale()); - - contelm = Database.selectSimulationContextsWhere(cprop.setType(product)); - title = locale.getString("label.study") + " " + String.valueOf(number + 1); - transax.commit(); - return SUCCESS; - } - - public String doCreate () throws Exception { -// ------------------------- - String[] input = context.split(","); - int valid = Integer.valueOf(input[0]); - String value = ""; // input[1] if exists - - Session session = Database.getSession(); - Transaction transax = session.beginTransaction(); - Study.Properties sprop = new Study.Properties(); - -// Check arguments and creation of the study - try { - if (valid == -1) throw new Exception(); - if (valid == 0) { - value = input[1].trim(); - if (value.length() == 0) return INPUT; // No need to reinitialize the list of existing products - } - sprop.setTitle(title).setManager(getConnectedUser()); - sprop.checkValidity(); - sprop.disableCheck(); - } - catch (Exception error) { - SimulationContext.Properties cprop = new SimulationContext.Properties(); - SimulationContextType product = SimulationContext.selectType("product"); - contelm = Database.selectSimulationContextsWhere(cprop.setType(product)); - transax.commit(); - return INPUT; // Title empty, simply wait for input without error message - } - try { - Study study = Database.createStudy(sprop); - -// Addition of a default scenario - ResourceBundle locale = ResourceBundle.getBundle("labels", ApplicationSettings.getCurrentLocale()); - Scenario.Properties oprop = new Scenario.Properties(); - oprop.setTitle(locale.getString("label.scenario") + " 1"); - study.addScenario(oprop); - -// Addition of the entered project context - if (valid == 0) { // Input of new project context - SimulationContext.Properties cprop = new SimulationContext.Properties(); - cprop.setType(SimulationContext.selectType("product")).setValue(value); - study.addProjectContext(cprop); + private String context = null; + + private static int number = 0; + private static final long serialVersionUID = 693943641800113782L; + + private StudyService _studyService; + + // ============================================================================================================================== + // Action methods + // ============================================================================================================================== + + public String doInitialize() { + // ----------------------------- + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + + SimulationContext.Properties cprop = new SimulationContext.Properties(); + SimulationContextType product = SimulationContext.selectType("product"); + ResourceBundle locale = ResourceBundle.getBundle("labels", + ApplicationSettings.getCurrentLocale()); + + contelm = Database + .selectSimulationContextsWhere(cprop.setType(product)); + title = locale.getString("label.study") + " " + + String.valueOf(number + 1); + transax.commit(); + return SUCCESS; + } + + public String doCreate() throws Exception { + // ------------------------- + String[] input = context.split(","); + int valid = Integer.valueOf(input[0]); + String value = ""; // input[1] if exists + + Session session = Database.getSession(); + Transaction transax = session.beginTransaction(); + Study.Properties sprop = new Study.Properties(); + + // Check arguments and creation of the study + try { + if (valid == -1) + throw new Exception(); + if (valid == 0) { + value = input[1].trim(); + if (value.length() == 0) + return INPUT; // No need to reinitialize the list of existing products + } + sprop.setTitle(title).setManager(getConnectedUser()); + sprop.checkValidity(); + sprop.disableCheck(); + } catch (Exception error) { + SimulationContext.Properties cprop = new SimulationContext.Properties(); + SimulationContextType product = SimulationContext + .selectType("product"); + contelm = Database.selectSimulationContextsWhere(cprop + .setType(product)); + transax.commit(); + return INPUT; // Title empty, simply wait for input without error message } - else { // Selection of existing project context - SimulationContext context = Database.selectSimulationContext(valid); - study.addProjectContext(context); + try { + Study study = getStudyService().createStudy(sprop); + + // Addition of a default scenario + ResourceBundle locale = ResourceBundle.getBundle("labels", + ApplicationSettings.getCurrentLocale()); + Scenario.Properties oprop = new Scenario.Properties(); + oprop.setTitle(locale.getString("label.scenario") + " 1"); + getStudyService().addScenario(study, oprop); + + // Addition of the entered project context + if (valid == 0) { // Input of new project context + SimulationContext.Properties cprop = new SimulationContext.Properties(); + cprop.setType(SimulationContext.selectType("product")) + .setValue(value); + getStudyService().addProjectContext(study, cprop); + } else { // Selection of existing project context + SimulationContext context = Database + .selectSimulationContext(valid); + getStudyService().addProjectContext(study, context); + } + // Update of the session + number += 1; + open(study); // Opens the study, + transax.commit(); + return SUCCESS; + } catch (Exception error) { + logger.error("Unable to save the study, reason:", error); + if (transax != null && transax.isActive()) { + // Second try-catch as the rollback could fail as well + try { + transax.rollback(); + } catch (HibernateException backerror) { + logger.debug("Error rolling back transaction", backerror); + } + } + return ERROR; } -// Update of the session - number += 1; - open(study); // Opens the study, - transax.commit(); - return SUCCESS; - } - catch (Exception error) { - logger.error("Unable to save the study, reason:", error); - if (transax != null && transax.isActive()) { -// Second try-catch as the rollback could fail as well - try { - transax.rollback(); - } catch (HibernateException backerror) { - logger.debug("Error rolling back transaction", backerror); - } - } - return ERROR; - } } -// ============================================================================================================================== -// Getters and setters -// ============================================================================================================================== - - public String getProjectContext () { -// ---------------------------------- - return context; - } - public List getProjectContextValues () { -// --------------------------------------------------------- - return contelm; - } - public String getTitle () { -// ---------------------------- - return title; - } - - public void setProjectContext (String value) { -// -------------------------------------------- - this.context = value; - } - public void setTitle (String value) { -// ----------------------------------- - this.title = value; - } + // ============================================================================================================================== + // Getters and setters + // ============================================================================================================================== + + public String getProjectContext() { + // ---------------------------------- + return context; + } + + public List getProjectContextValues() { + // --------------------------------------------------------- + return contelm; + } + + public String getTitle() { + // ---------------------------- + return title; + } + + public void setProjectContext(String value) { + // -------------------------------------------- + this.context = value; + } + + public void setTitle(String value) { + // ----------------------------------- + this.title = value; + } + + /** + * Get the studyService. + * + * @return the studyService + */ + public StudyService getStudyService() { + return _studyService; + } + + /** + * Set the studyService. + * + * @param studyService + * the studyService to set + */ + public void setStudyService(StudyService studyService) { + _studyService = studyService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/OpenKnowledge.java b/Workspace/Siman/src/org/splat/simer/OpenKnowledge.java index c454f7f..6e5a903 100644 --- a/Workspace/Siman/src/org/splat/simer/OpenKnowledge.java +++ b/Workspace/Siman/src/org/splat/simer/OpenKnowledge.java @@ -7,152 +7,195 @@ import java.util.List; import java.util.ResourceBundle; import java.util.Vector; -import org.splat.som.KnowledgeElement; -import org.splat.som.ProgressState; -import org.splat.som.Scenario; -import org.splat.som.SimulationContext; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.service.ProjectElementService; import org.splat.som.Step; import org.splat.wapp.SimpleMenu; - public class OpenKnowledge extends OpenObject { - private KnowledgeElement myknelm ; - private String credate; + private KnowledgeElement myknelm; + private String credate; + private ProjectElementService _projectElementService; public class Menu extends SimpleMenu { -// ------------------------------------ - public Menu (List context) - { - super("steps", "study"); - int i = 0; - int j = 0; - for (Iterator k=context.iterator(); k.hasNext(); i++) { - Step step = k.next(); - int number = step.getNumber(); - String icon; - if (step.mayContain(KnowledgeElement.class)) j = i + 1; // Steps are numbered from 1 to N - if (!step.isStarted()) icon = "icon.empty.png"; - else icon = "icon.done.png"; - addItem(String.valueOf(number), "folder.step." + number, icon, "step-knowledge?selection=" + number); - } - this.selects(String.valueOf(j)); - } - } - -// ============================================================================================================================== -// Constructor -// ============================================================================================================================== - - public OpenKnowledge (KnowledgeElement knelm) { -// --------------------------------------------- - ResourceBundle label = ResourceBundle.getBundle("labels", ApplicationSettings.getCurrentLocale()); - ResourceBundle custom = ResourceBundle.getBundle("som", ApplicationSettings.getCurrentLocale()); - SimpleDateFormat convert = new SimpleDateFormat(custom.getString("date.format")); - StringBuffer value = new StringBuffer(knelm.getValue()); - Scenario scene = knelm.getOwnerScenario(); - - myknelm = knelm; - -// Preparation of the display - credate = convert.format(myknelm.getDate()); - involving = getAllSteps(knelm.getOwnerScenario()); - context = new ArrayList(); - menu = new Menu(getInvolvedSteps()); - selection = menu.getSelection(); // The default selection is set in the menu definition - int index = Integer.valueOf(selection); - for (Iterator i=involving.iterator(); i.hasNext();) { - Step next = i.next(); - if (next.getNumber() == index) ustep = next; - for (Iterator j=next.getAllSimulationContexts().iterator(); j.hasNext(); ) { - context.add( new SimulationContextFacade(j.next()) ); - } - } - value.append("

") - .append("").append(label.getString("label.source")).append(":") - .append("
").append(label.getString("label.study")).append(" \"").append(scene.getOwnerStudy().getTitle()).append("\",") - .append("
").append(scene.getTitle()).append(".") - .append("

"); - description = value.toString(); - setupContents(); // Initializes documents and knowledge at ustep - } - -// ============================================================================================================================== -// Public member functions -// ============================================================================================================================== - - public String getAuthorName() { -// ----------------------------- - return myknelm.getAuthor().toString(); - } - public String getDate () { -// ------------------------ - return credate; - } - public Integer getIndex() { -// ------------------------- - return myknelm.getIndex(); - } - public Menu getMenu () { -// ---------------------- - return (Menu)menu; - } - public ProgressState getProgressState() { -// --------------------------------------- - return myknelm.getProgressState(); - } - public String getReference() { -// ---------------------------- - return myknelm.getReference(); - } - public KnowledgeElement getKnowledgeObject () { -// --------------------------------------------- - return myknelm; - } - public String getTitle() { -// ------------------------ - return myknelm.getTitle(); - } - public String getType () { -// ------------------------ - return ResourceBundle.getBundle("som", ApplicationSettings.getCurrentLocale()).getString("type.knowledge." + myknelm.getType().getName()); - } - - public void setSelection (String step) { -// -------------------------------------- - selection = step; - int index = Integer.valueOf(selection); - for (Iterator i=involving.iterator(); i.hasNext();) { - ustep = i.next(); - if (ustep.getNumber() == index) break; - } - menu.selects(selection); - setupContents(); // The contents may have changed even if the selection is the same - } - -// ============================================================================================================================== -// Private services -// ============================================================================================================================== - - private List getAllSteps (Scenario scenar) { -// ------------------------------------------------ - Vector result = new Vector(); - Step[] step = scenar.getSteps(); - - int base = step[0].getNumber(); - int last = step[step.length-1].getNumber(); - for (int i=0; i-1; i--) { - if(step[i].getNumber() >= base) continue; - result.add(0, step[i]); - } - for (int i=0; i context) { + super("steps", "study"); + int i = 0; + int j = 0; + for (Iterator k = context.iterator(); k.hasNext(); i++) { + Step step = k.next(); + int number = step.getNumber(); + String icon; + if (step.mayContain(KnowledgeElement.class)) + j = i + 1; // Steps are numbered from 1 to N + if (!step.isStarted()) + icon = "icon.empty.png"; + else + icon = "icon.done.png"; + addItem(String.valueOf(number), "folder.step." + number, icon, + "step-knowledge?selection=" + number); + } + this.selects(String.valueOf(j)); + } + } + + // ============================================================================================================================== + // Constructor + // ============================================================================================================================== + + public OpenKnowledge(KnowledgeElement knelm) { + // --------------------------------------------- + ResourceBundle label = ResourceBundle.getBundle("labels", + ApplicationSettings.getCurrentLocale()); + ResourceBundle custom = ResourceBundle.getBundle("som", + ApplicationSettings.getCurrentLocale()); + SimpleDateFormat convert = new SimpleDateFormat( + custom.getString("date.format")); + StringBuffer value = new StringBuffer(knelm.getValue()); + Scenario scene = knelm.getOwnerScenario(); + + myknelm = knelm; + + // Preparation of the display + credate = convert.format(myknelm.getDate()); + involving = getAllSteps(knelm.getOwnerScenario()); + context = new ArrayList(); + menu = new Menu(getInvolvedSteps()); + selection = menu.getSelection(); // The default selection is set in the menu definition + int index = Integer.valueOf(selection); + for (Iterator i = involving.iterator(); i.hasNext();) { + Step next = i.next(); + if (next.getNumber() == index) + ustep = next; + for (Iterator j = next + .getAllSimulationContexts().iterator(); j.hasNext();) { + context.add(new SimulationContextFacade(j.next())); + } + } + value.append("

").append("") + .append(label.getString("label.source")).append(":") + .append("
").append(label.getString("label.study")) + .append(" \"").append(scene.getOwnerStudy().getTitle()) + .append("\",").append("
").append(scene.getTitle()) + .append(".").append("

"); + description = value.toString(); + setupContents(); // Initializes documents and knowledge at ustep + } + + // ============================================================================================================================== + // Public member functions + // ============================================================================================================================== + + public String getAuthorName() { + // ----------------------------- + return myknelm.getAuthor().toString(); + } + + public String getDate() { + // ------------------------ + return credate; + } + + public Integer getIndex() { + // ------------------------- + return myknelm.getIndex(); + } + + public Menu getMenu() { + // ---------------------- + return (Menu) menu; + } + + public ProgressState getProgressState() { + // --------------------------------------- + return myknelm.getProgressState(); + } + + public String getReference() { + // ---------------------------- + return myknelm.getReference(); + } + + public KnowledgeElement getKnowledgeObject() { + // --------------------------------------------- + return myknelm; + } + + public String getTitle() { + // ------------------------ + return myknelm.getTitle(); + } + + public String getType() { + // ------------------------ + return ResourceBundle.getBundle("som", + ApplicationSettings.getCurrentLocale()).getString( + "type.knowledge." + myknelm.getType().getName()); + } + + public void setSelection(String step) { + // -------------------------------------- + selection = step; + int index = Integer.valueOf(selection); + for (Iterator i = involving.iterator(); i.hasNext();) { + ustep = i.next(); + if (ustep.getNumber() == index) + break; + } + menu.selects(selection); + setupContents(); // The contents may have changed even if the selection is the same + } + + // ============================================================================================================================== + // Private services + // ============================================================================================================================== + + private List getAllSteps(Scenario scenar) { + // ------------------------------------------------ + Vector result = new Vector(); + Step[] step = getProjectElementService().getSteps(scenar); + + int base = step[0].getNumber(); + int last = step[step.length - 1].getNumber(); + for (int i = 0; i < step.length; i++) { + result.add(step[i]); + } + step = getProjectElementService().getSteps(scenar.getOwnerStudy()); + for (int i = step.length - 1; i > -1; i--) { + if (step[i].getNumber() >= base) + continue; + result.add(0, step[i]); + } + for (int i = 0; i < step.length; i++) { + if (step[i].getNumber() <= last) + continue; + result.add(step[i]); + } + return result; + } + + /** + * Get the projectElementService. + * + * @return the projectElementService + */ + public ProjectElementService getProjectElementService() { + return _projectElementService; + } + + /** + * Set the projectElementService. + * + * @param projectElementService + * the projectElementService to set + */ + public void setProjectElementService( + ProjectElementService projectElementService) { + _projectElementService = projectElementService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/OpenObject.java b/Workspace/Siman/src/org/splat/simer/OpenObject.java index 06b75ea..92905fd 100644 --- a/Workspace/Siman/src/org/splat/simer/OpenObject.java +++ b/Workspace/Siman/src/org/splat/simer/OpenObject.java @@ -5,15 +5,15 @@ import java.util.Iterator; import java.util.List; import java.util.Vector; -import org.splat.kernel.User; -import org.splat.som.Document; -import org.splat.som.DocumentType; -import org.splat.som.KnowledgeElement; -import org.splat.som.KnowledgeElementType; -import org.splat.som.ProgressState; -import org.splat.som.Proxy; -import org.splat.som.Publication; -import org.splat.som.Scenario; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.KnowledgeElementType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.service.dto.Proxy; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.Scenario; import org.splat.som.Step; import org.splat.wapp.Menu; import org.splat.wapp.PopupMenu; diff --git a/Workspace/Siman/src/org/splat/simer/OpenStudy.java b/Workspace/Siman/src/org/splat/simer/OpenStudy.java index d869148..30f40f5 100644 --- a/Workspace/Siman/src/org/splat/simer/OpenStudy.java +++ b/Workspace/Siman/src/org/splat/simer/OpenStudy.java @@ -18,22 +18,23 @@ import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.splat.kernel.Do; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; import org.splat.manox.Toolbox; import org.splat.manox.Writer; -import org.splat.som.Database; -import org.splat.som.Document; -import org.splat.som.DocumentType; -import org.splat.som.KnowledgeElement; -import org.splat.som.ProgressState; -import org.splat.som.ProjectSettings; -import org.splat.som.Publication; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.ProgressState; +import org.splat.service.ProjectElementService; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.dal.bo.som.Publication; import org.splat.som.Revision; -import org.splat.som.Scenario; -import org.splat.som.SimulationContext; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.SimulationContext; import org.splat.som.Step; import org.splat.som.StepRights; -import org.splat.som.Study; +import org.splat.dal.bo.som.Study; import org.splat.som.StudyRights; import org.splat.wapp.ToolBar; @@ -47,6 +48,8 @@ public class OpenStudy extends OpenObject implements OpenStudyServices { private String credate; private String lasdate; private Publication selecdoc; + private ProjectSettingsService _projectSettingsService; + private ProjectElementService _projectElementService; protected final static Logger logger = org.splat.simer.Action.logger; @@ -58,7 +61,7 @@ public class OpenStudy extends OpenObject implements OpenStudyServices { // ----------------------------------------- ResourceBundle custom = ResourceBundle.getBundle("som", ApplicationSettings.getCurrentLocale()); SimpleDateFormat datstring = new SimpleDateFormat(custom.getString("date.format")); - Revision.Format verstring = new Revision.Format(ProjectSettings.getRevisionPattern()); + Revision.Format verstring = new Revision.Format(getProjectSettings().getRevisionPattern()); cuser = user; // May be null if nobody connected mystudy = study; @@ -72,7 +75,7 @@ public class OpenStudy extends OpenObject implements OpenStudyServices { description = mystudy.getDescription(); involving = new ArrayList(1); context = new ArrayList(); - ustep = mystudy.getFirstStep(); + ustep = getProjectElementService().getFirstStep(mystudy); ustep.setActor(cuser); involving.add(ustep); for (Iterator i=ustep.getAllSimulationContexts().iterator(); i.hasNext(); ) { @@ -290,10 +293,20 @@ public class OpenStudy extends OpenObject implements OpenStudyServices { protected void add (KnowledgeElement kelm) { // ------------------------------------------ KnowledgeElementFacade facade = new KnowledgeElementFacade(kelm); - KnowledgeIterator known = knowledge.get(kelm.getType().getIndex() - 2); +//RKV KnowledgeIterator known = knowledge.get(kelm.getType().getIndex() - 2); // Knowledges are ordered by type index, from 0 to n-1, the first one being reserved (reason for -2) - knowpres.put(kelm.getIndex(), facade); - known.list.add(facade); // Insert the new knowledge at the end of the corresponding knowledge type + //RKV:Begin: Find a knowledge iterator for appropriate knowledge type + KnowledgeIterator known = null; + for (KnowledgeIterator aKnowledgeSection : knowledge) { + if (aKnowledgeSection.getIndex().equals(String.valueOf(kelm.getType().getIndex()))) { + known = aKnowledgeSection; + break; + } + } + if (known != null) { //RKV:End + knowpres.put(kelm.getIndex(), facade); + known.list.add(facade); // Insert the new knowledge at the end of the corresponding knowledge type + } } protected void remove (Publication doctag) { @@ -389,17 +402,53 @@ public class OpenStudy extends OpenObject implements OpenStudyServices { scenar = branch[i]; if (scenar.getIndex() == major) break; // Supposed exist } - step = scenar.getSteps(); + step = getProjectElementService().getSteps(scenar); base = step[0].getNumber() - 1; for (int i=0; i+base-1; i--) { Step firstep = step[i]; if(firstep.getNumber() > base) continue; involving.add(0, firstep); } } + + /** + * Get project settings. + * @return Project settings service + */ + private ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + + /** + * Set project settings service. + * @param projectSettingsService project settings service + */ + public void setProjectSettings( + ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } + /** + * Get the projectElementService. + * + * @return the projectElementService + */ + public ProjectElementService getProjectElementService() { + return _projectElementService; + } + + /** + * Set the projectElementService. + * + * @param projectElementService + * the projectElementService to set + */ + public void setProjectElementService( + ProjectElementService projectElementService) { + _projectElementService = projectElementService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/OpenStudyServices.java b/Workspace/Siman/src/org/splat/simer/OpenStudyServices.java index b21cd24..6334e8a 100644 --- a/Workspace/Siman/src/org/splat/simer/OpenStudyServices.java +++ b/Workspace/Siman/src/org/splat/simer/OpenStudyServices.java @@ -1,8 +1,8 @@ package org.splat.simer; import java.net.URL; -import org.splat.kernel.User; -import org.splat.som.Publication; +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.Publication; public interface OpenStudyServices { diff --git a/Workspace/Siman/src/org/splat/simer/SearchBaseAction.java b/Workspace/Siman/src/org/splat/simer/SearchBaseAction.java index bb24e0a..6258bbf 100644 --- a/Workspace/Siman/src/org/splat/simer/SearchBaseAction.java +++ b/Workspace/Siman/src/org/splat/simer/SearchBaseAction.java @@ -9,14 +9,14 @@ import org.hibernate.Session; import org.hibernate.Transaction; import org.splat.kernel.InvalidPropertyException; import org.splat.kernel.Name; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; import org.splat.kernel.UserDirectory; import org.splat.som.ApplicationRights; -import org.splat.som.Database; -import org.splat.som.ProjectSettings; -import org.splat.som.Proxy; -import org.splat.som.SimulationContext; -import org.splat.som.SimulationContextType; +import org.splat.dal.dao.som.Database; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.service.dto.Proxy; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; public abstract class SearchBaseAction extends Action { @@ -208,7 +208,7 @@ public abstract class SearchBaseAction extends Action { // Ordering by alphabetical order of localized context types SimulationContextType[] types = critext.toArray( new SimulationContextType[critext.size()] ); ContextTypeComparator compare = new ContextTypeComparator(); - ProjectSettings.Step step = types[0].getAttachedStep(); + ProjectSettingsService.Step step = types[0].getAttachedStep(); int from = 0; int to = 0; while (to < types.length-1) { diff --git a/Workspace/Siman/src/org/splat/simer/SearchKnowledgeAction.java b/Workspace/Siman/src/org/splat/simer/SearchKnowledgeAction.java index e71219e..a442c3e 100644 --- a/Workspace/Siman/src/org/splat/simer/SearchKnowledgeAction.java +++ b/Workspace/Siman/src/org/splat/simer/SearchKnowledgeAction.java @@ -6,15 +6,16 @@ import java.util.Map; import org.hibernate.Session; import org.hibernate.Transaction; import org.splat.kernel.InvalidPropertyException; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; import org.splat.kernel.UserDirectory; -import org.splat.som.Database; -import org.splat.som.KnowledgeElement; -import org.splat.som.KnowledgeElementType; -import org.splat.som.ProgressState; -import org.splat.som.SimulationContext; -import org.splat.som.SimulationContextType; -import org.splat.som.Visibility; +import org.splat.service.SearchService; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.KnowledgeElementType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; +import org.splat.dal.bo.som.Visibility; public class SearchKnowledgeAction extends SearchBaseAction { @@ -26,8 +27,9 @@ public class SearchKnowledgeAction extends SearchBaseAction { private String refid = null; // Knowledge reference when among ref private String words = null; // Full text search words private List types; // Available knowledge types filter (initialized below) + private SearchService _searchService; - private static final long serialVersionUID = -3104321907432838476L; + private static final long serialVersionUID = -3104321907432838476L; // ============================================================================================================================== // Action methods @@ -81,7 +83,7 @@ public class SearchKnowledgeAction extends SearchBaseAction { sprop.setVisibility(Visibility.PRIVATE); sprop.setActor(user); - result = Database.selectKnowledgeElementsWhere(sprop, other); + result = getSearchService().selectKnowledgeElementsWhere(sprop, other); } else { Visibility reparea = null; @@ -90,7 +92,7 @@ public class SearchKnowledgeAction extends SearchBaseAction { sprop.setVisibility(reparea); if (reparea == Visibility.PRIVATE) sprop.setActor(user); - result = Database.selectKnowledgeElementsWhere(sprop); + result = getSearchService().selectKnowledgeElementsWhere(sprop); } session.put("search.result", result); // For redisplaying the page without re-executing the search return "refresh"; @@ -207,4 +209,19 @@ public class SearchKnowledgeAction extends SearchBaseAction { // Initialization required by all do functions types = KnowledgeElement.selectTypesWhere(ProgressState.APPROVED); } + /** + * Get the searchService. + * @return the searchService + */ + public SearchService getSearchService() { + return _searchService; + } + + /** + * Set the searchService. + * @param searchService the searchService to set + */ + public void setSearchService(SearchService searchService) { + _searchService = searchService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/SearchStudyAction.java b/Workspace/Siman/src/org/splat/simer/SearchStudyAction.java index 4e38b95..26bd2dd 100644 --- a/Workspace/Siman/src/org/splat/simer/SearchStudyAction.java +++ b/Workspace/Siman/src/org/splat/simer/SearchStudyAction.java @@ -6,15 +6,16 @@ import java.util.Map; import org.hibernate.Session; import org.hibernate.Transaction; import org.splat.kernel.InvalidPropertyException; -import org.splat.kernel.User; +import org.splat.dal.bo.kernel.User; import org.splat.kernel.UserDirectory; -import org.splat.som.Database; -import org.splat.som.ProgressState; -import org.splat.som.ProjectSettings; -import org.splat.som.SimulationContext; -import org.splat.som.SimulationContextType; -import org.splat.som.Study; -import org.splat.som.Visibility; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.ProgressState; +import org.splat.service.SearchService; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.Visibility; public class SearchStudyAction extends SearchBaseAction { @@ -25,6 +26,8 @@ public class SearchStudyAction extends SearchBaseAction { private String matcontext = null; // "all" or "any" private String refid = null; // Study reference private String words = null; // Full text search words + private SearchService _searchService; + private ProjectSettingsService _projectSettingsService; private static final long serialVersionUID = -1910481357051393077L; @@ -81,7 +84,7 @@ public class SearchStudyAction extends SearchBaseAction { sprop.setVisibility(Visibility.PRIVATE); sprop.setActor(user); - result = Database.selectStudiesWhere(sprop, other); + result = getSearchService().selectStudiesWhere(sprop, other); } else { Visibility reparea = null; @@ -90,7 +93,7 @@ public class SearchStudyAction extends SearchBaseAction { sprop.setVisibility(reparea); if (reparea == Visibility.PRIVATE) sprop.setActor(user); - result = Database.selectStudiesWhere(sprop); + result = getSearchService().selectStudiesWhere(sprop); } session.put("search.result", result); // For redisplaying the page without re-executing the search return "refresh"; @@ -160,8 +163,8 @@ public class SearchStudyAction extends SearchBaseAction { protected List getInvolvedContexts () { // ------------------------------------------------------------ - List steps = ProjectSettings.getStepsOf(Study.class); - ProjectSettings.Step[] number = steps.toArray(new ProjectSettings.Step[steps.size()]); + List steps = getProjectSettings().getStepsOf(Study.class); + ProjectSettingsService.Step[] number = steps.toArray(new ProjectSettingsService.Step[steps.size()]); return SimulationContext.selectTypesOf(number); } @@ -204,4 +207,35 @@ public class SearchStudyAction extends SearchBaseAction { context = (List)filter.get("context"); // Only criteria not part of the form } + /** + * Get the searchService. + * @return the searchService + */ + public SearchService getSearchService() { + return _searchService; + } + + /** + * Set the searchService. + * @param searchService the searchService to set + */ + public void setSearchService(SearchService searchService) { + _searchService = searchService; + } + /** + * Get project settings. + * @return Project settings service + */ + private ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + + /** + * Set project settings service. + * @param projectSettingsService project settings service + */ + public void setProjectSettings( + ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/SimulationContextFacade.java b/Workspace/Siman/src/org/splat/simer/SimulationContextFacade.java index d0b1d9a..1909107 100644 --- a/Workspace/Siman/src/org/splat/simer/SimulationContextFacade.java +++ b/Workspace/Siman/src/org/splat/simer/SimulationContextFacade.java @@ -3,10 +3,10 @@ package org.splat.simer; import java.util.Iterator; import java.util.ResourceBundle; -import org.splat.som.ProgressState; -import org.splat.som.ProjectSettings; -import org.splat.som.SimulationContext; -import org.splat.som.SimulationContextType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; import org.splat.wapp.PopupMenu; @@ -17,6 +17,7 @@ public class SimulationContextFacade { private int step; private ProgressState state; private PopupMenu popup; + private ProjectSettingsService _projectSettingsService; // ============================================================================================================================== // Constructor @@ -28,8 +29,8 @@ public class SimulationContextFacade { popup = ApplicationSettings.getPopupMenu("scontext"); SimulationContextType type = my.getType(); - for (Iterator i=ProjectSettings.getAllSteps().iterator(); i.hasNext(); ) { - ProjectSettings.Step next = i.next(); + for (Iterator i=getProjectSettings().getAllSteps().iterator(); i.hasNext(); ) { + ProjectSettingsService.Step next = i.next(); if (!type.isAttachedTo(next)) continue; step = next.getNumber(); break; @@ -89,4 +90,20 @@ public class SimulationContextFacade { // --------------------------------------------------------- return my.equals(represented); } + /** + * Get project settings. + * @return Project settings service + */ + private ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + + /** + * Set project settings service. + * @param projectSettingsService project settings service + */ + public void setProjectSettings( + ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/StampFacade.java b/Workspace/Siman/src/org/splat/simer/StampFacade.java index fe347fa..481133f 100644 --- a/Workspace/Siman/src/org/splat/simer/StampFacade.java +++ b/Workspace/Siman/src/org/splat/simer/StampFacade.java @@ -8,8 +8,8 @@ package org.splat.simer; import java.text.SimpleDateFormat; import java.util.ResourceBundle; -import org.splat.som.Timestamp; -import org.splat.som.ValidationStep; +import org.splat.dal.bo.som.Timestamp; +import org.splat.dal.bo.som.ValidationStep; public class StampFacade implements HistoryFacade { diff --git a/Workspace/Siman/src/org/splat/simer/StartAction.java b/Workspace/Siman/src/org/splat/simer/StartAction.java index 89133a5..9d432b5 100644 --- a/Workspace/Siman/src/org/splat/simer/StartAction.java +++ b/Workspace/Siman/src/org/splat/simer/StartAction.java @@ -9,13 +9,16 @@ import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.splat.som.ApplicationRights; -import org.splat.som.Database; -import org.splat.som.ProjectSettings; +import org.splat.dal.dao.som.Database; +import org.splat.service.technical.ProjectSettingsService; public class StartAction extends Action implements ServletRequestAware { private HttpServletRequest request = null; + + private ProjectSettingsService _projectSettingsService; + private ApplicationSettings _ApplicationSettings; private static final long serialVersionUID = 5875058140682652964L; @@ -31,8 +34,8 @@ public class StartAction extends Action implements ServletRequestAware { logger.info( new StringBuffer("Initializing ").append(wappurl).append("...").toString() ); try { - ProjectSettings project = ProjectSettings.getMe(); - ApplicationSettings wapp = new ApplicationSettings(wappurl.toString(), this.getLocale()); + ProjectSettingsService project = getProjectSettings(); + ApplicationSettings wapp = getApplicationSettings().init(wappurl.toString(), this.getLocale()); String root = wapp.getApplicationRootPath(); // Database configuration @@ -74,4 +77,36 @@ public class StartAction extends Action implements ServletRequestAware { // ---------------------------------------------------------- this.request = request; } + /** + * Get project settings. + * @return Project settings service + */ + private ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + + /** + * Set project settings service. + * @param projectSettingsService project settings service + */ + public void setProjectSettings( + ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } + + /** + * Get the applicationSettings. + * @return the applicationSettings + */ + public ApplicationSettings getApplicationSettings() { + return _ApplicationSettings; + } + + /** + * Set the applicationSettings. + * @param applicationSettings the applicationSettings to set + */ + public void setApplicationSettings(ApplicationSettings applicationSettings) { + _ApplicationSettings = applicationSettings; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/StudyMenu.java b/Workspace/Siman/src/org/splat/simer/StudyMenu.java index 5490106..ff571ab 100644 --- a/Workspace/Siman/src/org/splat/simer/StudyMenu.java +++ b/Workspace/Siman/src/org/splat/simer/StudyMenu.java @@ -3,10 +3,12 @@ package org.splat.simer; import java.util.Iterator; import java.util.Vector; -import org.splat.som.ProjectElement; -import org.splat.som.Scenario; +import org.splat.dal.bo.som.ProjectElement; +import org.splat.dal.bo.som.Scenario; +import org.splat.service.ProjectElementService; +import org.splat.service.ScenarioService; import org.splat.som.Step; -import org.splat.som.Study; +import org.splat.dal.bo.som.Study; import org.splat.wapp.MenuItem; import org.splat.wapp.SlidMenu; @@ -16,6 +18,8 @@ public class StudyMenu extends SlidMenu { private Study study; private Scenario scopen; // Currently "open" scenario private Step stopen; // Currently selected step + private ScenarioService _scenarioService; + private ProjectElementService _projectElementService; // ============================================================================================================================== // Constructor @@ -58,13 +62,13 @@ public class StudyMenu extends SlidMenu { // Study with several scenarii, non of them open // Collection of steps to be displayed Vector steps = new Vector(); - Step[] newstep = scenes[0].getSteps(); // All scenarii have the same steps + Step[] newstep = getProjectElementService().getSteps(scenes[0]); // All scenarii have the same steps int base = newstep[0].getNumber(); int last = newstep[newstep.length-1].getNumber(); - for (int i=0; i-1; i--) { if(newstep[i].getNumber() >= base) continue; @@ -90,7 +94,7 @@ public class StudyMenu extends SlidMenu { int index = group.getIndex(); String value = index + "." + number; if (group.isCheckedout()) icon = "icon.checkedout.png"; - else if (group.isEmpty()) icon = "icon.empty.png"; + else if (getScenarioService().isEmpty(group)) icon = "icon.empty.png"; // else if (group.isFinished()) icon = "icon.checked.png"; else icon = "icon.done.png"; addGroup(value, group.getTitle(), icon, "step-study?selection=" + value); @@ -103,20 +107,20 @@ public class StudyMenu extends SlidMenu { this.clear(); // Collection of steps to be displayed Vector steps = new Vector(); - Step[] newstep = scenew.getSteps(); + Step[] newstep = getProjectElementService().getSteps(scenew); int base = newstep[0].getNumber(); int last = newstep[newstep.length-1].getNumber(); for (int i=0; i-1; i--) steps.add(0, scenes[i].getFirstStep()); - newstep = study.getSteps(); + for (int i=askid-1; i>-1; i--) steps.add(0, getProjectElementService().getFirstStep(scenes[i])); + newstep = getProjectElementService().getSteps(study); for (int i=newstep.length-1; i>-1; i--) { if(newstep[i].getNumber() >= base) continue; steps.add(0, newstep[i]); } - for (int i=askid+1; i 0) study.removeContributor(toremove.toArray(new User[size])); + if (size > 0) getStudyService().removeContributor(study, toremove.toArray(new User[size])); for (int i=0; i usedby = null; - private String docusedby = null; - private String summary = null; // Summary of changes in the new version - private String docver = ""; // Version number extracted from the imported file, if exist - private String date = ""; // Date extracted from the imported file, if exist - - private static final long serialVersionUID = -5702264003232132168L; - -// ============================================================================================================================== -// Action methods -// ============================================================================================================================== - - public String doInitialize () { -// ----------------------------- - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - User user = getConnectedUser(); - File updir = Database.getDownloadDirectory(user); - File upfile = new File(updir.getPath() + "/" + filename); - - mystudy = getOpenStudy(); - - Publication tag = mystudy.getSelectedStep().getDocument(Integer.valueOf(index)); - Document doc = tag.value(); - deftype = doc.getType(); - docname = doc.getTitle(); - defuses = new Vector(); - usedby = new Vector(); - - Reader tool = Toolbox.getReader(upfile); - if (tool != null) { - String fileref = tool.extractProperty("reference"); - String filever = tool.extractProperty("version"); - if (fileref != null && !doc.getReference().equals(fileref)) { - setErrorCode("reference.mismatch"); - return ERROR; - } - if (filever != null) try { - Revision.Format get = new Revision.Format(ProjectSettings.getRevisionPattern()); - Revision newver = get.parse(filever); - Revision oldver = new Revision(doc.getVersion()); - if (!newver.isGraterThan(oldver)) throw new InvalidPropertyException("version"); - if ( newver.isMinor() ) state = ProgressState.inWORK; - else state = ProgressState.inDRAFT; - docver = newver.toString(); - } catch (Exception e) { - setErrorCode("version.mismatch"); - return ERROR; - } - summary = tool.extractProperty("history"); - date = tool.extractProperty("date"); - if (date != null) { - ResourceBundle locale = ResourceBundle.getBundle("som", ApplicationSettings.getCurrentLocale()); - SimpleDateFormat check = new SimpleDateFormat(locale.getString("date.format")); - try { - check.parse(date); - } catch (ParseException e) { - setErrorCode("format.date"); - return ERROR; - } - } else date = ""; - } - setupDefaultUses(deftype); -// Add additional documents used by the current version - List uses = doc.getRelations(UsesRelation.class); - for (Iterator i=uses.iterator(); i.hasNext();) { - Document used = (Document)i.next().getTo(); - if (!defuses.contains(used)) defuses.add(used); - } -// Setup dependencies - List relist = tag.getRelations(UsedByRelation.class); - for (Iterator i=relist.iterator(); i.hasNext();) { - usedby.add(i.next()); - } - transax.commit(); - return SUCCESS; - } - - public String doVersion () { -// ------------------------- - if (action == ToDo.cancel) return "cancel"; - - Session connex = Database.getSession(); - Transaction transax = connex.beginTransaction(); - try { -// Getting user inputs - mystudy = getOpenStudy(); - User user = getConnectedUser(); - Step step = mystudy.getSelectedStep(); - File updir = Database.getDownloadDirectory(user); - File upfile = new File(updir.getPath() + "/" + filename); - -// Versioning of the document - Document.Properties dprop = new Document.Properties(); - Publication current = step.getDocument(Integer.valueOf(index)); - Publication next; - - if (docver.length() == 0) { // Importation of a foreign document - next = step.versionDocument(current, dprop.setAuthor(user).setDescription(summary)); - updir = next.getSourceFile().asFile(); - if (logger.isInfoEnabled()) logger.info("Moving \"" + upfile.getName() + "\" to \"" + updir.getPath() + "\"."); - upfile.renameTo(updir); - try { - next.saveAs(state); // May throw FileNotFound if rename was not done - } catch (FileNotFoundException saverror) { - Thread.sleep(1000); - logger.info("Waiting for the file."); - upfile.renameTo(updir); - next.saveAs(state); // Forget it if throw again FileNotFound - } - } else { - if (date.length() > 0) { - ResourceBundle locale = ResourceBundle.getBundle("som", ApplicationSettings.getCurrentLocale()); - SimpleDateFormat get = new SimpleDateFormat(locale.getString("date.format")); - dprop.setDate(get.parse(date)); - } - next = step.versionDocument(current, dprop.setAuthor(user).setDescription(summary)); - updir = next.getSourceFile().asFile(); - if (logger.isInfoEnabled()) logger.info("Moving \"" + upfile.getName() + "\" to \"" + updir.getPath() + "\"."); - upfile.renameTo(updir); - try { - next.saveAs(new Revision(docver)); - } catch (FileNotFoundException saverror) { - Thread.sleep(1000); - logger.info("Waiting for the file."); - upfile.renameTo(updir); - next.saveAs(state); - } - } -//TODO: Remove current document details from the contents of open study - -// Creation of uses relations - if (docuses != null) { - String[] list = docuses.split(","); - for (int i=0; i compatible = new HashSet(); - if (docusedby != null) { - String[] list = docusedby.split(","); - for (int i=0; i relist = current.getRelations(UsedByRelation.class); - for (Iterator i=relist.iterator(); i.hasNext();) { - Publication using = i.next(); - if (!compatible.contains(using.getIndex())) using.outdate(); - } -// Update of the open study - mystudy.setSelection(mystudy.getSelection()); // Rebuilds the presentation -//TODO: Look is an optimization is possible (for example by updating the presentation of versioned document) - - transax.commit(); - return SUCCESS; - } - catch (FileNotFoundException error) { - logger.error("Reason:", error); - setErrorCode("import.file"); - } - catch (Exception error) { - logger.error("Reason:", error); - setErrorCode("internal"); - } - if (transax != null && transax.isActive()) { -// Second try-catch as the rollback could fail as well - try { - transax.rollback(); - } catch (HibernateException backerror) { - logger.debug("Error rolling back transaction", backerror); - } - } - return ERROR; - } -// ============================================================================================================================== -// Getters and setters -// ============================================================================================================================== - - public String getDate () { -// ------------------------ - return date; - } - public List getDependencies () { -// ------------------------------------------- - return usedby; - } - public String getDescription () { -// ------------------------------- - return summary; - } - public String getIndex () { -// ------------------------- - return index; - } - public String getVersion () { -// --------------------------- - return docver; - } - - public void setDate (String date) { -// --------------------------------- - this.date = date; - } - public void setDefaultDescription (String summary) { -// -------------------------------------------------- - if (this.summary == null) this.summary = summary; - } - public void setDescription (String summary) { -// ------------------------------------------- - this.summary = summary; - } - public void setIndex (String index) { -// ----------------------------------- - this.index = index; - } - public void setUsedBy (String list) { -// ----------------------------------- - this.docusedby = list; - } - public void setVersion (String value) { -// ------------------------------------- - this.docver = value; - } + private String index = null; // Versioned document index + private List usedby = null; + private String docusedby = null; + private String summary = null; // Summary of changes in the new version + private String docver = ""; // Version number extracted from the imported file, if exist + private String date = ""; // Date extracted from the imported file, if exist + private ProjectSettingsService _projectSettingsService; + private PublicationService _publicationService; + + private static final long serialVersionUID = -5702264003232132168L; + + // ============================================================================================================================== + // Action methods + // ============================================================================================================================== + + public String doInitialize() { + // ----------------------------- + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + User user = getConnectedUser(); + File updir = Database.getDownloadDirectory(user); + File upfile = new File(updir.getPath() + "/" + filename); + + mystudy = getOpenStudy(); + + Publication tag = mystudy.getSelectedStep().getDocument( + Integer.valueOf(index)); + Document doc = tag.value(); + deftype = doc.getType(); + docname = doc.getTitle(); + defuses = new Vector(); + usedby = new Vector(); + + Reader tool = Toolbox.getReader(upfile); + if (tool != null) { + String fileref = tool.extractProperty("reference"); + String filever = tool.extractProperty("version"); + if (fileref != null && !doc.getReference().equals(fileref)) { + setErrorCode("reference.mismatch"); + return ERROR; + } + if (filever != null) + try { + Revision.Format get = new Revision.Format( + getProjectSettings().getRevisionPattern()); + Revision newver = get.parse(filever); + Revision oldver = new Revision(doc.getVersion()); + if (!newver.isGraterThan(oldver)) + throw new InvalidPropertyException("version"); + if (newver.isMinor()) + state = ProgressState.inWORK; + else + state = ProgressState.inDRAFT; + docver = newver.toString(); + } catch (Exception e) { + setErrorCode("version.mismatch"); + return ERROR; + } + summary = tool.extractProperty("history"); + date = tool.extractProperty("date"); + if (date != null) { + ResourceBundle locale = ResourceBundle.getBundle("som", + ApplicationSettings.getCurrentLocale()); + SimpleDateFormat check = new SimpleDateFormat( + locale.getString("date.format")); + try { + check.parse(date); + } catch (ParseException e) { + setErrorCode("format.date"); + return ERROR; + } + } else + date = ""; + } + setupDefaultUses(deftype); + // Add additional documents used by the current version + List uses = doc.getRelations(UsesRelation.class); + for (Iterator i = uses.iterator(); i.hasNext();) { + Document used = (Document) i.next().getTo(); + if (!defuses.contains(used)) + defuses.add(used); + } + // Setup dependencies + List relist = tag.getRelations(UsedByRelation.class); + for (Iterator i = relist.iterator(); i.hasNext();) { + usedby.add(i.next()); + } + transax.commit(); + return SUCCESS; + } + + public String doVersion() { + // ------------------------- + if (action == ToDo.cancel) + return "cancel"; + + Session connex = Database.getSession(); + Transaction transax = connex.beginTransaction(); + try { + // Getting user inputs + mystudy = getOpenStudy(); + User user = getConnectedUser(); + Step step = mystudy.getSelectedStep(); + File updir = Database.getDownloadDirectory(user); + File upfile = new File(updir.getPath() + "/" + filename); + + // Versioning of the document + Document.Properties dprop = new Document.Properties(); + Publication current = step.getDocument(Integer.valueOf(index)); + Publication next; + + if (docver.length() == 0) { // Importation of a foreign document + next = step.versionDocument(current, dprop.setAuthor(user) + .setDescription(summary)); + updir = next.getSourceFile().asFile(); + if (logger.isInfoEnabled()) + logger.info("Moving \"" + upfile.getName() + "\" to \"" + + updir.getPath() + "\"."); + upfile.renameTo(updir); + try { + getPublicationService().saveAs(next, state); // May throw FileNotFound if rename was not done + } catch (FileNotFoundException saverror) { + Thread.sleep(1000); + logger.info("Waiting for the file."); + upfile.renameTo(updir); + getPublicationService().saveAs(next, state); // Forget it if throw again FileNotFound + } + } else { + if (date.length() > 0) { + ResourceBundle locale = ResourceBundle.getBundle("som", + ApplicationSettings.getCurrentLocale()); + SimpleDateFormat get = new SimpleDateFormat( + locale.getString("date.format")); + dprop.setDate(get.parse(date)); + } + next = step.versionDocument(current, dprop.setAuthor(user) + .setDescription(summary)); + updir = next.getSourceFile().asFile(); + if (logger.isInfoEnabled()) + logger.info("Moving \"" + upfile.getName() + "\" to \"" + + updir.getPath() + "\"."); + upfile.renameTo(updir); + try { + getPublicationService().saveAs(next, new Revision(docver)); + } catch (FileNotFoundException saverror) { + Thread.sleep(1000); + logger.info("Waiting for the file."); + upfile.renameTo(updir); + getPublicationService().saveAs(next, state); + } + } + // TODO: Remove current document details from the contents of open study + + // Creation of uses relations + if (docuses != null) { + String[] list = docuses.split(","); + for (int i = 0; i < list.length; i++) { + Integer index = Integer.valueOf(list[i].trim()); + Publication used = getPublication(index); + next.addDependency(used); + } + } + // Outdating impacted document + HashSet compatible = new HashSet(); + if (docusedby != null) { + String[] list = docusedby.split(","); + for (int i = 0; i < list.length; i++) + compatible.add(Integer.valueOf(list[i].trim())); + } + List relist = current + .getRelations(UsedByRelation.class); + for (Iterator i = relist.iterator(); i.hasNext();) { + Publication using = i.next(); + if (!compatible.contains(using.getIndex())) + using.outdate(); + } + // Update of the open study + mystudy.setSelection(mystudy.getSelection()); // Rebuilds the presentation + // TODO: Look is an optimization is possible (for example by updating the presentation of versioned document) + + transax.commit(); + return SUCCESS; + } catch (FileNotFoundException error) { + logger.error("Reason:", error); + setErrorCode("import.file"); + } catch (Exception error) { + logger.error("Reason:", error); + setErrorCode("internal"); + } + if (transax != null && transax.isActive()) { + // Second try-catch as the rollback could fail as well + try { + transax.rollback(); + } catch (HibernateException backerror) { + logger.debug("Error rolling back transaction", backerror); + } + } + return ERROR; + } + + // ============================================================================================================================== + // Getters and setters + // ============================================================================================================================== + + public String getDate() { + // ------------------------ + return date; + } + + public List getDependencies() { + // ------------------------------------------- + return usedby; + } + + public String getDescription() { + // ------------------------------- + return summary; + } + + public String getIndex() { + // ------------------------- + return index; + } + + public String getVersion() { + // --------------------------- + return docver; + } + + public void setDate(String date) { + // --------------------------------- + this.date = date; + } + + public void setDefaultDescription(String summary) { + // -------------------------------------------------- + if (this.summary == null) + this.summary = summary; + } + + public void setDescription(String summary) { + // ------------------------------------------- + this.summary = summary; + } + + public void setIndex(String index) { + // ----------------------------------- + this.index = index; + } + + public void setUsedBy(String list) { + // ----------------------------------- + this.docusedby = list; + } + + public void setVersion(String value) { + // ------------------------------------- + this.docver = value; + } + + /** + * Get project settings. + * + * @return Project settings service + */ + private ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + + /** + * Set project settings service. + * + * @param projectSettingsService + * project settings service + */ + public void setProjectSettings(ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } + + /** + * Get the publicationService. + * + * @return the publicationService + */ + public PublicationService getPublicationService() { + return _publicationService; + } + + /** + * Set the publicationService. + * + * @param publicationService + * the publicationService to set + */ + public void setPublicationService(PublicationService publicationService) { + _publicationService = publicationService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/admin/DatabaseIndexingAction.java b/Workspace/Siman/src/org/splat/simer/admin/DatabaseIndexingAction.java index c4297cd..303bde8 100644 --- a/Workspace/Siman/src/org/splat/simer/admin/DatabaseIndexingAction.java +++ b/Workspace/Siman/src/org/splat/simer/admin/DatabaseIndexingAction.java @@ -5,15 +5,19 @@ import java.util.Map; import org.hibernate.Session; import org.hibernate.Transaction; +import org.splat.service.SearchService; +import org.splat.service.SearchServiceImpl; import org.splat.simer.Action; -import org.splat.som.Database; -import org.splat.som.Study; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.Study; public class DatabaseIndexingAction extends Action { private List newstudies; private String indices; + private SearchService _searchService; + private ImportedStudy _importedStudy; private static final long serialVersionUID = 4194268823457749655L; @@ -26,7 +30,7 @@ public class DatabaseIndexingAction extends Action { Session connex = Database.getSession(); Transaction transax = connex.beginTransaction(); - newstudies = ImportedStudy.selectAll(); + newstudies = getImportedStudy().selectAll(); indices = ""; transax.commit(); @@ -44,7 +48,7 @@ public class DatabaseIndexingAction extends Action { for (int i=0; i table = new ArrayList(); + protected SelectStudies(SearchService searchService) { + } + public void execute (Connection connex) throws SQLException { Statement request = connex.createStatement(); @@ -37,13 +42,14 @@ public class ImportedStudy { String title = result.getString("title"); try { sprop.clear(); - if (Database.selectStudiesWhere(sprop.setReference(sid)).size() != 0) continue; + if (getSearchService().selectStudiesWhere(sprop.setReference(sid)).size() != 0) continue; } catch (Exception error) { continue; } table.add( new ImportedStudy(rid, sid, title) ); } } + public List getResult () { return table; @@ -54,13 +60,16 @@ public class ImportedStudy { // Constructor // ============================================================================================================================== - public ImportedStudy (int rid, String sid, String title) { -// -------------------------------------------------------- - this.rid = rid; - this.sid = sid; - this.title = title; + public ImportedStudy () { } + public ImportedStudy (int rid, String sid, String title) { + // -------------------------------------------------------- + this.rid = rid; + this.sid = sid; + this.title = title; + } + // ============================================================================================================================== // Public member functions // ============================================================================================================================== @@ -82,12 +91,20 @@ public class ImportedStudy { // Public services // ============================================================================================================================== - public static List selectAll () { + public List selectAll () { // ---------------------------------------------- Session session = Database.getSession(); - SelectStudies query = new SelectStudies(); + SelectStudies query = new SelectStudies(getSearchService()); session.doWork(query); return query.getResult(); } + + public org.splat.service.SearchService getSearchService() { + return _searchService; + } + + public void setSearchService(SearchService searchService) { + _searchService = searchService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/admin/ProjectElementFacade.java b/Workspace/Siman/src/org/splat/simer/admin/ProjectElementFacade.java index cd16947..a2e819f 100644 --- a/Workspace/Siman/src/org/splat/simer/admin/ProjectElementFacade.java +++ b/Workspace/Siman/src/org/splat/simer/admin/ProjectElementFacade.java @@ -1,9 +1,9 @@ package org.splat.simer.admin; -import org.splat.som.ProgressState; -import org.splat.som.ProjectSettings; -import org.splat.som.Scenario; -import org.splat.som.Study; +import org.splat.dal.bo.som.ProgressState; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.Study; public class ProjectElementFacade { @@ -17,14 +17,14 @@ public class ProjectElementFacade { // Constructor // ============================================================================================================================== - public ProjectElementFacade (Study represented, ProjectSettings.Step at) { + public ProjectElementFacade (Study represented, ProjectSettingsService.Step at) { // ------------------------------------------------------------------------ index = represented.getIndex(); // The index of scenarios AND studies are unique my = represented; subtitle = ""; step = "0." + at.getNumber(); } - public ProjectElementFacade (Scenario represented, ProjectSettings.Step at) { + public ProjectElementFacade (Scenario represented, ProjectSettingsService.Step at) { // --------------------------------------------------------------------------- index = represented.getIndex(); // The index of scenarios AND studies are unique my = represented.getOwnerStudy(); diff --git a/Workspace/Siman/src/org/splat/simer/admin/SimulationContextAction.java b/Workspace/Siman/src/org/splat/simer/admin/SimulationContextAction.java index 9725ecc..81018d7 100644 --- a/Workspace/Siman/src/org/splat/simer/admin/SimulationContextAction.java +++ b/Workspace/Siman/src/org/splat/simer/admin/SimulationContextAction.java @@ -16,14 +16,15 @@ import org.hibernate.Transaction; import org.splat.simer.Action; import org.splat.simer.ApplicationSettings; -import org.splat.som.Database; -import org.splat.som.KnowledgeElement; -import org.splat.som.ProgressState; -import org.splat.som.ProjectSettings; -import org.splat.som.Proxy; -import org.splat.som.SimulationContext; -import org.splat.som.SimulationContextType; -import org.splat.som.Study; +import org.splat.dal.dao.som.Database; +import org.splat.dal.bo.som.KnowledgeElement; +import org.splat.dal.bo.som.ProgressState; +import org.splat.service.SearchService; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.service.dto.Proxy; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; +import org.splat.dal.bo.som.Study; public class SimulationContextAction extends Action { @@ -31,11 +32,13 @@ public class SimulationContextAction extends Action { private List tocheck; // Simulation contexts to be approved private int selection; // Index of the selected simulation context presented in the approval form private SimulationContext edition; // Corresponding simulation context object - private ProjectSettings.Step step; // Study step to which the selected simulation context is attached + private ProjectSettingsService.Step step; // Study step to which the selected simulation context is attached private Set owner; // Study scenarios indexed by this simulation context private List existype; // Existing approved simulation context types ordered by localized names private List exisname; // Existing approved simulation context types ordered by internal codes private List existing; // Existing simulation contexts of selected type + private SearchService _searchService; + private ProjectSettingsService _projectSettingsService; private static final long serialVersionUID = 7083323229359094699L; @@ -128,7 +131,7 @@ public class SimulationContextAction extends Action { tocheck.add( new SimulationContextFacade(next) ); } KnowledgeElement.Properties kprop = new KnowledgeElement.Properties(); - List kelm = Database.selectKnowledgeElementsWhere(kprop.setSimulationContexts(selected).setState(ProgressState.inWORK)); + List kelm = getSearchService().selectKnowledgeElementsWhere(kprop.setSimulationContexts(selected).setState(ProgressState.inWORK)); step = edition.getType().getAttachedStep(); owner = new HashSet(); @@ -168,11 +171,40 @@ public class SimulationContextAction extends Action { // Getters and setters // ============================================================================================================================== - public List getAllStudySteps () { + /** + * @return + */ + public SearchService getSearchService() { + // TODO Auto-generated method stub + return _searchService; + } + + public void setSearchService(SearchService searchService) { + _searchService = searchService; + } + + public List getAllStudySteps () { // ----------------------------------------------------- - return ProjectSettings.getAllSteps(); + return getProjectSettings().getAllSteps(); } - public ProjectSettings.Step getAttachedStep () { + /** + * Get project settings. + * @return Project settings service + */ + private ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + + /** + * Set project settings service. + * @param projectSettingsService project settings service + */ + public void setProjectSettings( + ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } + + public ProjectSettingsService.Step getAttachedStep () { // ---------------------------------------------- return step; } diff --git a/Workspace/Siman/src/org/splat/simer/admin/SimulationContextFacade.java b/Workspace/Siman/src/org/splat/simer/admin/SimulationContextFacade.java index 80bb7b0..4904834 100644 --- a/Workspace/Siman/src/org/splat/simer/admin/SimulationContextFacade.java +++ b/Workspace/Siman/src/org/splat/simer/admin/SimulationContextFacade.java @@ -4,11 +4,11 @@ import java.util.Iterator; import java.util.List; import java.util.ResourceBundle; +import org.splat.service.technical.ProjectSettingsService; import org.splat.simer.ApplicationSettings; -import org.splat.som.ProgressState; -import org.splat.som.ProjectSettings; -import org.splat.som.SimulationContext; -import org.splat.som.SimulationContextType; +import org.splat.dal.bo.som.ProgressState; +import org.splat.dal.bo.som.SimulationContext; +import org.splat.dal.bo.som.SimulationContextType; public class SimulationContextFacade { @@ -17,6 +17,7 @@ public class SimulationContextFacade { private String name; private int at; private ProgressState state; + private ProjectSettingsService _projectSettingsService; // ============================================================================================================================== // Constructor @@ -24,13 +25,13 @@ public class SimulationContextFacade { public SimulationContextFacade (SimulationContext represented) { // -------------------------------------------------------------- - List steps = ProjectSettings.getAllSteps(); + List steps = getProjectSettings().getAllSteps(); SimulationContextType mytype; my = represented; mytype = my.getType(); - for (Iterator i=steps.iterator(); i.hasNext(); ) { - ProjectSettings.Step step = i.next(); + for (Iterator i=steps.iterator(); i.hasNext(); ) { + ProjectSettingsService.Step step = i.next(); if (!mytype.isAttachedTo(step)) continue; at = step.getNumber(); // There is no direct service for getting the step number break; @@ -69,4 +70,20 @@ public class SimulationContextFacade { // ------------------------- return my.getValue(); } + /** + * Get project settings. + * @return Project settings service + */ + private ProjectSettingsService getProjectSettings() { + return _projectSettingsService; + } + + /** + * Set project settings service. + * @param projectSettingsService project settings service + */ + public void setProjectSettings( + ProjectSettingsService projectSettingsService) { + _projectSettingsService = projectSettingsService; + } } \ No newline at end of file diff --git a/Workspace/Siman/src/spring/applicationContext.xml b/Workspace/Siman/src/spring/applicationContext.xml index d92fc4c..df90319 100644 --- a/Workspace/Siman/src/spring/applicationContext.xml +++ b/Workspace/Siman/src/spring/applicationContext.xml @@ -1,7 +1,146 @@ + xsi:schemaLocation="http://www.springframework.org/schema/beans +http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/Workspace/Siman/src/spy.properties b/Workspace/Siman/src/spy.properties new file mode 100644 index 0000000..81b5508 --- /dev/null +++ b/Workspace/Siman/src/spy.properties @@ -0,0 +1,246 @@ +################################################################# +# P6Spy Options File # +# See documentation for detailed instructions # +################################################################# + +################################################################# +# MODULES # +# # +# Modules provide the P6Spy functionality. If a module, such # +# as module_log is commented out, that functionality will not # +# be available. If it is not commented out (if it is active), # +# the functionality will be active. # +# # +# Values set in Modules cannot be reloaded using the # +# reloadproperties variable. Once they are loaded, they remain # +# in memory until the application is restarted. # +# # +################################################################# + +module.log=com.p6spy.engine.logging.P6LogFactory +#module.outage=com.p6spy.engine.outage.P6OutageFactory + +################################################################# +# REALDRIVER(s) # +# # +# In your application server configuration file you replace the # +# "real driver" name with com.p6spy.engine.P6SpyDriver. This is # +# where you put the name of your real driver P6Spy can find and # +# register your real driver to do the database work. # +# # +# If your application uses several drivers specify them in # +# realdriver2, realdriver3. See the documentation for more # +# details. # +# # +# Values set in REALDRIVER(s) cannot be reloaded using the # +# reloadproperties variable. Once they are loaded, they remain # +# in memory until the application is restarted. # +# # +################################################################# + +# oracle driver +# realdriver=oracle.jdbc.driver.OracleDriver + +# mysql Connector/J driver +realdriver=com.mysql.jdbc.Driver + +# informix driver +# realdriver=com.informix.jdbc.IfxDriver + +# ibm db2 driver +# realdriver=COM.ibm.db2.jdbc.net.DB2Driver + +# the mysql open source driver +#realdriver=org.gjt.mm.mysql.Driver + +#specifies another driver to use +realdriver2= +#specifies a third driver to use +realdriver3= + + +#the DriverManager class sequentially tries every driver that is +#registered to find the right driver. In some instances, it's possible to +#load up the realdriver before the p6spy driver, in which case your connections +#will not get wrapped as the realdriver will "steal" the connection before +#p6spy sees it. Set the following property to "true" to cause p6spy to +#explicitily deregister the realdrivers +deregisterdrivers=false + +################################################################ +# P6LOG SPECIFIC PROPERTIES # +################################################################ +# no properties currently available + +################################################################ +# EXECUTION THRESHOLD PROPERTIES # +################################################################ +# This feature applies to the standard logging of P6Spy. # +# While the standard logging logs out every statement # +# regardless of its execution time, this feature puts a time # +# condition on that logging. Only statements that have taken # +# longer than the time specified (in milliseconds) will be # +# logged. This way it is possible to see only statements that # +# have exceeded some high water mark. # +# This time is reloadable. # +# +# executionthreshold=integer time (milliseconds) +# +executionthreshold= + +################################################################ +# P6OUTAGE SPECIFIC PROPERTIES # +################################################################ +# Outage Detection +# +# This feature detects long-running statements that may be indicative of +# a database outage problem. If this feature is turned on, it will log any +# statement that surpasses the configurable time boundary during its execution. +# When this feature is enabled, no other statements are logged except the long +# running statements. The interval property is the boundary time set in seconds. +# For example, if this is set to 2, then any statement requiring at least 2 +# seconds will be logged. Note that the same statement will continue to be logged +# for as long as it executes. So if the interval is set to 2, and the query takes +# 11 seconds, it will be logged 5 times (at the 2, 4, 6, 8, 10 second intervals). +# +# outagedetection=true|false +# outagedetectioninterval=integer time (seconds) +# +outagedetection=false +outagedetectioninterval= + +################################################################ +# COMMON PROPERTIES # +################################################################ + +# filter what is logged +filter=false + +# comma separated list of tables to include when filtering +include = +# comma separated list of tables to exclude when filtering +exclude = + +# sql expression to evaluate if using regex filtering +sqlexpression = + + +# turn on tracing +autoflush = true + +# sets the date format using Java's SimpleDateFormat routine +dateformat= + +#list of categories to explicitly include +includecategories= + +#list of categories to exclude: error, info, batch, debug, statement, +#commit, rollback and result are valid values +excludecategories=info,debug,result,batch + + +#allows you to use a regex engine or your own matching engine to determine +#which statements to log +# +#stringmatcher=com.p6spy.engine.common.GnuRegexMatcher +#stringmatcher=com.p6spy.engine.common.JakartaRegexMatcher +stringmatcher= + +# prints a stack trace for every statement logged +stacktrace=false +# if stacktrace=true, specifies the stack trace to print +stacktraceclass= + +# determines if property file should be reloaded +reloadproperties=false +# determines how often should be reloaded in seconds +reloadpropertiesinterval=60 + +#if=true then url must be prefixed with p6spy: +useprefix=false + +#specifies the appender to use for logging +#appender=com.p6spy.engine.logging.appender.Log4jLogger +#appender=com.p6spy.engine.logging.appender.StdoutLogger +appender=com.p6spy.engine.logging.appender.FileLogger + +# name of logfile to use, note Windows users should make sure to use forward slashes in their pathname (e:/test/spy.log) (used for file logger only) +logfile = spy.log + +# append to the p6spy log file. if this is set to false the +# log file is truncated every time. (file logger only) +append=true + +#The following are for log4j logging only +log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender +log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout +log4j.appender.STDOUT.layout.ConversionPattern=p6spy - %m%n + +#log4j.appender.CHAINSAW_CLIENT=org.apache.log4j.net.SocketAppender +#log4j.appender.CHAINSAW_CLIENT.RemoteHost=localhost +#log4j.appender.CHAINSAW_CLIENT.Port=4445 +#log4j.appender.CHAINSAW_CLIENT.LocationInfo=true + +log4j.logger.p6spy=INFO,STDOUT + + +################################################################# +# DataSource replacement # +# # +# Replace the real DataSource class in your application server # +# configuration with the name com.p6spy.engine.spy.P6DataSource,# +# then add the JNDI name and class name of the real # +# DataSource here # +# # +# Values set in this item cannot be reloaded using the # +# reloadproperties variable. Once it is loaded, it remains # +# in memory until the application is restarted. # +# # +################################################################# +#realdatasource=/RealMySqlDS +#realdatasourceclass=com.mysql.jdbc.jdbc2.optional.MysqlDataSource + +################################################################# +# DataSource properties # +# # +# If you are using the DataSource support to intercept calls # +# to a DataSource that requires properties for proper setup, # +# define those properties here. Use name value pairs, separate # +# the name and value with a semicolon, and separate the # +# pairs with commas. # +# # +# The example shown here is for mysql # +# # +################################################################# +#realdatasourceproperties=port;3306,serverName;ibmhost,databaseName;mydb + + +################################################################# +# JNDI DataSource lookup # +# # +# If you are using the DataSource support outside of an app # +# server, you will probably need to define the JNDI Context # +# environment. # +# # +# If the P6Spy code will be executing inside an app server then # +# do not use these properties, and the DataSource lookup will # +# use the naming context defined by the app server. # +# # +# The two standard elements of the naming environment are # +# jndicontextfactory and jndicontextproviderurl. If you need # +# additional elements, use the jndicontextcustom property. # +# You can define multiple properties in jndicontextcustom, # +# in name value pairs. Separate the name and value with a # +# semicolon, and separate the pairs with commas. # +# # +# The example shown here is for a standalone program running on # +# a machine that is also running JBoss, so the JDNI context # +# is configured for JBoss (3.0.4). # +# # +################################################################# +#jndicontextfactory=org.jnp.interfaces.NamingContextFactory +#jndicontextproviderurl=localhost:1099 +#jndicontextcustom=java.naming.factory.url.pkgs;org.jboss.nameing:org.jnp.interfaces + +#jndicontextfactory=com.ibm.websphere.naming.WsnInitialContextFactory +#jndicontextproviderurl=iiop://localhost:900 diff --git a/Workspace/Siman/src/Test.java b/Workspace/Siman/src/test/Test.java similarity index 98% rename from Workspace/Siman/src/Test.java rename to Workspace/Siman/src/test/Test.java index 06502a2..32173a5 100644 --- a/Workspace/Siman/src/Test.java +++ b/Workspace/Siman/src/test/Test.java @@ -1,3 +1,4 @@ +package test; import org.splat.launcher.FileTransfer; import org.splat.launcher.WindowsRegistry; -- 2.30.2