From ab549559a7357459a16ec9c6ef2cf81ce0d18ec8 Mon Sep 17 00:00:00 2001 From: Rawvoid Date: Wed, 4 Feb 2026 14:24:01 +0800 Subject: [PATCH] feat(normalize-class): remove empty derived classes and update superclass references --- .../jaxb/plugin/NormalizeClassPlugin.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/NormalizeClassPlugin.java b/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/NormalizeClassPlugin.java index bc703cd..ad2936d 100644 --- a/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/NormalizeClassPlugin.java +++ b/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/NormalizeClassPlugin.java @@ -36,8 +36,23 @@ @Option(name = "Xnormalize-class", description = "Normalize generated classes") public class NormalizeClassPlugin extends AbstractPlugin { + private static final String FIELD_MODS = "mods"; + + private static final java.lang.reflect.Field JMODS_MODS_FIELD = getField(JMods.class, FIELD_MODS); + + private static java.lang.reflect.Field getField(Class type, String name) { + try { + var field = type.getDeclaredField(name); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException ex) { + throw new IllegalStateException("Failed to access field '" + name + "' on " + type.getName(), ex); + } + } + @Override public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException { + removeEmptyDerivedClasses(outline); outline.getAllPackageContexts().forEach(packageOutline -> { removeDuplicateClasses(packageOutline); }); @@ -47,6 +62,45 @@ public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) thro return true; } + private void removeEmptyDerivedClasses(Outline outline) { + var classOutlines = new ArrayList<>(outline.getClasses()); + classOutlines.forEach(classOutline -> { + var implClass = classOutline.implClass; + var superClass = implClass.superClass(); + if (!(superClass instanceof JDefinedClass definedSuperClass)) { + return; + } + if (!isEmptyClass(implClass)) { + return; + } + + JaxbClassRefactorUtil.removeFromParentContainer(implClass); + JaxbClassRefactorUtil.removeFromObjectFactory(implClass); + + outline.getClasses().forEach(c -> + JaxbClassRefactorUtil.replaceClassReferences(c.implClass, implClass, definedSuperClass)); + + if (definedSuperClass.isAbstract()) { + clearAbstractModifier(definedSuperClass.mods()); + } + }); + } + + private boolean isEmptyClass(JDefinedClass definedClass) { + return definedClass.fields().isEmpty() + && definedClass.methods().isEmpty() + && !definedClass.classes().hasNext(); + } + + private void clearAbstractModifier(JMods mods) { + try { + var flags = (int) JMODS_MODS_FIELD.get(mods); + JMODS_MODS_FIELD.set(mods, flags & ~JMod.ABSTRACT); + } catch (IllegalAccessException ex) { + throw new IllegalStateException("Failed to clear abstract modifier", ex); + } + } + private void removeDuplicateClasses(PackageOutline packageOutline) { var classOutlines = packageOutline.getClasses(); var classOutlinesGroup = classOutlines.stream()