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