Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bind XML with no grammar constraints to XSD / DTD
Fixes #151 Signed-off-by: azerr <azerr@redhat.com>
- Loading branch information
1 parent
68f9557
commit da67a76
Showing
7 changed files
with
289 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 167 additions & 0 deletions
167
...minx/extensions/contentmodel/participants/codeactions/NoGrammarConstraintsCodeAction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/******************************************************************************* | ||
* 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.codeactions; | ||
|
||
import java.io.File; | ||
import java.net.URI; | ||
import java.nio.file.Files; | ||
import java.nio.file.Paths; | ||
import java.util.List; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
|
||
import org.eclipse.lemminx.commons.BadLocationException; | ||
import org.eclipse.lemminx.commons.CodeActionFactory; | ||
import org.eclipse.lemminx.dom.DOMDocument; | ||
import org.eclipse.lemminx.dom.DOMElement; | ||
import org.eclipse.lemminx.extensions.generators.FileContentGeneratorManager; | ||
import org.eclipse.lemminx.extensions.generators.xml2dtd.DTDGeneratorSettings; | ||
import org.eclipse.lemminx.extensions.generators.xml2xsd.XMLSchemaGeneratorSettings; | ||
import org.eclipse.lemminx.services.XMLCompletions; | ||
import org.eclipse.lemminx.services.extensions.ICodeActionParticipant; | ||
import org.eclipse.lemminx.services.extensions.IComponentProvider; | ||
import org.eclipse.lemminx.settings.SharedSettings; | ||
import org.eclipse.lemminx.utils.StringUtils; | ||
import org.eclipse.lemminx.utils.XMLBuilder; | ||
import org.eclipse.lsp4j.CodeAction; | ||
import org.eclipse.lsp4j.Diagnostic; | ||
import org.eclipse.lsp4j.Position; | ||
import org.eclipse.lsp4j.Range; | ||
import org.eclipse.lsp4j.TextDocumentEdit; | ||
import org.eclipse.lsp4j.jsonrpc.messages.Either; | ||
|
||
/** | ||
* Code Action to bind a XML to a grammar (DTD, XSD) by generating the grammar. | ||
*/ | ||
public class NoGrammarConstraintsCodeAction implements ICodeActionParticipant { | ||
|
||
private static final Logger LOGGER = Logger.getLogger(XMLCompletions.class.getName()); | ||
|
||
@Override | ||
public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument document, List<CodeAction> codeActions, | ||
SharedSettings sharedSettings, IComponentProvider componentProvider) { | ||
try { | ||
DOMElement documentElement = document.getDocumentElement(); | ||
if (documentElement == null || StringUtils.isEmpty(documentElement.getTagName())) { | ||
return; | ||
} | ||
|
||
FileContentGeneratorManager generator = componentProvider.getComponent(FileContentGeneratorManager.class); | ||
String delimiter = document.lineDelimiter(0); | ||
int beforeTagOffset = documentElement.getStartTagOpenOffset(); | ||
int afterTagOffset = beforeTagOffset + 1 + documentElement.getTagName().length(); | ||
|
||
// ---------- XSD | ||
|
||
String schemaURI = getGrammarURI(document.getDocumentURI(), "xsd"); | ||
String schemaFileName = getFileName(schemaURI); | ||
String schemaTemplate = generator.generate(document, sharedSettings, new XMLSchemaGeneratorSettings()); | ||
|
||
// xsi:noNamespaceSchemaLocation | ||
// Create code action to create the XSD file with the generated XSD content | ||
String insertText = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" | ||
+ document.getTextDocument().lineDelimiter(0); | ||
insertText += " xsi:noNamespaceSchemaLocation=\"" + schemaFileName + "\""; | ||
CodeAction noNamespaceSchemaLocationAction = createGrammarFileAndBindIt( | ||
"Bind to the generated XSD '" + schemaFileName + "' with xsi:noNamespaceSchemaLocation", schemaURI, | ||
schemaTemplate, insertText, afterTagOffset, document, diagnostic); | ||
codeActions.add(noNamespaceSchemaLocationAction); | ||
|
||
// xml-model | ||
XMLBuilder xsdWithXmlModel = new XMLBuilder(sharedSettings, null, delimiter); | ||
xsdWithXmlModel.startPrologOrPI("xml-model"); | ||
xsdWithXmlModel.addSingleAttribute("href", schemaFileName, true); | ||
xsdWithXmlModel.endPrologOrPI(); | ||
xsdWithXmlModel.linefeed(); | ||
CodeAction xsdWithXmlModelAction = createGrammarFileAndBindIt( | ||
"Bind to the generated XSD '" + schemaFileName + "' with xml-model", schemaURI, schemaTemplate, | ||
xsdWithXmlModel.toString(), beforeTagOffset, document, diagnostic); | ||
codeActions.add(xsdWithXmlModelAction); | ||
|
||
// ---------- DTD | ||
|
||
String dtdURI = getGrammarURI(document.getDocumentURI(), "dtd"); | ||
String dtdFileName = getFileName(dtdURI); | ||
String dtdTemplate = generator.generate(document, sharedSettings, new DTDGeneratorSettings()); | ||
|
||
// <!DOCTYPE ${1:root-element} SYSTEM \"${2:file}.dtd\"> | ||
XMLBuilder docType = new XMLBuilder(sharedSettings, null, delimiter); | ||
docType.startDoctype(); | ||
docType.addParameter(documentElement.getLocalName()); | ||
docType.addContent(" SYSTEM \""); | ||
docType.addContent(dtdFileName); | ||
docType.addContent("\""); | ||
docType.endDoctype(); | ||
docType.linefeed(); | ||
CodeAction docTypeAction = createGrammarFileAndBindIt( | ||
"Bind to the generated DTD '" + dtdFileName + "' with DOCTYPE", dtdURI, dtdTemplate, | ||
docType.toString(), beforeTagOffset, document, diagnostic); | ||
codeActions.add(docTypeAction); | ||
|
||
// xml-model | ||
XMLBuilder dtdWithXmlModel = new XMLBuilder(sharedSettings, null, delimiter); | ||
dtdWithXmlModel.startPrologOrPI("xml-model"); | ||
dtdWithXmlModel.addSingleAttribute("href", dtdFileName, true); | ||
dtdWithXmlModel.endPrologOrPI(); | ||
dtdWithXmlModel.linefeed(); | ||
CodeAction dtdWithXmlModelAction = createGrammarFileAndBindIt( | ||
"Bind to the generated DTD '" + dtdFileName + "' with xml-model", dtdURI, dtdTemplate, | ||
dtdWithXmlModel.toString(), beforeTagOffset, document, diagnostic); | ||
codeActions.add(dtdWithXmlModelAction); | ||
|
||
} catch (BadLocationException e) { | ||
LOGGER.log(Level.SEVERE, "In NoGrammarConstraintsCodeAction position error", e); | ||
} | ||
} | ||
|
||
/** | ||
* Returns the unique grammar URI file. | ||
* | ||
* @param documentURI the XML document URI. | ||
* @param fileExtension the grammar file extension. | ||
* | ||
* @return the unique grammar URI file. | ||
*/ | ||
private static String getGrammarURI(String documentURI, String fileExtension) { | ||
int index = documentURI.lastIndexOf('.'); | ||
String grammarWithoutExtension = index != -1 ? documentURI.substring(0, index) : documentURI; | ||
String grammarURI = grammarWithoutExtension + "." + fileExtension; | ||
int i = 1; | ||
try { | ||
while (Files.exists(Paths.get(new URI(grammarURI)))) { | ||
grammarURI = grammarWithoutExtension + (i++) + "." + fileExtension; | ||
} | ||
} catch (Exception e) { | ||
// Do nothing | ||
} | ||
return grammarURI; | ||
} | ||
|
||
private static String getFileName(String schemaURI) { | ||
return new File(schemaURI).getName(); | ||
} | ||
|
||
private static CodeAction createGrammarFileAndBindIt(String title, String grammarURI, String grammarContent, | ||
String insertText, int insertOffset, DOMDocument document, Diagnostic diagnostic) | ||
throws BadLocationException { | ||
Position position = document.positionAt(insertOffset); | ||
TextDocumentEdit insertEdit = CodeActionFactory.insertEdit(insertText, position, document.getTextDocument()); | ||
return createGrammarFileAndBindIt(title, grammarURI, grammarContent, insertEdit, diagnostic); | ||
} | ||
|
||
private static CodeAction createGrammarFileAndBindIt(String title, String grammarURI, String grammarContent, | ||
TextDocumentEdit boundEdit, Diagnostic diagnostic) { | ||
CodeAction codeAction = CodeActionFactory.createFile(title, grammarURI, grammarContent, diagnostic); | ||
codeAction.getEdit().getDocumentChanges().add(Either.forLeft(boundEdit)); | ||
return codeAction; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.