Index: CMDIValidator/trunk/cmdi-validator-core/src/main/java/eu/clarin/cmdi/validator/CMDIValidator.java
===================================================================
--- CMDIValidator/trunk/cmdi-validator-core/src/main/java/eu/clarin/cmdi/validator/CMDIValidator.java	(revision 5391)
+++ CMDIValidator/trunk/cmdi-validator-core/src/main/java/eu/clarin/cmdi/validator/CMDIValidator.java	(revision 5394)
@@ -33,10 +33,12 @@
 
 import javax.xml.XMLConstants;
-import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.sax.SAXSource;
 import javax.xml.transform.stream.StreamSource;
 
 import net.java.truevfs.access.TFile;
 import net.java.truevfs.access.TFileInputStream;
-import net.sf.saxon.s9api.Axis;
+import net.sf.saxon.Configuration;
 import net.sf.saxon.s9api.DocumentBuilder;
 import net.sf.saxon.s9api.Processor;
@@ -45,10 +47,11 @@
 import net.sf.saxon.s9api.WhitespaceStrippingPolicy;
 import net.sf.saxon.s9api.XPathCompiler;
-import net.sf.saxon.s9api.XPathExecutable;
 import net.sf.saxon.s9api.XPathSelector;
+import net.sf.saxon.s9api.XQueryCompiler;
+import net.sf.saxon.s9api.XQueryEvaluator;
+import net.sf.saxon.s9api.XQueryExecutable;
 import net.sf.saxon.s9api.XdmDestination;
 import net.sf.saxon.s9api.XdmItem;
 import net.sf.saxon.s9api.XdmNode;
-import net.sf.saxon.s9api.XdmSequenceIterator;
 import net.sf.saxon.s9api.XsltCompiler;
 import net.sf.saxon.s9api.XsltExecutable;
@@ -58,5 +61,5 @@
 import org.apache.xerces.impl.xs.XMLSchemaLoader;
 import org.apache.xerces.impl.xs.XSDDescription;
-import org.apache.xerces.parsers.DOMParser;
+import org.apache.xerces.parsers.SAXParser;
 import org.apache.xerces.parsers.XML11Configuration;
 import org.apache.xerces.util.SymbolTable;
@@ -72,10 +75,12 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
+import org.xml.sax.ErrorHandler;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
 
 import eu.clarin.cmdi.validator.CMDIValidatorResult.Severity;
 import eu.clarin.cmdi.validator.utils.LRUCache;
+import eu.clarin.cmdi.validator.utils.SaxonLocationUtils;
 
 
@@ -92,4 +97,6 @@
     private static final String SCHEMATATRON_STAGE_3 =
             "/schematron/iso_svrl_for_xslt2.xsl";
+    private static final String ANALYZE_SVRL =
+            "/analyze-svrl.xq";
     private static final String DEFAULT_SCHEMATRON_SCHEMA =
             "/default.sch";
@@ -111,17 +118,10 @@
             "http://apache.org/xml/features/honour-all-schemaLocations";
     private static final int INITAL_SYMBOL_TABLE_SIZE = 16141;
-    private static final String SVRL_NAMESPACE_URI =
-            "http://purl.oclc.org/dsdl/svrl";
-    private static final String SVRL_NAMESPACE_PREFIX =
-            "svrl";
-    private static final QName SVRL_TEXT =
-            new QName(SVRL_NAMESPACE_URI, "text");
-    private static final QName SVRL_FAILED_ASSERT =
-            new QName(SVRL_NAMESPACE_URI, "failed-assert");
+    private static final QName SVRL_S = new QName("s");
+    private static final QName SVRL_L = new QName("l");
     private final Processor processor;
     private final CMDISchemaLoader schemaLoader;
     private final XsltExecutable schematronValidatorExecutable;
-    private final XPathExecutable xpath1;
-    private final XPathExecutable xpath2;
+    private final XQueryExecutable analyzeSchematronReport;
     private final List<CMDIValidatorExtension> extensions;
     private final FileEnumerator files;
@@ -156,4 +156,28 @@
         logger.debug("initializing Saxon ...");
         this.processor = new Processor(true);
+        final Configuration saxonConfig =
+                this.processor.getUnderlyingConfiguration();
+        saxonConfig.setErrorListener(new ErrorListener() {
+            @Override
+            public void warning(TransformerException exception)
+                    throws TransformerException {
+                throw exception;
+            }
+
+
+            @Override
+            public void fatalError(TransformerException exception)
+                    throws TransformerException {
+                throw exception;
+            }
+
+
+            @Override
+            public void error(TransformerException exception)
+                    throws TransformerException {
+                throw exception;
+            }
+        });
+
 
         /*
@@ -163,18 +187,28 @@
             this.schematronValidatorExecutable =
                     initSchematronValidator(config, processor);
+            InputStream stream = null;
             try {
-                final XPathCompiler compiler = processor.newXPathCompiler();
-                compiler.declareNamespace(SVRL_NAMESPACE_PREFIX, SVRL_NAMESPACE_URI);
-                this.xpath1 = compiler.compile("//(svrl:failed-assert|svrl:successful-report)");
-                this.xpath2 = compiler.compile("preceding-sibling::svrl:fired-rule/@role");
+                stream = getClass().getResourceAsStream(ANALYZE_SVRL);
+                final XQueryCompiler compiler = processor.newXQueryCompiler();
+                this.analyzeSchematronReport  = compiler.compile(stream);
+            } catch (IOException e) {
+                throw new CMDIValidatorInitException(
+                        "error initializing schematron validator", e);
             } catch (SaxonApiException e) {
                 throw new CMDIValidatorInitException(
                         "error initializing schematron validator", e);
+            } finally {
+                if (stream != null) {
+                    try {
+                        stream.close();
+                    } catch (IOException e) {
+                        /* IGNORE */
+                    }
+                }
             }
             logger.debug("Schematron validator successfully initialized");
         } else {
             this.schematronValidatorExecutable = null;
-            this.xpath1                        = null;
-            this.xpath2                        = null;
+            this.analyzeSchematronReport       = null;
         }
 
@@ -424,5 +458,5 @@
 
     private final class ThreadContext {
-        private final XML11Configuration xercesConfig;
+        private final SAXParser parser;
         private final XsltTransformer schematronValidator;
         private final DocumentBuilder builder;
@@ -434,5 +468,5 @@
              * initialize Xerces
              */
-            final XMLEntityResolver resolver = new XMLEntityResolver() {
+            XMLEntityResolver resolver = new XMLEntityResolver() {
                 @Override
                 public XMLInputSource resolveEntity(
@@ -505,5 +539,6 @@
             }
 
-            xercesConfig = new XML11Configuration(symbols, pool);
+            XML11Configuration xercesConfig =
+                    new XML11Configuration(symbols, pool);
             xercesConfig.setFeature(NAMESPACES_FEATURE_ID, true);
             xercesConfig.setFeature(VALIDATION_FEATURE_ID, true);
@@ -512,30 +547,35 @@
             xercesConfig.setFeature(HONOUR_ALL_SCHEMA_LOCATIONS_ID, true);
             xercesConfig.setEntityResolver(resolver);
-            xercesConfig.setErrorHandler(new XMLErrorHandler() {
+
+            /*
+             * create a reusable parser and also add an error handler.
+             * We cannot use a global error handler in xerces config, because
+             * Saxon ignores and overwrites it ...
+             */
+            this.parser = new SAXParser(xercesConfig);
+            this.parser.setErrorHandler(new ErrorHandler() {
                 @Override
-                public void warning(String domain, String key,
-                        XMLParseException e) throws XNIException {
+                public void warning(SAXParseException e) throws SAXException {
                     reportWarning(e.getLineNumber(),
                             e.getColumnNumber(),
-                            e.getMessage(), e);
-                }
-
+                            e.getMessage(),
+                            e);
+                }
 
                 @Override
-                public void error(String domain, String key,
-                        XMLParseException e) throws XNIException {
+                public void error(SAXParseException e) throws SAXException {
                     reportError(e.getLineNumber(),
                             e.getColumnNumber(),
-                            e.getMessage(), e);
+                            e.getMessage(),
+                            e);
                     throw e;
                 }
 
-
                 @Override
-                public void fatalError(String domain, String key,
-                        XMLParseException e) throws XNIException {
+                public void fatalError(SAXParseException e) throws SAXException {
                     reportError(e.getLineNumber(),
                             e.getColumnNumber(),
-                            e.getMessage(), e);
+                            e.getMessage(),
+                            e);
                     throw e;
                 }
@@ -543,12 +583,19 @@
 
             /*
-             * initialize Saxon document builder
+             * initialize and configure Saxon document builder
              */
             this.builder = processor.newDocumentBuilder();
             this.builder.setWhitespaceStrippingPolicy(
                     WhitespaceStrippingPolicy.IGNORABLE);
-
+            this.builder.setLineNumbering(true);
             /*
-             * initialize schematron
+             * even though, we need to perform Schema validation, tell
+             * Saxon to enable DTD validation. Otherwise, it will
+             * not validate at all ... :/
+             */
+            this.builder.setDTDValidation(true);
+
+            /*
+             * initialize Schematron validator
              */
             if (schematronValidatorExecutable != null) {
@@ -623,21 +670,20 @@
                 throws CMDIValidatorException {
             try {
-                final DOMParser parser = new DOMParser(xercesConfig);
                 try {
-                    parser.parse(new InputSource(stream));
-                    stream.close();
-
-                    final Document dom = parser.getDocument();
-                    if (dom == null) {
+                    final SAXSource source =
+                            new SAXSource(parser, new InputSource(stream));
+                    return builder.build(source);
+                } finally {
+                    /* recycle parser */
+                    try {
+                        parser.reset();
+                    } catch (XNIException e) {
                         throw new CMDIValidatorException(
-                                "parser returned no return result");
-                    }
-                    return builder.build(new DOMSource(dom));
-                } finally {
-                    parser.reset();
-                }
-            } catch (SAXException e) {
-                logger.trace("error parsing instance", e);
-                return null;
+                                "error resetting parser", e);
+                    } finally {
+                        /* really make sure, stream is closed */
+                        stream.close();
+                    }
+                }
             } catch (SaxonApiException e) {
                 logger.trace("error parsing instance", e);
@@ -648,11 +694,4 @@
                         : "input/output error";
                 throw new CMDIValidatorException(message, e);
-            } finally {
-                /* really make sure, stream is closed */
-                try {
-                    stream.close();
-                } catch (IOException e) {
-                    /* IGNORE */
-                }
             }
         }
@@ -668,27 +707,43 @@
                 schematronValidator.transform();
 
-                final XPathSelector selector = xpath1.load();
-                selector.setContextItem(destination.getXdmNode());
-                for (XdmItem item : selector) {
-                    final XdmNode node = (XdmNode) item;
-                    final XdmNode text = getFirstChild(node, SVRL_TEXT);
-                    String msg = (text != null)
-                            ? text.getStringValue().trim()
-                            : null;
-                    if (SVRL_FAILED_ASSERT.equals(node.getNodeName())) {
-                        final XPathSelector selector2 = xpath2.load();
-                        String role = null;
-                        selector2.setContextItem(node);
-                        XdmItem evaluateSingle = selector2.evaluateSingle();
-                        if (evaluateSingle != null) {
-                            role = evaluateSingle.getStringValue().trim();
+                final XdmNode report = destination.getXdmNode();
+                if (report != null) {
+                    XPathCompiler xpathCompiler = null;
+                    final XQueryEvaluator evaluator =
+                            analyzeSchematronReport.load();
+                    evaluator.setContextItem(report);
+                    for (final XdmItem item : evaluator) {
+                        /* lazy initialize XPath compiler */
+                        if (xpathCompiler == null) {
+                            xpathCompiler = processor.newXPathCompiler();
+                            xpathCompiler.setCaching(true);
                         }
-                        if ("warning".equalsIgnoreCase(role)) {
-                            result.reportWarning(-1, -1, msg);
+                        final XdmNode node = (XdmNode) item;
+                        final String s =
+                                nullSafeTrim(node.getAttributeValue(SVRL_S));
+                        final String l =
+                                nullSafeTrim(node.getAttributeValue(SVRL_L));
+                        final String m =
+                                nullSafeTrim(node.getStringValue());
+                        int line   = -1;
+                        int column = -1;
+                        if (l != null) {
+                            XPathSelector xs = xpathCompiler.compile(l).load();
+                            xs.setContextItem(document);
+                            XdmItem n = xs.evaluateSingle();
+                            line = SaxonLocationUtils.getLineNumber(n);
+                            column = SaxonLocationUtils.getColumnNumber(n);
+                        }
+                        if ("I".equals(s)) {
+                            result.reportInfo(line, column, m);
+                        } else if ("W".equals(s)) {
+                            result.reportWarning(line, column, m);
                         } else {
-                            result.reportError(-1, -1, msg);
+                            result.reportError(line, column, m);
                         }
-                    } else {
-                        result.reportInfo(-1, -1, msg);
+                    } // for
+                    if (xpathCompiler != null) {
+                        xpathCompiler.setCaching(false);
+                        xpathCompiler = null;
                     }
                 }
@@ -700,11 +755,12 @@
 
 
-        private XdmNode getFirstChild(XdmNode parent, QName name) {
-            XdmSequenceIterator i = parent.axisIterator(Axis.CHILD, name);
-            if (i.hasNext()) {
-                return (XdmNode) i.next();
-            } else {
-                return null;
-            }
+        private String nullSafeTrim(String s) {
+            if (s != null) {
+                s = s.trim();
+                if (s.isEmpty()) {
+                    s = null;
+                }
+            }
+            return s;
         }
 
Index: CMDIValidator/trunk/cmdi-validator-core/src/main/java/eu/clarin/cmdi/validator/extensions/CheckHandlesExtension.java
===================================================================
--- CMDIValidator/trunk/cmdi-validator-core/src/main/java/eu/clarin/cmdi/validator/extensions/CheckHandlesExtension.java	(revision 5391)
+++ CMDIValidator/trunk/cmdi-validator-core/src/main/java/eu/clarin/cmdi/validator/extensions/CheckHandlesExtension.java	(revision 5394)
@@ -22,7 +22,4 @@
 
 import org.apache.http.HttpStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import net.sf.saxon.s9api.SaxonApiException;
 import net.sf.saxon.s9api.XPathCompiler;
@@ -36,7 +33,8 @@
 import eu.clarin.cmdi.validator.CMDIValidatorWriteableResult;
 import eu.clarin.cmdi.validator.utils.HandleResolver;
+import eu.clarin.cmdi.validator.utils.SaxonLocationUtils;
 
 public class CheckHandlesExtension extends CMDIValidatorExtension {
-    private static final String XPATH = "//*:ResourceProxy[*:ResourceType/text() = 'Resource' or *:ResourceType/text() = 'Metadata']/*:ResourceRef/text()";
+    private static final String XPATH = "//*:ResourceProxy[*:ResourceType/text() = 'Resource' or *:ResourceType/text() = 'Metadata']/*:ResourceRef";
     private static final String HDL_SCHEME = "hdl";
     private static final String HDL_PROXY_HTTP = "http";
@@ -44,7 +42,4 @@
     private static final String HDL_PROXY_HOST = "hdl.handle.net";
     private static final String URN_SCHEME = "urn";
-    @SuppressWarnings("unused")
-    private static final Logger logger =
-            LoggerFactory.getLogger(CheckHandlesExtension.class);
     private final boolean resolveHandles;
     private HandleResolver resolver = null;
@@ -91,4 +86,6 @@
             for (XdmItem item : selector) {
                 String handle = null;
+                final int line   = SaxonLocationUtils.getLineNumber(item);
+                final int column = SaxonLocationUtils.getColumnNumber(item);
                 final String h = item.getStringValue();
                 if (h != null) {
@@ -98,5 +95,5 @@
                     } else {
                         if (!handle.equals(h)) {
-                            result.reportWarning(-1, -1, "handle '" + h +
+                            result.reportWarning(line, column, "handle '" + h +
                                     "' contains leading or tailing spaces " +
                                     "within <ResourceRef> element");
@@ -106,7 +103,7 @@
 
                 if (handle != null) {
-                    checkHandleURISyntax(handle, result);
-                } else {
-                    result.reportError(-1, -1,
+                    checkHandleURISyntax(handle, result, line, column);
+                } else {
+                    result.reportError(line, column,
                             "invalid handle (<ResourceRef> was empty)");
                 }
@@ -119,5 +116,6 @@
 
     private void checkHandleURISyntax(String handle,
-            CMDIValidatorWriteableResult result) throws CMDIValidatorException {
+            CMDIValidatorWriteableResult result, int line, int column)
+            throws CMDIValidatorException {
         try {
             final URI uri = new URI(handle);
@@ -130,5 +128,5 @@
                     final URI actionableURI =
                             new URI(HDL_PROXY_HTTP, HDL_PROXY_HOST, path, null);
-                    checkHandleResolves(actionableURI, result);
+                    checkHandleResolves(actionableURI, result, line, column);
                 } catch (URISyntaxException e) {
                     /* should not happen */
@@ -138,8 +136,8 @@
             } else if (URN_SCHEME.equals(uri.getScheme())) {
                 if (resolveHandles) {
-                    result.reportInfo(-1, -1, "PID '" + handle +
+                    result.reportInfo(line, column, "PID '" + handle +
                             "' skipped, because URN resolving is not supported");
                 } else {
-                    result.reportInfo(-1, -1, "PID '" + handle +
+                    result.reportInfo(line, column, "PID '" + handle +
                             "' skipped, because URN sytax checking is not supported");
                 }
@@ -148,27 +146,27 @@
                 if (uri.getHost() != null) {
                     if (!HDL_PROXY_HOST.equalsIgnoreCase(uri.getHost())) {
-                        result.reportError(-1, -1,
+                        result.reportError(line, column,
                                 "The URI of PID '" + handle +
                                 "' contains an unexpected host part of '" +
                                 uri.getHost() + "'");
                     }
-                    checkHandleResolves(uri, result);
-                } else {
-                    result.reportError(-1, -1, "The URI of PID '" + handle +
-                            "' is missing the host part");
+                    checkHandleResolves(uri, result, line, column);
+                } else {
+                    result.reportError(line, column, "The URI of PID '" +
+                            handle + "' is missing the host part");
                 }
             } else {
                 if (uri.getScheme() != null) {
-                    result.reportError(-1, -1,
+                    result.reportError(line, column,
                             "The URI of PID '" + handle +
                             "' contains an unexpected schema part of '" +
                             uri.getScheme() + "'");
                 } else {
-                    result.reportError(-1, -1, "The URI of PID '" + handle +
-                            "' is missing a proper schema part");
+                    result.reportError(line, column, "The URI of PID '" +
+                            handle + "' is missing a proper schema part");
                 }
             }
         } catch (URISyntaxException e) {
-            result.reportError(-1, -1, "PID '" + handle +
+            result.reportError(line, column, "PID '" + handle +
                     "' is not a well-formed URI: " + e.getMessage());
         }
@@ -177,5 +175,6 @@
 
     private void checkHandleResolves(URI uri,
-            CMDIValidatorWriteableResult result) throws CMDIValidatorException {
+            CMDIValidatorWriteableResult result, int line, int column)
+            throws CMDIValidatorException {
         if (resolver != null) {
             try {
@@ -188,31 +187,31 @@
                     /* FALL-THROUGH */
                 case HttpStatus.SC_FORBIDDEN:
-                    result.reportInfo(-1, -1, "PID '" + uri +
+                    result.reportInfo(line, column, "PID '" + uri +
                             "' resolved to an access protected resource (" +
                             code + ")");
                     break;
                 case HttpStatus.SC_NOT_FOUND:
-                    result.reportError(-1, -1, "PID '" + uri +
-                            "' resolved to an non-existing resource (" + code +
-                            ")");
+                    result.reportError(line, column, "PID '" + uri +
+                            "' resolved to an non-existing resource (" +
+                            code + ")");
                     break;
                 case HandleResolver.TIMEOUT:
-                    result.reportWarning(-1, -1, "Timeout while resolving PID '" +
-                            uri + "'");
+                    result.reportWarning(line, column,
+                            "Timeout while resolving PID '" + uri + "'");
                     break;
                 case HandleResolver.UNKNOWN_HOST:
-                    result.reportWarning(-1, -1, "Unable to resolve host '" +
-                            uri.getHost() + "' while resolving PID '" +
-                            uri + "'");
+                    result.reportWarning(line, column,
+                            "Unable to resolve host '" + uri.getHost() +
+                            "' while resolving PID '" + uri + "'");
                     break;
                 case HandleResolver.ERROR:
-                    result.reportWarning(-1, -1,
+                    result.reportWarning(line, column,
                             "An error occurred while resolving PID '" +
                             uri + "'");
                     break;
                 default:
-                    result.reportWarning(-1, -1, "PID '" + uri +
-                            "' resolved with an unexpected result (" + code +
-                            ")");
+                    result.reportWarning(-line, column, "PID '" + uri +
+                            "' resolved with an unexpected result (" +
+                            code + ")");
                     break;
                 } // switch
Index: CMDIValidator/trunk/cmdi-validator-core/src/main/java/eu/clarin/cmdi/validator/utils/SaxonLocationUtils.java
===================================================================
--- CMDIValidator/trunk/cmdi-validator-core/src/main/java/eu/clarin/cmdi/validator/utils/SaxonLocationUtils.java	(revision 5394)
+++ CMDIValidator/trunk/cmdi-validator-core/src/main/java/eu/clarin/cmdi/validator/utils/SaxonLocationUtils.java	(revision 5394)
@@ -0,0 +1,27 @@
+package eu.clarin.cmdi.validator.utils;
+
+import net.sf.saxon.s9api.XdmItem;
+import net.sf.saxon.s9api.XdmNode;
+
+public final class SaxonLocationUtils {
+    
+    private SaxonLocationUtils() {
+    }
+
+
+    public static int getLineNumber(final XdmItem item) {
+        if ((item != null) && (item instanceof XdmNode)) {
+            return ((XdmNode) item).getLineNumber();
+        }
+        return -1;
+    }
+
+
+    public static int getColumnNumber(final XdmItem item) {
+        if ((item != null) && (item instanceof XdmNode)) {
+            return ((XdmNode) item).getColumnNumber();
+        }
+        return -1;
+    }
+
+} // class SaxonLocationUtils
Index: CMDIValidator/trunk/cmdi-validator-core/src/main/resources/analyze-svrl.xq
===================================================================
--- CMDIValidator/trunk/cmdi-validator-core/src/main/resources/analyze-svrl.xq	(revision 5394)
+++ CMDIValidator/trunk/cmdi-validator-core/src/main/resources/analyze-svrl.xq	(revision 5394)
@@ -0,0 +1,17 @@
+declare namespace svrl = "http://purl.oclc.org/dsdl/svrl";
+
+for $node in //(svrl:failed-assert|svrl:successful-report)
+let $role := data($node/preceding-sibling::svrl:fired-rule[1]/@role)
+let $s :=
+  if (empty($role)) then
+    if (local-name($node) eq 'successful-report') then
+      'I'
+    else
+      'E'
+  else
+    if (fn:starts-with($role, 'info')) then 'I'
+    else if (fn:starts-with($role, 'warn')) then 'W'
+    else 'E'
+let $l := normalize-space(data($node/@location))
+let $m := normalize-space(data($node/svrl:text/text()))
+return <m s="{$s}" l="{$l}">{$m}</m>
