Skip to content

Create schema specific to the version to validate#3801

Open
samirromdhani wants to merge 12 commits intomainfrom
feat/2818-create-schema-specific-to-the-version-to-validate
Open

Create schema specific to the version to validate#3801
samirromdhani wants to merge 12 commits intomainfrom
feat/2818-create-schema-specific-to-the-version-to-validate

Conversation

@samirromdhani
Copy link
Copy Markdown
Contributor

@samirromdhani samirromdhani commented Mar 2, 2026

Please check if the PR fulfills these requirements

  • The commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
  • A PR or issue has been opened in all impacted repositories (if any)

Does this PR already have an issue describing the problem?

Fixes #2818

What kind of change does this PR introduce?

What is the current behavior?

What is the new behavior (if this is a feature change)?

Does this PR introduce a breaking change or deprecate an API?

  • Yes
  • No

If yes, please check if the following requirements are fulfilled

  • The Breaking Change or Deprecated label has been added
  • The migration steps are described in the following section

What changes might users need to make in their application due to this PR? (migration steps)

Other information:

  • Some extension XSD files reference IIDM XSD files from other versions (xs:import + schemaLocation), which can impact schema resolution . To handle this, extractSchemaLocations and getSupportedExtensionsByIIdmVersion were added.
  • To make version validation strict, we verify the root namespace first, then run XSD validation, this is handled by readRootNamespace and checkNamespace

@samirromdhani samirromdhani force-pushed the feat/2818-create-schema-specific-to-the-version-to-validate branch 3 times, most recently from 33132c3 to 65f0051 Compare March 3, 2026 16:10
@samirromdhani samirromdhani self-assigned this Mar 3, 2026
@samirromdhani samirromdhani changed the title WIP Create schema specific to the version to validate Create schema specific to the version to validate Mar 3, 2026
@samirromdhani samirromdhani force-pushed the feat/2818-create-schema-specific-to-the-version-to-validate branch from c76619e to a2cfa27 Compare March 4, 2026 10:03
@samirromdhani samirromdhani marked this pull request as ready for review March 4, 2026 10:29
@samirromdhani samirromdhani marked this pull request as draft March 4, 2026 10:52
@samirromdhani samirromdhani marked this pull request as ready for review March 4, 2026 10:57
@samirromdhani samirromdhani force-pushed the feat/2818-create-schema-specific-to-the-version-to-validate branch from ccfe442 to b356226 Compare March 4, 2026 12:32
@samirromdhani samirromdhani requested review from olperr1 and rolnico March 4, 2026 12:33
@samirromdhani samirromdhani force-pushed the feat/2818-create-schema-specific-to-the-version-to-validate branch from b356226 to fe0aa21 Compare March 4, 2026 12:46
@samirromdhani samirromdhani marked this pull request as draft March 5, 2026 08:23
@samirromdhani samirromdhani force-pushed the feat/2818-create-schema-specific-to-the-version-to-validate branch 2 times, most recently from fdea43a to 0180fc2 Compare March 5, 2026 16:14
@samirromdhani samirromdhani marked this pull request as ready for review March 5, 2026 16:15
@samirromdhani samirromdhani marked this pull request as draft March 6, 2026 08:27
@samirromdhani samirromdhani force-pushed the feat/2818-create-schema-specific-to-the-version-to-validate branch 11 times, most recently from f4d0545 to e44b19b Compare March 9, 2026 10:45
@samirromdhani samirromdhani force-pushed the feat/2818-create-schema-specific-to-the-version-to-validate branch from e44b19b to 59d72cb Compare March 9, 2026 13:35
@samirromdhani samirromdhani force-pushed the feat/2818-create-schema-specific-to-the-version-to-validate branch from 59d72cb to 5dce9ad Compare March 9, 2026 14:06
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>

validate specific version WIP

Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
…eation in XmlUtil

Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>

review: centralize SchemaFactory creation in XmlUtil

Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
…schema cache

Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>

fixes sonar
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
@samirromdhani samirromdhani force-pushed the feat/2818-create-schema-specific-to-the-version-to-validate branch from 5dce9ad to c4dafdb Compare March 9, 2026 14:21
@samirromdhani samirromdhani marked this pull request as ready for review March 9, 2026 14:24
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Mar 9, 2026

Comment on lines +187 to +191
try {
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
} catch (SAXException e) {
LOGGER.info("- Property unsupported by SchemaFactory implementation: {}", XMLConstants.ACCESS_EXTERNAL_SCHEMA);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can factorise this code in a small utility method used for the two properties

return initializeWriter(indent, indentString, xmlWriter);
}

public static SchemaFactory newSchemaFactory() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to createSchemaFactoryInstance to mirror createXMLInputFactoryInstance

}
}

public static void validate(InputStream is, IidmVersion version, ExtensionsSupplier extensionsSupplier) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add Javadoc for this method to explain its specificities.

Also, add a warning that it loads the full network file in memory.

Comment on lines +266 to +270
String actualNs = readRootNamespace(xmlBytes);
boolean matches = actualNs.equals(validationVersion.getNamespaceURI())
|| validationVersion.supportEquipmentValidationLevel() && actualNs.equals(validationVersion.getNamespaceURI(false));
if (!matches) {
throw new PowsyblException("Namespace mismatch: expected validation version " + validationVersion.toString(".") + ", found namespace " + actualNs);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
String actualNs = readRootNamespace(xmlBytes);
boolean matches = actualNs.equals(validationVersion.getNamespaceURI())
|| validationVersion.supportEquipmentValidationLevel() && actualNs.equals(validationVersion.getNamespaceURI(false));
if (!matches) {
throw new PowsyblException("Namespace mismatch: expected validation version " + validationVersion.toString(".") + ", found namespace " + actualNs);
String actualNamespace = readRootNamespace(xmlBytes);
boolean matches = actualNamespace.equals(validationVersion.getNamespaceURI())
|| validationVersion.supportEquipmentValidationLevel() && actualNamespace.equals(validationVersion.getNamespaceURI(false));
if (!matches) {
throw new PowsyblException("Namespace mismatch: expected validation version " + validationVersion.toString(".") + ", found namespace " + actualNamespace);

Comment on lines +277 to +284
* <p>XSD document snippet:</p>
* <pre>{@code
* ...
* targetNamespace="http://www.powsybl.org/schema/iidm/ext/extension-name/1_0"
* xmlns:iidm="http://www.powsybl.org/schema/iidm/1_10">
* <xs:import namespace="http://www.powsybl.org/schema/iidm/1_10" schemaLocation="iidm_V1_10.xsd"/>
* </xs:schema>
* }</pre>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea to illustrate with a small example!
Could you do the same with the other methods (when possible and useful)?

byte[] xmlBytes;
try {
xmlBytes = is.readAllBytes();
checkNamespace(xmlBytes, version);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does could be outside the try/catch as it does not throw an IOException

Comment on lines +184 to +190
// XSD validation
Schema schema;
if (extensionsSupplier == DefaultExtensionsSupplier.getInstance()) {
schema = DEFAULT_SCHEMAS_SUPPLIER.get().computeIfAbsent(version, v -> createSchema(DefaultExtensionsSupplier.getInstance(), v));
} else {
schema = createSchema(extensionsSupplier, version);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// XSD validation
Schema schema;
if (extensionsSupplier == DefaultExtensionsSupplier.getInstance()) {
schema = DEFAULT_SCHEMAS_SUPPLIER.get().computeIfAbsent(version, v -> createSchema(DefaultExtensionsSupplier.getInstance(), v));
} else {
schema = createSchema(extensionsSupplier, version);
}
// XSD validation
Schema schema = extensionsSupplier == DefaultExtensionsSupplier.getInstance() ?
DEFAULT_SCHEMAS_SUPPLIER.get().computeIfAbsent(version, v -> createSchema(DefaultExtensionsSupplier.getInstance(), v)) :
createSchema(extensionsSupplier, version);

}
}

private static Schema createSchema(ExtensionsSupplier extensionsSupplier, IidmVersion version) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method could be factorized with the existing one.
You might want to keep working on arrays instead of lists for efficiency (to be tested if possible)

byte[] xmlBytes;
try {
xmlBytes = is.readAllBytes();
checkNamespace(xmlBytes, version);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this check even necessary? There might be a better way to do this, avoiding loading the whole file in memory (which you are doing by creating the array of bytes).

For example something like this:

    public static void validate(InputStream is, IidmVersion version, ExtensionsSupplier extensionsSupplier) {
        Objects.requireNonNull(is);
        Objects.requireNonNull(version);
        Objects.requireNonNull(extensionsSupplier);

        // XSD validation
        Schema schema = extensionsSupplier == DefaultExtensionsSupplier.getInstance() ?
            DEFAULT_SCHEMAS_SUPPLIER.get().computeIfAbsent(version, v -> createSchema(DefaultExtensionsSupplier.getInstance(), v)) :
            createSchema(extensionsSupplier, version);
        try {
            XMLFilter xmlFilter = getXMLFilter(version);
            SAXSource saxSource = new SAXSource(xmlFilter, new InputSource(is));
            schema.newValidator().validate(saxSource);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        } catch (SAXException e) {
            throw new UncheckedSaxException(e);
        } catch (ParserConfigurationException e) {
            throw new UncheckedParserConfigurationException(e);
        }
    }

    private static XMLFilter getXMLFilter(IidmVersion validationVersion) throws ParserConfigurationException, SAXException {
        XMLFilter filter = new XMLFilterImpl() {
            private boolean rootSeen = false;

            @Override
            public void startElement(String uri, String localName, String qName, Attributes atts)
                throws SAXException {
                if (!rootSeen) {
                    checkNamespace(uri, validationVersion);
                    rootSeen = true;
                }
                super.startElement(uri, localName, qName, atts);
            }
        };

        XMLReader xmlReader = SAXParserFactory.newNSInstance().newSAXParser().getXMLReader();
        filter.setParent(xmlReader);
        return filter;
    }

    private static void checkNamespace(String actualNamespace, IidmVersion validationVersion) {
        boolean matches = actualNamespace.equals(validationVersion.getNamespaceURI())
            || validationVersion.supportEquipmentValidationLevel() && actualNamespace.equals(validationVersion.getNamespaceURI(false));
        if (!matches) {
            throw new PowsyblException("Namespace mismatch: expected validation version " + validationVersion.toString(".") + ", found namespace " + actualNamespace);
        }
    }

Note: this is an example, this has to be checked/improved/validated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create schema specific to the version to validate

2 participants