1 package org.splat.service.technical;
5 * @author Daniel Brunier-Coulin
6 * @copyright OPEN CASCADE 2012
10 import java.io.FileNotFoundException;
11 import java.io.IOException;
12 import java.sql.SQLException;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.LinkedHashMap;
17 import java.util.List;
19 import java.util.Properties;
22 import javax.xml.parsers.DocumentBuilder;
23 import javax.xml.parsers.DocumentBuilderFactory;
25 import org.apache.log4j.Logger;
26 import org.splat.dal.bo.som.Document;
27 import org.splat.dal.bo.som.DocumentType;
28 import org.splat.dal.bo.som.KnowledgeElement;
29 import org.splat.dal.bo.som.KnowledgeElementType;
30 import org.splat.dal.bo.som.ProjectElement;
31 import org.splat.dal.bo.som.Scenario;
32 import org.splat.dal.bo.som.SimulationContextType;
33 import org.splat.dal.bo.som.Study;
34 import org.splat.dal.bo.som.ValidationCycle.Actor;
35 import org.splat.dal.dao.som.Database;
36 import org.splat.manox.XDOM;
37 import org.splat.service.DocumentTypeService;
38 import org.splat.service.KnowledgeElementTypeService;
39 import org.splat.service.SimulationContextTypeService;
40 import org.springframework.transaction.annotation.Transactional;
41 import org.w3c.dom.Element;
42 import org.w3c.dom.NamedNodeMap;
43 import org.w3c.dom.Node;
44 import org.w3c.dom.NodeList;
47 * SIMAN workflow configuration data service.
49 public class ProjectSettingsServiceImpl implements ProjectSettingsService {
52 * The logger for the service.
54 protected final static Logger LOG = Logger
55 .getLogger(ProjectSettingsServiceImpl.class);
58 * Type attribute name.
60 private final static String TYPE_ATTR = "type";
62 // Non persistent configuration information
64 * Repository settings.
66 private transient final Properties _reprop = new Properties();
68 * Pattern of study references.
70 private transient String _pattern;
72 * Scheme of file names stored into the repository.
74 private transient FileNaming _naming;
76 * Pattern of the presentation of version numbers.
78 private transient String _versioning;
80 * Ordered list of (transient) study steps.
82 private transient final List<ProjectSettingsService.Step> _steps = new ArrayList<ProjectSettingsService.Step>();
84 * Configuration document validation cycles.
86 private transient final List<ProjectSettingsValidationCycle> _concycles = new ArrayList<ProjectSettingsValidationCycle>();
88 * Document type mappings to file formats which should be imported into SALOME during check-out.
90 private transient final Map<String, List<String>> _mapimport = new HashMap<String, List<String>>();
92 * Default document types structured by step.formats.
94 private transient final Map<String, DocumentType> _defdoctype = new LinkedHashMap<String, DocumentType>();
96 // Temporary attributes initialized from the configuration file for populating the database with object types
98 * Step.format keys structured by Default document types names. This map is used for loading config before document types are created in
99 * the database. When document types are created then _defdoctype is filled.
101 private transient final Map<String, List<String>> _defdoctypeKeys = new HashMap<String, List<String>>();
104 * Document type names and uses mapping.
106 private transient Map<String, String> _mapuse;
108 * Simulation Context type names.
110 private transient List<String> _context;
112 * Knowledge Element type names.
114 private transient List<String> _kname;
118 private transient List<NamedNodeMap> _flows;
120 * Study classifications.
122 private transient List<NamedNodeMap> _sclass;
126 * Database service to check its version, etc.
128 private Database _database;
130 * Injected simulation context type service.
132 private SimulationContextTypeService _simulationContextTypeService;
134 * Injected knowledge element type service.
136 private KnowledgeElementTypeService _knowledgeElementTypeService;
138 * Injected document type service.
140 private DocumentTypeService _documentTypeService;
143 * File naming strategy enumeration.
145 public enum FileNaming {
147 * Name by document title.
151 * Generate encoded name.
155 * Keep original file name.
161 * Validation cycle defined in the XML configuration.
163 public static class ProjectSettingsValidationCycle {
165 * Cycle (document) type name.
167 private transient final String _name;
169 * Array of cycle actors positions in the organization. TODO: Must be replaced by Roles.
171 private transient final Actor[] _actor;
174 * Default constructor.
176 private ProjectSettingsValidationCycle() {
177 this._name = "built-in";
178 this._actor = new Actor[] { null, null, null };
182 * Create a validation cycle definition for the given document type name and actors positions.
185 * the document type name
187 * the array of actors positions
189 private ProjectSettingsValidationCycle(final String name,
190 final Actor[] actor) {
196 * The processed document type name.
198 * @return the document type name
200 public String getName() {
205 * Get an array of cycle actors positions.
207 * @return the array of actors positions
208 * @see org.splat.dal.bo.som.ValidationCycle.Actor
210 public Actor[] getActorTypes() {
215 // ==============================================================================================================================
217 // ==============================================================================================================================
220 * Load workflow configuration from the given file. <br/> Create necessary default staff in the database if it is not initialized yet.
223 * the workflow configuration file
224 * @throws IOException
225 * if there is a file reading or index creation problem
226 * @throws SQLException
227 * if there is a database population problem
230 public void configure(final String filename) throws IOException,
232 if (!_steps.isEmpty()) {
233 return; // Project already configured
236 Database base = getDatabase().getCheckedDB();
237 File config = new File(filename);
238 if (config.exists()) {
239 loadCustomization(config);
241 LOG.fatal("Could not find the database configuration file \""
242 + config.getAbsolutePath() + "\"");
243 throw new FileNotFoundException();
245 base.configure(_reprop);
246 if (!base.isInitialized()) {
248 initialize(); // Populates the database with all necessary stuff
253 * Get ordered list of (transient) study steps.
255 * @return the list of steps from project settings
257 public List<ProjectSettingsService.Step> getAllSteps() {
262 * Return the validation cycles of result documents defined in the workflow, ordered by study activities and ending by the default
263 * validation cycle, if defined.
265 * @return the validation cycles of the workflow
267 public List<ProjectSettingsValidationCycle> getAllValidationCycles() {
272 * Get file naming scheme setting.
274 * @return file naming scheme
275 * @see org.splat.service.technical.ProjectSettingsServiceImpl.FileNaming
277 public FileNaming getFileNamingScheme() {
282 * Get a pattern of study references.
284 * @return the reference pattern
286 public String getReferencePattern() {
291 * Get a pattern of the presentation of version numbers.
293 * @return the version numbers presentation pattern
295 public String getRevisionPattern() {
300 * Get a study step by its sequential number.
306 public ProjectSettingsService.Step getStep(final int number) {
307 ProjectSettingsService.Step res = null;
308 for (int i = 0; i < _steps.size(); i++) {
309 ProjectSettingsService.Step step = _steps.get(i);
310 if (step.getNumber() == number) {
319 * Get steps of the given project element (study or scenario).
322 * the project element (study or scenario)
323 * @return the list of steps
325 public List<ProjectSettingsService.Step> getStepsOf(
326 final Class<? extends ProjectElement> level) {
327 List<ProjectSettingsService.Step> result = new ArrayList<ProjectSettingsService.Step>();
329 for (int i = 0; i < _steps.size(); i++) {
330 ProjectSettingsService.Step step = _steps.get(i);
331 if (step.appliesTo(level)) {
339 * Check if a file of the given format should be imported during check-out of a document of the given type.
345 * @return true if file should be imported
347 public boolean doImport(final String type, final String format) {
348 if (LOG.isDebugEnabled()) {
349 LOG.debug("Do import for ("
354 + (_mapimport.containsKey(type) && _mapimport.get(type)
357 return (_mapimport.containsKey(type) && _mapimport.get(type).contains(
362 * Get default document type for the given file format on the given study step.
367 * the file format (extension)
368 * @return document type
370 public DocumentType getDefaultDocumentType(final Step step,
371 final String format) {
372 String[] table = format.split("\\x2E");
373 if (LOG.isDebugEnabled()) {
374 LOG.debug("Trying to get default type: " + step.getNumber() + "."
375 + table[table.length - 1]);
378 .get(step.getNumber() + "." + table[table.length - 1]); // May be null
382 * Get the list of default formats for the given study step.
386 * @return list of formats (file extensions)
388 public List<String> getDefaultFormats(final Step step) {
389 Integer stepNumber = step.getNumber();
390 List<String> result = new ArrayList<String>();
392 for (String i : _defdoctype.keySet()) {
393 String[] key = i.split("\\x2E");
394 // PDF is not an authoring format
395 if (stepNumber.equals(Integer.valueOf(key[0]))
396 && (!key[1].equals("pdf"))) {
397 result.add(key[1]); // Formats are unique
404 * Initialize the database: create all necessary default staff defined in the configuration file.
406 protected void initialize() {
407 createDocumentTypes();
408 createSimulationContextTypes();
409 createKnowledgeElementTypes();
412 // ==============================================================================================================================
413 // Private member function
414 // ==============================================================================================================================
417 * Read the configuration file and fill transient project settings fields.
420 * the configuration XML file
422 private void loadCustomization(final File config) {
424 DocumentBuilderFactory dfactory = javax.xml.parsers.DocumentBuilderFactory
426 DocumentBuilder dBuilder = dfactory.newDocumentBuilder();
428 org.w3c.dom.Document conf = dBuilder.parse(config.getPath());
429 HashMap<String, Node> children = XDOM.getNamedChildNodes(conf
430 .getDocumentElement());
432 // Repository tag initializing the reprop attribute
433 Node child = children.get("database");
434 HashMap<String, Node> datag = XDOM.getNamedChildNodes(child);
436 String disk = datag.get("repository").getAttributes().getNamedItem(
437 "disk").getNodeValue();
438 if (!disk.endsWith("/")) {
441 LOG.info("Database root set to " + disk);
442 _reprop.setProperty("repository", disk);
444 // Formats tag initializing the reference pattern and date attributes
445 child = children.get("formats");
446 datag = XDOM.getNamedChildNodes(child);
448 NamedNodeMap natr = datag.get("references").getAttributes();
449 _pattern = natr.getNamedItem("study").getNodeValue();
451 natr = datag.get("files").getAttributes();
452 _naming = FileNaming.valueOf(natr.getNamedItem("name")
455 natr = datag.get("versions").getAttributes();
456 _versioning = natr.getNamedItem("pattern").getNodeValue();
458 // Activities tag initializing the steps and rex attributes
459 child = children.get("activities");
460 NodeList nlist = child.getChildNodes();
461 List<NamedNodeMap> flist = new ArrayList<NamedNodeMap>();
462 List<String> resultype = new ArrayList<String>();
463 List<NamedNodeMap> clist = new ArrayList<NamedNodeMap>();
465 int snum = 1; // Base number of steps
466 for (int i = 0; i < nlist.getLength(); i++) {
467 child = nlist.item(i);
468 if ("scenario".equals(child.getNodeName())) {
469 NodeList slist = child.getChildNodes();
470 for (int j = 0; j < slist.getLength(); j++) {
471 snum = loadStep(slist.item(j), Scenario.class, snum,
472 flist, clist, resultype);
475 snum = loadStep(child, Study.class, snum, flist, clist,
481 loadValidationCycles(children, resultype);
482 // Load steps result document types mapped to file formats
483 loadFormatMappings(children);
484 // Load default mapping of file formats to document types for each step
485 loadDefaultDocTypes(children);
487 if (!getDatabase().getCheckedDB().isInitialized()) {
488 // Load object type definitions
490 child = children.get("documents");
491 nlist = child.getChildNodes();
493 _flows = flist; // Kept for later use in document type definition
494 _sclass = clist; // Kept for later use in simulation context type definition
495 _mapuse = new LinkedHashMap<String, String>();
496 for (int i = 0; i < nlist.getLength(); i++) {
497 child = nlist.item(i);
498 if ("article".equals(child.getNodeName())) {
499 natr = child.getAttributes();
500 String type = natr.getNamedItem(TYPE_ATTR)
503 child = natr.getNamedItem("uses");
505 uses = child.getNodeValue();
507 _mapuse.put(type, uses); // Must be added to the map even if no (null) uses
510 // Simulation Contexts tag
511 _context = loadArticles(children, "contexts");
512 // Knowledge Elements tag
513 _kname = loadArticles(children, "knowledges");
515 } catch (Exception error) {
516 LOG.info("Error in customization", error);
521 * Load mappings of document types to lists of importable file formats.
526 private void loadFormatMappings(final Map<String, Node> children) {
528 Element maps = (Element) children.get("mappings");
531 List<String> formats;
532 NodeList docs, imports;
534 // Read document types
535 docs = maps.getElementsByTagName("document");
536 for (int i = 0; i < docs.getLength(); i++) {
537 doc = (Element) docs.item(i);
538 type = doc.getAttribute(TYPE_ATTR);
539 if (!type.isEmpty()) {
540 // Read file formats for the document type
541 imports = doc.getElementsByTagName("import");
542 formats = new ArrayList<String>();
543 for (int j = 0; j < imports.getLength(); j++) {
544 imp = (Element) imports.item(j);
545 format = imp.getAttribute("format");
546 if (!format.isEmpty()) {
550 if (!formats.isEmpty()) {
551 _mapimport.put(type, formats);
559 * Load default document types from XML configuration.
564 private void loadDefaultDocTypes(final Map<String, Node> children) {
566 _defdoctypeKeys.clear();
567 Node child = children.get("default-doctypes");
569 NodeList nlist = child.getChildNodes();
571 List<DocumentType> listype = getDocumentTypeService()
573 Map<String, DocumentType> maptype = new HashMap<String, DocumentType>();
574 for (Iterator<DocumentType> i = listype.iterator(); i.hasNext();) {
575 DocumentType type = i.next();
576 maptype.put(type.getName(), type);
578 for (int i = 0; i < nlist.getLength(); i++) {
579 child = nlist.item(i);
580 if (!child.getNodeName().equals("step")) {
584 String nstep = child.getAttributes().getNamedItem("number")
586 NodeList map = child.getChildNodes();
587 for (int j = 0; j < map.getLength(); j++) {
589 if (!child.getNodeName().equals("mapping")) {
592 NamedNodeMap natr = child.getAttributes();
593 String dext = natr.getNamedItem("extension").getNodeValue();
594 String type = natr.getNamedItem(TYPE_ATTR).getNodeValue();
595 if (LOG.isDebugEnabled()) {
596 LOG.debug("Map default type: " + nstep + "." + dext
597 + ": (type name = " + type + ")"
598 + maptype.get(type));
600 _defdoctype.put(nstep + "." + dext, maptype.get(type));
601 // Remember the key if type is not created yet
602 if (maptype.get(type) == null) {
604 if (_defdoctypeKeys.containsKey(type)) {
605 keys = _defdoctypeKeys.get(type);
607 keys = new ArrayList<String>();
608 _defdoctypeKeys.put(type, keys);
610 keys.add(nstep + "." + dext);
618 * Load a step from the given XML node. Return the next step's number.
623 * the class of a step's owner project element - study or scenario
629 * list of classifications
631 * list of flow results
632 * @return the next step's number
634 private int loadStep(final Node node,
635 final Class<? extends ProjectElement> ownerClass, final int snum,
636 final List<NamedNodeMap> flist, final List<NamedNodeMap> clist,
637 final List<String> resultype) {
639 if ("step".equals(node.getNodeName())) {
641 String name = ((Element) node).getAttribute("name");
642 HashMap<String, Node> tags = XDOM.getNamedChildNodes(node);
644 NamedNodeMap natr = tags.get("storage").getAttributes();
645 ProjectSettingsService.Step step = new ProjectSettingsService.Step(
646 snum, ownerClass, natr.getNamedItem("path").getNodeValue());
649 // Keeping flow and classification information for eventual later use
650 natr = tags.get("flow").getAttributes();
652 Node child = natr.getNamedItem("result");
654 resultype.add(child.getNodeValue());
657 child = tags.get("classification");
661 clist.add(child.getAttributes());
664 if (natr.getNamedItem("contents").getNodeValue()
665 .equals("knowledge")) {
666 if (Study.class.equals(ownerClass)) {
668 .error("Error: knowledges must be attached to scenarios.");
670 // TODO In a given scenario, only one step must contain knowledges
671 step._contents.add(KnowledgeElement.class);
674 step._contents.add(Document.class);
677 Element module = (Element) tags.get("module");
678 if (module != null) {
679 step.setModule(module.getAttribute("name"));
689 * Get custom validation cycles.
694 * list of result types
696 private void loadValidationCycles(final Map<String, Node> children,
697 final List<String> resultype) {
699 Node child = children.get("validations");
700 Map<String, Node> datag = XDOM.getNamedChildNodes(child);
703 String[] step = { "review", "approval", "acceptance" };
704 resultype.add("default");
705 for (Iterator<String> i = resultype.iterator(); i.hasNext();) {
706 Actor[] actor = { null, null, null };
707 String name = i.next();
708 child = datag.get(name);
710 // Document type is the subject of a validation
711 natr = child.getAttributes();
712 for (int j = 0; j < step.length; j++) {
713 child = natr.getNamedItem(step[j]);
715 actor[j] = Actor.valueOf(child.getNodeValue()); // Validation step is required
718 _concycles.add(new ProjectSettingsValidationCycle(name, actor));
721 _concycles.add(new ProjectSettingsValidationCycle()); // Adds the built-in validation cycle
725 * Read list of articles types.
728 * XML nodes containing articles
730 * the name of the list of articles
731 * @return list of articles types
733 private List<String> loadArticles(final Map<String, Node> children,
734 final String listName) {
735 Node child = children.get(listName);
736 NodeList nlist = child.getChildNodes();
738 List<String> articles = new ArrayList<String>();
739 for (int i = 0; i < nlist.getLength(); i++) {
740 child = nlist.item(i);
741 if (child.getNodeName().equals("article")) {
742 articles.add(child.getAttributes().getNamedItem(TYPE_ATTR)
750 * Create in the database document types defined in the custom configuration.
752 private void createDocumentTypes() {
753 if (LOG.isDebugEnabled()) {
754 LOG.debug("Creating documents types...");
756 DocumentType.Properties tprop = new DocumentType.Properties();
757 Map<String, List<ProjectSettingsService.Step>> mapsteps = new HashMap<String, List<ProjectSettingsService.Step>>();
758 Map<String, ProjectSettingsService.Step> mapresult = new HashMap<String, ProjectSettingsService.Step>();
759 Map<String, DocumentType> maptype = new HashMap<String, DocumentType>();
761 List<ProjectSettingsService.Step> slist = null; // List of Steps to which each document type is valid
762 int snum = 0; // Step number
765 for (Iterator<NamedNodeMap> i = _flows.iterator(); i.hasNext(); snum++) {
766 NamedNodeMap flow = i.next();
767 ProjectSettingsService.Step step = _steps.get(snum);
768 String[] contents = flow.getNamedItem("contents").getNodeValue()
770 for (int j = 0; j < contents.length; j++) {
772 if (!_mapuse.containsKey(type)) {
773 LOG.warn("Undefined \"" + type + "\" document type.");
776 slist = mapsteps.get(type);
778 slist = new ArrayList<ProjectSettingsService.Step>();
781 mapsteps.put(type, slist);
783 Node result = flow.getNamedItem("result");
784 if (result != null) {
785 mapresult.put(result.getNodeValue(), step);
789 DocumentType tdoc = null;
790 Set<String> tset = _mapuse.keySet();
791 ProjectSettingsService.Step step;
792 for (Iterator<String> i = tset.iterator(); i.hasNext();) {
794 slist = mapsteps.get(type);
796 uses = _mapuse.get(type);
797 step = mapresult.get(type);
800 tprop.setName(type).setStep(
801 slist.toArray(new ProjectSettingsService.Step[slist
804 tdoc = maptype.get(uses);
806 LOG.warn("Undefined \"" + uses
807 + "\" document type.");
813 tprop.setResult(step);
816 tprop.disableCheck();
817 tdoc = getDocumentTypeService().createType(tprop); // Creation of Document Types
818 getDocumentTypeService().approve(tdoc);
819 maptype.put(type, tdoc);
820 // Remember default type if it is
821 if (_defdoctypeKeys.containsKey(type)) {
822 for (String key : _defdoctypeKeys.get(type)) {
823 if (LOG.isDebugEnabled()) {
824 LOG.debug("Put mapping for default type: "
825 + key + ": " + tdoc);
827 _defdoctype.put(key, tdoc);
832 } catch (Exception error) {
833 LOG.warn("Error creating document types, reason:", error); // Should not happen
835 if (LOG.isDebugEnabled()) {
836 LOG.debug("Documents types are created: " + maptype.size());
841 * Create in the database knowledge types defined in the custom configuration.
843 private void createKnowledgeElementTypes() {
845 KnowledgeElementType ktype = getKnowledgeElementTypeService()
846 .createType("usecase"); // Internal reserved knowledge element type
847 getKnowledgeElementTypeService().reserve(ktype);
848 for (Iterator<String> i = _kname.iterator(); i.hasNext();) {
849 String type = i.next();
851 ktype = getKnowledgeElementTypeService().createType(type); // Knowledge Elements Types defined in the configuration
852 getKnowledgeElementTypeService().approve(ktype);
854 } catch (Exception error) {
855 LOG.warn("Error creating knowledge types, reason:", error); // Should not happen
860 * Create in the database simulation contexts types defined in the custom configuration.
862 private void createSimulationContextTypes() {
863 Map<String, ProjectSettingsService.Step> mapstep = new HashMap<String, ProjectSettingsService.Step>();
865 for (Iterator<NamedNodeMap> i = _sclass.iterator(); i.hasNext(); snum++) {
866 NamedNodeMap clatr = i.next();
868 String[] clist = clatr.getNamedItem("context").getNodeValue()
870 for (int j = 0; j < clist.length; j++) {
871 mapstep.put(clist[j], _steps.get(snum));
876 SimulationContextType tctex = null;
877 for (Iterator<String> i = _context.iterator(); i.hasNext();) {
878 String type = i.next();
879 if (mapstep.containsKey(type)) {
880 tctex = getSimulationContextTypeService().createType(type,
881 mapstep.get(type)); // Creation of Simulation Context Types
882 getSimulationContextTypeService().approve(tctex);
885 .warn("Could not find \""
887 + "\" classification. Simulation Context type ignored.");
890 } catch (Exception error) {
891 LOG.warn("Error creating context types, reason:", error); // Should not happen
898 * @return the database
900 public Database getDatabase() {
908 * the database to set
910 public void setDatabase(final Database database) {
911 _database = database;
915 * Get the simulationContextTypeService.
917 * @return the simulationContextTypeService
919 public SimulationContextTypeService getSimulationContextTypeService() {
920 return _simulationContextTypeService;
924 * Set the simulationContextTypeService.
926 * @param simulationContextTypeService
927 * the simulationContextTypeService to set
929 public void setSimulationContextTypeService(
930 final SimulationContextTypeService simulationContextTypeService) {
931 _simulationContextTypeService = simulationContextTypeService;
935 * Get the knowledgeElementTypeService.
937 * @return the knowledgeElementTypeService
939 public KnowledgeElementTypeService getKnowledgeElementTypeService() {
940 return _knowledgeElementTypeService;
944 * Set the knowledgeElementTypeService.
946 * @param knowledgeElementTypeService
947 * the knowledgeElementTypeService to set
949 public void setKnowledgeElementTypeService(
950 final KnowledgeElementTypeService knowledgeElementTypeService) {
951 _knowledgeElementTypeService = knowledgeElementTypeService;
955 * Get the documentTypeService.
957 * @return the documentTypeService
959 public DocumentTypeService getDocumentTypeService() {
960 return _documentTypeService;
964 * Set the documentTypeService.
966 * @param documentTypeService
967 * the documentTypeService to set
969 public void setDocumentTypeService(
970 final DocumentTypeService documentTypeService) {
971 _documentTypeService = documentTypeService;