diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/.gitignore b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ae3c1726048cd06b9a143e0376ed46dd9b9a8d53
--- /dev/null
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/.gitignore
@@ -0,0 +1 @@
+/bin/
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 2057aad55681263f9de6b7b040d8bd5c6ae01e30..70b6e48c79d2496fd6da9978220082ed9e30a0d1 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
@@ -19,4 +19,5 @@ Require-Bundle: org.eclipse.core.runtime,
  fr.centralesupelec.edf.riseclipse.iec61850.scl.edit,
  fr.centralesupelec.edf.riseclipse.validation.ocl,
  fr.centralesupelec.edf.riseclipse.iec61850.nsd,
- fr.centralesupelec.edf.riseclipse.iec61850.nsd.edit
+ fr.centralesupelec.edf.riseclipse.iec61850.nsd.edit,
+ org.eclipse.ocl.xtext.oclinecore
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/AnyLNValidator.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/AnyLNValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..62b539d8eb1721d28da4d34e23e90a56f56f70dc
--- /dev/null
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/AnyLNValidator.java
@@ -0,0 +1,165 @@
+/**
+ *  Copyright (c) 2019 CentraleSupélec & EDF.
+ *  All rights reserved. This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License v1.0
+ *  which accompanies this distribution, and is available at
+ *  http://www.eclipse.org/legal/epl-v10.html
+ * 
+ *  This file is part of the RiseClipse tool
+ *  
+ *  Contributors:
+ *      Computer Science Department, CentraleSupélec
+ *      EDF R&D
+ *  Contacts:
+ *      dominique.marcadet@centralesupelec.fr
+ *      aurelie.dehouck-neveu@edf.fr
+ *  Web site:
+ *      http://wdi.supelec.fr/software/RiseClipse/
+ */
+package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
+
+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.AnyLN;
+import fr.centralesupelec.edf.riseclipse.iec61850.scl.DOI;
+import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole;
+
+public class AnyLNValidator {
+
+    private String lnClass;
+    private HashMap< String, DataObject > doMap;
+    private HashMap< String, DOIValidator > cdcMap;
+
+    public AnyLNValidator( LNClass lnClass ) {
+        this.lnClass = lnClass.getName();
+        this.doMap = new HashMap<>(); // link between DOI (name) and its respective DataObject
+        this.cdcMap = new HashMap<>(); // link between CDC (name) and its respective DOIValidator
+        
+        generateValidators( doMap, cdcMap, lnClass );
+
+        // LNClass hierarchy taken into account
+        AbstractLNClass parent = lnClass.getRefersToAbstractLNClass();
+        while( parent != null ) {
+            generateValidators( doMap, cdcMap, parent );
+            parent = parent.getRefersToAbstractLNClass();
+        }
+
+    }
+
+    public void generateValidators( HashMap< String, DataObject > doMap, HashMap< String, DOIValidator > cdcMap, AnyLNClass lnClass ) {
+        for( DataObject dObj : lnClass.getDataObject() ) {
+            doMap.put( dObj.getName(), dObj );
+            if( dObj.getRefersToCDC() != null ) {
+                if( ! cdcMap.containsKey( dObj.getRefersToCDC().getName() )) {
+                    cdcMap.put( dObj.getRefersToCDC().getName(), new DOIValidator( dObj.getRefersToCDC() ));
+                }
+            }
+        }
+    }
+
+    public boolean validateLN( AnyLN ln, DiagnosticChain diagnostics ) {
+        boolean res = true;
+
+        HashSet< String > checkedDO = new HashSet<>();
+
+        for( DOI doi : ln.getDOI() ) {
+            AbstractRiseClipseConsole.getConsole().verbose( "validateDOI( " + doi.getName() + " )" );
+
+            // Test if DOI is a possible DOI in this LN
+            if( ! doMap.containsKey( doi.getName() ) ) {
+                diagnostics.add( new BasicDiagnostic(
+                        Diagnostic.ERROR,
+                        RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                        0,
+                        "DO " + doi.getName() + " in LN at line " + ln.getLineNumber() + " not found in LNClass " + ln.getLnClass(),
+                        new Object[] { ln } ));
+                continue;
+            }
+
+            // Control of DOI presence in LN  
+            updateCompulsory( doi, checkedDO, diagnostics );
+
+            // Validation of DOI content
+            if( ! validateDOI( doi, diagnostics ) ) {
+                res = false;
+            }
+
+        }
+
+        // Verify all necessary DOI were present
+        if( ! doMap.values().stream()
+                .map( x -> checkCompulsory( ln, x, checkedDO, diagnostics ))
+                .reduce( ( a, b ) -> a && b ).get() ) {
+            diagnostics.add( new BasicDiagnostic(
+                    Diagnostic.ERROR,
+                    RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                    0,
+                    "LN at line " + ln.getLineNumber() + " does not contain all mandatory DO from class " + ln.getLnClass(),
+                    new Object[] { ln } ));
+            res = false;
+        }
+        return res;
+    }
+
+    public boolean checkCompulsory( AnyLN ln, 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,
+                        "DO " + dataObject.getName() + " is missing in LN at line " + ln.getLineNumber(),
+                        new Object[] { ln } ));
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public boolean updateCompulsory( DOI doi, HashSet< String > checkedDO, DiagnosticChain diagnostics ) {
+        switch( doMap.get( doi.getName() ).getPresCond() ) {
+        case "M":
+        case "O":
+            if( checkedDO.contains( doi.getName() )) {
+                diagnostics.add( new BasicDiagnostic(
+                        Diagnostic.ERROR,
+                        RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                        0,
+                        "DO " + doi + " cannot appear more than once in LN at line " + doi.getAnyLN().getLineNumber(),
+                        new Object[] { doi } ));
+                return false;
+            }
+            checkedDO.add( doi.getName() );
+            break;
+        case "F":
+            diagnostics.add( new BasicDiagnostic(
+                    Diagnostic.ERROR,
+                    RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                    0,
+                    "DO " + doi + " is forbidden in LN at line " + doi.getAnyLN().getLineNumber(),
+                    new Object[] { doi } ));
+            return false;
+        }
+        return true;
+    }
+
+    public boolean validateDOI( DOI doi, DiagnosticChain diagnostics ) {
+
+        AbstractRiseClipseConsole.getConsole().verbose( "found DO " + doi.getName() + " in LNClass " + lnClass );
+
+        // DOIValidator validates DOI content
+        String cdc = doMap.get( doi.getName() ).getRefersToCDC().getName();
+        return cdcMap.get( cdc ).validateDOI( doi, diagnostics );
+    }
+
+}
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/DOIValidator.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/DOIValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f0d30695d9afca2f916d4795ce2dede4980eed6
--- /dev/null
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/DOIValidator.java
@@ -0,0 +1,347 @@
+/**
+ *  Copyright (c) 2019 CentraleSupélec & EDF.
+ *  All rights reserved. This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License v1.0
+ *  which accompanies this distribution, and is available at
+ *  http://www.eclipse.org/legal/epl-v10.html
+ * 
+ *  This file is part of the RiseClipse tool
+ *  
+ *  Contributors:
+ *      Computer Science Department, CentraleSupélec
+ *      EDF R&D
+ *  Contacts:
+ *      dominique.marcadet@centralesupelec.fr
+ *      aurelie.dehouck-neveu@edf.fr
+ *  Web site:
+ *      http://wdi.supelec.fr/software/RiseClipse/
+ */
+package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
+
+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.CDC;
+import fr.centralesupelec.edf.riseclipse.iec61850.nsd.DataAttribute;
+import fr.centralesupelec.edf.riseclipse.iec61850.scl.DAI;
+import fr.centralesupelec.edf.riseclipse.iec61850.scl.DOI;
+import fr.centralesupelec.edf.riseclipse.iec61850.scl.Val;
+import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole;
+
+public class DOIValidator {
+
+    private String cdc;
+    private HashMap< String, DataAttribute > daMap;
+
+    public DOIValidator( CDC cdc ) {
+        this.cdc = cdc.getName();
+        this.daMap = new HashMap<>(); // link between DAI (name) and its respective DataAttribute
+        
+        for( DataAttribute da : cdc.getDataAttribute() ) {
+            this.daMap.put( da.getName(), da );
+        }
+    }
+
+    public boolean validateDOI( DOI doi, DiagnosticChain diagnostics ) {
+        boolean res = true;
+        HashSet< String > checkedDA = new HashSet<>();
+
+        for( DAI dai : doi.getDAI() ) {
+            AbstractRiseClipseConsole.getConsole().verbose( "validateDAI( " + dai.getName() + " ) (line" + dai.getLineNumber() + ")" );
+
+            // Test if DAI is a possible DAI in this DOI
+            if( ! daMap.containsKey( dai.getName() ) ) {
+                diagnostics.add( new BasicDiagnostic(
+                        Diagnostic.ERROR,
+                        RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                        0,
+                        "DAI " + dai.getName() + " (line" + dai.getLineNumber() + ") not found in CDC",
+                        new Object[] { doi, cdc } ));
+                res = false;
+                continue;
+            }
+
+            // Control of DAI presence in DOI
+            updateCompulsory( dai, checkedDA, diagnostics );
+
+            // Validation of DAI content
+            if( ! validateDAI( dai, diagnostics ) ) {
+                res = false;
+            }
+
+        }
+
+        // Verify all necessary DAI were present
+        if( ! daMap.values().stream()
+                .map( x -> checkCompulsory( doi, x, checkedDA, diagnostics ) )
+                .reduce( ( a, b ) -> a && b ).get() ) {
+            diagnostics.add( new BasicDiagnostic(
+                    Diagnostic.ERROR,
+                    RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                    0,
+                    "DOI (line " + doi.getLineNumber() + ") does not contain all mandatory DA from CDC ",
+                    new Object[] { doi, cdc } ));
+            res = false;
+        }
+        return res;
+    }
+
+    public boolean checkCompulsory( DOI doi, DataAttribute da, HashSet< String > checked, DiagnosticChain diagnostics ) {
+        switch( da.getPresCond() ) {
+        case "M":
+            if( ! checked.contains( da.getName() )) {
+                diagnostics.add( new BasicDiagnostic(
+                        Diagnostic.ERROR,
+                        RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                        0,
+                        "DA " + da.getName() + " not found in DOI (line " + doi.getLineNumber() + ")",
+                        new Object[] { da } ));
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public boolean updateCompulsory( DAI dai, HashSet< String > checked, DiagnosticChain diagnostics ) {
+        switch( daMap.get( dai.getName() ).getPresCond() ) {
+        case "M":
+        case "O":
+            if( checked.contains( dai.getName() ) ) {
+                diagnostics.add( new BasicDiagnostic(
+                        Diagnostic.ERROR,
+                        RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                        0,
+                        "DAI " + dai.getName() + " (line " + dai.getLineNumber() + ") cannot appear more than once",
+                        new Object[] { dai } ));
+                return false;
+            }
+            else {
+                checked.add( dai.getName() );
+                break;
+            }
+        case "F":
+            diagnostics.add( new BasicDiagnostic(
+                    Diagnostic.ERROR,
+                    RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                    0,
+                    "DAI " + dai.getName() + " (line " + dai.getLineNumber() + ") is forbidden",
+                    new Object[] { dai } ));
+            return false;
+        }
+        return true;
+    }
+
+    public boolean validateDAI( DAI dai, DiagnosticChain diagnostics ) {
+
+        AbstractRiseClipseConsole.getConsole().verbose( "found DA " + dai.getName() + " in CDC " + cdc );
+
+        // DataAttributes that are BASIC have a BasicType which describes allowed Val of DA
+        DataAttribute da = daMap.get( dai.getName() );
+        if( da.getTypeKind().getName().equals( "BASIC" ) ) {
+            for( Val val : dai.getVal() ) {
+                if( ! validateVal( val.getValue(), da.getType() )) {
+                    diagnostics.add( new BasicDiagnostic(
+                            Diagnostic.ERROR,
+                            RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                            0,
+                            "Val " + val.getValue() + " (" + dai.getLineNumber() + ") of DA " + dai.getName() + " is not of type " + da.getType(),
+                            new Object[] { dai, val } ));
+                     return false;
+                }
+                AbstractRiseClipseConsole.getConsole().verbose( "Val " + val.getValue() + " (" + dai.getLineNumber() + ") of DA " + dai.getName() +
+                        " is of type " + da.getType() );
+            }
+        }
+
+        return true;
+    }
+
+    public boolean validateVal( String val, String type ) {
+        int v;
+        long l;
+        float f;
+        switch( type ) {
+        case "BOOLEAN":
+            return( val.equals( "0" ) || val.equals( "1" ) || val.equals( "false" ) || val.equals( "true" ) );
+        case "INT8":
+            try {
+                v = Integer.parseInt( val );
+            }
+            catch( Exception e ) {
+                return false;
+            }
+            return v >= -128 && v <= 127;
+        case "INT16":
+            try {
+                v = Integer.parseInt( val );
+            }
+            catch( Exception e ) {
+                return false;
+            }
+            return v >= -32768 && v <= 32767;
+        case "INT32":
+            try {
+                v = Integer.parseInt( val );
+            }
+            catch( Exception e ) {
+                return false;
+            }
+            return v >= Integer.MIN_VALUE && v <= Integer.MAX_VALUE;
+        case "INT64":
+            try {
+                l = Long.parseLong( val );
+            }
+            catch( Exception e ) {
+                return false;
+            }
+            return l >= Long.MIN_VALUE && l <= Long.MAX_VALUE;
+        case "INT8U":
+            try {
+                v = Integer.parseInt( val );
+            }
+            catch( Exception e ) {
+                return false;
+            }
+            return v >= 0 && v <= 255;
+        case "INT16U":
+            try {
+                v = Integer.parseInt( val );
+            }
+            catch( Exception e ) {
+                return false;
+            }
+            return v >= 0 && v <= 65535;
+        case "INT32U":
+            try {
+                l = Long.parseLong( val );
+            }
+            catch( Exception e ) {
+                return false;
+            }
+            String max = "4294967295";
+            return l >= 0 && l <= Long.parseLong( max );
+        case "FLOAT32":
+            try {
+                f = Float.parseFloat( val );
+            }
+            catch( Exception e ) {
+                return false;
+            }
+            return f >= -Float.MAX_VALUE && f <= Float.MAX_VALUE;
+        case "Octet64":
+            byte[] bytes = val.getBytes();
+            return bytes.length <= 64;
+        case "VisString64":
+            return val.length() <= 64;
+        case "VisString129":
+            return val.length() <= 129;
+        case "Unicode255":
+        case "VisString255":
+            return val.length() <= 255;
+        default:
+            return true;
+        }
+    }
+
+    private void testValidateVal() {
+        log( "\n--\tSTART TEST\t--\n" );
+        assertTrue( validateVal( "0", "BOOLEAN" ) );
+        assertTrue( validateVal( "1", "BOOLEAN" ) );
+        assertTrue( validateVal( "true", "BOOLEAN" ) );
+        assertTrue( validateVal( "false", "BOOLEAN" ) );
+        assertTrue( !validateVal( "2", "BOOLEAN" ) );
+        assertTrue( !validateVal( "-1", "BOOLEAN" ) );
+        assertTrue( !validateVal( "string", "BOOLEAN" ) );
+        log( "" );
+        assertTrue( validateVal( "1", "INT8" ) );
+        assertTrue( validateVal( "0", "INT8" ) );
+        assertTrue( validateVal( "-1", "INT8" ) );
+        assertTrue( validateVal( "127", "INT8" ) );
+        assertTrue( validateVal( "-128", "INT8" ) );
+        assertTrue( !validateVal( "128", "INT8" ) );
+        assertTrue( !validateVal( "-129", "INT8" ) );
+        assertTrue( !validateVal( "string", "INT8" ) );
+        assertTrue( !validateVal( "22.2", "INT8" ) );
+        log( "" );
+        assertTrue( validateVal( "32767", "INT16" ) );
+        assertTrue( validateVal( "-32768", "INT16" ) );
+        assertTrue( !validateVal( "32768", "INT16" ) );
+        assertTrue( !validateVal( "-32769", "INT16" ) );
+        log( "" );
+        assertTrue( validateVal( Integer.toString( Integer.MAX_VALUE ), "INT32" ) );
+        assertTrue( validateVal( Integer.toString( Integer.MIN_VALUE ), "INT32" ) );
+        assertTrue( !validateVal( "2147483648", "INT32" ) );
+        assertTrue( !validateVal( "-2147483649", "INT32" ) );
+        log( "" );
+        assertTrue( validateVal( Long.toString( Long.MAX_VALUE ), "INT64" ) );
+        assertTrue( validateVal( Long.toString( Long.MIN_VALUE ), "INT64" ) );
+        assertTrue( !validateVal( "9223372036854775808", "INT64" ) );
+        assertTrue( !validateVal( "-9223372036854775809", "INT64" ) );
+        log( "" );
+        assertTrue( validateVal( "0", "INT8U" ) );
+        assertTrue( validateVal( "255", "INT8U" ) );
+        assertTrue( !validateVal( "256", "INT8U" ) );
+        assertTrue( !validateVal( "-1", "INT8U" ) );
+        assertTrue( !validateVal( "-2", "INT8U" ) );
+        log( "" );
+        assertTrue( validateVal( "0", "INT16U" ) );
+        assertTrue( validateVal( "65535", "INT16U" ) );
+        assertTrue( !validateVal( "65536", "INT16U" ) );
+        assertTrue( !validateVal( "-1", "INT16U" ) );
+        assertTrue( !validateVal( "-2", "INT16U" ) );
+        log( "" );
+        assertTrue( validateVal( "0", "INT32U" ) );
+        assertTrue( validateVal( "4294967295", "INT32U" ) );
+        assertTrue( !validateVal( "4294967296", "INT32U" ) );
+        assertTrue( !validateVal( "-1", "INT32U" ) );
+        assertTrue( !validateVal( "-2", "INT32U" ) );
+        log( "" );
+        assertTrue( validateVal( "0.0", "FLOAT32" ) );
+        assertTrue( validateVal( "1.2345", "FLOAT32" ) );
+        assertTrue( validateVal( "-1.2345", "FLOAT32" ) );
+        assertTrue( validateVal( "100", "FLOAT32" ) );
+        assertTrue( validateVal( Float.toString( Float.MAX_VALUE ), "FLOAT32" ) );
+        assertTrue( validateVal( Float.toString( -Float.MAX_VALUE ), "FLOAT32" ) );
+        assertTrue( !validateVal( "3.4028236E38", "FLOAT32" ) );
+        assertTrue( !validateVal( "-3.4028236E38", "FLOAT32" ) );
+        assertTrue( !validateVal( "string", "FLOAT32" ) );
+        log( "" );
+        assertTrue( validateVal( "1234567890123456789012345678901234567890123456789012345678901234", "Octet64" ) );
+        assertTrue( !validateVal( "12345678901234567890123456789012345678901234567890123456789012345", "Octet64" ) );
+        log( "" );
+        assertTrue( validateVal( "1234567890123456789012345678901234567890123456789012345678901234", "VisString64" ) );
+        assertTrue(
+                !validateVal( "12345678901234567890123456789012345678901234567890123456789012345", "VisString64" ) );
+        log( "" );
+        assertTrue( validateVal( "1234567890123456789012345678901234567890123456789012345678901234"
+                + "12345678901234567890123456789012345678901234567890123456789012345", "VisString129" ) );
+        assertTrue( !validateVal( "1234567890123456789012345678901234567890123456789012345678901234"
+                + "123456789012345678901234567890123456789012345678901234567890123456", "VisString129" ) );
+        log( "" );
+        assertTrue( validateVal( "1234567890123456789012345678901234567890123456789012345678901234"
+                + "1234567890123456789012345678901234567890123456789012345678901234"
+                + "1234567890123456789012345678901234567890123456789012345678901234"
+                + "123456789012345678901234567890123456789012345678901234567890123", "VisString255" ) );
+        assertTrue( !validateVal( "1234567890123456789012345678901234567890123456789012345678901234"
+                + "1234567890123456789012345678901234567890123456789012345678901234"
+                + "1234567890123456789012345678901234567890123456789012345678901234"
+                + "1234567890123456789012345678901234567890123456789012345678901234", "VisString255" ) );
+    }
+
+    private void assertTrue( Boolean b ) {
+        if( b ) {
+            log( "Check" );
+        }
+        else {
+            log( "Error" );
+        }
+    }
+
+    private void log( String message ) {
+        AbstractRiseClipseConsole.getConsole().info( message );
+    }
+}
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NSDEObjectValidator.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NSDEObjectValidator.java
deleted file mode 100644
index 1839451c7751c89f8804b7352d30c514fc79e728..0000000000000000000000000000000000000000
--- a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NSDEObjectValidator.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- *  Copyright (c) 2019 CentraleSupélec & EDF.
- *  All rights reserved. This program and the accompanying materials
- *  are made available under the terms of the Eclipse Public License v1.0
- *  which accompanies this distribution, and is available at
- *  http://www.eclipse.org/legal/epl-v10.html
- * 
- *  This file is part of the RiseClipse tool
- *  
- *  Contributors:
- *      Computer Science Department, CentraleSupélec
- *      EDF R&D
- *  Contacts:
- *      dominique.marcadet@centralesupelec.fr
- *      aurelie.dehouck-neveu@edf.fr
- *  Web site:
- *      http://wdi.supelec.fr/software/RiseClipse/
- */
-package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
-
-import java.util.Map;
-
-import org.eclipse.emf.common.util.DiagnosticChain;
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EDataType;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EValidator;
-import org.eclipse.emf.ecore.resource.Resource;
-
-import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole;
-
-public class NSDEObjectValidator implements EValidator {
-    
-    private Resource nsdResource;
-
-    public NSDEObjectValidator( Resource nsdResource ) {
-        this.nsdResource = nsdResource;
-    }
-
-    @Override
-    public boolean validate( EObject eObject, DiagnosticChain diagnostics, Map< Object, Object > context ) {
-        return validate( eObject.eClass(), eObject, diagnostics, context );
-    }
-
-    @Override
-    public boolean validate( EClass eClass, EObject eObject, DiagnosticChain diagnostics,
-            Map< Object, Object > context ) {
-        AbstractRiseClipseConsole.getConsole().info( "NSDEObjectValidator.validate( EClass ): " + eClass.getName() );
-        // TODO: use nsdResource to validate eObject
-        return true;
-    }
-
-    @Override
-    public boolean validate( EDataType eDataType, Object value, DiagnosticChain diagnostics,
-            Map< Object, Object > context ) {
-        AbstractRiseClipseConsole.getConsole().info( "NSDEObjectValidator.validate( EDataType ): " + eDataType.getName() );
-        // TODO: use nsdResource to validate value
-        return true;
-    }
-
-}
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NSDModelLoader.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NSDModelLoader.java
deleted file mode 100644
index 0839ba203a33a33a7fdb6a889ab0653a135a16a7..0000000000000000000000000000000000000000
--- a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NSDModelLoader.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- *  Copyright (c) 2019 CentraleSupélec & EDF.
- *  All rights reserved. This program and the accompanying materials
- *  are made available under the terms of the Eclipse Public License v1.0
- *  which accompanies this distribution, and is available at
- *  http://www.eclipse.org/legal/epl-v10.html
- * 
- *  This file is part of the RiseClipse tool
- *  
- *  Contributors:
- *      Computer Science Department, CentraleSupélec
- *      EDF R&D
- *  Contacts:
- *      dominique.marcadet@centralesupelec.fr
- *      aurelie.dehouck-neveu@edf.fr
- *  Web site:
- *      http://wdi.supelec.fr/software/RiseClipse/
- */
-package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
-
-import org.eclipse.emf.ecore.EValidator;
-import org.eclipse.emf.ecore.resource.Resource;
-
-import fr.centralesupelec.edf.riseclipse.iec61850.nsd.NsdPackage;
-import fr.centralesupelec.edf.riseclipse.iec61850.nsd.util.NsdResourceFactoryImpl;
-import fr.centralesupelec.edf.riseclipse.util.IRiseClipseConsole;
-import fr.centralesupelec.edf.riseclipse.util.RiseClipseModelLoader;
-import fr.centralesupelec.edf.riseclipse.util.TextRiseClipseConsole;
-
-public class NSDModelLoader  extends RiseClipseModelLoader {
-    
-    public NSDModelLoader( IRiseClipseConsole console ) {
-        super( console );
-    }
-
-    @Override
-    public void reset() {
-        super.reset();
-
-        // Register the appropriate resource factory to handle all file
-        // extensions.
-        getResourceSet().getResourceFactoryRegistry().getExtensionToFactoryMap()
-            .put( Resource.Factory.Registry.DEFAULT_EXTENSION, new NsdResourceFactoryImpl() );
-
-        // Register the package to ensure it is available during loading.
-        getResourceSet().getPackageRegistry().put( NsdPackage.eNS_URI, NsdPackage.eINSTANCE );
-    }
-    
-    public Resource loadWithoutValidation( String name ) {
-        Object eValidator = EValidator.Registry.INSTANCE.remove( NsdPackage.eINSTANCE );
-
-        Resource resource = load( name );
-        
-        if( eValidator != null ) {
-            EValidator.Registry.INSTANCE.put( NsdPackage.eINSTANCE, eValidator );
-        }
-        return resource;
-    }
-    
-    public static void main( String[] args ) {
-        IRiseClipseConsole console = new TextRiseClipseConsole();
-        SCLModelLoader loader = new SCLModelLoader( console );
-        
-        for( int i = 0; i < args.length; ++i ) {
-            @SuppressWarnings( "unused" )
-            Resource resource = loader.load( args[i] );
-        }
-    }
-
-}
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NSDValidator.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NSDValidator.java
deleted file mode 100644
index 0bffb4600fcf0e8ea2c41b6037905f24106c834c..0000000000000000000000000000000000000000
--- a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NSDValidator.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- *  Copyright (c) 2019 CentraleSupélec & EDF.
- *  All rights reserved. This program and the accompanying materials
- *  are made available under the terms of the Eclipse Public License v1.0
- *  which accompanies this distribution, and is available at
- *  http://www.eclipse.org/legal/epl-v10.html
- * 
- *  This file is part of the RiseClipse tool
- *  
- *  Contributors:
- *      Computer Science Department, CentraleSupélec
- *      EDF R&D
- *  Contacts:
- *      dominique.marcadet@centralesupelec.fr
- *      aurelie.dehouck-neveu@edf.fr
- *  Web site:
- *      http://wdi.supelec.fr/software/RiseClipse/
- */
-package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.emf.common.notify.AdapterFactory;
-import org.eclipse.emf.common.util.Diagnostic;
-import org.eclipse.emf.ecore.EAttribute;
-import org.eclipse.emf.ecore.EDataType;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EPackage;
-import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecore.EValidator;
-import org.eclipse.emf.ecore.EValidator.SubstitutionLabelProvider;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.util.Diagnostician;
-import org.eclipse.emf.edit.provider.IItemLabelProvider;
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.ocl.pivot.validation.ComposedEValidator;
-
-import fr.centralesupelec.edf.riseclipse.util.IRiseClipseConsole;
-
-public class NSDValidator {
-
-    private @NonNull EPackage modelPackage;
-    private ComposedEValidator validator;
-
-    public NSDValidator( @NonNull EPackage modelPackage ) {
-        this.modelPackage = modelPackage;
-        validator = ComposedEValidator.install( modelPackage );
-    }
-        
-    public boolean addNSDDocument( Resource resource, IRiseClipseConsole console ) {
-        NSDEObjectValidator nsdObjectValidator = new NSDEObjectValidator( resource );
-        validator.addChild( nsdObjectValidator );
-        return true;
-    }
-
-    public void validate( Resource resource, final AdapterFactory adapter, IRiseClipseConsole console ) {
-        Map<Object, Object> context = new HashMap< Object, Object >();
-        SubstitutionLabelProvider substitutionLabelProvider = new EValidator.SubstitutionLabelProvider() {
-            
-            @Override
-            public String getValueLabel( EDataType eDataType, Object value ) {
-                return Diagnostician.INSTANCE.getValueLabel( eDataType, value );
-            }
-            
-            @Override
-            public String getObjectLabel( EObject eObject ) {
-                IItemLabelProvider labelProvider = ( IItemLabelProvider ) adapter.adapt( eObject, IItemLabelProvider.class );
-                return labelProvider.getText( eObject );
-            }
-            
-            @Override
-            public String getFeatureLabel( EStructuralFeature eStructuralFeature ) {
-                return Diagnostician.INSTANCE.getFeatureLabel( eStructuralFeature );
-            }
-        };
-        context.put(EValidator.SubstitutionLabelProvider.class, substitutionLabelProvider );
-
-        for( int n = 0; n < resource.getContents().size(); ++n ) {
-            Diagnostic diagnostic = Diagnostician.INSTANCE.validate( resource.getContents().get( n ), context );
-            
-            if( diagnostic.getSeverity() == Diagnostic.ERROR || diagnostic.getSeverity() == Diagnostic.WARNING ) {
-                //EObject root = ( EObject ) diagnostic.getData().get( 0 );
-                //URI uri = root.eResource().getURI();
-                //console.error( "in file " + uri.lastSegment() );
-                for( Iterator< Diagnostic > i = diagnostic.getChildren().iterator(); i.hasNext(); ) {
-                    Diagnostic childDiagnostic = i.next();
-                    switch( childDiagnostic.getSeverity() ) {
-                    case Diagnostic.ERROR:
-                    case Diagnostic.WARNING:
-                        List< ? > data = childDiagnostic.getData();
-                        EObject object = ( EObject ) data.get( 0 );
-                        if( data.size() == 1 ) {
-                            console.error( "\t" + childDiagnostic.getMessage() );
-                        }
-                        else if( data.get( 1 ) instanceof EAttribute ) {
-                            EAttribute attribute = ( EAttribute ) data.get( 1 );
-                            if( attribute == null ) continue;
-                            console.error( "\tAttribute " + attribute.getName() + " of " + substitutionLabelProvider.getObjectLabel( object ) + " : " + childDiagnostic.getChildren().get( 0 ).getMessage() );
-                        }
-                        else {
-                            console.error( "\t" + childDiagnostic.getMessage() );
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-}
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NsdEObjectValidator.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NsdEObjectValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa2ef5eaec29ce8f402b377bb2dd85a8c65c7fac
--- /dev/null
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NsdEObjectValidator.java
@@ -0,0 +1,112 @@
+/**
+ *  Copyright (c) 2019 CentraleSupélec & EDF.
+ *  All rights reserved. This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License v1.0
+ *  which accompanies this distribution, and is available at
+ *  http://www.eclipse.org/legal/epl-v10.html
+ * 
+ *  This file is part of the RiseClipse tool
+ *  
+ *  Contributors:
+ *      Computer Science Department, CentraleSupélec
+ *      EDF R&D
+ *  Contacts:
+ *      dominique.marcadet@centralesupelec.fr
+ *      aurelie.dehouck-neveu@edf.fr
+ *  Web site:
+ *      http://wdi.supelec.fr/software/RiseClipse/
+ */
+package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.emf.common.util.BasicDiagnostic;
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.common.util.DiagnosticChain;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EDataType;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EValidator;
+import fr.centralesupelec.edf.riseclipse.iec61850.nsd.LNClass;
+import fr.centralesupelec.edf.riseclipse.iec61850.nsd.util.NsdResourceSetImpl;
+import fr.centralesupelec.edf.riseclipse.iec61850.scl.AnyLN;
+import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole;
+
+public class NsdEObjectValidator implements EValidator {
+
+    private NsdResourceSetImpl nsdResourceSet;
+    private HashMap< String, AnyLNValidator > lnMap;
+
+    public NsdEObjectValidator( NsdResourceSetImpl nsdResourceSet ) {
+        this.nsdResourceSet = nsdResourceSet;
+    }
+
+    public void initializeValidationData() {
+        this.lnMap = this.nsdResourceSet.getLNClassStream()
+                .map( lnClass -> generateValidators( lnClass ) )
+                .reduce( ( a, b ) -> {
+                    a.putAll( b );
+                    return a;
+                } ).get();
+    }
+
+    private HashMap< String, AnyLNValidator > generateValidators( LNClass lnClass ) {
+        HashMap< String, AnyLNValidator > lnMap = new HashMap<>();
+        lnMap.put( lnClass.getName(), new AnyLNValidator( lnClass ));
+        return lnMap;
+    }
+
+    @Override
+    public boolean validate( EObject eObject, DiagnosticChain diagnostics, Map< Object, Object > context ) {
+        return validate( eObject.eClass(), eObject, diagnostics, context );
+    }
+
+    @Override
+    public boolean validate( EClass eClass, EObject eObject, DiagnosticChain diagnostics, Map< Object, Object > context ) {
+
+        if( this.lnMap == null ) {
+            this.initializeValidationData();
+        }
+
+        switch( eClass.getName() ) {
+        case "LN0":
+        case "LN":
+            AnyLN ln = ( AnyLN ) eObject;
+            return validateLN( ln, diagnostics );
+        default:
+            AbstractRiseClipseConsole.getConsole().info( "NOT IMPLEMENTED: NsdEObjectValidator.validate( " + eClass.getName() + " )" );
+            return false;
+        }
+    }
+
+    @Override
+    public boolean validate( EDataType eDataType, Object value, DiagnosticChain diagnostics, Map< Object, Object > context ) {
+        AbstractRiseClipseConsole.getConsole().info( "NOT IMPLEMENTED: NsdEObjectValidator.validate( " + eDataType.getName() + " )" );
+
+        // TODO: use nsdResource to validate value
+
+        return true;
+    }
+
+    public boolean validateLN( AnyLN ln, DiagnosticChain diagnostics ) {
+        AbstractRiseClipseConsole.getConsole().verbose( "" );
+        AbstractRiseClipseConsole.getConsole().verbose( "NsdEObjectValidator.validateLN( " + ln.getLnClass() + " )" );
+
+        // Check that LN has valid LNClass
+        if( ! this.lnMap.containsKey( ln.getLnClass() )) {
+            diagnostics.add( new BasicDiagnostic(
+                    Diagnostic.ERROR,
+                    RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
+                    0,
+                    "LNClass " + ln.getLnClass() + " not found in NSD files for LN at line " + ln.getLineNumber(),
+                    new Object[] { ln } ));
+            return false;
+        }
+        AbstractRiseClipseConsole.getConsole().verbose( "found LNClass " + ln.getLnClass() + " in NSD files for LN at line " + ln.getLineNumber() );
+
+        // AnyLNValidator validates LN content
+        return lnMap.get( ln.getLnClass() ).validateLN( ln, diagnostics );
+    }
+
+}
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NsdModelLoader.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NsdModelLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e7058aa5a7cd0a0cd6aad37830b91de80d3aee4
--- /dev/null
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NsdModelLoader.java
@@ -0,0 +1,129 @@
+/**
+ *  Copyright (c) 2019 CentraleSupélec & EDF.
+ *  All rights reserved. This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License v1.0
+ *  which accompanies this distribution, and is available at
+ *  http://www.eclipse.org/legal/epl-v10.html
+ * 
+ *  This file is part of the RiseClipse tool
+ *  
+ *  Contributors:
+ *      Computer Science Department, CentraleSupélec
+ *      EDF R&D
+ *  Contacts:
+ *      dominique.marcadet@centralesupelec.fr
+ *      aurelie.dehouck-neveu@edf.fr
+ *  Web site:
+ *      http://wdi.supelec.fr/software/RiseClipse/
+ */
+package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecore.EDataType;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.EValidator;
+import org.eclipse.emf.ecore.EValidator.SubstitutionLabelProvider;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.Diagnostician;
+//import org.eclipse.ocl.pivot.delegate.OCLDelegateDomain;
+import org.eclipse.emf.edit.provider.IItemLabelProvider;
+
+import fr.centralesupelec.edf.riseclipse.iec61850.nsd.NsdPackage;
+import fr.centralesupelec.edf.riseclipse.iec61850.nsd.provider.NsdItemProviderAdapterFactory;
+import fr.centralesupelec.edf.riseclipse.iec61850.nsd.util.NsdResourceFactoryImpl;
+import fr.centralesupelec.edf.riseclipse.iec61850.nsd.util.NsdResourceSetImpl;
+import fr.centralesupelec.edf.riseclipse.util.IRiseClipseConsole;
+import fr.centralesupelec.edf.riseclipse.util.RiseClipseModelLoader;
+import fr.centralesupelec.edf.riseclipse.util.TextRiseClipseConsole;
+
+public class NsdModelLoader extends RiseClipseModelLoader {
+
+    public NsdModelLoader( IRiseClipseConsole console ) {
+        super( console );
+    }
+
+    @Override
+    public void reset() {
+        super.reset( new NsdResourceSetImpl( true, console ) );
+
+        // Register the appropriate resource factory to handle all file
+        // extensions.
+        getResourceSet().getResourceFactoryRegistry().getExtensionToFactoryMap()
+                .put( Resource.Factory.Registry.DEFAULT_EXTENSION, new NsdResourceFactoryImpl() );
+
+        // Register the package to ensure it is available during loading.
+        getResourceSet().getPackageRegistry().put( NsdPackage.eNS_URI, NsdPackage.eINSTANCE );
+    }
+
+    @Override
+    public NsdResourceSetImpl getResourceSet() {
+        return ( NsdResourceSetImpl ) super.getResourceSet();
+    }
+
+    public Resource loadWithoutValidation( String name ) {
+        Object eValidator = EValidator.Registry.INSTANCE.remove( NsdPackage.eINSTANCE );
+
+        Resource resource = load( name );
+
+        if( eValidator != null ) {
+            EValidator.Registry.INSTANCE.put( NsdPackage.eINSTANCE, eValidator );
+        }
+        return resource;
+    }
+
+    public static void main( String[] args ) {
+        IRiseClipseConsole console = new TextRiseClipseConsole();
+        //console.setLevel( IRiseClipseConsole.ERROR_LEVEL );
+        NsdModelLoader loader = new NsdModelLoader( console );
+
+        org.eclipse.ocl.xtext.oclinecore.OCLinEcoreStandaloneSetup.doSetup();
+
+        Map< Object, Object > context = new HashMap< Object, Object >();
+        SubstitutionLabelProvider substitutionLabelProvider = new EValidator.SubstitutionLabelProvider() {
+
+            @Override
+            public String getValueLabel( EDataType eDataType, Object value ) {
+                return Diagnostician.INSTANCE.getValueLabel( eDataType, value );
+            }
+
+            @Override
+            public String getObjectLabel( EObject eObject ) {
+                NsdItemProviderAdapterFactory adapter = new NsdItemProviderAdapterFactory();
+                IItemLabelProvider labelProvider = ( IItemLabelProvider ) adapter.adapt( eObject,
+                        IItemLabelProvider.class );
+                return labelProvider.getText( eObject );
+            }
+
+            @Override
+            public String getFeatureLabel( EStructuralFeature eStructuralFeature ) {
+                return Diagnostician.INSTANCE.getFeatureLabel( eStructuralFeature );
+            }
+        };
+        context.put( EValidator.SubstitutionLabelProvider.class, substitutionLabelProvider );
+
+        for( int i = 0; i < args.length; ++i ) {
+            Resource resource = loader.load( args[i] );
+            if( resource == null ) continue;
+            if( resource.getContents().size() == 0 ) continue;
+            Diagnostic diagnostic = Diagnostician.INSTANCE.validate( resource.getContents().get( 0 ), context );
+
+            if( diagnostic.getSeverity() == Diagnostic.ERROR || diagnostic.getSeverity() == Diagnostic.WARNING ) {
+                for( Iterator< Diagnostic > d = diagnostic.getChildren().iterator(); d.hasNext(); ) {
+                    Diagnostic childDiagnostic = d.next();
+                    switch( childDiagnostic.getSeverity() ) {
+                    case Diagnostic.ERROR:
+                    case Diagnostic.WARNING:
+                        console.error( "\t" + childDiagnostic.getMessage() );
+                    }
+                }
+            }
+        }
+        loader.getResourceSet().finalizeLoad( console );
+    }
+
+}
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NsdValidator.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NsdValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7dc86353fef2b31548b770078d91058ebb92436
--- /dev/null
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/NsdValidator.java
@@ -0,0 +1,47 @@
+/**
+ *  Copyright (c) 2019 CentraleSupélec & EDF.
+ *  All rights reserved. This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License v1.0
+ *  which accompanies this distribution, and is available at
+ *  http://www.eclipse.org/legal/epl-v10.html
+ * 
+ *  This file is part of the RiseClipse tool
+ *  
+ *  Contributors:
+ *      Computer Science Department, CentraleSupélec
+ *      EDF R&D
+ *  Contacts:
+ *      dominique.marcadet@centralesupelec.fr
+ *      aurelie.dehouck-neveu@edf.fr
+ *  Web site:
+ *      http://wdi.supelec.fr/software/RiseClipse/
+ */
+package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
+
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.ocl.pivot.validation.ComposedEValidator;
+
+import fr.centralesupelec.edf.riseclipse.util.IRiseClipseConsole;
+
+public class NsdValidator {
+
+    private NsdModelLoader nsdLoader;
+
+    public NsdValidator( @NonNull EPackage modelPackage, IRiseClipseConsole console ) {
+        nsdLoader = new NsdModelLoader( console );
+    }
+
+    public void addNsdDocument( String nsdFile, IRiseClipseConsole console ) {
+        console.info( "Loading nsd: " + nsdFile );
+        nsdLoader.load( nsdFile );
+    }
+    
+    public void prepare( @NonNull ComposedEValidator validator, IRiseClipseConsole console ) {
+        nsdLoader.getResourceSet().finalizeLoad( console );
+        NsdEObjectValidator nsdEObjectValidator = new NsdEObjectValidator( nsdLoader.getResourceSet() );
+        nsdEObjectValidator.initializeValidationData();
+        validator.addChild( nsdEObjectValidator );
+    }
+
+}
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/RiseClipseValidatorSCL.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/RiseClipseValidatorSCL.java
index acfe5d5d052589e1c8d3983a67df26850689ae3b..68e562f5a639afdf821301a2d0d13b7b7edfc451 100644
--- a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/RiseClipseValidatorSCL.java
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/RiseClipseValidatorSCL.java
@@ -18,77 +18,96 @@
  */
 package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
 
-import java.io.File;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 
-import fr.centralesupelec.edf.riseclipse.iec61850.nsd.provider.NsdItemProviderAdapterFactory;
 import fr.centralesupelec.edf.riseclipse.iec61850.scl.SclPackage;
 import fr.centralesupelec.edf.riseclipse.iec61850.scl.provider.SclItemProviderAdapterFactory;
 import fr.centralesupelec.edf.riseclipse.util.IRiseClipseConsole;
+import fr.centralesupelec.edf.riseclipse.util.RiseClipseFatalException;
 import fr.centralesupelec.edf.riseclipse.util.TextRiseClipseConsole;
 import fr.centralesupelec.edf.riseclipse.validation.ocl.OCLValidator;
 
-//import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.common.notify.AdapterFactory;
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EDataType;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.EValidator;
+import org.eclipse.emf.ecore.EValidator.SubstitutionLabelProvider;
 import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.Diagnostician;
+import org.eclipse.emf.edit.provider.IItemLabelProvider;
+import org.eclipse.ocl.pivot.validation.ComposedEValidator;
 
 public class RiseClipseValidatorSCL {
 
+    public static final String DIAGNOSTIC_SOURCE = "fr.centralesupelec.edf.riseclipse";
+    
     private static OCLValidator oclValidator;
     private static SclItemProviderAdapterFactory sclAdapter;
     private static SCLModelLoader sclLoader;
-    private static NSDValidator nsdValidator;
+    private static NsdValidator nsdValidator;
     private static boolean oclValidation = false;
     private static boolean nsdValidation = false;
-    private static NsdItemProviderAdapterFactory nsdAdapter;
 
-    public static void usage( IRiseClipseConsole console ) {
+    private static final IRiseClipseConsole console = new TextRiseClipseConsole();
+
+    private static void usage() {
         console.setLevel( IRiseClipseConsole.INFO_LEVEL );
-        console.info( "java -jar RiseClipseValidatorSCL.jar [--verbose] [--make-explicit-links] [<oclFile> | <nsdFile> | <sclFile>]*" );
+        console.info(
+                "java -jar RiseClipseValidatorSCL.jar [--info | --verbose] [--make-explicit-links] [<oclFile> | <nsdFile> | <sclFile>]*" );
         console.info( "Files ending with \".ocl\" are considered OCL files, "
-                    + "files ending with \\\".nsd\\\" are considered NSD files, "
-                    + "all others are considered SCL files" );
+                + "files ending with \\\".nsd\\\" are considered NSD files, "
+                + "all others are considered SCL files" );
         System.exit( -1 );
     }
 
     public static void main( String[] args ) {
-        
-        final IRiseClipseConsole console = new TextRiseClipseConsole();
-        
+
         console.setLevel( IRiseClipseConsole.INFO_LEVEL );
-        displayLegal( console );
-        
+        displayLegal();
         console.setLevel( IRiseClipseConsole.WARNING_LEVEL );
         
-        if( args.length == 0 ) usage( console );
-        
+        console.doNotDisplayIdenticalMessages();
+
+        if( args.length == 0 ) usage();
+
         boolean make_explicit_links = false;
 
         int posFiles = 0;
         for( int i = 0; i < args.length; ++i ) {
-            if( args[i].startsWith( "--" )) {
+            if( args[i].startsWith( "--" ) ) {
                 posFiles = i + 1;
-                if( "--verbose".equals( args[i] )) {
+                if( "--info".equals( args[i] ) ) {
                     console.setLevel( IRiseClipseConsole.INFO_LEVEL );
                 }
-                else if( "--make-explicit-links".equals( args[i] )) {
+                else if( "--verbose".equals( args[i] ) ) {
+                    console.setLevel( IRiseClipseConsole.VERBOSE_LEVEL );
+                }
+                else if( "--make-explicit-links".equals( args[i] ) ) {
                     make_explicit_links = true;
                 }
                 else {
                     console.error( "Unrecognized option " + args[i] );
-                    usage( console );
+                    usage();
                 }
             }
         }
 
-        ArrayList< File > oclFiles = new ArrayList<>();
+        ArrayList< String > oclFiles = new ArrayList<>();
         ArrayList< String > nsdFiles = new ArrayList<>();
         ArrayList< String > sclFiles = new ArrayList<>();
         for( int i = posFiles; i < args.length; ++i ) {
-            if( args[i].endsWith( ".ocl" )) {
-                oclFiles.add( new File( args[i] ));
+            if( args[i].endsWith( ".ocl" ) ) {
+                oclFiles.add( args[i] );
                 oclValidation = true;
             }
-            else if( args[i].endsWith( ".nsd" )) {
+            else if( args[i].endsWith( ".nsd" ) ) {
                 nsdFiles.add( args[i] );
                 nsdValidation = true;
             }
@@ -96,17 +115,19 @@ public class RiseClipseValidatorSCL {
                 sclFiles.add( args[i] );
             }
         }
-        
-        prepare( console, oclFiles, nsdFiles );
+
+        prepare( oclFiles, nsdFiles );
         for( int i = 0; i < sclFiles.size(); ++i ) {
-            run( console, make_explicit_links, sclFiles.get( i ));
+            run( make_explicit_links, sclFiles.get( i ));
         }
     }
-    
-    public static void displayLegal( IRiseClipseConsole console ) {
+
+    private static void displayLegal() {
         console.info( "Copyright (c) 2019 CentraleSupélec & EDF." );
-        console.info( "All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0" );
-        console.info( "which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html" );
+        console.info(
+                "All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0" );
+        console.info(
+                "which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html" );
         console.info( "" );
         console.info( "This file is part of the RiseClipse tool." );
         console.info( "Contributors:" );
@@ -118,40 +139,41 @@ public class RiseClipseValidatorSCL {
         console.info( "Web site:" );
         console.info( "    http://wdi.supelec.fr/software/RiseClipse/" );
         console.info( "" );
-        console.info( "RiseClipseValidatorSCL version: 1.0.0 (28 January 2019)" );
+        console.info( "RiseClipseValidatorSCL version: 1.0.0 (2 april 2019)" );
         console.info( "" );
     }
 
-    public static void prepare( IRiseClipseConsole console, ArrayList< File > oclFiles, ArrayList< String > nsdFiles ) {
+    private static void prepare( ArrayList< String > oclFiles, ArrayList< String > nsdFiles ) {
+        SclPackage sclPg = SclPackage.eINSTANCE;
+        if( sclPg == null ) {
+            throw new RiseClipseFatalException( "SCL package not found", null );
+        }
+
+        ComposedEValidator validator = ComposedEValidator.install( sclPg );
+
         if( oclValidation ) {
-            oclValidator = new OCLValidator( SclPackage.eINSTANCE, true );
-    
+            oclValidator = new OCLValidator( sclPg, console );
+
             for( int i = 0; i < oclFiles.size(); ++i ) {
-                console.info( "Loading ocl: " + oclFiles.get( i ));
-                // workaround for bug 486872
-//              File file = new File( oclFiles.get( i ));
-//              URI uri = file.isFile() ? URI.createFileURI( file.getAbsolutePath() ) : URI.createURI( oclFiles.get( i ));
-//              oclFiles.add( uri );
-//              ocl.addOCLDocument( uri, console );
                 oclValidator.addOCLDocument( oclFiles.get( i ), console );
             }
+            oclValidator.prepare( validator, console );
         }
-        
+
         if( nsdValidation ) {
-            nsdValidator = new NSDValidator( SclPackage.eINSTANCE );
-            NSDModelLoader nsdLoader = new NSDModelLoader( console );
+            nsdValidator = new NsdValidator( sclPg, console );
             for( int i = 0; i < nsdFiles.size(); ++i ) {
-                console.info( "Loading nsd: " + nsdFiles.get( i ));
-                nsdValidator.addNSDDocument( nsdLoader.load( nsdFiles.get( i )), console );
+                nsdValidator.addNsdDocument( nsdFiles.get( i ), console );
             }
-            nsdAdapter = new NsdItemProviderAdapterFactory();
+            nsdValidator.prepare( validator, console );
         }
 
         sclLoader = new SCLModelLoader( console );
         sclAdapter = new SclItemProviderAdapterFactory();
+
     }
 
-    public static void run( IRiseClipseConsole console, boolean make_explicit_links, String sclFile ) {
+    private static void run( boolean make_explicit_links, String sclFile ) {
         sclLoader.reset();
         Resource resource = sclLoader.loadWithoutValidation( sclFile );
         if( make_explicit_links ) {
@@ -159,16 +181,62 @@ public class RiseClipseValidatorSCL {
             sclLoader.finalizeLoad();
         }
         if( resource != null ) {
-            if( oclValidation ) {
-                console.info( "Validating file: " + sclFile + " with OCL" );
-                oclValidator.validate( resource, sclAdapter, console );
+            console.info( "Validating file: " + sclFile );
+            validate( resource, sclAdapter );
+        }
+    }
+
+    private static void validate( Resource resource, final AdapterFactory adapter ) {
+        Map< Object, Object > context = new HashMap< Object, Object >();
+        SubstitutionLabelProvider substitutionLabelProvider = new EValidator.SubstitutionLabelProvider() {
+
+            @Override
+            public String getValueLabel( EDataType eDataType, Object value ) {
+                return Diagnostician.INSTANCE.getValueLabel( eDataType, value );
             }
-            if( nsdValidation ) {
-                console.info( "Validating file: " + sclFile + " with NSD" );
-                nsdValidator.validate( resource, nsdAdapter, console );
+
+            @Override
+            public String getObjectLabel( EObject eObject ) {
+                IItemLabelProvider labelProvider = ( IItemLabelProvider ) adapter.adapt( eObject,
+                        IItemLabelProvider.class );
+                return labelProvider.getText( eObject );
             }
-       }
+
+            @Override
+            public String getFeatureLabel( EStructuralFeature eStructuralFeature ) {
+                return Diagnostician.INSTANCE.getFeatureLabel( eStructuralFeature );
+            }
+        };
+        context.put( EValidator.SubstitutionLabelProvider.class, substitutionLabelProvider );
+
+        for( int n = 0; n < resource.getContents().size(); ++n ) {
+            Diagnostic diagnostic = Diagnostician.INSTANCE.validate( resource.getContents().get( n ), context );
+
+            if( diagnostic.getSeverity() == Diagnostic.ERROR || diagnostic.getSeverity() == Diagnostic.WARNING ) {
+                for( Iterator< Diagnostic > i = diagnostic.getChildren().iterator(); i.hasNext(); ) {
+                    Diagnostic childDiagnostic = i.next();
+                    switch( childDiagnostic.getSeverity() ) {
+                    case Diagnostic.ERROR:
+                    case Diagnostic.WARNING:
+                        List< ? > data = childDiagnostic.getData();
+                        EObject object = ( EObject ) data.get( 0 );
+                        if( data.size() == 1 ) {
+                            console.error( "\t" + childDiagnostic.getMessage() );
+                        }
+                        else if( data.get( 1 ) instanceof EAttribute ) {
+                            EAttribute attribute = ( EAttribute ) data.get( 1 );
+                            if( attribute == null ) continue;
+                            console.error( "\tAttribute " + attribute.getName() + " of "
+                                    + substitutionLabelProvider.getObjectLabel( object ) + " : "
+                                    + childDiagnostic.getChildren().get( 0 ).getMessage() );
+                        }
+                        else {
+                            console.error( "\t" + childDiagnostic.getMessage() );
+                        }
+                    }
+                }
+            }
+        }
     }
 
 }
-
diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/SCLModelLoader.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/SCLModelLoader.java
index 59c99bdfc141d0b49c6aa991496c14b0a87a2022..f725dabdd5de4302cad4d13b6ee6b164e6948dcc 100644
--- a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/SCLModelLoader.java
+++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/SCLModelLoader.java
@@ -27,9 +27,8 @@ import fr.centralesupelec.edf.riseclipse.util.IRiseClipseConsole;
 import fr.centralesupelec.edf.riseclipse.util.RiseClipseModelLoader;
 import fr.centralesupelec.edf.riseclipse.util.TextRiseClipseConsole;
 
-
 public class SCLModelLoader extends RiseClipseModelLoader {
-    
+
     public SCLModelLoader( IRiseClipseConsole console ) {
         super( console );
     }
@@ -41,27 +40,27 @@ public class SCLModelLoader extends RiseClipseModelLoader {
         // Register the appropriate resource factory to handle all file
         // extensions.
         getResourceSet().getResourceFactoryRegistry().getExtensionToFactoryMap()
-            .put( Resource.Factory.Registry.DEFAULT_EXTENSION, new SclResourceFactoryImpl() );
+                .put( Resource.Factory.Registry.DEFAULT_EXTENSION, new SclResourceFactoryImpl() );
 
         // Register the package to ensure it is available during loading.
         getResourceSet().getPackageRegistry().put( SclPackage.eNS_URI, SclPackage.eINSTANCE );
     }
-    
+
     public Resource loadWithoutValidation( String name ) {
         Object eValidator = EValidator.Registry.INSTANCE.remove( SclPackage.eINSTANCE );
 
         Resource resource = load( name );
-        
+
         if( eValidator != null ) {
             EValidator.Registry.INSTANCE.put( SclPackage.eINSTANCE, eValidator );
         }
         return resource;
     }
-    
+
     public static void main( String[] args ) {
         IRiseClipseConsole console = new TextRiseClipseConsole();
         SCLModelLoader loader = new SCLModelLoader( console );
-        
+
         for( int i = 0; i < args.length; ++i ) {
             @SuppressWarnings( "unused" )
             Resource resource = loader.load( args[i] );