├── pojomatic-test-utils ├── .gitignore ├── src │ ├── test │ │ └── java │ │ │ └── org │ │ │ └── pojomatic │ │ │ ├── junit │ │ │ └── PojomaticAssertTest.java │ │ │ ├── testng │ │ │ └── PojomaticAssertTest.java │ │ │ └── test │ │ │ ├── Container.java │ │ │ ├── DifferentPojo.java │ │ │ ├── OnlyPojomaticEqual.java │ │ │ ├── AssertUtilsTest.java │ │ │ └── AssertTest.java │ ├── site │ │ ├── site.xml │ │ └── xdoc │ │ │ ├── index.xml │ │ │ └── changes.xml │ └── main │ │ ├── javadoc │ │ └── overview.html │ │ └── java │ │ └── org │ │ └── pojomatic │ │ ├── junit │ │ └── PojomaticAssert.java │ │ ├── testng │ │ └── PojomaticAssert.java │ │ └── test │ │ └── AssertUtils.java └── pom.xml ├── pojomatic ├── .gitignore ├── src │ ├── main │ │ └── java │ │ │ ├── org │ │ │ └── pojomatic │ │ │ │ ├── package-info.java │ │ │ │ ├── internal │ │ │ │ ├── PropertyRole.java │ │ │ │ ├── ClassDefiner.java │ │ │ │ ├── package-info.java │ │ │ │ ├── LookupClassDefiner.java │ │ │ │ ├── PropertyField.java │ │ │ │ ├── PojomatorStub.java │ │ │ │ ├── ClassDefinerFactory.java │ │ │ │ ├── ClassLoaderClassDefiner.java │ │ │ │ ├── Primitives.java │ │ │ │ ├── PropertyFilter.java │ │ │ │ ├── AbstractPropertyElement.java │ │ │ │ ├── EnhancedPojoFormatterWrapper.java │ │ │ │ ├── PropertyAccessor.java │ │ │ │ ├── LocalVariable.java │ │ │ │ ├── SelfPopulatingMap.java │ │ │ │ ├── EnhancedPropertyFormatterWrapper.java │ │ │ │ ├── PojomatorFactory.java │ │ │ │ ├── OverridableMethods.java │ │ │ │ └── PropertyClassVisitor.java │ │ │ │ ├── annotations │ │ │ │ ├── package-info.java │ │ │ │ ├── SubclassCannotOverrideEquals.java │ │ │ │ ├── OverridesEquals.java │ │ │ │ ├── AutoDetectPolicy.java │ │ │ │ ├── PojoFormat.java │ │ │ │ ├── SkipArrayCheck.java │ │ │ │ ├── PropertyFormat.java │ │ │ │ ├── AutoProperty.java │ │ │ │ ├── Property.java │ │ │ │ ├── DefaultPojomaticPolicy.java │ │ │ │ └── PojomaticPolicy.java │ │ │ │ ├── diff │ │ │ │ ├── package-info.java │ │ │ │ ├── NoDifferences.java │ │ │ │ ├── Differences.java │ │ │ │ ├── Difference.java │ │ │ │ ├── ValueDifference.java │ │ │ │ └── PropertyDifferences.java │ │ │ │ ├── formatter │ │ │ │ ├── package-info.java │ │ │ │ ├── PropertyFormatter.java │ │ │ │ ├── DefaultPojoFormatter.java │ │ │ │ ├── AccountNumberFormatter.java │ │ │ │ ├── DefaultEnhancedPojoFormatter.java │ │ │ │ ├── EnhancedPojoFormatter.java │ │ │ │ ├── PojoFormatter.java │ │ │ │ ├── DefaultPropertyFormatter.java │ │ │ │ └── EnhancedPropertyFormatter.java │ │ │ │ ├── NoPojomaticPropertiesException.java │ │ │ │ └── PropertyElement.java │ │ │ └── module-info.java │ ├── test │ │ └── java │ │ │ ├── examples │ │ │ ├── PersonMain.java │ │ │ ├── GetterAccess.java │ │ │ ├── Interface.java │ │ │ ├── Implementation.java │ │ │ ├── IpAddressFormatter.java │ │ │ ├── Person.java │ │ │ ├── Employee.java │ │ │ ├── IpAddressFormatterTest.java │ │ │ ├── Manual.java │ │ │ ├── Common.java │ │ │ ├── Customer.java │ │ │ └── Auto.java │ │ │ └── org │ │ │ └── pojomatic │ │ │ ├── internal │ │ │ ├── NestParent.java │ │ │ ├── factory │ │ │ │ ├── Access.java │ │ │ │ ├── PropertyDescriptor.java │ │ │ │ ├── PojoFactoryTest.java │ │ │ │ ├── PojoDescriptor.java │ │ │ │ └── PojoFactory.java │ │ │ ├── a │ │ │ │ ├── C1.java │ │ │ │ └── C3.java │ │ │ ├── b │ │ │ │ ├── C2.java │ │ │ │ └── C4.java │ │ │ ├── RegexMatcher.java │ │ │ ├── PropertyAccessorTest.java │ │ │ ├── Type.java │ │ │ ├── ClassOnlyClassLoader.java │ │ │ ├── PropertyFilterTest.java │ │ │ ├── BaseType.java │ │ │ ├── SelfPopulatingMapTest.java │ │ │ ├── TypeProviders.java │ │ │ ├── EqualsInheritanceTest.java │ │ │ ├── EnhancedPojoFormatterWrapperTest.java │ │ │ ├── ArrayType.java │ │ │ ├── PropertyElementTest.java │ │ │ └── OverridableMethodsTest.java │ │ │ ├── TestUtils.java │ │ │ ├── diff │ │ │ ├── NoDifferencesTest.java │ │ │ ├── ValueDifferenceTest.java │ │ │ └── PropertyDifferencesTest.java │ │ │ ├── formatter │ │ │ ├── AccountNumberFormatterTest.java │ │ │ ├── DefaultPojoFormatterTest.java │ │ │ ├── DefaultEnhancedPojoFormatterTest.java │ │ │ ├── DefaultPropertyFormatterTest.java │ │ │ ├── EnhancedPropertyFormatterTest.java │ │ │ └── DefaultEnhancedPropertyFormatterTest.java │ │ │ └── PojomaticTest.java │ └── site │ │ ├── resources │ │ └── css │ │ │ └── site.css │ │ ├── site.xml │ │ └── xdoc │ │ ├── eclipse.xml │ │ └── index.xml └── jvmMatrix.sh ├── .gitignore ├── modular-test-project ├── src │ ├── main │ │ └── java │ │ │ ├── module-info.java │ │ │ └── org │ │ │ └── pojomatic │ │ │ └── moduletest │ │ │ └── Bean.java │ └── test │ │ └── java │ │ └── org │ │ └── pojomatic │ │ └── moduletest │ │ └── BeanTest.java └── pom.xml ├── releasing.txt ├── src └── site │ ├── apt │ └── index.apt │ └── site.xml ├── pojomatic-benchmarks ├── pom.xml ├── pojomatic-base-benchmark │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── pojomatic │ │ ├── BeanSpeedTest.java │ │ └── Bean.java ├── pojomatic2-benchmark │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── pojomatic │ │ └── benchmark │ │ └── ArrayInObject.java └── pojomatic1-benchmark │ └── pom.xml └── README.md /pojomatic-test-utils/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /pojomatic/.gitignore: -------------------------------------------------------------------------------- 1 | /test-output 2 | /buildStatuses 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .settings 2 | .project 3 | target 4 | .idea 5 | *.iml 6 | bin 7 | .classpath 8 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Core Pojomatic classes. 3 | */ 4 | package org.pojomatic; 5 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/PropertyRole.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | public enum PropertyRole { 4 | EQUALS, HASH_CODE, TO_STRING 5 | } 6 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Annotations and enums to control the behavior of Pojomatic. 3 | */ 4 | package org.pojomatic.annotations; 5 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/diff/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * API and implementation classes to support {@link org.pojomatic.Pojomatic#diff(Object, Object) Pojomatic.diff} 3 | */ 4 | package org.pojomatic.diff; 5 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/PersonMain.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | public class PersonMain { 4 | public static void main(String[] args) { 5 | System.out.println(new Person("John", "Doe", 32).toString()); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /modular-test-project/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.jamon.moduletest{ 2 | requires java.base; 3 | requires org.pojomatic; 4 | exports org.pojomatic.moduletest; 5 | opens org.pojomatic.moduletest to org.pojomatic; 6 | } 7 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/formatter/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * API and classes to help control the format used by 3 | * {@link org.pojomatic.Pojomatic#toString(Object)} Pojomatic.toString} 4 | */ 5 | package org.pojomatic.formatter; 6 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/ClassDefiner.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | public interface ClassDefiner { 4 | 5 | Class defineClass(String className, byte[] classBytes) throws IllegalAccessException; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /pojomatic/src/site/resources/css/site.css: -------------------------------------------------------------------------------- 1 | .source { 2 | border: 1px solid #999; 3 | background-color: #eeeeff 4 | } 5 | .ln { color: rgb(0,0,0); font-weight: normal; font-style: normal; } 6 | .s0 { color: rgb(0,0,128); font-weight: bold; } 7 | .s1 { } 8 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/NestParent.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import org.pojomatic.annotations.Property; 4 | 5 | public class NestParent { 6 | @Property 7 | int i; 8 | 9 | static class NestChild {} 10 | } 11 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.pojomatic { 2 | requires java.base; 3 | requires org.objectweb.asm; 4 | exports org.pojomatic; 5 | exports org.pojomatic.annotations; 6 | exports org.pojomatic.formatter; 7 | exports org.pojomatic.diff; 8 | } 9 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/package-info.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * The classes in this package are intended for internal use only. Additionally, these 4 | * implementations are subject to change in future versions. 5 | */ 6 | package org.pojomatic.internal; 7 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/GetterAccess.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | import org.pojomatic.annotations.AutoDetectPolicy; 4 | import org.pojomatic.annotations.AutoProperty; 5 | 6 | @AutoProperty(autoDetect=AutoDetectPolicy.METHOD) 7 | public class GetterAccess { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/Interface.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | import org.pojomatic.annotations.AutoProperty; 4 | import org.pojomatic.Pojomator; 5 | import org.pojomatic.Pojomatic; 6 | 7 | @AutoProperty 8 | public interface Interface { 9 | static Pojomator POJOMATOR = Pojomatic.pojomator(Interface.class); 10 | 11 | String getName(); 12 | } 13 | -------------------------------------------------------------------------------- /modular-test-project/src/test/java/org/pojomatic/moduletest/BeanTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.moduletest; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | public class BeanTest { 8 | 9 | @Test 10 | public void testToString() throws Exception { 11 | assertEquals("Bean{s: {hello, world}}", new Bean("hello, world").toString()); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/factory/Access.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal.factory; 2 | 3 | import static org.objectweb.asm.Opcodes.*; 4 | 5 | public enum Access { 6 | PRIVATE(ACC_PRIVATE), 7 | PACKAGE(0), 8 | PROTECTED(ACC_PROTECTED), 9 | PUBLIC(ACC_PUBLIC); 10 | 11 | private final int code; 12 | 13 | private Access(int code) { 14 | this.code = code; 15 | } 16 | 17 | public int getCode() { 18 | return code; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/a/C1.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal.a; 2 | 3 | import org.pojomatic.annotations.Property; 4 | 5 | public class C1 { 6 | @Property int packagePrivate() { return 1; } 7 | @Property int packagePrivateOverriddenProtected() { return 1; } 8 | @Property int packagePrivateOverriddenPublic() { return 1; } 9 | @Property protected int protectedMethod() { return 1; } 10 | @Property public int publicMethod() { return 1; } 11 | } 12 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/LookupClassDefiner.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.lang.invoke.MethodHandles; 4 | import java.lang.invoke.MethodHandles.Lookup; 5 | 6 | public class LookupClassDefiner implements ClassDefiner { 7 | private final Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE); 8 | 9 | @Override 10 | public Class defineClass(String className, byte[] classBytes) throws IllegalAccessException { 11 | return lookup.defineClass(classBytes); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/NoPojomaticPropertiesException.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic; 2 | 3 | /** 4 | * An exception thrown when asked to create a {@link Pojomator} for a class which has no properties 5 | * annotated for use with Pojomatic. 6 | */ 7 | public class NoPojomaticPropertiesException extends IllegalArgumentException { 8 | private static final long serialVersionUID = 1L; 9 | 10 | public NoPojomaticPropertiesException(Class pojoClass) { 11 | super("Class " + pojoClass.getName() + " has no Pojomatic properties"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/a/C3.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal.a; 2 | 3 | import org.pojomatic.annotations.Property; 4 | import org.pojomatic.internal.b.C2; 5 | 6 | @SuppressWarnings("all") 7 | public class C3 extends C2 { 8 | @Property int packagePrivate() { return 3; } 9 | @Property protected int packagePrivateOverriddenProtected() { return 3; } 10 | @Property public int packagePrivateOverriddenPublic() { return 3; } 11 | @Property protected int protectedMethod() { return 3; } 12 | @Property public int publicMethod() { return 3; } 13 | } 14 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/b/C2.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal.b; 2 | 3 | import org.pojomatic.annotations.Property; 4 | import org.pojomatic.internal.a.C1; 5 | 6 | @SuppressWarnings("all") 7 | public class C2 extends C1 { 8 | @Property int packagePrivate() { return 1; } 9 | @Property protected int packagePrivateOverriddenProtected() { return 2; } 10 | @Property public int packagePrivateOverriddenPublic() { return 2; } 11 | @Property protected int protectedMethod() { return 2; } 12 | @Property public int publicMethod() { return 2; } 13 | } 14 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/b/C4.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal.b; 2 | 3 | import org.pojomatic.annotations.Property; 4 | import org.pojomatic.internal.a.C3; 5 | 6 | @SuppressWarnings("all") 7 | public class C4 extends C3 { 8 | @Property int packagePrivate() { return 4; } 9 | @Property protected int packagePrivateOverriddenProtected() { return 4; } 10 | @Property public int packagePrivateOverriddenPublic() { return 4; } 11 | @Property protected int protectedMethod() { return 4; } 12 | @Property public int publicMethod() { return 4; } 13 | } 14 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/test/java/org/pojomatic/junit/PojomaticAssertTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.junit; 2 | 3 | import org.pojomatic.test.AssertTest; 4 | 5 | public class PojomaticAssertTest extends AssertTest { 6 | @Override 7 | protected void performAssertEquals(Object expected, Object actual) { 8 | PojomaticAssert.assertEqualsWithDiff(expected, actual); 9 | } 10 | 11 | @Override 12 | protected void performAssertEquals(Object expected, Object actual, String message) { 13 | PojomaticAssert.assertEqualsWithDiff(message, expected, actual); 14 | } 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/TestUtils.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic; 2 | 3 | import org.pojomatic.internal.PropertyAccessor; 4 | import org.pojomatic.internal.PropertyField; 5 | 6 | public class TestUtils { 7 | 8 | public static PropertyElement field(Class clazz, String fieldName) throws Exception { 9 | return new PropertyField(clazz.getDeclaredField(fieldName), ""); 10 | } 11 | 12 | public static PropertyElement method(Class clazz, String methodName) throws Exception { 13 | return new PropertyAccessor(clazz.getDeclaredMethod(methodName), ""); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/diff/NoDifferencesTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.diff; 2 | 3 | import org.testng.annotations.Test; 4 | import static org.testng.Assert.*; 5 | 6 | public class NoDifferencesTest { 7 | 8 | @Test 9 | public void testAreEqual() { 10 | assertTrue(NoDifferences.getInstance().areEqual()); 11 | } 12 | 13 | @Test 14 | public void testToString() { 15 | assertEquals(NoDifferences.getInstance().toString(), "no differences"); 16 | } 17 | 18 | @Test 19 | public void testDifferences() { 20 | assertFalse(NoDifferences.getInstance().differences().iterator().hasNext()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/Implementation.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | public class Implementation implements Interface { 4 | private final String name; 5 | 6 | public Implementation(String name) { 7 | this.name = name; 8 | } 9 | 10 | @Override 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | @Override public int hashCode() { 16 | return POJOMATOR.doHashCode(this); 17 | } 18 | 19 | @Override public boolean equals(Object other) { 20 | return POJOMATOR.doEquals(this, other); 21 | } 22 | 23 | @Override public String toString() { 24 | return POJOMATOR.doToString(this); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/SubclassCannotOverrideEquals.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.annotations; 2 | 3 | import java.lang.annotation.*; 4 | 5 | import org.pojomatic.Pojomator; 6 | 7 | /** 8 | * Declares that a subclass of the annotated type cannot override the behavior of equals. 9 | * 10 | * Absent this annotation, it is assumed that subclasses cannot override {@code equals} for interface 11 | * types, and can for other types. 12 | * 13 | * @see Pojomator#doEquals(Object, Object) 14 | */ 15 | @Documented 16 | @Target(ElementType.TYPE) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | public @interface SubclassCannotOverrideEquals { 19 | } 20 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/PropertyField.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | public class PropertyField extends AbstractPropertyElement { 6 | public PropertyField(Field propertyField, String name) { 7 | super(propertyField, name.length() == 0 ? propertyField.getName() : name); 8 | } 9 | 10 | @Override 11 | public String getElementName() { 12 | return element.getName(); 13 | } 14 | 15 | @Override 16 | public String getType() { 17 | return "field"; 18 | } 19 | 20 | @Override 21 | public Class getPropertyType() { 22 | return element.getType(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/diff/ValueDifferenceTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.diff; 2 | 3 | import static org.testng.Assert.*; 4 | import org.testng.annotations.Test; 5 | 6 | public class ValueDifferenceTest { 7 | private ValueDifference DIFFERENCE = new ValueDifference("foo", "this", "that"); 8 | 9 | @Test 10 | public void testPropertyName() { 11 | assertEquals(DIFFERENCE.propertyName(), "foo"); 12 | } 13 | 14 | @Test 15 | public void testLeftValue() { 16 | assertEquals(DIFFERENCE.leftValue(), "this"); 17 | } 18 | 19 | @Test 20 | public void testRightValue() { 21 | assertEquals(DIFFERENCE.rightValue(), "that"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /releasing.txt: -------------------------------------------------------------------------------- 1 | To release: 2 | 3 | *) edit pom files to reflect new versions. pojomatic-test-utils should 4 | go to 1.0 (unless a new version is released), and both children 5 | should point to the new parent. 6 | *) commit, and tag with pojomatic-all-x.y.z 7 | *) Run maven with jdk 9 or later; it seems the deploy operation for 8 | pojomatic may need jdk 11 (bugs in 9 and 10) 9 | *) in root: 10 | *) mvn clean site site:stage scm-publish:publish-scm 11 | *) mvn -N deploy 12 | *) in pojomatic: 13 | *) mvn clean deploy 14 | *) log into https://oss.sonatype.org/, check the staging repositories, 15 | and release them 16 | *) edit poms back to snapshot status. 17 | *) push 18 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/PojomatorStub.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | /** 4 | * A stub class to serve as a location for line numbers in byte-code generated by {@link PojomatorByteCodeGenerator}. 5 | * If you are looking at this class, the odds are that you have encountered an stack trace including a line which 6 | * is allegedly from this class. The actual code was generated by {@code PojomatorByteCodeGenerator}; you can look for 7 | * occurrences of {@code visitLineNumber} in that class to determine what byte code was likely corresponding to 8 | * the stack trace element. 9 | */ 10 | public final class PojomatorStub { 11 | private PojomatorStub() {} 12 | } 13 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/IpAddressFormatter.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | import org.pojomatic.formatter.DefaultEnhancedPropertyFormatter; 4 | 5 | public class IpAddressFormatter extends DefaultEnhancedPropertyFormatter { 6 | @Override 7 | public void appendFormatted(StringBuilder builder, byte[] array) { 8 | if (array == null) { 9 | super.appendFormatted(builder, array); 10 | } 11 | else { 12 | boolean first = true; 13 | for (byte b: array) { 14 | if (first) { 15 | first = false; 16 | } 17 | else { 18 | builder.append('.'); 19 | } 20 | builder.append(((int) b) & 0xff); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/diff/NoDifferences.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.diff; 2 | 3 | import java.util.Collections; 4 | 5 | public final class NoDifferences implements Differences { 6 | private static final NoDifferences INSTANCE = new NoDifferences(); 7 | 8 | private NoDifferences() { 9 | } 10 | 11 | @Override 12 | public boolean areEqual() { 13 | return true; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return "no differences"; 19 | } 20 | 21 | public static NoDifferences getInstance() { 22 | return INSTANCE; 23 | } 24 | 25 | @Override 26 | public Iterable differences() { 27 | return Collections.emptyList(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/OverridesEquals.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.annotations; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | import org.pojomatic.Pojomator; 10 | 11 | /** 12 | * Declares that the annotated type overrides the behavior of {@link Object#equals(Object) equals}, 13 | * and hence is not compatible for equals with its superclasses. 14 | * 15 | * @see Pojomator#doEquals(Object, Object) 16 | */ 17 | @Documented 18 | @Target(ElementType.TYPE) 19 | @Retention(RetentionPolicy.RUNTIME) 20 | public @interface OverridesEquals { 21 | } 22 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/test/java/org/pojomatic/testng/PojomaticAssertTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.testng; 2 | 3 | import org.pojomatic.test.AssertTest; 4 | 5 | public class PojomaticAssertTest extends AssertTest { 6 | 7 | @Override 8 | protected void performAssertEquals(Object expected, Object actual) { 9 | //in TestNG, the arguments are included in any failure message in reverse order 10 | PojomaticAssert.assertEqualsWithDiff(actual, expected); 11 | } 12 | 13 | @Override 14 | protected void performAssertEquals(Object expected, Object actual, String message) { 15 | //in TestNG, the arguments are included in any failure message in reverse order 16 | PojomaticAssert.assertEqualsWithDiff(actual, expected, message); 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /modular-test-project/src/main/java/org/pojomatic/moduletest/Bean.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.moduletest; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.annotations.AutoProperty; 5 | 6 | @AutoProperty 7 | public class Bean { 8 | private final String s; 9 | 10 | public Bean(String s) { 11 | this.s = s; 12 | } 13 | 14 | public String getS() { return s; } 15 | 16 | public static void main(String[] args) { 17 | System.out.println(new Bean("Hello, world")); 18 | } 19 | 20 | @Override public String toString() { return Pojomatic.toString(this); } 21 | @Override public boolean equals(Object other) { return Pojomatic.equals(this, other); } 22 | @Override public int hashCode() { return Pojomatic.hashCode(this); } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/RegexMatcher.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import org.hamcrest.Description; 4 | import org.hamcrest.TypeSafeMatcher; 5 | 6 | public class RegexMatcher extends TypeSafeMatcher { 7 | private final String regex; 8 | 9 | public RegexMatcher(String regex){ 10 | this.regex = regex; 11 | } 12 | 13 | public static RegexMatcher matches(String regex){ 14 | return new RegexMatcher(regex); 15 | } 16 | 17 | @Override 18 | public void describeTo(Description description){ 19 | description.appendText("matches regex=").appendText(regex); 20 | } 21 | 22 | @Override 23 | protected boolean matchesSafely(String item) { 24 | return item.matches(regex); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/formatter/AccountNumberFormatterTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import org.testng.annotations.Test; 4 | import static org.testng.Assert.*; 5 | 6 | @Deprecated 7 | public class AccountNumberFormatterTest { 8 | 9 | @Test public void testFormatNull() { 10 | assertEquals(new AccountNumberFormatter().format(null), "null"); 11 | } 12 | 13 | @Test public void testFormatBlank() { 14 | assertEquals(new AccountNumberFormatter().format(""), ""); 15 | } 16 | 17 | @Test public void testFormatMin() { 18 | assertEquals(new AccountNumberFormatter().format(1234), "1234"); 19 | } 20 | 21 | @Test public void testFormat() { 22 | assertEquals(new AccountNumberFormatter().format("0123456789"), "******6789"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/diff/Differences.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.diff; 2 | 3 | /** 4 | * A summary of differences (if any) between two POJOs. 5 | */ 6 | public interface Differences { 7 | 8 | /** 9 | * The differences between the two objects. If there are no differences, an 10 | * empty {@link Iterable}. 11 | * 12 | * @return the differences between the two objects, or an empty {@link Iterable} if there are none 13 | */ 14 | Iterable differences(); 15 | 16 | /** 17 | * @return {@code true} if the two POJOs were {@code equal} to each other; 18 | * {@code false} otherwise. 19 | */ 20 | boolean areEqual(); 21 | 22 | /** 23 | * @return a description of the differences 24 | */ 25 | @Override 26 | String toString(); 27 | } 28 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/test/java/org/pojomatic/test/Container.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.test; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.annotations.AutoProperty; 5 | 6 | /** 7 | * Simple one-object container for use in tests. 8 | */ 9 | @AutoProperty 10 | public class Container { 11 | 12 | private final Object test; 13 | 14 | public Container(Object test) { 15 | this.test = test; 16 | } 17 | 18 | public Object getTest() { 19 | return test; 20 | } 21 | 22 | @Override 23 | public int hashCode() { 24 | return Pojomatic.hashCode(this); 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return Pojomatic.toString(this); 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | return Pojomatic.equals(this, o); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/AutoDetectPolicy.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.annotations; 2 | 3 | /** 4 | * A policy for determining which class members are automatically detected as properties. This 5 | * policy is set class-wide using {@link AutoProperty}. 6 | */ 7 | public enum AutoDetectPolicy { 8 | /** 9 | * Auto-detect fields of the class as properties 10 | */ 11 | FIELD, 12 | 13 | /** 14 | * Auto-detect accessor methods of the class as properties using the JavaBean conventions 15 | * (i.e. getX and isX). 16 | */ 17 | METHOD, 18 | 19 | /** 20 | * Do not auto-detect properties for the class. This is be useful to specify 21 | * a different {@link PojomaticPolicy} in {@link AutoProperty} without enabling 22 | * property auto-detection. 23 | */ 24 | NONE 25 | } 26 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/test/java/org/pojomatic/test/DifferentPojo.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.test; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.annotations.AutoProperty; 5 | 6 | /** 7 | * Simple one-object container for use in tests. 8 | */ 9 | @AutoProperty 10 | public class DifferentPojo { 11 | 12 | private final Object test; 13 | 14 | public DifferentPojo(Object test) { 15 | this.test = test; 16 | } 17 | 18 | public Object getTest() { 19 | return test; 20 | } 21 | 22 | @Override 23 | public int hashCode() { 24 | return Pojomatic.hashCode(this); 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return Pojomatic.toString(this); 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | return Pojomatic.equals(this, o); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/test/java/org/pojomatic/test/OnlyPojomaticEqual.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.test; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.annotations.AutoProperty; 5 | 6 | /** 7 | * Class where every instance is equal via {@link Pojomatic#equals(Object, Object)}, but never 8 | * by {@code this.equals(other)}. 9 | */ 10 | @AutoProperty 11 | public class OnlyPojomaticEqual { 12 | @SuppressWarnings("unused") 13 | private final int number = 3; 14 | 15 | @Override 16 | public boolean equals(Object obj) { 17 | //cannot assert because only the unit under test should throw AssertionError 18 | if (!Pojomatic.equals(this, obj)) { 19 | throw new IllegalStateException("Invariant violated"); 20 | } 21 | return false; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "toString"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/site/apt/index.apt: -------------------------------------------------------------------------------- 1 | ------ 2 | PojomaticAll 3 | ------ 4 | ------ 5 | ------ 6 | 7 | {{{./pojomatic/index.html}Pojomatic}} 8 | 9 | Pojomatic provides configurable implementations of the <<>>, <<>> 10 | and <<>> methods inherited from <<>>. 11 | 12 | For examples and a quick start guide, see the {{{./pojomatic/index.html}pojomatic site}}. You can download Pojomatic from the {{{http://repo1.maven.org/maven2/org/pojomatic/}Maven central repository}}. For more information, see the {{{https://github.com/irobertson/pojomatic}GitHub project page}}. 13 | 14 | {{{./pojomatic-test-utils/index.html}Pojomatic Test Utils}} 15 | 16 | Pojomatic Test Utils provides utilities which are useful in testing classes which use Pojomatic. For example, it includes functionality to report the differences between two instances if they are not equal. 17 | -------------------------------------------------------------------------------- /src/site/site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | org.apache.maven.skins 11 | maven-fluido-skin 12 | 1.7 13 | 14 | 15 | 16 | 17 | irobertson/pojomatic 18 | right 19 | blue 20 | 21 | true 22 | true 23 | span2 24 | span10 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/PojoFormat.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.annotations; 2 | 3 | import static java.lang.annotation.ElementType.TYPE; 4 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 5 | 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.Target; 9 | 10 | import org.pojomatic.formatter.PojoFormatter; 11 | 12 | /** 13 | * Specifies formatting information to be used for creating {@code String} representations of POJOs. 14 | * @see PojoFormatter 15 | */ 16 | @SuppressWarnings("deprecation") 17 | @Target(TYPE) 18 | @Retention(RUNTIME) 19 | @Documented 20 | public @interface PojoFormat { 21 | 22 | /** 23 | * The formatter to use for creating a {@code String} representation. 24 | * @return the formatter to use for creating a {@code String} representation. 25 | */ 26 | public Class value(); 27 | } 28 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/Person.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.annotations.AutoProperty; 5 | 6 | @AutoProperty 7 | public class Person { 8 | private final String firstName; 9 | private final String lastName; 10 | private final int age; 11 | 12 | public Person(String firstName, String lastName, int age) { 13 | this.firstName = firstName; 14 | this.lastName = lastName; 15 | this.age = age; 16 | } 17 | 18 | public String getLastName() { return this.lastName; } 19 | public String getFirstName() { return this.firstName; } 20 | public int getAge() { return this.age; } 21 | 22 | @Override public int hashCode() { 23 | return Pojomatic.hashCode(this); 24 | } 25 | 26 | @Override public String toString() { 27 | return Pojomatic.toString(this); 28 | } 29 | 30 | @Override public boolean equals(Object o) { 31 | return Pojomatic.equals(this, o); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/Employee.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | import org.pojomatic.annotations.AutoProperty; 4 | import org.pojomatic.annotations.PojomaticPolicy; 5 | import org.pojomatic.annotations.Property; 6 | 7 | @AutoProperty 8 | public class Employee { 9 | private final String firstName; 10 | private final String lastName; 11 | 12 | @Property(policy=PojomaticPolicy.EQUALS_TO_STRING) 13 | private String securityLevel; 14 | 15 | 16 | public String getFirstName() { 17 | return this.firstName; 18 | } 19 | 20 | public String getLastName() { 21 | return this.lastName; 22 | } 23 | 24 | public String getSecurityLevel() { 25 | return this.securityLevel; 26 | } 27 | 28 | public void setSecurityLevel(String securityLevel) { 29 | this.securityLevel = securityLevel; 30 | } 31 | 32 | public Employee(String firstName, String lastName, String securityLevel) { 33 | this.firstName = firstName; 34 | this.lastName = lastName; 35 | this.securityLevel = securityLevel; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/diff/Difference.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.diff; 2 | 3 | import java.util.NoSuchElementException; 4 | 5 | /** 6 | * A difference between two objects, 'left' and 'right'. 7 | */ 8 | public interface Difference { 9 | 10 | /** 11 | * The name of the property. 12 | * 13 | * @return the name of the property 14 | */ 15 | String propertyName(); 16 | 17 | /** 18 | * The value from the left instance (possibly {@code null}). 19 | * 20 | * @return the value from the left instance (possibly {@code null}) 21 | * @throws NoSuchElementException if the value does not exist on the left instance 22 | */ 23 | Object leftValue() throws NoSuchElementException; 24 | 25 | /** 26 | * The value from the right instance (possibly {@code null}). 27 | * 28 | * @return the value from the right instance (possibly {@code null}) 29 | * @throws NoSuchElementException if the value does not exist on the right instance 30 | */ 31 | Object rightValue() throws NoSuchElementException; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/ClassDefinerFactory.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | public class ClassDefinerFactory { 4 | private static volatile ClassDefiner CLASS_DEFINER; 5 | private final static Object MUTEX = new Object(); 6 | 7 | // Avoid initializing when the class is first loaded, so that any exceptions don't get masked as NoClassDefFoundErrors 8 | public static ClassDefiner getDefiner() { 9 | if (CLASS_DEFINER == null) { 10 | synchronized (MUTEX) { 11 | if (CLASS_DEFINER == null) { 12 | CLASS_DEFINER = makeDefiner(); 13 | } 14 | } 15 | } 16 | return CLASS_DEFINER; 17 | } 18 | 19 | private static ClassDefiner makeDefiner() { 20 | try { 21 | return (ClassDefiner) ClassDefiner.class.getClassLoader().loadClass("org.pojomatic.internal.LookupClassDefiner") 22 | .getConstructor() 23 | .newInstance(); 24 | } catch (ReflectiveOperationException | UnsupportedClassVersionError e) { 25 | return new ClassLoaderClassDefiner(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/ClassLoaderClassDefiner.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.security.AccessController; 4 | import java.security.PrivilegedAction; 5 | 6 | public class ClassLoaderClassDefiner implements ClassDefiner { 7 | 8 | private static final class DynamicClassLoader extends ClassLoader { 9 | private DynamicClassLoader(ClassLoader parent) { 10 | super(parent); 11 | } 12 | 13 | Class loadClass(String name, byte[] classBytes) { 14 | return defineClass(name, classBytes, 0, classBytes.length); 15 | } 16 | } 17 | 18 | private DynamicClassLoader classLoader = AccessController.doPrivileged( 19 | new PrivilegedAction() { 20 | @Override 21 | public DynamicClassLoader run() { 22 | return new DynamicClassLoader(PojomatorFactory.class.getClassLoader()); 23 | } 24 | }); 25 | 26 | 27 | @Override 28 | public Class defineClass(String className, byte[] classBytes) { 29 | return classLoader.loadClass(className, classBytes); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/IpAddressFormatterTest.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | import org.pojomatic.formatter.EnhancedPropertyFormatter; 4 | import org.testng.annotations.Test; 5 | 6 | import static org.testng.Assert.*; 7 | 8 | public class IpAddressFormatterTest { 9 | private final static EnhancedPropertyFormatter FORMATTER = new IpAddressFormatter(); 10 | 11 | @Test 12 | public void appendFormattedNull() { 13 | assertFormat("null", null); 14 | } 15 | 16 | @Test 17 | public void appendFormattedEmpty() { 18 | assertFormat(""); 19 | } 20 | 21 | @Test 22 | public void appendFormatSingleByte() { 23 | assertFormat("4", (byte) 4); 24 | assertFormat("252", (byte) 252); 25 | } 26 | 27 | @Test void appendFormatMultiByte() { 28 | assertFormat("10.254.7.3", (byte) 10, (byte) -2, (byte) 7, (byte)3); 29 | } 30 | 31 | private void assertFormat(String expected, byte... array) { 32 | StringBuilder builder = new StringBuilder(); 33 | FORMATTER.appendFormatted(builder, array); 34 | assertEquals(builder.toString(), expected); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/site/site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | org.apache.maven.skins 16 | maven-fluido-skin 17 | 1.7 18 | 19 | 20 | 21 | 22 | irobertson/pojomatic 23 | right 24 | blue 25 | 26 | true 27 | true 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/site/xdoc/index.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Pojomatic Test Utils 7 | 8 | 9 | 14 | 15 | 16 |

17 | Pojomatic Test Utils provides utilities to help with writing tests for, or using, classes which 18 | use Pojomatic. This library includes classes with JUnit and TestNG style 19 | assertEquals methods which include differences (via Pojomatic.diff(Object, Object)) 20 | in the error message if the assertion fails. 21 |

22 | 23 |

For more information and examples, see the JavaDocs.

24 | 25 |
26 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/Manual.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.Pojomator; 5 | import org.pojomatic.annotations.AutoProperty; 6 | 7 | @AutoProperty //all fields are included by default 8 | public class Manual { 9 | private String firstName, lastName; 10 | 11 | public Manual(String firstName, String lastName) { 12 | this.firstName = firstName; 13 | this.lastName = lastName; 14 | } 15 | 16 | 17 | public String getFirstName() { return this.firstName; } 18 | public String getLastName() { return this.lastName; } 19 | 20 | private final static Pojomator POJOMATOR = Pojomatic.pojomator(Manual.class); 21 | 22 | @Override public boolean equals(Object other) { 23 | return POJOMATOR.doEquals(this, other); 24 | } 25 | 26 | @Override public int hashCode() { 27 | return POJOMATOR.doHashCode(this); 28 | } 29 | 30 | @Override public String toString() { 31 | return POJOMATOR.doToString(this); 32 | } 33 | 34 | public static void main(String[] args) { 35 | System.out.println(new Manual("first", "last")); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pojomatic/src/site/site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | org.apache.maven.skins 17 | maven-fluido-skin 18 | 1.7 19 | 20 | 21 | 22 | 23 | irobertson/pojomatic 24 | right 25 | blue 26 | 27 | true 28 | true 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/Common.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.annotations.AutoProperty; 5 | 6 | @AutoProperty //all fields are included by default 7 | public class Common { 8 | private boolean test; 9 | 10 | private String myString; 11 | 12 | private int anInt; 13 | 14 | @Override public int hashCode() { 15 | return Pojomatic.hashCode(this); 16 | } 17 | 18 | @Override public String toString() { 19 | return Pojomatic.toString(this); 20 | } 21 | 22 | @Override public boolean equals(Object o) { 23 | return Pojomatic.equals(this, o); 24 | } 25 | 26 | public boolean isTest() { 27 | return this.test; 28 | } 29 | 30 | public void setTest(boolean test) { 31 | this.test = test; 32 | } 33 | 34 | public String getMyString() { 35 | return this.myString; 36 | } 37 | 38 | public void setMyString(String myString) { 39 | this.myString = myString; 40 | } 41 | 42 | public int getAnInt() { 43 | return this.anInt; 44 | } 45 | 46 | public void setAnInt(int anInt) { 47 | this.anInt = anInt; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/SkipArrayCheck.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.annotations; 2 | 3 | import static java.lang.annotation.ElementType.FIELD; 4 | import static java.lang.annotation.ElementType.METHOD; 5 | 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * If the annotated property is of type {@code Object}, then Pojomatic should not consider the possibility that it could 13 | * be an array. In particular, if a pair of values are both arrays, they will only be considered equal if they are the 14 | * same instance. This is primarily intended as a performance improvement in cases where a field of type Object is not 15 | * expected to contain array values, as it can avoid calls to 16 | * {@link Object#getClass()}.{@link Class#isArray() isArray()} 17 | *

18 | * If the annotated property is not of type {@code Object}, this annotation has no effect. 19 | * 20 | * @since 2.0 21 | */ 22 | @Target({FIELD, METHOD}) 23 | @Retention(RetentionPolicy.RUNTIME) 24 | @Documented 25 | public @interface SkipArrayCheck { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /pojomatic-benchmarks/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | org.pojomatic 5 | pojomatic-all 6 | master-SNAPSHOT 7 | 8 | pojomatic-benchmarks 9 | pom 10 | Pojomatic Benchmarks 11 | 12 | Parent and aggregator for pojomatic benchmarks 13 | 14 | 15 | 16 | pojomatic-base-benchmark 17 | pojomatic1-benchmark 18 | pojomatic2-benchmark 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-deploy-plugin 27 | 28 | true 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/Customer.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.annotations.AutoProperty; 5 | import org.pojomatic.annotations.PropertyFormat; 6 | 7 | @AutoProperty 8 | public class Customer { 9 | private final String firstName; 10 | 11 | private final String lastName; 12 | 13 | @PropertyFormat(IpAddressFormatter.class) 14 | private final byte[] ipAddress; 15 | 16 | public Customer(String firstName, String lastName, byte[] ipAddress) { 17 | this.firstName = firstName; 18 | this.lastName = lastName; 19 | this.ipAddress = ipAddress; 20 | } 21 | 22 | public String getFirstName() { return firstName; } 23 | public String getLastName() { return lastName; } 24 | public byte[] getIpAddress() { return ipAddress; } 25 | 26 | @Override public int hashCode() { 27 | return Pojomatic.hashCode(this); 28 | } 29 | 30 | @Override public String toString() { 31 | return Pojomatic.toString(this); 32 | } 33 | 34 | @Override public boolean equals(Object o) { 35 | return Pojomatic.equals(this, o); 36 | } 37 | 38 | public static void main(String[] args) { 39 | System.out.println(new Customer("Joe", "Blow", new byte[] {127, 0, 0, 1})); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/formatter/PropertyFormatter.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import java.lang.reflect.AnnotatedElement; 4 | 5 | /** 6 | * A formatter for a property. 7 | *

8 | * Any implementation of {@code PropertyFormatter} must have a public no-argument constructor. 9 | * 10 | * @deprecated Since 2.0. Implement {@link EnhancedPropertyFormatter} instead. This class is unaware of primitives, 11 | * and does not leverage StringBuilder. 12 | */ 13 | @Deprecated 14 | public interface PropertyFormatter { 15 | /** 16 | * Initialize the formatter for use; this method will be called exactly once on an instance, prior 17 | * to any calls to {@link #format(Object)}. This method does not need to be thread-safe. A typical implementation 18 | * might inspect the element for additional annotations used to configure the formatter. 19 | * @param element the field or method this formatter will be used for. 20 | */ 21 | public void initialize(AnnotatedElement element); 22 | 23 | /** 24 | * Format a given value. This method must be thread safe. 25 | * @param value the value to format 26 | * @return the value, formatted (must not be null) 27 | */ 28 | public String format(Object value); 29 | } 30 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/main/javadoc/overview.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | Overview 8 | 9 | 10 |

11 | Pojomatic Test Utils provide an enhancement to the traditional {@code assertEquals} method seen in 12 | unit test libraries, in the form of a new method, {@code assertEqualsWithDiff}. This new method has 13 | the same basic behavior as the original, with the added feature that if the two objects being 14 | asserted to be equal are in fact not equal, but have types compatible for equality, then the 15 | message of the thrown {@code AssertionError} will include the differences between the two objects 16 | as determined by {@link org.pojomatic.Pojomatic#diff(Object, Object) Pojomatic.diff}. 17 |

18 |

19 | Because Junit and TestNG disagree as to the order of the expected and actual parameters to 20 | {@code assertEquals}, two {@code PojomaticAssert} classes are provided: 21 | {@link org.pojomatic.junit.PojomaticAssert} and {@link org.pojomatic.testng.PojomaticAssert}. 22 |

23 | 24 | 25 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/test/java/org/pojomatic/test/AssertUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.test; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | public class AssertUtilsTest extends AssertTest { 8 | 9 | @Override 10 | protected void performAssertEquals(Object first, Object second) { 11 | AssertUtils.assertEquals(null, first, second); 12 | } 13 | 14 | @Override 15 | protected void performAssertEquals(Object first, Object second, String message) { 16 | AssertUtils.assertEquals(message, first, second); 17 | } 18 | 19 | @Test 20 | public void testEquals() { 21 | assertTrue(AssertUtils.equal(new Container(""), new Container(""))); 22 | } 23 | 24 | @Test 25 | public void testEqualsReflexive() { 26 | Container instance = new Container(""); 27 | assertTrue(AssertUtils.equal(instance, instance)); 28 | } 29 | 30 | @Test 31 | public void testEqualsBothNull() { 32 | assertTrue(AssertUtils.equal(null, null)); 33 | } 34 | 35 | @Test 36 | public void testEqualsNullFirst() { 37 | assertFalse(AssertUtils.equal(null, new Container(null))); 38 | } 39 | 40 | @Test 41 | public void testEqualsNullSecond() { 42 | assertFalse(AssertUtils.equal(new Container(null), null)); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/diff/ValueDifference.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.diff; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.annotations.Property; 5 | 6 | public class ValueDifference implements Difference { 7 | @Property 8 | private final String propertyName; 9 | 10 | @Property 11 | private final Object leftValue; 12 | 13 | @Property 14 | private final Object rightValue; 15 | 16 | public ValueDifference(String propertyName, Object lhs, Object rhs) { 17 | this.propertyName = propertyName; 18 | this.leftValue = lhs; 19 | this.rightValue = rhs; 20 | } 21 | 22 | @Override 23 | public String propertyName() { 24 | return propertyName; 25 | } 26 | 27 | @Override 28 | public Object leftValue() { 29 | return leftValue; 30 | } 31 | 32 | @Override 33 | public Object rightValue() { 34 | return rightValue; 35 | } 36 | 37 | @Override 38 | public boolean equals(Object obj) { 39 | return Pojomatic.equals(this, obj); 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | return Pojomatic.hashCode(this); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | //TODO - can we do better here? 50 | return propertyName + ": {" + leftValue + "} versus {" + rightValue + "}"; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/PropertyFormat.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.annotations; 2 | 3 | import static java.lang.annotation.ElementType.FIELD; 4 | import static java.lang.annotation.ElementType.METHOD; 5 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 6 | 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.Target; 10 | 11 | import org.pojomatic.formatter.PropertyFormatter; 12 | 13 | /** 14 | * Specifies formatting information to be used for creating {@code String} representations of 15 | * properties. Note that using a {@code PropertyFormat} annotation on a property does not influence 16 | * whether that property will be included in the {@code toString} implementation. That is 17 | * determined solely by any {@link Property} and {@link AutoProperty} annotations on the POJO. 18 | * @see PropertyFormatter 19 | */ 20 | @SuppressWarnings("deprecation") 21 | @Target({FIELD, METHOD}) 22 | @Retention(RUNTIME) 23 | @Documented 24 | public @interface PropertyFormat { 25 | 26 | /** 27 | * The formatter to use for creating a {@code String} representation. 28 | * @return the formatter to use for creating a {@code String} representation. 29 | */ 30 | public Class value(); 31 | } 32 | -------------------------------------------------------------------------------- /pojomatic/src/site/xdoc/eclipse.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Using with eclipse 7 | 8 | 9 |
10 |

11 | Adding an eclipse template for pojomatic can make pojomating a class even easier. Open up the 12 | Preferences dialog, and then go to Java->Editor->Templates. Then add a template named 13 | "pojomatic" with the following body: 14 |

15 | 16 | @Override public boolean equals(Object other) { return Pojomatic.equals(this, other); } 17 | @Override public String toString() { return Pojomatic.toString(this); } 18 | @Override public int hashCode() { return Pojomatic.hashCode(this); } 19 | ${:import(org.pojomatic.Pojomatic)} 20 | 21 |

22 | After that, to pojomate a class, simply add an @AutoProperty annotation to the 23 | class, and insert the macro in the class body by typing "pojomatic" and hitting Control-space. 24 |

25 |
26 | 27 |
28 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/examples/Auto.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | import static org.pojomatic.annotations.PojomaticPolicy.NONE; 4 | 5 | import org.pojomatic.Pojomatic; 6 | import org.pojomatic.annotations.AutoProperty; 7 | import org.pojomatic.annotations.Property; 8 | 9 | @AutoProperty //all fields are included by default 10 | public class Auto { 11 | private boolean test; //included by default 12 | 13 | @Property(policy=NONE) 14 | private int exclude; 15 | 16 | @Property //include a method as well, even though it does not follow the getX convention 17 | public String derived() { 18 | return String.valueOf(System.currentTimeMillis()); 19 | } 20 | 21 | 22 | @Override public int hashCode() { 23 | return Pojomatic.hashCode(this); 24 | } 25 | 26 | @Override public String toString() { 27 | return Pojomatic.toString(this); 28 | } 29 | 30 | @Override public boolean equals(Object o) { 31 | return Pojomatic.equals(this, o); 32 | } 33 | 34 | public boolean isTest() { 35 | return test; 36 | } 37 | 38 | public void setTest(boolean test) { 39 | this.test = test; 40 | } 41 | 42 | public int getExclude() { 43 | return exclude; 44 | } 45 | 46 | public void setExclude(int exclude) { 47 | this.exclude = exclude; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/factory/PropertyDescriptor.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal.factory; 2 | 3 | import java.lang.annotation.Annotation; 4 | 5 | import org.objectweb.asm.Opcodes; 6 | 7 | public class PropertyDescriptor { 8 | final Class type; 9 | final Class[] annotations; 10 | 11 | String name = "x"; 12 | Access access = Access.PRIVATE; 13 | boolean isMethod; 14 | boolean isSynthetic; 15 | 16 | @SuppressWarnings("unchecked") 17 | public PropertyDescriptor(Class type) { 18 | this(type, new Class[0]); 19 | } 20 | 21 | public PropertyDescriptor(Class type, Class[] annotations) { 22 | this.type = type; 23 | this.annotations = annotations; 24 | } 25 | 26 | public PropertyDescriptor withName(String name) { 27 | this.name = name; 28 | return this; 29 | } 30 | 31 | public PropertyDescriptor withAccess(Access access) { 32 | this.access = access; 33 | return this; 34 | } 35 | 36 | public PropertyDescriptor asMethod() { 37 | isMethod = true; 38 | return this; 39 | } 40 | 41 | public PropertyDescriptor asSynthetic() { 42 | isSynthetic = true; 43 | return this; 44 | } 45 | 46 | public int getFlags() { 47 | return access.getCode() | (isSynthetic ? Opcodes.ACC_SYNTHETIC : 0); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/site/xdoc/changes.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Changes in Pojomatic Test Utils 7 | 8 | 9 |
10 | 11 |
    12 |
  • 13 | Changed the signature of org.pojomatic.junit.PojomaticAssert.assertEqualsWithDiff() 14 | such that the message parameter appears first in order to match the style of the 15 | standard JUnit assertions. Warning: this is a backward-incompatible change. 16 |
  • 17 |
18 |
19 | 20 |
    21 |
  • 22 | Updates to handle changes to Pojomatic's differences API. 23 |
  • 24 |
25 |
26 | 27 |
    28 |
  • 29 | Initial Release. 30 |
  • 31 |
32 |
33 |
34 | 35 |
36 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/AutoProperty.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.annotations; 2 | 3 | import static java.lang.annotation.ElementType.TYPE; 4 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 5 | 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.Target; 9 | 10 | import org.pojomatic.Pojomatic; 11 | 12 | /** 13 | * Assigns the defaults for {@link Pojomatic} at the class level and provides a way to 14 | * configure the automatic detection of properties. 15 | * Note that this can is overridden (case by case) by the {@link Property}} annotation. 16 | */ 17 | @Target(TYPE) 18 | @Retention(RUNTIME) 19 | @Documented 20 | public @interface AutoProperty { 21 | /** 22 | * Which sets of {@link Pojomatic} operations ({@code equals}, {@code hashCode} and 23 | * {@code toString}) should use properties, unless otherwise stated by {@link Property @Property}. 24 | * @return Which operations should properties by default. 25 | */ 26 | public DefaultPojomaticPolicy policy() default DefaultPojomaticPolicy.ALL; 27 | 28 | /** 29 | * Specifies whether to auto-detect properties by their fields, getters or not at all. 30 | * @return whether to auto-detect properties by their fields, getters or not at all. 31 | */ 32 | public AutoDetectPolicy autoDetect() default AutoDetectPolicy.FIELD; 33 | } 34 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/Primitives.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.objectweb.asm.Opcodes; 7 | 8 | class Primitives { 9 | private final static Map, Class> WRAPPER_CLASSES = new HashMap<>(); 10 | private final static Map, Integer> OPCODES = new HashMap<>(); 11 | 12 | static { 13 | register(void.class, Void.class, Opcodes.NULL); 14 | register(boolean.class, Boolean.class, Opcodes.INTEGER); 15 | register(byte.class, Byte.class, Opcodes.INTEGER); 16 | register(char.class, Character.class, Opcodes.INTEGER); 17 | register(short.class, Short.class, Opcodes.INTEGER); 18 | register(int.class, Integer.class, Opcodes.INTEGER); 19 | register(long.class, Long.class, Opcodes.LONG); 20 | register(float.class, Float.class, Opcodes.FLOAT); 21 | register(double.class, Double.class, Opcodes.DOUBLE); 22 | } 23 | 24 | static Class getWrapperClass(Class primitiveClass) { 25 | return WRAPPER_CLASSES.get(primitiveClass); 26 | } 27 | 28 | static Integer getOpcode(Class primitiveClass) { 29 | return OPCODES.get(primitiveClass); 30 | } 31 | 32 | private static void register(Class clazz, Class wrapperClass, Integer opcode) { 33 | WRAPPER_CLASSES.put(clazz, wrapperClass); 34 | OPCODES.put(clazz, opcode); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/PropertyAccessorTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import static org.testng.Assert.*; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | import org.testng.annotations.Test; 8 | 9 | public class PropertyAccessorTest { 10 | 11 | public static class MethodHolder { 12 | public int getFoo() { return 0; } 13 | public int isFoo() { return 0; } 14 | public boolean getBar() { return true; } 15 | public boolean isBar() { return true; } 16 | } 17 | 18 | @Test 19 | public void testGetter() { 20 | assertEquals(new PropertyAccessor(getMethod("getFoo"), "").getName(), "foo"); 21 | } 22 | 23 | @Test 24 | public void testBooleanGetter() { 25 | assertEquals(new PropertyAccessor(getMethod("getBar"), "").getName(), "bar"); 26 | } 27 | 28 | @Test 29 | public void testNonBooleanIs() { 30 | assertEquals(new PropertyAccessor(getMethod("isFoo"), "").getName(), "isFoo"); 31 | } 32 | 33 | @Test 34 | public void testBooleanIs() { 35 | assertEquals(new PropertyAccessor(getMethod("isBar"), "").getName(), "bar"); 36 | } 37 | 38 | 39 | private Method getMethod(String methodName) { 40 | for (Method m: MethodHolder.class.getMethods()) { 41 | if (methodName.equals(m.getName())) { 42 | return m; 43 | } 44 | } 45 | throw new IllegalArgumentException("No method named " + methodName); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /pojomatic-benchmarks/pojomatic-base-benchmark/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | org.pojomatic 6 | pojomatic-benchmarks 7 | master-SNAPSHOT 8 | 9 | 4.0.0 10 | pojomatic-base-benchmark 11 | jar 12 | Pojomatic Base Benchmark 13 | http://maven.apache.org 14 | 15 | 16 | org.pojomatic 17 | pojomatic 18 | 19 | 20 | com.google.caliper 21 | caliper 22 | 24 | 1.0-beta-SNAPSHOT 25 | 26 | 27 | com.google.code.java-allocation-instrumenter 28 | java-allocation-instrumenter 29 | 2.1 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/PropertyElement.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic; 2 | 3 | import java.lang.reflect.AnnotatedElement; 4 | 5 | /** 6 | * A "property" on a class. In this context, all a property is is a means of obtaining a value from 7 | * an instance. 8 | */ 9 | public interface PropertyElement { 10 | 11 | /** 12 | * Get the name of this property. 13 | * @return the name of this property. 14 | */ 15 | String getName(); 16 | 17 | /** 18 | * Get the original annotated element that this property is derived from. 19 | * @return the original annotated element that this property is derived from. 20 | */ 21 | AnnotatedElement getElement(); 22 | 23 | /** 24 | * Get the class object representing the class or interface declaring this property. 25 | * @return the declaring class or interface of this property. 26 | */ 27 | Class getDeclaringClass(); 28 | 29 | /** 30 | * Get the name of the underlying field or method. 31 | * @return the name of the underlying field or method. 32 | */ 33 | String getElementName(); 34 | 35 | /** 36 | * Get the type of element returns either "field" or "method". 37 | * @return the type of element. 38 | */ 39 | String getType(); 40 | 41 | 42 | /** 43 | * Get the type of this property - the return type of a method, or the type of a field 44 | * @return the type of this property 45 | */ 46 | Class getPropertyType(); 47 | } 48 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/PropertyFilter.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.util.Collections; 4 | import java.util.Set; 5 | 6 | import org.pojomatic.annotations.DefaultPojomaticPolicy; 7 | import org.pojomatic.annotations.PojomaticPolicy; 8 | 9 | public class PropertyFilter { 10 | private PropertyFilter() {} 11 | 12 | /** 13 | * Get the roles specified by a property policy and class policy. 14 | * @param elementPolicy the policy on the property ({@code null} if unspecified) 15 | * @param classPolicy the policy on the class ({@code null} if unspecified). 16 | * @return the roles specified by the policies. 17 | * @throws IllegalArgumentException if both {@code elementPolicy} and {@code classPolicy} are 18 | * {@code null}. 19 | */ 20 | public static Set getRoles( 21 | PojomaticPolicy elementPolicy, DefaultPojomaticPolicy classPolicy) { 22 | if (elementPolicy != null) { 23 | Set roles = elementPolicy.getRoles(); 24 | if (roles == null) { // this will be the case for PojomaticPolicy.DEFAULT 25 | return classPolicy != null ? classPolicy.getRoles() : PojomaticPolicy.ALL.getRoles(); 26 | } 27 | else { 28 | return roles; 29 | } 30 | } 31 | else if(classPolicy != null) { 32 | return classPolicy.getRoles(); 33 | } 34 | else { 35 | return Collections.emptySet(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pojomatic/jvmMatrix.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jvmCount=$#; 4 | jvms=($@) 5 | 6 | red="\e[31m" 7 | green="\e[32m" 8 | blue="\e[34m" 9 | reset="\e[0m" 10 | 11 | function javaVersion { 12 | local javaExecutable=$1 13 | local version=$($javaExecutable -version|&head -1|sed 's/.*"\(.*\)".*/\1/') 14 | case $version in 15 | 1.7*) echo 7 ;; 16 | 1.8*) echo 8 ;; 17 | 9) echo 9 ;; 18 | 10*) echo 10 ;; 19 | 11*) echo 11 ;; 20 | *) echo unable to parse java version $version >&2; exit 1 ;; 21 | esac 22 | } 23 | 24 | rm -f buildStatuses; 25 | 26 | declare -A javaVersions 27 | for jvm in $@; do 28 | javaVersions[$jvm]=$(javaVersion $jvm/bin/java) 29 | done 30 | 31 | for jvm in $@; do 32 | echo $jvm "->" ${javaVersions[$jvm]} 33 | done 34 | 35 | for testRunnerJvm in $@; do 36 | for testTargetJvm in $@; do 37 | if [[ ${javaVersions[$testTargetJvm]} -le ${javaVersions[$testRunnerJvm]} ]]; then 38 | javaVersion=${javaVersions[$testTargetJvm]} 39 | # maven-surefire-plugin will run tests using the java executable defined via ${jvm} 40 | command="mvn clean -Djvm=$testRunnerJvm/bin/java -DtestJavac=${testTargetJvm}/bin/javac -Djava.test.version=$javaVersion test"; 41 | echo -e "running ${blue}$command${reset}" 42 | echo 43 | eval $command && result="${green}passed${reset}" || result="${red}failed${reset}" 44 | echo -e $command $result >> buildStatuses 45 | fi; 46 | done; 47 | done; 48 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/diff/PropertyDifferences.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.diff; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | 6 | import org.pojomatic.Pojomatic; 7 | import org.pojomatic.annotations.Property; 8 | 9 | public class PropertyDifferences implements Differences { 10 | @Property 11 | private final List differences; 12 | 13 | /** 14 | * @param differences cannot be {@code null} or empty 15 | * @throws NullPointerException if {@code differences} is {@code null} 16 | * @throws IllegalArgumentException if {@code differences.isEmpty()} is {@code true} 17 | */ 18 | public PropertyDifferences(List differences) { 19 | if (differences == null) { 20 | throw new NullPointerException("list of differences is null"); 21 | } 22 | if (differences.isEmpty()) { 23 | throw new IllegalArgumentException("list of differences is empty"); 24 | } 25 | this.differences = Collections.unmodifiableList(differences); 26 | } 27 | 28 | @Override 29 | public List differences() { 30 | return differences; 31 | } 32 | 33 | @Override 34 | public boolean areEqual() { 35 | return false; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return differences.toString(); 41 | } 42 | 43 | @Override 44 | public boolean equals(Object other) { 45 | return Pojomatic.equals(this, other); 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | return Pojomatic.hashCode(this); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/Type.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.util.List; 4 | 5 | public interface Type { 6 | 7 | /** 8 | * @return the class represented by this type 9 | */ 10 | Class getClazz(); 11 | 12 | /** 13 | * @return a list of sample values for the type 14 | */ 15 | List getSampleValues(); 16 | 17 | /** 18 | * @return the number of array levels in this type. 0 for primitives or non-array classes, 1 for arrays thereof, etc. 19 | */ 20 | int arrayDepth(); 21 | 22 | /** 23 | * Compute the hashCode for a value of this type. Do not recursively process arrays 24 | * @param value 25 | * @return the non-recursive hashCode for {@code value}. 26 | */ 27 | int hashCode(Object value); 28 | 29 | /** 30 | * Compute the hashCode for a value of this type, recursing into arrays as needed. 31 | * @param value 32 | * @return recursive hashCode for {@code value} 33 | */ 34 | int deepHashCode(Object value); 35 | 36 | /** 37 | * Compute the desired toString representation for a value of this type. 38 | * @param value 39 | * @return the desired non-recursive toString representation for {@code value}. 40 | */ 41 | String toString(Object value); 42 | 43 | /** 44 | * Compute the desired toString representation for a value of this type, recursing into arrays as needed. 45 | * @param value 46 | * @return the desired recursive toString representation for {@code value}. 47 | */ 48 | String deepToString(Object value); 49 | } 50 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/factory/PojoFactoryTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal.factory; 2 | 3 | import static org.testng.Assert.*; 4 | 5 | import org.testng.annotations.Test; 6 | 7 | import java.lang.reflect.Field; 8 | 9 | import org.pojomatic.annotations.Property; 10 | 11 | public class PojoFactoryTest { 12 | @Test 13 | public void testFactory() throws Exception { 14 | PojoFactory pojoFactory = new PojoFactory(new PojoDescriptor(new PropertyDescriptor(int.class))); 15 | Object pojo = pojoFactory.create().with("x", 4).pojo(); 16 | Field field = pojo.getClass().getDeclaredField("x"); 17 | field.setAccessible(true); 18 | assertEquals(field.get(pojo), (Object) 4); 19 | assertTrue(field.isAnnotationPresent(Property.class)); 20 | 21 | assertEquals(pojoFactory.pojomator().doHashCode(pojo), 31 + 4); 22 | } 23 | 24 | @Test 25 | public void testParent() throws Exception { 26 | PojoDescriptor parent = new PojoDescriptor(new PropertyDescriptor(String.class)); 27 | PojoClassFactory pojoClassFactory = new PojoClassFactory(); 28 | pojoClassFactory.generateClass(parent); 29 | PojoFactory pojoFactory = new PojoFactory( 30 | pojoClassFactory, 31 | new PojoDescriptor( 32 | "foo", 33 | "bar", 34 | Access.PUBLIC, 35 | parent, 36 | new PropertyDescriptor(int.class))); 37 | Object pojo = pojoFactory.create().with("x", 4).withParent("x", "foo").pojo(); 38 | assertEquals(pojoFactory.pojomator().doToString(pojo), "bar{x: {foo}, x: {4}}"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/AbstractPropertyElement.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.lang.reflect.AccessibleObject; 4 | import java.lang.reflect.AnnotatedElement; 5 | import java.lang.reflect.Member; 6 | 7 | import org.pojomatic.PropertyElement; 8 | 9 | public abstract class AbstractPropertyElement 10 | implements PropertyElement { 11 | protected final E element; 12 | private final String name; 13 | 14 | protected AbstractPropertyElement(E element, String name) { 15 | this.element = element; 16 | this.name = name; 17 | } 18 | 19 | @Override 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | /* (non-Javadoc) 25 | * @see org.pojomatic.PropertyElement#getElement() 26 | */ 27 | @Override 28 | public AnnotatedElement getElement() { 29 | return this.element; 30 | } 31 | 32 | @Override 33 | public Class getDeclaringClass() { 34 | return element.getDeclaringClass(); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | return element.hashCode(); 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | if (this == obj) { 45 | return true; 46 | } 47 | if (obj == null) { 48 | return false; 49 | } 50 | if (getClass() != obj.getClass()) { 51 | return false; 52 | } 53 | final AbstractPropertyElement other = (AbstractPropertyElement) obj; 54 | return element.equals(other.element); 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return element.toString(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/ClassOnlyClassLoader.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.security.cert.Certificate; 7 | import java.security.CodeSource; 8 | import java.security.ProtectionDomain; 9 | 10 | import com.google.common.base.Preconditions; 11 | import com.google.common.io.ByteStreams; 12 | 13 | /** 14 | * A {@link ClassLoader} which loads classes, but will not expose their byte code. 15 | */ 16 | class ClassOnlyClassLoader extends ClassLoader { 17 | private final ClassLoader source; 18 | 19 | public ClassOnlyClassLoader(ClassLoader source) { 20 | super(null); 21 | this.source = Preconditions.checkNotNull(source); 22 | } 23 | 24 | @Override 25 | protected Class findClass(String name) throws ClassNotFoundException { 26 | if (name.startsWith("org.pojomatic.annotations")) { 27 | return getClass().getClassLoader().loadClass(name); 28 | } 29 | try (InputStream classBytesStream = source.getResourceAsStream(name.replace('.', '/') + ".class")) { 30 | if (classBytesStream == null) { 31 | throw new ClassNotFoundException(name); 32 | } 33 | byte[] bytes = ByteStreams.toByteArray(classBytesStream); 34 | ProtectionDomain protectionDomain = 35 | new ProtectionDomain(new CodeSource(new File("/no/such/file").toURI().toURL(), new Certificate[0]), null); 36 | return defineClass(name, bytes, 0, bytes.length, protectionDomain); 37 | } catch (IOException e) { 38 | throw new ClassNotFoundException(name, e); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pojomatic 2 | ========= 3 | 4 | Pojomatic provides configurable implementations of the `equals(Object)`, `hashCode()` and `toString()` methods 5 | inherited from `java.lang.Object`. 6 | 7 | For example, the following bean has been "pojomated": 8 | 9 | import org.pojomatic.Pojomatic; 10 | import org.pojomatic.annotations.AutoProperty; 11 | 12 | @AutoProperty 13 | public class Person { 14 | private final String firstName; 15 | private final String lastName; 16 | private final int age; 17 | 18 | public Person(String firstName, String lastName, int age) { 19 | this.firstName = firstName; 20 | this.lastName = lastName; 21 | this.age = age; 22 | } 23 | 24 | public String getFirstName() { return this.firstName; } 25 | public String getLastName() { return this.lastName; } 26 | public int getAge() { return this.age; } 27 | 28 | @Override public boolean equals(Object o) { 29 | return Pojomatic.equals(this, o); 30 | } 31 | 32 | @Override public int hashCode() { 33 | return Pojomatic.hashCode(this); 34 | } 35 | 36 | @Override public String toString() { 37 | return Pojomatic.toString(this); 38 | } 39 | } 40 | 41 | The above class implements equals and hashCode methods following the best practices outlined in Josh Bloch's Efective Java. Moreover, running 42 | 43 | System.out.println(new Person("John", "Doe", 32).toString()); 44 | 45 | will result in the following output: 46 | 47 | Person{firstName: {John}, lastName: {Doe}, age: {32}} 48 | 49 | For more information and examples, see the [Pojomatic site](http://www.pojomatic.org) 50 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/formatter/DefaultPojoFormatter.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.PropertyElement; 5 | 6 | /** 7 | * Default formatter for classes that use {@link Pojomatic}. This implementation first presents 8 | * the class name, and then each property in turn, separated by commas, using braces to indicate 9 | * nesting. 10 | *

11 | * For example, if a class Person has two properties, firstName and LastName, and these properties 12 | * are using {@link DefaultPropertyFormatter}, then the Person 13 | * instance representing Joe Blow would be represented as 14 | * "Person{firstName: {Joe}, lastName: {Blow}}" 15 | * 16 | * @deprecated Since 2.0. Use {@link DefaultEnhancedPojoFormatter} instead. 17 | */ 18 | @Deprecated 19 | public class DefaultPojoFormatter implements PojoFormatter { 20 | private boolean firstPropertyPrinted = false; 21 | 22 | @Override 23 | public String getPropertyPrefix(PropertyElement property) { 24 | StringBuilder result = new StringBuilder(); 25 | if (firstPropertyPrinted) { 26 | result.append(", "); 27 | } 28 | else { 29 | firstPropertyPrinted = true; 30 | } 31 | return result.append(property.getName()).append(": {").toString(); 32 | } 33 | 34 | @Override 35 | public String getPropertySuffix(PropertyElement property) { 36 | return "}"; 37 | } 38 | 39 | @Override 40 | public String getToStringPrefix(Class pojoClass) { 41 | return pojoClass.getSimpleName() + "{"; 42 | } 43 | 44 | @Override 45 | public String getToStringSuffix(Class pojoClass) { 46 | return "}"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/diff/PropertyDifferencesTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.diff; 2 | 3 | import org.testng.annotations.Test; 4 | import static org.testng.Assert.*; 5 | 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | 9 | public class PropertyDifferencesTest { 10 | 11 | @Test(expectedExceptions=NullPointerException.class) 12 | public void testConstructorNullPointerException() { 13 | new PropertyDifferences(null); 14 | } 15 | 16 | @Test(expectedExceptions=IllegalArgumentException.class) 17 | public void testEmptyDifferences() { 18 | new PropertyDifferences(Collections.emptyList()); 19 | } 20 | 21 | @Test 22 | public void testSingleDifferenceToString() { 23 | PropertyDifferences propertyDifferences = new PropertyDifferences( 24 | Arrays.asList(new ValueDifference("foo", 3, 4))); 25 | assertEquals(propertyDifferences.toString(), "[foo: {3} versus {4}]"); 26 | 27 | propertyDifferences = new PropertyDifferences( 28 | Arrays.asList(new ValueDifference("foo", null, 4))); 29 | assertEquals(propertyDifferences.toString(), "[foo: {null} versus {4}]"); 30 | } 31 | 32 | @Test 33 | public void testMultipleDifferencesToString() { 34 | assertEquals(new PropertyDifferences(Arrays.asList( 35 | new ValueDifference("foo", 3, 4), new ValueDifference("bar", "this", "that"))).toString(), "[foo: {3} versus {4}, bar: {this} versus {that}]"); 36 | } 37 | 38 | @Test 39 | public void testAreEqual() { 40 | PropertyDifferences propertyDifferences = new PropertyDifferences( 41 | Arrays.asList(new ValueDifference("foo", 3, 4))); 42 | assertFalse(propertyDifferences.areEqual()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pojomatic-benchmarks/pojomatic2-benchmark/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | org.pojomatic 5 | pojomatic-benchmarks 6 | master-SNAPSHOT 7 | 8 | pojomatic2-benchmark 9 | Pojomatic2 benchmark 10 | Benchmark for Pojomatic version 2.0 11 | 12 | 13 | org.pojomatic 14 | pojomatic-base-benchmark 15 | master-SNAPSHOT 16 | 17 | 18 | org.pojomatic 19 | pojomatic 20 | 21 | 22 | 23 | 24 | 25 | org.codehaus.mojo 26 | exec-maven-plugin 27 | 1.2.1 28 | 29 | 30 | 31 | java 32 | 33 | integration-test 34 | 35 | 36 | 37 | org.pojomatic.BeanSpeedTest 38 | 39 | --run-name=pojomatic-2 40 | --vm=jdk7,jdk8 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/Property.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.annotations; 2 | 3 | import static java.lang.annotation.ElementType.FIELD; 4 | import static java.lang.annotation.ElementType.METHOD; 5 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 6 | 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.Target; 10 | 11 | import org.pojomatic.Pojomatic; 12 | 13 | /** 14 | * Marks a property of a class to be used by {@link Pojomatic} 15 | * @see PojomaticPolicy 16 | */ 17 | @Target({FIELD, METHOD}) 18 | @Retention(RUNTIME) 19 | @Documented 20 | public @interface Property { 21 | 22 | /** 23 | * Which sets of {@link Pojomatic} operations ({@code equals}, {@code hashCode} and 24 | * {@code toString}) should use a property. 25 | * @return Which operations should use the annotated property 26 | */ 27 | public PojomaticPolicy policy() default PojomaticPolicy.DEFAULT; 28 | 29 | /** 30 | * The name used to identify the property in the standard {@code toString} representation. If 31 | * empty, the following algorithm is used to determine the name. For a property referenced by 32 | * field, the name of the field is used. For a property referenced by a method whose name is of 33 | * the form {@code getSomeField}, the name {@code someField} will be used. For a boolean property 34 | * referenced by a method whose name is of the form {@code isSomeField}, the name 35 | * {@code someField} will be used. For any other property referenced by a method, the name of 36 | * the method is used. 37 | * 38 | * @return the name used to identify the property in the standard {@code toString} representation. 39 | */ 40 | public String name() default ""; 41 | } 42 | -------------------------------------------------------------------------------- /pojomatic-benchmarks/pojomatic1-benchmark/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | org.pojomatic 5 | pojomatic-benchmarks 6 | master-SNAPSHOT 7 | 8 | pojomatic1-benchmark 9 | Pojomatic1 benchmark 10 | Benchmark for Pojomatic version 1.0 11 | 12 | 13 | org.pojomatic 14 | pojomatic-base-benchmark 15 | master-SNAPSHOT 16 | 17 | 18 | org.pojomatic 19 | pojomatic 20 | 1.0 21 | 22 | 23 | 24 | 25 | 26 | org.codehaus.mojo 27 | exec-maven-plugin 28 | 1.2.1 29 | 30 | 31 | 32 | java 33 | 34 | integration-test 35 | 36 | 37 | 38 | org.pojomatic.BeanSpeedTest 39 | 40 | --run-name=pojomatic-1 41 | --vm=jdk7,jdk8 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/EnhancedPojoFormatterWrapper.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import org.pojomatic.PropertyElement; 4 | import org.pojomatic.formatter.EnhancedPojoFormatter; 5 | import org.pojomatic.formatter.PojoFormatter; 6 | 7 | @Deprecated 8 | public class EnhancedPojoFormatterWrapper implements EnhancedPojoFormatter { 9 | private final PojoFormatter delegate; 10 | 11 | public EnhancedPojoFormatterWrapper(PojoFormatter delegate) { 12 | this.delegate = delegate; 13 | } 14 | 15 | @Override 16 | public String getToStringPrefix(Class pojoClass) { 17 | return delegate.getToStringPrefix(pojoClass); 18 | } 19 | 20 | @Override 21 | public String getToStringSuffix(Class pojoClass) { 22 | return delegate.getToStringSuffix(pojoClass); 23 | } 24 | 25 | @Override 26 | public String getPropertyPrefix(PropertyElement property) { 27 | return delegate.getPropertyPrefix(property); 28 | } 29 | 30 | @Override 31 | public String getPropertySuffix(PropertyElement property) { 32 | return delegate.getPropertySuffix(property); 33 | } 34 | 35 | @Override 36 | public void appendToStringPrefix(StringBuilder builder, Class pojoClass) { 37 | builder.append(getToStringPrefix(pojoClass)); 38 | } 39 | 40 | @Override 41 | public void appendToStringSuffix(StringBuilder builder, Class pojoClass) { 42 | builder.append(getToStringSuffix(pojoClass)); 43 | } 44 | 45 | @Override 46 | public void appendPropertyPrefix(StringBuilder builder, PropertyElement property) { 47 | builder.append(getPropertyPrefix(property)); 48 | } 49 | 50 | @Override 51 | public void appendPropertySuffix(StringBuilder builder, PropertyElement property) { 52 | builder.append(getPropertySuffix(property)); 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/PropertyAccessor.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | public class PropertyAccessor extends AbstractPropertyElement { 6 | private final static String GET = "get", IS = "is"; 7 | 8 | public PropertyAccessor(Method method, String name) { 9 | super(method, name.length() == 0 ? getName(method) : name); 10 | } 11 | 12 | private static String getName(Method method) { 13 | String methodName = method.getName(); 14 | if (isPrefixedWith(methodName, GET)) { 15 | return decapitalize(methodName.substring(GET.length())); 16 | } 17 | else if (isBoolean(method.getReturnType()) && isPrefixedWith(methodName, IS)) { 18 | return decapitalize(methodName.substring(IS.length())); 19 | } 20 | else { 21 | return methodName; 22 | } 23 | } 24 | 25 | private static boolean isBoolean(Class clazz) { 26 | return Boolean.class.equals(clazz) || Boolean.TYPE.equals(clazz); 27 | } 28 | 29 | private static boolean isPrefixedWith(String name, String prefix) { 30 | return name.length() > prefix.length() 31 | && name.startsWith(prefix) 32 | && Character.isUpperCase(name.charAt(prefix.length())); 33 | } 34 | 35 | @Override 36 | public String getElementName() { 37 | return element.getName(); 38 | } 39 | 40 | @Override 41 | public String getType() { 42 | return "method"; 43 | } 44 | 45 | @Override 46 | public Class getPropertyType() { 47 | return element.getReturnType(); 48 | } 49 | 50 | private static String decapitalize(String name) { 51 | if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && 52 | Character.isUpperCase(name.charAt(0))){ 53 | return name; 54 | } 55 | char chars[] = name.toCharArray(); 56 | chars[0] = Character.toLowerCase(chars[0]); 57 | return new String(chars); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/formatter/DefaultPojoFormatterTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import org.testng.annotations.Test; 4 | import org.testng.annotations.BeforeMethod; 5 | import static org.testng.Assert.*; 6 | import org.pojomatic.PropertyElement; 7 | import org.pojomatic.TestUtils; 8 | 9 | @Deprecated 10 | public class DefaultPojoFormatterTest { 11 | 12 | private DefaultPojoFormatter formatter; 13 | 14 | private final static class Foo { 15 | @SuppressWarnings("unused") 16 | private String firstName, lastName, age; 17 | } 18 | 19 | private final static PropertyElement FIRST_NAME_FIELD, LAST_NAME_FIELD, AGE_FIELD; 20 | static { 21 | try { 22 | FIRST_NAME_FIELD = TestUtils.field(Foo.class, "firstName"); 23 | LAST_NAME_FIELD = TestUtils.field(Foo.class, "lastName"); 24 | AGE_FIELD = TestUtils.field(Foo.class, "age"); 25 | } 26 | catch (Exception e) { 27 | throw new RuntimeException(e); 28 | } 29 | } 30 | 31 | @BeforeMethod 32 | public void setUp() { 33 | formatter = new DefaultPojoFormatter(); 34 | } 35 | 36 | @Test 37 | public void testGetPropertyPrefix() { 38 | assertEquals(formatter.getPropertyPrefix(FIRST_NAME_FIELD), "firstName: {"); 39 | assertEquals(formatter.getPropertyPrefix(LAST_NAME_FIELD), ", lastName: {"); 40 | assertEquals(formatter.getPropertyPrefix(AGE_FIELD), ", age: {"); 41 | } 42 | 43 | @Test 44 | public void testGetPropertySuffix() { 45 | assertEquals(formatter.getPropertySuffix(FIRST_NAME_FIELD), "}"); 46 | assertEquals(formatter.getPropertySuffix(LAST_NAME_FIELD), "}"); 47 | assertEquals(formatter.getPropertySuffix(AGE_FIELD), "}"); 48 | } 49 | 50 | @Test 51 | public void testGetToStringPrefix() { 52 | assertEquals(formatter.getToStringPrefix(Integer.class), "Integer{"); 53 | } 54 | 55 | @Test 56 | public void testGetToStringSuffix() { 57 | assertEquals(formatter.getToStringSuffix(Integer.class), "}"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/factory/PojoDescriptor.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal.factory; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import org.pojomatic.annotations.AutoDetectPolicy; 7 | 8 | public class PojoDescriptor { 9 | public final String className; 10 | public final String packageName; 11 | public final Access access; 12 | public final PojoDescriptor parent; 13 | public final List properties; 14 | public AutoDetectPolicy autoDetectPolicy; 15 | 16 | public PojoDescriptor(PropertyDescriptor... properties) { 17 | this("Pojo", properties); 18 | } 19 | 20 | public PojoDescriptor(String className, PropertyDescriptor... properties) { 21 | this("pojos", className, properties); 22 | } 23 | 24 | public PojoDescriptor(String packageName, String className, PropertyDescriptor... properties) { 25 | this(packageName, className, Access.PUBLIC, properties); 26 | } 27 | 28 | public PojoDescriptor(String packageName, String className, Access access, PropertyDescriptor... properties) { 29 | this(packageName, className, access, null, properties); 30 | } 31 | 32 | public PojoDescriptor(String packageName, String className, Access access, PojoDescriptor parent, PropertyDescriptor... properties) { 33 | this.packageName = packageName; 34 | this.className = className; 35 | this.access = access; 36 | this.parent = parent; 37 | this.properties = Arrays.asList(properties); 38 | } 39 | 40 | public PojoDescriptor withAutoDetectPolicy(AutoDetectPolicy autoDetectPolicy) { 41 | this.autoDetectPolicy = autoDetectPolicy; 42 | return this; 43 | } 44 | 45 | public String qualifiedName() { 46 | return packageName + "." + className; 47 | } 48 | 49 | public String internalName() { 50 | return packageName + "/" + className; 51 | } 52 | 53 | public String parentInternalName() { 54 | return parent == null ? "java/lang/Object" : parent.internalName(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/PropertyFilterTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import static org.testng.Assert.*; 4 | 5 | import org.testng.annotations.Test; 6 | 7 | import java.util.Collections; 8 | import java.util.EnumSet; 9 | 10 | import org.pojomatic.annotations.DefaultPojomaticPolicy; 11 | import org.pojomatic.annotations.PojomaticPolicy; 12 | 13 | public class PropertyFilterTest { 14 | private static final Iterable ALL_BUT_DEFAULT = 15 | EnumSet.complementOf(EnumSet.of(PojomaticPolicy.DEFAULT)); 16 | 17 | @Test public void testGetRolesWithNoClassPolicy() { 18 | for (PojomaticPolicy policy: ALL_BUT_DEFAULT) { 19 | assertEquals(PropertyFilter.getRoles(policy, null), policy.getRoles()); 20 | } 21 | } 22 | 23 | @Test public void testGetRolesWithPropertyPolicyAndClassPolicy() { 24 | for (PojomaticPolicy policy: ALL_BUT_DEFAULT) { 25 | for (DefaultPojomaticPolicy defaultPolicy: DefaultPojomaticPolicy.values()) { 26 | assertEquals(PropertyFilter.getRoles(policy, defaultPolicy), policy.getRoles()); 27 | } 28 | } 29 | } 30 | 31 | @Test public void testGetRolesWithDefaultPropertyPolicyAndNoClassPolicy() { 32 | assertEquals(PropertyFilter.getRoles(PojomaticPolicy.DEFAULT, null), PojomaticPolicy.ALL.getRoles()); 33 | } 34 | 35 | @Test public void testGetRolesWithDefaultPropertyPolicyAndClassPolicy() { 36 | for (DefaultPojomaticPolicy defaultPolicy: DefaultPojomaticPolicy.values()) { 37 | assertEquals(PropertyFilter.getRoles(PojomaticPolicy.DEFAULT, defaultPolicy), defaultPolicy.getRoles()); 38 | } 39 | } 40 | 41 | @Test public void testGetRolesWithOnlyClassPolicy() { 42 | for (DefaultPojomaticPolicy defaultPolicy: DefaultPojomaticPolicy.values()) { 43 | assertEquals(PropertyFilter.getRoles(null, defaultPolicy), defaultPolicy.getRoles()); 44 | } 45 | } 46 | 47 | @Test 48 | public void testGetRolesWithNoPolicy() { 49 | assertEquals(PropertyFilter.getRoles(null, null), Collections.EMPTY_SET); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/LocalVariable.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import org.objectweb.asm.Label; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.Type; 7 | 8 | class LocalVariable { 9 | private final String name; 10 | private final String signature; 11 | private Label scopeStart; 12 | private Label scopeEnd; 13 | private final int position; 14 | private final Type type; 15 | 16 | public LocalVariable(String name, Class type, String signature, int position) { 17 | super(); 18 | this.name = name; 19 | this.type = Type.getType(Type.getDescriptor(type)); 20 | this.signature = signature; 21 | this.position = position; 22 | } 23 | 24 | public LocalVariable(String name, String typeDescriptor, String signature, int position) { 25 | super(); 26 | this.name = name; 27 | this.type = Type.getType(typeDescriptor); 28 | this.signature = signature; 29 | this.position = position; 30 | } 31 | 32 | public LocalVariable withScope(Label start, Label end) { 33 | if (this.scopeStart != null) { 34 | throw new IllegalStateException("scopeStart already set"); 35 | } 36 | if (this.scopeEnd != null) { 37 | throw new IllegalStateException("scopeEnd already set"); 38 | } 39 | this.scopeStart = start; 40 | this.scopeEnd = end; 41 | return this; 42 | } 43 | 44 | public void acceptLocalVariable(MethodVisitor mv) { 45 | if (scopeStart == null) { 46 | throw new IllegalStateException("scopeStart not set"); 47 | } 48 | if (scopeEnd == null) { 49 | throw new IllegalStateException("scopeEnd not set"); 50 | } 51 | mv.visitLocalVariable(name, type.getDescriptor(), signature, scopeStart, scopeEnd, position); 52 | } 53 | 54 | public void acceptStore(MethodVisitor mv) { 55 | mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), position); 56 | } 57 | 58 | public void acceptLoad(MethodVisitor mv) { 59 | mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), position); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/main/java/org/pojomatic/junit/PojomaticAssert.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.junit; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.test.AssertUtils; 5 | 6 | /** 7 | * Pojomatic-related JUnit-style assertion methods useful for writing tests. 8 | * @see org.junit.Assert 9 | */ 10 | public class PojomaticAssert { 11 | 12 | /** 13 | * Asserts that two objects are either both null or are equal according to 14 | * {@link Object#equals(Object)}. If not, an {@code AssertionError} is thrown. If the objects are 15 | * not equal, but the types of two objects are compatible for equality, then the differences as 16 | * determined by {@link Pojomatic#diff(Object, Object)} are included in the failure message. 17 | * 18 | * @param expected the expected object 19 | * @param actual the object which should be tested to equal the expected object 20 | * @throws AssertionError if the objects are not equal. 21 | * @see #assertEqualsWithDiff(String, Object, Object) 22 | */ 23 | public static void assertEqualsWithDiff(Object expected, Object actual) { 24 | assertEqualsWithDiff(null, expected, actual); 25 | } 26 | 27 | /** 28 | * Asserts that two objects are either both null or are equal according to 29 | * {@link Object#equals(Object)}. If not, an {@code AssertionError} is thrown. If the objects are 30 | * not equal, but the types of two objects are compatible for equality, then the differences as 31 | * determined by {@link Pojomatic#diff(Object, Object)} are included in the failure message. 32 | * @param message a message (possibly {@code null}) to include at the beginning of the 33 | * {@code AssertionError} message. 34 | * @param expected the expected object 35 | * @param actual the object which should be tested to equal the expected object 36 | * 37 | * @throws AssertionError if the objects are not equal. 38 | */ 39 | public static void assertEqualsWithDiff(String message, Object expected, Object actual) { 40 | AssertUtils.assertEquals(message, expected, actual); 41 | } 42 | 43 | private PojomaticAssert() {} 44 | } 45 | -------------------------------------------------------------------------------- /modular-test-project/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | org.pojomatic 4 | modular-test-project 5 | master-SNAPSHOT 6 | 7 | 8 | org.pojomatic 9 | pojomatic 10 | master-SNAPSHOT 11 | 12 | 13 | junit 14 | junit 15 | 4.12 16 | 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 3.8.0 24 | 25 | 26 | 1.9 27 | 1.9 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-install-plugin 33 | 2.5.2 34 | 35 | true 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-site-plugin 41 | 3.1 42 | 43 | true 44 | true 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-surefire-plugin 53 | 2.22.0 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/SelfPopulatingMap.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | import java.util.concurrent.ConcurrentMap; 5 | 6 | /** 7 | * A thread-safe "map" which generates values on demand, with the guarantee that no more than one 8 | * value will be auto-created for a given key. 9 | * Classes extending this class should override {@link #create(Object)}. 10 | * @param the key type 11 | * @param the value type 12 | */ 13 | public abstract class SelfPopulatingMap { 14 | 15 | public V get(final K key) { 16 | V value = valueMap.get(key); 17 | if (value == null) { 18 | final Object mutex = new Object(); 19 | synchronized (mutex) { 20 | Object existingMutex = mutexMap.putIfAbsent(key, mutex); 21 | if (existingMutex == null) { 22 | return tryCreatingValue(key); 23 | } 24 | else { 25 | synchronized (existingMutex) { 26 | V oldValue = valueMap.get(key); 27 | // if the previous holder of this mutex failed to create a value, we'll give it a shot. 28 | return oldValue != null ? oldValue : tryCreatingValue(key); 29 | } 30 | } 31 | } 32 | } 33 | return value; 34 | } 35 | 36 | /** 37 | * Create a value for a key. This will be called by {@link #get(Object)} when there is not 38 | * already an existing value, and no other thread is already creating a value for that key. 39 | * The value returned must not be null. 40 | * @param key the key to create the value for 41 | * @return the value 42 | */ 43 | protected abstract V create(K key); 44 | 45 | private V tryCreatingValue(K key) { 46 | V value = create(key); 47 | valueMap.put(key, value); 48 | return value; 49 | } 50 | 51 | /** 52 | * The values held by this map. 53 | */ 54 | private final ConcurrentMap valueMap = new ConcurrentHashMap<>(); 55 | 56 | /** 57 | * Mutexes created on demand to ensure that only a single value is created for each key. 58 | */ 59 | private final ConcurrentMap mutexMap = new ConcurrentHashMap<>(); 60 | } 61 | 62 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/BaseType.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.Objects; 7 | 8 | public enum BaseType implements Type { 9 | BOOLEAN(boolean.class, false, true), 10 | BYTE(byte.class, (byte) 0, (byte) 1, (byte) -1), 11 | CHAR(char.class, 'a', '\u0000', '\u009a', '\u1234') { 12 | @Override 13 | public String toString(Object value) { 14 | switch ((char) value) { 15 | case 'a': return "'a'"; 16 | case '\u0000': return "'\\u0000'"; 17 | case '\u009a': return "'\\u009a'"; 18 | case '\u1234': return "'" + '\u1234' + "'"; 19 | default: 20 | throw new IllegalArgumentException("unexpected character " + value); 21 | } 22 | } 23 | }, 24 | SHORT(short.class, (short) 0, (short) -1, (short) 1), 25 | INT(int.class, -12314, 0, 2352362), 26 | LONG(long.class, 0L, -23413513515L, 2L << 16 + 5L), 27 | FLOAT(float.class, 0f, 123151f, -151f), 28 | DOUBLE(double.class, 0.0, -141.2, 12351351.26), 29 | OBJECT(Object.class, null, "", "hello", new String("hello"), 29), 30 | ; 31 | 32 | private final Class clazz; 33 | private final List sampleValues; 34 | 35 | @SafeVarargs 36 | private BaseType(Class clazz, T... values) { 37 | this.clazz = clazz; 38 | this.sampleValues = Collections.unmodifiableList(Arrays.asList(values)); 39 | } 40 | 41 | @Override 42 | public Class getClazz() { 43 | return clazz; 44 | } 45 | 46 | @Override 47 | public List getSampleValues() { 48 | return sampleValues; 49 | } 50 | 51 | @Override 52 | public int hashCode(Object value) { 53 | return Objects.hashCode(value); 54 | } 55 | 56 | @Override 57 | public int deepHashCode(Object value) { 58 | return hashCode(value); 59 | } 60 | 61 | @Override 62 | public String toString(Object value) { 63 | return Objects.toString(value); 64 | } 65 | 66 | @Override 67 | public String deepToString(Object value) { 68 | return toString(value); 69 | } 70 | 71 | @Override 72 | public int arrayDepth() { 73 | return 0; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/formatter/AccountNumberFormatter.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.pojomatic.annotations.Property; 6 | 7 | /** 8 | * A property formatter which shows only the last 4 characters of the string representation of the 9 | * {@link Property}, with all others replaced by an asterisk ('*'). 10 | * Useful for credit card numbers, social security numbers, etc. 11 | *

12 | * For example, a 16 character {@code String} representing a credit card number would 13 | * be formatted as "************1234". 14 | * 15 | * @deprecated While this formatter can prevent the toString representation of an object including a full account 16 | * number, the full number can still be visible in heap dumps. A more secure approach is to avoid storing plaintext 17 | * private account numbers in memory at all (or at most, for as long as it takes to encrypt them). This formatter will 18 | * be removed in a future release of Pojomatic. Clients who feel they still need this functionality should implement it 19 | * themselves. 20 | */ 21 | @Deprecated 22 | public class AccountNumberFormatter extends DefaultPropertyFormatter { 23 | private static final int DEFAULT_PLAINTEXT_CHARS = 4; 24 | private static final int DEFAULT_FILL_CHAR = '*'; 25 | 26 | private int plaintextChars = DEFAULT_PLAINTEXT_CHARS; 27 | private char fillChar = DEFAULT_FILL_CHAR; 28 | 29 | @Override 30 | public String format(Object value) { 31 | String rep = super.format(value); 32 | int repLength = rep.length(); 33 | if (repLength <= getPlaintextChars()) { 34 | return rep; 35 | } else { 36 | char[] repChars = rep.toCharArray(); 37 | Arrays.fill(repChars, 0, repLength - getPlaintextChars(), getFillChar()); 38 | return String.valueOf(repChars); 39 | } 40 | } 41 | 42 | protected int getPlaintextChars() { 43 | return plaintextChars; 44 | } 45 | 46 | protected void setPlaintextChars(int plaintextChars) { 47 | this.plaintextChars = plaintextChars; 48 | } 49 | 50 | protected char getFillChar() { 51 | return fillChar; 52 | } 53 | 54 | protected void setFillChar(char fillChar) { 55 | this.fillChar = fillChar; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/SelfPopulatingMapTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import static org.testng.Assert.*; 4 | import org.testng.annotations.Test; 5 | import java.util.concurrent.atomic.AtomicBoolean; 6 | 7 | public class SelfPopulatingMapTest { 8 | /** 9 | * Test case which exposes a subtle threading bug 10 | * @throws Exception 11 | */ 12 | @Test public void testThreading() throws Exception { 13 | final String token = "token"; 14 | final SelfPopulatingMap selfPopulatingMap = 15 | new SelfPopulatingMap() { 16 | @Override protected String create(String key) { 17 | try { 18 | Thread.sleep(10); // ensure that two threads have time to collide. 19 | } 20 | catch (InterruptedException e) {} 21 | return new String(key); 22 | } 23 | }; 24 | 25 | int numThreads = 2; 26 | Thread[] threads = new Thread[numThreads]; 27 | final String[] results = new String[numThreads]; 28 | for (int i = 0; i < threads.length; i++) { 29 | final int threadNumber = i; 30 | threads[i] = new Thread() { 31 | @Override public void run() { 32 | results[threadNumber] = selfPopulatingMap.get(token); 33 | } 34 | }; 35 | } 36 | for (Thread t: threads) { 37 | t.start(); 38 | } 39 | for (Thread t: threads) { 40 | t.join(); 41 | } 42 | assertSame(results[1], results[0]); 43 | } 44 | 45 | @Test 46 | public void testBadConstructionFirstTime() { 47 | final AtomicBoolean firstTime = new AtomicBoolean(false); 48 | final SelfPopulatingMap selfPopulatingMap = 49 | new SelfPopulatingMap() { 50 | @Override protected String create(String key) { 51 | if (firstTime.getAndSet(true)) { 52 | return new String(key); 53 | } 54 | else { 55 | throw new RuntimeException("first"); 56 | } 57 | } 58 | }; 59 | 60 | try { 61 | selfPopulatingMap.get("x"); 62 | fail("Exception expected"); 63 | } 64 | catch(RuntimeException e) { 65 | assertEquals(e.getMessage(), "first"); 66 | } 67 | 68 | assertEquals(selfPopulatingMap.get("x"), "x"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/main/java/org/pojomatic/testng/PojomaticAssert.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.testng; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.test.AssertUtils; 5 | 6 | /** 7 | * Pojomatic-related TestNG-style assertion methods useful for writing tests. 8 | * @see org.testng.Assert 9 | */ 10 | public class PojomaticAssert { 11 | 12 | /** 13 | * Asserts that two objects are either both null or are equal according to 14 | * {@link Object#equals(Object)}. If not, an {@code AssertionError} is thrown. If the objects are 15 | * not equal, but the types of two objects are compatible for equality, then the differences as 16 | * determined by {@link Pojomatic#diff(Object, Object)} are included in the failure message. 17 | * 18 | * @param expected the expected object 19 | * @param actual the object which should be tested to equal the expected object 20 | * @throws AssertionError if the objects are not equal, with details of the differences 21 | * included in the message 22 | * @see #assertEqualsWithDiff(Object, Object, String) 23 | */ 24 | public static void assertEqualsWithDiff(Object actual, Object expected) { 25 | assertEqualsWithDiff(actual, expected, null); 26 | } 27 | 28 | /** 29 | * Asserts that two objects are either both null or are equal according to 30 | * {@link Object#equals(Object)}. If not, an {@code AssertionError} is thrown. If the objects are 31 | * not equal, but the types of two objects are compatible for equality, then the differences as 32 | * determined by {@link Pojomatic#diff(Object, Object)} are included in the failure message. 33 | * 34 | * @param expected the expected object 35 | * @param actual the object which should be tested to equal the expected object 36 | * @param message an optional message provided along with the diff if the objects are not equal 37 | * @throws AssertionError if the objects are not equal, with details of the differences 38 | * included in the message 39 | */ 40 | public static void assertEqualsWithDiff(Object actual, Object expected, String message) { 41 | //the arguments are passed as follows according to display order for a potential error message 42 | AssertUtils.assertEquals(message, expected, actual); 43 | } 44 | 45 | private PojomaticAssert() {} 46 | } 47 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/formatter/DefaultEnhancedPojoFormatterTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import org.testng.annotations.Test; 4 | import org.testng.annotations.BeforeMethod; 5 | import static org.testng.Assert.*; 6 | import org.pojomatic.PropertyElement; 7 | import org.pojomatic.TestUtils; 8 | 9 | public class DefaultEnhancedPojoFormatterTest { 10 | 11 | private DefaultEnhancedPojoFormatter formatter; 12 | private StringBuilder builder; 13 | 14 | private final static class Foo { 15 | @SuppressWarnings("unused") 16 | private String firstName, lastName, age; 17 | } 18 | 19 | private final static PropertyElement FIRST_NAME_FIELD, LAST_NAME_FIELD, AGE_FIELD; 20 | static { 21 | try { 22 | FIRST_NAME_FIELD = TestUtils.field(Foo.class, "firstName"); 23 | LAST_NAME_FIELD = TestUtils.field(Foo.class, "lastName"); 24 | AGE_FIELD = TestUtils.field(Foo.class, "age"); 25 | } 26 | catch (Exception e) { 27 | throw new RuntimeException(e); 28 | } 29 | } 30 | 31 | @BeforeMethod 32 | public void setUp() { 33 | formatter = new DefaultEnhancedPojoFormatter(); 34 | builder = new StringBuilder(); 35 | } 36 | 37 | @Test 38 | public void testGetPropertyPrefix() { 39 | formatter.appendPropertyPrefix(builder, FIRST_NAME_FIELD); 40 | builder.append('|'); 41 | formatter.appendPropertyPrefix(builder, LAST_NAME_FIELD); 42 | builder.append('|'); 43 | formatter.appendPropertyPrefix(builder, AGE_FIELD); 44 | 45 | assertFormatted("firstName: {|, lastName: {|, age: {"); 46 | } 47 | 48 | @Test 49 | public void testGetPropertySuffix() { 50 | formatter.appendPropertySuffix(builder, FIRST_NAME_FIELD); 51 | builder.append('|'); 52 | formatter.appendPropertySuffix(builder, LAST_NAME_FIELD); 53 | builder.append('|'); 54 | formatter.appendPropertySuffix(builder, AGE_FIELD); 55 | assertFormatted("}|}|}"); 56 | } 57 | 58 | @Test 59 | public void testGetToStringPrefix() { 60 | formatter.appendToStringPrefix(builder, Integer.class); 61 | assertFormatted("Integer{"); 62 | } 63 | 64 | @Test 65 | public void testGetToStringSuffix() { 66 | formatter.appendToStringSuffix(builder, Integer.class); 67 | assertFormatted("}"); 68 | } 69 | 70 | private void assertFormatted(String expected) { 71 | assertEquals(builder.toString(), expected); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/formatter/DefaultPropertyFormatterTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import org.testng.annotations.Test; 4 | import org.testng.annotations.BeforeMethod; 5 | import static org.testng.Assert.*; 6 | 7 | import java.util.Arrays; 8 | 9 | @Deprecated 10 | public class DefaultPropertyFormatterTest { 11 | private PropertyFormatter formatter; 12 | 13 | @BeforeMethod 14 | public void setUp() { 15 | formatter = new DefaultPropertyFormatter(); 16 | formatter.initialize(null); 17 | } 18 | 19 | @Test public void testFormat() { 20 | assertEquals(formatter.format(7), "7"); 21 | } 22 | 23 | @Test public void testFormatNull() { 24 | assertEquals(formatter.format(null), "null"); 25 | } 26 | 27 | @Test public void testFormatList() { 28 | assertEquals(formatter.format(Arrays.asList(5, 7)), "[5, 7]"); 29 | } 30 | 31 | @Test public void testFormatArrayOfObjects() { 32 | assertEquals(formatter.format(new Integer[] {5, 7}), "[5, 7]"); 33 | } 34 | 35 | @Test public void testFormatArrayOfBooleans() { 36 | assertEquals(formatter.format(new boolean[] {true, false}), "[true, false]"); 37 | } 38 | 39 | @Test public void testFormatArrayOfBytes() { 40 | assertEquals(formatter.format(new byte[] {5, 7}), "[5, 7]"); 41 | } 42 | 43 | @Test public void testFormatArrayOfChars() { 44 | assertEquals(formatter.format(new char[] {5, 'b'}), "['0x5', 'b']"); 45 | } 46 | 47 | @Test public void testFormatArrayOfShorts() { 48 | assertEquals(formatter.format(new short[] {5, 7}), "[5, 7]"); 49 | } 50 | 51 | @Test public void testFormatArrayOfInts() { 52 | assertEquals(formatter.format(new int[] {5, 7}), "[5, 7]"); 53 | } 54 | 55 | @Test public void testFormatArrayOfLongs() { 56 | assertEquals(formatter.format(new long[] {5, 7}), "[5, 7]"); 57 | } 58 | 59 | @Test public void testFormatArrayOfFloats() { 60 | assertEquals(formatter.format(new float[] {5, 7}), "[5.0, 7.0]"); 61 | } 62 | 63 | @Test public void testFormatArrayOfDoubles() { 64 | assertEquals(formatter.format(new double[] {5, 7}), "[5.0, 7.0]"); 65 | } 66 | 67 | @Test public void testFormatDoubleArray() { 68 | assertEquals(formatter.format(new Integer[][] {new Integer[] { 1, 2 }, new Integer[] {3, 4} }), "[[1, 2], [3, 4]]"); 69 | } 70 | 71 | @Test public void testFormatDoubleArrayOfPrimitives() { 72 | assertEquals(formatter.format(new int[][] {new int[] { 1, 2 }, new int[] {3, 4} }), "[[1, 2], [3, 4]]"); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/DefaultPojomaticPolicy.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.annotations; 2 | 3 | import java.util.Collections; 4 | import java.util.EnumSet; 5 | import java.util.Set;import java.util.Arrays; 6 | 7 | import org.pojomatic.Pojomatic; 8 | import org.pojomatic.internal.PropertyRole; 9 | 10 | /** 11 | * A policy for defining which sets of {@link Pojomatic} operations 12 | * ({@code equals}, {@code hashCode} and {@code toString}) should use all properties by default. 13 | * This is set class-wide using {@link AutoProperty}. 14 | * @see PojomaticPolicy 15 | */ 16 | public enum DefaultPojomaticPolicy { 17 | 18 | /** 19 | * Use all properties for both {@code hashCode} and {@code equals} by default. 20 | * Anything included in {@code public int hashCode()} should also be included in 21 | * {@code public boolean equals(Object)} to preserve the general 22 | * contract of {@link Object#hashCode()}. 23 | * 24 | * @see Object#hashCode() 25 | * @see Object#equals(Object) 26 | */ 27 | HASHCODE_EQUALS(PropertyRole.HASH_CODE, PropertyRole.EQUALS), 28 | 29 | /** 30 | * Use all properties for {@code equals} only by default. 31 | * 32 | * @see Object#equals(Object) 33 | */ 34 | EQUALS(PropertyRole.EQUALS), 35 | 36 | /** 37 | * Use all properties for both {@code equals} and {@code toString} by default. 38 | * 39 | * @see Object#equals(Object) 40 | * @see Object#toString() 41 | */ 42 | EQUALS_TO_STRING(PropertyRole.EQUALS, PropertyRole.TO_STRING), 43 | 44 | /** 45 | * Use all properties for both {@code toString} only by default. 46 | * 47 | * @see Object#toString() 48 | */ 49 | TO_STRING(PropertyRole.TO_STRING), 50 | 51 | /** 52 | * Use all properties for {@code hashCode}, {@code equals} and {@code toString} by default. 53 | */ 54 | ALL(PropertyRole.EQUALS, PropertyRole.HASH_CODE, PropertyRole.TO_STRING), 55 | 56 | /** 57 | * Do not use any properties for any of {@code hashCode}, {@code equals} or {@code toString} 58 | * by default. 59 | */ 60 | NONE(); 61 | 62 | /** 63 | * @return the roles this specified by this policy. 64 | */ 65 | public Set getRoles() { 66 | return roles; 67 | } 68 | 69 | private DefaultPojomaticPolicy(PropertyRole... roles) { 70 | Set roleSet = EnumSet.noneOf(PropertyRole.class); 71 | roleSet.addAll(Arrays.asList(roles)); 72 | this.roles = Collections.unmodifiableSet(roleSet); 73 | } 74 | 75 | private final Set roles; 76 | } 77 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/formatter/DefaultEnhancedPojoFormatter.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.PropertyElement; 5 | 6 | /** 7 | * Default formatter for classes that use {@link Pojomatic}. This implementation first presents 8 | * the class name, and then each property in turn, separated by commas, using braces to indicate 9 | * nesting. 10 | *

11 | * For example, if a class Person has two properties, firstName and LastName, and these properties 12 | * are using {@link DefaultPropertyFormatter}, then the Person 13 | * instance representing Joe Blow would be represented as 14 | * "Person{firstName: {Joe}, lastName: {Blow}}" 15 | * 16 | * @since 2.0 17 | */ 18 | public class DefaultEnhancedPojoFormatter implements EnhancedPojoFormatter { 19 | private boolean firstPropertyPrinted = false; 20 | 21 | @Override 22 | public final String getPropertyPrefix(PropertyElement property) { 23 | StringBuilder builder = new StringBuilder(); 24 | appendPropertyPrefix(builder, property); 25 | return builder.toString(); 26 | } 27 | 28 | @Override 29 | public final String getPropertySuffix(PropertyElement property) { 30 | StringBuilder builder = new StringBuilder(); 31 | appendPropertySuffix(builder, property); 32 | return builder.toString(); 33 | } 34 | 35 | @Override 36 | public final String getToStringPrefix(Class pojoClass) { 37 | StringBuilder builder = new StringBuilder(); 38 | appendToStringPrefix(builder, pojoClass); 39 | return builder.toString(); 40 | } 41 | 42 | @Override 43 | public final String getToStringSuffix(Class pojoClass) { 44 | StringBuilder builder = new StringBuilder(); 45 | appendToStringSuffix(builder, pojoClass); 46 | return builder.toString(); 47 | } 48 | 49 | @Override 50 | public void appendToStringPrefix(StringBuilder builder, Class pojoClass) { 51 | builder.append(pojoClass.getSimpleName()).append('{'); 52 | } 53 | 54 | @Override 55 | public void appendToStringSuffix(StringBuilder builder, Class pojoClass) { 56 | builder.append('}'); 57 | } 58 | 59 | @Override 60 | public void appendPropertyPrefix(StringBuilder builder, PropertyElement property) { 61 | if (firstPropertyPrinted) { 62 | builder.append(", "); 63 | } 64 | else { 65 | firstPropertyPrinted = true; 66 | } 67 | builder.append(property.getName()).append(": {"); 68 | } 69 | 70 | @Override 71 | public void appendPropertySuffix(StringBuilder builder, PropertyElement property) { 72 | builder.append('}'); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/formatter/EnhancedPojoFormatter.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import org.pojomatic.Pojomator; 4 | import org.pojomatic.PropertyElement; 5 | 6 | /** 7 | * A formatter to aid in creating a {@code String} representation of a POJO. 8 | * 9 | * Any implementation of {@code EnhancedPojoFormatter} must have a public no-argument constructor. A new instance will 10 | * be created for each time that {@link Pojomator#doToString(Object)} is called. Consequently, implementations do 11 | * not need to be thread safe. 12 | * 13 | * @since 2.0 14 | * @see DefaultEnhancedPojoFormatter 15 | */ 16 | @SuppressWarnings("deprecation") 17 | public interface EnhancedPojoFormatter extends PojoFormatter { 18 | /** 19 | * Append the {@code String} which should appear at the beginning of the result of 20 | * {@code toString()} to the supplied StringBuilder. 21 | * 22 | * @param builder the builder to append to. 23 | * @param pojoClass the class for which {@code toString()} is being called 24 | * @see Object#toString() 25 | */ 26 | void appendToStringPrefix(StringBuilder builder, Class pojoClass); 27 | 28 | /** 29 | * Append the {@code String} which should appear at the end of the result of 30 | * {@code toString()} to the supplied StringBuilder. 31 | * 32 | * @param builder the builder to append to. 33 | * @param pojoClass the class for which {@code toString()} is being called 34 | * @see Object#toString() 35 | */ 36 | void appendToStringSuffix(StringBuilder builder, Class pojoClass); 37 | 38 | /** 39 | * Append the {@code String} prefix for a given {@code PropertyElement} to the supplied 40 | * StringBuilder. This method will be called once for each property used in the result of 41 | * {@code toString()}, in the order in which those properties will appear in that result, 42 | * and before the call to {@link PropertyFormatter#format(Object)} for the property's value. 43 | * 44 | * @param builder the builder to append to. 45 | * @param property the property for which to generate a prefix 46 | */ 47 | void appendPropertyPrefix(StringBuilder builder, PropertyElement property); 48 | 49 | /** 50 | * Append the {@code String} suffix for a given {@code PropertyElement} to the supplied 51 | * StringBuilder. This method will be called once after each call to 52 | * {@link PropertyFormatter#format(Object)} for the property's value. 53 | * 54 | * @param builder the builder to append to. 55 | * @param property the property for which to generate a suffix 56 | */ 57 | void appendPropertySuffix(StringBuilder builder, PropertyElement property); 58 | } 59 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/annotations/PojomaticPolicy.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.annotations; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.EnumSet; 6 | import java.util.Set; 7 | 8 | import org.pojomatic.Pojomatic; 9 | import org.pojomatic.internal.PropertyRole; 10 | 11 | /** 12 | * A policy for defining which sets of {@link Pojomatic} operations 13 | * ({@code equals}, {@code hashCode} and {@code toString}) should use a property. 14 | * This is set using {@link Property}. 15 | * @see DefaultPojomaticPolicy 16 | */ 17 | public enum PojomaticPolicy { 18 | 19 | /** 20 | * Use the property for both {@code hashCode} and {@code equals}. 21 | * Anything included in {@code public int hashCode()} should also be included in 22 | * {@code public boolean equals(Object)} to preserve the general 23 | * contract of {@link Object#hashCode()}. 24 | * 25 | * @see Object#hashCode() 26 | * @see Object#equals(Object) 27 | */ 28 | HASHCODE_EQUALS(PropertyRole.HASH_CODE, PropertyRole.EQUALS), 29 | 30 | /** 31 | * Use the property for both {@code equals} and {@code toString}. 32 | * 33 | * @see Object#equals(Object) 34 | * @see Object#toString() 35 | */ 36 | EQUALS_TO_STRING(PropertyRole.EQUALS, PropertyRole.TO_STRING), 37 | 38 | 39 | /** 40 | * Use the property for {@code equals} only. 41 | * 42 | * @see Object#equals(Object) 43 | */ 44 | EQUALS(PropertyRole.EQUALS), 45 | 46 | /** 47 | * Use the property for both {@code toString} only. 48 | * 49 | * @see Object#toString() 50 | */ 51 | TO_STRING(PropertyRole.TO_STRING), 52 | 53 | /** 54 | * Use the property for {@code hashCode}, {@code equals} and {@code toString}. 55 | */ 56 | ALL(PropertyRole.EQUALS, PropertyRole.HASH_CODE, PropertyRole.TO_STRING), 57 | 58 | /** 59 | * Do not use the property for any of {@code hashCode}, {@code equals} or {@code toString}. 60 | */ 61 | NONE(), 62 | 63 | /** 64 | * Use the default policy specified via the {@code @AutoProperty} annotation, or 65 | * {@code ALL} if none was specified. 66 | */ 67 | DEFAULT { 68 | @Override public Set getRoles() { 69 | return null; 70 | } 71 | }; 72 | 73 | 74 | /** 75 | * @return the roles this specified by this policy. Will be {@code null} for {@code DEFAULT}. 76 | */ 77 | public Set getRoles() { 78 | return roles; 79 | } 80 | 81 | private PojomaticPolicy(PropertyRole... roles) { 82 | Set roleSet = EnumSet.noneOf(PropertyRole.class); 83 | roleSet.addAll(Arrays.asList(roles)); 84 | this.roles = Collections.unmodifiableSet(roleSet); 85 | } 86 | 87 | private final Set roles; 88 | } 89 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/TypeProviders.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | import org.testng.annotations.DataProvider; 8 | 9 | import com.google.common.base.Function; 10 | import com.google.common.collect.FluentIterable; 11 | import com.google.common.collect.Iterables; 12 | import com.google.common.collect.Sets; 13 | 14 | public class TypeProviders { 15 | @DataProvider(name = "types") 16 | public static Object[][] types() { 17 | return FluentIterable.from(Iterables.concat(simpleTypes(), simpleArrays())) 18 | .transform(ArrayWrap.INSTANCE) 19 | .toArray(Object[].class); 20 | } 21 | 22 | @DataProvider(name = "arrayTypes") 23 | public static Object[][] arrayTypes() { 24 | return annotatedTypes(simpleArrays(), doubleArrays()); 25 | } 26 | 27 | @DataProvider(name = "deepArrayTypes") 28 | public static Object[][] deepArrayTypes() { 29 | return annotatedTypes(doubleArrays()); 30 | } 31 | 32 | @SuppressWarnings("unchecked") 33 | @DataProvider(name = "annotations") 34 | public static Object[][] annotationCombinations() { 35 | return FluentIterable.from(Sets.cartesianProduct(booleans())) 36 | .transform(ListToArray.INSTANCE) 37 | .toArray(Object[].class); 38 | } 39 | 40 | @SafeVarargs 41 | @SuppressWarnings("unchecked") 42 | private static Object[][] annotatedTypes(Iterable... types) { 43 | return FluentIterable.from( 44 | Sets.cartesianProduct(Sets.newLinkedHashSet(Iterables.concat(types)), booleans())) 45 | .transform(ListToArray.INSTANCE) 46 | .toArray(Object[].class); 47 | } 48 | 49 | static Iterable simpleArrays() { 50 | return Iterables.transform(simpleTypes(), Arrayify.INSTANCE); 51 | } 52 | 53 | static Iterable doubleArrays() { 54 | return Iterables.transform(simpleArrays(), Arrayify.INSTANCE); 55 | } 56 | 57 | private static enum Arrayify implements Function { 58 | INSTANCE { @Override public Type apply(Type type) { return new ArrayType(type); } } 59 | } 60 | 61 | private static enum ArrayWrap implements Function { 62 | INSTANCE { @Override public Object[] apply(Type type) { return new Object[] { type }; } } 63 | } 64 | 65 | private static enum ListToArray implements Function, Object[]> { 66 | INSTANCE { @Override public Object[] apply(List o) { return o.toArray(); } } 67 | } 68 | 69 | private static Set booleans() { 70 | return Sets.newLinkedHashSet(Arrays.asList(false, true)); 71 | } 72 | 73 | private static Iterable simpleTypes() { 74 | return Arrays.asList(BaseType.values()); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/formatter/PojoFormatter.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import org.pojomatic.PropertyElement; 4 | import org.pojomatic.Pojomator; 5 | 6 | /** 7 | * A formatter to aid in creating a {@code String} representation of a POJO. A new instance will be 8 | * created for each time that {@link Pojomator#doToString(Object)} is called. 9 | * 10 | * @deprecated Since 2.0. Use {@link EnhancedPojoFormatter} instead. Use of this interface typically requires creating additional 11 | * StringBuidler instances. 12 | */ 13 | @Deprecated 14 | public interface PojoFormatter { 15 | 16 | /** 17 | * Get the {@code String} which should appear at the beginning of the result of 18 | * {@code toString()}. 19 | * 20 | * @param pojoClass the class for which {@code toString()} is being called 21 | * @return the prefix to appear at the beginning of the result of {@code toString()} 22 | * @see Object#toString() 23 | * 24 | * @deprecated Use {@link EnhancedPojoFormatter#appendToStringPrefix(StringBuilder, Class)} instead 25 | */ 26 | @Deprecated 27 | String getToStringPrefix(Class pojoClass); 28 | 29 | /** 30 | * Get the {@code String} which should appear at the end of the result of 31 | * {@code toString()}. 32 | * @param pojoClass the class for which {@code toString()} is being called 33 | * @return the suffix to appear at the end of the result of {@code toString()} 34 | * @see Object#toString() 35 | * 36 | * @deprecated Use {@link EnhancedPojoFormatter#appendToStringSuffix(StringBuilder, Class)} instead 37 | */ 38 | @Deprecated 39 | String getToStringSuffix(Class pojoClass); 40 | 41 | /** 42 | * Get the {@code String} prefix for a given {@code PropertyElement}. This method will be called 43 | * once for each property used in the result of {@code toString()}, in the order in which 44 | * those properties will appear in that result, and before the call to 45 | * {@link PropertyFormatter#format(Object)} for the property's value. 46 | * @param property the property for which to generate a prefix 47 | * @return the prefix for the given property 48 | * 49 | * @deprecated Use {@link EnhancedPojoFormatter#appendPropertyPrefix(StringBuilder, PropertyElement)} 50 | */ 51 | @Deprecated 52 | String getPropertyPrefix(PropertyElement property); 53 | 54 | /** 55 | * Get the {@code String} suffix for a given {@code PropertyElement}. This method will be called 56 | * once after each call to {@link PropertyFormatter#format(Object)} for the property's value. 57 | * @param property the property for which to generate a suffix 58 | * @return the suffix for the given property 59 | * 60 | * @deprecated Use {@link EnhancedPojoFormatter#appendPropertySuffix(StringBuilder, PropertyElement)} 61 | */ 62 | @Deprecated 63 | String getPropertySuffix(PropertyElement property); 64 | } 65 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/EqualsInheritanceTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import static org.testng.Assert.*; 4 | 5 | import org.testng.annotations.Test; 6 | import org.pojomatic.annotations.Property; 7 | import org.pojomatic.Pojomator; 8 | import org.pojomatic.Pojomatic; 9 | 10 | public class EqualsInheritanceTest { 11 | private static class Parent { 12 | @Property int x = 3; 13 | 14 | @Override 15 | public int hashCode() { 16 | return Pojomatic.hashCode(this); 17 | } 18 | 19 | @Override 20 | public boolean equals(Object o) { 21 | return Pojomatic.equals(this, o); 22 | } 23 | } 24 | 25 | private static Pojomator PARENT_POJOMATOR = PojomatorFactory.makePojomator(Parent.class); 26 | private static Parent PARENT = new Parent(); 27 | 28 | @Test public void testChildWithNoNewProperties() { 29 | class Child extends Parent {} 30 | Child child = new Child(); 31 | Pojomator childPojomator = PojomatorFactory.makePojomator(Child.class); 32 | 33 | assertTrue(PARENT_POJOMATOR.doEquals(PARENT, child)); 34 | assertTrue(PARENT_POJOMATOR.doEquals(child, PARENT)); 35 | assertTrue(childPojomator.doEquals(child, PARENT)); 36 | } 37 | 38 | @Test public void testChildWithNewProperty() { 39 | class Child extends Parent { @Property int y = 4; } 40 | Child child = new Child(); 41 | Pojomator childPojomator = PojomatorFactory.makePojomator(Child.class); 42 | 43 | assertFalse(PARENT.equals(child)); 44 | assertFalse(child.equals(PARENT)); 45 | assertFalse(PARENT_POJOMATOR.doEquals(PARENT, child)); 46 | assertFalse(childPojomator.doEquals(child, PARENT)); 47 | // If we explicitly use a PARENT_POJOMATOR to compare child to parent, we'll miss the additional 48 | //child property. 49 | assertTrue(PARENT_POJOMATOR.doEquals(child, PARENT)); 50 | } 51 | 52 | @Test public void testTwoChildrenWithNoNewProperties() { 53 | class Child1 extends Parent {} 54 | class Child2 extends Parent {} 55 | Child1 child1 = new Child1(); 56 | Child2 child2 = new Child2(); 57 | Pojomator childPojomator = PojomatorFactory.makePojomator(Child1.class); 58 | 59 | assertTrue(PARENT_POJOMATOR.doEquals(child1, child2)); 60 | assertTrue(childPojomator.doEquals(child1, child2)); 61 | } 62 | 63 | @Test public void testTwoChildrenWithOneHavingNewProperties() { 64 | class Child1 extends Parent { @Property int y = 4; } 65 | class Child2 extends Parent {} 66 | Child1 child1 = new Child1(); 67 | Child2 child2 = new Child2(); 68 | Pojomator child1Pojomator = PojomatorFactory.makePojomator(Child1.class); 69 | Pojomator child2Pojomator = PojomatorFactory.makePojomator(Child2.class); 70 | 71 | assertFalse(child1Pojomator.doEquals(child1, child2)); 72 | assertFalse(child2Pojomator.doEquals(child2, child1)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/EnhancedPojoFormatterWrapperTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import static org.testng.Assert.assertEquals; 4 | 5 | import java.util.List; 6 | 7 | import org.pojomatic.PropertyElement; 8 | import org.pojomatic.formatter.EnhancedPojoFormatter; 9 | import org.pojomatic.formatter.PojoFormatter; 10 | import org.testng.annotations.BeforeClass; 11 | import org.testng.annotations.BeforeMethod; 12 | import org.testng.annotations.Test; 13 | 14 | @Deprecated 15 | public class EnhancedPojoFormatterWrapperTest { 16 | 17 | private static class SimplePojoFormatter implements PojoFormatter { 18 | @Override public String getToStringPrefix(Class pojoClass) { return "pre-" + pojoClass.getSimpleName(); } 19 | @Override public String getToStringSuffix(Class pojoClass) { return "post-" + pojoClass.getSimpleName(); } 20 | @Override public String getPropertyPrefix(PropertyElement property) { return property.getName() + "-pre"; } 21 | @Override public String getPropertySuffix(PropertyElement property) { return property.getName() + "-post"; } 22 | } 23 | 24 | int sampleField; 25 | 26 | private static EnhancedPojoFormatter wrapper = new EnhancedPojoFormatterWrapper(new SimplePojoFormatter()); 27 | 28 | private static PropertyElement property; 29 | 30 | private StringBuilder builder; 31 | 32 | @BeforeClass 33 | public static void initProperty() throws ReflectiveOperationException { 34 | property = new PropertyField(EnhancedPojoFormatterWrapperTest.class.getDeclaredField("sampleField"), "foo"); 35 | } 36 | 37 | @BeforeMethod 38 | public void initStringBuidler() { 39 | builder = new StringBuilder(); 40 | } 41 | 42 | @Test 43 | public void appendPropertyPrefix() { 44 | wrapper.appendPropertyPrefix(builder, property); 45 | assertEquals(builder.toString(), "foo-pre");//delegate.getPropertyPrefix(property)); 46 | } 47 | 48 | @Test 49 | public void appendPropertySuffix() { 50 | wrapper.appendPropertySuffix(builder, property); 51 | assertEquals(builder.toString(), "foo-post"); 52 | } 53 | 54 | @Test 55 | public void appendToStringPrefix() { 56 | wrapper.appendToStringPrefix(builder, List.class); 57 | assertEquals(builder.toString(), "pre-List"); 58 | } 59 | 60 | @Test 61 | public void appendToStringSuffix() { 62 | wrapper.appendToStringSuffix(builder, List.class); 63 | assertEquals(builder.toString(), "post-List"); 64 | } 65 | 66 | @Test 67 | public void getPropertyPrefix() { 68 | assertEquals(wrapper.getPropertyPrefix(property), "foo-pre"); 69 | } 70 | 71 | @Test 72 | public void getPropertySuffix() { 73 | assertEquals(wrapper.getPropertySuffix(property), "foo-post"); 74 | } 75 | 76 | @Test 77 | public void getToStringPrefix() { 78 | assertEquals(wrapper.getToStringPrefix(List.class), "pre-List"); 79 | } 80 | 81 | @Test 82 | public void getToStringSuffix() { 83 | assertEquals(wrapper.getToStringSuffix(List.class), "post-List"); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/EnhancedPropertyFormatterWrapper.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.lang.reflect.AnnotatedElement; 4 | 5 | import org.pojomatic.formatter.EnhancedPropertyFormatter; 6 | import org.pojomatic.formatter.PropertyFormatter; 7 | 8 | @Deprecated 9 | public class EnhancedPropertyFormatterWrapper implements EnhancedPropertyFormatter { 10 | private final PropertyFormatter delegate; 11 | 12 | public EnhancedPropertyFormatterWrapper(PropertyFormatter delegate) { 13 | this.delegate = delegate; 14 | } 15 | 16 | @Override 17 | public void initialize(AnnotatedElement element) { 18 | delegate.initialize(element); 19 | } 20 | 21 | @Override 22 | public String format(Object value) { 23 | return delegate.format(value); 24 | } 25 | 26 | @Override public void appendFormatted(StringBuilder builder, Object o) { builder.append(delegate.format(o)); } 27 | @Override public void appendFormatted(StringBuilder builder, boolean b) { builder.append(delegate.format(b)); } 28 | @Override public void appendFormatted(StringBuilder builder, byte b) { builder.append(delegate.format(b)); } 29 | @Override public void appendFormatted(StringBuilder builder, short s) { builder.append(delegate.format(s)); } 30 | @Override public void appendFormatted(StringBuilder builder, char c) { builder.append(delegate.format(c)); } 31 | @Override public void appendFormatted(StringBuilder builder, int i) { builder.append(delegate.format(i)); } 32 | @Override public void appendFormatted(StringBuilder builder, long l) { builder.append(delegate.format(l)); } 33 | @Override public void appendFormatted(StringBuilder builder, float f) { builder.append(delegate.format(f)); } 34 | @Override public void appendFormatted(StringBuilder builder, double d) { builder.append(delegate.format(d)); } 35 | @Override public void appendFormatted(StringBuilder builder, boolean[] b) { builder.append(delegate.format(b)); } 36 | @Override public void appendFormatted(StringBuilder builder, byte[] b) { builder.append(delegate.format(b)); } 37 | @Override public void appendFormatted(StringBuilder builder, short[] s) { builder.append(delegate.format(s)); } 38 | @Override public void appendFormatted(StringBuilder builder, char[] c) { builder.append(delegate.format(c)); } 39 | @Override public void appendFormatted(StringBuilder builder, int[] i) { builder.append(delegate.format(i)); } 40 | @Override public void appendFormatted(StringBuilder builder, long[] l) { builder.append(delegate.format(l)); } 41 | @Override public void appendFormatted(StringBuilder builder, float[] f) { builder.append(delegate.format(f)); } 42 | @Override public void appendFormatted(StringBuilder builder, double[] d) { builder.append(delegate.format(d)); } 43 | @Override public void appendFormatted(StringBuilder builder, Object[] o) { builder.append(delegate.format(o)); } 44 | 45 | @Override public void appendFormattedPossibleArray(StringBuilder builder, Object o) { 46 | builder.append(delegate.format(o)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/ArrayType.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.lang.reflect.Array; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Objects; 7 | 8 | public class ArrayType implements Type { 9 | private final Type componentType; 10 | private final List sampleValues; 11 | 12 | public ArrayType(Type componentType) { 13 | this.componentType = componentType; 14 | sampleValues = new ArrayList<>(); 15 | sampleValues.add(null); 16 | for (int size = 0; size < componentType.getSampleValues().size(); size++) { 17 | Object array = Array.newInstance(componentType.getClazz(), size); 18 | for (int i = 0; i < size; i++) { 19 | Array.set(array, i, componentType.getSampleValues().get(i)); 20 | } 21 | sampleValues.add(array); 22 | } 23 | } 24 | 25 | @Override 26 | public Class getClazz() { 27 | return Array.newInstance(componentType.getClazz(), 0).getClass(); 28 | } 29 | 30 | @Override 31 | public List getSampleValues() { 32 | return sampleValues; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return componentType.toString() + "[]"; 38 | } 39 | 40 | @Override 41 | public int hashCode(Object value) { 42 | return value == null ? 0 : arrayToList(value).hashCode(); 43 | } 44 | @Override 45 | public int deepHashCode(Object value) { 46 | if (value == null) { 47 | return 0; 48 | } 49 | else { 50 | int hash = 1; 51 | for (Object element: arrayToList(value)) { 52 | hash = hash*31 + componentType.deepHashCode(element); 53 | } 54 | return hash; 55 | } 56 | } 57 | 58 | @Override 59 | public String toString(Object value) { 60 | if (value == null) { 61 | return "null"; 62 | } 63 | else { 64 | ArrayList strings = new ArrayList<>(); 65 | for (Object element: arrayToList(value)) { 66 | if (componentType instanceof ArrayType) { 67 | strings.add(Objects.toString(element)); 68 | } 69 | else { 70 | strings.add(componentType.toString(element)); 71 | } 72 | } 73 | return strings.toString(); 74 | } 75 | } 76 | 77 | @Override 78 | public String deepToString(Object value) { 79 | if (value == null) { 80 | return "null"; 81 | } 82 | else { 83 | ArrayList strings = new ArrayList<>(); 84 | for (Object element: arrayToList(value)) { 85 | strings.add(componentType.deepToString(element)); 86 | } 87 | return strings.toString(); 88 | } 89 | } 90 | 91 | private static List arrayToList(Object array) { 92 | List result = new ArrayList<>(); 93 | for (int i = 0; i < Array.getLength(array); i++) { 94 | result.add(Array.get(array, i)); 95 | } 96 | return result; 97 | } 98 | 99 | @Override 100 | public int arrayDepth() { 101 | return componentType.arrayDepth() + 1; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/formatter/DefaultPropertyFormatter.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import java.lang.reflect.AnnotatedElement; 4 | import java.util.Arrays; 5 | 6 | /** 7 | * The default property formatter used by Pojomatic. While the particulars of the formatting 8 | * strategy are subject to change, the general principle is to provide a meaningful representation. 9 | * In particular, arrays are formatted "deeply", rather than simply showing the default toString 10 | * representation of Java arrays. 11 | * 12 | * @deprecated Since 2.0. Use {@link DefaultEnhancedPropertyFormatter} instead. 13 | */ 14 | @Deprecated 15 | public class DefaultPropertyFormatter implements PropertyFormatter { 16 | //FIXME - this currently prevents formatter reusability, and for very little benefit. Perhaps an initializable annotation? 17 | @Override 18 | public void initialize(AnnotatedElement element) { 19 | //Not applicable 20 | } 21 | 22 | @Override 23 | public String format(Object value) { 24 | if (value == null) { 25 | return "null"; 26 | } 27 | else if (value.getClass().isArray()) { 28 | Class componentClass = value.getClass().getComponentType(); 29 | if (componentClass.isPrimitive()) { 30 | if (Boolean.TYPE == componentClass) { 31 | return Arrays.toString((boolean[]) value); 32 | } 33 | if (Character.TYPE == componentClass) { 34 | StringBuilder builder = new StringBuilder().append('['); 35 | boolean seenOne = false; 36 | for (char c: ((char[]) value)) { 37 | if(seenOne) { 38 | builder.append(", "); 39 | } 40 | else { 41 | seenOne = true; 42 | } 43 | builder.append('\''); 44 | if (Character.isISOControl(c)) { 45 | builder.append("0x").append(Integer.toHexString(c)); 46 | } 47 | else { 48 | builder.append(c); 49 | } 50 | builder.append('\''); 51 | } 52 | return builder.append(']').toString(); 53 | } 54 | if (Byte.TYPE == componentClass) { 55 | return Arrays.toString((byte[]) value); 56 | } 57 | if (Short.TYPE == componentClass) { 58 | return Arrays.toString((short[]) value); 59 | } 60 | if (Integer.TYPE == componentClass) { 61 | return Arrays.toString((int[]) value); 62 | } 63 | if (Long.TYPE == componentClass) { 64 | return Arrays.toString((long[]) value); 65 | } 66 | if (Float.TYPE == componentClass) { 67 | return Arrays.toString((float[]) value); 68 | } 69 | if (Double.TYPE == componentClass) { 70 | return Arrays.toString((double[]) value); 71 | } 72 | else { 73 | throw new IllegalStateException("unexpected primitive array base type: " + componentClass); 74 | } 75 | } 76 | else { 77 | return Arrays.deepToString((Object[]) value); 78 | } 79 | } 80 | else { 81 | return value.toString(); 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /pojomatic-test-utils/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | pojomatic-all 4 | org.pojomatic 5 | 2.2.0 6 | ../pom.xml 7 | 8 | 4.0.0 9 | pojomatic-test-utils 10 | master-SNAPSHOT 11 | jar 12 | PojomaticTestUtils 13 | 14 | Utilities to help with writing tests for, or using, classes which use Pojomatic. 15 | This library includes classes with JUnit and TestNG style 16 | assertEquals methods which include differences (via Pojomatic.diff(Object, Object)) 17 | in the error message if the assertion fails. 18 | 19 | 20 | 21 | 22 | org.pojomatic 23 | pojomatic 24 | 25 | 26 | junit 27 | junit 28 | 4.11 29 | test 30 | 31 | 32 | 33 | 34 | 35 | 36 | maven-javadoc-plugin 37 | 38 | 39 | https://junit.org/junit4/javadoc/latest 40 | https://jitpack.io/com/github/cbeust/testng/6.10/javadoc 41 | 42 | 43 | 44 | junit 45 | junit 46 | 4.12 47 | 48 | 49 | org.testng 50 | testng 51 | 6.9.10 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | maven-javadoc-plugin 63 | 64 | 65 | https://junit.org/junit4/javadoc/latest 66 | https://jitpack.io/com/github/cbeust/testng/6.10/javadoc 67 | 68 | 69 | 70 | junit 71 | junit 72 | 4.12 73 | 74 | 75 | org.testng 76 | testng 77 | 6.9.10 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/factory/PojoFactory.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal.factory; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.InvocationTargetException; 6 | 7 | import org.pojomatic.Pojomator; 8 | import org.pojomatic.internal.PojomatorFactory; 9 | 10 | public class PojoFactory { 11 | public static class PojoAssembler { 12 | private final Object pojo; 13 | 14 | public PojoAssembler(Object pojo) { 15 | this.pojo = pojo; 16 | } 17 | 18 | 19 | public PojoAssembler with(String propertyName, Object value) { 20 | Field field; 21 | try { 22 | field = pojo.getClass().getDeclaredField(propertyName); 23 | } catch (NoSuchFieldException | SecurityException e) { 24 | throw new RuntimeException(e); 25 | } 26 | field.setAccessible(true); 27 | try { 28 | field.set(pojo, value); 29 | } catch (IllegalArgumentException | IllegalAccessException e) { 30 | throw new RuntimeException(e); 31 | } 32 | return this; 33 | } 34 | 35 | public PojoAssembler withParent(String propertyName, Object value) { 36 | Field field; 37 | try { 38 | field = pojo.getClass().getSuperclass().getDeclaredField(propertyName); 39 | } catch (NoSuchFieldException | SecurityException e) { 40 | throw new RuntimeException(e); 41 | } 42 | field.setAccessible(true); 43 | try { 44 | field.set(pojo, value); 45 | } catch (IllegalArgumentException | IllegalAccessException e) { 46 | throw new RuntimeException(e); 47 | } 48 | return this; 49 | } 50 | 51 | public Object pojo() { 52 | return pojo; 53 | } 54 | } 55 | 56 | private final PojoDescriptor pojoDescriptor; 57 | private final Class pojoClass; 58 | private final Pojomator pojomator; 59 | 60 | public PojoFactory(PojoDescriptor pojoDescriptor) { 61 | this(new PojoClassFactory(), pojoDescriptor); 62 | } 63 | 64 | @SuppressWarnings("unchecked") 65 | public PojoFactory(PojoClassFactory classFactory, PojoDescriptor pojoDescriptor) { 66 | this.pojoDescriptor = pojoDescriptor; 67 | this.pojoClass = classFactory.generateClass(pojoDescriptor); 68 | this.pojomator = (Pojomator) PojomatorFactory.makePojomator(pojoClass); 69 | 70 | } 71 | 72 | public PojoAssembler create() { 73 | try { 74 | Constructor constructor = pojoClass.getConstructor(); 75 | constructor.setAccessible(true); 76 | return new PojoAssembler(constructor.newInstance()); 77 | } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) { 78 | throw new RuntimeException(e); 79 | } 80 | } 81 | 82 | public Object create(Object value) { 83 | if (pojoDescriptor.properties.size() != 1) { 84 | throw new IllegalArgumentException("expected one property, found " + pojoDescriptor.properties.size()); 85 | } 86 | return create().with(pojoDescriptor.properties.get(0).name, value).pojo(); 87 | } 88 | 89 | public Pojomator pojomator() { 90 | return pojomator; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/main/java/org/pojomatic/test/AssertUtils.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.test; 2 | 3 | import org.pojomatic.Pojomatic; 4 | import org.pojomatic.NoPojomaticPropertiesException; 5 | 6 | /** 7 | * This class is not meant to be a part of the public API. 8 | */ 9 | public class AssertUtils { 10 | 11 | /** 12 | * Determines if two objects are both null or are equal according to 13 | * {@link Object#equals(Object)}. 14 | * 15 | * @param first the first object to compare 16 | * @param second the second object to compare 17 | * @return {@code true} if both objects are null, 18 | * or {@code first} is non-null and {@code first.equals(second)}, 19 | * {@code false} otherwise 20 | * @see Object#equals(Object) 21 | */ 22 | public static boolean equal(Object first, Object second) { 23 | return first == null ? second == null : first.equals(second); 24 | } 25 | 26 | /** 27 | * Asserts that two objects are either both null or are equal according to 28 | * {@link Object#equals(Object)}. If not, an {@code AssertionError} is thrown. If the objects are 29 | * not equal, but the types of two objects are compatible for equality, then the differences as 30 | * determined by {@link Pojomatic#diff(Object, Object)} are included in the failure message. 31 | * 32 | * @param message the message to add if the assertion fails 33 | * @param expected will be displayed first if the assertion fails 34 | * @param actual will be displayed second if the assertion fails 35 | * @throws AssertionError if the objects are not equal. {@link AssertionError#getMessage()} will 36 | * include information about the differences 37 | */ 38 | public static void assertEquals(String message, Object expected, Object actual) { 39 | if (!equal(expected, actual)) { 40 | if (expected == null) { 41 | throw new AssertionError( 42 | makeBuilder(message).append("expected is null, but actual is ").append(actual)); 43 | } 44 | if (actual == null) { 45 | throw new AssertionError( 46 | makeBuilder(message).append("actual is null, but expected is ").append(expected)); 47 | } 48 | try { 49 | if (Pojomatic.areCompatibleForEquals(expected.getClass(), actual.getClass())) { 50 | throw new AssertionError(appendStandardEqualityMessage( 51 | makeBuilder(message).append("differences between expected and actual:") 52 | .append(Pojomatic.diff(expected, actual)) 53 | .append(" ("), expected, actual).append(")").toString()); 54 | } 55 | } 56 | catch (NoPojomaticPropertiesException e) {} 57 | throw new AssertionError( 58 | appendStandardEqualityMessage(makeBuilder(message), expected, actual).toString()); 59 | } 60 | } 61 | 62 | private static StringBuilder appendStandardEqualityMessage( 63 | StringBuilder builder, Object expected, Object actual) { 64 | return builder 65 | .append("expected:<").append(expected).append("> but was:<").append(actual).append(">"); 66 | } 67 | 68 | private static StringBuilder makeBuilder(String message) { 69 | return message == null ? new StringBuilder() : new StringBuilder(message).append(" "); 70 | } 71 | 72 | private AssertUtils() {} 73 | 74 | } 75 | -------------------------------------------------------------------------------- /pojomatic/src/site/xdoc/index.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Pojomatic 7 | 8 | 9 |
10 |

11 | Pojomatic provides configurable implementations of the equals(Object), hashCode() 12 | and toString() methods inherited from java.lang.Object. 13 |

14 |

For example, the following bean has been "pojomated":

15 |
16 | import org.pojomatic.Pojomatic;
17 | import org.pojomatic.annotations.AutoProperty;
18 | 
19 | @AutoProperty
20 | public class Person {
21 |   private final String firstName;
22 |   private final String lastName;
23 |   private final int age;
24 | 
25 |   public Person(String firstName, String lastName, int age) {
26 |     this.firstName = firstName;
27 |     this.lastName = lastName;
28 |     this.age = age;
29 |   }
30 | 
31 |   public String getFirstName() { return this.firstName; }
32 |   public String getLastName() { return this.lastName; }
33 |   public int getAge() { return this.age; }
34 | 
35 |   @Override public boolean equals(Object o) {
36 |     return Pojomatic.equals(this, o);
37 |   }
38 | 
39 |   @Override public int hashCode() {
40 |     return Pojomatic.hashCode(this);
41 |   }
42 | 
43 |   @Override public String toString() {
44 |     return Pojomatic.toString(this);
45 |   }
46 | }
47 | 
48 |

49 | The above class implements equals and hashCode methods following the best practices 50 | outlined in Josh Bloch's Efective Java. Moreover, running 51 |

52 | 53 | System.out.println(new Person("John", "Doe", 32).toString()); 54 |

will result in the following output:

55 | Person{firstName: {John}, lastName: {Doe}, age: {32}} 56 | 57 |

For more information and examples, see the JavaDocs.

58 |
59 | 60 | 61 |
62 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/formatter/EnhancedPropertyFormatterTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import static org.testng.Assert.*; 4 | 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Method; 7 | 8 | import org.mockito.Mockito; 9 | import org.pojomatic.internal.ArrayType; 10 | import org.pojomatic.internal.EnhancedPropertyFormatterWrapper; 11 | import org.pojomatic.internal.Type; 12 | import org.pojomatic.internal.TypeProviders; 13 | import org.testng.annotations.Test; 14 | 15 | @Deprecated 16 | public class EnhancedPropertyFormatterTest { 17 | 18 | // for testInitialize 19 | int x; 20 | 21 | @Test 22 | public void testInitialize() throws Exception { 23 | PropertyFormatter mock = Mockito.mock(PropertyFormatter.class); 24 | Field field = getClass().getDeclaredField("x"); 25 | new EnhancedPropertyFormatterWrapper(mock).initialize(field); 26 | Mockito.verify(mock).initialize(field); 27 | } 28 | 29 | @Test(dataProvider="types", dataProviderClass=TypeProviders.class) 30 | public void testAppend(Type type) throws ReflectiveOperationException { 31 | EnhancedPropertyFormatter wrapper = new EnhancedPropertyFormatterWrapper(new DefaultPropertyFormatter()); 32 | Method m = EnhancedPropertyFormatter.class.getDeclaredMethod("appendFormatted", StringBuilder.class, type.getClazz()); 33 | for (Object value: type.getSampleValues()) { 34 | StringBuilder builder = new StringBuilder(); 35 | m.invoke(wrapper, builder, value); 36 | assertEquals(builder.toString(), new DefaultPropertyFormatter().format(value)); 37 | } 38 | } 39 | 40 | @Test(dataProvider="types", dataProviderClass=TypeProviders.class) 41 | public void testAppendArraysAsObject(Type type) throws ReflectiveOperationException { 42 | Type arrayType = new ArrayType(type); 43 | EnhancedPropertyFormatter wrapper = new EnhancedPropertyFormatterWrapper(new DefaultPropertyFormatter()); 44 | Method m = EnhancedPropertyFormatter.class.getDeclaredMethod("appendFormatted", StringBuilder.class, Object.class); 45 | for (Object value: arrayType.getSampleValues()) { 46 | StringBuilder builder = new StringBuilder(); 47 | m.invoke(wrapper, builder, value); 48 | assertEquals(builder.toString(), new DefaultPropertyFormatter().format(value)); 49 | } 50 | } 51 | 52 | @Test(dataProvider="types", dataProviderClass=TypeProviders.class) 53 | public void testAppendArraysAsPossibleArray(Type type) throws ReflectiveOperationException { 54 | Type arrayType = new ArrayType(type); 55 | EnhancedPropertyFormatter wrapper = new EnhancedPropertyFormatterWrapper(new DefaultPropertyFormatter()); 56 | Method m = EnhancedPropertyFormatter.class.getDeclaredMethod("appendFormattedPossibleArray", StringBuilder.class, Object.class); 57 | for (Object value: arrayType.getSampleValues()) { 58 | StringBuilder builder = new StringBuilder(); 59 | m.invoke(wrapper, builder, value); 60 | assertEquals(builder.toString(), new DefaultPropertyFormatter().format(value)); 61 | } 62 | } 63 | 64 | @Test(dataProvider="types", dataProviderClass=TypeProviders.class) 65 | public void testFormat(Type type) throws ReflectiveOperationException { 66 | DefaultPropertyFormatter formatter = new DefaultPropertyFormatter(); 67 | EnhancedPropertyFormatter wrapper = new EnhancedPropertyFormatterWrapper(formatter); 68 | for (Object value: type.getSampleValues()) { 69 | assertEquals(wrapper.format(value), formatter.format(value)); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /pojomatic-benchmarks/pojomatic2-benchmark/src/main/java/org/pojomatic/benchmark/ArrayInObject.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.benchmark; 2 | 3 | import java.io.PrintWriter; 4 | import java.util.Random; 5 | 6 | import org.pojomatic.Pojomatic; 7 | import org.pojomatic.Pojomator; 8 | import org.pojomatic.annotations.Property; 9 | import org.pojomatic.annotations.SkipArrayCheck; 10 | 11 | import com.google.caliper.BeforeExperiment; 12 | import com.google.caliper.Benchmark; 13 | import com.google.caliper.Param; 14 | import com.google.caliper.runner.CaliperMain; 15 | 16 | public class ArrayInObject { 17 | public static void main(String[] args) throws Exception { 18 | String[] fullArgs = new String[args.length + 1]; 19 | fullArgs[0] = ArrayInObject.class.getName(); 20 | System.arraycopy(args, 0, fullArgs, 1, args.length); 21 | CaliperMain.exitlessMain( 22 | fullArgs, 23 | new PrintWriter(System.out, true), 24 | new PrintWriter(System.err, true)); 25 | } 26 | 27 | private final static Random rand = new Random(); 28 | 29 | public interface ObjectBean { 30 | void setX(Object x); 31 | } 32 | 33 | public static class ArrayPossible implements ObjectBean { 34 | @Property 35 | Object x; 36 | 37 | @Override 38 | public void setX(Object x) { 39 | this.x = x; 40 | } 41 | } 42 | 43 | public static class ArrayNotPossible implements ObjectBean { 44 | @Property 45 | @SkipArrayCheck 46 | Object x; 47 | 48 | @Override 49 | public void setX(Object x) { 50 | this.x = x; 51 | } 52 | } 53 | 54 | private static ObjectBean[] beans; 55 | 56 | @Param({ "ARRAY_POSSIBLE", "ARRAY_NOT_POSSIBLE" }) 57 | private Checker checker; 58 | 59 | @Benchmark 60 | public void equals(int reps) { 61 | checker.checkEquals(beans, reps); 62 | } 63 | 64 | @BeforeExperiment 65 | public void setUp() { 66 | beans = makeBeans(800); 67 | } 68 | 69 | private ObjectBean[] makeBeans(int beanCount) { 70 | ObjectBean[] beans = new ObjectBean[beanCount]; 71 | for (int i = 0; i < beanCount; i++) { 72 | ObjectBean bean = checker.makeBean(); 73 | bean.setX(String.valueOf(rand.nextDouble())); 74 | beans[i] = bean;; 75 | } 76 | return beans; 77 | } 78 | 79 | public static enum Checker { 80 | ARRAY_POSSIBLE(ArrayPossible.class), ARRAY_NOT_POSSIBLE(ArrayNotPossible.class); 81 | 82 | public void checkEquals(ObjectBean[] beans, int reps) { 83 | int i = 0, j = 0, rep = 0; 84 | 85 | while (rep++ < reps) { 86 | if (pojomator.doEquals(beans[i], beans[j]) != (i == j)) { 87 | System.out.println("error at " + i + ", " + j); 88 | } 89 | if (++j == beans.length) { 90 | j = 0; 91 | i = (i + 1) % beans.length; 92 | } 93 | } 94 | } 95 | 96 | @SuppressWarnings("unchecked") 97 | private Checker(Class beanClass) { 98 | pojomator = (Pojomator) Pojomatic.pojomator(beanClass); 99 | this.beanClass = beanClass; 100 | } 101 | private final Pojomator pojomator; 102 | private final Class beanClass; 103 | 104 | public ObjectBean makeBean() { 105 | try { 106 | return beanClass.newInstance(); 107 | } catch (InstantiationException | IllegalAccessException e) { 108 | throw new RuntimeException(e); 109 | } 110 | } 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/formatter/DefaultEnhancedPropertyFormatterTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import static org.testng.Assert.*; 4 | 5 | import java.lang.reflect.Array; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Method; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Objects; 11 | 12 | import org.pojomatic.annotations.SkipArrayCheck; 13 | import org.pojomatic.internal.Type; 14 | import org.pojomatic.internal.TypeProviders; 15 | import org.testng.annotations.BeforeClass; 16 | import org.testng.annotations.Test; 17 | 18 | public class DefaultEnhancedPropertyFormatterTest { 19 | 20 | @SuppressWarnings("unused") // used to initialize the formatter. 21 | private Object unAnnotated; 22 | 23 | @SkipArrayCheck 24 | private Object skipArrayCheckAnnotated; 25 | 26 | private Field unAnnotatedField, skipArrayCheckAnnotatedField; 27 | 28 | @BeforeClass public void setUp() throws Exception { 29 | unAnnotatedField = getClass().getDeclaredField("unAnnotated"); 30 | skipArrayCheckAnnotatedField = getClass().getDeclaredField("skipArrayCheckAnnotated"); 31 | } 32 | 33 | @Test(dataProvider = "types", dataProviderClass = TypeProviders.class) 34 | public void testSimpleAppendFormatted(Type type) throws Exception { 35 | DefaultEnhancedPropertyFormatter formatter = new DefaultEnhancedPropertyFormatter(); 36 | Method appendFormatted = 37 | formatter.getClass().getMethod("appendFormatted", new Class[] { StringBuilder.class, type.getClazz() }); 38 | for (Object value: type.getSampleValues()) { 39 | StringBuilder builder = new StringBuilder(); 40 | appendFormatted.invoke(formatter, builder, value); 41 | assertEquals( 42 | builder.toString(), 43 | type.toString(value), 44 | "value: " + possibleArrayToList(value)); 45 | } 46 | } 47 | 48 | @Test(dataProvider = "arrayTypes", dataProviderClass = TypeProviders.class) 49 | public void testArrayAsObjectToString(Type type, boolean skipArrayCheck) { 50 | DefaultEnhancedPropertyFormatter simpleFormatter = new DefaultEnhancedPropertyFormatter(); 51 | for (Object value: type.getSampleValues()) { 52 | testArrayAsObjectToStringForValue(type, skipArrayCheck, simpleFormatter, 53 | value); 54 | } 55 | } 56 | 57 | private void testArrayAsObjectToStringForValue(Type type, 58 | boolean skipArrayCheck, DefaultEnhancedPropertyFormatter simpleFormatter, 59 | Object value) { 60 | StringBuilder builder = new StringBuilder(); 61 | simpleFormatter.initialize(skipArrayCheck ? skipArrayCheckAnnotatedField : unAnnotatedField); 62 | if (skipArrayCheck) { 63 | simpleFormatter.appendFormatted(builder, value); 64 | } 65 | else { 66 | simpleFormatter.appendFormattedPossibleArray(builder, value); 67 | } 68 | if (! builder.toString().equals(skipArrayCheck ? Objects.toString(value) : type.deepToString(value))) 69 | assertEquals( 70 | builder.toString(), 71 | skipArrayCheck ? Objects.toString(value) : type.deepToString(value), 72 | "value: " + possibleArrayToList(value)); 73 | } 74 | 75 | 76 | /** 77 | * Convert arrays to lists, leaving other types alone. 78 | * @param value 79 | * @return {@code value} if value is not an array, or the List equivalent of {@code value} if value is an array 80 | */ 81 | private Object possibleArrayToList(Object value) { 82 | if (value == null || ! value.getClass().isArray()) { 83 | return value; 84 | } 85 | List result = new ArrayList<>(); 86 | for (int i = 0; i < Array.getLength(value); i++) { 87 | result.add(possibleArrayToList(Array.get(value, i))); 88 | } 89 | return result; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/PojomatorFactory.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.lang.reflect.AnnotatedElement; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.InvocationTargetException; 6 | import java.security.AccessController; 7 | import java.security.PrivilegedActionException; 8 | import java.security.PrivilegedExceptionAction; 9 | 10 | import org.pojomatic.Pojomator; 11 | import org.pojomatic.PropertyElement; 12 | import org.pojomatic.annotations.PropertyFormat; 13 | import org.pojomatic.formatter.DefaultEnhancedPropertyFormatter; 14 | import org.pojomatic.formatter.EnhancedPropertyFormatter; 15 | 16 | public class PojomatorFactory { 17 | public static Pojomator makePojomator(final Class pojoClass) { 18 | try { 19 | return AccessController.doPrivileged(new PrivilegedExceptionAction>() { 20 | @Override 21 | public Pojomator run() throws Exception { 22 | return makePojomatorChecked(pojoClass); 23 | } 24 | }); 25 | } catch (PrivilegedActionException e) { 26 | throw new RuntimeException(e.getCause()); 27 | } 28 | } 29 | 30 | private static Pojomator makePojomatorChecked(Class pojoClass) 31 | throws IllegalAccessException, NoSuchFieldException, SecurityException, InstantiationException, 32 | InvocationTargetException, NoSuchMethodException { 33 | ClassProperties classProperties = ClassProperties.forClass(pojoClass); 34 | PojomatorByteCodeGenerator generator = new PojomatorByteCodeGenerator(pojoClass, classProperties); 35 | Class pojomatorClass = ClassDefinerFactory.getDefiner().defineClass(generator.pojomatorClassName, generator.makeClassBytes()); 36 | @SuppressWarnings("unchecked") 37 | Pojomator pojomator = (Pojomator) pojomatorClass.getConstructor(Class.class, ClassProperties.class) 38 | .newInstance(pojoClass, classProperties); 39 | for (PropertyElement propertyElement: classProperties.getToStringProperties()) { 40 | setStaticField( 41 | pojomatorClass, 42 | PojomatorByteCodeGenerator.propertyFormatterName(propertyElement), 43 | createPropertyFormatter(propertyElement.getElement())); 44 | } 45 | for (PropertyElement propertyElement: classProperties.getAllProperties()) { 46 | setStaticField(pojomatorClass, PojomatorByteCodeGenerator.propertyElementName(propertyElement), propertyElement); 47 | } 48 | return pojomator; 49 | } 50 | 51 | private static void setStaticField(Class clazz, String fieldName, Object value) 52 | throws NoSuchFieldException, SecurityException, IllegalAccessException { 53 | Field field = clazz.getDeclaredField(fieldName); 54 | field.setAccessible(true); 55 | field.set(null, value); 56 | } 57 | 58 | private static EnhancedPropertyFormatter createPropertyFormatter(AnnotatedElement annotatedElement) 59 | throws InstantiationException, IllegalAccessException { 60 | PropertyFormat propertyFormat = annotatedElement.getAnnotation(PropertyFormat.class); 61 | EnhancedPropertyFormatter propertyFormatter = constructPropertyFormatter(propertyFormat); 62 | propertyFormatter.initialize(annotatedElement); 63 | return propertyFormatter; 64 | 65 | } 66 | 67 | private static EnhancedPropertyFormatter constructPropertyFormatter(PropertyFormat propertyFormat) 68 | throws InstantiationException, IllegalAccessException { 69 | if (propertyFormat == null) { 70 | return new DefaultEnhancedPropertyFormatter(); 71 | } 72 | else { 73 | if (EnhancedPropertyFormatter.class.isAssignableFrom(propertyFormat.value())) { 74 | return (EnhancedPropertyFormatter) propertyFormat.value().newInstance(); 75 | } 76 | else { 77 | @SuppressWarnings("deprecation") 78 | EnhancedPropertyFormatterWrapper wrapper = new EnhancedPropertyFormatterWrapper(propertyFormat.value().newInstance()); 79 | return wrapper; 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/PropertyElementTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import static org.testng.Assert.*; 4 | 5 | import org.testng.annotations.Test; 6 | 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.reflect.Field; 10 | import java.lang.reflect.Method; 11 | 12 | public class PropertyElementTest { 13 | 14 | @Retention(RetentionPolicy.RUNTIME) @interface Expected { 15 | String value(); 16 | } 17 | 18 | @Test 19 | public void testGetDeclaringClass() throws Exception { 20 | assertEquals(new PropertyAccessor(getTestMethod(), "").getDeclaringClass(), PropertyElementTest.class); 21 | assertEquals(new PropertyField(getTestField(), "").getDeclaringClass(), PropertyElementTest.class); 22 | } 23 | 24 | @Test 25 | public void testEquals() throws Exception { 26 | PropertyAccessor testMethodProperty = new PropertyAccessor(getTestMethod(), ""); 27 | assertEquals(testMethodProperty, testMethodProperty); 28 | assertFalse(testMethodProperty.equals(null)); 29 | assertFalse(testMethodProperty.equals("someOtherClass")); 30 | assertEquals(new PropertyAccessor(getTestMethod(), ""), testMethodProperty); 31 | final PropertyField testFieldProperty = new PropertyField(getTestField(), ""); 32 | assertFalse(testMethodProperty.equals(testFieldProperty)); 33 | assertFalse(testFieldProperty.equals(testMethodProperty)); 34 | } 35 | 36 | @Test 37 | public void testToString() throws Exception { 38 | assertEquals(new PropertyAccessor(getTestMethod(), "").toString(), getTestMethod().toString()); 39 | } 40 | 41 | @Test 42 | public void testMethodHashCode() throws Exception { 43 | assertEquals(new PropertyAccessor(getTestMethod(), "salt").hashCode(), getTestMethod().hashCode()); 44 | } 45 | 46 | @Test 47 | public void testFieldHashCode() throws Exception { 48 | assertEquals(new PropertyField(getTestField(), "salt").hashCode(), getTestField().hashCode()); 49 | } 50 | 51 | @Test 52 | public void testGetNameForField() throws Exception { 53 | assertEquals(new PropertyField(getTestField(), "").getName(), "testField"); 54 | assertEquals(new PropertyField(getTestField(), "foo").getName(), "foo"); 55 | } 56 | 57 | @Test 58 | public void testOverrideNameForAccessor() throws Exception { 59 | assertEquals(new PropertyAccessor(getTestMethod(), "bar").getName(), "bar"); 60 | } 61 | 62 | @Test 63 | public void testGetNameForAccessor() throws Exception { 64 | for (Method method: getClass().getDeclaredMethods()) { 65 | Expected expected = method.getAnnotation(Expected.class); 66 | if (expected != null) { 67 | assertEquals(new PropertyAccessor(method, "").getName(), expected.value(), "name for method " + method.getName()); 68 | } 69 | } 70 | } 71 | 72 | // methods tested in testGetNameForAccessor 73 | @Expected("foo") public Object getFoo() { return null; } 74 | @Expected("URL") public Object getURL() { return null; } 75 | @Expected("get") public Object get() { return null; } 76 | @Expected("getter") public Object getter() { return null; } 77 | @Expected("isString") public Object isString() { return null; } 78 | @Expected("boolean") public Boolean isBoolean() { return null; } 79 | @Expected("bool") public boolean isBool() { return false; } 80 | @Expected("is") public boolean is() { return false; } 81 | 82 | private Method getTestMethod() throws Exception { 83 | return getMethod("testAccessor"); 84 | } 85 | 86 | private Method getMethod(String name) throws Exception { 87 | return this.getClass().getDeclaredMethod(name, (Class[])null); 88 | } 89 | 90 | private Field getTestField() throws Exception { 91 | return getClass().getDeclaredField("testField"); 92 | } 93 | 94 | // test properties 95 | @SuppressWarnings("unused") 96 | private Object testAccessor() { return "Test string"; } 97 | @SuppressWarnings("unused") 98 | private final Object testField = new Object(); 99 | } 100 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/PojomaticTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic; 2 | 3 | import static org.testng.Assert.*; 4 | 5 | import org.testng.annotations.Test; 6 | import org.pojomatic.annotations.Property; 7 | import org.pojomatic.annotations.SkipArrayCheck; 8 | import org.pojomatic.diff.NoDifferences; 9 | import org.pojomatic.internal.PojomatorFactory; 10 | 11 | public class PojomaticTest { 12 | public static class Bean { 13 | @Property public final int x; 14 | public Bean(int x) { this.x = x; } 15 | public Bean() { x = 0; } 16 | } 17 | 18 | private static Pojomator BEAN_POJOMATOR = PojomatorFactory.makePojomator(Bean.class); 19 | private static Bean BEAN = new Bean(1); 20 | 21 | @Test 22 | public void testPojomator() { 23 | Pojomator pojomator = Pojomatic.pojomator(Bean.class); 24 | assertEquals(pojomator.doToString(BEAN), BEAN_POJOMATOR.doToString(BEAN)); 25 | } 26 | 27 | @Test 28 | public void testToString() { 29 | assertEquals(Pojomatic.toString(BEAN), BEAN_POJOMATOR.doToString(BEAN)); 30 | } 31 | 32 | @Test 33 | public void testDiffNoDifferences() { 34 | assertEquals(Pojomatic.diff(BEAN, BEAN), NoDifferences.getInstance()); 35 | } 36 | 37 | @Test(expectedExceptions=NullPointerException.class) 38 | public void testDiffBothNull() { 39 | Pojomatic.diff(null, null); 40 | } 41 | 42 | @Test(expectedExceptions=NullPointerException.class) 43 | public void testDiffNullFirst() { 44 | Pojomatic.diff(null, BEAN); 45 | } 46 | 47 | @Test(expectedExceptions=NullPointerException.class) 48 | public void testDiffNullSecond() { 49 | Pojomatic.diff(BEAN, null); 50 | } 51 | 52 | @Test 53 | public void testHashCode() { 54 | assertEquals(Pojomatic.hashCode(BEAN), BEAN_POJOMATOR.doHashCode(BEAN)); 55 | } 56 | 57 | @Test 58 | public void testEquals() { 59 | assertTrue(Pojomatic.equals(new Bean(3), new Bean(3))); 60 | assertFalse(Pojomatic.equals(new Bean(3), new Bean(4))); 61 | } 62 | 63 | @Test 64 | public void testCompatibleForEquality() { 65 | class BeanSubClass extends Bean{} 66 | 67 | class BeanWithExtraData extends Bean { 68 | @Property public int getY() { return 0; } 69 | } 70 | assertTrue(Pojomatic.areCompatibleForEquals(Bean.class, BeanSubClass.class)); 71 | assertFalse(Pojomatic.areCompatibleForEquals(Bean.class, BeanWithExtraData.class)); 72 | assertFalse(Pojomatic.areCompatibleForEquals(BeanWithExtraData.class, Bean.class)); 73 | } 74 | 75 | @Test 76 | public void testSkipArrayCheck() { 77 | class Box { 78 | @Property 79 | @SkipArrayCheck 80 | Object o; 81 | 82 | Box(Object o) { this.o = o; } 83 | } 84 | assertFalse(Pojomatic.equals(new Box(new String[] { "x" }), new Box(new String[] { "x" }))); 85 | assertNotEquals(Pojomatic.hashCode(new Box(new String[] { "x" })), Pojomatic.hashCode(new Box(new String[] { "x" }))); 86 | 87 | String[] array = new String[] { "y" }; 88 | assertTrue(Pojomatic.equals(new Box(array), new Box(array))); 89 | assertEquals(Pojomatic.hashCode(new Box(array)), Pojomatic.hashCode(new Box(array))); 90 | } 91 | 92 | @Test 93 | public void testNoSkipArrayCheck() { 94 | class Box { 95 | @Property 96 | Object o; 97 | 98 | Box(Object o) { this.o = o; } 99 | } 100 | assertTrue(Pojomatic.equals(new Box(new String[] { "x" }), new Box(new String[] { "x" }))); 101 | assertEquals(Pojomatic.hashCode(new Box(new String[] { "x" })), Pojomatic.hashCode(new Box(new String[] { "x" }))); 102 | } 103 | 104 | @Test 105 | public void testSkipArrayCheckIgnoredForArrayType() { 106 | class Box { 107 | @Property 108 | @SkipArrayCheck 109 | Object[] os; 110 | 111 | Box(Object o) { this.os = new Object[] { o }; } 112 | } 113 | assertTrue(Pojomatic.equals(new Box(new String[] { "x" }), new Box(new String[] { "x" }))); 114 | assertEquals(Pojomatic.hashCode(new Box(new String[] { "x" })), Pojomatic.hashCode(new Box(new String[] { "x" }))); 115 | } 116 | 117 | 118 | } 119 | -------------------------------------------------------------------------------- /pojomatic-benchmarks/pojomatic-base-benchmark/src/main/java/org/pojomatic/BeanSpeedTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic; 2 | 3 | import java.io.PrintWriter; 4 | import java.util.Arrays; 5 | import java.util.Random; 6 | 7 | import com.google.caliper.BeforeExperiment; 8 | import com.google.caliper.Benchmark; 9 | import com.google.caliper.Param; 10 | import com.google.caliper.runner.CaliperMain; 11 | 12 | public class BeanSpeedTest { 13 | public static void main(String[] args) throws Exception { 14 | String[] fullArgs = new String[args.length + 1]; 15 | fullArgs[0] = BeanSpeedTest.class.getName(); 16 | System.arraycopy(args, 0, fullArgs, 1, args.length); 17 | CaliperMain.exitlessMain( 18 | fullArgs, 19 | new PrintWriter(System.out, true), 20 | new PrintWriter(System.err, true)); 21 | } 22 | 23 | private final static Random rand = new Random(); 24 | private static Bean[] beans; 25 | 26 | @Param({ "STANDARD", "STANDARD_INDIRECT", "POJOMATIC", "POJOMATIC_FAST" }) 27 | private Checker checker; 28 | 29 | @Benchmark 30 | public void equals(int reps) { 31 | checker.checkEquals(beans, reps); 32 | } 33 | 34 | @Benchmark 35 | public void hashCode(int reps) { 36 | checker.checkHashCode(beans, reps); 37 | } 38 | 39 | @BeforeExperiment 40 | public void setUp() { 41 | beans = makeBeans(800); 42 | } 43 | 44 | private static Bean[] makeBeans(int beanCount) { 45 | Bean[] beans = new Bean[beanCount]; 46 | for (int i = 0; i < beanCount; i++) { 47 | beans[i] = randomBean(); 48 | } 49 | return beans; 50 | } 51 | 52 | private static Bean randomBean() { 53 | Bean bean = new Bean(); 54 | bean.setI(rand.nextInt()); 55 | bean.setInteger(rand.nextInt()); 56 | int[] ints = new int[rand.nextInt(10)]; 57 | for (int i = 0; i < ints.length; i++) { 58 | ints[i] = rand.nextInt(); 59 | } 60 | bean.setInts(ints); 61 | bean.setString(String.valueOf(rand.nextDouble())); 62 | String[] strings = new String[rand.nextInt(5)]; 63 | for (int i = 0; i < strings.length; i++) { 64 | strings[i] = String.valueOf(rand.nextInt()); 65 | } 66 | bean.setStrings(Arrays.asList(strings)); 67 | return bean; 68 | } 69 | 70 | public static enum Checker { 71 | STANDARD { 72 | @Override protected boolean equals(Bean bean1, Bean bean2) { 73 | return bean1.equals(bean2); 74 | } 75 | 76 | @Override protected long hashCode(Bean bean) { 77 | return bean.hashCode(); 78 | } 79 | }, 80 | STANDARD_INDIRECT { 81 | @Override protected boolean equals(Bean bean1, Bean bean2) { 82 | return bean1.indirectEquals(bean2); 83 | } 84 | 85 | @Override protected long hashCode(Bean bean) { 86 | return bean.hashCode(); 87 | } 88 | }, 89 | POJOMATIC { 90 | @Override protected boolean equals(Bean bean1, Bean bean2) { 91 | return bean1.pmequals(bean2); 92 | } 93 | @Override protected long hashCode(Bean bean) { 94 | return bean.pmHashCode(); 95 | } 96 | } 97 | , 98 | POJOMATIC_FAST { 99 | @Override protected boolean equals(Bean bean1, Bean bean2) { 100 | return bean1.pmFastequals(bean2); 101 | } 102 | @Override protected long hashCode(Bean bean) { 103 | return bean.pmFastHashCode(); 104 | } 105 | }; 106 | 107 | public void checkEquals(Bean[] beans, int reps) { 108 | int i = 0, j = 0, rep = 0; 109 | 110 | while (rep++ < reps) { 111 | if (equals(beans[i], beans[j]) != (i == j)) { 112 | System.out.println("error at " + i + ", " + j); 113 | } 114 | if (++j == beans.length) { 115 | j = 0; 116 | i = (i + 1) % beans.length; 117 | } 118 | } 119 | } 120 | 121 | public void checkHashCode(Bean[] beans, int reps) { 122 | int i = 0, j = 0, rep = 0; 123 | 124 | while (rep++ < reps) { 125 | if ((hashCode(beans[i]) == hashCode(beans[j])) != (i == j)) { 126 | System.out.println("error at " + i + ", " + j); 127 | } 128 | if (++j == beans.length) { 129 | j = 0; 130 | i = (i + 1) % beans.length; 131 | } 132 | } 133 | } 134 | 135 | protected abstract long hashCode(Bean bean); 136 | 137 | protected abstract boolean equals(Bean bean1, Bean bean2); 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /pojomatic-benchmarks/pojomatic-base-benchmark/src/main/java/org/pojomatic/Bean.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import org.pojomatic.Pojomatic; 7 | import org.pojomatic.Pojomator; 8 | import org.pojomatic.annotations.AutoDetectPolicy; 9 | import org.pojomatic.annotations.AutoProperty; 10 | 11 | @AutoProperty(autoDetect=AutoDetectPolicy.FIELD) 12 | public class Bean { 13 | private int i; 14 | private String string; 15 | private Integer integer; 16 | private int[] ints; 17 | private List strings; 18 | 19 | public int getI() { 20 | return i; 21 | } 22 | public void setI(int i) { 23 | this.i = i; 24 | } 25 | public String getString() { 26 | return string; 27 | } 28 | public void setString(String string) { 29 | this.string = string; 30 | } 31 | public Integer getInteger() { 32 | return integer; 33 | } 34 | public void setInteger(Integer integer) { 35 | this.integer = integer; 36 | } 37 | public int[] getInts() { 38 | return ints; 39 | } 40 | public void setInts(int[] ints) { 41 | this.ints = ints; 42 | } 43 | public List getStrings() { 44 | return strings; 45 | } 46 | public void setStrings(List strings) { 47 | this.strings = strings; 48 | } 49 | 50 | public boolean pmequals(Object other) { 51 | return Pojomatic.equals(this, other); 52 | } 53 | 54 | public int pmHashCode() { 55 | return Pojomatic.hashCode(this); 56 | } 57 | 58 | private final static Pojomator POJOMATOR = Pojomatic.pojomator(Bean.class); 59 | 60 | public boolean pmFastequals(Object other) { 61 | return POJOMATOR.doEquals(this, other); 62 | } 63 | 64 | public int pmFastHashCode() { 65 | return POJOMATOR.doHashCode(this); 66 | } 67 | 68 | @Override public int hashCode() { 69 | final int prime = 31; 70 | int result = 1; 71 | result = prime * result + i; 72 | result = prime * result + ((integer == null) 73 | ? 0 74 | : integer.hashCode()); 75 | result = prime * result + Arrays.hashCode(ints); 76 | result = prime * result + ((string == null) 77 | ? 0 78 | : string.hashCode()); 79 | result = prime * result + ((strings == null) 80 | ? 0 81 | : strings.hashCode()); 82 | return result; 83 | } 84 | 85 | public static boolean doEquals(Bean left, Object right) { 86 | if (left == right) 87 | return true; 88 | if (right == null) 89 | return false; 90 | if (left.getClass() != right.getClass()) 91 | return false; 92 | final Bean other = (Bean) right; 93 | if (left.i != other.i) 94 | return false; 95 | if (left.integer == null) { 96 | if (other.integer != null) 97 | return false; 98 | } 99 | else if (!left.integer.equals(other.integer)) 100 | return false; 101 | if (!Arrays.equals(left.ints, other.ints)) 102 | return false; 103 | if (left.string == null) { 104 | if (other.string != null) 105 | return false; 106 | } 107 | else if (!left.string.equals(other.string)) 108 | return false; 109 | if (left.strings == null) { 110 | if (other.strings != null) 111 | return false; 112 | } 113 | else if (!left.strings.equals(other.strings)) 114 | return false; 115 | return true; 116 | } 117 | 118 | public boolean indirectEquals(Object obj) { 119 | return doEquals(this, obj); 120 | } 121 | 122 | @Override public boolean equals(Object obj) { 123 | if (this == obj) 124 | return true; 125 | if (obj == null) 126 | return false; 127 | if (getClass() != obj.getClass()) 128 | return false; 129 | final Bean other = (Bean) obj; 130 | if (i != other.i) 131 | return false; 132 | if (integer == null) { 133 | if (other.integer != null) 134 | return false; 135 | } 136 | else if (!integer.equals(other.integer)) 137 | return false; 138 | if (!Arrays.equals(ints, other.ints)) 139 | return false; 140 | if (string == null) { 141 | if (other.string != null) 142 | return false; 143 | } 144 | else if (!string.equals(other.string)) 145 | return false; 146 | if (strings == null) { 147 | if (other.strings != null) 148 | return false; 149 | } 150 | else if (!strings.equals(other.strings)) 151 | return false; 152 | return true; 153 | } 154 | 155 | 156 | } 157 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/OverridableMethods.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.lang.reflect.Method; 4 | import java.lang.reflect.Modifier; 5 | import java.util.*; 6 | 7 | /** 8 | * A mutable set of methods which can be overridden. All methods are assumed to take no arguments 9 | * and either public, protected or package private. 10 | */ 11 | class OverridableMethods { 12 | 13 | /** 14 | * Check to see if roles should be added to a method, and add them if so. Only roles not already 15 | * on the method will be added. If {@code method} already has an 16 | * {@link PropertyRole#EQUALS EQUALS} role, and it is requested to add the 17 | * {@link PropertyRole#HASH_CODE HASH_CODE} role, an {@link IllegalArgumentException} will be 18 | * thrown. 19 | * 20 | * @param method the method to check 21 | * @param newRoles the roles to add 22 | * @return the roles which were actually added. 23 | * @throws IllegalArgumentException if {@code method} already has an 24 | * {@link PropertyRole#EQUALS EQUALS} role, and it is requested to add the 25 | * {@link PropertyRole#HASH_CODE HASH_CODE} role 26 | */ 27 | Set checkAndMaybeAddRolesToMethod(Method method, Set newRoles) { 28 | Set existingRoles = findExistingRoles(method); 29 | if (existingRoles.contains(PropertyRole.EQUALS) 30 | && !existingRoles.contains(PropertyRole.HASH_CODE) 31 | && newRoles.contains(PropertyRole.HASH_CODE)) { 32 | throw new IllegalArgumentException( 33 | "Method " + method.getDeclaringClass().getName() + "." + method.getName() 34 | + " is requested to be included in hashCode computations, but already overrides a method" 35 | + " which is requested for equals computations, but not hashCode computations."); 36 | } 37 | Set addedRoles = EnumSet.noneOf(PropertyRole.class); 38 | for (PropertyRole role : newRoles) { 39 | if (!existingRoles.contains(role)) { 40 | addedRoles.add(role); 41 | existingRoles.add(role); 42 | } 43 | } 44 | return addedRoles; 45 | } 46 | 47 | private Set findExistingRoles(Method method) { 48 | Set existingRoles; 49 | if (isPackagePrivate(method)) { 50 | // This can only override another package private method 51 | PackageMethod key = new PackageMethod(method); 52 | existingRoles = packageMethods.get(key); 53 | if (existingRoles == null) { 54 | existingRoles = EnumSet.noneOf(PropertyRole.class); 55 | packageMethods.put(key, existingRoles); 56 | } 57 | } 58 | else { 59 | // If there is a public method already declared, then this is an override. Otherwise, 60 | // we need to track it as a public override going forward, even if it is overriding a 61 | // superclass method which was declared package private. 62 | existingRoles = publicOrProtectedMethods.get(method.getName()); 63 | if (existingRoles == null) { 64 | existingRoles = packageMethods.get(new PackageMethod(method)); 65 | } 66 | if (existingRoles == null) { 67 | existingRoles = EnumSet.noneOf(PropertyRole.class); 68 | publicOrProtectedMethods.put(method.getName(), existingRoles); 69 | } 70 | } 71 | return existingRoles; 72 | } 73 | 74 | /** 75 | * A bean to track the package and name of a package-private method 76 | */ 77 | private static class PackageMethod { 78 | PackageMethod(Method method) { 79 | name = method.getName(); 80 | pakage = method.getDeclaringClass().getPackage(); 81 | } 82 | 83 | final String name; 84 | final Package pakage; 85 | 86 | @Override 87 | public int hashCode() { 88 | return name.hashCode() * 31 + pakage.hashCode(); 89 | } 90 | 91 | @Override 92 | public boolean equals(Object obj) { 93 | if (this == obj) { 94 | return true; 95 | } 96 | if (obj instanceof PackageMethod) { 97 | PackageMethod other = (PackageMethod) obj; 98 | return name.equals(other.name) && pakage.equals(other.pakage); 99 | } 100 | else { 101 | return false; 102 | } 103 | } 104 | } 105 | 106 | private final Map> publicOrProtectedMethods = new HashMap<>(); 107 | private final Map> packageMethods = new HashMap<>(); 108 | 109 | 110 | private static boolean isPackagePrivate(Method method) { 111 | return !(Modifier.isPublic(method.getModifiers()) 112 | || Modifier.isProtected(method.getModifiers())); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /pojomatic/src/test/java/org/pojomatic/internal/OverridableMethodsTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import static org.testng.Assert.*; 4 | 5 | import org.testng.annotations.Test; 6 | import java.lang.reflect.Method; 7 | import java.util.EnumSet; 8 | 9 | import org.pojomatic.internal.a.C1; 10 | import org.pojomatic.internal.a.C3; 11 | import org.pojomatic.internal.b.C2; 12 | import org.pojomatic.internal.b.C4; 13 | 14 | public class OverridableMethodsTest { 15 | private static final EnumSet EQUALS_HASH_CODE = 16 | EnumSet.of(PropertyRole.EQUALS, PropertyRole.HASH_CODE); 17 | private static final EnumSet EQUALS = EnumSet.of(PropertyRole.EQUALS); 18 | private static final EnumSet ALL = EnumSet.allOf(PropertyRole.class); 19 | 20 | @Test public void testPackagePrivate() throws Exception { 21 | checkMethod("packagePrivate", true, true, false, false); 22 | } 23 | 24 | @Test public void testPackagePrivateToProtected() throws Exception { 25 | checkMethod("packagePrivateOverriddenProtected", true, true, false, false); 26 | } 27 | 28 | @Test public void testPackagePrivateToPublic() throws Exception { 29 | checkMethod("packagePrivateOverriddenPublic", true, true, false, false); 30 | } 31 | 32 | @Test public void testProtected() throws Exception { 33 | checkMethod("protectedMethod", true, false, false, false); 34 | } 35 | 36 | @Test public void testPublic() throws Exception { 37 | checkMethod("publicMethod", true, false, false, false); 38 | } 39 | 40 | @Test 41 | public void testEqualsThenEquals() throws Exception { 42 | OverridableMethods overridableMethods = new OverridableMethods(); 43 | assertEquals(overridableMethods.checkAndMaybeAddRolesToMethod( 44 | method(C1.class, "publicMethod"), EQUALS), EQUALS); 45 | assertEquals(overridableMethods.checkAndMaybeAddRolesToMethod( 46 | method(C2.class, "publicMethod"), EQUALS), EnumSet.noneOf(PropertyRole.class)); 47 | } 48 | 49 | @Test 50 | public void testEqualsThenToString() throws Exception { 51 | OverridableMethods overridableMethods = new OverridableMethods(); 52 | assertEquals(overridableMethods.checkAndMaybeAddRolesToMethod( 53 | method(C1.class, "publicMethod"), EQUALS), EQUALS); 54 | assertEquals(overridableMethods.checkAndMaybeAddRolesToMethod( 55 | method(C2.class, "publicMethod"), EnumSet.of(PropertyRole.TO_STRING)), EnumSet.of(PropertyRole.TO_STRING)); 56 | } 57 | 58 | @Test 59 | public void testEqualsThenToEqualsString() throws Exception { 60 | OverridableMethods overridableMethods = new OverridableMethods(); 61 | assertEquals(overridableMethods.checkAndMaybeAddRolesToMethod( 62 | method(C1.class, "publicMethod"), EQUALS), EQUALS); 63 | assertEquals(overridableMethods.checkAndMaybeAddRolesToMethod( 64 | method(C2.class, "publicMethod"), EnumSet.of(PropertyRole.TO_STRING, PropertyRole.EQUALS)), EnumSet.of(PropertyRole.TO_STRING)); 65 | } 66 | 67 | @Test 68 | public void testEqualsHashCodeThenEqualsHashCode() throws Exception { 69 | OverridableMethods overridableMethods = new OverridableMethods(); 70 | assertEquals(overridableMethods.checkAndMaybeAddRolesToMethod( 71 | method(C1.class, "publicMethod"), EQUALS_HASH_CODE), EQUALS_HASH_CODE); 72 | assertEquals(overridableMethods.checkAndMaybeAddRolesToMethod( 73 | method(C2.class, "publicMethod"), EQUALS_HASH_CODE), EnumSet.noneOf(PropertyRole.class)); 74 | } 75 | 76 | @Test 77 | public void testEqualsThenEqualsHashCode() throws Exception { 78 | OverridableMethods overridableMethods = new OverridableMethods(); 79 | assertEquals(overridableMethods.checkAndMaybeAddRolesToMethod( 80 | method(C1.class, "publicMethod"), EQUALS), EQUALS); 81 | try { 82 | overridableMethods.checkAndMaybeAddRolesToMethod( 83 | method(C2.class, "publicMethod"), EQUALS_HASH_CODE); 84 | fail("Exception expected"); 85 | } 86 | catch (IllegalArgumentException e) { 87 | assertEquals(e.getMessage(), "Method org.pojomatic.internal.b.C2.publicMethod is requested to be included in hashCode" 88 | + " computations, but already overrides a method which is requested for" 89 | + " equals computations, but not hashCode computations."); 90 | } 91 | } 92 | 93 | private void checkMethod( 94 | String methodName, boolean c1Add, boolean c2Add, boolean c3Add, boolean c4Add) 95 | throws Exception { 96 | OverridableMethods overridableMethods = new OverridableMethods(); 97 | assertEquals(!overridableMethods.checkAndMaybeAddRolesToMethod( 98 | method(C1.class, methodName), ALL).isEmpty(), c1Add); 99 | assertEquals(!overridableMethods.checkAndMaybeAddRolesToMethod( 100 | method(C2.class, methodName), ALL).isEmpty(), c2Add); 101 | assertEquals(!overridableMethods.checkAndMaybeAddRolesToMethod( 102 | method(C3.class, methodName), ALL).isEmpty(), c3Add); 103 | assertEquals(!overridableMethods.checkAndMaybeAddRolesToMethod( 104 | method(C4.class, methodName), ALL).isEmpty(), c4Add); 105 | } 106 | 107 | private static Method method(Class clazz, String name) throws Exception { 108 | return clazz.getDeclaredMethod(name); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/internal/PropertyClassVisitor.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.internal; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.ArrayList; 6 | import java.util.EnumMap; 7 | import java.util.LinkedHashSet; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | import org.objectweb.asm.ClassReader; 13 | import org.objectweb.asm.ClassVisitor; 14 | import org.objectweb.asm.FieldVisitor; 15 | import org.objectweb.asm.MethodVisitor; 16 | import org.objectweb.asm.Opcodes; 17 | import org.pojomatic.PropertyElement; 18 | 19 | class PropertyClassVisitor extends ClassVisitor { 20 | 21 | private final Map> fieldsMap; 22 | private final Map> methodsMap; 23 | private final Map> sortedProperties = makeProperties(); 24 | 25 | PropertyClassVisitor( 26 | Map> fieldsMap, 27 | Map> methodsMap) { 28 | super(Opcodes.ASM7); 29 | this.fieldsMap = fieldsMap; 30 | this.methodsMap = methodsMap; 31 | 32 | } 33 | 34 | static PropertyClassVisitor visitClass( 35 | Class clazz, 36 | Map> fieldsMap, 37 | Map> methodsMap) { 38 | String classPath = clazz.getName().replace(".", "/") + ".class"; 39 | ClassLoader classLoader = clazz.getClassLoader(); 40 | if (classLoader == null) { 41 | return null; 42 | } 43 | try (InputStream stream = classLoader.getResourceAsStream(classPath)) { 44 | ClassReader classReader = new ClassReader(stream); 45 | PropertyClassVisitor propertyClassVisitor = new PropertyClassVisitor(fieldsMap, methodsMap); 46 | classReader.accept(propertyClassVisitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG); 47 | verifyAllPropertiesFound(clazz, fieldsMap, methodsMap, propertyClassVisitor); 48 | return propertyClassVisitor; 49 | } catch (IOException e) { 50 | return null; 51 | } 52 | } 53 | 54 | private static void verifyAllPropertiesFound(Class clazz, 55 | Map> fieldsMap, 56 | Map> methodsMap, 57 | PropertyClassVisitor propertyClassVisitor) { 58 | for (PropertyRole role: PropertyRole.values()) { 59 | List sortedProperties = propertyClassVisitor.getSortedProperties().get(role); 60 | Map fields = fieldsMap.get(role); 61 | Map methods = methodsMap.get(role); 62 | if (fields.size() + methods.size() != sortedProperties.size()) { 63 | throwReflectionMissmatch(clazz, fields, methods, sortedProperties); 64 | } 65 | } 66 | } 67 | 68 | private static void throwReflectionMissmatch( 69 | Class clazz, 70 | Map fields, 71 | Map methods, 72 | List sortedProperties) { 73 | Set expectedProperties = new LinkedHashSet<>(); 74 | expectedProperties.addAll(fields.values()); 75 | expectedProperties.addAll(methods.values()); 76 | expectedProperties.removeAll(sortedProperties); 77 | StringBuilder message = new StringBuilder("In class ").append(clazz.getName()).append(", properties "); 78 | boolean seenOne = false; 79 | for (PropertyElement property: expectedProperties) { 80 | if (seenOne) { 81 | message.append(", "); 82 | } 83 | seenOne = true; 84 | message.append(property); 85 | } 86 | message.append(" were found in reflection, but not when visiting the bytecode"); 87 | throw new IllegalStateException(message.toString()); 88 | } 89 | 90 | @Override 91 | public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { 92 | if ((access & Opcodes.ACC_STATIC )== 0) { 93 | for (PropertyRole role: PropertyRole.values()) { 94 | PropertyElement propertyElement = fieldsMap.get(role).get(name); 95 | if (propertyElement != null) { 96 | sortedProperties.get(role).add(propertyElement); 97 | } 98 | } 99 | } 100 | return null; 101 | } 102 | 103 | @Override 104 | public MethodVisitor visitMethod(int access, String name, String desc, 105 | String signature, String[] exceptions) { 106 | if (desc.startsWith("()") && ((access & (Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC)) == 0)) { 107 | for (PropertyRole role: PropertyRole.values()) { 108 | PropertyElement propertyElement = methodsMap.get(role).get(name); 109 | if (propertyElement != null) { 110 | sortedProperties.get(role).add(propertyElement); 111 | } 112 | } 113 | } 114 | return null; 115 | } 116 | 117 | public Map> getSortedProperties() { 118 | return sortedProperties; 119 | }; 120 | 121 | private static Map> makeProperties() { 122 | Map> properties = 123 | new EnumMap<>(PropertyRole.class); 124 | for (PropertyRole role : PropertyRole.values()) { 125 | properties.put(role, new ArrayList()); 126 | } 127 | return properties; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /pojomatic-test-utils/src/test/java/org/pojomatic/test/AssertTest.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.test; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | import org.pojomatic.Pojomatic; 7 | import org.pojomatic.junit.PojomaticAssert; 8 | 9 | /** 10 | * Tests assertion methods such as assertEquals(Object, Object) for correctness of assertions. 11 | */ 12 | public abstract class AssertTest { 13 | 14 | 15 | /** 16 | * Only the unit under test should throw {@link AssertionError}, so no assertions are allowed 17 | * to be thrown from within the implementation of this method. 18 | * 19 | * @param expected the expected object 20 | * @param actual the actual object 21 | */ 22 | protected abstract void performAssertEquals(Object expected, Object actual); 23 | 24 | /** 25 | * Only the unit under test should throw {@link AssertionError}, so no assertions are allowed 26 | * to be thrown from within the implementation of this method. 27 | * 28 | * @param expected the expected object 29 | * @param actual the actual object 30 | * @param message the messaage to include with the assertion 31 | */ 32 | protected abstract void performAssertEquals(Object expected, Object actual, String message); 33 | 34 | private void performAssertEquals( 35 | Object expected, Object actual, String message, String expectedMessage) { 36 | try { 37 | performAssertEquals(expected, actual, message); 38 | fail("exception expected"); 39 | } 40 | catch (AssertionError e) { 41 | assertEquals(expectedMessage, e.getMessage()); 42 | } 43 | } 44 | 45 | @Test 46 | public final void assertEqualsWhenEqual() { 47 | performAssertEquals(new Container(3), new Container(3), "message"); 48 | } 49 | 50 | @Test 51 | public final void assertEqualsWhenEqualNoMessage() { 52 | performAssertEquals(new Container(3), new Container(3)); 53 | } 54 | 55 | @Test 56 | public final void assertEqualsBothNull() { 57 | performAssertEquals(null, null, null); 58 | } 59 | 60 | @Test 61 | public final void assertEqualsBothNullNoMessage() { 62 | performAssertEquals(null, null); 63 | } 64 | 65 | @Test 66 | public final void assertEqualsNullExpected() { 67 | performAssertEquals( 68 | null, new Container(null), null, 69 | "expected is null, but actual is Container{test: {null}}"); 70 | } 71 | 72 | @Test 73 | public final void assertEqualsNullExpectedNoMessage() { 74 | performAssertEquals( 75 | null, new Container(null), null, 76 | "expected is null, but actual is Container{test: {null}}"); 77 | } 78 | 79 | @Test 80 | public final void assertEqualsNullActual() { 81 | performAssertEquals( 82 | new Container(null), null, null, "actual is null, but expected is Container{test: {null}}"); 83 | } 84 | 85 | /** 86 | * Tests that {@link PojomaticAssert#assertEqualsWithDiff(Object, Object)} 87 | * uses {@link Object#equals(Object)} instead of {@link Pojomatic#equals(Object, Object)}. 88 | */ 89 | @Test 90 | public final void assertEqualsViaInheritedEquals() { 91 | //create objects which are never equal via Object.equals(Object), but are equal via 92 | //Pojomatic.equals(Object, Object) 93 | OnlyPojomaticEqual first = new OnlyPojomaticEqual(); 94 | OnlyPojomaticEqual second = new OnlyPojomaticEqual(); 95 | performAssertEquals(first, second, null, 96 | "differences between expected and actual:no differences (expected: but was:)"); 97 | } 98 | 99 | @Test 100 | public final void assertEqualsNoMessage() { 101 | try { 102 | performAssertEquals(new Container("foo"), new Container("bar")); 103 | } 104 | catch (AssertionError e) { 105 | assertEquals("differences between expected and actual:[test: {foo} versus {bar}]" + 106 | " (expected: but was:)", e.getMessage()); 107 | } 108 | } 109 | 110 | @Test 111 | public final void assertEqualsNullMessage() { 112 | performAssertEquals( 113 | new Container("foo"), new Container("bar"), null, 114 | "differences between expected and actual:[test: {foo} versus {bar}]" + 115 | " (expected: but was:)"); 116 | } 117 | 118 | @Test 119 | public final void assertEqualsMessage2() { 120 | String first = "foo"; 121 | String second = "bar"; 122 | performAssertEquals( 123 | new Container(first), new Container(second), null, 124 | "differences between expected and actual:[test: {foo} versus {bar}]" + 125 | " (expected: but was:)"); 126 | } 127 | 128 | @Test 129 | public final void assertEqualsCustomMessage() { 130 | performAssertEquals( 131 | new Container("foo"), new Container("bar"), "hello", 132 | "hello differences between expected and actual:[test: {foo} versus {bar}]" + 133 | " (expected: but was:)"); 134 | } 135 | 136 | @Test 137 | public final void assertEqualsNonPojomatic() { 138 | performAssertEquals("string a", "string b", null, "expected: but was:"); 139 | } 140 | 141 | @Test 142 | public final void assertEqualsNonComparable() { 143 | performAssertEquals(new Container("foo"), new DifferentPojo("foo"), null, 144 | "expected: but was:"); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /pojomatic/src/main/java/org/pojomatic/formatter/EnhancedPropertyFormatter.java: -------------------------------------------------------------------------------- 1 | package org.pojomatic.formatter; 2 | 3 | import java.lang.reflect.AnnotatedElement; 4 | 5 | /** 6 | * A formatter for a property. 7 | *

8 | * Any implementation of {@code EnhancedPropertyFormatter} must have a public no-argument constructor. A single instance 9 | * will be created for each property; consequently, implementations must be thread safe. 10 | * 11 | * @since 2.0 12 | * @see DefaultEnhancedPropertyFormatter 13 | */ 14 | @SuppressWarnings("deprecation") 15 | public interface EnhancedPropertyFormatter extends PropertyFormatter { 16 | @Override // avoid deprecation warning 17 | void initialize(AnnotatedElement element); 18 | 19 | /** 20 | * Format an object; no attempt will be made to format it as an array. 21 | * @param builder the builder to append the formatted representation of the object to 22 | * @param o the object to format 23 | */ 24 | void appendFormatted(StringBuilder builder, Object o); 25 | 26 | /** 27 | * Format a boolean. 28 | * @param builder the builder to append the formatted representation of the boolean to 29 | * @param b the boolean to format 30 | */ 31 | void appendFormatted(StringBuilder builder, boolean b); 32 | 33 | /** 34 | * Format a byte 35 | * @param builder the builder to append the formatted representation of the byte to 36 | * @param b the byte to format 37 | */ 38 | void appendFormatted(StringBuilder builder, byte b); 39 | 40 | /** 41 | * Format a short 42 | * @param builder the builder to append the formatted representation of the short to 43 | * @param s the short to format 44 | */ 45 | void appendFormatted(StringBuilder builder, short s); 46 | 47 | /** 48 | * Format a character 49 | * @param builder the builder to append the formatted representation of the character to 50 | * @param c the character to format 51 | */ 52 | void appendFormatted(StringBuilder builder, char c); 53 | 54 | /** 55 | * Format an integer 56 | * @param builder the builder to append the formatted representation of the integer to 57 | * @param i the integer to format 58 | */ 59 | void appendFormatted(StringBuilder builder, int i); 60 | 61 | /** 62 | * Format a long 63 | * @param builder the builder to append the formatted representation of the long to 64 | * @param l the long to format 65 | */ 66 | void appendFormatted(StringBuilder builder, long l); 67 | 68 | /** 69 | * Format a float 70 | * @param builder the builder to append the formatted representation of the float to 71 | * @param f the float to format 72 | */ 73 | void appendFormatted(StringBuilder builder, float f); 74 | 75 | /** 76 | * Format a double 77 | * @param builder the builder to append the formatted representation of the double to 78 | * @param d the double to format 79 | */ 80 | void appendFormatted(StringBuilder builder, double d); 81 | 82 | /** 83 | * Format an array of booleans 84 | * @param builder the builder to append the formatted representation of the array to 85 | * @param booleans the array to format 86 | */ 87 | void appendFormatted(StringBuilder builder, boolean[] booleans); 88 | 89 | /** 90 | * Format an array of bytes 91 | * @param builder the builder to append the formatted representation of the array to 92 | * @param bytes the array to format 93 | */ 94 | void appendFormatted(StringBuilder builder, byte[] bytes); 95 | 96 | /** 97 | * Format an array of shorts 98 | * @param builder the builder to append the formatted representation of the array to 99 | * @param shorts the array to format 100 | */ 101 | void appendFormatted(StringBuilder builder, short[] shorts); 102 | 103 | /** 104 | * Format an array of characters 105 | * @param builder the builder to append the formatted representation of the array to 106 | * @param chars the array to format 107 | */ 108 | void appendFormatted(StringBuilder builder, char[] chars); 109 | 110 | /** 111 | * Format an array of integers 112 | * @param builder the builder to append the formatted representation of the array to 113 | * @param ints the array to format 114 | */ 115 | void appendFormatted(StringBuilder builder, int[] ints); 116 | 117 | /** 118 | * Format an array of longs 119 | * @param builder the builder to append the formatted representation of the array to 120 | * @param longs the array to format 121 | */ 122 | void appendFormatted(StringBuilder builder, long[] longs); 123 | 124 | /** 125 | * Format an array of floats 126 | * @param builder the builder to append the formatted representation of the array to 127 | * @param floats the array to format 128 | */ 129 | void appendFormatted(StringBuilder builder, float[] floats); 130 | 131 | /** 132 | * Format an array of doubles 133 | * @param builder the builder to append the formatted representation of the array to 134 | * @param doubles the array to format 135 | */ 136 | void appendFormatted(StringBuilder builder, double[] doubles); 137 | 138 | /** 139 | * Format an array of Objects 140 | * @param builder the builder to append the formatted representation of the array to 141 | * @param objects the array to format 142 | */ 143 | void appendFormatted(StringBuilder builder, Object[] objects); 144 | 145 | /** 146 | * Format an object, which may be an array. 147 | * @param builder the builder to append the formatted representation of the object to 148 | * @param o the object to format 149 | */ 150 | void appendFormattedPossibleArray(StringBuilder builder, Object o); 151 | } 152 | --------------------------------------------------------------------------------