001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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.naming;
021
022import java.util.Arrays;
023import java.util.HashSet;
024import java.util.LinkedList;
025import java.util.List;
026import java.util.Set;
027import java.util.stream.Collectors;
028
029import com.puppycrawl.tools.checkstyle.StatelessCheck;
030import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
031import com.puppycrawl.tools.checkstyle.api.DetailAST;
032import com.puppycrawl.tools.checkstyle.api.TokenTypes;
033import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
034import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
035
036/**
037 * <p>
038 * Validates abbreviations (consecutive capital letters) length in
039 * identifier name, it also allows to enforce camel case naming. Please read more at
040 * <a href="https://checkstyle.org/styleguides/google-java-style-20180523/javaguide.html#s5.3-camel-case">
041 * Google Style Guide</a> to get to know how to avoid long abbreviations in names.
042 * </p>
043 * <p>
044 * {@code allowedAbbreviationLength} specifies how many consecutive capital letters are
045 * allowed in the identifier.
046 * A value of <i>3</i> indicates that up to 4 consecutive capital letters are allowed,
047 * one after the other, before a violation is printed. The identifier 'MyTEST' would be
048 * allowed, but 'MyTESTS' would not be.
049 * A value of <i>0</i> indicates that only 1 consecutive capital letter is allowed. This
050 * is what should be used to enforce strict camel casing. The identifier 'MyTest' would
051 * be allowed, but 'MyTEst' would not be.
052 * </p>
053 * <p>
054 * {@code ignoreFinal}, {@code ignoreStatic}, and {@code ignoreStaticFinal}
055 * control whether variables with the respective modifiers are to be ignored.
056 * Note that a variable that is both static and final will always be considered under
057 * {@code ignoreStaticFinal} only, regardless of the values of {@code ignoreFinal}
058 * and {@code ignoreStatic}. So for example if {@code ignoreStatic} is true but
059 * {@code ignoreStaticFinal} is false, then static final variables will not be ignored.
060 * </p>
061 * <ul>
062 * <li>
063 * Property {@code allowedAbbreviationLength} - Indicate the number of consecutive capital
064 * letters allowed in targeted identifiers (abbreviations in the classes, interfaces, variables
065 * and methods names, ... ). Default value is {@code 3}.
066 * </li>
067 * <li>
068 * Property {@code allowedAbbreviations} - Specify list of abbreviations that must be skipped for
069 * checking. Abbreviations should be separated by comma. Default value is {@code {}}.
070 * </li>
071 * <li>
072 * Property {@code ignoreFinal} - Allow to skip variables with {@code final} modifier. Default
073 * value is {@code true}.
074 * </li>
075 * <li>
076 * Property {@code ignoreStatic} - Allow to skip variables with {@code static} modifier. Default
077 * value is {@code true}.
078 * </li>
079 * <li>
080 * Property {@code ignoreStaticFinal} - Allow to skip variables with both {@code static} and
081 * {@code final} modifiers. Default value is {@code true}.
082 * </li>
083 * <li>
084 * Property {@code ignoreOverriddenMethods} - Allow to ignore methods tagged with {@code @Override}
085 * annotation (that usually mean inherited name). Default value is {@code true}.
086 * </li>
087 * <li>
088 * Property {@code tokens} - tokens to check Default value is:
089 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
090 * CLASS_DEF</a>,
091 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
092 * INTERFACE_DEF</a>,
093 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
094 * ENUM_DEF</a>,
095 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF">
096 * ANNOTATION_DEF</a>,
097 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF">
098 * ANNOTATION_FIELD_DEF</a>,
099 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#PARAMETER_DEF">
100 * PARAMETER_DEF</a>,
101 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#VARIABLE_DEF">
102 * VARIABLE_DEF</a>,
103 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF">
104 * METHOD_DEF</a>.
105 * </li>
106 * </ul>
107 * <p>
108 * To configure the check:
109 * </p>
110 * <pre>
111 * &lt;module name="AbbreviationAsWordInName"/&gt;
112 * </pre>
113 * <p>
114 * Example:
115 * </p>
116 * <pre>
117 * public class MyClass extends SuperClass { // OK, camel case
118 *   int CURRENT_COUNTER; // violation, at most 4 consecutive capital letters allowed
119 *   static int GLOBAL_COUNTER; // OK, static is ignored
120 *   final Set&lt;String&gt; stringsFOUND = new HashSet&lt;&gt;(); // OK, final is ignored
121 *
122 *   &#64;Override
123 *   void printCOUNTER() { // OK, overridden method is ignored
124 *     System.out.println(CURRENT_COUNTER); // OK, only definitions are checked
125 *   }
126 *
127 *   void incrementCOUNTER() { // violation, at most 4 consecutive capital letters allowed
128 *     CURRENT_COUNTER++; // OK, only definitions are checked
129 *   }
130 *
131 *   static void incrementGLOBAL() { // violation, static method is not ignored
132 *     GLOBAL_COUNTER++; // OK, only definitions are checked
133 *   }
134 *
135 * }
136 * </pre>
137 * <p>
138 * To configure to include static variables and methods tagged with
139 * {@code @Override} annotation.
140 * </p>
141 * <p>Configuration:</p>
142 * <pre>
143 * &lt;module name="AbbreviationAsWordInName"&gt;
144 *   &lt;property name="ignoreStatic" value="false"/&gt;
145 *   &lt;property name="ignoreOverriddenMethods" value="false"/&gt;
146 * &lt;/module&gt;
147 * </pre>
148 * <p>Example:</p>
149 * <pre>
150 * public class MyClass extends SuperClass { // OK, camel case
151 *   int CURRENT_COUNTER; // violation, at most 4 consecutive capital letters allowed
152 *   static int GLOBAL_COUNTER; // violation, static is not ignored
153 *   final Set&lt;String&gt; stringsFOUND = new HashSet&lt;&gt;(); // OK, final is ignored
154 *
155 *   &#64;Override
156 *   void printCOUNTER() { // violation, overridden method is not ignored
157 *     System.out.println(CURRENT_COUNTER); // OK, only definitions are checked
158 *   }
159 *
160 *   void incrementCOUNTER() { // violation, at most 4 consecutive capital letters allowed
161 *     CURRENT_COUNTER++; // OK, only definitions are checked
162 *   }
163 *
164 *   static void incrementGLOBAL() { // violation, at most 4 consecutive capital letters allowed
165 *     GLOBAL_COUNTER++; // OK, only definitions are checked
166 *   }
167 *
168 * }
169 * </pre>
170 * <p>
171 * To configure to check all variables and identifiers
172 * (including ones with the static modifier) and enforce
173 * no abbreviations (essentially camel case) except for
174 * words like 'XML' and 'URL'.
175 * </p>
176 * <p>Configuration:</p>
177 * <pre>
178 * &lt;module name="AbbreviationAsWordInName"&gt;
179 *   &lt;property name="tokens" value="VARIABLE_DEF,CLASS_DEF"/&gt;
180 *   &lt;property name="ignoreStatic" value="false"/&gt;
181 *   &lt;property name="allowedAbbreviationLength" value="0"/&gt;
182 *   &lt;property name="allowedAbbreviations" value="XML,URL"/&gt;
183 * &lt;/module&gt;
184 * </pre>
185 * <p>Example:</p>
186 * <pre>
187 * public class MyClass { // OK
188 *   int firstNum; // OK
189 *   int secondNUM; // violation, it allowed only 1 consecutive capital letter
190 *   static int thirdNum; // OK, the static modifier would be checked
191 *   static int fourthNUm; // violation, the static modifier would be checked,
192 *                         // and only 1 consecutive capital letter is allowed
193 *   String firstXML; // OK, XML abbreviation is allowed
194 *   String firstURL; // OK, URL abbreviation is allowed
195 *   final int TOTAL = 5; // OK, final is ignored
196 *   static final int LIMIT = 10; // OK, static final is ignored
197 * }
198 * </pre>
199 * <p>
200 * To configure to check variables, excluding fields with
201 * the static modifier, and allow abbreviations up to 2
202 * consecutive capital letters ignoring the longer word 'CSV'.
203 * </p>
204 * <p>Configuration:</p>
205 * <pre>
206 * &lt;module name="AbbreviationAsWordInName"&gt;
207 *   &lt;property name="tokens" value="VARIABLE_DEF"/&gt;
208 *   &lt;property name="ignoreStatic" value="true"/&gt;
209 *   &lt;property name="allowedAbbreviationLength" value="1"/&gt;
210 *   &lt;property name="allowedAbbreviations" value="CSV"/&gt;
211 * &lt;/module&gt;
212 * </pre>
213 * <p>Example:</p>
214 * <pre>
215 * public class MyClass { // OK, ignore checking the class name
216 *   int firstNum; // OK, abbreviation "N" is of allowed length 1
217 *   int secondNUm; // OK
218 *   int secondMYNum; // violation, found "MYN" but only
219 *                    // 2 consecutive capital letters are allowed
220 *   int thirdNUM; // violation, found "NUM" but it is allowed
221 *                 // only 2 consecutive capital letters
222 *   static int fourthNUM; // OK, variables with static modifier
223 *                         // would be ignored
224 *   String firstCSV; // OK, CSV abbreviation is allowed
225 *   String firstXML; // violation, XML abbreviation is not allowed
226 *   final int TOTAL = 5; // OK, final is ignored
227 *   static final int LIMIT = 10; // OK, static final is ignored
228 * }
229 * </pre>
230 * <p>
231 * To configure to check variables, enforcing no abbreviations
232 * except for variables that are both static and final.
233 * </p>
234 * <p>Configuration:</p>
235 * <pre>
236 * &lt;module name="AbbreviationAsWordInName"&gt;
237 *     &lt;property name="tokens" value="VARIABLE_DEF"/&gt;
238 *     &lt;property name="ignoreFinal" value="false"/&gt;
239 *     &lt;property name="ignoreStatic" value="false"/&gt;
240 *     &lt;property name="ignoreStaticFinal" value="true"/&gt;
241 *     &lt;property name="allowedAbbreviationLength" value="0"/&gt;
242 * &lt;/module&gt;
243 * </pre>
244 * <p>Example:</p>
245 * <pre>
246 * public class MyClass {
247 *     public int counterXYZ = 1;                // violation
248 *     public final int customerID = 2;          // violation
249 *     public static int nextID = 3;             // violation
250 *     public static final int MAX_ALLOWED = 4;  // OK, ignored
251 * }
252 * </pre>
253 * <p>
254 * To configure to check variables, enforcing no abbreviations
255 * and ignoring static (but non-final) variables only.
256 * </p>
257 * <p>Configuration:</p>
258 * <pre>
259 * &lt;module name="AbbreviationAsWordInName"&gt;
260 *     &lt;property name="tokens" value="VARIABLE_DEF"/&gt;
261 *     &lt;property name="ignoreFinal" value="false"/&gt;
262 *     &lt;property name="ignoreStatic" value="true"/&gt;
263 *     &lt;property name="ignoreStaticFinal" value="false"/&gt;
264 *     &lt;property name="allowedAbbreviationLength" value="0"/&gt;
265 * &lt;/module&gt;
266 * </pre>
267 * <p>Example:</p>
268 * <pre>
269 * public class MyClass {
270 *     public int counterXYZ = 1;                // violation
271 *     public final int customerID = 2;          // violation
272 *     public static int nextID = 3;             // OK, ignored
273 *     public static final int MAX_ALLOWED = 4;  // violation
274 * }
275 * </pre>
276 *
277 * @since 5.8
278 */
279@StatelessCheck
280public class AbbreviationAsWordInNameCheck extends AbstractCheck {
281
282    /**
283     * Warning message key.
284     */
285    public static final String MSG_KEY = "abbreviation.as.word";
286
287    /**
288     * The default value of "allowedAbbreviationLength" option.
289     */
290    private static final int DEFAULT_ALLOWED_ABBREVIATIONS_LENGTH = 3;
291
292    /**
293     * Indicate the number of consecutive capital letters allowed in
294     * targeted identifiers (abbreviations in the classes, interfaces, variables
295     * and methods names, ... ).
296     */
297    private int allowedAbbreviationLength =
298            DEFAULT_ALLOWED_ABBREVIATIONS_LENGTH;
299
300    /**
301     * Specify list of abbreviations that must be skipped for checking. Abbreviations
302     * should be separated by comma.
303     */
304    private Set<String> allowedAbbreviations = new HashSet<>();
305
306    /** Allow to skip variables with {@code final} modifier. */
307    private boolean ignoreFinal = true;
308
309    /** Allow to skip variables with {@code static} modifier. */
310    private boolean ignoreStatic = true;
311
312    /** Allow to skip variables with both {@code static} and {@code final} modifiers. */
313    private boolean ignoreStaticFinal = true;
314
315    /**
316     * Allow to ignore methods tagged with {@code @Override} annotation (that
317     * usually mean inherited name).
318     */
319    private boolean ignoreOverriddenMethods = true;
320
321    /**
322     * Setter to allow to skip variables with {@code final} modifier.
323     *
324     * @param ignoreFinal
325     *        Defines if ignore variables with 'final' modifier or not.
326     */
327    public void setIgnoreFinal(boolean ignoreFinal) {
328        this.ignoreFinal = ignoreFinal;
329    }
330
331    /**
332     * Setter to allow to skip variables with {@code static} modifier.
333     *
334     * @param ignoreStatic
335     *        Defines if ignore variables with 'static' modifier or not.
336     */
337    public void setIgnoreStatic(boolean ignoreStatic) {
338        this.ignoreStatic = ignoreStatic;
339    }
340
341    /**
342     * Setter to allow to skip variables with both {@code static} and {@code final} modifiers.
343     * @param ignoreStaticFinal
344     *        Defines if ignore variables with both 'static' and 'final' modifiers or not.
345     */
346    public void setIgnoreStaticFinal(boolean ignoreStaticFinal) {
347        this.ignoreStaticFinal = ignoreStaticFinal;
348    }
349
350    /**
351     * Setter to allow to ignore methods tagged with {@code @Override}
352     * annotation (that usually mean inherited name).
353     *
354     * @param ignoreOverriddenMethods
355     *        Defines if ignore methods with "@Override" annotation or not.
356     */
357    public void setIgnoreOverriddenMethods(boolean ignoreOverriddenMethods) {
358        this.ignoreOverriddenMethods = ignoreOverriddenMethods;
359    }
360
361    /**
362     * Setter to indicate the number of consecutive capital letters allowed
363     * in targeted identifiers (abbreviations in the classes, interfaces,
364     * variables and methods names, ... ).
365     *
366     * @param allowedAbbreviationLength amount of allowed capital letters in
367     *        abbreviation.
368     */
369    public void setAllowedAbbreviationLength(int allowedAbbreviationLength) {
370        this.allowedAbbreviationLength = allowedAbbreviationLength;
371    }
372
373    /**
374     * Setter to specify list of abbreviations that must be skipped for checking.
375     * Abbreviations should be separated by comma.
376     *
377     * @param allowedAbbreviations an string of abbreviations that must be
378     *        skipped from checking, each abbreviation separated by comma.
379     */
380    public void setAllowedAbbreviations(String... allowedAbbreviations) {
381        if (allowedAbbreviations != null) {
382            this.allowedAbbreviations =
383                Arrays.stream(allowedAbbreviations).collect(Collectors.toSet());
384        }
385    }
386
387    @Override
388    public int[] getDefaultTokens() {
389        return new int[] {
390            TokenTypes.CLASS_DEF,
391            TokenTypes.INTERFACE_DEF,
392            TokenTypes.ENUM_DEF,
393            TokenTypes.ANNOTATION_DEF,
394            TokenTypes.ANNOTATION_FIELD_DEF,
395            TokenTypes.PARAMETER_DEF,
396            TokenTypes.VARIABLE_DEF,
397            TokenTypes.METHOD_DEF,
398        };
399    }
400
401    @Override
402    public int[] getAcceptableTokens() {
403        return new int[] {
404            TokenTypes.CLASS_DEF,
405            TokenTypes.INTERFACE_DEF,
406            TokenTypes.ENUM_DEF,
407            TokenTypes.ANNOTATION_DEF,
408            TokenTypes.ANNOTATION_FIELD_DEF,
409            TokenTypes.PARAMETER_DEF,
410            TokenTypes.VARIABLE_DEF,
411            TokenTypes.METHOD_DEF,
412            TokenTypes.ENUM_CONSTANT_DEF,
413        };
414    }
415
416    @Override
417    public int[] getRequiredTokens() {
418        return CommonUtil.EMPTY_INT_ARRAY;
419    }
420
421    @Override
422    public void visitToken(DetailAST ast) {
423        if (!isIgnoreSituation(ast)) {
424            final DetailAST nameAst = ast.findFirstToken(TokenTypes.IDENT);
425            final String typeName = nameAst.getText();
426
427            final String abbr = getDisallowedAbbreviation(typeName);
428            if (abbr != null) {
429                log(nameAst, MSG_KEY, typeName, allowedAbbreviationLength + 1);
430            }
431        }
432    }
433
434    /**
435     * Checks if it is an ignore situation.
436     *
437     * @param ast input DetailAST node.
438     * @return true if it is an ignore situation found for given input DetailAST
439     *         node.
440     */
441    private boolean isIgnoreSituation(DetailAST ast) {
442        final DetailAST modifiers = ast.getFirstChild();
443
444        final boolean result;
445        if (ast.getType() == TokenTypes.VARIABLE_DEF) {
446            if (isInterfaceDeclaration(ast)) {
447                // field declarations in interface are static/final
448                result = ignoreStaticFinal;
449            }
450            else {
451                result = hasIgnoredModifiers(modifiers);
452            }
453        }
454        else if (ast.getType() == TokenTypes.METHOD_DEF) {
455            result = ignoreOverriddenMethods && hasOverrideAnnotation(modifiers);
456        }
457        else {
458            result = CheckUtil.isReceiverParameter(ast);
459        }
460        return result;
461    }
462
463    /**
464     * Checks if a variable is to be ignored based on its modifiers.
465     * @param modifiers modifiers of the variable to be checked
466     * @return true if there is a modifier to be ignored
467     */
468    private boolean hasIgnoredModifiers(DetailAST modifiers) {
469        final boolean isStatic = modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
470        final boolean isFinal = modifiers.findFirstToken(TokenTypes.FINAL) != null;
471        final boolean result;
472        if (isStatic && isFinal) {
473            result = ignoreStaticFinal;
474        }
475        else {
476            result = ignoreStatic && isStatic || ignoreFinal && isFinal;
477        }
478        return result;
479    }
480
481    /**
482     * Check that variable definition in interface or @interface definition.
483     *
484     * @param variableDefAst variable definition.
485     * @return true if variable definition(variableDefAst) is in interface
486     *     or @interface definition.
487     */
488    private static boolean isInterfaceDeclaration(DetailAST variableDefAst) {
489        boolean result = false;
490        final DetailAST astBlock = variableDefAst.getParent();
491        final DetailAST astParent2 = astBlock.getParent();
492
493        if (astParent2.getType() == TokenTypes.INTERFACE_DEF
494                || astParent2.getType() == TokenTypes.ANNOTATION_DEF) {
495            result = true;
496        }
497        return result;
498    }
499
500    /**
501     * Checks that the method has "@Override" annotation.
502     *
503     * @param methodModifiersAST
504     *        A DetailAST nod is related to the given method modifiers
505     *        (MODIFIERS type).
506     * @return true if method has "@Override" annotation.
507     */
508    private static boolean hasOverrideAnnotation(DetailAST methodModifiersAST) {
509        boolean result = false;
510        for (DetailAST child : getChildren(methodModifiersAST)) {
511            final DetailAST annotationIdent = child.findFirstToken(TokenTypes.IDENT);
512
513            if (annotationIdent != null && "Override".equals(annotationIdent.getText())) {
514                result = true;
515                break;
516            }
517        }
518        return result;
519    }
520
521    /**
522     * Gets the disallowed abbreviation contained in given String.
523     *
524     * @param str
525     *        the given String.
526     * @return the disallowed abbreviation contained in given String as a
527     *         separate String.
528     */
529    private String getDisallowedAbbreviation(String str) {
530        int beginIndex = 0;
531        boolean abbrStarted = false;
532        String result = null;
533
534        for (int index = 0; index < str.length(); index++) {
535            final char symbol = str.charAt(index);
536
537            if (Character.isUpperCase(symbol)) {
538                if (!abbrStarted) {
539                    abbrStarted = true;
540                    beginIndex = index;
541                }
542            }
543            else if (abbrStarted) {
544                abbrStarted = false;
545
546                final int endIndex = index - 1;
547                result = getAbbreviationIfIllegal(str, beginIndex, endIndex);
548                if (result != null) {
549                    break;
550                }
551                beginIndex = -1;
552            }
553        }
554        // if abbreviation at the end of name (example: scaleX)
555        if (abbrStarted) {
556            final int endIndex = str.length() - 1;
557            result = getAbbreviationIfIllegal(str, beginIndex, endIndex);
558        }
559        return result;
560    }
561
562    /**
563     * Get Abbreviation if it is illegal, where {@code beginIndex} and {@code endIndex} are
564     * inclusive indexes of a sequence of consecutive upper-case characters.
565     *
566     * @param str name
567     * @param beginIndex begin index
568     * @param endIndex end index
569     * @return the abbreviation if it is bigger than required and not in the
570     *         ignore list, otherwise {@code null}
571     */
572    private String getAbbreviationIfIllegal(String str, int beginIndex, int endIndex) {
573        String result = null;
574        final int abbrLength = endIndex - beginIndex;
575        if (abbrLength > allowedAbbreviationLength) {
576            final String abbr = getAbbreviation(str, beginIndex, endIndex);
577            if (!allowedAbbreviations.contains(abbr)) {
578                result = abbr;
579            }
580        }
581        return result;
582    }
583
584    /**
585     * Gets the abbreviation, where {@code beginIndex} and {@code endIndex} are
586     * inclusive indexes of a sequence of consecutive upper-case characters.
587     * <p>
588     * The character at {@code endIndex} is only included in the abbreviation if
589     * it is the last character in the string; otherwise it is usually the first
590     * capital in the next word.
591     * </p>
592     * <p>
593     * For example, {@code getAbbreviation("getXMLParser", 3, 6)} returns "XML"
594     * (not "XMLP"), and so does {@code getAbbreviation("parseXML", 5, 7)}.
595     * </p>
596     *
597     * @param str name
598     * @param beginIndex begin index
599     * @param endIndex end index
600     * @return the specified abbreviation
601     */
602    private static String getAbbreviation(String str, int beginIndex, int endIndex) {
603        final String result;
604        if (endIndex == str.length() - 1) {
605            result = str.substring(beginIndex);
606        }
607        else {
608            result = str.substring(beginIndex, endIndex);
609        }
610        return result;
611    }
612
613    /**
614     * Gets all the children which are one level below on the current DetailAST
615     * parent node.
616     *
617     * @param node
618     *        Current parent node.
619     * @return The list of children one level below on the current parent node.
620     */
621    private static List<DetailAST> getChildren(final DetailAST node) {
622        final List<DetailAST> result = new LinkedList<>();
623        DetailAST curNode = node.getFirstChild();
624        while (curNode != null) {
625            result.add(curNode);
626            curNode = curNode.getNextSibling();
627        }
628        return result;
629    }
630
631}