From b5d3e34eae6cc62761a16eba337d47144d428aad Mon Sep 17 00:00:00 2001
From: Dominique Marcadet <Dominique Marcadet@centralesupelec.fr>
Date: Fri, 3 May 2019 19:21:01 +0800
Subject: [PATCH] WIP

Initial design for checking presence conditions
---
 .../META-INF/MANIFEST.MF                      |   3 +-
 .../scl/validator/nsd/LNodeTypeValidator.java | 141 +--
 .../validator/nsd/NsdEObjectValidator.java    |   2 +-
 .../nsd/PresenceConditionValidator.java       | 849 ++++++++++++++++++
 4 files changed, 859 insertions(+), 136 deletions(-)
 create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/PresenceConditionValidator.java

diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/META-INF/MANIFEST.MF b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/META-INF/MANIFEST.MF
index e5d5f00..90a5427 100644
--- a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/META-INF/MANIFEST.MF
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/META-INF/MANIFEST.MF
@@ -22,4 +22,5 @@ Require-Bundle: org.eclipse.core.runtime,
  fr.centralesupelec.edf.riseclipse.iec61850.nsd,
  fr.centralesupelec.edf.riseclipse.iec61850.nsd.edit,
  fr.centralesupelec.edf.riseclipse.iec61850.nsd.utilities,
- org.eclipse.ocl.xtext.oclinecore
+ org.eclipse.ocl.xtext.oclinecore,
+ org.apache.commons.lang3
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/LNodeTypeValidator.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/LNodeTypeValidator.java
index f9c0e31..a585eec 100644
--- a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/LNodeTypeValidator.java
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/LNodeTypeValidator.java
@@ -18,156 +18,29 @@
  */
 package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.nsd;
 
-import java.util.HashMap;
-import java.util.HashSet;
-
-import org.eclipse.emf.common.util.BasicDiagnostic;
-import org.eclipse.emf.common.util.Diagnostic;
 import org.eclipse.emf.common.util.DiagnosticChain;
 
-import fr.centralesupelec.edf.riseclipse.iec61850.nsd.AbstractLNClass;
-import fr.centralesupelec.edf.riseclipse.iec61850.nsd.DataObject;
-import fr.centralesupelec.edf.riseclipse.iec61850.nsd.LNClass;
 import fr.centralesupelec.edf.riseclipse.iec61850.nsd.AnyLNClass;
 import fr.centralesupelec.edf.riseclipse.iec61850.scl.DO;
 import fr.centralesupelec.edf.riseclipse.iec61850.scl.LNodeType;
-import fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.RiseClipseValidatorSCL;
 import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole;
 
 public class LNodeTypeValidator {
 
-    private String lnClassName;
-    private HashMap< String, DataObject > dataObjectMap;
-    private HashMap< String, DOValidator > doValidatorMap;
-
-    public LNodeTypeValidator( LNClass lnClass ) {
-        this.lnClassName = lnClass.getName();
-        this.dataObjectMap = new HashMap<>(); // link between DOI (name) and its respective DataObject
-        this.doValidatorMap = new HashMap<>(); // link between CDC (name) and its respective DOIValidator
-        
-        generateValidators( lnClass );
-
-        // LNClass hierarchy taken into account
-        AbstractLNClass parent = lnClass.getRefersToAbstractLNClass();
-        while( parent != null ) {
-            generateValidators( parent );
-            parent = parent.getRefersToAbstractLNClass();
-        }
-
-    }
+    private PresenceConditionValidator presenceConditionValidator;
 
-    private void generateValidators( AnyLNClass lnClass ) {
-        for( DataObject dObj : lnClass.getDataObject() ) {
-            dataObjectMap.put( dObj.getName(), dObj );
-            if( dObj.getRefersToCDC() != null ) {
-                if( ! doValidatorMap.containsKey( dObj.getRefersToCDC().getName() )) {
-                    doValidatorMap.put( dObj.getRefersToCDC().getName(), new DOValidator( dObj.getRefersToCDC() ));
-                }
-            }
-        }
+    public LNodeTypeValidator( AnyLNClass lnClass ) {
+        presenceConditionValidator = PresenceConditionValidator.get( lnClass );
     }
 
     public boolean validateLNodeType( LNodeType lNodeType, DiagnosticChain diagnostics ) {
         AbstractRiseClipseConsole.getConsole().verbose( "[NSD] validateLNodeType( " + lNodeType.getId() + " )" );
-        boolean res = true;
-
-        HashSet< String > checkedDO = new HashSet<>();
-
+        presenceConditionValidator.reset();
         for( DO do_ : lNodeType.getDO() ) {
-            AbstractRiseClipseConsole.getConsole().verbose( "[NSD] validateDO( " + do_.getName() + " )" );
-
-            // Test if DOI is a possible DOI in this LN
-            if( ! dataObjectMap.containsKey( do_.getName() ) ) {
-                diagnostics.add( new BasicDiagnostic(
-                        Diagnostic.ERROR,
-                        RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
-                        0,
-                        "[NSD] DO " + do_.getName() + " in LN at line " + lNodeType.getLineNumber() + " not found in LNClass " + lNodeType.getLnClass(),
-                        new Object[] { lNodeType } ));
-                continue;
-            }
-
-            // Control of DOI presence in LN  
-            updateCompulsory( do_, checkedDO, diagnostics );
-
-            // Validation of DOI content
-            if( ! validateDO( do_, diagnostics ) ) {
-                res = false;
-            }
-
-        }
-
-        // Verify all necessary DOI were present
-        if( ! dataObjectMap.values().stream()
-                .map( x -> checkCompulsory( lNodeType, x, checkedDO, diagnostics ))
-                .reduce( ( a, b ) -> a && b ).get() ) {
-            diagnostics.add( new BasicDiagnostic(
-                    Diagnostic.ERROR,
-                    RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
-                    0,
-                    "[NSD] LNodeType at line " + lNodeType.getLineNumber() + " does not contain all mandatory DO from class " + lNodeType.getLnClass(),
-                    new Object[] { lNodeType } ));
-            res = false;
-        }
-        return res;
-    }
-
-    private boolean checkCompulsory( LNodeType lNodeType, DataObject dataObject, HashSet< String > checkedDO, DiagnosticChain diagnostics ) {
-        switch( dataObject.getPresCond() ) {
-        case "M":
-            if( ! checkedDO.contains( dataObject.getName() ) ) {
-                diagnostics.add( new BasicDiagnostic(
-                        Diagnostic.ERROR,
-                        RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
-                        0,
-                        "[NSD] DO " + dataObject.getName() + " is missing in LN at line " + lNodeType.getLineNumber(),
-                        new Object[] { lNodeType } ));
-                return false;
-            }
-            break;
-        default:
-            AbstractRiseClipseConsole.getConsole().info( "NOT IMPLEMENTED: AnyLNValidator.checkCompulsory( " + dataObject.getPresCond() + " )" );
-            break;
+            presenceConditionValidator.addDO( do_, diagnostics );
         }
-        return true;
-    }
-
-    private boolean updateCompulsory( DO do_, HashSet< String > checkedDO, DiagnosticChain diagnostics ) {
-        switch( dataObjectMap.get( do_.getName() ).getPresCond() ) {
-        case "M":
-        case "O":
-            if( checkedDO.contains( do_.getName() )) {
-                diagnostics.add( new BasicDiagnostic(
-                        Diagnostic.ERROR,
-                        RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
-                        0,
-                        "[NSD] DO " + do_ + " cannot appear more than once in LN at line " + do_.getParentLNodeType().getLineNumber(),
-                        new Object[] { do_ } ));
-                return false;
-            }
-            checkedDO.add( do_.getName() );
-            break;
-        case "F":
-            diagnostics.add( new BasicDiagnostic(
-                    Diagnostic.ERROR,
-                    RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
-                    0,
-                    "[NSD] DO " + do_ + " is forbidden in LN at line " + do_.getParentLNodeType().getLineNumber(),
-                    new Object[] { do_ } ));
-            return false;
-        default:
-            AbstractRiseClipseConsole.getConsole().info( "NOT IMPLEMENTED: AnyLNValidator.updateCompulsory( " + dataObjectMap.get( do_.getName() ).getPresCond() + " )" );
-            break;
-        }
-        return true;
-    }
-
-    private boolean validateDO( DO do_, DiagnosticChain diagnostics ) {
-        AbstractRiseClipseConsole.getConsole().verbose( "[NSD] found DO " + do_.getName() + " in LNClass " + lnClassName );
-
-        // DOIValidator validates DOI content
-        String cdc = dataObjectMap.get( do_.getName() ).getRefersToCDC().getName();
-        return doValidatorMap.get( cdc ).validateDO( do_, diagnostics );
+      
+        return presenceConditionValidator.validate( lNodeType, diagnostics );
     }
 
 }
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/NsdEObjectValidator.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/NsdEObjectValidator.java
index b428306..8820de5 100644
--- a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/NsdEObjectValidator.java
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/NsdEObjectValidator.java
@@ -90,7 +90,7 @@ public class NsdEObjectValidator implements EValidator {
 
             @Override
             public Boolean caseAnyLN( AnyLN anyLN ) {
-                return validateAnyLN( anyLN, diagnostics );
+                return true;//validateAnyLN( anyLN, diagnostics );
             }
 
             @Override
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/PresenceConditionValidator.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/PresenceConditionValidator.java
new file mode 100644
index 0000000..796051b
--- /dev/null
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/nsd/PresenceConditionValidator.java
@@ -0,0 +1,849 @@
+package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.nsd;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.eclipse.emf.common.util.BasicDiagnostic;
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.common.util.DiagnosticChain;
+
+import fr.centralesupelec.edf.riseclipse.iec61850.nsd.AnyLNClass;
+import fr.centralesupelec.edf.riseclipse.iec61850.nsd.DataObject;
+import fr.centralesupelec.edf.riseclipse.iec61850.scl.DO;
+import fr.centralesupelec.edf.riseclipse.iec61850.scl.LNodeType;
+import fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.RiseClipseValidatorSCL;
+import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole;
+import fr.centralesupelec.edf.riseclipse.util.IRiseClipseConsole;
+
+public class PresenceConditionValidator {
+    
+    private static HashMap< String, PresenceConditionValidator > validators = new HashMap<>();
+    
+    public static PresenceConditionValidator get( AnyLNClass anyLNClass ) {
+        if( ! validators.containsKey( anyLNClass.getName() )) {
+            validators.put( anyLNClass.getName(), new PresenceConditionValidator( anyLNClass ));
+        }
+        return validators.get( anyLNClass.getName() );
+    }
+    
+    private AnyLNClass anyLNClass;
+    private PresenceConditionValidator base;
+        
+    // Name of the DataObject/DO, DO
+    private HashMap< String, DO > presentDO = new HashMap<>();
+    
+    private HashSet< String > mandatory;
+    private HashSet< String > optional;
+    private HashSet< String > forbidden;
+//    private HashSet< String > notApplicable;
+//    private HashSet< String > mandatoryMulti;
+//    private HashSet< String > optionalMulti;
+    private HashMap< Integer, HashSet< String > > atLeastOne;
+    private HashMap< Integer, HashSet< String > > atMostOne;
+    private HashMap< Integer, HashSet< String > > allOrNonePerGroup;
+    private HashMap< Integer, HashSet< String > > allOnlyOneGroup;
+    private HashMap< Integer, HashSet< String > > allAtLeastOneGroup;
+    private HashMap< String, String > mandatoryIfSiblingPresentElseForbidden;
+    private HashMap< String, String > mandatoryIfSiblingPresentElseOptional;
+    private HashMap< String, String > optionalIfSiblingPresentElseMandatory;
+    private HashMap< String, String > forbiddenIfSiblingPresentElseMandatory;
+    private HashMap< String, String > mandatoryIfTextConditionElseOptional;
+    private HashMap< String, String > mandatoryIfTextConditionElseForbidden;
+    private HashMap< String, String > optionalIfTextConditionElseForbidden;
+    private HashMap< String, Pair< Integer, Integer > > mandatoryMultiRange;
+    private HashMap< String, Pair< Integer, Integer > > optionalMultiRange;
+    private HashSet< String > mandatoryIfSubstitutionElseForbidden;
+    private HashSet< String > mandatoryInLLN0ElseOptional;
+    private HashSet< String > mandatoryInLLN0ElseForbidden;
+    private HashSet< String > mandatoryIfNameSpaceOfLogicalNodeDeviatesElseOptional;
+    private HashSet< String > mandatoryIfNameSpaceOfDataObjectDeviatesElseOptional;
+    private HashSet< String > mandatoryIfAnalogValueIncludesIElseForbidden;
+    private HashSet< String > mandatoryIfVectorSiblingIncludesIAsChildMagElseForbidden;
+    private HashSet< String > mandatoryIfVectorSiblingIncludesIAsChildAngElseForbidden;
+    private HashSet< String > mandatoryIfHarmonicValuesCalculatedAsRatioElseOptional;
+    private HashSet< String > mandatoryInRootLogicalDeviceElseOptional;
+    private HashSet< String > mandatoryIfControlSupportsTimeElseOptional;
+    private HashMap< String, String > oneOrMoreIfSiblingPresentElseForbidden;
+    private HashSet< String > mandatoryIfControlSupportsSecurity1ElseOptional;
+    private HashSet< String > mandatoryIfControlSupportsSecurity2ElseOptional;
+    private HashMap< String, String > optionalIfSiblingPresentElseForbidden;
+    private HashSet< String > mandatoryIfNameSpaceOfLogicalNodeDeviatesElseOptional2;
+    private HashSet< String > mandatoryIfMeasuredValueExposesRange;
+    private HashSet< String > optionalIfPhsRefIsSynchrophasorElseMandatory;
+    
+    private final IRiseClipseConsole console = AbstractRiseClipseConsole.getConsole();
+    
+    private PresenceConditionValidator( AnyLNClass anyLNClass ) {
+        this.anyLNClass = anyLNClass;
+        
+        for( DataObject dObj : anyLNClass.getDataObject() ) {
+            addSpecification( dObj.getName(), dObj.getPresCond(), dObj.getPresCondArgs() );
+        }
+        checkSpecification();
+        
+        AnyLNClass parent = anyLNClass.getRefersToAbstractLNClass();
+        if( parent != null ) {
+            base = get( parent );
+        }
+    }
+    
+    public void reset() {
+        presentDO = new HashMap<>();
+        
+        if( base != null ) base.reset();
+    }
+    
+    private void addSpecification( String name, String presCond, String presCondArgs ) {
+        if( presentDO.containsKey( name )) {
+            console.warning( "[NSD] " + name + " has already been added to PresenceConditionValidator" );
+            return;
+        }
+        presentDO.put( name, null );
+
+        switch( presCond ) {
+        case "M" :
+            // Element is mandatory
+            if( mandatory == null ) mandatory = new HashSet<>();
+            mandatory.add( name );
+            break;
+        case "O" :
+            // Element is optional
+            if( optional == null ) optional = new HashSet<>();
+            optional.add( name );
+            break;
+        case "F" :
+            // Element is forbidden
+            if( forbidden == null ) forbidden = new HashSet<>();
+            forbidden.add( name );
+            break;
+        case "na" :
+            // Element is not applicable
+            // -> TODO: what does it mean ? what do we have to check ?
+            console.warning( "[NSD] NOT IMPLEMENTED" + name + " declared as \"na\" in PresenceCondition" );
+//            if( notApplicable == null ) notApplicable = new HashSet<>();
+//            notApplicable.add( name );
+            break;
+        case "Mmulti" :
+            // At least one element shall be present; all instances have an instance number > 0
+            // -> TODO: not sure what is the instance number and no example found 
+            console.warning( "[NSD] NOT IMPLEMENTED" + name + " declared as \"Mmulti\" in PresenceCondition" );
+//            if( mandatoryMulti == null ) mandatoryMulti = new HashSet<>();
+//            mandatoryMulti.add( name );
+            break;
+        case "Omulti" :
+            // Zero or more elements may be present; all instances have an instance number > 0
+            // -> TODO: not sure what is the instance number and no example found 
+            console.warning( "[NSD] NOT IMPLEMENTED" + name + " declared as \"Omulti\" in PresenceCondition" );
+//            if( optionalMulti == null ) optionalMulti = new HashSet<>();
+//            optionalMulti.add( name );
+            break;
+        case "AtLeastOne" :
+            // Parameter n: group number (> 0).
+            // At least one of marked elements of a group n shall be present
+            if( atLeastOne == null ) atLeastOne = new HashMap<>();
+            try {
+                Integer arg = Integer.valueOf( presCondArgs );
+                if( arg <= 0 ) {
+                    console.warning( "NSD: argument of PresenceCondition \"AtLeastOne\" is not a positive integer" );
+                    break;
+                }
+                if( ! atLeastOne.containsKey( arg )) {
+                    atLeastOne.put( arg, new HashSet<>() );
+                }
+                atLeastOne.get( arg ).add( name );
+                break;
+            }
+            catch( NumberFormatException e ) {
+                console.warning( "NSD: argument of PresenceCondition \"AtLeastOne\" is not an integer" );
+                break;
+            }
+        case "AtMostOne" :
+            // At most one of marked elements shall be present
+            if( atMostOne == null ) atMostOne = new HashMap<>();
+            try {
+                Integer arg = Integer.valueOf( presCondArgs );
+                if( arg <= 0 ) {
+                    console.warning( "NSD: argument of PresenceCondition \"AtMostOne\" is not a positive integer" );
+                    break;
+                }
+                if( ! atMostOne.containsKey( arg )) {
+                    atMostOne.put( arg, new HashSet<>() );
+                }
+                atMostOne.get( arg ).add( name );
+                break;
+            }
+            catch( NumberFormatException e ) {
+                console.warning( "NSD: argument of PresenceCondition \"AtMostOne\" is not an integer" );
+                break;
+            }
+        case "AllOrNonePerGroup" :
+            // Parameter n: group number (> 0).
+            // All or none of the elements of a group n shall be present
+            if( allOrNonePerGroup == null ) allOrNonePerGroup = new HashMap<>();
+            try {
+                Integer arg = Integer.valueOf( presCondArgs );
+                if( arg <= 0 ) {
+                    console.warning( "NSD: argument of PresenceCondition \"AllOrNonePerGroup\" is not a positive integer" );
+                    break;
+                }
+                if( ! allOrNonePerGroup.containsKey( arg )) {
+                    allOrNonePerGroup.put( arg, new HashSet<>() );
+                }
+                allOrNonePerGroup.get( arg ).add( name );
+                break;
+            }
+            catch( NumberFormatException e ) {
+                console.warning( "NSD: argument of PresenceCondition \"AllOrNonePerGroup\" is not an integer" );
+                break;
+            }
+        case "AllOnlyOneGroup" :
+            // Parameter n: group number (> 0).
+            // All elements of only one group n shall be present
+            if( allOnlyOneGroup == null ) allOnlyOneGroup = new HashMap<>();
+            try {
+                Integer arg = Integer.valueOf( presCondArgs );
+                if( arg <= 0 ) {
+                    console.warning( "NSD: argument of PresenceCondition \"AllOnlyOneGroup\" is not a positive integer" );
+                    break;
+                }
+                if( ! allOnlyOneGroup.containsKey( arg )) {
+                    allOnlyOneGroup.put( arg, new HashSet<>() );
+                }
+                allOnlyOneGroup.get( arg ).add( name );
+                break;
+            }
+            catch( NumberFormatException e ) {
+                console.warning( "NSD: argument of PresenceCondition \"AllOnlyOneGroup\" is not an integer" );
+                break;
+            }
+        case "AllAtLeastOneGroup" :
+            // Parameter n: group number (> 0).
+            // All elements of at least one group n shall be present
+            if( allAtLeastOneGroup == null ) allAtLeastOneGroup = new HashMap<>();
+            try {
+                Integer arg = Integer.valueOf( presCondArgs );
+                if( arg <= 0 ) {
+                    console.warning( "NSD: argument of PresenceCondition \"AllAtLeastOneGroup\" is not a positive integer" );
+                    break;
+                }
+                if( ! allAtLeastOneGroup.containsKey( arg )) {
+                    allAtLeastOneGroup.put( arg, new HashSet<>() );
+                }
+                allAtLeastOneGroup.get( arg ).add( name );
+                break;
+            }
+            catch( NumberFormatException e ) {
+                console.warning( "NSD: argument of PresenceCondition \"AllAtLeastOneGroup\" is not an integer" );
+                break;
+            }
+        case "MF" :
+            // Parameter sibling: sibling element name.
+            // Mandatory if sibling element is present, otherwise forbidden
+            if( mandatoryIfSiblingPresentElseForbidden == null ) mandatoryIfSiblingPresentElseForbidden = new HashMap<>();
+            mandatoryIfSiblingPresentElseForbidden.put( name, presCondArgs );
+            break;
+        case "MO" :
+            // Parameter sibling: sibling element name.
+            // Mandatory if sibling element is present, otherwise optional
+            if( mandatoryIfSiblingPresentElseOptional == null ) mandatoryIfSiblingPresentElseOptional = new HashMap<>();
+            mandatoryIfSiblingPresentElseOptional.put( name, presCondArgs );
+            break;
+        case "OM" :
+            // Parameter sibling: sibling element name.
+            // Optional if sibling element is present, otherwise mandatory
+            if( optionalIfSiblingPresentElseMandatory == null ) optionalIfSiblingPresentElseMandatory = new HashMap<>();
+            optionalIfSiblingPresentElseMandatory.put( name, presCondArgs );
+            break;
+        case "FM" :
+            // Parameter sibling: sibling element name.
+            // Forbidden if sibling element is present, otherwise mandatory
+            if( forbiddenIfSiblingPresentElseMandatory == null ) forbiddenIfSiblingPresentElseMandatory = new HashMap<>();
+            forbiddenIfSiblingPresentElseMandatory.put( name, presCondArgs );
+            break;
+        case "MOcond" :
+            // Parameter condID: condition number (> 0).
+            // Textual presence condition (non-machine processable) with reference condID to context specific text.
+            // If satisfied, the element is mandatory, otherwise optional
+            if( mandatoryIfTextConditionElseOptional == null ) mandatoryIfTextConditionElseOptional = new HashMap<>();
+            try {
+                Integer arg = Integer.valueOf( presCondArgs );
+                if( arg <= 0 ) {
+                    console.warning( "NSD: argument of PresenceCondition \"MOcond\" is not a positive integer" );
+                    break;
+                }
+                mandatoryIfTextConditionElseOptional.put( name, presCondArgs );
+                break;
+            }
+            catch( NumberFormatException e ) {
+                console.warning( "NSD: argument of PresenceCondition \"MOcond\" is not an integer" );
+                break;
+            }
+        case "MFcond" :
+            // Parameter condID: condition number (> 0).
+            // Textual presence condition (non-machine processable) with reference condID to context specific text.
+            // If satisfied, the element is mandatory, otherwise forbidden
+            if( mandatoryIfTextConditionElseForbidden == null ) mandatoryIfTextConditionElseForbidden = new HashMap<>();
+            try {
+                Integer arg = Integer.valueOf( presCondArgs );
+                if( arg <= 0 ) {
+                    console.warning( "NSD: argument of PresenceCondition \"MFcond\" is not a positive integer" );
+                    break;
+                }
+                mandatoryIfTextConditionElseForbidden.put( name, presCondArgs );
+                break;
+            }
+            catch( NumberFormatException e ) {
+                console.warning( "NSD: argument of PresenceCondition \"MFcond\" is not an integer" );
+                break;
+            }
+        case "OFcond" :
+            // Parameter condID: condition number (> 0).
+            // Textual presence condition (non-machine processable) with reference condID to context specific text.
+            // If satisfied, the element is optional, otherwise forbidden
+            if( optionalIfTextConditionElseForbidden == null ) optionalIfTextConditionElseForbidden = new HashMap<>();
+            try {
+                Integer arg = Integer.valueOf( presCondArgs );
+                if( arg <= 0 ) {
+                    console.warning( "NSD: argument of PresenceCondition \"MFcond\" is not a positive integer" );
+                    break;
+                }
+                optionalIfTextConditionElseForbidden.put( name, presCondArgs );
+                break;
+            }
+            catch( NumberFormatException e ) {
+                console.warning( "NSD: argument of PresenceCondition \"MFcond\" is not an integer" );
+                break;
+            }
+        case "MmultiRange" :
+            // Parameters min, max: limits for instance number (> 0).
+            // One or more elements shall be present; all instances have an instance number within range [min, max] (see IEC 61850-7-1)
+            // -> TODO: not sure what is the instance number and no example found 
+            if( mandatoryMultiRange == null ) mandatoryMultiRange = new HashMap<>();
+            String[] limits1 = presCondArgs.split( "\\d[ ,]+\\d" );
+            if( limits1.length != 2 ) {
+                console.warning( "NSD: argument of PresenceCondition \"MmultiRange\" is not two integers" );
+                break;
+            }
+            Integer min1 = Integer.valueOf( limits1[0] );
+            if( min1 <= 0 ) {
+                console.warning( "NSD: first argument of PresenceCondition \"MmultiRange\" is not a positive integer" );
+                break;
+            }
+            Integer max1 = Integer.valueOf( limits1[1] );
+            if( max1 <= 0 ) {
+                console.warning( "NSD: second argument of PresenceCondition \"MmultiRange\" is not a positive integer" );
+                break;
+            }
+            mandatoryMultiRange.put( name, Pair.of( min1, max1 ));
+            break;
+        case "OmultiRange" :
+            // Parameters min, max: limits for instance number (> 0).
+            // Zero or more elements may be present; all instances have an instance number within range [min, max] (see IEC 61850-7-1)
+            // -> TODO: not sure what is the instance number and no example found 
+            if( optionalMultiRange == null ) optionalMultiRange = new HashMap<>();
+            String[] limits2 = presCondArgs.split( "\\d[ ,]+\\d" );
+            if( limits2.length != 2 ) {
+                console.warning( "NSD: argument of PresenceCondition \"OmultiRange\" is not two integers" );
+                break;
+            }
+            Integer min2 = Integer.valueOf( limits2[0] );
+            if( min2 <= 0 ) {
+                console.warning( "NSD: first argument of PresenceCondition \"OmultiRange\" is not a positive integer" );
+                break;
+            }
+            Integer max2 = Integer.valueOf( limits2[1] );
+            if( max2 <= 0 ) {
+                console.warning( "NSD: second argument of PresenceCondition \"OmultiRange\" is not a positive integer" );
+                break;
+            }
+            optionalMultiRange.put( name, Pair.of( min2, max2 ));
+            break;
+        case "MFsubst" :
+            // Element is mandatory if substitution is supported (for substitution, see IEC 61850-7-3), otherwise forbidden
+            if( mandatoryIfSubstitutionElseForbidden == null ) mandatoryIfSubstitutionElseForbidden = new HashSet<>();
+            mandatoryIfSubstitutionElseForbidden.add( name );
+            break;
+        case "MOln0" :
+            // Element is mandatory in the context of LLN0; otherwise optional
+            if( mandatoryInLLN0ElseOptional == null ) mandatoryInLLN0ElseOptional = new HashSet<>();
+            mandatoryInLLN0ElseOptional.add( name );
+            break;
+        case "MFln0" :
+            // Element is mandatory in the context of LLN0; otherwise forbidden
+            if( mandatoryInLLN0ElseForbidden == null ) mandatoryInLLN0ElseForbidden = new HashSet<>();
+            mandatoryInLLN0ElseForbidden.add( name );
+            break;
+        case "MOlnNs" :
+            // Element is mandatory if the name space of its logical node deviates from the name space of the containing
+            // logical device, otherwise optional. See IEC 61850-7-1 for use of name space
+            if( mandatoryIfNameSpaceOfLogicalNodeDeviatesElseOptional == null ) mandatoryIfNameSpaceOfLogicalNodeDeviatesElseOptional = new HashSet<>();
+            mandatoryIfNameSpaceOfLogicalNodeDeviatesElseOptional.add( name );
+            break;
+        case "MOdataNs" :
+            // Element is mandatory if the name space of its data object deviates from the name space of its logical node,
+            // otherwise optional. See IEC 61850-7-1 for use of name space
+            if( mandatoryIfNameSpaceOfDataObjectDeviatesElseOptional == null ) mandatoryIfNameSpaceOfDataObjectDeviatesElseOptional = new HashSet<>();
+            mandatoryIfNameSpaceOfDataObjectDeviatesElseOptional.add( name );
+            break;
+        case "MFscaledAV" :
+            // Element is mandatory* if any sibling elements of type AnalogueValue include 'i' as a child, otherwise forbidden.
+            // *Even though devices without floating point capability cannot exchange floating point values through ACSI services,
+            // the description of scaling remains mandatory for their (SCL) configuration
+            if( mandatoryIfAnalogValueIncludesIElseForbidden == null ) mandatoryIfAnalogValueIncludesIElseForbidden = new HashSet<>();
+            mandatoryIfAnalogValueIncludesIElseForbidden.add( name );
+            break;
+        case "MFscaledMagV" :
+            // Element is mandatory* if any sibling elements of type Vector include 'i' as a child of their 'mag' attribute, otherwise forbidden.
+            // *See MFscaledAV
+            if( mandatoryIfVectorSiblingIncludesIAsChildMagElseForbidden == null ) mandatoryIfVectorSiblingIncludesIAsChildMagElseForbidden = new HashSet<>();
+            mandatoryIfVectorSiblingIncludesIAsChildMagElseForbidden.add( name );
+            break;
+        case "MFscaledAngV" :
+            // Element is mandatory* if any sibling elements of type Vector include 'i' as a child of their 'ang' attribute, otherwise forbidden.
+            // *See MFscaledAV
+            if( mandatoryIfVectorSiblingIncludesIAsChildAngElseForbidden == null ) mandatoryIfVectorSiblingIncludesIAsChildAngElseForbidden = new HashSet<>();
+            mandatoryIfVectorSiblingIncludesIAsChildAngElseForbidden.add( name );
+            break;
+        case "MOrms" :
+            // Element is mandatory if the harmonic values in the context are calculated as a ratio to RMS value
+            // (value of data attribute 'hvRef' is 'rms'), optional otherwise
+            if( mandatoryIfHarmonicValuesCalculatedAsRatioElseOptional == null ) mandatoryIfHarmonicValuesCalculatedAsRatioElseOptional = new HashSet<>();
+            mandatoryIfHarmonicValuesCalculatedAsRatioElseOptional.add( name );
+            break;
+        case "MOrootLD" :
+            // Element is mandatory in the context of a root logical device; otherwise it is optional
+            if( mandatoryInRootLogicalDeviceElseOptional == null ) mandatoryInRootLogicalDeviceElseOptional = new HashSet<>();
+            mandatoryInRootLogicalDeviceElseOptional.add( name );
+            break;
+        case "MOoperTm" :
+            // Element is mandatory if at least one controlled object on the IED supports time activation service; otherwise it is optional
+            if( mandatoryIfControlSupportsTimeElseOptional == null ) mandatoryIfControlSupportsTimeElseOptional = new HashSet<>();
+            mandatoryIfControlSupportsTimeElseOptional.add( name );
+            break;
+        case "MmultiF" :
+            // Parameter sibling: sibling element name.
+            // One or more elements must be present if sibling element is present, otherwise forbidden
+            if( oneOrMoreIfSiblingPresentElseForbidden == null ) oneOrMoreIfSiblingPresentElseForbidden = new HashMap<>();
+            oneOrMoreIfSiblingPresentElseForbidden.put( name, presCondArgs );
+            break;
+        case "MOsbo" :
+            // Element is mandatory if declared control model supports 'sbo-with-normal-security' or 'sbo-with-enhanced-security',
+            // otherwise optional and value is of no impact
+            if( mandatoryIfControlSupportsSecurity1ElseOptional == null ) mandatoryIfControlSupportsSecurity1ElseOptional = new HashSet<>();
+            mandatoryIfControlSupportsSecurity1ElseOptional.add( name );
+            break;
+        case "MOenhanced" :
+            // Element is mandatory if declared control model supports 'direct-with-enhanced-security' or 'sbo-with-enhanced-security',
+            // otherwise optional and value is of no impact
+            if( mandatoryIfControlSupportsSecurity2ElseOptional == null ) mandatoryIfControlSupportsSecurity2ElseOptional = new HashSet<>();
+            mandatoryIfControlSupportsSecurity2ElseOptional.add( name );
+            break;
+        case "MONamPlt" :
+            // Element is mandatory if the name space of its logical node deviates from the name space of the containing
+            // logical device, otherwise optional. See IEC 61850-7-1 for use of name space
+            // TODO: same as "MOlnNs" ?
+            if( mandatoryIfNameSpaceOfLogicalNodeDeviatesElseOptional2 == null ) mandatoryIfNameSpaceOfLogicalNodeDeviatesElseOptional2 = new HashSet<>();
+            mandatoryIfNameSpaceOfLogicalNodeDeviatesElseOptional2.add( name );
+            break;
+        case "OF" :
+            // Parameter sibling: sibling element name.
+            // Optional if sibling element is present, otherwise forbidden
+            if( optionalIfSiblingPresentElseForbidden == null ) optionalIfSiblingPresentElseForbidden = new HashMap<>();
+            optionalIfSiblingPresentElseForbidden.put( name, presCondArgs );
+            break;
+        case "MORange" :
+            // Element is mandatory if the measured value associated (amplitude respectively angle) exposes the range eventing
+            // (with the attribute range respectively rangeAng)
+            if( mandatoryIfMeasuredValueExposesRange == null ) mandatoryIfMeasuredValueExposesRange = new HashSet<>();
+            mandatoryIfMeasuredValueExposesRange.add( name );
+            break;
+        case "OMSynPh" :
+            // This attribute is optional if value of 'phsRef'' is Synchrophasor otherwise Mandatory]]></Doc>
+            if( optionalIfPhsRefIsSynchrophasorElseMandatory == null ) optionalIfPhsRefIsSynchrophasorElseMandatory = new HashSet<>();
+            optionalIfPhsRefIsSynchrophasorElseMandatory.add( name );
+            break;
+        default:
+            console.warning( "[NSD] the PresenceCondition " + presCond + " of AnyLNClass " + name + " is unknown" );
+            break;
+        }
+        
+    }
+    
+    private void checkSpecification() {
+        if( mandatoryIfSiblingPresentElseForbidden != null ) {
+            for( Entry< String, String > e : mandatoryIfSiblingPresentElseForbidden.entrySet() ) {
+                if( ! presentDO.containsKey( e.getValue() )) {
+                    console.warning( "[NSD] the sibling of " + e.getKey() + " in PresenceCondition od DataObject " + e.getKey() + " is unknown" );
+                }
+            }
+        }
+        if( mandatoryIfSiblingPresentElseOptional != null ) {
+            for( Entry< String, String > e : mandatoryIfSiblingPresentElseOptional.entrySet() ) {
+                if( ! presentDO.containsKey( e.getValue() )) {
+                    console.warning( "[NSD] the sibling of " + e.getKey() + " in PresenceCondition od DataObject " + e.getKey() + " is unknown" );
+                }
+            }
+        }
+        if( optionalIfSiblingPresentElseMandatory != null ) {
+            for( Entry< String, String > e : optionalIfSiblingPresentElseMandatory.entrySet() ) {
+                if( ! presentDO.containsKey( e.getValue() )) {
+                    console.warning( "[NSD] the sibling of " + e.getKey() + " in PresenceCondition od DataObject " + e.getKey() + " is unknown" );
+                }
+            }
+        }
+        if( forbiddenIfSiblingPresentElseMandatory != null ) {
+            for( Entry< String, String > e : forbiddenIfSiblingPresentElseMandatory.entrySet() ) {
+                if( ! presentDO.containsKey( e.getValue() )) {
+                    console.warning( "[NSD] the sibling of " + e.getKey() + " in PresenceCondition od DataObject " + e.getKey() + " is unknown" );
+                }
+            }
+        }
+        if( oneOrMoreIfSiblingPresentElseForbidden != null ) {
+            for( Entry< String, String > e : oneOrMoreIfSiblingPresentElseForbidden.entrySet() ) {
+                if( ! presentDO.containsKey( e.getValue() )) {
+                    console.warning( "[NSD] the sibling of " + e.getKey() + " in PresenceCondition od DataObject " + e.getKey() + " is unknown" );
+                }
+            }
+        }
+        if( optionalIfSiblingPresentElseForbidden != null ) {
+            for( Entry< String, String > e : optionalIfSiblingPresentElseForbidden.entrySet() ) {
+                if( ! presentDO.containsKey( e.getValue() )) {
+                    console.warning( "[NSD] the sibling of " + e.getKey() + " in PresenceCondition od DataObject " + e.getKey() + " is unknown" );
+                }
+            }
+        }
+    }
+
+    public boolean addDO( DO do_, DiagnosticChain diagnostics ) {
+        if( ! presentDO.containsKey( do_.getName() )) {
+            if( base != null ) {
+                return base.addDO( do_, diagnostics );
+            }
+            diagnostics.add( new BasicDiagnostic(
+                    Diagnostic.ERROR,
+                    RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                    0,
+                    "[NSD] DO " + do_.getName() + " in LNodeType (line " + do_.getParentLNodeType().getLineNumber() + ") not found in LNClass " + anyLNClass.getName(),
+                    new Object[] { do_ } ));
+            return false;
+        }
+
+        if( presentDO.get( do_.getName() ) != null ) {
+            diagnostics.add( new BasicDiagnostic(
+                    Diagnostic.ERROR,
+                    RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                    0,
+                    "[NSD] DO " + do_.getName() + " in LNodeType (line " + do_.getParentLNodeType().getLineNumber() + ") already present in LNClass " + anyLNClass.getName(),
+                    new Object[] { do_ } ));
+            return false;
+        }
+        presentDO.put( do_.getName(), do_ );
+        return true;
+    }
+    
+    public boolean validate( LNodeType lNodeType, DiagnosticChain diagnostics ) {
+        boolean res = true;
+        
+        // presCond: "M"
+        // Element is mandatory
+        if( mandatory != null ) {
+            for( String name : this.mandatory ) {
+                if( presentDO.get( name ) == null ) {
+                  diagnostics.add( new BasicDiagnostic(
+                          Diagnostic.ERROR,
+                          RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                          0,
+                          "[NSD] DO " + name + " is mandatory in LNodeType (line " + lNodeType.getLineNumber() + ") with LNClass " + anyLNClass.getName(),
+                          new Object[] { lNodeType } ));
+                  res = false;
+                }
+            }
+        }
+
+        // presCond: "O"
+        // Element is optional
+        // Nothing to do
+
+        // presCond: "F"
+        // Element is forbidden
+        if( forbidden != null ) {
+            for( String name : this.forbidden ) {
+                if( presentDO.get( name ) != null ) {
+                  diagnostics.add( new BasicDiagnostic(
+                          Diagnostic.ERROR,
+                          RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                          0,
+                          "[NSD] DO " + name + " is forbidden in LNodeType (line " + lNodeType.getLineNumber() + ") with LNClass " + anyLNClass.getName(),
+                          new Object[] { lNodeType } ));
+                  res = false;
+                }
+            }
+        }
+
+        // presCond: "na"
+        // Element is not applicable
+        // -> TODO: what does it mean ? what do we have to check ?
+        
+        // presCond: "Mmulti"
+        // At least one element shall be present; all instances have an instance number > 0
+        // -> TODO: not sure what is the instance number and no example found 
+
+        // presCond: "Omulti"
+        // Zero or more elements may be present; all instances have an instance number > 0
+        // -> TODO: not sure what is the instance number and no example found 
+
+        // presCond: "AtLeastOne"
+        // Parameter n: group number (> 0).
+        // At least one of marked elements of a group n shall be present
+        if( atLeastOne != null ) {
+            for( Entry< Integer, HashSet< String > > e1 : atLeastOne.entrySet() ) {
+                boolean groupOK = false;
+                for( String member : e1.getValue() ) {
+                    if( presentDO.get( member ) != null ) {
+                        groupOK = true;
+                        break;
+                    }
+                }
+                if( ! groupOK ) {
+                    diagnostics.add( new BasicDiagnostic(
+                            Diagnostic.ERROR,
+                            RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                            0,
+                            "[NSD] group " + e1.getKey() + " has no elements in LNodeType (line " + lNodeType.getLineNumber() + ") with LNClass " + anyLNClass.getName(),
+                            new Object[] { lNodeType } ));
+                    res = false;
+                }
+            }
+        }
+        // presCond: "AtMostOne" :
+        // At most one of marked elements shall be present
+        if( atMostOne != null ) {
+            for( Entry< Integer, HashSet< String > > e1 : atMostOne.entrySet() ) {
+                int groupCount = 0;
+                for( String member : e1.getValue() ) {
+                    if( presentDO.get( member ) != null ) {
+                        ++groupCount;
+                    }
+                }
+                if( groupCount > 1 ) {
+                    diagnostics.add( new BasicDiagnostic(
+                            Diagnostic.ERROR,
+                            RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                            0,
+                            "[NSD] group " + e1.getKey() + " has more than one element in LNodeType (line " + lNodeType.getLineNumber() + ") with LNClass " + anyLNClass.getName(),
+                            new Object[] { lNodeType } ));
+                    res = false;
+                }
+            }
+        }
+        // presCond: "AllOrNonePerGroup" :
+        // Parameter n: group number (> 0).
+        // All or none of the elements of a group n shall be present
+        if( allOrNonePerGroup != null ) {
+            for( Entry< Integer, HashSet< String > > e1 : allOrNonePerGroup.entrySet() ) {
+                int groupCount = 0;
+                for( String member : e1.getValue() ) {
+                    if( presentDO.get( member ) != null ) {
+                        ++groupCount;
+                    }
+                }
+                if(( groupCount > 0 ) && (groupCount < e1.getValue().size() )) {
+                    diagnostics.add( new BasicDiagnostic(
+                            Diagnostic.ERROR,
+                            RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                            0,
+                            "[NSD] group " + e1.getKey() + " has neither none nor all elements in LNodeType (line " + lNodeType.getLineNumber() + ") with LNClass " + anyLNClass.getName(),
+                            new Object[] { lNodeType } ));
+                    res = false;
+                }
+            }
+        }
+        // presCond: "AllOnlyOneGroup" :
+        // Parameter n: group number (> 0).
+        // All elements of only one group n shall be present
+        if( allOnlyOneGroup != null ) {
+            int groupNumber = 0;
+            for( Entry< Integer, HashSet< String > > e1 : allOnlyOneGroup.entrySet() ) {
+                int groupCount = 0;
+                for( String member : e1.getValue() ) {
+                    if( presentDO.get( member ) != null ) {
+                        ++groupCount;
+                    }
+                }
+                if(( groupCount > 0 ) && (groupCount < e1.getValue().size() )) {
+                    diagnostics.add( new BasicDiagnostic(
+                            Diagnostic.ERROR,
+                            RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                            0,
+                            "[NSD] group " + e1.getKey() + " has neither none nor all elements in LNodeType (line " + lNodeType.getLineNumber() + ") with LNClass " + anyLNClass.getName(),
+                            new Object[] { lNodeType } ));
+                    res = false;
+                }
+                else if( groupCount > 0 ) {
+                    if( groupNumber == 0 ) {
+                        groupNumber = e1.getKey();
+                    }
+                    else {
+                        diagnostics.add( new BasicDiagnostic(
+                                Diagnostic.ERROR,
+                                RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                                0,
+                                "[NSD] LNodeType (line " + lNodeType.getLineNumber() + ") with LNClass " + anyLNClass.getName() + " has several groups with all elements",
+                                new Object[] { lNodeType } ));
+                        res = false;
+                    }
+                }
+            }
+            if( groupNumber == 0 ) {
+                diagnostics.add( new BasicDiagnostic(
+                        Diagnostic.ERROR,
+                        RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                        0,
+                        "[NSD] no group in LNodeType (line " + lNodeType.getLineNumber() + ") with LNClass " + anyLNClass.getName() + " has all elements",
+                        new Object[] { lNodeType } ));
+                res = false;
+            }
+        }
+        // presCond: "AllAtLeastOneGroup" :
+        // Parameter n: group number (> 0).
+        // All elements of at least one group n shall be present
+        if( allAtLeastOneGroup != null ) {
+            int groupNumber = 0;
+            for( Entry< Integer, HashSet< String > > e1 : allAtLeastOneGroup.entrySet() ) {
+                int groupCount = 0;
+                for( String member : e1.getValue() ) {
+                    if( presentDO.get( member ) != null ) {
+                        ++groupCount;
+                    }
+                }
+                if( groupCount == e1.getValue().size() ) {
+                    groupNumber = e1.getKey();
+                }
+            }
+            if( groupNumber == 0 ) {
+                diagnostics.add( new BasicDiagnostic(
+                        Diagnostic.ERROR,
+                        RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                        0,
+                        "[NSD] no group in LNodeType (line " + lNodeType.getLineNumber() + ") with LNClass " + anyLNClass.getName() + " has all elements",
+                        new Object[] { lNodeType } ));
+                res = false;
+            }
+        }
+        /*
+        // presCond: "MF" :
+        // Parameter sibling: sibling element name.
+        // Mandatory if sibling element is present, otherwise forbidden
+        mandatoryIfSiblingPresentElseForbidden.put( name, Pair.of( mandatoryIfSiblingPresentElseForbidden.get( name ).getKey(), true ));
+        // presCond: "MO" :
+        // Parameter sibling: sibling element name.
+        // Mandatory if sibling element is present, otherwise optional
+        mandatoryIfSiblingPresentElseOptional.put( name, Pair.of( mandatoryIfSiblingPresentElseOptional.get( name ).getKey(), true ));
+        // presCond: "OM" :
+        // Parameter sibling: sibling element name.
+        // Optional if sibling element is present, otherwise mandatory
+        optionalIfSiblingPresentElseMandatory.put( name, Pair.of( optionalIfSiblingPresentElseMandatory.get( name ).getKey(), true ));
+        // presCond: "FM" :
+        // Parameter sibling: sibling element name.
+        // Forbidden if sibling element is present, otherwise mandatory
+        forbiddenIfSiblingPresentElseMandatory.put( name, Pair.of( forbiddenIfSiblingPresentElseMandatory.get( name ).getKey(), true ));
+        // presCond: "MOcond" :
+        // Parameter condID: condition number (> 0).
+        // Textual presence condition (non-machine processable) with reference condID to context specific text.
+        // If satisfied, the element is mandatory, otherwise optional
+        mandatoryIfTextConditionElseOptional.put( name, Pair.of( mandatoryIfTextConditionElseOptional.get( name ).getKey(), false ));
+        // presCond: "MFcond" :
+        // Parameter condID: condition number (> 0).
+        // Textual presence condition (non-machine processable) with reference condID to context specific text.
+        // If satisfied, the element is mandatory, otherwise forbidden
+        mandatoryIfTextConditionElseForbidden.put( name, Pair.of( mandatoryIfTextConditionElseForbidden.get( name ).getKey(), false ));
+        // presCond: "OFcond" :
+        // Parameter condID: condition number (> 0).
+        // Textual presence condition (non-machine processable) with reference condID to context specific text.
+        // If satisfied, the element is optional, otherwise forbidden
+        optionalIfTextConditionElseForbidden.put( name, Pair.of( optionalIfTextConditionElseForbidden.get( name ).getKey(), false ));
+        // presCond: "MmultiRange" :
+        // Parameters min, max: limits for instance number (> 0).
+        // One or more elements shall be present; all instances have an instance number within range [min, max] (see IEC 61850-7-1)
+        // -> TODO: not sure what is the instance number and no example found 
+        mandatoryMultiRange.put( name, Pair.of( Pair.of( min1, max1 ), new HashSet<>() ));
+        // presCond: "OmultiRange" :
+        // Parameters min, max: limits for instance number (> 0).
+        // Zero or more elements may be present; all instances have an instance number within range [min, max] (see IEC 61850-7-1)
+        // -> TODO: not sure what is the instance number and no example found 
+        optionalMultiRange.put( name, Pair.of( Pair.of( min2, max2 ), new HashSet<>() ));
+        // presCond: "MFsubst" :
+        // Element is mandatory if substitution is supported (for substitution, see IEC 61850-7-3), otherwise forbidden
+        mandatoryIfSubstitutionElseForbidden.put( name, false );
+        // presCond: "MOln0" :
+        // Element is mandatory in the context of LLN0; otherwise optional
+        mandatoryInLLN0ElseOptional.put( name, false );
+        // presCond: "MFln0" :
+        // Element is mandatory in the context of LLN0; otherwise forbidden
+        mandatoryInLLN0ElseForbidden.put( name, false );
+        // presCond: "MOlnNs" :
+        // Element is mandatory if the name space of its logical node deviates from the name space of the containing
+        // logical device, otherwise optional. See IEC 61850-7-1 for use of name space
+        mandatoryIfNameSpaceOfLogicalNodeDeviatesElseOptional.put( name, false );
+        // presCond: "MOdataNs" :
+        // Element is mandatory if the name space of its data object deviates from the name space of its logical node,
+        // otherwise optional. See IEC 61850-7-1 for use of name space
+        mandatoryIfNameSpaceOfDataObjectDeviatesElseOptional.put( name, false );
+        // presCond: "MFscaledAV" :
+        // Element is mandatory* if any sibling elements of type AnalogueValue include 'i' as a child, otherwise forbidden.
+        // *Even though devices without floating point capability cannot exchange floating point values through ACSI services,
+        // the description of scaling remains mandatory for their (SCL) configuration
+        mandatoryIfAnalogValueIncludesIElseForbidden.put( name, false );
+        // presCond: "MFscaledMagV" :
+        // Element is mandatory* if any sibling elements of type Vector include 'i' as a child of their 'mag' attribute, otherwise forbidden.
+        // *See MFscaledAV
+        mandatoryIfVectorSiblingIncludesIAsChildMagElseForbidden.put( name, false );
+        // presCond: "MFscaledAngV" :
+        // Element is mandatory* if any sibling elements of type Vector include 'i' as a child of their 'ang' attribute, otherwise forbidden.
+        // *See MFscaledAV
+        mandatoryIfVectorSiblingIncludesIAsChildAngElseForbidden.put( name, false );
+        // presCond: "MOrms" :
+        // Element is mandatory if the harmonic values in the context are calculated as a ratio to RMS value
+        // (value of data attribute 'hvRef' is 'rms'), optional otherwise
+        mandatoryIfHarmonicValuesCalculatedAsRatioElseOptional.put( name, false );
+        // presCond: "MOrootLD" :
+        // Element is mandatory in the context of a root logical device; otherwise it is optional
+        mandatoryInRootLogicalDeviceElseOptional.put( name, false );
+        // presCond: "MOoperTm" :
+        // Element is mandatory if at least one controlled object on the IED supports time activation service; otherwise it is optional
+        mandatoryIfControlSupportsTimeElseOptional.put( name, false );
+        // presCond: "MmultiF" :
+        // Parameter sibling: sibling element name.
+        // One or more elements must be present if sibling element is present, otherwise forbidden
+        oneOrMoreIfSiblingPresentElseForbidden.put( name, Pair.of( presCondArgs, false ));
+        // presCond: "MOsbo" :
+        // Element is mandatory if declared control model supports 'sbo-with-normal-security' or 'sbo-with-enhanced-security',
+        // otherwise optional and value is of no impact
+        mandatoryIfControlSupportsSecurity1ElseOptional.put( name, false );
+        // presCond: "MOenhanced" :
+        // Element is mandatory if declared control model supports 'direct-with-enhanced-security' or 'sbo-with-enhanced-security',
+        // otherwise optional and value is of no impact
+        mandatoryIfControlSupportsSecurity2ElseOptional.put( name, false );
+        // presCond: "MONamPlt" :
+        // Element is mandatory if the name space of its logical node deviates from the name space of the containing
+        // logical device, otherwise optional. See IEC 61850-7-1 for use of name space
+        // TODO: same as "MOlnNs" ?
+        mandatoryIfNameSpaceOfLogicalNodeDeviatesElseOptional2.put( name, false );
+        // presCond: "OF" :
+        // Parameter sibling: sibling element name.
+        // Optional if sibling element is present, otherwise forbidden
+        optionalIfSiblingPresentElseForbidden.put( name, Pair.of( presCondArgs, false ));
+        // presCond: "MORange" :
+        // Element is mandatory if the measured value associated (amplitude respectively angle) exposes the range eventing
+        // (with the attribute range respectively rangeAng)
+        mandatoryIfMeasuredValueExposesRange.put( name, false );
+        // presCond: "OMSynPh" :
+        // This attribute is optional if value of 'phsRef'' is Synchrophasor otherwise Mandatory]]></Doc>
+        optionalIfPhsRefIsSynchrophasorElseMandatory.put( name, false );
+*/
+        return res;
+    }
+
+}
-- 
GitLab