From 5d28905c4833b0c82ea202a650c7b1d08d504430 Mon Sep 17 00:00:00 2001 From: slachiewicz Date: Sat, 8 Aug 2026 15:43:03 +0200 Subject: [PATCH] Allow arbitrary attributes on DOM elements in generated XSD A DOM field maps to an element that carries free-form XML, so merge hints such as combine.self and combine.children may appear on it. The generated schema declared no attributes there, which made a POM using them fail validation. Emit for DOM fields only. Properties fields share the same writer but their container element never carries attributes, so they keep the stricter schema. Fixes #482 Co-authored-by: XenoAmess --- .../modello/plugin/xsd/XsdGenerator.java | 20 ++++- .../plugin/xsd/DomXsdGeneratorTest.java | 88 +++++++++++++++++++ .../dom-invalid-properties-attribute.xml | 8 ++ .../src/test/resources/dom.mdo | 42 +++++++++ .../src/test/resources/dom.xml | 11 +++ 5 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 modello-plugins/modello-plugin-xsd/src/test/java/org/codehaus/modello/plugin/xsd/DomXsdGeneratorTest.java create mode 100644 modello-plugins/modello-plugin-xsd/src/test/resources/dom-invalid-properties-attribute.xml create mode 100644 modello-plugins/modello-plugin-xsd/src/test/resources/dom.mdo create mode 100644 modello-plugins/modello-plugin-xsd/src/test/resources/dom.xml diff --git a/modello-plugins/modello-plugin-xsd/src/main/java/org/codehaus/modello/plugin/xsd/XsdGenerator.java b/modello-plugins/modello-plugin-xsd/src/main/java/org/codehaus/modello/plugin/xsd/XsdGenerator.java index 99af81981..b5ca4c063 100644 --- a/modello-plugins/modello-plugin-xsd/src/main/java/org/codehaus/modello/plugin/xsd/XsdGenerator.java +++ b/modello-plugins/modello-plugin-xsd/src/main/java/org/codehaus/modello/plugin/xsd/XsdGenerator.java @@ -324,7 +324,7 @@ private void writeComplexTypeDescriptor( writeListElement(w, xmlFieldMetadata, xmlAssociationMetadata, field, getXsdType("String")); } else if (Properties.class.getName().equals(field.getType()) || "DOM".equals(field.getType())) { - writePropertiesElement(w); + writePropertiesElement(w, "DOM".equals(field.getType())); } else { throw new IllegalStateException("Non-association field of a non-primitive type '" + field.getType() + "' for '" + field.getName() + "' in '" @@ -408,7 +408,16 @@ private static void writeCharElement(XMLWriter w) { w.endElement(); } - private static void writePropertiesElement(XMLWriter w) { + /** + * Writes the complexType of a field holding free-form XML content: either a {@code java.util.Properties} or + * a {@code DOM} field. + * + * @param w the writer + * @param dom whether the field is a {@code DOM} field, in which case arbitrary attributes are allowed on the + * element itself, so that DOM merge hints such as {@code combine.self} or {@code combine.children} + * validate. A {@code Properties} element carries no attributes, so none are allowed there. + */ + private static void writePropertiesElement(XMLWriter w, boolean dom) { w.startElement("xs:complexType"); w.startElement("xs:sequence"); @@ -422,6 +431,13 @@ private static void writePropertiesElement(XMLWriter w) { w.endElement(); + if (dom) { + w.startElement("xs:anyAttribute"); + w.addAttribute("processContents", "skip"); + + w.endElement(); + } + w.endElement(); } diff --git a/modello-plugins/modello-plugin-xsd/src/test/java/org/codehaus/modello/plugin/xsd/DomXsdGeneratorTest.java b/modello-plugins/modello-plugin-xsd/src/test/java/org/codehaus/modello/plugin/xsd/DomXsdGeneratorTest.java new file mode 100644 index 000000000..4b82b9932 --- /dev/null +++ b/modello-plugins/modello-plugin-xsd/src/test/java/org/codehaus/modello/plugin/xsd/DomXsdGeneratorTest.java @@ -0,0 +1,88 @@ +package org.codehaus.modello.plugin.xsd; + +/* + * Copyright (c) 2005, Codehaus.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import javax.inject.Inject; +import javax.xml.XMLConstants; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; + +import java.io.File; +import java.util.Map; + +import org.codehaus.modello.AbstractModelloGeneratorTest; +import org.codehaus.modello.ModelloException; +import org.codehaus.modello.core.ModelloCore; +import org.codehaus.modello.model.Model; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.Test; +import org.xml.sax.SAXParseException; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * Checks that a DOM field accepts arbitrary attributes on its element, so that DOM merge hints such as + * {@code combine.self} validate, while a Properties field does not. + * + * @see issue 482 + */ +@PlexusTest +public class DomXsdGeneratorTest extends AbstractModelloGeneratorTest { + @Inject + private ModelloCore modello; + + public DomXsdGeneratorTest() { + super("xsd-dom"); + } + + @Test + public void testDomAttributesAreAllowed() throws Throwable { + Model model = modello.loadModel(getXmlResourceReader("/dom.mdo")); + + Map parameters = getModelloParameters("1.0.0"); + + modello.generate(model, "xsd", parameters); + + SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = sf.newSchema(new StreamSource(new File(getOutputDirectory(), "dom-1.0.0.xsd"))); + Validator validator = schema.newValidator(); + + try { + validator.validate(new StreamSource(getClass().getResourceAsStream("/dom.xml"))); + } catch (SAXParseException e) { + throw new ModelloException("line " + e.getLineNumber() + " column " + e.getColumnNumber(), e); + } + + try { + validator.validate( + new StreamSource(getClass().getResourceAsStream("/dom-invalid-properties-attribute.xml"))); + fail("attributes should not be allowed on a Properties element"); + } catch (SAXParseException e) { + // ok, expected exception + assertTrue(e.getMessage().contains("combine.self")); + } + } +} diff --git a/modello-plugins/modello-plugin-xsd/src/test/resources/dom-invalid-properties-attribute.xml b/modello-plugins/modello-plugin-xsd/src/test/resources/dom-invalid-properties-attribute.xml new file mode 100644 index 000000000..e66057197 --- /dev/null +++ b/modello-plugins/modello-plugin-xsd/src/test/resources/dom-invalid-properties-attribute.xml @@ -0,0 +1,8 @@ + + + + + someValue + + diff --git a/modello-plugins/modello-plugin-xsd/src/test/resources/dom.mdo b/modello-plugins/modello-plugin-xsd/src/test/resources/dom.mdo new file mode 100644 index 000000000..cddb19a83 --- /dev/null +++ b/modello-plugins/modello-plugin-xsd/src/test/resources/dom.mdo @@ -0,0 +1,42 @@ + + + dom + Dom + + Model exercising the XSD generated for DOM and Properties fields. + + + + package + org.codehaus.modello.test.dom + + + + + DomDemo + 1.0.0+ + + + configuration + 1.0.0+ + DOM + Free-form XML, carrying arbitrary attributes such as combine.self. + + + properties + 1.0.0+ + Properties + + String + * + + Free-form XML elements, but no attributes on the container. + + + + + diff --git a/modello-plugins/modello-plugin-xsd/src/test/resources/dom.xml b/modello-plugins/modello-plugin-xsd/src/test/resources/dom.xml new file mode 100644 index 000000000..f8462ab77 --- /dev/null +++ b/modello-plugins/modello-plugin-xsd/src/test/resources/dom.xml @@ -0,0 +1,11 @@ + + + + + + + + someValue + +