Currently HiddenField
module has an option to ignore only simple setter methods returning void
. Assume the checkstyle.cfg
has the following:
... <module name="HiddenField"> <property name="ignoreSetter" value="true"/> </module> ...
Then this source file would be flagged:
class MyBuilder { String prop; public void setProp(String prop) { // this gets correctly ignored due to 'ignoreSetter' this.prop = prop; } public MyBuilder withProp(String prop) { // this gets flagged this.prop = prop; return this; } }
The proposed improvement is to add another boolean property 'ignoreBuilderSetter' to HiddenField
module which would for all types named as .*Builder
allow to ignore setter methods named either withX
or setX
and returning the defining type.
See committed xdoc for details.
==============================
1) please squash all commits in one.
2) I do not like that Builder definition is hard-coded, it should be setup by user.
3) why mIgnoreSetter does not work for you ? (please provide detailed
description of code that you have, config that you use and what you
would like to have)
===========================
That for description ,now It see clearly what is your intend.
2) I do not like that Builder definition is hard-coded, it should be setup by user.
you missed that point to fix.
Consider builder example: https://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/querycriteria.html
if you rely on name to distinguish "Builder" do not assume that will
always be named like this. User have to define it, but you can give good
default values.
(mIgnoreBuilderSetter ? "(set|with)" : "set")
Why you think that with set and with are the only names that could be used in Builder, see link above?
===================================
Ok, to have more flexibility over the configuration only makes sense. But a bit more work will be required then.
Before I move further and outline the proposed changes and in order to
avoid possible confusion, I would like to clarify that in the mentioned
Hibernate's Criteria
class, method add(Restriction)
is not a valid setter method, while setMaxResults(int)
is.
Now, in order to accommodate the required features, instead of a newignoreBuilderSetter
property several other properties could be added:
Property Name | Description | Default Value | Example Setting |
---|---|---|---|
setterPrefixRegex |
The regular expression defining acceptable prefixes for setter
methods. The method will considered to be a valid setter method if its
name matches the regular expression ^<setterPrefixRegex><cFieldName>$ , where cFieldName is the capitalized name of the variable being set |
set |
(set|with) |
setterCanReturnItsTypeIn (or suggest an alternative name) |
The regular expression defining what type names are allowed to have setter methods return their defining type. | ^$ |
.*Builder |
Let me know what you think.
=====================================
@wigbam , looks closer to true but .....
Lets come back to original problem. You have problem with setter
recognition, so you need to have option that allow you to specify what
prefix should mark method as "setter".
In you approach we try to specify set of classes(regexp) where that setter recognition is custom.
But what if have few types (.*Builder with "with.*" and .*Creator with
"add.*") in in each of them its own custom prefix that mean setter.
So as we would have one option for Prefix and ClassName we need to
combine regexp to cover all, so we might have false-positives between
Builder and Creator groups (in Creator with is not a setter).
So we come back to original idea that setter recognition should be
general for all classes. Update is simple - just implement
setterPrefixRegex. But this is also give your missed cases when method
"withItem" appear in non Builder class.
term "setter" is highly debatable in Java - it is up to a user , what is setter and what is not. So I think that just implementing setterPrefixRegex will be enough.
do you follow me ?
====================================
@romani, not really, the original problem was to do with Checkstyle's expectation for setters to return void
. Therefore, simply implementing setterPrefixRegex
would not be enough for builders where, typically, setter methods return themselves.
The existing logic could be amended to ignore return types of setters,
but that would be a breaking change, which is bad for obvious reasons.
The proposal I made earlier would allow us to keep current
functionality, while adding the new features on top.
======================================
@wigbam, one more idea:
It look to me that we need only one option that detect Builders classes.
And we will ignore by that type name all inner methods that return that
Builder type - as in builder all methods that return himself is kind of
Setter (no extra options for detecting "with" prefix is required).
So option could be :
ignoreSetterInBuilderTypes="*Builder$"
in description (javadoc) of that option we need describe in detail how it works
Does it make sense ?
============================================
Hi All.
But isn't it is common to have setters in the form
Foo setX(int x) {
this.x = x;
return this;
}
in classes that do not nessesaerly named ".*Builder$"?
I would say that if method satisfy following rules:
It is named (using perl regex) /set(.*)\([^\S,]\s+\1\)/ and it returns either void
or class is in which it is defined or its parent class ( assuming that @override is set )
Then such method should be considered a setter. However in order to maintain backward
compatibily we need to keep meaning of ignoreSetter property intact. Therefore, I would propose
that we call such setters "Exetended Setter" and add following property
<property name="ignoreExtectedSetter" value="true"/>
to the HiddenField module.
================================================
Hi Dmitri,
I got your idea of option. Yes, you are right, your idea is better than all above.
But we will go with simple boolean option
ignoreBuilderSetter.
I do not like enums, as they cost more in further support, and name "extended" is not descriptive:
http://checkstyle.sourceforge.net/property_types.html#parenPad
Thanks,
Roman Ivanov
Put a link at mail list for review, option should be boolean.
* An example of how to configure the check so that it ignores the parameter of |
* a setter method is: |
* An example of how to configure the check so that it ignores the parameter of |
* a chain-setter method. A chain-setter is a setter that returns instance of |
* the class in which it itself is declared. |
* @author Rick Giles |
* @version 1.0 |
// Does it return void or class in which it is declared? |
final DetailAST typeAST = methodAST.findFirstToken(TokenTypes.TYPE); |
if (!typeAST.branchContains(TokenTypes.LITERAL_VOID)) { |
// return type is not void. |
if (!mSetterCanReturnThisClass) { |
for (;;) { |
if (classAST == null |
|| classAST.getType() == TokenTypes.CLASS_DEF) |
{ |
break; |
} |
classAST = classAST.getParent(); |
} |
Hi Dmitri,1) please do squash for all your changes, it will ease code review.1) please pull and rebase on latest master , I recommend to keep your changes in topic branch and do PR from that branch.I see "</p>" in javadoc , but in java8 that tags cause compilation problems2)> A method is recognized as a setter if it is in the following formplease describe first of what is general setter and then your perfect description of builder. I like it, but it bit confusing with "void".Please do not update xdoc till we come to agreement on javadoc content (that will economize you time)3)// go up until we encounter class definitionDetailAST classAST = methodAST.getParent();I do not like that, please update FieldFrame or create wrapped Class to keep reference to class that is currently under investigation4)// we went all the way up but did not find class definition and// therefore unable to verify if this return type is allowed in a// setter or not. HiddenField check will not be ignored.please show me code that will cause us to fail to find top class5)> private boolean isIgnoredSetterParam(DetailAST aAST, String aName)I do not like amount of "returns" in that method, what do you think to make it as one-return method.6) we need to test algorithm on open source. Please take a look at http://maxvetrenko.github.io/, sources are at https://github.com/maxvetrenko/guava-libraries-17-0 and https://github.com/maxvetrenko/maxvetrenko.github.io , http://roman-ivanov.blogspot.com/2014/10/how-to-use-snapshot-checkstyle-version.html .Please create a report and lets review results together, if you think that is reasonable to test on bigger project - you are welcome.thanks,Roman Ivanov
Hi Dmitri,
1) please do squash for all your changes, it will ease code review.
1) please pull and rebase on latest master , I recommend to keep your changes in topic branch and do PR from that branch.I see "</p>" in javadoc , but in java8 that tags cause compilation problems
2)> A method is recognized as a setter if it is in the following formplease describe first of what is general setter and then your perfect description of builder. I like it, but it bit confusing with "void".Please do not update xdoc till we come to agreement on javadoc content (that will economize you time)
3)// go up until we encounter class definitionDetailAST classAST = methodAST.getParent();I do not like that, please update FieldFrame or create wrapped Class to keep reference to class that is currently under investigation
4)// we went all the way up but did not find class definition and// therefore unable to verify if this return type is allowed in a// setter or not. HiddenField check will not be ignored.please show me code that will cause us to fail to find top class
5)> private boolean isIgnoredSetterParam(DetailAST aAST, String aName)I do not like amount of "returns" in that method, what do you think to make it as one-return method.
6) we need to test algorithm on open source. Please take a look at http://maxvetrenko.github.io/, sources are at https://github.com/maxvetrenko/guava-libraries-17-0 and https://github.com/maxvetrenko/maxvetrenko.github.io , http://roman-ivanov.blogspot.com/2014/10/how-to-use-snapshot-checkstyle-version.html .Please create a report and lets review results together, if you think that is reasonable to test on bigger project - you are welcome.
thanks,Roman Ivanov
Hi Dmitriy,Thanks or your persistence and time . I will provide code review a bit later.
> Any ideas what could be wrong?I am attaching pom.xml file that is working, at least for me and for now :) . Base version is https://github.com/maxvetrenko/guava-libraries-17-0
/** stack of sets of field names, |
* one for each class of a set of nested classes. |
*/ |
private FieldFrame mCurrentFrame; lets refactor that to stack and rename class to ClassFrame. 5) private String mInClassName; as we refactor that to stack , that field will not be required. |
Hi Dmiriy,Looks like with such update we end up in refactoring whole Check .... as even testing will be serious no matter what.0) please move all your changes to topic branches, pull our master to you master to let be on 6.1, as there was a lot of changes, but not in your Check ( you should not have any conflictsproblem).1)* </p>please do me favor - remove that from javadoc, for some kind of understandable reason java8 started to put a violation on close P tag.
2)* Any other return types are disallowed.Any other return types will not let method match a setter pattern.
3)* a setter is expanded, so that setter return type can also be a class inwhat a bout enums ? in interface there is no members , but enums are kind of the same class , most likely used for Singletons
4)
/** stack of sets of field names, * one for each class of a set of nested classes. */ private FieldFrame mCurrentFrame;
lets refactor that to stack and rename class to ClassFrame.
5)
private String mInClassName;
as we refactor that to stack , that field will not be required.
6)public void visitToken(DetailAST aAST)we need to refactor that method to haveswitch() {VARIABLES:processVariable()CLASS and ENUM:processType()}too much is put in that methods
7)private void processVariable(DetailAST aAST)please make a one return, that will simplify method.
8)private boolean isMethodASetter(DetailAST aMethodAST, String aName)I like you changes in logic, but please keep one return in method, there is no reason to have two there
9)private boolean isIgnoredSetterParam(DetailAST aAST, String aName)please move that method to me located above isMethodASetter, as it is callled first.
10)private static String capitalize(final String aName)private boolean isIgnoredSetterParam(DetailAST aAST, String aName)I understand that is not your code , but please do me favor - lets keep one return point from method.
11)please do me a favor , please activate only your Check , all other should be removed from config, report should be regenerated.
From: Roman Ivanov Sent: Tuesday, January 20, 2015 6:14 PM To: Dmitri Priimak |
Subject: Re: hidden field ignore rule for builder setter methods |
--
You received this message because you are subscribed to the Google Groups "checkstyle-devel" group.
To unsubscribe from this group and stop receiving emails from it, send an email to checkstyle-dev...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Hi Dmitry,please always send me link to sources, you are not alone who try to push change to Checkstyle, I can not remember all of you.1)> if (!ScopeUtils.inInterfaceOrAnnotationBlock(aAST)&& ( ScopeUtils.isLocalVariableDef(aAST)|| (aAST.getType() == TokenTypes.PARAMETER_DEF))) {//local variable or parameter. Does it shadow a field?final DetailAST nameAST = aAST.findFirstToken(TokenTypes.IDENT);final String name = nameAST.getText();if ((mCurrentFrame.containsStaticField(name)please add "\n" , before "//local ...." and " if ((mCurrentFrame.containsStaticField(name)", it is hard to read.
2)> private boolean isMethodASetter(DetailAST aMethodAST, String aName)isSetterMethod. please avoid abbreviation and abreviation-like names, we already have a Check for that, One day I will enforce it over checkstyle .
3)> boolean isASetter = false;the same as above, please rename.
4)> private boolean isIgnoredConstructorParam(DetailAST aAST)> private boolean isIgnoredParamOfAbstractMethod(DetailAST aAST)I usually ok, so multiple return point from method if method is small, but here you use "!" too much. Please redo that in one return point method.Example:boolean result = false;if ((aAST.getType() == TokenTypes.PARAMETER_DEF)&& mIgnoreConstructorParameter){final DetailAST parametersAST = aAST.getParent();final DetailAST constructorAST = parametersAST.getParent();result = (constructorAST.getType() == TokenTypes.CTOR_DEF);}return result;
5)still waiting from you testing report, code looks ok.