Skip to content

Commit

Permalink
Validate XML Schema with Xerces XSD validator. See #190 (#390)
Browse files Browse the repository at this point in the history
Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr authored and NikolasKomonen committed May 29, 2019
1 parent c914529 commit 12f97c0
Show file tree
Hide file tree
Showing 15 changed files with 254 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.extensions.contentmodel.ContentModelPlugin;
import org.eclipse.lsp4xml.services.extensions.diagnostics.IDiagnosticsParticipant;
import org.eclipse.lsp4xml.utils.DOMUtils;

/**
* Validate XML files with Xerces for general SYNTAX validation and XML Schema, DTD.
* Validate XML files with Xerces for general SYNTAX validation and XML Schema,
* DTD.
*
*/
public class ContentModelDiagnosticsParticipant implements IDiagnosticsParticipant {
Expand All @@ -33,8 +35,8 @@ public ContentModelDiagnosticsParticipant(ContentModelPlugin contentModelPlugin)

@Override
public void doDiagnostics(DOMDocument xmlDocument, List<Diagnostic> diagnostics, CancelChecker monitor) {
if (xmlDocument.isDTD()) {
// Don't validate DTD with XML validator
if (xmlDocument.isDTD() || DOMUtils.isXSD(xmlDocument)) {
// Don't validate DTD / XML Schema with XML validator
return;
}
// Get entity resolver (XML catalog resolver, XML schema from the file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public enum XSDErrorCode implements IXMLErrorCode {
s4s_att_invalid_value("s4s-att-invalid-value"), //
s4s_elt_character("s4s-elt-character"), //
src_resolve_4_2("src-resolve.4.2"), //
src_resolve("src-resolve");
src_resolve("src-resolve"), src_element_2_1("src-element.2.1");

private final String code;

Expand Down Expand Up @@ -85,20 +85,27 @@ public static Range toLSPRange(XMLLocator location, XSDErrorCode code, Object[]
case s4s_elt_must_match_1:
case s4s_att_must_appear:
case s4s_elt_invalid_content_2:
case src_element_2_1:
return XMLPositionUtility.selectStartTag(offset, document);
case s4s_att_not_allowed:
return XMLPositionUtility.selectAttributeNameAt(offset, document);
case s4s_att_not_allowed: {
String attrName = (String) arguments[1];
return XMLPositionUtility.selectAttributeNameFromGivenNameAt(attrName, offset, document);
}
case s4s_att_invalid_value: {
String attrName = "";
String attrName = (String) arguments[1];
return XMLPositionUtility.selectAttributeValueAt(attrName, offset, document);
}
case s4s_elt_character:
return XMLPositionUtility.selectContent(offset, document);
case src_resolve_4_2:
case src_resolve:
case src_resolve_4_2: {
String attrValue = (String) arguments[2];
return XMLPositionUtility.selectAttributeValueByGivenValueAt(attrValue, offset, document);
}
case src_resolve: {
String attrValue = (String) arguments[0];
return XMLPositionUtility.selectAttributeValueByGivenValueAt(attrValue, offset, document);
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4xml.dom.DOMDocument;
import org.eclipse.lsp4xml.extensions.contentmodel.participants.XMLSyntaxErrorCode;
import org.eclipse.lsp4xml.extensions.xsd.participants.XSDErrorCode;
import org.eclipse.lsp4xml.services.extensions.diagnostics.AbstractLSPErrorReporter;
import org.xml.sax.ErrorHandler;
Expand Down Expand Up @@ -54,6 +55,13 @@ protected Range toLSPRange(XMLLocator location, String key, Object[] arguments,
return range;
}
}
XMLSyntaxErrorCode syntaxCode = XMLSyntaxErrorCode.get(key);
if (syntaxCode != null) {
Range range = XMLSyntaxErrorCode.toLSPRange(location, syntaxCode, arguments, document);
if (range != null) {
return range;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ public void doDiagnostics(DOMDocument xmlDocument, List<Diagnostic> diagnostics,
// associations settings., ...)
XMLEntityResolver entityResolver = xmlDocument.getResolverExtensionManager();
// Process validation
// XSDValidator.doDiagnostics(xmlDocument, entityResolver, diagnostics,
// monitor);
XSDValidator.doDiagnostics(xmlDocument, entityResolver, diagnostics, monitor);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,20 @@
package org.eclipse.lsp4xml.extensions.xsd.participants.diagnostics;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.impl.xs.XMLSchemaLoader;
import org.apache.xerces.impl.xs.opti.SchemaDOMParser;
import org.apache.xerces.impl.xs.traversers.XSDHandler;
import org.apache.xerces.parsers.XMLGrammarPreparser;
import org.apache.xerces.util.XMLGrammarPoolImpl;
import org.apache.xerces.xni.grammars.XMLGrammarDescription;
Expand All @@ -34,12 +42,18 @@ public class XSDValidator {

private static final Logger LOGGER = Logger.getLogger(XSDValidator.class.getName());

private static boolean canCustomizeReporter = true;

public static void doDiagnostics(DOMDocument document, XMLEntityResolver entityResolver,
List<Diagnostic> diagnostics, CancelChecker monitor) {

try {
XMLErrorReporter reporter = new LSPErrorReporterForXSD(document, diagnostics);

XMLGrammarPreparser grammarPreparser = new LSPXMLGrammarPreparser();
grammarPreparser.registerPreparser(XMLGrammarDescription.XML_SCHEMA, null/* schemaLoader */);
XMLSchemaLoader schemaLoader = createSchemaLoader(reporter);

grammarPreparser.registerPreparser(XMLGrammarDescription.XML_SCHEMA, schemaLoader);

grammarPreparser.setProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY,
new XMLGrammarPoolImpl());
Expand All @@ -57,50 +71,63 @@ public static void doDiagnostics(DOMDocument document, XMLEntityResolver entityR
grammarPreparser.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.WARN_ON_DUPLICATE_ATTDEF_FEATURE,
true);

/*
* if(configuration.getFeature(XSDValidationConfiguration.
* HONOUR_ALL_SCHEMA_LOCATIONS)) { try {
* grammarPreparser.setFeature(Constants.XERCES_FEATURE_PREFIX +
* "honour-all-schemaLocations", true); //$NON-NLS-1$ } catch (Exception e) { //
* catch the exception and ignore } }
*
* if(configuration.getFeature(XSDValidationConfiguration.
* FULL_SCHEMA_CONFORMANCE)) { try {
* grammarPreparser.setFeature(Constants.XERCES_FEATURE_PREFIX +
* Constants.SCHEMA_FULL_CHECKING, true); } catch (Exception e) { // ignore
* since we don't want to set it or can't. }
*
* }
*/

// Add LSP content handler to stop XML parsing if monitor is canceled.
// grammarPreparser.setContentHandler(new LSPContentHandler(monitor));

// Add LSP error reporter to fill LSP diagnostics from Xerces errors
grammarPreparser.setProperty("http://apache.org/xml/properties/internal/error-reporter",
new LSPErrorReporterForXSD(document, diagnostics));
grammarPreparser.setProperty("http://apache.org/xml/properties/internal/error-reporter", reporter);

if (entityResolver != null) {
grammarPreparser.setEntityResolver(entityResolver);
}

String content = document.getText();
String uri = document.getDocumentURI();
InputStream inputStream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
XMLInputSource is = new XMLInputSource(null, uri, uri, inputStream, null);
grammarPreparser.getLoader(XMLGrammarDescription.XML_SCHEMA);
grammarPreparser.preparseGrammar(XMLGrammarDescription.XML_SCHEMA, is);
} catch (IOException | CancellationException exception) {
// ignore error
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Unexpected XMLValidator error", e);
}
}

/**
* Create the XML Schema loader to use to validate the XML Schema.
*
* @param reporter the lsp reporter.
* @return the XML Schema loader to use to validate the XML Schema.
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private static XMLSchemaLoader createSchemaLoader(XMLErrorReporter reporter) {
XMLSchemaLoader schemaLoader = new XMLSchemaLoader();

// To validate XML syntax for XML Schema, we need to use the Xerces Reporter
// (XMLErrorReporter)
// (and not the Xerces XML ErrorHandler because we need the arguments array to
// retrieve the attribut e name, element name, etc)

// Xerces XSD validator can work with Xerces reporter for XSD error but not for
// XML syntax (only XMLErrorHandler is allowed).
// To fix this problem, we set the Xerces reporter with Java Reflection.
if (canCustomizeReporter) {
try {
String content = document.getText();
String uri = document.getDocumentURI();
InputStream inputStream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
XMLInputSource is = new XMLInputSource(null, uri, uri, inputStream, null);
grammarPreparser.getLoader(XMLGrammarDescription.XML_SCHEMA);
grammarPreparser.preparseGrammar(XMLGrammarDescription.XML_SCHEMA, is);
Field f = XMLSchemaLoader.class.getDeclaredField("fSchemaHandler");
f.setAccessible(true);
XSDHandler handler = (XSDHandler) f.get(schemaLoader);

Field g = XSDHandler.class.getDeclaredField("fSchemaParser");
g.setAccessible(true);
SchemaDOMParser domParser = (SchemaDOMParser) g.get(handler);
domParser.setProperty("http://apache.org/xml/properties/internal/error-reporter", reporter);
} catch (Exception e) {
// parser will return null pointer exception if the document is structurally
// invalid
// TODO: log error message
// System.out.println(e);
canCustomizeReporter = false;
}
} catch (Exception e) {
// TODO: log error.
// System.out.println(e);
}
return schemaLoader;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.lsp4xml.dom.DOMElement;
import org.eclipse.lsp4xml.dom.DOMNode;
import org.eclipse.lsp4xml.dom.DOMProcessingInstruction;
import org.eclipse.lsp4xml.dom.DOMText;
import org.eclipse.lsp4xml.dom.DTDAttlistDecl;
import org.eclipse.lsp4xml.dom.DTDDeclNode;
import org.eclipse.lsp4xml.dom.DTDDeclParameter;
Expand Down Expand Up @@ -385,6 +386,9 @@ public static Range selectContent(int offset, DOMDocument document) {
}
// node has NO content (ex: <root></root>, select the start tag
return selectStartTag(node);
} else if (node.isText()) {
DOMText text = (DOMText) node;
return createRange(text.getStartContent(), text.getEndContent(), document);
}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,21 +356,6 @@ public void issue217() {
testDiagnosticsFor(xml, d);
}

@Test
public void cvc_elt_3_1() throws Exception {
String xml = "<?xml version=\"1.0\"?>\r\n" + //
"<xs:schema\r\n" + //
" elementFormDefault=\"qualified\"\r\n" + //
" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\r\n" + //
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n" + //
" <xs:complexType\r\n" + //
" name=\"property\"\r\n" + //
" xsi:nil=\"true\"></xs:complexType>\r\n" + //
"</xs:schema>";
XMLAssert.testDiagnosticsFor(xml, d(7, 3, 7, 17, XMLSchemaErrorCode.cvc_elt_3_1));

}

@Test
public void cvc_type_3_2_1() throws Exception {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
Expand Down
Loading

0 comments on commit 12f97c0

Please sign in to comment.