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/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 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