Skip to content

Commit

Permalink
RNG validator
Browse files Browse the repository at this point in the history
Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
azerr authored and angelozerr committed Dec 20, 2022
1 parent 5db338d commit 23c94c5
Show file tree
Hide file tree
Showing 14 changed files with 794 additions and 18 deletions.
Expand Up @@ -44,7 +44,7 @@ public ContentModelDiagnosticsParticipant(ContentModelPlugin contentModelPlugin)
public void doDiagnostics(DOMDocument xmlDocument, List<Diagnostic> diagnostics,
XMLValidationSettings validationSettings, CancelChecker monitor) {
downloadExternalResourcesIfNeeded(diagnostics);
if (xmlDocument.isDTD() || DOMUtils.isXSD(xmlDocument)) {
if (xmlDocument.isDTD() || DOMUtils.isXSD(xmlDocument) || DOMUtils.isRelaxNG(xmlDocument)) {
// Don't validate DTD / XML Schema with XML validator
return;
}
Expand Down
Expand Up @@ -129,9 +129,8 @@ && isSchemaValidationEnabled(document, validationSettings)
parser.setFeature("http://xml.org/sax/features/namespaces", namespacesValidationEnabled); //$NON-NLS-1$

// Parse XML
String content = document.getText();
String uri = document.getDocumentURI();
parseXML(content, uri, parser);
InputSource input = DOMUtils.createInputSource(document);
parser.parse(input);
} catch (IOException | SAXException | CancellationException exception) {
// ignore error
} catch (CacheResourceException e) {
Expand Down Expand Up @@ -320,13 +319,6 @@ private static boolean hasExternalSchemaGrammar(DOMDocument document) {
|| externalGrammarLocation.containsKey(IExternalGrammarLocationProvider.SCHEMA_LOCATION);
}

private static void parseXML(String content, String uri, SAXParser parser) throws SAXException, IOException {
InputSource inputSource = new InputSource();
inputSource.setCharacterStream(new StringReader(content));
inputSource.setSystemId(uri);
parser.parse(inputSource);
}

/**
* Returns true is DTD validation must be disabled and false otherwise.
*
Expand Down
Expand Up @@ -17,6 +17,7 @@
import org.eclipse.lemminx.extensions.relaxng.grammar.rng.RNGCodeLensParticipant;
import org.eclipse.lemminx.extensions.relaxng.grammar.rng.RNGCompletionParticipant;
import org.eclipse.lemminx.extensions.relaxng.grammar.rng.RNGDefinitionParticipant;
import org.eclipse.lemminx.extensions.relaxng.grammar.rng.RNGDiagnosticsParticipant;
import org.eclipse.lemminx.extensions.relaxng.grammar.rng.RNGDocumentLinkParticipant;
import org.eclipse.lemminx.extensions.relaxng.grammar.rng.RNGHighlightingParticipant;
import org.eclipse.lemminx.extensions.relaxng.grammar.rng.RNGReferenceParticipant;
Expand All @@ -32,6 +33,7 @@
import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry;
import org.eclipse.lemminx.services.extensions.codelens.ICodeLensParticipant;
import org.eclipse.lemminx.services.extensions.completion.ICompletionParticipant;
import org.eclipse.lemminx.services.extensions.diagnostics.IDiagnosticsParticipant;
import org.eclipse.lemminx.services.extensions.save.ISaveContext;
import org.eclipse.lemminx.uriresolver.URIResolverExtension;
import org.eclipse.lemminx.utils.DOMUtils;
Expand All @@ -55,6 +57,8 @@ public class RelaxNGPlugin implements IXMLExtension {
private final IRenameParticipant renameParticipant;
private final IDocumentLinkParticipant documentLinkParticipant;

private final IDiagnosticsParticipant diagnosticsParticipant;

public RelaxNGPlugin() {
completionParticipant = new RNGCompletionParticipant();
definitionParticipant = new RNGDefinitionParticipant();
Expand All @@ -63,6 +67,7 @@ public RelaxNGPlugin() {
highlightingParticipant = new RNGHighlightingParticipant();
renameParticipant = new RNGRenameParticipant();
documentLinkParticipant = new RNGDocumentLinkParticipant();
this.diagnosticsParticipant = new RNGDiagnosticsParticipant(this);
}

@Override
Expand Down Expand Up @@ -93,6 +98,7 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
registry.registerHighlightingParticipant(highlightingParticipant);
registry.registerRenameParticipant(renameParticipant);
registry.registerDocumentLinkParticipant(documentLinkParticipant);
registry.registerDiagnosticsParticipant(diagnosticsParticipant);
}

@Override
Expand All @@ -106,6 +112,7 @@ public void stop(XMLExtensionsRegistry registry) {
registry.unregisterHighlightingParticipant(highlightingParticipant);
registry.unregisterRenameParticipant(renameParticipant);
registry.unregisterDocumentLinkParticipant(documentLinkParticipant);
registry.unregisterDiagnosticsParticipant(diagnosticsParticipant);
}

public ContentModelManager getContentModelManager() {
Expand Down
@@ -0,0 +1,81 @@
/*******************************************************************************
* Copyright (c) 2022 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.extensions.relaxng.grammar.rng;

import java.util.List;

import org.apache.xerces.xni.parser.XMLEntityResolver;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics.XMLValidator;
import org.eclipse.lemminx.extensions.contentmodel.settings.SchemaEnabled;
import org.eclipse.lemminx.extensions.contentmodel.settings.XMLSchemaSettings;
import org.eclipse.lemminx.extensions.contentmodel.settings.XMLValidationSettings;
import org.eclipse.lemminx.extensions.relaxng.RelaxNGPlugin;
import org.eclipse.lemminx.extensions.xerces.LSPXMLEntityResolver;
import org.eclipse.lemminx.services.extensions.diagnostics.DiagnosticsResult;
import org.eclipse.lemminx.services.extensions.diagnostics.IDiagnosticsParticipant;
import org.eclipse.lemminx.utils.DOMUtils;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;

/**
* Validate RNG file with Jing.
*
*/
public class RNGDiagnosticsParticipant implements IDiagnosticsParticipant {

private final RelaxNGPlugin rngPlugin;

public RNGDiagnosticsParticipant(RelaxNGPlugin rngPlugin) {
this.rngPlugin = rngPlugin;
}

@Override
public void doDiagnostics(DOMDocument xmlDocument, List<Diagnostic> diagnostics,
XMLValidationSettings validationSettings, CancelChecker monitor) {
if (!DOMUtils.isRelaxNGXMLSyntax(xmlDocument)) {
// Don't use the RNG validator, if the XML document is not a RNG Schema.
return;
}

// Get entity resolver (XML catalog resolver, XML schema from the file
// associations settings., ...)
XMLEntityResolver entityResolver = xmlDocument.getResolverExtensionManager();
LSPXMLEntityResolver entityResolverWrapper = new LSPXMLEntityResolver(entityResolver,
(DiagnosticsResult) diagnostics);
ContentModelManager contentModelManager = rngPlugin.getContentModelManager();
if (!isSchemaEnabled(validationSettings)) {
// Validate only XML syntax for RNG
// Process validation
XMLValidator.doDiagnostics(xmlDocument, entityResolverWrapper, diagnostics, validationSettings,
contentModelManager, monitor);
return;
}

// Process RNG validation
RNGValidator.doDiagnostics(xmlDocument, entityResolverWrapper, diagnostics, validationSettings,
rngPlugin.getContentModelManager(), monitor);
}

private static boolean isSchemaEnabled(XMLValidationSettings validationSettings) {
if (validationSettings == null) {
return true;
}
XMLSchemaSettings schemaSettings = validationSettings.getSchema();
if (schemaSettings == null) {
return true;
}
return !SchemaEnabled.never.equals(schemaSettings.getEnabled());
}

}
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2022 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.extensions.relaxng.grammar.rng;

import org.apache.xerces.parsers.XIncludeAwareParserConfiguration;
import org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics.LSPErrorReporterForXML;

/**
* RNG parser configuration used to set the LSP error reporter.
*
* @author Angelo ZERR
*
*/
public class RNGParserConfiguration extends XIncludeAwareParserConfiguration {


public RNGParserConfiguration (LSPErrorReporterForXML reporter) {
fErrorReporter = reporter;
}
}
@@ -0,0 +1,48 @@
/*******************************************************************************
* Copyright (c) 2022 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.extensions.relaxng.grammar.rng;

import org.apache.xerces.impl.Constants;
import org.apache.xerces.parsers.SAXParser;
import org.apache.xerces.xni.parser.XMLParserConfiguration;
import org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics.LSPErrorReporterForXML;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;

/**
* Extends Xerces SAX Parser to use the LSP error reporter.
*
* @author Angelo ZERR
*
*/
public class RNGSAXParser extends SAXParser {

protected static final String VALIDATION_MANAGER = Constants.XERCES_PROPERTY_PREFIX
+ Constants.VALIDATION_MANAGER_PROPERTY;

protected static final String ENTITY_MANAGER = Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_MANAGER_PROPERTY;

public RNGSAXParser(LSPErrorReporterForXML reporter, XMLParserConfiguration config) {
super(config);
init(reporter);
}

private void init(LSPErrorReporterForXML reporter) {
try {
// Add LSP error reporter to fill LSP diagnostics from Xerces errors
super.setProperty("http://apache.org/xml/properties/internal/error-reporter", reporter);
} catch (SAXNotRecognizedException | SAXNotSupportedException e) {
// Should never occur.
}
}

}
@@ -0,0 +1,73 @@
/*******************************************************************************
* Copyright (c) 2022 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.extensions.relaxng.grammar.rng;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.xerces.xni.parser.XMLEntityResolver;
import org.apache.xerces.xni.parser.XMLParseException;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics.LSPErrorReporterForXML;
import org.eclipse.lemminx.extensions.contentmodel.settings.XMLValidationSettings;
import org.eclipse.lemminx.extensions.relaxng.jing.SchemaProvider;
import org.eclipse.lemminx.extensions.xerces.ReferencedGrammarDiagnosticsInfo;
import org.eclipse.lemminx.utils.DOMUtils;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
import org.xml.sax.InputSource;

import com.thaiopensource.relaxng.pattern.MySchemaPatternBuilder;
import com.thaiopensource.validate.IncorrectSchemaException;
import com.thaiopensource.xml.sax.XMLReaderCreator;

/**
* RNG validator utilities class.
*
*/
public class RNGValidator {

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

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

Map<String, ReferencedGrammarDiagnosticsInfo> referencedGrammarDiagnosticsInfoCache = new HashMap<>();
// When referenced grammar (XSD, DTD) have an error (ex : syntax error), the
// error must be reported.
// We create a reporter for grammar since Xerces reporter stores the XMLLocator
// for XML and Grammar.
LSPErrorReporterForXML reporterForRNG = new LSPErrorReporterForXML(document, diagnostics, contentModelManager,
validationSettings != null ? validationSettings.isRelatedInformation() : false,
referencedGrammarDiagnosticsInfoCache);
try {
InputSource input = DOMUtils.createInputSource(document);
XMLReaderCreator creator = new RNGXMLReaderCreator(reporterForRNG);
SchemaProvider.loadSchema(input, entityResolver, reporterForRNG,
new MySchemaPatternBuilder(), creator);
} catch (IOException | CancellationException | XMLParseException | IncorrectSchemaException exception ) {
// ignore error
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Unexpected RNG Validator error", e);
} finally {
reporterForRNG.endReport();
}
}

}
@@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2022 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lemminx.extensions.relaxng.grammar.rng;

import org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics.LSPErrorReporterForXML;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import com.thaiopensource.xml.sax.XMLReaderCreator;

/**
* {@link XMLReaderCreator} to create the RNG parser.
*
* @author Angelo ZERR
*
*/
public class RNGXMLReaderCreator implements XMLReaderCreator {

private final LSPErrorReporterForXML reporter;

public RNGXMLReaderCreator(LSPErrorReporterForXML reporter) {
this.reporter = reporter;
}

public XMLReader createXMLReader() throws SAXException {
RNGSAXParser parser = new RNGSAXParser(reporter, new RNGParserConfiguration(reporter));
parser.setFeature("http://xml.org/sax/features/namespace-prefixes", false); //$NON-NLS-1$
parser.setFeature("http://xml.org/sax/features/namespaces", true); //$NON-NLS-1$
parser.setFeature("http://xml.org/sax/features/validation", false);
return parser;
}
}

0 comments on commit 23c94c5

Please sign in to comment.