From bc897362b368cf13567897e11ffac60a09b7f2fa Mon Sep 17 00:00:00 2001 From: william dutton Date: Wed, 15 Apr 2026 15:33:36 +1000 Subject: [PATCH 1/2] #691 Introduce mockito dependancy for testing --- jaxb-plugins-parent/jaxb-plugins/pom.xml | 5 +++++ pom.xml | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/jaxb-plugins-parent/jaxb-plugins/pom.xml b/jaxb-plugins-parent/jaxb-plugins/pom.xml index 153e9323b..010d9f284 100644 --- a/jaxb-plugins-parent/jaxb-plugins/pom.xml +++ b/jaxb-plugins-parent/jaxb-plugins/pom.xml @@ -37,6 +37,11 @@ jaxb-maven-plugin-testing test + + org.mockito + mockito-core + test + diff --git a/pom.xml b/pom.xml index 2420b96b5..22624c0ae 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ 3.0 4.1.1.Final 5.11.0 + 5.23.0 1.7.36 1.8.3 1.2.0 @@ -387,6 +388,11 @@ hamcrest ${hamcrest.version} + + org.mockito + mockito-core + ${mockito-core.version} + org.slf4j From 9d42c3364838002e6f822460f787582f3b1b8051 Mon Sep 17 00:00:00 2001 From: william dutton Date: Wed, 15 Apr 2026 16:21:16 +1000 Subject: [PATCH 2/2] Changes: Updated documentation Introduce equals to allow deep recursive equals checks vs by reference (default): -Xcommons-lang:equalsTestTransients=TRUE|FALSE (default: FALSE) -Xcommons-lang:equalsTestRecursive=TRUE|FALSE (default: FALSE) Enable ability to disable parts of commons-lang to allow mixing and matching of plugins -Xcommons-lang:addToStringMethod=TRUE|FALSE (default: TRUE) -Xcommons-lang:addHashCodeMethod=TRUE|FALSE (default: TRUE) -Xcommons-lang:addEqualsMethod=TRUE|FALSE (default: TRUE) Streamlined -Xcommons-lang:ToStringStyle to look prefer Static Final variables that passes regex ^[A-Z0-9_]+$ else treat it as a Class, don't error if it can't find it, since it may not be visible to the generator (But do provide warnings of such cases). Also allow classes to be passed through even if NOT available to generator so plugin dependancies do not need to include additional jar's unless the developer wants to remove the warning. Requres: #692 for unit testing to pass. --- .../commons_lang/XjcCommonsLangPlugin.java | 222 +++++++++--- .../XjcCommonsLangPluginTest.java | 315 ++++++++++++++++++ .../tests/commons_lang_custom_class/pom.xml | 47 +++ .../java/com/example/CustomToStringStyle.java | 33 ++ .../src/main/resources/Person.xsd | 28 ++ .../tests/commons_lang/AddressTest.java | 22 ++ .../tests/commons_lang/PersonTest.java | 21 ++ .../src/test/resources/log4j.properties | 5 + .../tests/commons_lang_custom_equals/pom.xml | 51 +++ .../java/com/example/CustomToStringStyle.java | 33 ++ .../src/main/resources/Person.xsd | 28 ++ .../tests/commons_lang/AddressTest.java | 22 ++ .../tests/commons_lang/PersonTest.java | 21 ++ .../src/test/resources/log4j.properties | 5 + jaxb-plugins-parent/tests/pom.xml | 2 + 15 files changed, 810 insertions(+), 45 deletions(-) create mode 100644 jaxb-plugins-parent/jaxb-plugins/src/test/java/org/jvnet/jaxb/plugin/commons_lang/XjcCommonsLangPluginTest.java create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_class/pom.xml create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_class/src/main/java/com/example/CustomToStringStyle.java create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_class/src/main/resources/Person.xsd create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/AddressTest.java create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/PersonTest.java create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/resources/log4j.properties create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_equals/pom.xml create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_equals/src/main/java/com/example/CustomToStringStyle.java create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_equals/src/main/resources/Person.xsd create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/AddressTest.java create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/PersonTest.java create mode 100644 jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/resources/log4j.properties diff --git a/jaxb-plugins-parent/jaxb-plugins/src/main/java/org/jvnet/jaxb/plugin/commons_lang/XjcCommonsLangPlugin.java b/jaxb-plugins-parent/jaxb-plugins/src/main/java/org/jvnet/jaxb/plugin/commons_lang/XjcCommonsLangPlugin.java index 1af1ba23d..83ed7ab97 100644 --- a/jaxb-plugins-parent/jaxb-plugins/src/main/java/org/jvnet/jaxb/plugin/commons_lang/XjcCommonsLangPlugin.java +++ b/jaxb-plugins-parent/jaxb-plugins/src/main/java/org/jvnet/jaxb/plugin/commons_lang/XjcCommonsLangPlugin.java @@ -15,6 +15,8 @@ */ package org.jvnet.jaxb.plugin.commons_lang; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.xml.sax.ErrorHandler; import com.sun.codemodel.JCodeModel; @@ -30,9 +32,12 @@ import com.sun.tools.xjc.outline.ClassOutline; import com.sun.tools.xjc.outline.Outline; +import static com.sun.codemodel.JExpr._null; +import static com.sun.codemodel.JExpr.lit; + /** * Automatically generates the toString(), hashCode() and equals() methods - * using Jakarta's commons-lang. + * using org.apache.commons:commons-lang3. * * Supports the optional ToStringStyle command line parameter to specify * the style for use within the toString method. @@ -50,11 +55,11 @@ * Example 2: * * -Xcommons-lang - * -Xcommons-lang:ToStringStyle=my.CustomToStringStyle + * -Xcommons-lang:ToStringStyle=my.CustomToStringStyleClass * * to specify the use of * - * my.CustomToStringStyle, which must be a subclass of + * my.CustomToStringStyleClass, which must be a subclass of * * org.apache.commons.lang3.builder.ToStringStyle, and contains a public no-arg constructor. * @@ -62,17 +67,53 @@ * * The default ToStringStyle adopted by this plugin is MULTI_LINE_STYLE. * + * Equals options: + *
+ * -Xcommons-lang:equalsTestTransients=TRUE|FALSE (default: FALSE)
+ * -Xcommons-lang:equalsTestRecursive=TRUE|FALSE (default: FALSE)
+ * 
+ *

NOTE:
+ * When enabled equals passing when sub objects where last object POJO returns by ref check only but contents is equals + *

+ * Performance Cost: Deep reflection is significantly slower than standard equality checks. + *

+ * + * To disable one of the generated plugins if you wish to use another module use one of the following: + * + *
+ *  -Xcommons-lang:addToStringMethod=TRUE|FALSE (default: TRUE)
+ *  -Xcommons-lang:addHashCodeMethod=TRUE|FALSE (default: TRUE)
+ *  -Xcommons-lang:addEqualsMethod=TRUE|FALSE (default: TRUE)
+ * 
+ * * @author Hanson Char + * @author William Dutton (disable methods) */ public class XjcCommonsLangPlugin extends Plugin { private static final String TOSTRING_STYLE_PARAM = "-Xcommons-lang:ToStringStyle="; + private static final String TOSTRING_DISABLED_PARAM = "-Xcommons-lang:addToStringMethod="; + private static final String HASH_CODE_DISABLED_PARAM = "-Xcommons-lang:addHashCodeMethod="; + private static final String EQUALS_DISABLED_PARAM = "-Xcommons-lang:addEqualsMethod="; + private static final String EQUALS_TEST_TRANSIENTS_PARAM = "-Xcommons-lang:equalsTestTransients="; + private static final String EQUALS_TEST_RECURSIVE_PARAM = "-Xcommons-lang:equalsTestRecursive="; + + //Classes private static final String TOSTRINGSTYLE_CLASSNAME = "org.apache.commons.lang3.builder.ToStringStyle"; private static final String EQUALSBUILDER_CLASSNAME = "org.apache.commons.lang3.builder.EqualsBuilder"; private static final String HASHCODEBUILDER_CLASSNAME = "org.apache.commons.lang3.builder.HashCodeBuilder"; private static final String TOSTRINGBUILDER_CLASSNAME = "org.apache.commons.lang3.builder.ToStringBuilder"; - private String toStringStyle = "MULTI_LINE_STYLE"; - private Class customToStringStyle; + + protected Log logger = LogFactory.getLog(getClass()); + + private String toStringStyle = "MULTI_LINE_STYLE"; //Default Style + private String toStringClass = null; + + private boolean toStringEnabled = true; + private boolean equalsEnabled = true; + private boolean hashCodeEnabled = true; + private boolean equalsTestTransients = false; + private boolean equalsTestRecursive = false; @Override public String getOptionName() @@ -83,13 +124,30 @@ public String getOptionName() @Override public String getUsage() { - return " -Xcommons-lang : generate toString(), hashCode() and equals() for generated code using Jakarta's common-lang\n" - + " [-Xcommons-lang:ToStringStyle=MULTI_LINE_STYLE\n\t" - + "| DEFAULT_STYLE\n\t" - + "| NO_FIELD_NAMES_STYLE\n\t" - + "| SHORT_PREFIX_STYLE\n\t" - + "| SIMPLE_STYLE\n\t" - + "| ]\n" + return " -Xcommons-lang : generate toString(), hashCode() and equals() for generated code using Jakarta's common-lang " + + "[\n" + + "\t -Xcommons-lang:ToStringStyle=MULTI_LINE_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=DEFAULT_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=NO_FIELD_NAMES_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=SHORT_PREFIX_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=SIMPLE_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=NO_CLASS_NAME_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=JSON_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=$\n" + + "\t| -Xcommons-lang:ToStringStyle=\n" + + "]\n" + + " Note: custom ToStringStyle class is needed if you wish to turn off setUseIdentityHashCode on top of MULTI_LINE_STYLE" + + "\n" + + " Equals options:\n" + + " -Xcommons-lang:equalsTestTransients=TRUE|FALSE (default: FALSE)\n" + + " -Xcommons-lang:equalsTestRecursive=TRUE|FALSE (default: FALSE)\n" + + " NOTE: \n" + + " Performance Cost: When equalsTestRecursive is TRUE, due to Deep reflection is significantly slower than standard equality checks.\n" + + "\n" + + " To disable one of the generated plugins if you wish to use another module use one of the following:\n" + + " -Xcommons-lang:addToStringMethod=FALSE\n" + + " -Xcommons-lang:addHashCodeMethod=FALSE\n" + + " -Xcommons-lang:addEqualsMethod=FALSE\n" ; } @@ -108,51 +166,76 @@ public boolean run(Outline outline, return true; } - private void createToStringMethod(JDefinedClass implClass) - { + private void createToStringMethod(JDefinedClass implClass) { + if (!toStringEnabled) { + return; + } JCodeModel codeModel = implClass.owner(); JMethod toStringMethod = implClass.method(JMod.PUBLIC, codeModel.ref(String.class), "toString"); // Annotate with @Override toStringMethod.annotate(Override.class); - final JExpression toStringStyleExpr = - customToStringStyle == null - ? codeModel.ref(TOSTRINGSTYLE_CLASSNAME) - .staticRef(toStringStyle) - : JExpr._new( - codeModel.ref(customToStringStyle)) - ; + + //Build body of method + final JExpression toStringStyleExpr; + if (toStringClass == null) { + // Call Static Reference i.e. ToStringStyle.JSON_STYLE; + toStringStyleExpr = codeModel.ref(TOSTRINGSTYLE_CLASSNAME).staticRef(toStringStyle); + } else { + //Direct class passed and calls new on it; + toStringStyleExpr = JExpr._new(codeModel.ref(toStringClass)); + } + // Invoke ToStringBuilder.reflectionToString(Object,StringStyle) toStringMethod.body() - ._return( - codeModel.ref(TOSTRINGBUILDER_CLASSNAME) - .staticInvoke("reflectionToString") - .arg(JExpr._this()) - .arg(toStringStyleExpr) - ); - return; + ._return( + codeModel.ref(TOSTRINGBUILDER_CLASSNAME) + .staticInvoke("reflectionToString") + .arg(JExpr._this()) + .arg(toStringStyleExpr) + ); } private void createEqualsMethod(JDefinedClass implClass) { + if (!equalsEnabled) { + return; + } JCodeModel codeModel = implClass.owner(); JMethod toStringMethod = implClass.method(JMod.PUBLIC, codeModel.BOOLEAN, "equals"); JVar that = toStringMethod.param(Object.class, "that"); // Annotate with @Override toStringMethod.annotate(Override.class); - // Invoke EqualsBuilder.reflectionEquals(Object,Object); - toStringMethod.body()._return( - codeModel.ref(EQUALSBUILDER_CLASSNAME) - .staticInvoke("reflectionEquals") - .arg(JExpr._this()) - .arg(that) - ); - return; + if (equalsTestTransients || equalsTestRecursive) { + //Since: 3.6 + toStringMethod.body()._return( + codeModel.ref(EQUALSBUILDER_CLASSNAME) + .staticInvoke("reflectionEquals") // public static boolean reflectionEquals( + .arg(JExpr._this()) //final Object lhs, + .arg(that) // final Object rhs, + .arg(lit(equalsTestTransients)) // final boolean testTransients, + .arg(_null()) // final Class reflectUpToClass, + .arg(lit(equalsTestRecursive)) // final boolean testRecursive, + .arg(_null()) // final String... excludeFields) { + ); + } else { + // Invoke EqualsBuilder.reflectionEquals(Object,Object); + toStringMethod.body()._return( + codeModel.ref(EQUALSBUILDER_CLASSNAME) + .staticInvoke("reflectionEquals") + .arg(JExpr._this()) + .arg(that) + + ); + } } private void createHashCodeMethod(JDefinedClass implClass) { + if (!hashCodeEnabled) { + return; + } JCodeModel codeModel = implClass.owner(); JMethod toStringMethod = implClass.method(JMod.PUBLIC, codeModel.INT, "hashCode"); @@ -171,26 +254,75 @@ private void createHashCodeMethod(JDefinedClass implClass) public int parseArgument(Options opt, String[] args, int i) throws BadCommandLineException { - // eg. -Xcommons-lang ToStringStyle=SIMPLE_STYLE + // eg. + // -Xcommons-lang:ToStringStyle=SIMPLE_STYLE + // or + // -Xcommons-lang:ToStringStyle= String arg = args[i].trim(); if (arg.startsWith(TOSTRING_STYLE_PARAM)) { - toStringStyle = arg.substring(TOSTRING_STYLE_PARAM.length()); - try { - Class.forName(TOSTRINGSTYLE_CLASSNAME).getField(toStringStyle); + String value = arg.substring(TOSTRING_STYLE_PARAM.length()); + + if (value.matches("^[A-Z0-9_]+$")) { + try { + Class.forName(TOSTRINGSTYLE_CLASSNAME).getField(value); + } catch (NoSuchFieldException | ClassNotFoundException | SecurityException e) { + logger.error("Could not verify : '" + TOSTRINGSTYLE_CLASSNAME + "." + value + "'. Assume it's not visible to generator.", e); + } + toStringStyle = value; return 1; - } catch (ClassNotFoundException | SecurityException e) { - throw new BadCommandLineException(e.getMessage()); - } catch (NoSuchFieldException ignore) { } + try { - customToStringStyle = Class.forName(toStringStyle); + Class.forName(value); } catch (ClassNotFoundException e) { - throw new BadCommandLineException(e.getMessage()); + logger.warn("Could not find class: '" + value + "'. Assume it's not visible to generator."); } + toStringClass = value; return 1; } + + // eg. -Xcommons-lang:addToStringMethod=TRUE + if (arg.startsWith(TOSTRING_DISABLED_PARAM)) + { + String toStringBoolean = arg.substring(TOSTRING_DISABLED_PARAM.length()); + toStringEnabled = Boolean.parseBoolean(toStringBoolean); + return 1; + } + + // eg. -Xcommons-lang:addEqualsMethod=TRUE + if (arg.startsWith(EQUALS_DISABLED_PARAM)) + { + String toStringBoolean = arg.substring(EQUALS_DISABLED_PARAM.length()); + equalsEnabled = Boolean.parseBoolean(toStringBoolean); + return 1; + } + + // eg. -Xcommons-lang:addHashCodeMethod=TRUE + if (arg.startsWith(HASH_CODE_DISABLED_PARAM)) + { + String toStringBoolean = arg.substring(HASH_CODE_DISABLED_PARAM.length()); + hashCodeEnabled = Boolean.parseBoolean(toStringBoolean); + return 1; + } + + // eg. -Xcommons-lang:equalsTestTransients=TRUE + if (arg.startsWith(EQUALS_TEST_TRANSIENTS_PARAM)) + { + String toStringBoolean = arg.substring(EQUALS_TEST_TRANSIENTS_PARAM.length()); + equalsTestTransients = Boolean.parseBoolean(toStringBoolean); + return 1; + } + + // eg. -Xcommons-lang:equalsTestRecursive=TRUE + if (arg.startsWith(EQUALS_TEST_RECURSIVE_PARAM)) + { + String toStringBoolean = arg.substring(EQUALS_TEST_RECURSIVE_PARAM.length()); + equalsTestRecursive = Boolean.parseBoolean(toStringBoolean); + return 1; + } + return 0; } } diff --git a/jaxb-plugins-parent/jaxb-plugins/src/test/java/org/jvnet/jaxb/plugin/commons_lang/XjcCommonsLangPluginTest.java b/jaxb-plugins-parent/jaxb-plugins/src/test/java/org/jvnet/jaxb/plugin/commons_lang/XjcCommonsLangPluginTest.java new file mode 100644 index 000000000..7c5457031 --- /dev/null +++ b/jaxb-plugins-parent/jaxb-plugins/src/test/java/org/jvnet/jaxb/plugin/commons_lang/XjcCommonsLangPluginTest.java @@ -0,0 +1,315 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jvnet.jaxb.plugin.commons_lang; + +import com.sun.codemodel.*; +import com.sun.tools.xjc.BadCommandLineException; +import com.sun.tools.xjc.Options; +import com.sun.tools.xjc.model.CClassInfo; +import com.sun.tools.xjc.outline.ClassOutline; +import com.sun.tools.xjc.outline.Outline; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.xml.sax.ErrorHandler; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +class XjcCommonsLangPluginTest { + + @Mock + private Outline outline; + @Mock + private Options options; + @Mock + private ErrorHandler errorHandler; + @Mock + private CClassInfo cClassInfo; + + private JDefinedClass implClass; + private XjcCommonsLangPlugin plugin; + + @BeforeEach + void setUp() throws Exception { + MockitoAnnotations.openMocks(this); + + JCodeModel codeModel = new JCodeModel(); + implClass = codeModel._class("TestGeneratedClass"); + + ClassOutline classOutline = new TestableClassOutline(cClassInfo, implClass); + when(outline.getClasses()).thenReturn((Collection) Collections.singletonList(classOutline)); + + plugin = new XjcCommonsLangPlugin(); + } + + @Test + void testGetOptionName() { + assertEquals("Xcommons-lang", plugin.getOptionName()); + } + + @Test + void testGetUsage() { + String usage = plugin.getUsage(); + assertNotNull(usage); + assertEquals(" -Xcommons-lang : generate toString(), hashCode() and equals() for generated code using Jakarta's common-lang [\n" + + "\t -Xcommons-lang:ToStringStyle=MULTI_LINE_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=DEFAULT_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=NO_FIELD_NAMES_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=SHORT_PREFIX_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=SIMPLE_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=NO_CLASS_NAME_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=JSON_STYLE\n" + + "\t| -Xcommons-lang:ToStringStyle=$\n" + + "\t| -Xcommons-lang:ToStringStyle=\n" + + "]\n" + + " Note: custom ToStringStyle class is needed if you wish to turn off setUseIdentityHashCode on top of MULTI_LINE_STYLE\n" + + " Equals options:\n" + + " -Xcommons-lang:equalsTestTransients=TRUE|FALSE (default: FALSE)\n" + + " -Xcommons-lang:equalsTestRecursive=TRUE|FALSE (default: FALSE)\n" + + " NOTE: \n" + + " Performance Cost: When equalsTestRecursive is TRUE, due to Deep reflection is significantly slower than standard equality checks.\n" + + "\n" + + " To disable one of the generated plugins if you wish to use another module use one of the following:\n" + + " -Xcommons-lang:addToStringMethod=FALSE\n" + + " -Xcommons-lang:addHashCodeMethod=FALSE\n" + + " -Xcommons-lang:addEqualsMethod=FALSE\n", usage); + } + + @Test + void testRunGeneratesAllMethods() throws Exception { + plugin.run(outline, options, errorHandler); + + assertTrue(hasMethod(implClass, "toString"), "toString() should be generated"); + assertTrue(hasMethod(implClass, "equals"), "equals() should be generated"); + assertTrue(hasMethod(implClass, "hashCode"), "hashCode() should be generated"); + } + + @Test + void testRunSkipsMethodsWhenDisabled() throws Exception { + // Set flags to disable generation + plugin.parseArgument(options, new String[]{"-Xcommons-lang:addToStringMethod=FALSE"}, 0); + plugin.parseArgument(options, new String[]{"-Xcommons-lang:addEqualsMethod=FALSE"}, 0); + plugin.parseArgument(options, new String[]{"-Xcommons-lang:addHashCodeMethod=FALSE"}, 0); + + plugin.run(outline, options, errorHandler); + + // Verify that the methods were NOT added to the real object + assertFalse(hasMethod(implClass, "toString"), "toString() should not be generated"); + assertFalse(hasMethod(implClass, "equals"), "equals() should not be generated"); + assertFalse(hasMethod(implClass, "hashCode"), "hashCode() should not be generated"); + } + + + @Test + void testOnlyToStringGenerated() throws Exception { + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addToStringMethod=TRUE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addEqualsMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addHashCodeMethod=FALSE"}, 0)); + + plugin.run(outline, options, errorHandler); + + assertTrue(hasMethod(implClass, "toString"), "toString() should be generated"); + assertFalse(hasMethod(implClass, "equals"), "equals() should be generated"); + assertFalse(hasMethod(implClass, "hashCode"), "hashCode() should be generated"); + } + + @Test + void testOnlyEqualsGenerated() throws Exception { + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addToStringMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addEqualsMethod=TRUE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addHashCodeMethod=FALSE"}, 0)); + + plugin.run(outline, options, errorHandler); + + assertFalse(hasMethod(implClass, "toString"), "toString() should be generated"); + assertTrue(hasMethod(implClass, "equals"), "equals() should be generated"); + JMethod method = getMethod(implClass, "equals").get(); + assertEquals(1, method.listParams().length); + assertEquals("that", Arrays.stream(method.listParams()).findFirst().get().name()); + assertEquals("java.lang.Object", Arrays.stream(method.listParams()).findFirst().get().type().fullName()); + assertFalse(hasMethod(implClass, "hashCode"), "hashCode() should be generated"); + } + + @Test + void testOnlyEqualsGeneratedWithTestTransients() throws Exception { + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addToStringMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addEqualsMethod=TRUE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addHashCodeMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:equalsTestTransients=TRUE"}, 0)); + + plugin.run(outline, options, errorHandler); + + assertFalse(hasMethod(implClass, "toString"), "toString() should be generated"); + assertTrue(hasMethod(implClass, "equals"), "equals() should be generated"); + assertFalse(hasMethod(implClass, "hashCode"), "hashCode() should be generated"); + } + + @Test + void testOnlyEqualsGeneratedWithRecursive() throws Exception { + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addToStringMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addEqualsMethod=TRUE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addHashCodeMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:equalsTestRecursive=TRUE"}, 0)); + + plugin.run(outline, options, errorHandler); + + assertFalse(hasMethod(implClass, "toString"), "toString() should be generated"); + assertTrue(hasMethod(implClass, "equals"), "equals() should be generated"); + assertFalse(hasMethod(implClass, "hashCode"), "hashCode() should be generated"); + } + + @Test + void testOnlyEqualsGeneratedWithTransiantAndRecursive() throws Exception { + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addToStringMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addEqualsMethod=TRUE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addHashCodeMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:equalsTestTransients=TRUE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:equalsTestRecursive=TRUE"}, 0)); + + plugin.run(outline, options, errorHandler); + + assertFalse(hasMethod(implClass, "toString"), "toString() should be generated"); + assertTrue(hasMethod(implClass, "equals"), "equals() should be generated"); + JMethod method = getMethod(implClass, "equals").get(); + assertEquals(1, method.listParams().length); + assertEquals("that", Arrays.stream(method.listParams()).findFirst().get().name()); + assertEquals("java.lang.Object", Arrays.stream(method.listParams()).findFirst().get().type().fullName()); + //Unsure how to verify args inside since post jdk8 its now in a class that does hides its classes externally. + assertFalse(hasMethod(implClass, "hashCode"), "hashCode() should be generated"); + } + + @Test + void testOnlyHashCodeGenerated() throws Exception { + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addToStringMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addEqualsMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addHashCodeMethod=TRUE"}, 0)); + + plugin.run(outline, options, errorHandler); + + assertFalse(hasMethod(implClass, "toString"), "toString() should not be generated"); + assertFalse(hasMethod(implClass, "equals"), "equals() should not be generated"); + assertTrue(hasMethod(implClass, "hashCode"), "hashCode() should be generated"); + } + + + @Test + void testParseArgumentToStringStyleStandard() throws BadCommandLineException { + String arg = "-Xcommons-lang:ToStringStyle=SIMPLE_STYLE"; + assertEquals(1, plugin.parseArgument(options, new String[]{arg}, 0)); + + plugin.run(outline, options, errorHandler); + + assertTrue(hasMethod(implClass, "toString"), "toString() should be generated"); + } + + @Test + void testParseArgumentToStringStyleCustom() throws BadCommandLineException { + String arg = "-Xcommons-lang:ToStringStyle=java.lang.String"; + assertEquals(1, plugin.parseArgument(options, new String[]{arg}, 0)); + + plugin.run(outline, options, errorHandler); + + assertTrue(hasMethod(implClass, "toString"), "toString() should be generated"); + } + + @Test + void testParseArgumentToStringStyleToClassGeneratorCantSeet() throws BadCommandLineException { + String arg = "-Xcommons-lang:ToStringStyle=com.non.existent.Style"; + assertEquals(1, plugin.parseArgument(options, new String[]{arg}, 0)); + + plugin.run(outline, options, errorHandler); + + assertTrue(hasMethod(implClass, "toString"), "toString() should be generated"); + } + + @Test + void testParseArgumentDisablingFlags() throws BadCommandLineException { + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addToStringMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addEqualsMethod=FALSE"}, 0)); + assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:addHashCodeMethod=FALSE"}, 0)); + + plugin.run(outline, options, errorHandler); + + assertFalse(hasMethod(implClass, "toString"), "toString() should not be generated"); + assertFalse(hasMethod(implClass, "equals"), "equals() should not be generated"); + assertFalse(hasMethod(implClass, "hashCode"), "hashCode() should not be generated"); + } + + @Test + void testParseArgumentUnknown() throws BadCommandLineException { + assertEquals(0, plugin.parseArgument(options, new String[]{"-Xunknown-param"}, 0)); + + plugin.run(outline, options, errorHandler); + + assertTrue(hasMethod(implClass, "toString"), "toString() should be generated"); + assertTrue(hasMethod(implClass, "equals"), "equals() should be generated"); + assertTrue(hasMethod(implClass, "hashCode"), "hashCode() should be generated"); + } + + @Test + void testParseArgumentToStringStyleInvalidClassError() throws BadCommandLineException { + String arg = "-Xcommons-lang:ToStringStyle=NO_FINAL_STATIC_EXISTS_FOR_THIS"; + assertEquals(1, plugin.parseArgument(options, new String[]{arg}, 0)); + + plugin.run(outline, options, errorHandler); + + assertTrue(hasMethod(implClass, "toString"), "toString() should be generated"); + } + + + @Test + void testCreateToStringMethodWithCustomClassBranch() throws Exception { + String arg = "-Xcommons-lang:ToStringStyle=java.lang.String"; + plugin.parseArgument(options, new String[]{arg}, 0); + + assertTrue(plugin.run(outline, options, errorHandler)); + + assertTrue(hasMethod(implClass, "toString"), "toString() should be generated using custom class branch"); + } + + private boolean hasMethod(JDefinedClass clazz, String methodName) { + for (JMethod m : clazz.methods()) { + if (m.name().equals(methodName)) { + return true; + } + } + return false; + } + + private Optional getMethod(JDefinedClass clazz, String methodName) { + return clazz.methods().stream() + .filter(anno -> anno.name().equals(methodName)) + .findFirst(); + } + + public static class TestableClassOutline extends ClassOutline { + public TestableClassOutline(CClassInfo target, JDefinedClass implClass) { + super(target, null, null, implClass); + } + + @Override + public Outline parent() { + return null; + } + } +} diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_class/pom.xml b/jaxb-plugins-parent/tests/commons_lang_custom_class/pom.xml new file mode 100644 index 000000000..d03d063be --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_class/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + + org.jvnet.jaxb + jaxb-plugins-tests + 4.1.0-SNAPSHOT + ../pom.xml + + jaxb-plugins-test-commons_lang_custom_class + jar + JAXB Tools :: JAXB Plugins :: Test [commons_lang_custom_class] + + + org.jvnet.jaxb + jaxb-maven-plugin-testing + test + + + org.apache.commons + commons-lang3 + + + + test + + + org.jvnet.jaxb + jaxb-maven-plugin + + true + + -Xcommons-lang + -Xcommons-lang:ToStringStyle=com.example.CustomToStringStyle + + + + org.jvnet.jaxb + jaxb-plugins + + + + + + + diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_class/src/main/java/com/example/CustomToStringStyle.java b/jaxb-plugins-parent/tests/commons_lang_custom_class/src/main/java/com/example/CustomToStringStyle.java new file mode 100644 index 000000000..cd04dd3b7 --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_class/src/main/java/com/example/CustomToStringStyle.java @@ -0,0 +1,33 @@ +package com.example; + +import org.apache.commons.lang3.builder.ToStringStyle; + +public class CustomToStringStyle extends ToStringStyle { + + public static final ToStringStyle CUSTOM_TO_STRING_STYLE = new CustomToStringStyle(); + + + private static final long serialVersionUID = 1L; + + /** + * Constructs a new instance. + * + *

Use the static constant rather than instantiating.

+ */ + public CustomToStringStyle() { + setUseIdentityHashCode(false); + setContentStart("["); + setFieldSeparator(System.lineSeparator() + " "); + setFieldSeparatorAtStart(true); + setContentEnd(System.lineSeparator() + "]"); + } + + /** + * Ensure Singleton after serialization. + * @return the singleton + */ + private Object readResolve() { + return CUSTOM_TO_STRING_STYLE; + } + +} diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_class/src/main/resources/Person.xsd b/jaxb-plugins-parent/tests/commons_lang_custom_class/src/main/resources/Person.xsd new file mode 100644 index 000000000..1ee143788 --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_class/src/main/resources/Person.xsd @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/AddressTest.java b/jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/AddressTest.java new file mode 100644 index 000000000..3fa2776af --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/AddressTest.java @@ -0,0 +1,22 @@ +package org.jvnet.jaxb2_commons.tests.commons_lang; + +import com.example.CustomToStringStyle; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import generated.Address; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AddressTest { + + @Test + public void testAddress() { + Address a = new Address(); + // No plugin default-value present, checking everything is null or default java value + Assertions.assertEquals(0, a.getNumber()); + Assertions.assertNull(a.getCareOf()); + Assertions.assertNull(a.getStreet()); + + Assertions.assertEquals(ToStringBuilder.reflectionToString(a, new CustomToStringStyle()), a.toString()); + } +} diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/PersonTest.java b/jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/PersonTest.java new file mode 100644 index 000000000..be06f8bd9 --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/PersonTest.java @@ -0,0 +1,21 @@ +package org.jvnet.jaxb2_commons.tests.commons_lang; + +import com.example.CustomToStringStyle; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import generated.Person; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PersonTest { + + @Test + public void testPerson() { + Person p = new Person(); + // No plugin default-value present, checking everything is null or default java value + Assertions.assertEquals(false, p.isMailingAddressIdentical()); + + Assertions.assertEquals(ToStringBuilder.reflectionToString(p, new CustomToStringStyle()), p.toString()); + } +} diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/resources/log4j.properties b/jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/resources/log4j.properties new file mode 100644 index 000000000..ca4ee5e2c --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_class/src/test/resources/log4j.properties @@ -0,0 +1,5 @@ +log4j.rootCategory=DEBUG, stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.target=system.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_equals/pom.xml b/jaxb-plugins-parent/tests/commons_lang_custom_equals/pom.xml new file mode 100644 index 000000000..1c1fca8f9 --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_equals/pom.xml @@ -0,0 +1,51 @@ + + 4.0.0 + + org.jvnet.jaxb + jaxb-plugins-tests + 4.1.0-SNAPSHOT + ../pom.xml + + jaxb-plugins-test-commons_lang_custom_equals + jar + JAXB Tools :: JAXB Plugins :: Test [commons_lang_custom_equals] + + + org.jvnet.jaxb + jaxb-maven-plugin-testing + test + + + org.apache.commons + commons-lang3 + + + + test + + + org.jvnet.jaxb + jaxb-maven-plugin + + true + + -Xcommons-lang + -Xcommons-lang:addToStringMethod=FALSE + -Xcommons-lang:addHashCodeMethod=FALSE + -Xcommons-lang:addEqualsMethod=TRUE + -Xcommons-lang:equalsTestTransients=FALSE + -Xcommons-lang:equalsTestRecursive=TRUE + + + + org.jvnet.jaxb + jaxb-plugins + + + + + + + diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/main/java/com/example/CustomToStringStyle.java b/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/main/java/com/example/CustomToStringStyle.java new file mode 100644 index 000000000..cd04dd3b7 --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/main/java/com/example/CustomToStringStyle.java @@ -0,0 +1,33 @@ +package com.example; + +import org.apache.commons.lang3.builder.ToStringStyle; + +public class CustomToStringStyle extends ToStringStyle { + + public static final ToStringStyle CUSTOM_TO_STRING_STYLE = new CustomToStringStyle(); + + + private static final long serialVersionUID = 1L; + + /** + * Constructs a new instance. + * + *

Use the static constant rather than instantiating.

+ */ + public CustomToStringStyle() { + setUseIdentityHashCode(false); + setContentStart("["); + setFieldSeparator(System.lineSeparator() + " "); + setFieldSeparatorAtStart(true); + setContentEnd(System.lineSeparator() + "]"); + } + + /** + * Ensure Singleton after serialization. + * @return the singleton + */ + private Object readResolve() { + return CUSTOM_TO_STRING_STYLE; + } + +} diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/main/resources/Person.xsd b/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/main/resources/Person.xsd new file mode 100644 index 000000000..1ee143788 --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/main/resources/Person.xsd @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/AddressTest.java b/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/AddressTest.java new file mode 100644 index 000000000..489d4f6f6 --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/AddressTest.java @@ -0,0 +1,22 @@ +package org.jvnet.jaxb2_commons.tests.commons_lang; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import generated.Address; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AddressTest { + + @Test + public void testAddress() { + Address a = new Address(); + // No plugin default-value present, checking everything is null or default java value + Assertions.assertEquals(0, a.getNumber()); + Assertions.assertNull(a.getCareOf()); + Assertions.assertNull(a.getStreet()); + + Assertions.assertEquals(a, a); + } +} diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/PersonTest.java b/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/PersonTest.java new file mode 100644 index 000000000..f0a4bf952 --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/java/org/jvnet/jaxb2_commons/tests/commons_lang/PersonTest.java @@ -0,0 +1,21 @@ +package org.jvnet.jaxb2_commons.tests.commons_lang; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import generated.Person; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PersonTest { + + @Test + public void testPerson() { + Person p = new Person(); + Person p2 = new Person(); + // No plugin default-value present, checking everything is null or default java value + Assertions.assertEquals(false, p.isMailingAddressIdentical()); + Assertions.assertEquals(p, p2); + + } +} diff --git a/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/resources/log4j.properties b/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/resources/log4j.properties new file mode 100644 index 000000000..ca4ee5e2c --- /dev/null +++ b/jaxb-plugins-parent/tests/commons_lang_custom_equals/src/test/resources/log4j.properties @@ -0,0 +1,5 @@ +log4j.rootCategory=DEBUG, stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.target=system.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n diff --git a/jaxb-plugins-parent/tests/pom.xml b/jaxb-plugins-parent/tests/pom.xml index 41b95f7cf..0871afbd2 100644 --- a/jaxb-plugins-parent/tests/pom.xml +++ b/jaxb-plugins-parent/tests/pom.xml @@ -19,6 +19,8 @@ camelcase commons_lang commons_lang_custom + commons_lang_custom_class + commons_lang_custom_equals copyable defaultvalue elementwrapper