From 5488280279ff7473fc8f5ae739e5b04f55952781 Mon Sep 17 00:00:00 2001 From: azerr Date: Fri, 15 Jan 2021 16:17:57 +0100 Subject: [PATCH] Manage namespaces / prefix validation with a settings Fixes #960 Signed-off-by: azerr --- .../diagnostics/XMLValidator.java | 28 +++++++ .../settings/NamespacesEnabled.java | 17 +++++ .../settings/XMLNamespacesSettings.java | 56 ++++++++++++++ .../settings/XMLValidationSettings.java | 43 +++++++++-- .../META-INF/native-image/reflect-config.json | 13 ++++ .../XMLSyntaxDiagnosticsTest.java | 73 ++++++++++++++++++- 6 files changed, 223 insertions(+), 7 deletions(-) create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/NamespacesEnabled.java create mode 100644 org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/XMLNamespacesSettings.java diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/diagnostics/XMLValidator.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/diagnostics/XMLValidator.java index 2f91376ff..4e62090fa 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/diagnostics/XMLValidator.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/participants/diagnostics/XMLValidator.java @@ -36,7 +36,9 @@ import org.eclipse.lemminx.dom.SchemaLocationHint; import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager; import org.eclipse.lemminx.extensions.contentmodel.participants.XMLSyntaxErrorCode; +import org.eclipse.lemminx.extensions.contentmodel.settings.NamespacesEnabled; import org.eclipse.lemminx.extensions.contentmodel.settings.SchemaEnabled; +import org.eclipse.lemminx.extensions.contentmodel.settings.XMLNamespacesSettings; import org.eclipse.lemminx.extensions.contentmodel.settings.XMLSchemaSettings; import org.eclipse.lemminx.extensions.contentmodel.settings.XMLValidationSettings; import org.eclipse.lemminx.services.extensions.diagnostics.LSPContentHandler; @@ -111,6 +113,10 @@ && isSchemaValidationEnabled(document, validationSettings) } parser.setFeature("http://xml.org/sax/features/validation", hasGrammar); //$NON-NLS-1$ + boolean namespacesValidationEnabled = isNamespacesValidationEnabled(document, validationSettings); + parser.setFeature("http://xml.org/sax/features/namespace-prefixes", namespacesValidationEnabled); //$NON-NLS-1$ + parser.setFeature("http://xml.org/sax/features/namespaces", namespacesValidationEnabled); //$NON-NLS-1$ + // Parse XML String content = document.getText(); String uri = document.getDocumentURI(); @@ -127,6 +133,28 @@ && isSchemaValidationEnabled(document, validationSettings) } } + private static boolean isNamespacesValidationEnabled(DOMDocument document, + XMLValidationSettings validationSettings) { + if (validationSettings == null) { + return true; + } + NamespacesEnabled enabled = NamespacesEnabled.always; + XMLNamespacesSettings namespacesSettings = validationSettings.getNamespaces(); + if (namespacesSettings != null && namespacesSettings.getEnabled() != null) { + enabled = namespacesSettings.getEnabled(); + } + switch (enabled) { + case always: + return true; + case never: + return false; + case onNamespaceEncountered: + return document.hasNamespaces(); + default: + return true; + } + } + private static boolean isSchemaValidationEnabled(DOMDocument document, XMLValidationSettings validationSettings) { if (validationSettings == null) { return true; diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/NamespacesEnabled.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/NamespacesEnabled.java new file mode 100644 index 000000000..9d758b12d --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/NamespacesEnabled.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2021 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * 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.contentmodel.settings; + +public enum NamespacesEnabled { + always, never, onNamespaceEncountered; +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/XMLNamespacesSettings.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/XMLNamespacesSettings.java new file mode 100644 index 000000000..07a1b60c2 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/XMLNamespacesSettings.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2021 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * 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.contentmodel.settings; + +/** + * XML Namespaces settings. + * + */ +public class XMLNamespacesSettings { + + public XMLNamespacesSettings() { + setEnabled(NamespacesEnabled.always); + } + + private NamespacesEnabled enabled; + + public void setEnabled(NamespacesEnabled enabled) { + this.enabled = enabled; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((enabled == null) ? 0 : enabled.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + XMLNamespacesSettings other = (XMLNamespacesSettings) obj; + if (enabled != other.enabled) + return false; + return true; + } + + public NamespacesEnabled getEnabled() { + return enabled; + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/XMLValidationSettings.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/XMLValidationSettings.java index 7ff8d32c7..0d48cff97 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/XMLValidationSettings.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/contentmodel/settings/XMLValidationSettings.java @@ -20,10 +20,12 @@ */ public class XMLValidationSettings { - private XMLSchemaSettings schema; - private Boolean enabled; + private XMLNamespacesSettings namespaces; + + private XMLSchemaSettings schema; + private boolean disallowDocTypeDecl; private boolean resolveExternalEntities; @@ -46,19 +48,41 @@ public XMLValidationSettings() { } /** - * @return the syntax + * Returns true if the validation is enabled and false otherwise. + * + * @return true if the validation is enabled and false otherwise. */ public boolean isEnabled() { return enabled; } /** - * @param syntax the syntax to set + * Set true if the validation is enabled and false otherwise. + * + * @param enabled true if the validation is enabled and false otherwise. */ public void setEnabled(boolean enabled) { this.enabled = enabled; } + /** + * Returns the XML Namespaces validation settings. + * + * @return the XML Namespaces validation settings. + */ + public XMLNamespacesSettings getNamespaces() { + return namespaces; + } + + /** + * Set the XML Namespaces validation settings. + * + * @param namespaces the XML Namespaces validation settings. + */ + public void setNamespaces(XMLNamespacesSettings namespaces) { + this.namespaces = namespaces; + } + /** * Returns the XML Schema validation settings. * @@ -69,7 +93,9 @@ public XMLSchemaSettings getSchema() { } /** - * @param schema the schema to set + * Set the XML Schema validation settings. + * + * @param schema the XML Schema validation settings. */ public void setSchema(XMLSchemaSettings schema) { this.schema = schema; @@ -151,6 +177,7 @@ public static DiagnosticSeverity getNoGrammarSeverity(XMLValidationSettings vali public XMLValidationSettings merge(XMLValidationSettings settings) { if (settings != null) { + this.namespaces = settings.namespaces; this.schema = settings.schema; this.enabled = settings.enabled; this.disallowDocTypeDecl = settings.disallowDocTypeDecl; @@ -174,6 +201,7 @@ public int hashCode() { int result = 1; result = prime * result + (disallowDocTypeDecl ? 1231 : 1237); result = prime * result + ((enabled == null) ? 0 : enabled.hashCode()); + result = prime * result + ((namespaces == null) ? 0 : namespaces.hashCode()); result = prime * result + ((noGrammar == null) ? 0 : noGrammar.hashCode()); result = prime * result + (resolveExternalEntities ? 1231 : 1237); result = prime * result + ((schema == null) ? 0 : schema.hashCode()); @@ -196,6 +224,11 @@ public boolean equals(Object obj) { return false; } else if (!enabled.equals(other.enabled)) return false; + if (namespaces == null) { + if (other.namespaces != null) + return false; + } else if (!namespaces.equals(other.namespaces)) + return false; if (noGrammar == null) { if (other.noGrammar != null) return false; diff --git a/org.eclipse.lemminx/src/main/resources/META-INF/native-image/reflect-config.json b/org.eclipse.lemminx/src/main/resources/META-INF/native-image/reflect-config.json index a07e34f34..8ecc0bd04 100644 --- a/org.eclipse.lemminx/src/main/resources/META-INF/native-image/reflect-config.json +++ b/org.eclipse.lemminx/src/main/resources/META-INF/native-image/reflect-config.json @@ -285,8 +285,21 @@ "parameterTypes": [] }] }, + { + "name": "org.eclipse.lemminx.extensions.contentmodel.settings.XMLNamespacesSettings", + "allDeclaredFields": true, + "methods": [{ + "name": "", + "parameterTypes": [] + }] + }, + { + "name": "org.eclipse.lemminx.extensions.contentmodel.settings.NamespacesEnabled", + "allDeclaredFields": true + }, { "name": "org.eclipse.lemminx.extensions.contentmodel.settings.XMLSchemaSettings", + "allDeclaredFields": true, "methods": [{ "name": "", "parameterTypes": [] diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java index b8f5000d6..f60241716 100644 --- a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/contentmodel/XMLSyntaxDiagnosticsTest.java @@ -20,7 +20,10 @@ import org.eclipse.lemminx.XMLAssert; import org.eclipse.lemminx.extensions.contentmodel.participants.XMLSyntaxErrorCode; +import org.eclipse.lemminx.extensions.contentmodel.settings.ContentModelSettings; +import org.eclipse.lemminx.extensions.contentmodel.settings.NamespacesEnabled; import org.eclipse.lemminx.extensions.contentmodel.settings.SchemaEnabled; +import org.eclipse.lemminx.extensions.contentmodel.settings.XMLNamespacesSettings; import org.eclipse.lemminx.settings.EnforceQuoteStyle; import org.eclipse.lemminx.settings.QuoteStyle; import org.eclipse.lemminx.settings.SharedSettings; @@ -632,8 +635,8 @@ public void testMissingQuotesForAttributeSingleQuotes() throws Exception { public void testOpenQuoteExpectedDisabledPreference() throws Exception { String xml = " 10000000"; testDiagnosticsFor(xml, null, null, null, true, XMLAssert.getContentModelSettings(false, SchemaEnabled.always)); // validation - // is - // disabled + // is + // disabled } @Test @@ -781,4 +784,70 @@ public void closeTag() throws Exception { testDiagnosticsFor(xml, d); testCodeActionsFor(xml, d, ca(d, te(0, 5, 0, 5, "a>"))); } + + @Test + public void namespacesSettingsWithoutXMLNS() throws Exception { + String xml = "\r\n" + // + " \r\n" + // + ""; + // always + ContentModelSettings settings = getSettingsForNamespaces(NamespacesEnabled.always); + testDiagnosticsFor(xml, null, null, null, true, settings, // + d(1, 2, 1, 7, XMLSyntaxErrorCode.ElementPrefixUnbound)); + + // never + settings = getSettingsForNamespaces(NamespacesEnabled.never); + testDiagnosticsFor(xml, null, null, null, true, settings); + + // onNamespaceEncountered + settings = getSettingsForNamespaces(NamespacesEnabled.onNamespaceEncountered); + testDiagnosticsFor(xml, null, null, null, true, settings); + } + + @Test + public void namespacesSettingsWithUnvalidXMLNS() throws Exception { + String xml = "\r\n" + // + " \r\n" + // + ""; + // always + ContentModelSettings settings = getSettingsForNamespaces(NamespacesEnabled.always); + testDiagnosticsFor(xml, null, null, null, true, settings, // + d(1, 2, 1, 7, XMLSyntaxErrorCode.ElementPrefixUnbound)); + + // never + settings = getSettingsForNamespaces(NamespacesEnabled.never); + testDiagnosticsFor(xml, null, null, null, true, settings); + + // onNamespaceEncountered + settings = getSettingsForNamespaces(NamespacesEnabled.onNamespaceEncountered); + testDiagnosticsFor(xml, null, null, null, true, settings, // + d(1, 2, 1, 7, XMLSyntaxErrorCode.ElementPrefixUnbound)); + } + + @Test + public void namespacesSettingsWithValidXMLNS() throws Exception { + String xml = "\r\n" + // + " \r\n" + // + ""; + // always + ContentModelSettings settings = getSettingsForNamespaces(NamespacesEnabled.always); + testDiagnosticsFor(xml, null, null, null, true, settings); + + // never + settings = getSettingsForNamespaces(NamespacesEnabled.never); + testDiagnosticsFor(xml, null, null, null, true, settings); + + // onNamespaceEncountered + settings = getSettingsForNamespaces(NamespacesEnabled.onNamespaceEncountered); + testDiagnosticsFor(xml, null, null, null, true, settings); + } + + private static ContentModelSettings getSettingsForNamespaces(NamespacesEnabled namespacesEnabled) { + ContentModelSettings settings = XMLAssert.getContentModelSettings(true, SchemaEnabled.never); + settings.getValidation().setNoGrammar("ignore"); + XMLNamespacesSettings namespaces = new XMLNamespacesSettings(); + namespaces.setEnabled(namespacesEnabled); + settings.getValidation().setNamespaces(namespaces); + return settings; + } }