Fork me on GitHub

Detection of Matcher for Class

General concepts

Since version 0.2.0, the detection of link between classes has been enhanced.

Conceptually, for every fields or parent classes, the annotation processor will try to find a Matcher to compare the values. This is done by detecting the relation between the class in compilation, but also by looking for existing Matcher (new in version 0.2.0).

To understand this concept, let's assume we have a first maven project, that use powerunit-extensions-matchers, with the following class annotated with @ProvideMatchers :

  • Pojo1

Based on this class, a Matcher class named Pojo1Matchers will also be generated. This class will provide several DSL method, like :

  • pojo1With()
  • pojo1WithSameValue()

Now, we have a second maven project, which uses the first one as dependency, we have the following annotated classes :

  • Pojo4 which extends Pojo1
  • Pojo6 which have a Pojo1 field (named field1)

Based on these classes, several Matchers classes will be generated :

  • Pojo4Matchers - The annotation processor detects that the Pojo1Matchers class is a valid Matcher for Pojo1 event if it is already compiled.
    • The method pojo4With() starts a DSL without check on the parent class
    • The method pojo4With(org.hamcrest.Matcher<? super ch.powerunit.extensions.matchers.multi.parent.Pojo1> matcherOnParent) starts a DSL with a matcher on the parent class.
    • The method pojo4WithSameValue() start a DSL to compare for same value. The pojo1WithSameValue() from Pojo1Matchers will be used for the control of the parent class.
  • Pojo6Matchers - The annotation processor detects that the Pojo1Matchers class is a valid Matcher for Pojo1 event if it is already compiled.
    • The method pojo6With() starts the DSL. This DSL will provides a method field1With() chaining to the pojo1With DSL.
    • The method pojo4WithSameValue() start a DSL to compare for same value. The pojo6WithSameValue() from Pojo1Matchers will be used for the control of field1.

Limitations

Circular references

It is possible to create classes that may produce StackOverflowError for the hasSameValue matcher. This is the case with bidirectional class references or as a more generic case, circular references. It is almost impossible to detect these case at compile time ; For example, event if a class A has a field of class B and class B a field of class A, it is not possible to assume we have a bidirectional referencing (the issue is only when an instance of A references a specific instance of B and this instance of B references the previous instance of A.

Handling this case with version 0.2.0

In version 0.2.0, the easy way to break the cycle is to :

  • At compile time, based on the structure on the link, it is possible to annotated one of the referencing fields with @IgnoreInMatcher. The field will be ignored which will break the circular reference.
Handling this case with version 0.3.0

Since version 0.3.0, an additional hasSameValue methods is available to specify fields (based on field name) to be ignored. The previous option is still available, but it is also possible to use this ignore parameter. It is possible to chain the ignore by using a syntax like fieldName.fieldNameChildren.

In case one of the referenced fields or the super class use an old version of the generated matchers and doesn't support the ignore feature, this feature is also not available on fields of the referenced fields or on fields of the super class.

Since version 0.3.0, the generated matchers try to detect the cycle. The hasSameValue methods may detect the cycle and break it by replacing the deep reference with a sameInstance Matcher. This feature is not able to detect cycle involving Map, Collection, Optional or Array.