001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2021 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.coding;
021
022import java.util.HashSet;
023import java.util.Locale;
024import java.util.Objects;
025import java.util.Set;
026import java.util.regex.Pattern;
027
028import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
029import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
030import com.puppycrawl.tools.checkstyle.api.DetailAST;
031import com.puppycrawl.tools.checkstyle.api.Scope;
032import com.puppycrawl.tools.checkstyle.api.TokenTypes;
033import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
034import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
035import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
036
037/**
038 * <p>
039 * Checks that a local variable or a parameter does not shadow
040 * a field that is defined in the same class.
041 * </p>
042 * <p>
043 * It is possible to configure the check to ignore all property setter methods.
044 * </p>
045 * <p>
046 * A method is recognized as a setter if it is in the following form
047 * </p>
048 * <pre>
049 * ${returnType} set${Name}(${anyType} ${name}) { ... }
050 * </pre>
051 * <p>
052 * where ${anyType} is any primitive type, class or interface name;
053 * ${name} is name of the variable that is being set and ${Name} its
054 * capitalized form that appears in the method name. By default it is expected
055 * that setter returns void, i.e. ${returnType} is 'void'. For example
056 * </p>
057 * <pre>
058 * void setTime(long time) { ... }
059 * </pre>
060 * <p>
061 * Any other return types will not let method match a setter pattern. However,
062 * by setting <em>setterCanReturnItsClass</em> property to <em>true</em>
063 * definition of a setter is expanded, so that setter return type can also be
064 * a class in which setter is declared. For example
065 * </p>
066 * <pre>
067 * class PageBuilder {
068 *   PageBuilder setName(String name) { ... }
069 * }
070 * </pre>
071 * <p>
072 * Such methods are known as chain-setters and a common when Builder-pattern
073 * is used. Property <em>setterCanReturnItsClass</em> has effect only if
074 * <em>ignoreSetter</em> is set to true.
075 * </p>
076 * <ul>
077 * <li>
078 * Property {@code ignoreFormat} - Define the RegExp for names of variables
079 * and parameters to ignore.
080 * Type is {@code java.util.regex.Pattern}.
081 * Default value is {@code null}.
082 * </li>
083 * <li>
084 * Property {@code ignoreConstructorParameter} - Control whether to ignore constructor parameters.
085 * Type is {@code boolean}.
086 * Default value is {@code false}.
087 * </li>
088 * <li>
089 * Property {@code ignoreSetter} - Allow to ignore the parameter of a property setter method.
090 * Type is {@code boolean}.
091 * Default value is {@code false}.
092 * </li>
093 * <li>
094 * Property {@code setterCanReturnItsClass} - Allow to expand the definition of a setter method
095 * to include methods that return the class' instance.
096 * Type is {@code boolean}.
097 * Default value is {@code false}.
098 * </li>
099 * <li>
100 * Property {@code ignoreAbstractMethods} - Control whether to ignore parameters
101 * of abstract methods.
102 * Type is {@code boolean}.
103 * Default value is {@code false}.
104 * </li>
105 * <li>
106 * Property {@code tokens} - tokens to check
107 * Type is {@code java.lang.String[]}.
108 * Validation type is {@code tokenSet}.
109 * Default value is:
110 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
111 * VARIABLE_DEF</a>,
112 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PARAMETER_DEF">
113 * PARAMETER_DEF</a>,
114 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PATTERN_VARIABLE_DEF">
115 * PATTERN_VARIABLE_DEF</a>,
116 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
117 * LAMBDA</a>,
118 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_COMPONENT_DEF">
119 * RECORD_COMPONENT_DEF</a>.
120 * </li>
121 * </ul>
122 * <p>
123 * To configure the check:
124 * </p>
125 * <pre>
126 *  &lt;module name=&quot;HiddenField&quot;/&gt;
127 * </pre>
128 * <pre>
129 * public class SomeClass {
130 *
131 *   private String field;
132 *   private String testField;
133 *
134 *   public SomeClass(String testField) { // violation, 'testField' param hides 'testField' field
135 *   }
136 *   public void method(String param) { // OK
137 *       String field = param; // violation, 'field' variable hides 'field' field
138 *   }
139 *   public void setTestField(String testField) { // violation, 'testField' param
140 *                                                // hides 'testField' field
141 *       this.field = field;
142 *   }
143 *   public SomeClass setField(String field) { // violation, 'field' param hides 'field' field
144 *       this.field = field;
145 *   }
146 * }
147 * </pre>
148 *
149 * <p>
150 * To configure the check so that it checks local variables but not parameters:
151 * </p>
152 * <pre>
153 * &lt;module name=&quot;HiddenField&quot;&gt;
154 *   &lt;property name=&quot;tokens&quot; value=&quot;VARIABLE_DEF&quot;/&gt;
155 * &lt;/module&gt;
156 * </pre>
157 * <pre>
158 * public class SomeClass {
159 *
160 *   private String field;
161 *   private String testField;
162 *
163 *   public SomeClass(String testField) { // OK, 'testField' param doesn't hide any field
164 *   }
165 *   public void method(String param) { // OK
166 *       String field = param; // violation, 'field' variable hides 'field' field
167 *   }
168 *   public void setTestField(String testField) { // OK, 'testField' param doesn't hide any field
169 *       this.field = field;
170 *   }
171 *   public SomeClass setField(String field) { // OK, 'field' param doesn't hide any field
172 *       this.field = field;
173 *   }
174 * }
175 * </pre>
176 *
177 * <p>
178 * To configure the check so that it ignores the variables and parameters named "test":
179 * </p>
180 * <pre>
181 * &lt;module name=&quot;HiddenField&quot;&gt;
182 *   &lt;property name=&quot;ignoreFormat&quot; value=&quot;^testField&quot;/&gt;
183 * &lt;/module&gt;
184 * </pre>
185 * <pre>
186 * public class SomeClass {
187 *
188 *   private String field;
189 *   private String testField;
190 *
191 *   public SomeClass(String testField) { // OK, because it match ignoreFormat
192 *   }
193 *   public void method(String param) { // OK
194 *       String field = param; // violation, 'field' variable hides 'field' field
195 *   }
196 *   public void setTestField(String testField) { // OK, because it match ignoreFormat
197 *       this.field = field;
198 *   }
199 *   public SomeClass setField(String field) { // violation, 'field' param hides 'field' field
200 *       this.field = field;
201 *   }
202 * }
203 * </pre>
204 * <p>
205 * To configure the check so that it ignores constructor parameters:
206 * </p>
207 * <pre>
208 * &lt;module name=&quot;HiddenField&quot;&gt;
209 *   &lt;property name=&quot;ignoreConstructorParameter&quot; value=&quot;true&quot;/&gt;
210 * &lt;/module&gt;
211 * </pre>
212 * <pre>
213 * public class SomeClass {
214 *
215 *   private String field;
216 *   private String testField;
217 *
218 *   public SomeClass(String testField) { // OK, 'testField' param doesn't hide any field
219 *   }
220 *   public void method(String param) { // OK
221 *       String field = param; // violation, 'field' variable hides 'field' field
222 *   }
223 *   public void setTestField(String testField) { // violation, 'testField' variable
224 *                                                // hides 'testField' field
225 *       this.field = field;
226 *   }
227 *   public SomeClass setField(String field) { // violation, 'field' param hides 'field' field
228 *       this.field = field;
229 *   }
230 * }
231 * </pre>
232 * <p>
233 * To configure the check so that it ignores the parameter of setter methods:
234 * </p>
235 * <pre>
236 * &lt;module name=&quot;HiddenField&quot;&gt;
237 *   &lt;property name=&quot;ignoreSetter&quot; value=&quot;true&quot;/&gt;
238 * &lt;/module&gt;
239 * </pre>
240 * <pre>
241 * public class SomeClass {
242 *
243 *   private String field;
244 *   private String testField;
245 *
246 *   public SomeClass(String testField) { // violation, 'testField' param hides 'testField' field
247 *   }
248 *   public void method(String param) { // OK
249 *       String field = param; // violation, 'field' variable hides 'field' field
250 *   }
251 *   public void setTestField(String testField) { // OK, 'testField' param doesn't hide any field
252 *       this.field = field;
253 *   }
254 *   public SomeClass setField(String field) { // violation, 'field' param hides 'field' field
255 *       this.field = field;
256 *   }
257 * }
258 * </pre>
259 * <p>
260 * To configure the check so that it ignores the parameter of setter methods
261 * recognizing setter as returning either {@code void} or a class in which it is declared:
262 * </p>
263 * <pre>
264 * &lt;module name=&quot;HiddenField&quot;&gt;
265 *   &lt;property name=&quot;ignoreSetter&quot; value=&quot;true&quot;/&gt;
266 *   &lt;property name=&quot;setterCanReturnItsClass&quot; value=&quot;true&quot;/&gt;
267 * &lt;/module&gt;
268 * </pre>
269 * <pre>
270 * public class SomeClass {
271 *
272 *   private String field;
273 *   private String testField;
274 *
275 *   public SomeClass(String testField) { // violation, 'testField' param hides 'testField' field
276 *   }
277 *   public void method(String param) { // OK
278 *       String field = param; // violation, 'field' variable hides 'field' field
279 *   }
280 *   public void setTestField(String testField) { // OK, 'testField' param doesn't hide any field
281 *       this.field = field;
282 *   }
283 *   public SomeClass setField(String field) { // OK, 'field' param doesn't hide any field
284 *       this.field = field;
285 *   }
286 * }
287 * </pre>
288 * <p>
289 * To configure the check so that it ignores parameters of abstract methods:
290 * </p>
291 * <pre>
292 * &lt;module name=&quot;HiddenField&quot;&gt;
293 *   &lt;property name=&quot;ignoreAbstractMethods&quot; value=&quot;true&quot;/&gt;
294 * &lt;/module&gt;
295 * </pre>
296 * <pre>
297 * abstract class SomeClass {
298 *
299 *   private String field;
300 *
301 *   public SomeClass(int field) { // violation, 'field' param hides a 'field' field
302 *     float field; // violation, 'field' variable hides a 'field' field
303 *   }
304 *   public abstract int method(String field); // OK
305 * }
306 *
307 * public class Demo extends SomeClass {
308 *
309 *   public int method(String param){
310 *     return param;
311 *   }
312 * }
313 * </pre>
314 * <p>
315 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
316 * </p>
317 * <p>
318 * Violation Message Keys:
319 * </p>
320 * <ul>
321 * <li>
322 * {@code hidden.field}
323 * </li>
324 * </ul>
325 *
326 * @since 3.0
327 */
328@FileStatefulCheck
329public class HiddenFieldCheck
330    extends AbstractCheck {
331
332    /**
333     * A key is pointing to the warning message text in "messages.properties"
334     * file.
335     */
336    public static final String MSG_KEY = "hidden.field";
337
338    /**
339     * Stack of sets of field names,
340     * one for each class of a set of nested classes.
341     */
342    private FieldFrame frame;
343
344    /** Define the RegExp for names of variables and parameters to ignore. */
345    private Pattern ignoreFormat;
346
347    /**
348     * Allow to ignore the parameter of a property setter method.
349     */
350    private boolean ignoreSetter;
351
352    /**
353     * Allow to expand the definition of a setter method to include methods
354     * that return the class' instance.
355     */
356    private boolean setterCanReturnItsClass;
357
358    /** Control whether to ignore constructor parameters. */
359    private boolean ignoreConstructorParameter;
360
361    /** Control whether to ignore parameters of abstract methods. */
362    private boolean ignoreAbstractMethods;
363
364    @Override
365    public int[] getDefaultTokens() {
366        return getAcceptableTokens();
367    }
368
369    @Override
370    public int[] getAcceptableTokens() {
371        return new int[] {
372            TokenTypes.VARIABLE_DEF,
373            TokenTypes.PARAMETER_DEF,
374            TokenTypes.CLASS_DEF,
375            TokenTypes.ENUM_DEF,
376            TokenTypes.ENUM_CONSTANT_DEF,
377            TokenTypes.PATTERN_VARIABLE_DEF,
378            TokenTypes.LAMBDA,
379            TokenTypes.RECORD_DEF,
380            TokenTypes.RECORD_COMPONENT_DEF,
381        };
382    }
383
384    @Override
385    public int[] getRequiredTokens() {
386        return new int[] {
387            TokenTypes.CLASS_DEF,
388            TokenTypes.ENUM_DEF,
389            TokenTypes.ENUM_CONSTANT_DEF,
390            TokenTypes.RECORD_DEF,
391        };
392    }
393
394    @Override
395    public void beginTree(DetailAST rootAST) {
396        frame = new FieldFrame(null, true, null);
397    }
398
399    @Override
400    public void visitToken(DetailAST ast) {
401        final int type = ast.getType();
402        switch (type) {
403            case TokenTypes.VARIABLE_DEF:
404            case TokenTypes.PARAMETER_DEF:
405            case TokenTypes.PATTERN_VARIABLE_DEF:
406            case TokenTypes.RECORD_COMPONENT_DEF:
407                processVariable(ast);
408                break;
409            case TokenTypes.LAMBDA:
410                processLambda(ast);
411                break;
412            default:
413                visitOtherTokens(ast, type);
414        }
415    }
416
417    /**
418     * Process a lambda token.
419     * Checks whether a lambda parameter shadows a field.
420     * Note, that when parameter of lambda expression is untyped,
421     * ANTLR parses the parameter as an identifier.
422     *
423     * @param ast the lambda token.
424     */
425    private void processLambda(DetailAST ast) {
426        final DetailAST firstChild = ast.getFirstChild();
427        if (firstChild != null
428                && firstChild.getType() == TokenTypes.IDENT) {
429            final String untypedLambdaParameterName = firstChild.getText();
430            if (frame.containsStaticField(untypedLambdaParameterName)
431                || isInstanceField(firstChild, untypedLambdaParameterName)) {
432                log(firstChild, MSG_KEY, untypedLambdaParameterName);
433            }
434        }
435    }
436
437    /**
438     * Called to process tokens other than {@link TokenTypes#VARIABLE_DEF}
439     * and {@link TokenTypes#PARAMETER_DEF}.
440     *
441     * @param ast token to process
442     * @param type type of the token
443     */
444    private void visitOtherTokens(DetailAST ast, int type) {
445        // A more thorough check of enum constant class bodies is
446        // possible (checking for hidden fields against the enum
447        // class body in addition to enum constant class bodies)
448        // but not attempted as it seems out of the scope of this
449        // check.
450        final DetailAST typeMods = ast.findFirstToken(TokenTypes.MODIFIERS);
451        final boolean isStaticInnerType =
452                typeMods != null
453                        && typeMods.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
454        final String frameName;
455
456        if (type == TokenTypes.CLASS_DEF
457                || type == TokenTypes.ENUM_DEF) {
458            frameName = ast.findFirstToken(TokenTypes.IDENT).getText();
459        }
460        else {
461            frameName = null;
462        }
463        final FieldFrame newFrame = new FieldFrame(frame, isStaticInnerType, frameName);
464
465        // add fields to container
466        final DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK);
467        // enum constants may not have bodies
468        if (objBlock != null) {
469            DetailAST child = objBlock.getFirstChild();
470            while (child != null) {
471                if (child.getType() == TokenTypes.VARIABLE_DEF) {
472                    final String name =
473                        child.findFirstToken(TokenTypes.IDENT).getText();
474                    final DetailAST mods =
475                        child.findFirstToken(TokenTypes.MODIFIERS);
476                    if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
477                        newFrame.addInstanceField(name);
478                    }
479                    else {
480                        newFrame.addStaticField(name);
481                    }
482                }
483                child = child.getNextSibling();
484            }
485        }
486        if (ast.getType() == TokenTypes.RECORD_DEF) {
487            final DetailAST recordComponents =
488                ast.findFirstToken(TokenTypes.RECORD_COMPONENTS);
489
490            // For each record component definition, we will add it to this frame.
491            TokenUtil.forEachChild(recordComponents,
492                TokenTypes.RECORD_COMPONENT_DEF, node -> {
493                    final String name = node.findFirstToken(TokenTypes.IDENT).getText();
494                    newFrame.addInstanceField(name);
495                });
496        }
497        // push container
498        frame = newFrame;
499    }
500
501    @Override
502    public void leaveToken(DetailAST ast) {
503        if (ast.getType() == TokenTypes.CLASS_DEF
504            || ast.getType() == TokenTypes.ENUM_DEF
505            || ast.getType() == TokenTypes.ENUM_CONSTANT_DEF
506            || ast.getType() == TokenTypes.RECORD_DEF) {
507            // pop
508            frame = frame.getParent();
509        }
510    }
511
512    /**
513     * Process a variable token.
514     * Check whether a local variable or parameter shadows a field.
515     * Store a field for later comparison with local variables and parameters.
516     *
517     * @param ast the variable token.
518     */
519    private void processVariable(DetailAST ast) {
520        if (!ScopeUtil.isInInterfaceOrAnnotationBlock(ast)
521            && !CheckUtil.isReceiverParameter(ast)
522            && (ScopeUtil.isLocalVariableDef(ast)
523                || ast.getType() == TokenTypes.PARAMETER_DEF
524                || ast.getType() == TokenTypes.PATTERN_VARIABLE_DEF)) {
525            // local variable or parameter. Does it shadow a field?
526            final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
527            final String name = nameAST.getText();
528
529            if ((frame.containsStaticField(name) || isInstanceField(ast, name))
530                    && !isMatchingRegexp(name)
531                    && !isIgnoredParam(ast, name)) {
532                log(nameAST, MSG_KEY, name);
533            }
534        }
535    }
536
537    /**
538     * Checks whether method or constructor parameter is ignored.
539     *
540     * @param ast the parameter token.
541     * @param name the parameter name.
542     * @return true if parameter is ignored.
543     */
544    private boolean isIgnoredParam(DetailAST ast, String name) {
545        return isIgnoredSetterParam(ast, name)
546            || isIgnoredConstructorParam(ast)
547            || isIgnoredParamOfAbstractMethod(ast);
548    }
549
550    /**
551     * Check for instance field.
552     *
553     * @param ast token
554     * @param name identifier of token
555     * @return true if instance field
556     */
557    private boolean isInstanceField(DetailAST ast, String name) {
558        return !isInStatic(ast) && frame.containsInstanceField(name);
559    }
560
561    /**
562     * Check name by regExp.
563     *
564     * @param name string value to check
565     * @return true is regexp is matching
566     */
567    private boolean isMatchingRegexp(String name) {
568        return ignoreFormat != null && ignoreFormat.matcher(name).find();
569    }
570
571    /**
572     * Determines whether an AST node is in a static method or static
573     * initializer.
574     *
575     * @param ast the node to check.
576     * @return true if ast is in a static method or a static block;
577     */
578    private static boolean isInStatic(DetailAST ast) {
579        DetailAST parent = ast.getParent();
580        boolean inStatic = false;
581
582        while (parent != null && !inStatic) {
583            if (parent.getType() == TokenTypes.STATIC_INIT) {
584                inStatic = true;
585            }
586            else if (parent.getType() == TokenTypes.METHOD_DEF
587                        && !ScopeUtil.isInScope(parent, Scope.ANONINNER)
588                        || parent.getType() == TokenTypes.VARIABLE_DEF) {
589                final DetailAST mods =
590                    parent.findFirstToken(TokenTypes.MODIFIERS);
591                inStatic = mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
592                break;
593            }
594            else {
595                parent = parent.getParent();
596            }
597        }
598        return inStatic;
599    }
600
601    /**
602     * Decides whether to ignore an AST node that is the parameter of a
603     * setter method, where the property setter method for field 'xyz' has
604     * name 'setXyz', one parameter named 'xyz', and return type void
605     * (default behavior) or return type is name of the class in which
606     * such method is declared (allowed only if
607     * {@link #setSetterCanReturnItsClass(boolean)} is called with
608     * value <em>true</em>).
609     *
610     * @param ast the AST to check.
611     * @param name the name of ast.
612     * @return true if ast should be ignored because check property
613     *     ignoreSetter is true and ast is the parameter of a setter method.
614     */
615    private boolean isIgnoredSetterParam(DetailAST ast, String name) {
616        boolean isIgnoredSetterParam = false;
617        if (ignoreSetter && ast.getType() == TokenTypes.PARAMETER_DEF) {
618            final DetailAST parametersAST = ast.getParent();
619            final DetailAST methodAST = parametersAST.getParent();
620            if (parametersAST.getChildCount() == 1
621                && methodAST.getType() == TokenTypes.METHOD_DEF
622                && isSetterMethod(methodAST, name)) {
623                isIgnoredSetterParam = true;
624            }
625        }
626        return isIgnoredSetterParam;
627    }
628
629    /**
630     * Determine if a specific method identified by methodAST and a single
631     * variable name aName is a setter. This recognition partially depends
632     * on mSetterCanReturnItsClass property.
633     *
634     * @param aMethodAST AST corresponding to a method call
635     * @param aName name of single parameter of this method.
636     * @return true of false indicating of method is a setter or not.
637     */
638    private boolean isSetterMethod(DetailAST aMethodAST, String aName) {
639        final String methodName =
640            aMethodAST.findFirstToken(TokenTypes.IDENT).getText();
641        boolean isSetterMethod = false;
642
643        if (("set" + capitalize(aName)).equals(methodName)) {
644            // method name did match set${Name}(${anyType} ${aName})
645            // where ${Name} is capitalized version of ${aName}
646            // therefore this method is potentially a setter
647            final DetailAST typeAST = aMethodAST.findFirstToken(TokenTypes.TYPE);
648            final String returnType = typeAST.getFirstChild().getText();
649            if (typeAST.findFirstToken(TokenTypes.LITERAL_VOID) != null
650                    || setterCanReturnItsClass && frame.isEmbeddedIn(returnType)) {
651                // this method has signature
652                //
653                //     void set${Name}(${anyType} ${name})
654                //
655                // and therefore considered to be a setter
656                //
657                // or
658                //
659                // return type is not void, but it is the same as the class
660                // where method is declared and and mSetterCanReturnItsClass
661                // is set to true
662                isSetterMethod = true;
663            }
664        }
665
666        return isSetterMethod;
667    }
668
669    /**
670     * Capitalizes a given property name the way we expect to see it in
671     * a setter name.
672     *
673     * @param name a property name
674     * @return capitalized property name
675     */
676    private static String capitalize(final String name) {
677        String setterName = name;
678        // we should not capitalize the first character if the second
679        // one is a capital one, since according to JavaBeans spec
680        // setXYzz() is a setter for XYzz property, not for xYzz one.
681        if (name.length() == 1 || !Character.isUpperCase(name.charAt(1))) {
682            setterName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
683        }
684        return setterName;
685    }
686
687    /**
688     * Decides whether to ignore an AST node that is the parameter of a
689     * constructor.
690     *
691     * @param ast the AST to check.
692     * @return true if ast should be ignored because check property
693     *     ignoreConstructorParameter is true and ast is a constructor parameter.
694     */
695    private boolean isIgnoredConstructorParam(DetailAST ast) {
696        boolean result = false;
697        if (ignoreConstructorParameter
698                && ast.getType() == TokenTypes.PARAMETER_DEF) {
699            final DetailAST parametersAST = ast.getParent();
700            final DetailAST constructorAST = parametersAST.getParent();
701            result = constructorAST.getType() == TokenTypes.CTOR_DEF;
702        }
703        return result;
704    }
705
706    /**
707     * Decides whether to ignore an AST node that is the parameter of an
708     * abstract method.
709     *
710     * @param ast the AST to check.
711     * @return true if ast should be ignored because check property
712     *     ignoreAbstractMethods is true and ast is a parameter of abstract methods.
713     */
714    private boolean isIgnoredParamOfAbstractMethod(DetailAST ast) {
715        boolean result = false;
716        if (ignoreAbstractMethods
717                && ast.getType() == TokenTypes.PARAMETER_DEF) {
718            final DetailAST method = ast.getParent().getParent();
719            if (method.getType() == TokenTypes.METHOD_DEF) {
720                final DetailAST mods = method.findFirstToken(TokenTypes.MODIFIERS);
721                result = mods.findFirstToken(TokenTypes.ABSTRACT) != null;
722            }
723        }
724        return result;
725    }
726
727    /**
728     * Setter to define the RegExp for names of variables and parameters to ignore.
729     *
730     * @param pattern a pattern.
731     */
732    public void setIgnoreFormat(Pattern pattern) {
733        ignoreFormat = pattern;
734    }
735
736    /**
737     * Setter to allow to ignore the parameter of a property setter method.
738     *
739     * @param ignoreSetter decide whether to ignore the parameter of
740     *     a property setter method.
741     */
742    public void setIgnoreSetter(boolean ignoreSetter) {
743        this.ignoreSetter = ignoreSetter;
744    }
745
746    /**
747     * Setter to allow to expand the definition of a setter method to include methods
748     * that return the class' instance.
749     *
750     * @param aSetterCanReturnItsClass if true then setter can return
751     *        either void or class in which it is declared. If false then
752     *        in order to be recognized as setter method (otherwise
753     *        already recognized as a setter) must return void.  Later is
754     *        the default behavior.
755     */
756    public void setSetterCanReturnItsClass(
757        boolean aSetterCanReturnItsClass) {
758        setterCanReturnItsClass = aSetterCanReturnItsClass;
759    }
760
761    /**
762     * Setter to control whether to ignore constructor parameters.
763     *
764     * @param ignoreConstructorParameter decide whether to ignore
765     *     constructor parameters.
766     */
767    public void setIgnoreConstructorParameter(
768        boolean ignoreConstructorParameter) {
769        this.ignoreConstructorParameter = ignoreConstructorParameter;
770    }
771
772    /**
773     * Setter to control whether to ignore parameters of abstract methods.
774     *
775     * @param ignoreAbstractMethods decide whether to ignore
776     *     parameters of abstract methods.
777     */
778    public void setIgnoreAbstractMethods(
779        boolean ignoreAbstractMethods) {
780        this.ignoreAbstractMethods = ignoreAbstractMethods;
781    }
782
783    /**
784     * Holds the names of static and instance fields of a type.
785     */
786    private static class FieldFrame {
787
788        /** Name of the frame, such name of the class or enum declaration. */
789        private final String frameName;
790
791        /** Is this a static inner type. */
792        private final boolean staticType;
793
794        /** Parent frame. */
795        private final FieldFrame parent;
796
797        /** Set of instance field names. */
798        private final Set<String> instanceFields = new HashSet<>();
799
800        /** Set of static field names. */
801        private final Set<String> staticFields = new HashSet<>();
802
803        /**
804         * Creates new frame.
805         *
806         * @param parent parent frame.
807         * @param staticType is this a static inner type (class or enum).
808         * @param frameName name associated with the frame, which can be a
809         */
810        /* package */ FieldFrame(FieldFrame parent, boolean staticType, String frameName) {
811            this.parent = parent;
812            this.staticType = staticType;
813            this.frameName = frameName;
814        }
815
816        /**
817         * Adds an instance field to this FieldFrame.
818         *
819         * @param field  the name of the instance field.
820         */
821        public void addInstanceField(String field) {
822            instanceFields.add(field);
823        }
824
825        /**
826         * Adds a static field to this FieldFrame.
827         *
828         * @param field  the name of the instance field.
829         */
830        public void addStaticField(String field) {
831            staticFields.add(field);
832        }
833
834        /**
835         * Determines whether this FieldFrame contains an instance field.
836         *
837         * @param field the field to check.
838         * @return true if this FieldFrame contains instance field field.
839         */
840        public boolean containsInstanceField(String field) {
841            return instanceFields.contains(field)
842                    || parent != null
843                    && !staticType
844                    && parent.containsInstanceField(field);
845        }
846
847        /**
848         * Determines whether this FieldFrame contains a static field.
849         *
850         * @param field the field to check.
851         * @return true if this FieldFrame contains static field field.
852         */
853        public boolean containsStaticField(String field) {
854            return staticFields.contains(field)
855                    || parent != null
856                    && parent.containsStaticField(field);
857        }
858
859        /**
860         * Getter for parent frame.
861         *
862         * @return parent frame.
863         */
864        public FieldFrame getParent() {
865            return parent;
866        }
867
868        /**
869         * Check if current frame is embedded in class or enum with
870         * specific name.
871         *
872         * @param classOrEnumName name of class or enum that we are looking
873         *     for in the chain of field frames.
874         *
875         * @return true if current frame is embedded in class or enum
876         *     with name classOrNameName
877         */
878        private boolean isEmbeddedIn(String classOrEnumName) {
879            FieldFrame currentFrame = this;
880            boolean isEmbeddedIn = false;
881            while (currentFrame != null) {
882                if (Objects.equals(currentFrame.frameName, classOrEnumName)) {
883                    isEmbeddedIn = true;
884                    break;
885                }
886                currentFrame = currentFrame.parent;
887            }
888            return isEmbeddedIn;
889        }
890
891    }
892
893}