@FieldNameConstants delegate/subproperties

1,286 views
Skip to first unread message

Alan

unread,
Apr 16, 2019, 12:20:25 AM4/16/19
to Project Lombok
Not sure if this has been proposed before. Fairly sure it might have been thought about.
In our code base FieldNameConstants is invaluable. We primarily use it for references to our form model fields for binding gui components.
One very common use case for us is when we want a field of a field.

At the moment we have quite verbose and almost as easy to mess up as standard constants, but at least we get compile error in case of renamed field.

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldNameConstants;

@Getter
@Setter
@FieldNameConstants
public class Example {
  private final ComplexDomainObject field = new ComplexDomainObject();

  @FieldNameConstants
  static class SimpleDomainObject {
    String stringProperty;
    boolean booleanProperty;
  }

  @FieldNameConstants
  static class ComplexDomainObject {
    SimpleDomainObject one;
    SimpleDomainObject two;
  }

  public static final class Fields {
    public static final String field_one = Fields.field + "." + ComplexDomainObject.Fields.one;
    public static final String field_two = Fields.field + "." + ComplexDomainObject.Fields.two;
    public static final String field_one_stringProperty = Fields.field + "." + ComplexDomainObject.Fields.one + "." + SimpleDomainObject.Fields.stringProperty;
    public static final String field_one_booleanProperty = Fields.field + "." + ComplexDomainObject.Fields.one + "." + SimpleDomainObject.Fields.booleanProperty;
    public static final String field_two_stringProperty = Fields.field + "." + ComplexDomainObject.Fields.two + "." + SimpleDomainObject.Fields.stringProperty;
    public static final String field_two_booleanProperty = Fields.field + "." + ComplexDomainObject.Fields.two + "." + SimpleDomainObject.Fields.booleanProperty;
  }
}




Or good old fashioned constants but,  no compile error in case something gets renamed.

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldNameConstants;

@Getter
@Setter
@FieldNameConstants
public class Example {
  private final ComplexDomainObject field = new ComplexDomainObject();

  @FieldNameConstants
  static class SimpleDomainObject {
    String stringProperty;
    boolean booleanProperty;
  }

  @FieldNameConstants
  static class ComplexDomainObject {
    SimpleDomainObject one;
    SimpleDomainObject two;
  }

  public static final class Fields {
    public static final String field_one = "field.one";
    public static final String field_two = "field.two";
    public static final String field_one_stringProperty = "field.one.stringProperty";
    public static final String field_one_booleanProperty = "field.one.booleanProperty";
    public static final String field_two_stringProperty = "field.two.stringProperty";
    public static final String field_two_booleanProperty = "field.two.booleanProperty";
  }
}






Would you be open to the idea of adding some way to include delegate fields?

With lombok
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@FieldNameConstants
public class Example {
  private final ComplexDomainObject field = new ComplexDomainObject();

  static class SimpleDomainObject {
    String stringProperty;
    boolean booleanProperty;
  }

  static class ComplexDomainObject {
    SimpleDomainObject one;
    SimpleDomainObject two;
  }
}



delombok @FieldNameConstants

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Example {
  private final ComplexDomainObject field = new ComplexDomainObject();

  static class SimpleDomainObject {
    String stringProperty;
    boolean booleanProperty;
  }

  static class ComplexDomainObject {
    SimpleDomainObject one;
    SimpleDomainObject two;
  }

  public static final class Fields {
    public static final String field_one = "field.one";//very common use case for me ~80% of the time we are using constants like this for binding fields to UI components
    public static final String field_two = "field.two";
    public static final String field_one_stringProperty = "field.one.stringProperty";//less common but still not extraordinary
    public static final String field_one_booleanProperty = "field.one.booleanProperty";
    public static final String field_two_stringProperty = "field.two.stringProperty";
    public static final String field_two_booleanProperty = "field.two.booleanProperty";
  }
}



Thanks for your consideration,

Alan

Mat Jaggard

unread,
Apr 16, 2019, 2:01:08 AM4/16/19
to project-lombok
Could you give more indication as to why you want this please? I've never wanted constants like this in my code.

Many thanks,
Mat.


--
You received this message because you are subscribed to the Google Groups "Project Lombok" group.
To unsubscribe from this group and stop receiving emails from it, send an email to project-lombo...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alan

unread,
Apr 16, 2019, 12:10:50 PM4/16/19
to Project Lombok
Hi Mat,

We have pretty much 3 common use cases for String constants like that. Lets call it property paths.
Starting from most common.

We use spring rcp(were stuck with it for the foreseeable future) to build our GUI form layouts. Access form model via property paths etc.

We have a custom implementation of Table that we configure via property paths. Ie, which columns to display, which cell renderers/editors to use etc.
vaguely similar to spring rcp AbstractObjectTable https://dzone.com/articles/getting-further-with-spring-rc

We have various usages of spring's BeanWrapperImpl that accept property paths. One example is we use an custom annotation to map search bean properties to result bean properties for creating matcher editors that access via bean wrapper for usage with GlazedLists.

Ideally our model would have flat structure, but many times it does not.
Hope that helps some.

Thanks,
Alan
To unsubscribe from this group and stop receiving emails from it, send an email to project...@googlegroups.com.

Alan

unread,
Apr 16, 2019, 12:17:20 PM4/16/19
to Project Lombok
Formatting/naming of the constant names is not particularly important to me at least. We normally use FIELD_PROPERTY_NAME style but as we know that was not the current direction taken. The values of the constants is what really matter for me.

Reinier Zwitserloot

unread,
Apr 23, 2019, 10:34:21 AM4/23/19
to Project Lombok
It feels utterly bewildering without context, that's my main problem with it.

_without_ the context of: This is useful specifically when you're working with Spring RCP, this is the description of the feature:

When you enable 'innerClassDelagation=true' on @FieldNameConstants, for any fields of the so marked class whose type so happens to be an inner type in this very same source file, the fields of said inner type will also show up in the generated Fields container as a field name constant. The naming pattern is based on concatenating the fieldname in the main class (whose type is one of your inner types) with the fieldname in the inner type, using an underscore as separator. The process is recursive; if the type of the field in the inner class is itself of a type that is both present in your source file and not already processed (to avoid infinite loops), then you'd end up with a field name constant for that field, too. Note that this means you can end up with multiple field name constants that are all referring to the exact same field. The string value of these field name constants is not, as one might expect, the actual field name. It's the concatenation of the same chain of field names... but this time with a dot instead of an underscore.


I do believe the correct response to the above paragraph, which probably doesn't quite entirely fully specify this feature, is: ????!!!???? WHAT THE .... ?????

and there in lies a problem.

We can't add features whose purpose is so esoteric it doesn't even make any sense.

We _CAN_ introduce features that are designed specifically with a library or pattern in mind. This description is fine:

When you enable SpringRPC mode, using: @lombok.experimental.FieldNameConstants @lombok.extern.springrpc.SpringFieldNameConstants, lombok generates springRPC-style field name constants for all relevant fields. See this example (example here).  (full description of the convoluted process as above in the smallprint).

But, I don't even know what spring RPC is, and your evidently somewhat disdainful opinion of it makes me rather hesitant to believe that we can find enough maintainers for this feature, and would imply it's hard to convince us to pick up the mantle of supporting this feature.

In regards to how this meshes well with your custom needs: Lombok cannot cater to the needs of a single organization like this. What you're effectively asking for is a feature to be included in the package 'lombok.extern.internalStuffNotOnAnyOpenSourceRepo.yourcompanynamehere.CustomFeature'. Which would be absolutely fantastic if we can support that... but obviously NOT in lombok itself, more as the notion that you can expand lombok with your own needs.

And whilst that is most absolutely a feature we want to add, it's very complicated and it's not even on the roadmap yet. Other stuff to do first.

That's a rather longwinded way of saying: I really, really doubt this feature would make it, and without some further insights, we wouldn't accept a PR with a full impl due to the maintenance burden it would imply.

Alan

unread,
Apr 23, 2019, 6:54:44 PM4/23/19
to Project Lombok
It could be simplified some by throwing out recursive. Would still cover 80 percent of our use cases.
Simplify it even more with regards to naming(user specified prefix for name and value) and inner classes(any field that is marked for delegate inclusion via annotation). 
I can see how the value not being the actual field name could be misleading, but i think that's where naming pattern would help. Or as you suggested a different lombok feature name all together.
I'm curious to know what other applications of spring framework bean wrapper are out there and if people would be interested. As that is the underlying reason to need such constants.

As you said it is still quite specific. Without other requests or support for such a feature i can absolutely understand your position and would say fair enough :)

Thank you for taking the time,
Alan

Martin Grajcar

unread,
Apr 23, 2019, 7:53:23 PM4/23/19
to project...@googlegroups.com
This feature could be used with Hibernate / JPA (not JPA2, which needs no such strings) as well.
But AFAIK Lombok could only generate things like "address.street", when "Address" is defined in the same source file.
Looking at my Hibernate entities, this is pretty never the case.

I was thinking about a solution (for me, JPA2 is more of a problem than a solution) and I believe, I've got a workable workaround (using no Lombok):
Simply use reflection (and class path scanning) for generating one or more Java files containing the constants.
This is somewhat brittle as you need the code to compile in order to generate parts of it, but for me, it works well.
Because of that brittleness, the generated code should be tracked by version control.

I've never implemented exactly this, but I did something similarly hacky and got no problems with it.
The necessity of placing the generated code in new files is no big deal and that's what Hibernate does as well.
As reflection is way simpler than APT or even hacking Lombok, I'm pretty happy with my solution and I'd never switch to writing all the stuff manually.




To unsubscribe from this group and stop receiving emails from it, send an email to project-lombo...@googlegroups.com.

Mat Jaggard

unread,
Apr 24, 2019, 2:07:17 AM4/24/19
to project-lombok
Just FYI, we use Spring extensively in my organisation but I've not cone across a requirement anything like this one. But I do LOVE @FieldNameConstants for handling serialised versions of data.

Martin Grajcar

unread,
Apr 24, 2019, 10:07:36 PM4/24/19
to project...@googlegroups.com
I've just found this article describing (mainly) source code generation in the test phase.
A very similar idea to what I do, just much more elaborated and offering a code generation tool.

Reinier Zwitserloot

unread,
May 2, 2019, 10:50:10 AM5/2/19
to Project Lombok
implementation-wise the proposal is simple enough, _IFF_ it is acceptable that ONLY types _within the same source file_ need apply (otherwise, it would require resolution which is very complicated, see lombok wiki entry 'resolution').

The problem is spec-wise. How do we explain what this does? How do we ensure someone who reads a source file with @ThisFeature at the top of it kinda clues into what it might do even without documentation?

akaine

unread,
Jun 26, 2019, 10:43:03 PM6/26/19
to Project Lombok
+1 to add this feature!

String.join(".", ParentBean.Fields.childBeanField, ChildBean.Fields.field)
is just a torture.

akaine

unread,
Jun 26, 2019, 11:01:24 PM6/26/19
to Project Lombok
Add "recursive" (or something similar) boolean attribute to FieldNameConstants annotation.

The logic could be:

- Recursive check if field type is also annotated with @FieldNameConstants.
- If yes, generate additional constant of the lower level:
child_childOfChild = "child.childOfChild";
- Go down one level and repeat.
- To avoid cyclical dependency issues, keep track of processed types and stop when hitting any type twice.

Example code:
@FieldNameConstants(recursive = true)
public class Parent {
   
private Child child;
   
...

   
// would generate

   
public static final class Fields {

       
public static final String child = "child";
       
public static final String child_childOfChild = "child.childOfChild";
       
public static final String child_childOfChild_someField = "child.childOfChild.someField";
   
}
}

@FieldNameConstants(recursive = true)
public class Child {
   
private ChildOfChild childOfChild;
   
...

   
// would generate

   
public static final class Fields {

       
public static final String childOfChild = "childOfChild";
       
public static final String childOfChild_someField = "child.childOfChild.someField";
   
}
}

@FieldNameConstants(recursive = true)
public class ChildOfChild {
   
private String someField;
   
...

   
// would generate

   
public static final class Fields {

       
public static final String someField = "someField";
   
}
}


Reinier Zwitserloot

unread,
Jul 1, 2019, 8:54:55 AM7/1/19
to Project Lombok
@akaine as I said, if the child type is not in the exact same source file, we can't do this easily. See https://github.com/rzwitserloot/lombok/wiki/LOMBOK-CONCEPT:-Resolution - given that so many already chimed in thinking that's how it would work (that lombok will check if the field type is itself @FNC, even if it is not in the same source file), we can't add this; it would cause too much confusion.

akaine

unread,
Jul 3, 2019, 2:51:01 PM7/3/19
to Project Lombok
@Reinier Zwitserloot, The day I made my post I started tinkering with lombok source code and actually was able to follow bean threes through their properties by just making LombokProcessor.roundEnv variable public static. This way it is possible to get access to source files outside the currently processed file:

public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameConstants> {
   
...
   
final Set<? extends Element> elemetns = LombokProcessor.roundEnv.getElementsAnnotatedWith(FieldNameConstants.class);
   
...
}

I'm still in the process writing the necessary logic, but at the moment everything works just fine: the key logic consisting in field type detection and corresponding source file follow up works. I'm expecting a lot of trouble with HandleFieldNameConstants extends EclipseAnnotationHandler<FieldNameConstants> though, since I have no knowledge of org.eclipse.jdt.internal.compiler.

Reinier Zwitserloot

unread,
Jul 5, 2019, 10:57:19 AM7/5/19
to Project Lombok
As you've discovered, this is easier in javac, but you're being deceived a little bit; for local classes and the like I don't think what you're doing now is going to work. Admittedly you have a fallback (that local class would neccessarily be in the same source file so you can fall back to AST scanning as normal), but...

More to the point, in eclipse, this is MUCH more difficult, partly because eclipse is orders of magnitude faster than javac, and needs to be for the editor to feel snappy. ecj made some concessions to gain this speed, and as part of that, attempting to chase down types like this is doable but verrrry tricky especially if you don't want the IDE's performance to go down the drain.

We have an extremely strict policy on resolution-requiring features: We cannot accept them no matter how well tested, documented, and written, unless you staple a legally actionable guarantee to it that you'll be around for YEARS to support the feature. That, or roel and/or I needs to find it sufficiently relevant to our own itches that we take on that burden.
Reply all
Reply to author
Forward
0 new messages