diff --git a/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/DedupeClassPlugin.java b/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/DedupeClassPlugin.java index fca2b9c..f0139b8 100644 --- a/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/DedupeClassPlugin.java +++ b/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/DedupeClassPlugin.java @@ -93,6 +93,12 @@ * Shell-to-shell exact merges remain allowed. Default is auto: on when * {@code -Xelement-wrapper} is also active; force with {@code true}/{@code false}. *
+ *+ * Logging: each accepted merge is {@code DEBUG}; merge and element-class-clear counts are + * {@code INFO}. Routine skips stay {@code DEBUG}; element-class root mismatches and + * ObjectFactory collisions after dedupe are {@code WARN} (ObjectFactory: one multi-line + * summary). + *
* * @author Rawvoid */ @@ -869,7 +875,11 @@ public void postProcessModel(Model model, ErrorHandler errorHandler) { warnObjectFactoryCollisions(model); } if (merged > 0) { - log.info("Deduped {} bean merge(s){}", merged, session.dry ? " (dry-run)" : ""); + if (session.dry) { + log.info("Deduped {} bean merge(s) (dry-run)", merged); + } else { + log.info("Deduped {} bean merge(s)", merged); + } } } @@ -1019,7 +1029,7 @@ private boolean tryMerge( if (!isPackageLevel(host) && host.parent() != victim.parent() && !allowCrossNestedParent) { log.debug( - "Skip dedupe {}: cross-hierarchy nested '{}' vs '{}'", + "Skip dedupe {}: cross-hierarchy nested {} vs {}", reason, victim.fullName(), host.fullName() ); return false; @@ -1028,11 +1038,11 @@ private boolean tryMerge( if (victim.isElement() && host.isElement() && !Objects.equals(victim.getElementName(), host.getElementName())) { log.warn( - "Skip dedupe {}: both '{}' and '{}' are element-classes with different roots ({} vs {})", + "Skip dedupe {}: both element-classes with different roots: {} ({}) vs {} ({})", reason, victim.fullName(), - host.fullName(), victim.getElementName(), + host.fullName(), host.getElementName() ); return false; @@ -1044,7 +1054,7 @@ private boolean tryMerge( && ModelUtils.isPureCollectionShell(victim) && !ModelUtils.isPureCollectionShell(host)) { log.debug( - "Skip dedupe {}: pure collection shell '{}' must not merge into non-shell '{}'", + "Skip dedupe {}: pure collection shell {} must not merge into non-shell {}", reason, victim.fullName(), host.fullName() ); return false; @@ -1057,8 +1067,8 @@ private boolean tryMerge( return false; } - log.info( - "Dedupe {}: '{}' -> '{}' (nameKey={})", + log.debug( + "Dedupe {}: {} → {} (nameKey={})", reason, victim.fullName(), host.fullName(), nameKey(victim.shortName) ); @@ -1102,7 +1112,11 @@ private boolean prepareNestedMerges(Model model, Session session, CClassInfo vic var childNorm = child.shortName.toLowerCase(Locale.ROOT); for (var hc : directNestedBeans(model, host)) { if (hc.shortName.toLowerCase(Locale.ROOT).equals(childNorm)) { - log.debug("Skip dedupe: nested name clash {} under {}", child.shortName, host.fullName()); + log.debug( + "Skip dedupe: nested name clash {} under {}", + child.shortName, + host.fullName() + ); return false; } } @@ -1165,9 +1179,10 @@ private boolean prepareNestedEnums(Model model, Session session, CClassInfo vict } } if (match != null) { - log.info( - "Dedupe exact-enum: '{}' -> '{}'", - fullEnumName(victimEnum), fullEnumName(match) + log.debug( + "Dedupe exact-enum: {} → {}", + fullEnumName(victimEnum), + fullEnumName(match) ); session.countedMerges.add(IdentityPair.directed(victimEnum, match)); if (!session.dry) { @@ -1298,7 +1313,7 @@ private static void collapseRedundantElementClasses(Model model, Set+ * Logging: each flattened field and the removed-wrapper name list are {@code DEBUG}; the + * flatten count and removed-wrapper count are {@code INFO}. Skipped wrapper removals and + * stale annotation owners (plugin-order issues) are one multi-line {@code WARN} each. + * Per-field flatten failures stay single-line {@code WARN}. + *
* * @author Rawvoid */ @@ -136,22 +142,25 @@ public void postProcessModel(Model model, ErrorHandler errorHandler) { @Override public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) throws SAXException { + var skipped = new ArrayList+ * Logging: each successful split is {@code DEBUG}; the property count is {@code INFO}. + * Failed {@code addProperty} / empty replacement cases are single-line {@code WARN}. + *
* * @author Rawvoid */ @@ -112,7 +116,7 @@ public void postProcessModel(Model model, ErrorHandler errorHandler) { flattened += handleClass(bean); } if (flattened > 0) { - log.info("Flattened {} multi-element property(ies) into individual fields", flattened); + log.info("Flattened {} multi-element property(ies)", flattened); } } @@ -137,12 +141,20 @@ private int handleClass(CClassInfo bean) { continue; } + var originalName = prop.getName(false); // Replace the original property at position i with all the new ones. - if (replaceProperty(bean, i, prop, replacements)) { + var addedNames = replaceProperty(bean, i, prop, replacements); + if (!addedNames.isEmpty()) { // After replacement, i now points at the first new property. // Advance past all inserted properties so the loop continues correctly. - i += replacements.size() - 1; + i += addedNames.size() - 1; count++; + log.debug( + "Flattened {}.{} → {}", + bean.fullName(), + originalName, + addedNames + ); } } return count; @@ -360,8 +372,10 @@ private static String resolveJavadoc(QName elementName, CElement element, CClass * new property. The appended properties are then moved from the tail back to * the original position. * + * + * @return short names of properties that were actually appended (empty if none) */ - private boolean replaceProperty( + private List+ * Logging: each successful one-level lift is {@code DEBUG}; the hop count is + * {@code INFO}. ObjectFactory collision rollbacks stay {@code DEBUG}. Name-slot + * contention is silent (types simply stop). + *
* * @author Rawvoid */ @@ -129,6 +134,11 @@ private static String normalize(String name) { return name.toLowerCase(Locale.ROOT); } + private static String parentLabel(CClassInfoParent parent) { + var label = parent.fullName(); + return label == null || label.isEmpty() ? "(default package)" : label; + } + /** * Nesting is defined on the model; changing parents here is enough for * BeanGenerator to emit the right containers. {@link #run} is a no-op. @@ -145,7 +155,7 @@ public void postProcessModel(Model model, ErrorHandler errorHandler) { } if (hops > 0) { // Counts one-level moves, not distinct types (a deep type may hop several times). - log.info("Promoted {} nested type placement(s) (beans and enums)", hops); + log.info("Promoted {} nested type placement(s)", hops); } } @@ -213,17 +223,33 @@ private int promoteOneLevel(Model model) { for (var move : shortNameOk) { if (move.bean != null) { var previous = move.bean.parent(); + // fullName still reflects the old parent until re-parent succeeds. + var typeName = move.bean.fullName(); setFieldValue(CCLASSINFO_PARENT_FIELD, move.bean, move.target); if (ModelUtils.hasObjectFactorySqueezedCollision(model)) { setFieldValue(CCLASSINFO_PARENT_FIELD, move.bean, previous); log.debug( - "Skip promoting {} — ObjectFactory squeezed name collision", - move.bean.fullName() + "Skip promote {}: ObjectFactory name collision", + typeName ); continue; } + log.debug( + "Promoted {} ({} → {})", + typeName, + parentLabel(previous), + parentLabel(move.target) + ); } else { + var previous = move.enumInfo.parent; + var typeName = move.enumInfo.fullName(); setFieldValue(CENUMLEAFINFO_PARENT_FIELD, move.enumInfo, move.target); + log.debug( + "Promoted {} ({} → {})", + typeName, + parentLabel(previous), + parentLabel(move.target) + ); } moved++; } diff --git a/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/RemoveUnusedClassPlugin.java b/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/RemoveUnusedClassPlugin.java index 84d5daa..2d87325 100644 --- a/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/RemoveUnusedClassPlugin.java +++ b/plugins/src/main/java/io/github/rawvoid/jaxb/plugin/RemoveUnusedClassPlugin.java @@ -32,9 +32,14 @@ /** * XJC plugin that removes unreferenced JAXB classes and enums from the {@link Model} * during {@link #postProcessModel(Model, ErrorHandler)}. - * - *Uses a graph reachability analysis (Mark & Sweep) starting from global XML root elements - * and user-configured white-list patterns to identify and prune unreachable classes and enums.
+ *+ * Uses a graph reachability analysis (Mark & Sweep) starting from global XML root elements + * and user-configured white-list patterns to identify and prune unreachable classes and enums. + *
+ *+ * Logging: the removed count is {@code INFO}; the class/enum name list is {@code DEBUG}. + * When nothing is removed, only a {@code DEBUG} line is emitted. + *
* * @author Rawvoid */ @@ -49,9 +54,6 @@ public class RemoveUnusedClassPlugin extends OptionPlugin { @Option(name = "preserve-polymorphism", defaultValue = "false", description = "Whether to treat subclasses of a reachable base class as reachable (default: false)") Boolean preservePolymorphism = false; - @Option(name = "verbose", defaultValue = "false", description = "Enable detailed logging of reachability and deleted classes (default: false)") - Boolean verbose = false; - @Override public boolean run(Outline outline, Options opt, ErrorHandler errorHandler) { return true; @@ -86,30 +88,45 @@ public void postProcessModel(Model model, ErrorHandler errorHandler) { .toList(); if (deadClasses.isEmpty() && deadEnums.isEmpty()) { - if (Boolean.TRUE.equals(verbose)) { - log.info("[Xremove-unused-class] No unreferenced classes or enums found."); - } + log.debug("No unreferenced classes or enums found"); return; } // 4. Remove dead classes + var removedClasses = new ArrayList* Conflict policy. Conflicting types keep their original names; non-conflicting - * renames still apply. Conflicts are reported as warnings (build does not fail). Checks cover: + * renames still apply. Conflicts are reported as a single non-fatal warning with one detail + * line per clash ({@code old→new}). Checks cover: *
*+ * Logging: each applied rename is {@code DEBUG}; the total count is {@code INFO}. Conflicts + * are one {@code WARN} (summary + detail lines). Invalid mapping/strip results are {@code WARN}. + *
+ *
* When both this plugin and {@link PromoteNestedClassPlugin} are active, running rename
* before promote lets named global types claim short names first; promote-first is
* safer for some ObjectFactory edge cases but can leave dual names ({@code Foo} + {@code FooType}).
@@ -136,7 +141,6 @@ private static List
* Field renames do not affect {@code ObjectFactory} (which keys on class squeezed names).
*
+ * Logging: each renamed property is {@code DEBUG}; the rename count is {@code INFO}.
+ * Invalid {@code -name} values are a single {@code WARN} before falling back to {@code items}.
+ *