Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/maven-build-action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
branches: [ master, jaxb-tools-4.0.x, jaxb-tools-3.x, jaxb-tools-2.x, 0.15.x ]
pull_request:
branches: [ master, jaxb-tools-4.0.x, jaxb-tools-3.x, jaxb-tools-2.x, 0.15.x ]
workflow_dispatch:
## Allow's manual runs

jobs:
build_and_test:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
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.
Expand Down Expand Up @@ -62,18 +65,36 @@
*
* The default ToStringStyle adopted by this plugin is MULTI_LINE_STYLE.
*
* Equals options:
* <pre>
* -Xcommons-lang:equalsTestTransients=TRUE|FALSE (default: FALSE)
* -Xcommons-lang:equalsTestRecursive=TRUE|FALSE (default: FALSE)
* </pre>
* <p>NOTE:
* When enabled equals passing when sub objects where last object POJO returns by ref check only but contents is equals
* </p><p>
* Performance Cost: Deep reflection is significantly slower than standard equality checks.
* </p>
*
* @author Hanson Char
*/
public class XjcCommonsLangPlugin extends Plugin
{
private static final String TOSTRING_STYLE_PARAM = "-Xcommons-lang:ToStringStyle=";
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;

private boolean equalsTestTransients = false;
private boolean equalsTestRecursive = false;

@Override
public String getOptionName()
{
Expand All @@ -90,6 +111,13 @@ public String getUsage()
+ "| SHORT_PREFIX_STYLE\n\t"
+ "| SIMPLE_STYLE\n\t"
+ "| <Fully qualified class name of a ToStringStyle subtype>]\n"
+ "\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"
;
}

Expand Down Expand Up @@ -141,14 +169,27 @@ private void createEqualsMethod(JDefinedClass implClass)
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)
Expand Down Expand Up @@ -191,6 +232,22 @@ public int parseArgument(Options opt, String[] args, int i)
}
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
* 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" +
" [-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| <Fully qualified class name of a ToStringStyle subtype>]\n" +
"\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", 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 testOnlyEqualsGeneratedWithTestTransients() throws Exception {
assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:equalsTestTransients=TRUE"}, 0));

plugin.run(outline, options, errorHandler);


assertTrue(hasMethod(implClass, "equals"), "equals() should be generated");

}

@Test
void testOnlyEqualsGeneratedWithRecursive() throws Exception {
assertEquals(1, plugin.parseArgument(options, new String[]{"-Xcommons-lang:equalsTestRecursive=TRUE"}, 0));

plugin.run(outline, options, errorHandler);

assertTrue(hasMethod(implClass, "equals"), "equals() should be generated");

}

@Test
void testOnlyEqualsGeneratedWithTransiantAndRecursive() throws Exception {
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);

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.
}

@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 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 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<JMethod> 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;
}
}
}
48 changes: 48 additions & 0 deletions jaxb-plugins-parent/tests/commons_lang_custom_equals/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jvnet.jaxb</groupId>
<artifactId>jaxb-plugins-tests</artifactId>
<version>4.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>jaxb-plugins-test-commons_lang_custom_equals</artifactId>
<packaging>jar</packaging>
<name>JAXB Tools :: JAXB Plugins :: Test [commons_lang_custom_equals]</name>
<dependencies>
<dependency>
<groupId>org.jvnet.jaxb</groupId>
<artifactId>jaxb-maven-plugin-testing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<defaultGoal>test</defaultGoal>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb</groupId>
<artifactId>jaxb-maven-plugin</artifactId>
<configuration>
<extension>true</extension>
<args>
<arg>-Xcommons-lang</arg>
<arg>-Xcommons-lang:equalsTestTransients=FALSE</arg>
<arg>-Xcommons-lang:equalsTestRecursive=TRUE</arg>
</args>
<plugins>
<plugin>
<groupId>org.jvnet.jaxb</groupId>
<artifactId>jaxb-plugins</artifactId>
</plugin>
</plugins>
</configuration>
</plugin>
</plugins>
</build>
</project>
Loading
Loading