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.w3c.dom.Element;
41 import org.w3c.dom.NamedNodeMap;
42 import org.w3c.dom.Node;
43 import org.w3c.dom.NodeList;
46 * SIMAN workflow configuration data service.
48 public class ProjectSettingsServiceImpl implements ProjectSettingsService {
51 * The logger for the service.
53 protected final static Logger LOG = Logger
54 .getLogger(ProjectSettingsServiceImpl.class);
57 * Type attribute name.
59 private final static String TYPE_ATTR = "type";
61 // Non persistent configuration information
63 * Repository settings.
65 private transient final Properties _reprop = new Properties();
67 * Pattern of study references.
69 private transient String _pattern;
71 * Scheme of file names stored into the repository.
73 private transient FileNaming _naming;
75 * Pattern of the presentation of version numbers.
77 private transient String _versioning;
79 * Ordered list of (transient) study steps.
81 private transient final List<ProjectSettingsService.Step> _steps = new ArrayList<ProjectSettingsService.Step>();
83 * Configuration document validation cycles.
85 private transient final List<ProjectSettingsValidationCycle> _concycles = new ArrayList<ProjectSettingsValidationCycle>();
87 * Document type mappings to file formats which should be imported into SALOME during check-out.
89 private transient final Map<String, List<String>> _mapimport = new HashMap<String, List<String>>();
91 * Default document types structured by step.formats.
93 private transient final Map<String, DocumentType> _defdoctype = new LinkedHashMap<String, DocumentType>();
95 // Temporary attributes initialized from the configuration file for populating the database with object types
97 * Step.format keys structured by Default document types names. This map is used for loading config before document types are created in
98 * the database. When document types are created then _defdoctype is filled.
100 private transient final Map<String, List<String>> _defdoctypeKeys = new HashMap<String, List<String>>();
103 * Document type names and uses mapping.
105 private transient Map<String, String> _mapuse;
107 * Simulation Context type names.
109 private transient List<String> _context;
111 * Knowledge Element type names.
113 private transient List<String> _kname;
117 private transient List<NamedNodeMap> _flows;
119 * Study classifications.
121 private transient List<NamedNodeMap> _sclass;
125 * Database service to check its version, etc.
127 private Database _database;
129 * Injected simulation context type service.
131 private SimulationContextTypeService _simulationContextTypeService;
133 * Injected knowledge element type service.
135 private KnowledgeElementTypeService _knowledgeElementTypeService;
137 * Injected document type service.
139 private DocumentTypeService _documentTypeService;
142 * File naming strategy enumeration.
144 public enum FileNaming {
146 * Name by document title.
150 * Generate encoded name.
154 * Keep original file name.
160 * Validation cycle defined in the XML configuration.
162 public static class ProjectSettingsValidationCycle {
164 * Cycle (document) type name.
166 private transient final String _name;
168 * Array of cycle actors positions in the organization. TODO: Must be replaced by Roles.
170 private transient final Actor[] _actor;
173 * Default constructor.
175 private ProjectSettingsValidationCycle() {
176 this._name = "built-in";
177 this._actor = new Actor[] { null, null, null };
181 * Create a validation cycle definition for the given document type name and actors positions.
184 * the document type name
186 * the array of actors positions
188 private ProjectSettingsValidationCycle(final String name,
189 final Actor[] actor) {
195 * The processed document type name.
197 * @return the document type name
199 public String getName() {
204 * Get an array of cycle actors positions.
206 * @return the array of actors positions
207 * @see org.splat.dal.bo.som.ValidationCycle.Actor
209 public Actor[] getActorTypes() {
214 // ==============================================================================================================================
216 // ==============================================================================================================================
219 * Load workflow configuration from the given file. <br/> Create necessary default staff in the database if it is not initialized yet.
222 * the workflow configuration file
223 * @throws IOException
224 * if there is a file reading or index creation problem
225 * @throws SQLException
226 * if there is a database population problem
228 public void configure(final String filename) throws IOException,
230 if (!_steps.isEmpty()) {
231 return; // Project already configured
234 Database base = getDatabase().getCheckedDB();
235 File config = new File(filename);
236 if (config.exists()) {
237 loadCustomization(config);
239 LOG.fatal("Could not find the database configuration file \""
240 + config.getAbsolutePath() + "\"");
241 throw new FileNotFoundException();
243 base.configure(_reprop);
244 if (!base.isInitialized()) {
246 initialize(); // Populates the database with all necessary stuff
251 * Get ordered list of (transient) study steps.
253 * @return the list of steps from project settings
255 public List<ProjectSettingsService.Step> getAllSteps() {
260 * Return the validation cycles of result documents defined in the workflow, ordered by study activities and ending by the default
261 * validation cycle, if defined.
263 * @return the validation cycles of the workflow
265 public List<ProjectSettingsValidationCycle> getAllValidationCycles() {
270 * Get file naming scheme setting.
272 * @return file naming scheme
273 * @see org.splat.service.technical.ProjectSettingsServiceImpl.FileNaming
275 public FileNaming getFileNamingScheme() {
280 * Get a pattern of study references.
282 * @return the reference pattern
284 public String getReferencePattern() {
289 * Get a pattern of the presentation of version numbers.
291 * @return the version numbers presentation pattern
293 public String getRevisionPattern() {
298 * Get a study step by its sequential number.
304 public ProjectSettingsService.Step getStep(final int number) {
305 ProjectSettingsService.Step res = null;
306 for (int i = 0; i < _steps.size(); i++) {
307 ProjectSettingsService.Step step = _steps.get(i);
308 if (step.getNumber() == number) {
317 * Get steps of the given project element (study or scenario).
320 * the project element (study or scenario)
321 * @return the list of steps
323 public List<ProjectSettingsService.Step> getStepsOf(
324 final Class<? extends ProjectElement> level) {
325 List<ProjectSettingsService.Step> result = new ArrayList<ProjectSettingsService.Step>();
327 for (int i = 0; i < _steps.size(); i++) {
328 ProjectSettingsService.Step step = _steps.get(i);
329 if (step.appliesTo(level)) {
337 * Check if a file of the given format should be imported during check-in of a document of the given type.
343 * @return true if file should be imported
345 public boolean doImport(final String type, final String format) {
346 return (_mapimport.containsKey(type) && _mapimport.get(type).contains(
351 * Get default document type for the given file format on the given study step.
356 * the file format (extension)
357 * @return document type
359 public DocumentType getDefaultDocumentType(final Step step,
360 final String format) {
361 String[] table = format.split("\\x2E");
362 if (LOG.isDebugEnabled()) {
363 LOG.debug("Trying to get default type: " + step.getNumber() + "."
364 + table[table.length - 1]);
367 .get(step.getNumber() + "." + table[table.length - 1]); // May be null
371 * Get the list of default formats for the given study step.
375 * @return list of formats (file extensions)
377 public List<String> getDefaultFormats(final Step step) {
378 Integer stepNumber = step.getNumber();
379 List<String> result = new ArrayList<String>();
381 for (String i : _defdoctype.keySet()) {
382 String[] key = i.split("\\x2E");
383 // PDF is not an authoring format
384 if (stepNumber.equals(Integer.valueOf(key[0]))
385 && (!key[1].equals("pdf"))) {
386 result.add(key[1]); // Formats are unique
393 * Initialize the database: create all necessary default staff defined in the configuration file.
395 protected void initialize() {
396 createDocumentTypes();
397 createSimulationContextTypes();
398 createKnowledgeElementTypes();
401 // ==============================================================================================================================
402 // Private member function
403 // ==============================================================================================================================
406 * Read the configuration file and fill transient project settings fields.
409 * the configuration XML file
411 private void loadCustomization(final File config) {
413 DocumentBuilderFactory dfactory = javax.xml.parsers.DocumentBuilderFactory
415 DocumentBuilder dBuilder = dfactory.newDocumentBuilder();
417 org.w3c.dom.Document conf = dBuilder.parse(config.getPath());
418 HashMap<String, Node> children = XDOM.getNamedChildNodes(conf
419 .getDocumentElement());
421 // Repository tag initializing the reprop attribute
422 Node child = children.get("database");
423 HashMap<String, Node> datag = XDOM.getNamedChildNodes(child);
425 String disk = datag.get("repository").getAttributes().getNamedItem(
426 "disk").getNodeValue();
427 if (!disk.endsWith("/")) {
430 LOG.info("Database root set to " + disk);
431 _reprop.setProperty("repository", disk);
433 // Formats tag initializing the reference pattern and date attributes
434 child = children.get("formats");
435 datag = XDOM.getNamedChildNodes(child);
437 NamedNodeMap natr = datag.get("references").getAttributes();
438 _pattern = natr.getNamedItem("study").getNodeValue();
440 natr = datag.get("files").getAttributes();
441 _naming = FileNaming.valueOf(natr.getNamedItem("name")
444 natr = datag.get("versions").getAttributes();
445 _versioning = natr.getNamedItem("pattern").getNodeValue();
447 // Activities tag initializing the steps and rex attributes
448 child = children.get("activities");
449 NodeList nlist = child.getChildNodes();
450 List<NamedNodeMap> flist = new ArrayList<NamedNodeMap>();
451 List<String> resultype = new ArrayList<String>();
452 List<NamedNodeMap> clist = new ArrayList<NamedNodeMap>();
454 int snum = 1; // Base number of steps
455 for (int i = 0; i < nlist.getLength(); i++) {
456 child = nlist.item(i);
457 if ("scenario".equals(child.getNodeName())) {
458 NodeList slist = child.getChildNodes();
459 for (int j = 0; j < slist.getLength(); j++) {
460 snum = loadStep(slist.item(j), Scenario.class, snum,
461 flist, clist, resultype);
464 snum = loadStep(child, Study.class, snum, flist, clist,
470 loadValidationCycles(children, resultype);
471 // Load steps result document types mapped to file formats
472 loadFormatMappings(children);
473 // Load default mapping of file formats to document types for each step
474 loadDefaultDocTypes(children);
476 if (!getDatabase().getCheckedDB().isInitialized()) {
477 // Load object type definitions
479 child = children.get("documents");
480 nlist = child.getChildNodes();
482 _flows = flist; // Kept for later use in document type definition
483 _sclass = clist; // Kept for later use in simulation context type definition
484 _mapuse = new LinkedHashMap<String, String>();
485 for (int i = 0; i < nlist.getLength(); i++) {
486 child = nlist.item(i);
487 if ("article".equals(child.getNodeName())) {
488 natr = child.getAttributes();
489 String type = natr.getNamedItem(TYPE_ATTR)
492 child = natr.getNamedItem("uses");
494 uses = child.getNodeValue();
496 _mapuse.put(type, uses); // Must be added to the map even if no (null) uses
499 // Simulation Contexts tag
500 _context = loadArticles(children, "contexts");
501 // Knowledge Elements tag
502 _kname = loadArticles(children, "knowledges");
504 } catch (Exception error) {
505 LOG.info("Error in customization", error);
510 * Load mappings of document types to lists of importable file formats.
515 private void loadFormatMappings(final Map<String, Node> children) {
517 Element maps = (Element) children.get("mappings");
520 List<String> formats;
521 NodeList docs, imports;
523 // Read document types
524 docs = maps.getElementsByTagName("document");
525 for (int i = 0; i < docs.getLength(); i++) {
526 doc = (Element) docs.item(i);
527 type = doc.getAttribute(TYPE_ATTR);
528 if (!type.isEmpty()) {
529 // Read file formats for the document type
530 imports = doc.getElementsByTagName("import");
531 formats = new ArrayList<String>();
532 for (int j = 0; j < imports.getLength(); j++) {
533 imp = (Element) imports.item(j);
534 format = imp.getAttribute("format");
535 if (!format.isEmpty()) {
539 if (!formats.isEmpty()) {
540 _mapimport.put(type, formats);
548 * Load default document types from XML configuration.
553 private void loadDefaultDocTypes(final Map<String, Node> children) {
555 _defdoctypeKeys.clear();
556 Node child = children.get("default-doctypes");
558 NodeList nlist = child.getChildNodes();
560 List<DocumentType> listype = getDocumentTypeService()
562 Map<String, DocumentType> maptype = new HashMap<String, DocumentType>();
563 for (Iterator<DocumentType> i = listype.iterator(); i.hasNext();) {
564 DocumentType type = i.next();
565 maptype.put(type.getName(), type);
567 for (int i = 0; i < nlist.getLength(); i++) {
568 child = nlist.item(i);
569 if (!child.getNodeName().equals("step")) {
573 String nstep = child.getAttributes().getNamedItem("number")
575 NodeList map = child.getChildNodes();
576 for (int j = 0; j < map.getLength(); j++) {
578 if (!child.getNodeName().equals("mapping")) {
581 NamedNodeMap natr = child.getAttributes();
582 String dext = natr.getNamedItem("extension").getNodeValue();
583 String type = natr.getNamedItem(TYPE_ATTR).getNodeValue();
584 if (LOG.isDebugEnabled()) {
585 LOG.debug("Map default type: " + nstep + "." + dext
586 + ": (type name = " + type + ")"
587 + maptype.get(type));
589 _defdoctype.put(nstep + "." + dext, maptype.get(type));
590 // Remember the key if type is not created yet
591 if (maptype.get(type) == null) {
593 if (_defdoctypeKeys.containsKey(type)) {
594 keys = _defdoctypeKeys.get(type);
596 keys = new ArrayList<String>();
597 _defdoctypeKeys.put(type, keys);
599 keys.add(nstep + "." + dext);
607 * Load a step from the given XML node. Return the next step's number.
612 * the class of a step's owner project element - study or scenario
618 * list of classifications
620 * list of flow results
621 * @return the next step's number
623 private int loadStep(final Node node,
624 final Class<? extends ProjectElement> ownerClass, final int snum,
625 final List<NamedNodeMap> flist, final List<NamedNodeMap> clist,
626 final List<String> resultype) {
628 if ("step".equals(node.getNodeName())) {
630 String name = ((Element) node).getAttribute("name");
631 HashMap<String, Node> tags = XDOM.getNamedChildNodes(node);
633 NamedNodeMap natr = tags.get("storage").getAttributes();
634 ProjectSettingsService.Step step = new ProjectSettingsService.Step(
635 snum, ownerClass, natr.getNamedItem("path").getNodeValue());
638 // Keeping flow and classification information for eventual later use
639 natr = tags.get("flow").getAttributes();
641 Node child = natr.getNamedItem("result");
643 resultype.add(child.getNodeValue());
646 child = tags.get("classification");
650 clist.add(child.getAttributes());
653 if (natr.getNamedItem("contents").getNodeValue()
654 .equals("knowledge")) {
655 if (Study.class.equals(ownerClass)) {
657 .error("Error: knowledges must be attached to scenarios.");
659 // TODO In a given scenario, only one step must contain knowledges
660 step._contents.add(KnowledgeElement.class);
663 step._contents.add(Document.class);
666 Element module = (Element) tags.get("module");
667 if (module != null) {
668 step.setModule(module.getAttribute("name"));
678 * Get custom validation cycles.
683 * list of result types
685 private void loadValidationCycles(final Map<String, Node> children,
686 final List<String> resultype) {
688 Node child = children.get("validations");
689 Map<String, Node> datag = XDOM.getNamedChildNodes(child);
692 String[] step = { "review", "approval", "acceptance" };
693 resultype.add("default");
694 for (Iterator<String> i = resultype.iterator(); i.hasNext();) {
695 Actor[] actor = { null, null, null };
696 String name = i.next();
697 child = datag.get(name);
699 // Document type is the subject of a validation
700 natr = child.getAttributes();
701 for (int j = 0; j < step.length; j++) {
702 child = natr.getNamedItem(step[j]);
704 actor[j] = Actor.valueOf(child.getNodeValue()); // Validation step is required
707 _concycles.add(new ProjectSettingsValidationCycle(name, actor));
710 _concycles.add(new ProjectSettingsValidationCycle()); // Adds the built-in validation cycle
714 * Read list of articles types.
717 * XML nodes containing articles
719 * the name of the list of articles
720 * @return list of articles types
722 private List<String> loadArticles(final Map<String, Node> children,
723 final String listName) {
724 Node child = children.get(listName);
725 NodeList nlist = child.getChildNodes();
727 List<String> articles = new ArrayList<String>();
728 for (int i = 0; i < nlist.getLength(); i++) {
729 child = nlist.item(i);
730 if (child.getNodeName().equals("article")) {
731 articles.add(child.getAttributes().getNamedItem(TYPE_ATTR)
739 * Create in the database document types defined in the custom configuration.
741 private void createDocumentTypes() {
742 if (LOG.isDebugEnabled()) {
743 LOG.debug("Creating documents types...");
745 DocumentType.Properties tprop = new DocumentType.Properties();
746 Map<String, List<ProjectSettingsService.Step>> mapsteps = new HashMap<String, List<ProjectSettingsService.Step>>();
747 Map<String, ProjectSettingsService.Step> mapresult = new HashMap<String, ProjectSettingsService.Step>();
748 Map<String, DocumentType> maptype = new HashMap<String, DocumentType>();
750 List<ProjectSettingsService.Step> slist = null; // List of Steps to which each document type is valid
751 int snum = 0; // Step number
754 for (Iterator<NamedNodeMap> i = _flows.iterator(); i.hasNext(); snum++) {
755 NamedNodeMap flow = i.next();
756 ProjectSettingsService.Step step = _steps.get(snum);
757 String[] contents = flow.getNamedItem("contents").getNodeValue()
759 for (int j = 0; j < contents.length; j++) {
761 if (!_mapuse.containsKey(type)) {
762 LOG.warn("Undefined \"" + type + "\" document type.");
765 slist = mapsteps.get(type);
767 slist = new ArrayList<ProjectSettingsService.Step>();
770 mapsteps.put(type, slist);
772 Node result = flow.getNamedItem("result");
773 if (result != null) {
774 mapresult.put(result.getNodeValue(), step);
778 DocumentType tdoc = null;
779 Set<String> tset = _mapuse.keySet();
780 ProjectSettingsService.Step step;
781 for (Iterator<String> i = tset.iterator(); i.hasNext();) {
783 slist = mapsteps.get(type);
785 uses = _mapuse.get(type);
786 step = mapresult.get(type);
789 tprop.setName(type).setStep(
790 slist.toArray(new ProjectSettingsService.Step[slist
793 tdoc = maptype.get(uses);
795 LOG.warn("Undefined \"" + uses
796 + "\" document type.");
802 tprop.setResult(step);
805 tprop.disableCheck();
806 tdoc = getDocumentTypeService().createType(tprop); // Creation of Document Types
807 getDocumentTypeService().approve(tdoc);
808 maptype.put(type, tdoc);
809 // Remember default type if it is
810 if (_defdoctypeKeys.containsKey(type)) {
811 for (String key : _defdoctypeKeys.get(type)) {
812 if (LOG.isDebugEnabled()) {
813 LOG.debug("Put mapping for default type: "
814 + key + ": " + tdoc);
816 _defdoctype.put(key, tdoc);
821 } catch (Exception error) {
822 LOG.warn("Error creating document types, reason:", error); // Should not happen
824 if (LOG.isDebugEnabled()) {
825 LOG.debug("Documents types are created: " + maptype.size());
830 * Create in the database knowledge types defined in the custom configuration.
832 private void createKnowledgeElementTypes() {
834 KnowledgeElementType ktype = getKnowledgeElementTypeService()
835 .createType("usecase"); // Internal reserved knowledge element type
836 getKnowledgeElementTypeService().reserve(ktype);
837 for (Iterator<String> i = _kname.iterator(); i.hasNext();) {
838 String type = i.next();
840 ktype = getKnowledgeElementTypeService().createType(type); // Knowledge Elements Types defined in the configuration
841 getKnowledgeElementTypeService().approve(ktype);
843 } catch (Exception error) {
844 LOG.warn("Error creating knowledge types, reason:", error); // Should not happen
849 * Create in the database simulation contexts types defined in the custom configuration.
851 private void createSimulationContextTypes() {
852 Map<String, ProjectSettingsService.Step> mapstep = new HashMap<String, ProjectSettingsService.Step>();
854 for (Iterator<NamedNodeMap> i = _sclass.iterator(); i.hasNext(); snum++) {
855 NamedNodeMap clatr = i.next();
857 String[] clist = clatr.getNamedItem("context").getNodeValue()
859 for (int j = 0; j < clist.length; j++) {
860 mapstep.put(clist[j], _steps.get(snum));
865 SimulationContextType tctex = null;
866 for (Iterator<String> i = _context.iterator(); i.hasNext();) {
867 String type = i.next();
868 if (mapstep.containsKey(type)) {
869 tctex = getSimulationContextTypeService().createType(type,
870 mapstep.get(type)); // Creation of Simulation Context Types
871 getSimulationContextTypeService().approve(tctex);
874 .warn("Could not find \""
876 + "\" classification. Simulation Context type ignored.");
879 } catch (Exception error) {
880 LOG.warn("Error creating context types, reason:", error); // Should not happen
887 * @return the database
889 public Database getDatabase() {
897 * the database to set
899 public void setDatabase(final Database database) {
900 _database = database;
904 * Get the simulationContextTypeService.
906 * @return the simulationContextTypeService
908 public SimulationContextTypeService getSimulationContextTypeService() {
909 return _simulationContextTypeService;
913 * Set the simulationContextTypeService.
915 * @param simulationContextTypeService
916 * the simulationContextTypeService to set
918 public void setSimulationContextTypeService(
919 final SimulationContextTypeService simulationContextTypeService) {
920 _simulationContextTypeService = simulationContextTypeService;
924 * Get the knowledgeElementTypeService.
926 * @return the knowledgeElementTypeService
928 public KnowledgeElementTypeService getKnowledgeElementTypeService() {
929 return _knowledgeElementTypeService;
933 * Set the knowledgeElementTypeService.
935 * @param knowledgeElementTypeService
936 * the knowledgeElementTypeService to set
938 public void setKnowledgeElementTypeService(
939 final KnowledgeElementTypeService knowledgeElementTypeService) {
940 _knowledgeElementTypeService = knowledgeElementTypeService;
944 * Get the documentTypeService.
946 * @return the documentTypeService
948 public DocumentTypeService getDocumentTypeService() {
949 return _documentTypeService;
953 * Set the documentTypeService.
955 * @param documentTypeService
956 * the documentTypeService to set
958 public void setDocumentTypeService(
959 final DocumentTypeService documentTypeService) {
960 _documentTypeService = documentTypeService;