Skip to content

Commit

Permalink
XSD with targetNamespace cannot be used with xml.fileAssociations
Browse files Browse the repository at this point in the history
See redhat-developer/vscode-xml#223

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
azerr authored and datho7561 committed Nov 5, 2020
1 parent c9c0a42 commit a1c820e
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 21 deletions.
Expand Up @@ -261,8 +261,8 @@ private synchronized void initializeReferencedSchema() {
for (DOMAttr attr : documentElement.getAttributeNodes()) {
String attributeName = attr.getName();
if (attributeName != null) {
if (attributeName.equals("xmlns") || attributeName.startsWith("xmlns:")) //$NON-NLS-1$ //$NON-NLS-2$
{
if (attributeName.equals(DOMAttr.XMLNS_ATTR)
|| attributeName.startsWith(DOMAttr.XMLNS_NO_DEFAULT_ATTR)) {
hasNamespaces = true;
String attributeValue = documentElement.getAttribute(attributeName);
if (attributeValue != null && attributeValue.startsWith("http://www.w3.org/")) {
Expand Down
Expand Up @@ -23,6 +23,7 @@
import org.apache.xerces.parsers.SAXParser;
import org.apache.xerces.xni.grammars.XMLGrammarPool;
import org.apache.xerces.xni.parser.XMLEntityResolver;
import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMDocumentType;
import org.eclipse.lemminx.dom.DOMElement;
Expand All @@ -32,6 +33,7 @@
import org.eclipse.lemminx.services.extensions.diagnostics.LSPContentHandler;
import org.eclipse.lemminx.uriresolver.CacheResourceDownloadingException;
import org.eclipse.lemminx.uriresolver.IExternalGrammarLocationProvider;
import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lemminx.utils.XMLPositionUtility;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
Expand Down Expand Up @@ -82,7 +84,7 @@ public static void doDiagnostics(DOMDocument document, XMLEntityResolver entityR
// If diagnostics for Schema preference is enabled
if ((validationSettings == null) || validationSettings.isSchema()) {

checkExternalSchema(document.getExternalGrammarLocation(), parser);
updateExternalGrammarLocation(document, parser);
parser.setFeature("http://apache.org/xml/features/validation/schema", hasSchemaGrammar); //$NON-NLS-1$

// warn if XML document is not bound to a grammar according the settings
Expand Down Expand Up @@ -192,16 +194,33 @@ private static void warnNoGrammar(DOMDocument document, List<Diagnostic> diagnos
}
}

private static void checkExternalSchema(Map<String, String> result, SAXParser reader)
private static void updateExternalGrammarLocation(DOMDocument document, SAXParser reader)
throws SAXNotRecognizedException, SAXNotSupportedException {
if (result != null) {
String noNamespaceSchemaLocation = result
.get(IExternalGrammarLocationProvider.NO_NAMESPACE_SCHEMA_LOCATION);
if (noNamespaceSchemaLocation != null) {
reader.setProperty(IExternalGrammarLocationProvider.NO_NAMESPACE_SCHEMA_LOCATION,
noNamespaceSchemaLocation);
Map<String, String> externalGrammarLocation = document.getExternalGrammarLocation();
if (externalGrammarLocation != null) {
String xsd = externalGrammarLocation.get(IExternalGrammarLocationProvider.NO_NAMESPACE_SCHEMA_LOCATION);
if (xsd != null) {
// Try to get the xmlns attribute (default namespace) value from the DOM
// document
String defaultNamespace = null;
DOMElement documentElement = document.getDocumentElement();
if (documentElement != null) {
defaultNamespace = documentElement.getAttribute(DOMAttr.XMLNS_ATTR);
}
if (StringUtils.isEmpty(defaultNamespace)) {
// The DOM document has no namespace, we consider that it's the same thing than
// xsi:noNamespaceSchemaLocation
String noNamespaceSchemaLocation = xsd;
reader.setProperty(IExternalGrammarLocationProvider.NO_NAMESPACE_SCHEMA_LOCATION,
noNamespaceSchemaLocation);
} else {
// The DOM document has namespace, we consider that it's the same thing than
// xsi:schemaLocation
String schemaLocation = defaultNamespace + " " + xsd;
reader.setProperty(IExternalGrammarLocationProvider.SCHEMA_LOCATION, schemaLocation);
}
} else {
String doctype = result.get(IExternalGrammarLocationProvider.DOCTYPE);
String doctype = externalGrammarLocation.get(IExternalGrammarLocationProvider.DOCTYPE);
if (doctype != null) {
reader.setProperty(IExternalGrammarLocationProvider.DOCTYPE, doctype);
}
Expand Down
Expand Up @@ -111,7 +111,7 @@ private static Grammar createGrammar(Document sourceDocument, boolean flat) {
String defaultNamespace = null;
Element documentElement = sourceDocument.getDocumentElement();
if (documentElement != null) {
defaultNamespace = sourceDocument.getDocumentElement().getAttribute(DOMAttr.XMLNS_ATTR);
defaultNamespace = documentElement.getAttribute(DOMAttr.XMLNS_ATTR);
}
grammar.setDefaultNamespace(defaultNamespace);
// Update elements information
Expand Down
Expand Up @@ -13,6 +13,7 @@
package org.eclipse.lemminx.extensions.contentmodel;

import static org.eclipse.lemminx.XMLAssert.c;
import static org.eclipse.lemminx.XMLAssert.te;

import java.util.function.Consumer;

Expand All @@ -29,11 +30,14 @@
*/
public class XMLFileAssociationsCompletionTest {

// ------- XML file association with XSD xs:noNamespaceShemaLocation like

@Test
public void completionOnRootWithXSD() throws BadLocationException {
Consumer<XMLLanguageService> configuration = ls -> {
ContentModelManager contentModelManager = ls.getComponent(ContentModelManager.class);
contentModelManager.setFileAssociations(createXSDAssociations("src/test/resources/xsd/"));
contentModelManager
.setFileAssociations(createXSDAssociationsNoNamespaceSchemaLocationLike("src/test/resources/xsd/"));
};
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
" <|";
Expand All @@ -52,7 +56,8 @@ public void completionAfterRootWithXSD() throws BadLocationException {
Consumer<XMLLanguageService> configuration = ls -> {
// Configure language service with file asociations
ContentModelManager contentModelManager = ls.getComponent(ContentModelManager.class);
contentModelManager.setFileAssociations(createXSDAssociations("src/test/resources/xsd/"));
contentModelManager
.setFileAssociations(createXSDAssociationsNoNamespaceSchemaLocationLike("src/test/resources/xsd/"));
};

String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
Expand All @@ -78,7 +83,7 @@ public void rootURIEndsWithSlashWithXSD() throws BadLocationException {
ContentModelManager contentModelManager = ls.getComponent(ContentModelManager.class);
// Use root URI which ends with slash
contentModelManager.setRootURI("src/test/resources/xsd/");
contentModelManager.setFileAssociations(createXSDAssociations(""));
contentModelManager.setFileAssociations(createXSDAssociationsNoNamespaceSchemaLocationLike(""));
};

String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
Expand All @@ -99,7 +104,7 @@ public void rootURIEndsWithNoSlashWithXSD() throws BadLocationException {
ContentModelManager contentModelManager = ls.getComponent(ContentModelManager.class);
// Use root URI which ends with no slash
contentModelManager.setRootURI("src/test/resources/xsd");
contentModelManager.setFileAssociations(createXSDAssociations(""));
contentModelManager.setFileAssociations(createXSDAssociationsNoNamespaceSchemaLocationLike(""));
};

String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + //
Expand All @@ -114,7 +119,7 @@ public void rootURIEndsWithNoSlashWithXSD() throws BadLocationException {
c("Configuration", "<Configuration></Configuration>"));
}

private static XMLFileAssociation[] createXSDAssociations(String baseSystemId) {
private static XMLFileAssociation[] createXSDAssociationsNoNamespaceSchemaLocationLike(String baseSystemId) {
XMLFileAssociation format = new XMLFileAssociation();
format.setPattern("**/*.Format.ps1xml");
format.setSystemId(baseSystemId + "Format.xsd");
Expand All @@ -124,6 +129,32 @@ private static XMLFileAssociation[] createXSDAssociations(String baseSystemId) {
return new XMLFileAssociation[] { format, resources };
}

// ------- XML file association with XSD xs:schemaLocation like

@Test
public void completionOnRootWithXSDAndNS() throws BadLocationException {
Consumer<XMLLanguageService> configuration = ls -> {
ContentModelManager contentModelManager = ls.getComponent(ContentModelManager.class);
contentModelManager.setFileAssociations(createXSDAssociationsSchemaLocationLike("src/test/resources/xsd/"));
};
// completion on <|
String xml = "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\r\n>\r\n" + //
" <|" + //
"</project>";
testCompletionFor(xml, "file:///test/pom.xml", configuration, //
c("modelVersion", te(2, 1, 2, 2, "<modelVersion></modelVersion>"), "<modelVersion"), //
c("parent", "<parent></parent>", "<parent"));
}

private static XMLFileAssociation[] createXSDAssociationsSchemaLocationLike(String baseSystemId) {
XMLFileAssociation maven = new XMLFileAssociation();
maven.setPattern("**/pom.xml");
maven.setSystemId(baseSystemId + "maven-4.0.0.xsd");
return new XMLFileAssociation[] { maven };
}

// ------- XML file association with DTD

@Test
public void completionOnRootWithDTD() throws BadLocationException {
Consumer<XMLLanguageService> configuration = ls -> {
Expand Down
Expand Up @@ -42,13 +42,15 @@
*/
public class XMLFileAssociationsDiagnosticsTest {

// ------- XML file association with XSD xs:noNamespaceShemaLocation like

@Test
public void validationOnRoot() throws BadLocationException {
Consumer<XMLLanguageService> configuration = ls -> {
ContentModelManager contentModelManager = ls.getComponent(ContentModelManager.class);
// Use root URI which ends with slash
contentModelManager.setRootURI("src/test/resources/xsd/");
contentModelManager.setFileAssociations(createXSDAssociations(""));
contentModelManager.setFileAssociations(createXSDAssociationsNoNamespaceSchemaLocationLike(""));
};

// Use Format.xsd which defines Configuration as root element
Expand Down Expand Up @@ -179,7 +181,7 @@ public void validationOnRootWithRequiredAttr() throws BadLocationException {
ContentModelManager contentModelManager = ls.getComponent(ContentModelManager.class);
// Use root URI which ends with slash
contentModelManager.setRootURI("src/test/resources/xsd/");
contentModelManager.setFileAssociations(createXSDAssociations(""));
contentModelManager.setFileAssociations(createXSDAssociationsNoNamespaceSchemaLocationLike(""));
};

// Use resources.xsd which defines resources as root element and @variant as
Expand All @@ -206,7 +208,7 @@ public void validationAfterRoot() throws BadLocationException {
ContentModelManager contentModelManager = ls.getComponent(ContentModelManager.class);
// Use root URI which ends with slash
contentModelManager.setRootURI("src/test/resources/xsd/");
contentModelManager.setFileAssociations(createXSDAssociations(""));
contentModelManager.setFileAssociations(createXSDAssociationsNoNamespaceSchemaLocationLike(""));
};

// Use resources.xsd which defines resources as root element and @variant as
Expand All @@ -221,7 +223,7 @@ public void validationAfterRoot() throws BadLocationException {

}

private static XMLFileAssociation[] createXSDAssociations(String baseSystemId) {
private static XMLFileAssociation[] createXSDAssociationsNoNamespaceSchemaLocationLike(String baseSystemId) {
XMLFileAssociation format = new XMLFileAssociation();
format.setPattern("**/*.Format.ps1xml");
format.setSystemId(baseSystemId + "Format.xsd");
Expand All @@ -231,6 +233,31 @@ private static XMLFileAssociation[] createXSDAssociations(String baseSystemId) {
return new XMLFileAssociation[] { format, resources };
}

// ------- XML file association with XSD xs:schemaLocation like

@Test
public void validationWithExternalXSDAndNS() throws BadLocationException {
Consumer<XMLLanguageService> configuration = ls -> {
ContentModelManager contentModelManager = ls.getComponent(ContentModelManager.class);
contentModelManager.setFileAssociations(createXSDAssociationsSchemaLocationLike("src/test/resources/xsd/"));
};
String xml = "<project xmlns=\"http://maven.apache.org/POM/4.0.0\">\r\n" + //
" <XXX></XXX>\r\n" + // <- error
"</project>";

testDiagnosticsFor(xml, "file:///test/pom.xml", configuration,
d(1, 2, 1, 5, XMLSchemaErrorCode.cvc_complex_type_2_4_a));
}

private static XMLFileAssociation[] createXSDAssociationsSchemaLocationLike(String baseSystemId) {
XMLFileAssociation maven = new XMLFileAssociation();
maven.setPattern("**/pom.xml");
maven.setSystemId(baseSystemId + "maven-4.0.0.xsd");
return new XMLFileAssociation[] { maven };
}

// ------- XML file association with DTD

@Test
public void validationWithExternalDTD() throws BadLocationException {
Consumer<XMLLanguageService> configuration = ls -> {
Expand Down

0 comments on commit a1c820e

Please sign in to comment.