Skip to content

Commit

Permalink
validation only happens on starting VS code
Browse files Browse the repository at this point in the history
Fixes redhat-developer/vscode-xml#647

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Feb 6, 2022
1 parent ba438fe commit f72787a
Show file tree
Hide file tree
Showing 11 changed files with 414 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ public void setSystemId(int start, int end) {
systemId = addNewParameter(start, end);
}

public DTDDeclParameter getSystemIdNode() {
return systemId;
}

/*
* (non-Javadoc)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMDocumentType;
import org.eclipse.lemminx.dom.DOMRange;
import org.eclipse.lemminx.dom.DTDEntityDecl;
import org.eclipse.lemminx.dom.NoNamespaceSchemaLocation;
import org.eclipse.lemminx.dom.SchemaLocation;
import org.eclipse.lemminx.dom.SchemaLocationHint;
import org.eclipse.lemminx.dom.XMLModel;
import org.eclipse.lemminx.services.extensions.IDocumentLinkParticipant;
import org.eclipse.lemminx.uriresolver.URIResolverExtensionManager;
import org.eclipse.lsp4j.DocumentLink;
import org.w3c.dom.NamedNodeMap;

/**
* Document link for :
Expand Down Expand Up @@ -82,6 +84,23 @@ public void findDocumentLinks(DOMDocument document, List<DocumentLink> links) {
// Do nothing
}
}

NamedNodeMap entities = docType.getEntities();
for (int i = 0; i < entities.getLength(); i++) {
DTDEntityDecl entity = (DTDEntityDecl) entities.item(i);
location = resolverManager.resolve(document.getDocumentURI(), entity.getPublicId(),
entity.getSystemId());
if (location != null) {
try {
DOMRange systemIdRange = entity.getSystemIdNode();
if (systemIdRange != null) {
links.add(createDocumentLink(systemIdRange, location, true));
}
} catch (BadLocationException e) {
// Do nothing
}
}
}
}
// Document link for xml-model/href
List<XMLModel> xmlModels = document.getXMLModels();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@
import java.util.HashMap;
import java.util.Map;

import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.util.URI.MalformedURIException;
import org.apache.xerces.xni.XMLLocator;
import org.eclipse.lemminx.commons.BadLocationException;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMDocumentType;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.dom.DOMRange;
import org.eclipse.lemminx.dom.DTDEntityDecl;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.ElementDeclUnterminatedCodeAction;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.EntityNotDeclaredCodeAction;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.FixMissingSpaceCodeAction;
Expand All @@ -35,6 +38,7 @@
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.ResourceOperationKind;
import org.w3c.dom.NamedNodeMap;

/**
* DTD error code.
Expand Down Expand Up @@ -212,19 +216,33 @@ public static Range toLSPRange(XMLLocator location, DTDErrorCode code, Object[]
}

case dtd_not_found: {
// Check if DTD location comes from a xml-model/@href
// Check if DTD location is declared with xml-model/@href
String hrefLocation = (String) arguments[1];
DOMRange locationRange = XMLModelUtils.getHrefNode(document, hrefLocation);
if (locationRange != null) {
return XMLPositionUtility.createRange(locationRange);
}
try {
DOMDocumentType docType = document.getDoctype();
return new Range(document.positionAt(docType.getSystemIdNode().getStart()),
document.positionAt(docType.getSystemIdNode().getEnd()));
if (docType != null) {
String documentURI = document.getDocumentURI();
String dtdLocation = getResolvedLocation(documentURI, docType.getSystemIdWithoutQuotes());
if (hrefLocation.equals(dtdLocation)) {
return new Range(document.positionAt(docType.getSystemIdNode().getStart()),
document.positionAt(docType.getSystemIdNode().getEnd()));
}
NamedNodeMap entities = docType.getEntities();
for (int i = 0; i < entities.getLength(); i++) {
DTDEntityDecl entity = (DTDEntityDecl) entities.item(i);
String entityLocation = getResolvedLocation(documentURI, entity.getSystemId());
if (hrefLocation.equals(entityLocation)) {
return XMLPositionUtility.createRange(entity.getSystemIdNode());
}
}
}
} catch (BadLocationException e) {
}
return null;
return XMLPositionUtility.selectRootStartTag(document);
}
default:
try {
Expand All @@ -235,6 +253,22 @@ public static Range toLSPRange(XMLLocator location, DTDErrorCode code, Object[]
return null;
}

/**
* Returns the expanded system location
*
* @return the expanded system location
*/
private static String getResolvedLocation(String documentURI, String location) {
if (location == null) {
return null;
}
try {
return XMLEntityManager.expandSystemId(location, documentURI, false);
} catch (MalformedURIException e) {
return location;
}
}

public static void registerCodeActionParticipants(Map<String, ICodeActionParticipant> codeActions,
SharedSettings sharedSettings) {
codeActions.put(ElementDeclUnterminated.getCode(), new ElementDeclUnterminatedCodeAction());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,16 @@ public void doctypeDecl(String rootElement, String publicId, String systemId, Au
String eid = grammarDesc.getExpandedSystemId();

try {
XMLInputSource input = entityManager.resolveEntity(grammarDesc);
String resolvedSystemId = input.getSystemId();
if (resolvedSystemId != null && resolvedSystemId.startsWith(FilesUtils.FILE_SCHEME)) {
// The resolved DTD is a file, check if the file exists.
if (!FilesUtils.toFile(resolvedSystemId).exists()) {
// The declared DTD file doesn't exist
// <!DOCTYPE root-element SYSTEM "./dtd-doesnt-exist.dtd" []>
throw new FileNotFoundException(resolvedSystemId);
}
}
// XMLInputSource input = entityManager.resolveEntity(grammarDesc);
// String resolvedSystemId = input.getSystemId();
// if (resolvedSystemId != null && resolvedSystemId.startsWith(FilesUtils.FILE_SCHEME)) {
// // The resolved DTD is a file, check if the file exists.
// if (!FilesUtils.toFile(resolvedSystemId).exists()) {
// // The declared DTD file doesn't exist
// // <!DOCTYPE root-element SYSTEM "./dtd-doesnt-exist.dtd" []>
// throw new FileNotFoundException(resolvedSystemId);
// }
// }

if (grammarPool != null) {
// FIX [BUG 2]
Expand Down Expand Up @@ -141,7 +141,7 @@ public void doctypeDecl(String rootElement, String publicId, String systemId, Au
ValidationManager fValidationManager = (ValidationManager) fConfiguration
.getProperty(VALIDATION_MANAGER);
if (fValidationManager != null) {
fValidationManager.setCachedDTD(true);
//fValidationManager.setCachedDTD(true);
}

// As we don't throw an error when DTD content is downloaded, we need to remove
Expand Down Expand Up @@ -198,10 +198,10 @@ private static void fillEntities(DTDGrammar grammar, XMLEntityManager entityMana
@Override
public void setValues(String name, String publicId, String systemId, String baseSystemId, String notation,
String value, boolean isPE, boolean inExternal) {
if (inExternal) {
if (inExternal && value != null) {
// Only entities declared in the cached DTD grammar must be added in the XML
// entity manager.
entityManager.addInternalEntity(name, value);
//entityManager.addInternalEntity(name, value);
}
};
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics;

import java.io.IOException;
import java.io.StringReader;

import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.impl.xs.XSDDescription;
import org.apache.xerces.xni.XMLLocator;
import org.apache.xerces.xni.XMLResourceIdentifier;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.parser.XMLInputSource;
import org.eclipse.lemminx.extensions.contentmodel.participants.DTDErrorCode;
import org.eclipse.lemminx.extensions.xerces.xmlmodel.msg.XMLModelMessageFormatter;

class LSPXMLEntityManager extends XMLEntityManager {

private final XMLErrorReporter errorReporter;

private final LSPXMLGrammarPool grammarPool;
private boolean hasProblemsWithReferencedDTD;

public LSPXMLEntityManager(XMLErrorReporter errorReporter, LSPXMLGrammarPool grammarPool) {
this.errorReporter = errorReporter;
this.grammarPool = grammarPool;
this.hasProblemsWithReferencedDTD = false;
}

@Override
public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) throws IOException, XNIException {
if(resourceIdentifier instanceof XSDDescription) {
return super.resolveEntity(resourceIdentifier);
}
try {
return super.resolveEntity(resourceIdentifier);
} catch (Exception e) {
reportError(resourceIdentifier.getLiteralSystemId(), e);
XMLInputSource in = new XMLInputSource(resourceIdentifier);
in.setCharacterStream(new StringReader(""));
return in;
}
}

@Override
public String setupCurrentEntity(String name, XMLInputSource xmlInputSource, boolean literal, boolean isExternal)
throws IOException, XNIException {
try {
return super.setupCurrentEntity(name, xmlInputSource, literal, isExternal);
} catch (Exception e) {
reportError(xmlInputSource.getSystemId(), e);
XMLInputSource in = new XMLInputSource(xmlInputSource.getPublicId(), xmlInputSource.getSystemId(),
xmlInputSource.getBaseSystemId(), new StringReader(""), null);
return super.setupCurrentEntity(name, in, literal, isExternal);
}
}

private void reportError(String location, Exception e) {
hasProblemsWithReferencedDTD = true;
errorReporter.reportError(new XMLLocator() {

@Override
public String getXMLVersion() {
// TODO Auto-generated method stub
return null;
}

@Override
public String getPublicId() {
// TODO Auto-generated method stub
return null;
}

@Override
public String getLiteralSystemId() {
// TODO Auto-generated method stub
return null;
}

@Override
public int getLineNumber() {
// TODO Auto-generated method stub
return 0;
}

@Override
public String getExpandedSystemId() {
// TODO Auto-generated method stub
return null;
}

@Override
public String getEncoding() {
// TODO Auto-generated method stub
return null;
}

@Override
public int getColumnNumber() {
// TODO Auto-generated method stub
return 0;
}

@Override
public int getCharacterOffset() {
// TODO Auto-generated method stub
return 0;
}

@Override
public String getBaseSystemId() {
// TODO Auto-generated method stub
return null;
}
}, XMLModelMessageFormatter.XML_MODEL_DOMAIN, DTDErrorCode.dtd_not_found.getCode(),
new Object[] { null, location }, XMLErrorReporter.SEVERITY_ERROR, e);
}

public void dispose() {
if (hasProblemsWithReferencedDTD) {
grammarPool.clear();
}
// problems.forEach(uri -> grammarPool.removeGrammar(uri));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics;

import java.util.ArrayList;
import java.util.List;

import org.apache.xerces.xni.grammars.Grammar;
import org.apache.xerces.xni.grammars.XMLGrammarDescription;

public class LSPXMLGrammarPoolWrapper extends LSPXMLGrammarPool {

private final LSPXMLGrammarPool delegate;

private final List<Grammar> cachedGrammars;

public LSPXMLGrammarPoolWrapper(LSPXMLGrammarPool delegate) {
this.delegate = delegate;
this.cachedGrammars = new ArrayList<>();
}

public Grammar[] retrieveInitialGrammarSet(String grammarType) {
return delegate.retrieveInitialGrammarSet(grammarType);
}

public void cacheGrammars(String grammarType, Grammar[] grammars) {
for (Grammar grammar : grammars) {
cachedGrammars.add(grammar);
}
delegate.cacheGrammars(grammarType, grammars);
}

public int hashCode() {
return delegate.hashCode();
}

public Grammar retrieveGrammar(XMLGrammarDescription desc) {
return delegate.retrieveGrammar(desc);
}

public Grammar removeGrammar(XMLGrammarDescription desc) {
return delegate.removeGrammar(desc);
}

public boolean equals(Object obj) {
return delegate.equals(obj);
}

public void removeGrammar(String grammarURI) {
delegate.removeGrammar(grammarURI);
}

public void lockPool() {
delegate.lockPool();
}

public void unlockPool() {
delegate.unlockPool();
}

public void clear() {
for (Grammar grammar : cachedGrammars) {
delegate.removeGrammar(grammar.getGrammarDescription());
}
}

public boolean equals(XMLGrammarDescription desc1, XMLGrammarDescription desc2) {
return delegate.equals(desc1, desc2);
}

public int hashCode(XMLGrammarDescription desc) {
return delegate.hashCode(desc);
}

public String toString() {
return delegate.toString();
}

}
Loading

0 comments on commit f72787a

Please sign in to comment.