Skip to content

xml: extend RemoveEmptyXmlTags with XPath whitelist, file matcher, and file deletion#7834

Open
Jenson3210 wants to merge 2 commits into
mainfrom
Jenson3210/remove-empty-xml-tags
Open

xml: extend RemoveEmptyXmlTags with XPath whitelist, file matcher, and file deletion#7834
Jenson3210 wants to merge 2 commits into
mainfrom
Jenson3210/remove-empty-xml-tags

Conversation

@Jenson3210
Copy link
Copy Markdown
Contributor

@Jenson3210 Jenson3210 commented May 29, 2026

Summary

  • Extends org.openrewrite.xml.RemoveEmptyXmlTags with three optional, JSON-deserializable options so it can serve as the generic cleanup recipe described in moderneinc/customer-requests#2460 §11:

  • xPaths — whitelist of XPath expressions identifying which empty tags are eligible for removal. Attribute predicates are reinterpreted as an attribute allowlist: a candidate tag matches only when its attribute set is a subset of the union of attribute names appearing in the matched predicates. @* is the wildcard, @name enumerates a single allowed name, and @a or @b allows either. So /server only matches attribute-free <server> tags, while /server[@*] matches any, /server[@description] matches no-attribute or description-only servers, and /server[@description or @other] matches when attributes are a subset of {description, other}.

  • fileMatcher — glob restricting which files the recipe touches (wired via Preconditions.check(new FindSourceFiles(...), …)).

  • deleteFileIfEmpty — when the root tag itself would be a removal candidate, delete the entire source file. Defaults to true.

The visitor is wrapped in Repeat.repeatUntilStable(..., 50) so nested empties cascade and trigger file deletion in a single recipe run. The original no-arg constructor is preserved via Lombok @NoArgsConstructor(force = true), and the explicit all-args constructor is @JsonCreator-annotated. Existing callers and tests are unaffected — the defaults reproduce the prior behavior on documents whose root retains content.

Motivating example (Liberty mp-telemetry.xml cleanup once another recipe strips <feature>mpTelemetry-2.0</feature>):

new RemoveEmptyXmlTags(
    List.of("/server/featureManager", "/server[@*]"),
    "**/mp-telemetry.xml",
    true)

collapses the now-empty <featureManager>, then <server> (the [@*] opts in to its description attribute), then deletes the file.

Test plan

  • ./gradlew :rewrite-xml:test --tests "org.openrewrite.xml.RemoveEmptyXmlTagsTest" — 14 tests pass (3 original + 11 new covering file deletion, attribute-allowlist semantics, fileMatcher scoping, and multi-level nested collapse).
  • ./gradlew :rewrite-xml:recipeCsvGenerate regenerates rewrite-xml/.../recipes.csv; recipeCsvValidateContent passes.
  • ./gradlew :rewrite-xml:licenseMain :rewrite-xml:licenseTest pass.

…and file deletion

Adds three optional, JSON-deserializable options to `RemoveEmptyXmlTags`:
- `xPaths` — whitelist of XPath expressions; attribute predicates act as
  an attribute allowlist (`@*` wildcard, `@a`/`@a or @b` enumerated names),
  so a tag matches only when its attribute set is a subset of the allowed
  names. Empty/null preserves the existing "remove empty no-attribute tag"
  behavior.
- `fileMatcher` — restrict the recipe to matching files.
- `deleteFileIfEmpty` — when the root tag would itself be removed, delete
  the source file. Defaults to true.

The visitor runs inside `Repeat.repeatUntilStable` so nested empties
cascade and trigger file deletion in a single recipe run. The original
no-arg constructor is preserved via Lombok `@NoArgsConstructor(force = true)`
so existing callers and tests are unaffected.
Comment thread rewrite-xml/src/main/java/org/openrewrite/xml/RemoveEmptyXmlTags.java Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants