View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import java.util.HashSet;
23  import java.util.Locale;
24  import java.util.Objects;
25  import java.util.Set;
26  import java.util.regex.Pattern;
27  
28  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
29  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
30  import com.puppycrawl.tools.checkstyle.api.DetailAST;
31  import com.puppycrawl.tools.checkstyle.api.Scope;
32  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
33  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
34  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
35  import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
36  
37  /**
38   * <div>
39   * Checks that a local variable or a parameter does not shadow
40   * a field that is defined in the same class.
41   * </div>
42   *
43   * <p>
44   * It is possible to configure the check to ignore all property setter methods.
45   * </p>
46   *
47   * <p>
48   * A method is recognized as a setter if it is in the following form
49   * </p>
50   * <pre>
51   * ${returnType} set${Name}(${anyType} ${name}) { ... }
52   * </pre>
53   *
54   * <p>
55   * where ${anyType} is any primitive type, class or interface name;
56   * ${name} is name of the variable that is being set and ${Name} its
57   * capitalized form that appears in the method name. By default, it is expected
58   * that setter returns void, i.e. ${returnType} is 'void'. For example
59   * </p>
60   * <pre>
61   * void setTime(long time) { ... }
62   * </pre>
63   *
64   * <p>
65   * Any other return types will not let method match a setter pattern. However,
66   * by setting <em>setterCanReturnItsClass</em> property to <em>true</em>
67   * definition of a setter is expanded, so that setter return type can also be
68   * a class in which setter is declared. For example
69   * </p>
70   * <pre>
71   * class PageBuilder {
72   *   PageBuilder setName(String name) { ... }
73   * }
74   * </pre>
75   *
76   * <p>
77   * Such methods are known as chain-setters and a common when Builder-pattern
78   * is used. Property <em>setterCanReturnItsClass</em> has effect only if
79   * <em>ignoreSetter</em> is set to true.
80   * </p>
81   * <ul>
82   * <li>
83   * Property {@code ignoreAbstractMethods} - Control whether to ignore parameters
84   * of abstract methods.
85   * Type is {@code boolean}.
86   * Default value is {@code false}.
87   * </li>
88   * <li>
89   * Property {@code ignoreConstructorParameter} - Control whether to ignore constructor parameters.
90   * Type is {@code boolean}.
91   * Default value is {@code false}.
92   * </li>
93   * <li>
94   * Property {@code ignoreFormat} - Define the RegExp for names of variables
95   * and parameters to ignore.
96   * Type is {@code java.util.regex.Pattern}.
97   * Default value is {@code null}.
98   * </li>
99   * <li>
100  * Property {@code ignoreSetter} - Allow to ignore the parameter of a property setter method.
101  * Type is {@code boolean}.
102  * Default value is {@code false}.
103  * </li>
104  * <li>
105  * Property {@code setterCanReturnItsClass} - Allow to expand the definition of a setter method
106  * to include methods that return the class' instance.
107  * Type is {@code boolean}.
108  * Default value is {@code false}.
109  * </li>
110  * <li>
111  * Property {@code tokens} - tokens to check
112  * Type is {@code java.lang.String[]}.
113  * Validation type is {@code tokenSet}.
114  * Default value is:
115  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
116  * VARIABLE_DEF</a>,
117  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PARAMETER_DEF">
118  * PARAMETER_DEF</a>,
119  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PATTERN_VARIABLE_DEF">
120  * PATTERN_VARIABLE_DEF</a>,
121  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
122  * LAMBDA</a>,
123  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_COMPONENT_DEF">
124  * RECORD_COMPONENT_DEF</a>.
125  * </li>
126  * </ul>
127  *
128  * <p>
129  * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
130  * </p>
131  *
132  * <p>
133  * Violation Message Keys:
134  * </p>
135  * <ul>
136  * <li>
137  * {@code hidden.field}
138  * </li>
139  * </ul>
140  *
141  * @since 3.0
142  */
143 @FileStatefulCheck
144 public class HiddenFieldCheck
145     extends AbstractCheck {
146 
147     /**
148      * A key is pointing to the warning message text in "messages.properties"
149      * file.
150      */
151     public static final String MSG_KEY = "hidden.field";
152 
153     /**
154      * Stack of sets of field names,
155      * one for each class of a set of nested classes.
156      */
157     private FieldFrame frame;
158 
159     /** Define the RegExp for names of variables and parameters to ignore. */
160     private Pattern ignoreFormat;
161 
162     /**
163      * Allow to ignore the parameter of a property setter method.
164      */
165     private boolean ignoreSetter;
166 
167     /**
168      * Allow to expand the definition of a setter method to include methods
169      * that return the class' instance.
170      */
171     private boolean setterCanReturnItsClass;
172 
173     /** Control whether to ignore constructor parameters. */
174     private boolean ignoreConstructorParameter;
175 
176     /** Control whether to ignore parameters of abstract methods. */
177     private boolean ignoreAbstractMethods;
178 
179     @Override
180     public int[] getDefaultTokens() {
181         return getAcceptableTokens();
182     }
183 
184     @Override
185     public int[] getAcceptableTokens() {
186         return new int[] {
187             TokenTypes.VARIABLE_DEF,
188             TokenTypes.PARAMETER_DEF,
189             TokenTypes.CLASS_DEF,
190             TokenTypes.ENUM_DEF,
191             TokenTypes.ENUM_CONSTANT_DEF,
192             TokenTypes.PATTERN_VARIABLE_DEF,
193             TokenTypes.LAMBDA,
194             TokenTypes.RECORD_DEF,
195             TokenTypes.RECORD_COMPONENT_DEF,
196         };
197     }
198 
199     @Override
200     public int[] getRequiredTokens() {
201         return new int[] {
202             TokenTypes.CLASS_DEF,
203             TokenTypes.ENUM_DEF,
204             TokenTypes.ENUM_CONSTANT_DEF,
205             TokenTypes.RECORD_DEF,
206         };
207     }
208 
209     @Override
210     public void beginTree(DetailAST rootAST) {
211         frame = new FieldFrame(null, true, null);
212     }
213 
214     @Override
215     public void visitToken(DetailAST ast) {
216         final int type = ast.getType();
217         switch (type) {
218             case TokenTypes.VARIABLE_DEF:
219             case TokenTypes.PARAMETER_DEF:
220             case TokenTypes.PATTERN_VARIABLE_DEF:
221             case TokenTypes.RECORD_COMPONENT_DEF:
222                 processVariable(ast);
223                 break;
224             case TokenTypes.LAMBDA:
225                 processLambda(ast);
226                 break;
227             default:
228                 visitOtherTokens(ast, type);
229         }
230     }
231 
232     /**
233      * Process a lambda token.
234      * Checks whether a lambda parameter shadows a field.
235      * Note, that when parameter of lambda expression is untyped,
236      * ANTLR parses the parameter as an identifier.
237      *
238      * @param ast the lambda token.
239      */
240     private void processLambda(DetailAST ast) {
241         final DetailAST firstChild = ast.getFirstChild();
242         if (TokenUtil.isOfType(firstChild, TokenTypes.IDENT)) {
243             final String untypedLambdaParameterName = firstChild.getText();
244             if (frame.containsStaticField(untypedLambdaParameterName)
245                 || isInstanceField(firstChild, untypedLambdaParameterName)) {
246                 log(firstChild, MSG_KEY, untypedLambdaParameterName);
247             }
248         }
249     }
250 
251     /**
252      * Called to process tokens other than {@link TokenTypes#VARIABLE_DEF}
253      * and {@link TokenTypes#PARAMETER_DEF}.
254      *
255      * @param ast token to process
256      * @param type type of the token
257      */
258     private void visitOtherTokens(DetailAST ast, int type) {
259         // A more thorough check of enum constant class bodies is
260         // possible (checking for hidden fields against the enum
261         // class body in addition to enum constant class bodies)
262         // but not attempted as it seems out of the scope of this
263         // check.
264         final DetailAST typeMods = ast.findFirstToken(TokenTypes.MODIFIERS);
265         final boolean isStaticInnerType =
266                 typeMods != null
267                         && typeMods.findFirstToken(TokenTypes.LITERAL_STATIC) != null
268                         // inner record is implicitly static
269                         || ast.getType() == TokenTypes.RECORD_DEF;
270         final String frameName;
271 
272         if (type == TokenTypes.CLASS_DEF
273                 || type == TokenTypes.ENUM_DEF) {
274             frameName = ast.findFirstToken(TokenTypes.IDENT).getText();
275         }
276         else {
277             frameName = null;
278         }
279         final FieldFrame newFrame = new FieldFrame(frame, isStaticInnerType, frameName);
280 
281         // add fields to container
282         final DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK);
283         // enum constants may not have bodies
284         if (objBlock != null) {
285             DetailAST child = objBlock.getFirstChild();
286             while (child != null) {
287                 if (child.getType() == TokenTypes.VARIABLE_DEF) {
288                     final String name =
289                         child.findFirstToken(TokenTypes.IDENT).getText();
290                     final DetailAST mods =
291                         child.findFirstToken(TokenTypes.MODIFIERS);
292                     if (mods.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
293                         newFrame.addInstanceField(name);
294                     }
295                     else {
296                         newFrame.addStaticField(name);
297                     }
298                 }
299                 child = child.getNextSibling();
300             }
301         }
302         if (ast.getType() == TokenTypes.RECORD_DEF) {
303             final DetailAST recordComponents =
304                 ast.findFirstToken(TokenTypes.RECORD_COMPONENTS);
305 
306             // For each record component definition, we will add it to this frame.
307             TokenUtil.forEachChild(recordComponents,
308                 TokenTypes.RECORD_COMPONENT_DEF, node -> {
309                     final String name = node.findFirstToken(TokenTypes.IDENT).getText();
310                     newFrame.addInstanceField(name);
311                 });
312         }
313         // push container
314         frame = newFrame;
315     }
316 
317     @Override
318     public void leaveToken(DetailAST ast) {
319         if (ast.getType() == TokenTypes.CLASS_DEF
320             || ast.getType() == TokenTypes.ENUM_DEF
321             || ast.getType() == TokenTypes.ENUM_CONSTANT_DEF
322             || ast.getType() == TokenTypes.RECORD_DEF) {
323             // pop
324             frame = frame.getParent();
325         }
326     }
327 
328     /**
329      * Process a variable token.
330      * Check whether a local variable or parameter shadows a field.
331      * Store a field for later comparison with local variables and parameters.
332      *
333      * @param ast the variable token.
334      */
335     private void processVariable(DetailAST ast) {
336         if (!ScopeUtil.isInInterfaceOrAnnotationBlock(ast)
337             && !CheckUtil.isReceiverParameter(ast)
338             && (ScopeUtil.isLocalVariableDef(ast)
339                 || ast.getType() == TokenTypes.PARAMETER_DEF
340                 || ast.getType() == TokenTypes.PATTERN_VARIABLE_DEF)) {
341             // local variable or parameter. Does it shadow a field?
342             final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
343             final String name = nameAST.getText();
344 
345             if ((frame.containsStaticField(name) || isInstanceField(ast, name))
346                     && !isMatchingRegexp(name)
347                     && !isIgnoredParam(ast, name)) {
348                 log(nameAST, MSG_KEY, name);
349             }
350         }
351     }
352 
353     /**
354      * Checks whether method or constructor parameter is ignored.
355      *
356      * @param ast the parameter token.
357      * @param name the parameter name.
358      * @return true if parameter is ignored.
359      */
360     private boolean isIgnoredParam(DetailAST ast, String name) {
361         return isIgnoredSetterParam(ast, name)
362             || isIgnoredConstructorParam(ast)
363             || isIgnoredParamOfAbstractMethod(ast);
364     }
365 
366     /**
367      * Check for instance field.
368      *
369      * @param ast token
370      * @param name identifier of token
371      * @return true if instance field
372      */
373     private boolean isInstanceField(DetailAST ast, String name) {
374         return !isInStatic(ast) && frame.containsInstanceField(name);
375     }
376 
377     /**
378      * Check name by regExp.
379      *
380      * @param name string value to check
381      * @return true is regexp is matching
382      */
383     private boolean isMatchingRegexp(String name) {
384         return ignoreFormat != null && ignoreFormat.matcher(name).find();
385     }
386 
387     /**
388      * Determines whether an AST node is in a static method or static
389      * initializer.
390      *
391      * @param ast the node to check.
392      * @return true if ast is in a static method or a static block;
393      */
394     private static boolean isInStatic(DetailAST ast) {
395         DetailAST parent = ast.getParent();
396         boolean inStatic = false;
397 
398         while (parent != null && !inStatic) {
399             if (parent.getType() == TokenTypes.STATIC_INIT) {
400                 inStatic = true;
401             }
402             else if (parent.getType() == TokenTypes.METHOD_DEF
403                         && !ScopeUtil.isInScope(parent, Scope.ANONINNER)
404                         || parent.getType() == TokenTypes.VARIABLE_DEF) {
405                 final DetailAST mods =
406                     parent.findFirstToken(TokenTypes.MODIFIERS);
407                 inStatic = mods.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
408                 break;
409             }
410             else {
411                 parent = parent.getParent();
412             }
413         }
414         return inStatic;
415     }
416 
417     /**
418      * Decides whether to ignore an AST node that is the parameter of a
419      * setter method, where the property setter method for field 'xyz' has
420      * name 'setXyz', one parameter named 'xyz', and return type void
421      * (default behavior) or return type is name of the class in which
422      * such method is declared (allowed only if
423      * {@link #setSetterCanReturnItsClass(boolean)} is called with
424      * value <em>true</em>).
425      *
426      * @param ast the AST to check.
427      * @param name the name of ast.
428      * @return true if ast should be ignored because check property
429      *     ignoreSetter is true and ast is the parameter of a setter method.
430      */
431     private boolean isIgnoredSetterParam(DetailAST ast, String name) {
432         boolean isIgnoredSetterParam = false;
433         if (ignoreSetter) {
434             final DetailAST parametersAST = ast.getParent();
435             final DetailAST methodAST = parametersAST.getParent();
436             if (parametersAST.getChildCount() == 1
437                 && methodAST.getType() == TokenTypes.METHOD_DEF
438                 && isSetterMethod(methodAST, name)) {
439                 isIgnoredSetterParam = true;
440             }
441         }
442         return isIgnoredSetterParam;
443     }
444 
445     /**
446      * Determine if a specific method identified by methodAST and a single
447      * variable name aName is a setter. This recognition partially depends
448      * on mSetterCanReturnItsClass property.
449      *
450      * @param aMethodAST AST corresponding to a method call
451      * @param aName name of single parameter of this method.
452      * @return true of false indicating of method is a setter or not.
453      */
454     private boolean isSetterMethod(DetailAST aMethodAST, String aName) {
455         final String methodName =
456             aMethodAST.findFirstToken(TokenTypes.IDENT).getText();
457         boolean isSetterMethod = false;
458 
459         if (("set" + capitalize(aName)).equals(methodName)) {
460             // method name did match set${Name}(${anyType} ${aName})
461             // where ${Name} is capitalized version of ${aName}
462             // therefore this method is potentially a setter
463             final DetailAST typeAST = aMethodAST.findFirstToken(TokenTypes.TYPE);
464             final String returnType = typeAST.getFirstChild().getText();
465             if (typeAST.findFirstToken(TokenTypes.LITERAL_VOID) != null
466                     || setterCanReturnItsClass && frame.isEmbeddedIn(returnType)) {
467                 // this method has signature
468                 //
469                 //     void set${Name}(${anyType} ${name})
470                 //
471                 // and therefore considered to be a setter
472                 //
473                 // or
474                 //
475                 // return type is not void, but it is the same as the class
476                 // where method is declared and mSetterCanReturnItsClass
477                 // is set to true
478                 isSetterMethod = true;
479             }
480         }
481 
482         return isSetterMethod;
483     }
484 
485     /**
486      * Capitalizes a given property name the way we expect to see it in
487      * a setter name.
488      *
489      * @param name a property name
490      * @return capitalized property name
491      */
492     private static String capitalize(final String name) {
493         String setterName = name;
494         // we should not capitalize the first character if the second
495         // one is a capital one, since according to JavaBeans spec
496         // setXYzz() is a setter for XYzz property, not for xYzz one.
497         if (name.length() == 1 || !Character.isUpperCase(name.charAt(1))) {
498             setterName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
499         }
500         return setterName;
501     }
502 
503     /**
504      * Decides whether to ignore an AST node that is the parameter of a
505      * constructor.
506      *
507      * @param ast the AST to check.
508      * @return true if ast should be ignored because check property
509      *     ignoreConstructorParameter is true and ast is a constructor parameter.
510      */
511     private boolean isIgnoredConstructorParam(DetailAST ast) {
512         boolean result = false;
513         if (ignoreConstructorParameter
514                 && ast.getType() == TokenTypes.PARAMETER_DEF) {
515             final DetailAST parametersAST = ast.getParent();
516             final DetailAST constructorAST = parametersAST.getParent();
517             result = constructorAST.getType() == TokenTypes.CTOR_DEF;
518         }
519         return result;
520     }
521 
522     /**
523      * Decides whether to ignore an AST node that is the parameter of an
524      * abstract method.
525      *
526      * @param ast the AST to check.
527      * @return true if ast should be ignored because check property
528      *     ignoreAbstractMethods is true and ast is a parameter of abstract methods.
529      */
530     private boolean isIgnoredParamOfAbstractMethod(DetailAST ast) {
531         boolean result = false;
532         if (ignoreAbstractMethods) {
533             final DetailAST method = ast.getParent().getParent();
534             if (method.getType() == TokenTypes.METHOD_DEF) {
535                 final DetailAST mods = method.findFirstToken(TokenTypes.MODIFIERS);
536                 result = mods.findFirstToken(TokenTypes.ABSTRACT) != null;
537             }
538         }
539         return result;
540     }
541 
542     /**
543      * Setter to define the RegExp for names of variables and parameters to ignore.
544      *
545      * @param pattern a pattern.
546      * @since 3.2
547      */
548     public void setIgnoreFormat(Pattern pattern) {
549         ignoreFormat = pattern;
550     }
551 
552     /**
553      * Setter to allow to ignore the parameter of a property setter method.
554      *
555      * @param ignoreSetter decide whether to ignore the parameter of
556      *     a property setter method.
557      * @since 3.2
558      */
559     public void setIgnoreSetter(boolean ignoreSetter) {
560         this.ignoreSetter = ignoreSetter;
561     }
562 
563     /**
564      * Setter to allow to expand the definition of a setter method to include methods
565      * that return the class' instance.
566      *
567      * @param aSetterCanReturnItsClass if true then setter can return
568      *        either void or class in which it is declared. If false then
569      *        in order to be recognized as setter method (otherwise
570      *        already recognized as a setter) must return void.  Later is
571      *        the default behavior.
572      * @since 6.3
573      */
574     public void setSetterCanReturnItsClass(
575         boolean aSetterCanReturnItsClass) {
576         setterCanReturnItsClass = aSetterCanReturnItsClass;
577     }
578 
579     /**
580      * Setter to control whether to ignore constructor parameters.
581      *
582      * @param ignoreConstructorParameter decide whether to ignore
583      *     constructor parameters.
584      * @since 3.2
585      */
586     public void setIgnoreConstructorParameter(
587         boolean ignoreConstructorParameter) {
588         this.ignoreConstructorParameter = ignoreConstructorParameter;
589     }
590 
591     /**
592      * Setter to control whether to ignore parameters of abstract methods.
593      *
594      * @param ignoreAbstractMethods decide whether to ignore
595      *     parameters of abstract methods.
596      * @since 4.0
597      */
598     public void setIgnoreAbstractMethods(
599         boolean ignoreAbstractMethods) {
600         this.ignoreAbstractMethods = ignoreAbstractMethods;
601     }
602 
603     /**
604      * Holds the names of static and instance fields of a type.
605      */
606     private static final class FieldFrame {
607 
608         /** Name of the frame, such name of the class or enum declaration. */
609         private final String frameName;
610 
611         /** Is this a static inner type. */
612         private final boolean staticType;
613 
614         /** Parent frame. */
615         private final FieldFrame parent;
616 
617         /** Set of instance field names. */
618         private final Set<String> instanceFields = new HashSet<>();
619 
620         /** Set of static field names. */
621         private final Set<String> staticFields = new HashSet<>();
622 
623         /**
624          * Creates new frame.
625          *
626          * @param parent parent frame.
627          * @param staticType is this a static inner type (class or enum).
628          * @param frameName name associated with the frame, which can be a
629          */
630         private FieldFrame(FieldFrame parent, boolean staticType, String frameName) {
631             this.parent = parent;
632             this.staticType = staticType;
633             this.frameName = frameName;
634         }
635 
636         /**
637          * Adds an instance field to this FieldFrame.
638          *
639          * @param field  the name of the instance field.
640          */
641         public void addInstanceField(String field) {
642             instanceFields.add(field);
643         }
644 
645         /**
646          * Adds a static field to this FieldFrame.
647          *
648          * @param field  the name of the instance field.
649          */
650         public void addStaticField(String field) {
651             staticFields.add(field);
652         }
653 
654         /**
655          * Determines whether this FieldFrame contains an instance field.
656          *
657          * @param field the field to check
658          * @return true if this FieldFrame contains instance field
659          */
660         public boolean containsInstanceField(String field) {
661             FieldFrame currentParent = parent;
662             boolean contains = instanceFields.contains(field);
663             boolean isStaticType = staticType;
664             while (!isStaticType && !contains) {
665                 contains = currentParent.instanceFields.contains(field);
666                 isStaticType = currentParent.staticType;
667                 currentParent = currentParent.parent;
668             }
669             return contains;
670         }
671 
672         /**
673          * Determines whether this FieldFrame contains a static field.
674          *
675          * @param field the field to check
676          * @return true if this FieldFrame contains static field
677          */
678         public boolean containsStaticField(String field) {
679             FieldFrame currentParent = parent;
680             boolean contains = staticFields.contains(field);
681             while (currentParent != null && !contains) {
682                 contains = currentParent.staticFields.contains(field);
683                 currentParent = currentParent.parent;
684             }
685             return contains;
686         }
687 
688         /**
689          * Getter for parent frame.
690          *
691          * @return parent frame.
692          */
693         public FieldFrame getParent() {
694             return parent;
695         }
696 
697         /**
698          * Check if current frame is embedded in class or enum with
699          * specific name.
700          *
701          * @param classOrEnumName name of class or enum that we are looking
702          *     for in the chain of field frames.
703          *
704          * @return true if current frame is embedded in class or enum
705          *     with name classOrNameName
706          */
707         private boolean isEmbeddedIn(String classOrEnumName) {
708             FieldFrame currentFrame = this;
709             boolean isEmbeddedIn = false;
710             while (currentFrame != null) {
711                 if (Objects.equals(currentFrame.frameName, classOrEnumName)) {
712                     isEmbeddedIn = true;
713                     break;
714                 }
715                 currentFrame = currentFrame.parent;
716             }
717             return isEmbeddedIn;
718         }
719 
720     }
721 
722 }