Skip to content

Commit

Permalink
Validate XML with DTD/XML Schema by using xml-model
Browse files Browse the repository at this point in the history
See eclipse-lemminx#697

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed May 15, 2020
1 parent 6fb6e08 commit c4804f0
Show file tree
Hide file tree
Showing 19 changed files with 1,034 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.EntityReference;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;

/**
* XML document.
*
*/
public class DOMDocument extends DOMNode implements Document {

private static final String XML_MODEL_PI = "xml-model";

private SchemaLocation schemaLocation;
private NoNamespaceSchemaLocation noNamespaceSchemaLocation;
private boolean referencedExternalGrammarInitialized;
Expand Down Expand Up @@ -148,7 +151,20 @@ public TextDocument getTextDocument() {
* @return true if the document is bound to a grammar and false otherwise.
*/
public boolean hasGrammar() {
return hasDTD() || hasSchemaLocation() || hasNoNamespaceSchemaLocation() || hasExternalGrammar();
return hasGrammar(false);
}

/**
* Returns true if the document is bound to a grammar and false otherwise.
*
* @param excludeXMLModel true if xml-model must be excluded and false
* otherwise.
*
* @return true if the document is bound to a grammar and false otherwise.
*/
public boolean hasGrammar(boolean excludeXMLModel) {
return hasDTD() || hasSchemaLocation() || hasNoNamespaceSchemaLocation() || hasExternalGrammar()
|| (!excludeXMLModel && hasXMLModel());
}

// -------------------------- Grammar with XML Schema
Expand Down Expand Up @@ -319,6 +335,29 @@ public boolean hasDTD() {
return getDoctype() != null;
}

// -------------------------- Grammar with <?xml-model processing instruction

/**
* Returns true if XML document has a xml-model processing declaration and false
* otherwise.
*
* @return true if XML document has a xml-model processing declaration and false
* otherwise.
*/
private boolean hasXMLModel() {
List<DOMNode> children = getChildren();
if (children != null && !children.isEmpty()) {
return children.stream().anyMatch(child -> {
return isXMLModel(child);
});
}
return false;
}

private static boolean isXMLModel(DOMNode node) {
return node.isProcessingInstruction() && XML_MODEL_PI.equals(((ProcessingInstruction) node).getTarget());
}

// -------------------------- External Grammar (XML file associations, catalog)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ public static Range toLSPRange(XMLLocator location, XMLSchemaErrorCode code, Obj
}
case SchemaLocation:
case schema_reference_4: {
DOMNode attrValueNode;
DOMNode attrValueNode = null;
if (code.equals(SchemaLocation)) {
SchemaLocation schemaLocation = document.getSchemaLocation();
attrValueNode = schemaLocation.getAttr().getNodeAttrValue();
Expand All @@ -176,7 +176,9 @@ public static Range toLSPRange(XMLLocator location, XMLSchemaErrorCode code, Obj
attrValueNode = noNamespaceSchemaLocation.getAttr().getNodeAttrValue();
} else {
SchemaLocation schemaLocation = document.getSchemaLocation();
attrValueNode = schemaLocation.getAttr().getNodeAttrValue();
if (schemaLocation != null) {
attrValueNode = schemaLocation.getAttr().getNodeAttrValue();
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import org.eclipse.lemminx.extensions.contentmodel.participants.DTDErrorCode;
import org.eclipse.lemminx.extensions.contentmodel.participants.XMLSchemaErrorCode;
import org.eclipse.lemminx.extensions.contentmodel.participants.XMLSyntaxErrorCode;
import org.eclipse.lemminx.services.extensions.xerces.AbstractLSPErrorReporter;
import org.eclipse.lemminx.extensions.xerces.AbstractLSPErrorReporter;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Range;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
package org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics;

import org.apache.xerces.impl.dtd.XMLDTDValidator;
import org.apache.xerces.parsers.XIncludeAwareParserConfiguration;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.grammars.XMLGrammarPool;
import org.apache.xerces.xni.parser.XMLComponentManager;
import org.apache.xerces.xni.parser.XMLConfigurationException;
import org.eclipse.lemminx.extensions.contentmodel.settings.XMLValidationSettings;
import org.eclipse.lemminx.extensions.xerces.xmlmodel.XMLModelAwareParserConfiguration;

/**
* Custom Xerces XML parser configuration to :
Expand All @@ -31,7 +31,7 @@
* </ul>
*
*/
class LSPXMLParserConfiguration extends XIncludeAwareParserConfiguration {
class LSPXMLParserConfiguration extends XMLModelAwareParserConfiguration {

private final boolean disableDTDValidation;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public static void doDiagnostics(DOMDocument document, XMLEntityResolver entityR
// Add LSP content handler to stop XML parsing if monitor is canceled.
parser.setContentHandler(new LSPContentHandler(monitor));

boolean hasGrammar = document.hasGrammar();
boolean hasGrammar = document.hasGrammar(true);

// If diagnostics for Schema preference is enabled
if ((validationSettings == null) || validationSettings.isSchema()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Contributors:
* Angelo Zerr <[email protected]> - initial API and implementation
*/
package org.eclipse.lemminx.services.extensions.xerces;
package org.eclipse.lemminx.extensions.xerces;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eclipse.lemminx.services.extensions.xerces;
package org.eclipse.lemminx.extensions.xerces;

import static org.eclipse.lemminx.dom.parser.Constants.*;
import static org.eclipse.lemminx.utils.StringUtils.getString;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*******************************************************************************
* Copyright (c) 2020 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.xerces.xmlmodel;

import org.apache.xerces.parsers.XIncludeAwareParserConfiguration;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.xni.XMLDocumentHandler;
import org.apache.xerces.xni.grammars.XMLGrammarPool;
import org.apache.xerces.xni.parser.XMLComponentManager;
import org.apache.xerces.xni.parser.XMLDocumentSource;

/**
* This class is the configuration used to parse XML 1.0 and XML 1.1 documents
* and provides support for xml-model association.
*
* @see https://www.w3.org/TR/xml-model/
*/
public class XMLModelAwareParserConfiguration extends XIncludeAwareParserConfiguration {

protected boolean xmlModelEnabled = true;
private XMLModelHandler xmlModelHandler;

/** Default constructor. */
public XMLModelAwareParserConfiguration() {
this(null, null, null);
} // <init>()

/**
* Constructs a parser configuration using the specified symbol table.
*
* @param symbolTable The symbol table to use.
*/
public XMLModelAwareParserConfiguration(SymbolTable symbolTable) {
this(symbolTable, null, null);
} // <init>(SymbolTable)

/**
* Constructs a parser configuration using the specified symbol table and
* grammar pool.
* <p>
*
* @param symbolTable The symbol table to use.
* @param grammarPool The grammar pool to use.
*/
public XMLModelAwareParserConfiguration(SymbolTable symbolTable, XMLGrammarPool grammarPool) {
this(symbolTable, grammarPool, null);
} // <init>(SymbolTable,XMLGrammarPool)

/**
* Constructs a parser configuration using the specified symbol table, grammar
* pool, and parent settings.
* <p>
*
* @param symbolTable The symbol table to use.
* @param grammarPool The grammar pool to use.
* @param parentSettings The parent settings.
*/
public XMLModelAwareParserConfiguration(SymbolTable symbolTable, XMLGrammarPool grammarPool,
XMLComponentManager parentSettings) {
super(symbolTable, grammarPool, parentSettings);
}

@Override
protected void configurePipeline() {
super.configurePipeline();
configureXMLModelPipeline();
}

@Override
protected void configureXML11Pipeline() {
super.configureXML11Pipeline();
configureXMLModelPipeline();
}

private void configureXMLModelPipeline() {
if (xmlModelEnabled) {
// If the xml-model handler was not in the pipeline insert it.
if (xmlModelHandler == null) {
xmlModelHandler = new XMLModelHandler();
// add XMLModel component
// setProperty(XMLModel_HANDLER, fXMLModelHandler);
addCommonComponent(xmlModelHandler);
xmlModelHandler.reset(this);
}
// configure XML document pipeline: insert after DTDValidator and
// before XML Schema validator
XMLDocumentSource prev = null;
if (fFeatures.get(XMLSCHEMA_VALIDATION) == Boolean.TRUE) {
// we don't have to worry about fSchemaValidator being null, since
// super.configurePipeline() instantiated it if the feature was set
prev = fSchemaValidator.getDocumentSource();
}
// Otherwise, insert after the last component in the pipeline
else {
prev = fLastComponent;
fLastComponent = xmlModelHandler;
}

XMLDocumentHandler next = prev.getDocumentHandler();
prev.setDocumentHandler(xmlModelHandler);
xmlModelHandler.setDocumentSource(prev);
if (next != null) {
xmlModelHandler.setDocumentHandler(next);
next.setDocumentSource(xmlModelHandler);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright (c) 2020 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.xerces.xmlmodel;

/**
* XML model constants.
*
*/
public class XMLModelConstants {

private XMLModelConstants() {
}

public static final String XML_MODEL_PI = "xml-model";

public static final String HREF_ATTR = "href";
}
Loading

0 comments on commit c4804f0

Please sign in to comment.