diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/AbstractXML2GrammarGenerator.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/AbstractXML2GrammarGenerator.java new file mode 100644 index 0000000000..2660af8dd2 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/AbstractXML2GrammarGenerator.java @@ -0,0 +1,124 @@ +/******************************************************************************* +* 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.generators; + +import java.util.List; + +import org.eclipse.lemminx.commons.TextDocument; +import org.eclipse.lemminx.dom.DOMAttr; +import org.eclipse.lemminx.services.IXMLFullFormatter; +import org.eclipse.lemminx.settings.SharedSettings; +import org.eclipse.lemminx.utils.XMLBuilder; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextEdit; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Abstract class to generate a grammar (XSD, DTD, etcc) from a given XML + * source. + * + *

+ * The process is to build a generic {@link GrammarInfo} instance from the XML + * source and each implementation uses this grammar information to generate the + * proper grammar. + *

+ * + * @param the grammar generator settings. + */ +public abstract class AbstractXML2GrammarGenerator + implements IFileContentGenerator { + + @Override + public String generate(Document document, SharedSettings sharedSettings, T generatorSettings, + IXMLFullFormatter formatter) { + String newText = doGenerate(document, sharedSettings, generatorSettings); + if (formatter == null) { + return newText; + } + return formatter.formatFull(new TextDocument(newText, "grammar." + getFileExtension()), + sharedSettings); + } + + protected abstract String getFileExtension(); + + private String doGenerate(Document document, SharedSettings sharedSettings, T generatorSettings) { + // Create the generic grammar information from the XML source document. + GrammarInfo grammar = createGrammar(document); + XMLBuilder builder = new XMLBuilder(sharedSettings, "", ""); + // Generate the grammar content from the grammar information. + generate(grammar, generatorSettings, builder); + return builder.toString(); + } + + /** + * Generate the grammar content from the given grammr information into the given + * builder. + * + * @param grammar the grammar information. + * @param grammarSettings the grammar settings + * @param out the XML builder to update. + */ + protected abstract void generate(GrammarInfo grammar, T grammarSettings, XMLBuilder out); + + private GrammarInfo createGrammar(Document document) { + GrammarInfo grammar = new GrammarInfo(); + // Update default namespace + String defaultNamespace = null; + Element documentElement = document.getDocumentElement(); + if (documentElement != null) { + defaultNamespace = document.getDocumentElement().getAttribute(DOMAttr.XMLNS_ATTR); + } + grammar.setDefaultNamespace(defaultNamespace); + // Update elements information + fillElements(document, grammar); + return grammar; + } + + private static void fillElements(Node node, ChildrenInfo grammar) { + NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) child; + ElementInfo grammarElt = grammar.getElement(element.getLocalName()); + // Has text + grammarElt.setHasCharacterContent(hasCharacterContent(element)); + // Attributes + NamedNodeMap attributes = element.getAttributes(); + if (attributes != null) { + for (int j = 0; j < attributes.getLength(); j++) { + Attr attr = (Attr) attributes.item(i); + grammarElt.getAttribute(attr.getName()); + } + } + fillElements(element, grammarElt); + } + } + } + + private static boolean hasCharacterContent(Element element) { + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.TEXT_NODE) { + return true; + } + } + return false; + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/AttributeInfo.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/AttributeInfo.java new file mode 100644 index 0000000000..f8c006c01d --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/AttributeInfo.java @@ -0,0 +1,34 @@ +/******************************************************************************* +* 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.generators; + +/** + * Attribute information. + * + */ +public class AttributeInfo { + + private final String name; + + public AttributeInfo(String name) { + this.name = name; + } + + /** + * Returns the attribute name. + * + * @return the attribute name. + */ + public String getName() { + return name; + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/ChildrenInfo.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/ChildrenInfo.java new file mode 100644 index 0000000000..fb86e9e67b --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/ChildrenInfo.java @@ -0,0 +1,78 @@ +/******************************************************************************* +* 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.generators; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Children information. + * + */ +public class ChildrenInfo { + + private final Map children; + + public ChildrenInfo() { + this.children = new LinkedHashMap<>(); + } + + /** + * Returns the element information for the given name and create it if not + * found. + * + * @param name the element name + * @return the element information for the given name and create it if not + * found. + */ + public ElementInfo getElement(String name) { + ElementInfo element = children.get(name); + if (element != null) { + return element; + } + element = new ElementInfo(name); + children.put(name, element); + return element; + } + + /** + * Returns the elements information of the node. + * + * @return the elements information of the node. + */ + public Collection getElements() { + return children.values(); + } + + /** + * Returns the all elements information from the node and children. + * + * @return the all elements information from the node and children. + */ + public Collection getAllElements() { + List all = new ArrayList<>(); + fillWithAllElements(all); + return all; + } + + void fillWithAllElements(List allElements) { + for (ElementInfo element : getElements()) { + if (!allElements.contains(element)) { + allElements.add(element); + element.fillWithAllElements(allElements); + } + } + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/ElementInfo.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/ElementInfo.java new file mode 100644 index 0000000000..674aff767e --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/ElementInfo.java @@ -0,0 +1,78 @@ +/******************************************************************************* +* 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.generators; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Element information. + * + */ +public class ElementInfo extends ChildrenInfo { + + private final String name; + + private final Map attributes; + + private boolean hasCharacterContent; + + public ElementInfo(String name) { + this.name = name; + this.attributes = new HashMap<>(); + } + + /** + * Returns the element name. + * + * @return the element name. + */ + public String getName() { + return name; + } + + /** + * Returns the attribute information for the given name and create it if not + * found. + * + * @param name the attribute name + * @return the attribute information for the given name and create it if not + * found. + */ + public AttributeInfo getAttribute(String name) { + AttributeInfo attribute = attributes.get(name); + if (attribute != null) { + return attribute; + } + attribute = new AttributeInfo(name); + attributes.put(name, attribute); + return attribute; + } + + public Collection getAttributes() { + return attributes.values(); + } + + /** + * Returns true if element has character content and false otherwise. + * + * @return true if element has character content and false otherwise. + */ + public boolean hasCharacterContent() { + return hasCharacterContent; + } + + void setHasCharacterContent(boolean hasCharacterContent) { + this.hasCharacterContent = hasCharacterContent; + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/FileContentGeneratorManager.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/FileContentGeneratorManager.java new file mode 100644 index 0000000000..ab94683139 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/FileContentGeneratorManager.java @@ -0,0 +1,92 @@ +/******************************************************************************* +* 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.generators; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.lemminx.extensions.generators.xml2dtd.DTDGeneratorSettings; +import org.eclipse.lemminx.extensions.generators.xml2dtd.XML2DTDGenerator; +import org.eclipse.lemminx.extensions.generators.xml2xsd.XML2XMLSchemaGenerator; +import org.eclipse.lemminx.extensions.generators.xml2xsd.XMLSchemaGeneratorSettings; +import org.eclipse.lemminx.services.IXMLFullFormatter; +import org.eclipse.lemminx.settings.SharedSettings; + +/** + * Generic file generator. + * + *

+ * By default the manager is able to generate : + * + *

    + *
  • XML Schema from a given XML.
  • + *
  • DTD from a given XML.
  • + *
+ *

+ */ +public class FileContentGeneratorManager { + + private static final FileContentGeneratorManager INSTANCE = new FileContentGeneratorManager(); + private final Map, IFileContentGenerator> generators; + + private FileContentGeneratorManager() { + generators = new HashMap<>(); + registerDefaultGenerators(); + } + + /** + * Register default generators. + */ + private void registerDefaultGenerators() { + registerGenerator(new XML2DTDGenerator(), DTDGeneratorSettings.class); + registerGenerator(new XML2XMLSchemaGenerator(), XMLSchemaGeneratorSettings.class); + } + + public static FileContentGeneratorManager getInstance() { + return INSTANCE; + } + + /** + * Register the given generator by using settings class as key generator. + * + * @param generator the generator. + * @param generatorKey the key of the generator identified by the settings + * class. + */ + public void registerGenerator(IFileContentGenerator generator, + Class generatorKey) { + generators.put(generatorKey, generator); + } + + /** + * Generates a file content (ex : XSD, DTD) from the given document source + * document (ex: XML) by using the given settings + * generatorSettings. + * + * @param document the document source (ex : XML). + * @param sharedSettings the shared settings. + * @param generatorSettings the generator settings. + * @param formatter the formatter. + * @return the result of the generation of the file content (ex : XSD, DTD) from + * the given document source document (ex: XML) by using + * the given settings generatorSettings. + */ + public String generate(Source document, + SharedSettings sharedSettings, Settings generatorSettings, IXMLFullFormatter formatter) { + // Get the generator by the generator settings class key + @SuppressWarnings("unchecked") + IFileContentGenerator generator = (IFileContentGenerator) generators + .get(generatorSettings.getClass()); + // process the generator + return generator.generate(document, sharedSettings, generatorSettings, formatter); + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/FileContentGeneratorSettings.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/FileContentGeneratorSettings.java new file mode 100644 index 0000000000..a5a67d4a35 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/FileContentGeneratorSettings.java @@ -0,0 +1,20 @@ +/******************************************************************************* +* 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.generators; + +/** + * Base class for generator settings. + * + */ +public class FileContentGeneratorSettings { + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/GrammarInfo.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/GrammarInfo.java new file mode 100644 index 0000000000..fa40ffb949 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/GrammarInfo.java @@ -0,0 +1,40 @@ +/******************************************************************************* +* 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.generators; + +/** + * Grammar information. + * + */ +public class GrammarInfo extends ChildrenInfo { + + private String defaultNamespace; + + /** + * Returns the default namespace and null otherwise. + * + * @return the default namespace and null otherwise. + */ + public String getDefaultNamespace() { + return defaultNamespace; + } + + /** + * Set the default namespace. + * + * @param defaultNamespace the default namespace + */ + void setDefaultNamespace(String defaultNamespace) { + this.defaultNamespace = defaultNamespace; + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/IFileContentGenerator.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/IFileContentGenerator.java new file mode 100644 index 0000000000..b7aa97841f --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/IFileContentGenerator.java @@ -0,0 +1,41 @@ +/******************************************************************************* +* 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.generators; + +import org.eclipse.lemminx.services.IXMLFullFormatter; +import org.eclipse.lemminx.settings.SharedSettings; + +/** + * File content generator API. + * + * @param the source document (ex : XML) + * @param generator settings class. + */ +public interface IFileContentGenerator { + + /** + * Generates a file content (ex : XSD, DTD) from the given document source + * document (ex: XML) by using the given settings + * generatorSettings. + * + * @param document the XML document source. + * @param sharedSettings the shared settings. + * @param generatorSettings the generator settings. + * @param formatter the formatter. + * @return the result of the generation of the file content (ex : XSD, DTD) from + * the given document source document (ex: XML) by using + * the given settings generatorSettings. + */ + String generate(Source document, SharedSettings sharedSettings, Settings generatorSettings, + IXMLFullFormatter formatter); + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2dtd/DTDGeneratorSettings.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2dtd/DTDGeneratorSettings.java new file mode 100644 index 0000000000..5f70d5d6c4 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2dtd/DTDGeneratorSettings.java @@ -0,0 +1,22 @@ +/******************************************************************************* +* 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.generators.xml2dtd; + +import org.eclipse.lemminx.extensions.generators.FileContentGeneratorSettings; + +/** + * DTD generator settings + * + */ +public class DTDGeneratorSettings extends FileContentGeneratorSettings { + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2dtd/XML2DTDGenerator.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2dtd/XML2DTDGenerator.java new file mode 100644 index 0000000000..e7e69fcd01 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2dtd/XML2DTDGenerator.java @@ -0,0 +1,79 @@ +/******************************************************************************* +* 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.generators.xml2dtd; + +import java.util.Collection; + +import org.eclipse.lemminx.extensions.generators.AbstractXML2GrammarGenerator; +import org.eclipse.lemminx.extensions.generators.AttributeInfo; +import org.eclipse.lemminx.extensions.generators.ElementInfo; +import org.eclipse.lemminx.extensions.generators.GrammarInfo; +import org.eclipse.lemminx.utils.XMLBuilder; + +/** + * File Generator implementation to generate DTD from a given XML source. + * + */ +public class XML2DTDGenerator extends AbstractXML2GrammarGenerator { + + @Override + protected void generate(GrammarInfo grammar, DTDGeneratorSettings settings, XMLBuilder dtd) { + for (ElementInfo element : grammar.getAllElements()) { + + // children = element.getElements(); + if (children.isEmpty()) { + if (element.hasCharacterContent()) { + dtd.addContent(" (#PCDATA)"); + } else { + dtd.addContent(" EMPTY"); + } + } else { + dtd.addContent(" ("); + if (element.hasCharacterContent()) { + dtd.addContent("#PCDATA|"); + } + boolean first = true; + for (ElementInfo elementInfo : children) { + if (!first) { + dtd.addContent(", "); + } + first = false; + dtd.addContent(elementInfo.getName()); + } + dtd.addContent(")"); + } + dtd.closeStartElement(); + + // attributes = element.getAttributes(); + if (!attributes.isEmpty()) { + dtd.startDTDAttlistDecl(); + dtd.addParameter(element.getName()); + for (AttributeInfo attribute : attributes) { + dtd.addParameter(attribute.getName()); + dtd.addParameter("CDATA"); + dtd.addParameter("#IMPLIED"); + } + dtd.closeStartElement(); + } + } + } + + @Override + protected String getFileExtension() { + return "dtd"; + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2xsd/XML2XMLSchemaGenerator.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2xsd/XML2XMLSchemaGenerator.java new file mode 100644 index 0000000000..e563077aa6 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2xsd/XML2XMLSchemaGenerator.java @@ -0,0 +1,98 @@ +/******************************************************************************* +* 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.generators.xml2xsd; + +import java.util.Collection; + +import org.eclipse.lemminx.extensions.generators.AbstractXML2GrammarGenerator; +import org.eclipse.lemminx.extensions.generators.AttributeInfo; +import org.eclipse.lemminx.extensions.generators.ElementInfo; +import org.eclipse.lemminx.extensions.generators.GrammarInfo; +import org.eclipse.lemminx.utils.StringUtils; +import org.eclipse.lemminx.utils.XMLBuilder; + +/** + * File Generator implementation to generate XML Schema (XSD) from a given XML + * source. + * + */ +public class XML2XMLSchemaGenerator extends AbstractXML2GrammarGenerator { + + private static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema"; + + private static final String SCHEMA_TARGET_NAMESPACE_ATTR = "targetNamespace"; + + private static final String ELEMENT_TYPE_ATTR = "type"; + + private static final String ELEMENT_NAME_ATTR = "name"; + + private static final String SEQUENCE_ELT = "sequence"; + + private static final String COMPLEX_TYPE_ELT = "complexType"; + + private static final String SCHEMA_ELT = "schema"; + + private static final String ELEMENT_ELT = "element"; + + private static final String ATTRIBUTE_ELT = "attribute"; + private static final String ATTRIBUTE_NAME_ATTR = "name"; + + @Override + protected void generate(GrammarInfo grammar, XMLSchemaGeneratorSettings settings, XMLBuilder schema) { + String prefix = "xs"; + schema.startElement(prefix, SCHEMA_ELT, false); + schema.addSingleAttribute("xmlns:" + prefix, XML_SCHEMA_NS, true); + String targetNamespace = grammar.getDefaultNamespace(); + if (!StringUtils.isEmpty(targetNamespace)) { + schema.addSingleAttribute(SCHEMA_TARGET_NAMESPACE_ATTR, targetNamespace, true); + } + schema.closeStartElement(); + + // xs:element + for (ElementInfo element : grammar.getElements()) { + schema.startElement(prefix, ELEMENT_ELT, false); + schema.addSingleAttribute(ELEMENT_NAME_ATTR, element.getName(), true); + schema.closeStartElement(); + schema.startElement(prefix, COMPLEX_TYPE_ELT, true); + + // xs:sequence + Collection children = element.getElements(); + if (!children.isEmpty()) { + schema.startElement(prefix, SEQUENCE_ELT, true); + for (ElementInfo child : children) { + schema.startElement(prefix, ELEMENT_ELT, false); + schema.addSingleAttribute(ELEMENT_NAME_ATTR, child.getName(), true); + schema.addSingleAttribute(ELEMENT_TYPE_ATTR, prefix + ":string", true); + schema.selfCloseElement(); + } + schema.endElement(prefix, SEQUENCE_ELT); + } + + // xs:attribute + for (AttributeInfo attribute : element.getAttributes()) { + schema.startElement(prefix, ATTRIBUTE_ELT, false); + schema.addSingleAttribute(ATTRIBUTE_NAME_ATTR, attribute.getName(), true); + schema.selfCloseElement(); + } + + schema.endElement(prefix, COMPLEX_TYPE_ELT); + schema.endElement(prefix, ELEMENT_ELT); + } + + schema.endElement(prefix, SCHEMA_ELT); + } + + @Override + protected String getFileExtension() { + return "xsd"; + } +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2xsd/XMLSchemaGeneratorSettings.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2xsd/XMLSchemaGeneratorSettings.java new file mode 100644 index 0000000000..1310b860ff --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/extensions/generators/xml2xsd/XMLSchemaGeneratorSettings.java @@ -0,0 +1,22 @@ +/******************************************************************************* +* 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.generators.xml2xsd; + +import org.eclipse.lemminx.extensions.generators.FileContentGeneratorSettings; + +/** + * XML Schema generator settings + * + */ +public class XMLSchemaGeneratorSettings extends FileContentGeneratorSettings { + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/IXMLFullFormatter.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/IXMLFullFormatter.java new file mode 100644 index 0000000000..18961d6a5a --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/IXMLFullFormatter.java @@ -0,0 +1,37 @@ +/******************************************************************************* +* 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.services; + +import java.util.List; + +import org.eclipse.lemminx.commons.TextDocument; +import org.eclipse.lemminx.settings.SharedSettings; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextEdit; + +/** + * XML formatter API. + * + */ +public interface IXMLFullFormatter { + + /** + * Format the given text document by using range and shared settings. + * + * @param document the text document. + * @param range the range and null otherwise. + * @param sharedSettings the shared settings. + * + * @return the formatted text document by using range and shared settings. + */ + String formatFull(TextDocument document, SharedSettings sharedSettings); +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLanguageService.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLanguageService.java index 3591035953..5b88e348dd 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLanguageService.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/XMLLanguageService.java @@ -57,7 +57,7 @@ * XML Language service. * */ -public class XMLLanguageService extends XMLExtensionsRegistry { +public class XMLLanguageService extends XMLExtensionsRegistry implements IXMLFullFormatter { private static final CancelChecker NULL_CHECKER = new CancelChecker() { @@ -99,8 +99,13 @@ public XMLLanguageService() { this.rename = new XMLRename(this); } - public List format(TextDocument document, Range range, - SharedSettings sharedSettings) { + @Override + public String formatFull(TextDocument document, SharedSettings sharedSettings) { + List edits = format(document, null, sharedSettings); + return edits.isEmpty() ? null : edits.get(0).getNewText(); + } + + public List format(TextDocument document, Range range, SharedSettings sharedSettings) { return formatter.format(document, range, sharedSettings); } @@ -117,7 +122,8 @@ public List findSymbolInformations(DOMDocument xmlDocument, X return findSymbolInformations(xmlDocument, symbolSettings, NULL_CHECKER); } - public SymbolInformationResult findSymbolInformations(DOMDocument xmlDocument, XMLSymbolSettings symbolSettings, CancelChecker cancelChecker) { + public SymbolInformationResult findSymbolInformations(DOMDocument xmlDocument, XMLSymbolSettings symbolSettings, + CancelChecker cancelChecker) { return symbolsProvider.findSymbolInformations(xmlDocument, symbolSettings, cancelChecker); } @@ -125,7 +131,8 @@ public List findDocumentSymbols(DOMDocument xmlDocument, XMLSymb return findDocumentSymbols(xmlDocument, symbolSettings, NULL_CHECKER); } - public DocumentSymbolsResult findDocumentSymbols(DOMDocument xmlDocument, XMLSymbolSettings symbolSettings, CancelChecker cancelChecker) { + public DocumentSymbolsResult findDocumentSymbols(DOMDocument xmlDocument, XMLSymbolSettings symbolSettings, + CancelChecker cancelChecker) { return symbolsProvider.findDocumentSymbols(xmlDocument, symbolSettings, cancelChecker); } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLBuilder.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLBuilder.java index 9f981e37cc..0d7accb15c 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLBuilder.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/utils/XMLBuilder.java @@ -388,6 +388,16 @@ public XMLBuilder startDoctype() { return this; } + public XMLBuilder startDTDElementDecl() { + xml.append(" exp List actualEdits = workspaceEdit.getChanges().get("test://test/test.html"); assertArrayEquals(expectedEdits.toArray(), actualEdits.toArray()); } + + // ------------------- Generator assert + + public static void assertGrammarGenerator(String xml, FileContentGeneratorSettings grammarSettings, + String expected) { + DOMDocument document = DOMParser.getInstance().parse(xml, "test.xml", null); + SharedSettings sharedSettings = new SharedSettings(); + XMLLanguageService languageService = new XMLLanguageService(); + String actual = FileContentGeneratorManager.getInstance().generate(document, sharedSettings, grammarSettings, + languageService); + Assertions.assertEquals(expected, actual); + } } \ No newline at end of file diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/generators/xml2dtd/XML2DTDGeneratorTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/generators/xml2dtd/XML2DTDGeneratorTest.java new file mode 100644 index 0000000000..5ab3da05d7 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/generators/xml2dtd/XML2DTDGeneratorTest.java @@ -0,0 +1,50 @@ +/******************************************************************************* +* 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.generators.xml2dtd; + +import static java.lang.System.lineSeparator; +import static org.eclipse.lemminx.XMLAssert.assertGrammarGenerator; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +/** + * Tests for generating DTD from XML source. + * + */ +public class XML2DTDGeneratorTest { + + @Test + public void dtd() throws IOException { + String xml = "\r\n" + // + " Tove\r\n" + // + " Jani\r\n" + // + " Reminder\r\n" + // + " Don't forget me this weekend!\r\n" + // + ""; + String dtd = "" + lineSeparator() + // + "" + lineSeparator() + // + "" + lineSeparator() + // + "" + lineSeparator() + // + ""; + assertGrammarGenerator(xml, new DTDGeneratorSettings(), dtd); + } + + @Test + public void dtdWithAttr() throws IOException { + String xml = ""; + String dtd = "" + lineSeparator() + // + ""; + assertGrammarGenerator(xml, new DTDGeneratorSettings(), dtd); + } +} diff --git a/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/generators/xml2xsd/XML2XMLSchemaGeneratorTest.java b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/generators/xml2xsd/XML2XMLSchemaGeneratorTest.java new file mode 100644 index 0000000000..49b4ba1cc3 --- /dev/null +++ b/org.eclipse.lemminx/src/test/java/org/eclipse/lemminx/extensions/generators/xml2xsd/XML2XMLSchemaGeneratorTest.java @@ -0,0 +1,98 @@ +/******************************************************************************* +* 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.generators.xml2xsd; + +import static java.lang.System.lineSeparator; +import static org.eclipse.lemminx.XMLAssert.assertGrammarGenerator; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +/** + * Tests for generating XSD (XML Schema) from XML source. + * + */ +public class XML2XMLSchemaGeneratorTest { + + @Test + public void schema() throws IOException { + String xml = "\r\n" + // + " Tove\r\n" + // + " Jani\r\n" + // + " Reminder\r\n" + // + " Don't forget me this weekend!\r\n" + // + ""; + String xsd = "" + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + ""; + assertGrammarGenerator(xml, new XMLSchemaGeneratorSettings(), xsd); + } + + @Test + public void schemaWithNS() throws IOException { + String xml = "\r\n" + // + " Tove\r\n" + // + " Jani\r\n" + // + " Reminder\r\n" + // + " Don't forget me this weekend!\r\n" + // + ""; + String xsd = "" + + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + ""; + assertGrammarGenerator(xml, new XMLSchemaGeneratorSettings(), xsd); + } + + @Test + public void schemaWithAttr() throws IOException { + String xml = "\r\n" + // + " Tove\r\n" + // + " Jani\r\n" + // + " Reminder\r\n" + // + " Don't forget me this weekend!\r\n" + // + ""; + String xsd = "" + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + " " + lineSeparator() + // + ""; + assertGrammarGenerator(xml, new XMLSchemaGeneratorSettings(), xsd); + } +}