Skip to content

Commit

Permalink
Missing xml-model reference generates multiple similar warnings
Browse files Browse the repository at this point in the history
Fixes eclipse-lemminx#795

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Jun 23, 2020
1 parent 2dafd72 commit 15fd1d6
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.lemminx.commons.BadLocationException;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.dom.DOMRange;
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.dtd_not_foundCodeAction;
Expand Down Expand Up @@ -74,7 +75,7 @@ public enum DTDErrorCode implements IXMLErrorCode {
QuoteRequiredInPublicID, //
QuoteRequiredInSystemID, //
SpaceRequiredAfterSYSTEM, //
dtd_not_found("dtd-not-found");
dtd_not_found;

private final String code;

Expand Down Expand Up @@ -194,6 +195,15 @@ public static Range toLSPRange(XMLLocator location, DTDErrorCode code, Object[]
case MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL: {
return XMLPositionUtility.selectDTDDeclTagNameAt(offset, document);
}
case dtd_not_found: {
// Check if DTD location comes from a xml-model/@href
String hrefLocation = (String) arguments[1];
DOMRange locationRange = XMLModelUtils.getHrefNode(document, hrefLocation);
if (locationRange != null) {
return XMLPositionUtility.createRange(locationRange);
}
return null;
}
default:
try {
return new Range(new Position(0, 0), document.positionAt(document.getEnd()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*******************************************************************************
* 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.contentmodel.participants;

import java.util.List;

import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.util.URI.MalformedURIException;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMRange;
import org.eclipse.lemminx.dom.XMLModel;

/**
* XML model utilities.
*
*/
public class XMLModelUtils {

/**
* Returns the DOM range of the href of the xml-model processing instruction
* which matches the given hrefLocation.
*
* @param document the DOM document.
* @param hrefLocation the href location.
* @return the DOM range of the href of the xml-model processing instruction
* which matches the given hrefLocation.
*/
public static DOMRange getHrefNode(DOMDocument document, String hrefLocation) {
if (hrefLocation == null) {
return null;
}
// Check if location comes from a xml-model/@href
List<XMLModel> xmlModels = document.getXMLModels();
if (!xmlModels.isEmpty()) {
String documentURI = document.getDocumentURI();
for (XMLModel xmlModel : xmlModels) {
String href = xmlModel.getHref();
String xmlModelLocation = getResolvedLocation(documentURI, href);
if (hrefLocation.equals(xmlModelLocation)) {
return xmlModel.getHrefNode();
}
}
}
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.dom.DOMNode;
import org.eclipse.lemminx.dom.DOMRange;
import org.eclipse.lemminx.dom.NoNamespaceSchemaLocation;
import org.eclipse.lemminx.dom.SchemaLocation;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.TargetNamespace_1CodeAction;
Expand Down Expand Up @@ -174,25 +174,30 @@ public static Range toLSPRange(XMLLocator location, XMLSchemaErrorCode code, Obj
}
case SchemaLocation:
case schema_reference_4: {
DOMNode attrValueNode = null;
DOMRange locationRange = null;
if (code.equals(SchemaLocation)) {
SchemaLocation schemaLocation = document.getSchemaLocation();
attrValueNode = schemaLocation.getAttr().getNodeAttrValue();
locationRange = schemaLocation.getAttr().getNodeAttrValue();
} else {
NoNamespaceSchemaLocation noNamespaceSchemaLocation = document.getNoNamespaceSchemaLocation();
if (noNamespaceSchemaLocation != null) {
attrValueNode = noNamespaceSchemaLocation.getAttr().getNodeAttrValue();
} else {
SchemaLocation schemaLocation = document.getSchemaLocation();
if (schemaLocation != null) {
attrValueNode = schemaLocation.getAttr().getNodeAttrValue();
String hrefLocation = arguments.length == 1 ? (String) arguments[0] : null;
// Check if location comes from a xml-model/@href
locationRange = XMLModelUtils.getHrefNode(document, hrefLocation);
if (locationRange == null) {
NoNamespaceSchemaLocation noNamespaceSchemaLocation = document.getNoNamespaceSchemaLocation();
if (noNamespaceSchemaLocation != null) {
locationRange = noNamespaceSchemaLocation.getAttr().getNodeAttrValue();
} else {
SchemaLocation schemaLocation = document.getSchemaLocation();
if (schemaLocation != null) {
locationRange = schemaLocation.getAttr().getNodeAttrValue();
}
}
}
}

if (attrValueNode != null) {
int startOffset = attrValueNode.getStart();
int endOffset = attrValueNode.getEnd();
if (locationRange != null) {
int startOffset = locationRange.getStart();
int endOffset = locationRange.getEnd();
try {
Position startPosition = document.positionAt(startOffset);
Position endPosition = document.positionAt(endOffset);
Expand Down Expand Up @@ -272,9 +277,10 @@ public static void registerCodeActionParticipants(Map<String, ICodeActionPartici
codeActions.put(cvc_enumeration_valid.getCode(), new cvc_enumeration_validCodeAction());
codeActions.put(cvc_complex_type_2_1.getCode(), new cvc_complex_type_2_1CodeAction());
codeActions.put(TargetNamespace_1.getCode(), new TargetNamespace_1CodeAction());
codeActions.put(TargetNamespace_2.getCode(), new TargetNamespace_2CodeAction());
codeActions.put(TargetNamespace_2.getCode(), new TargetNamespace_2CodeAction());
if (sharedSettings.getWorkspaceSettings().isResourceOperationSupported(ResourceOperationKind.Create)) {
codeActions.put(schema_reference_4.getCode(), new schema_reference_4CodeAction());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.lemminx.commons.BadLocationException;
import org.eclipse.lemminx.commons.TextDocument;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.extensions.xerces.xmlmodel.msg.XMLModelMessageFormatter;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.Position;
Expand Down Expand Up @@ -52,6 +53,7 @@ public AbstractLSPErrorReporter(String source, DOMDocument xmlDocument, List<Dia
super.putMessageFormatter(XMLMessageFormatter.XML_DOMAIN, xmft);
super.putMessageFormatter(XMLMessageFormatter.XMLNS_DOMAIN, xmft);
super.putMessageFormatter(XSMessageFormatter.SCHEMA_DOMAIN, new LSPMessageFormatter());
super.putMessageFormatter(XMLModelMessageFormatter.XML_MODEL_DOMAIN, new XMLModelMessageFormatter());
}

public String reportError(XMLLocator location, String domain, String key, Object[] arguments, short severity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ public String formatMessage(Locale locale, String key, Object[] arguments) throw

String msg = null;

try {
if (newResourceBundle.containsKey(key)) {
msg = newResourceBundle.getString(key);
usedNewResourceBundle = true;
} catch (NullPointerException | MissingResourceException e) {
} else {
msg = fResourceBundle.getString(key);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
*******************************************************************************/
package org.eclipse.lemminx.extensions.xerces.xmlmodel;

import java.io.IOException;
import java.lang.reflect.Field;

import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.impl.dtd.DTDGrammar;
import org.apache.xerces.impl.dtd.XMLDTDDescription;
import org.apache.xerces.impl.dtd.XMLDTDLoader;
Expand All @@ -28,6 +28,7 @@
import org.apache.xerces.xni.parser.XMLComponentManager;
import org.apache.xerces.xni.parser.XMLConfigurationException;
import org.apache.xerces.xni.parser.XMLInputSource;
import org.eclipse.lemminx.extensions.xerces.xmlmodel.msg.XMLModelMessageFormatter;

/**
* XML model validator which process validation with DTD:
Expand All @@ -39,6 +40,8 @@
*/
public class XMLModelDTDValidator extends XMLDTDValidator implements XMLModelValidator {

private static final String DTD_NOT_FOUND_KEY = "dtd_not_found";

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

private String href;
Expand Down Expand Up @@ -102,8 +105,11 @@ public void startElement(QName element, XMLAttributes attributes, Augmentations
loader.setEntityResolver(entityManager);
try {
fDTDGrammar = (DTDGrammar) loader.loadGrammar(new XMLInputSource(null, eid, null));
} catch (IOException e) {
// TODO : manage report error for DTD not found in xml-model/@ref
} catch (Exception e) {
// DTD declared in xml-model href="" doesn't exist, report the error and disable the DTD validation.
fErrorReporter.reportError(locator, XMLModelMessageFormatter.XML_MODEL_DOMAIN, DTD_NOT_FOUND_KEY,
new Object[] { element, eid }, XMLErrorReporter.SEVERITY_ERROR);
super.fValidation = false;
}
} else {
// we've found a cached one;so let's make sure not to read
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.eclipse.lemminx.extensions.xerces.xmlmodel.msg;

import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import org.apache.xerces.util.MessageFormatter;

/**
* XMLModelMessageFormatter provides error messages for the xml-model
* Recommendation.
*
* <p>
* This class is a copy/paste of Xerces
* org.apache.xerces.impl.msg.XMLMessageFormatter class.
* </p>
*
* @author Eric Ye, IBM
* @author Angelo ZERR
*/
public class XMLModelMessageFormatter implements MessageFormatter {
/**
* The domain of messages concerning the XML 1.0 specification.
*/
public static final String XML_MODEL_DOMAIN = "https://www.w3.org/TR/xml-model/";

// private objects to cache the locale and resource bundle
private Locale fLocale = null;
private ResourceBundle fResourceBundle = null;

//
// MessageFormatter methods
//

/**
* Formats a message with the specified arguments using the given locale
* information.
*
* @param locale The locale of the message.
* @param key The message key.
* @param arguments The message replacement text arguments. The order of the
* arguments must match that of the placeholders in the actual
* message.
*
* @return Returns the formatted message.
*
* @throws MissingResourceException Thrown if the message with the specified key
* cannot be found.
*/
public String formatMessage(Locale locale, String key, Object[] arguments) throws MissingResourceException {

if (locale == null) {
locale = Locale.getDefault();
}
if (locale != fLocale) {
fResourceBundle = ResourceBundle.getBundle("org.eclipse.lemminx.extensions.xerces.xmlmodel.msg.XMLMessages",
locale);
// memorize the most-recent locale
fLocale = locale;
}

// format message
String msg;
try {
msg = fResourceBundle.getString(key);
if (arguments != null) {
try {
msg = java.text.MessageFormat.format(msg, arguments);
} catch (Exception e) {
msg = fResourceBundle.getString("FormatFailed");
msg += " " + fResourceBundle.getString(key);
}
}
}

// error
catch (MissingResourceException e) {
msg = fResourceBundle.getString("BadMessageKey");
throw new MissingResourceException(key, msg, key);
}

// no message
if (msg == null) {
msg = key;
if (arguments.length > 0) {
StringBuilder str = new StringBuilder(msg);
str.append('?');
for (int i = 0; i < arguments.length; i++) {
if (i > 0) {
str.append('&');
}
str.append(String.valueOf(arguments[i]));
}
}
}

return msg;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# added
dtd_not_found = Cannot find DTD ''{1}''.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
*/
public class XMLModelDiagnosticsTest {

@Test
public void xmlModelWithBadDTD() throws Exception {
String xml = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> \r\n" + //
"<?xml-model href=\"BAD.dtd\"?>\r\n" + //
"<root><item /></root>";
testDiagnosticsFor(xml, d(1, 17, 26, DTDErrorCode.dtd_not_found));
}

@Test
public void xmlModelWithDTD() throws Exception {
String xml = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> \r\n" + //
Expand All @@ -36,6 +44,15 @@ public void xmlModelWithDTD() throws Exception {
d(2, 1, 8, DTDErrorCode.MSG_CONTENT_INVALID));
}

@Test
public void xmlModelWithBadXSD() throws Exception {
String xml = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> \r\n" + //
"<?xml-model href=\"BAD.xsd\"?>\r\n" + //
"<root><item /></root>";
testDiagnosticsFor(xml, d(1, 17, 26, XMLSchemaErrorCode.schema_reference_4), //
d(2, 1, 5, XMLSchemaErrorCode.cvc_elt_1_a));
}

@Test
public void xmlModelWithXSD() throws Exception {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n" + //
Expand Down

0 comments on commit 15fd1d6

Please sign in to comment.