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