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.metrics; 021 022import java.math.BigInteger; 023import java.util.ArrayDeque; 024import java.util.Deque; 025import java.util.concurrent.atomic.AtomicInteger; 026 027import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 028import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 029import com.puppycrawl.tools.checkstyle.api.DetailAST; 030import com.puppycrawl.tools.checkstyle.api.TokenTypes; 031import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 032 033/** 034 * <p> 035 * Checks the NPATH complexity against a specified limit. 036 * </p> 037 * <p> 038 * The NPATH metric computes the number of possible execution paths through a 039 * function(method). It takes into account the nesting of conditional statements 040 * and multipart boolean expressions (A && B, C || D, E ? F :G and 041 * their combinations). 042 * </p> 043 * <p> 044 * The NPATH metric was designed base on Cyclomatic complexity to avoid problem 045 * of Cyclomatic complexity metric like nesting level within a function(method). 046 * </p> 047 * <p> 048 * Metric was described at <a href="http://dl.acm.org/citation.cfm?id=42379"> 049 * "NPATH: a measure of execution pathcomplexity and its applications"</a>. 050 * If you need detailed description of algorithm, please read that article, 051 * it is well written and have number of examples and details. 052 * </p> 053 * <p> 054 * Here is some quotes: 055 * </p> 056 * <blockquote> 057 * An NPATH threshold value of 200 has been established for a function. 058 * The value 200 is based on studies done at AT&T Bell Laboratories [1988 year]. 059 * </blockquote> 060 * <blockquote> 061 * Some of the most effective methods of reducing the NPATH value include: 062 * <ul> 063 * <li> 064 * distributing functionality; 065 * </li> 066 * <li> 067 * implementing multiple if statements as a switch statement; 068 * </li> 069 * <li> 070 * creating a separate function for logical expressions with a high count of 071 * variables and (&&) and or (||) operators. 072 * </li> 073 * </ul> 074 * </blockquote> 075 * <blockquote> 076 * Although strategies to reduce the NPATH complexity of functions are important, 077 * care must be taken not to distort the logical clarity of the software by 078 * applying a strategy to reduce the complexity of functions. That is, there is 079 * a point of diminishing return beyond which a further attempt at reduction of 080 * complexity distorts the logical clarity of the system structure. 081 * </blockquote> 082 * <table> 083 * <caption>Examples</caption> 084 * <thead><tr><th>Structure</th><th>Complexity expression</th></tr></thead> 085 * <tr><td>if ([expr]) { [if-range] }</td><td>NP(if-range) + 1 + NP(expr)</td></tr> 086 * <tr><td>if ([expr]) { [if-range] } else { [else-range] }</td> 087 * <td>NP(if-range)+ NP(else-range) + NP(expr)</td></tr> 088 * <tr><td>while ([expr]) { [while-range] }</td><td>NP(while-range) + NP(expr) + 1</td></tr> 089 * <tr><td>do { [do-range] } while ([expr])</td><td>NP(do-range) + NP(expr) + 1</td></tr> 090 * <tr><td>for([expr1]; [expr2]; [expr3]) { [for-range] }</td> 091 * <td>NP(for-range) + NP(expr1)+ NP(expr2) + NP(expr3) + 1</td></tr> 092 * <tr><td>switch ([expr]) { case : [case-range] default: [default-range] }</td> 093 * <td>S(i=1:i=n)NP(case-range[i]) + NP(default-range) + NP(expr)</td></tr> 094 * <tr><td>[expr1] ? [expr2] : [expr3]</td><td>NP(expr1) + NP(expr2) + NP(expr3) + 2</td></tr> 095 * <tr><td>goto label</td><td>1</td></tr><tr><td>break</td><td>1</td></tr> 096 * <tr><td>Expressions</td> 097 * <td>Number of && and || operators in expression. No operators - 0</td></tr> 098 * <tr><td>continue</td><td>1</td></tr><tr><td>return</td><td>1</td></tr> 099 * <tr><td>Statement (even sequential statements)</td><td>1</td></tr> 100 * <tr><td>Empty block {}</td><td>1</td></tr><tr><td>Function call</td><td>1</td> 101 * </tr><tr><td>Function(Method) declaration or Block</td><td>P(i=1:i=N)NP(Statement[i])</td></tr> 102 * </table> 103 * <p> 104 * <b>Rationale:</b> Nejmeh says that his group had an informal NPATH limit of 105 * 200 on individual routines; functions(methods) that exceeded this value were 106 * candidates for further decomposition - or at least a closer look. 107 * <b>Please do not be fanatic with limit 200</b> - choose number that suites 108 * your project style. Limit 200 is empirical number base on some sources of at 109 * AT&T Bell Laboratories of 1988 year. 110 * </p> 111 * <ul> 112 * <li> 113 * Property {@code max} - Specify the maximum threshold allowed. 114 * Type is {@code int}. 115 * Default value is {@code 200}. 116 * </li> 117 * </ul> 118 * <p> 119 * To configure the check: 120 * </p> 121 * <pre> 122 * <module name="NPathComplexity"/> 123 * </pre> 124 * <p> 125 * Example: 126 * </p> 127 * <pre> 128 * public abstract class Test { 129 * 130 * final int a = 0; 131 * int b = 0; 132 * 133 * public void foo() { // OK, NPath complexity is less than default threshold 134 * // function consists of one if-else block with an NPath Complexity of 3 135 * if (a > 10) { 136 * if (a > b) { // nested if-else decision tree adds 2 to the complexity count 137 * buzz(); 138 * } else { 139 * fizz(); 140 * } 141 * } else { // last possible outcome of the main if-else block, adds 1 to complexity 142 * buzz(); 143 * } 144 * } 145 * 146 * public void boo() { // violation, NPath complexity is 217 (max allowed is 200) 147 * // looping through 3 switch statements produces 6^3 + 1 (217) possible outcomes 148 * for(int i = 0; i < b; i++) { // for statement adds 1 to final complexity 149 * switch(i) { // each independent switch statement multiplies complexity by 6 150 * case a: 151 * // ternary with && adds 3 to switch's complexity 152 * print(f(i) && g(i) ? fizz() : buzz()); 153 * default: 154 * // ternary with || adds 3 to switch's complexity 155 * print(f(i) || g(i) ? fizz() : buzz()); 156 * } 157 * switch(i - 1) { // multiplies complexity by 6 158 * case a: 159 * print(f(i) && g(i) ? fizz() : buzz()); 160 * default: 161 * print(f(i) || g(i) ? fizz() : buzz()); 162 * } 163 * switch(i + 1) { // multiplies complexity by 6 164 * case a: 165 * print(f(i) && g(i) ? fizz() : buzz()); 166 * default: 167 * print(f(i) || g(i) ? fizz() : buzz()); 168 * } 169 * } 170 * } 171 * 172 * public abstract boolean f(int x); 173 * public abstract boolean g(int x); 174 * public abstract String fizz(); 175 * public abstract String buzz(); 176 * public abstract void print(String str); 177 * } 178 * </pre> 179 * <p> 180 * To configure the check with a threshold of 100: 181 * </p> 182 * <pre> 183 * <module name="NPathComplexity"> 184 * <property name="max" value="100"/> 185 * </module> 186 * </pre> 187 * <p> 188 * Example: 189 * </p> 190 * <pre> 191 * public abstract class Test1 { 192 * public void foo() { // violation, NPath complexity is 128 (max allowed is 100) 193 * int a,b,t,m,n; 194 * a=b=t=m=n = 0; 195 * 196 * // Complexity is achieved by choosing from 2 options 7 times (2^7 = 128 possible outcomes) 197 * if (a > b) { // non-nested if-else decision tree multiplies complexity by 2 198 * bar(); 199 * } else { 200 * baz(); 201 * } 202 * 203 * print(t > 1 ? bar() : baz()); // 5 ternary statements multiply complexity by 2^5 204 * print(t > 2 ? bar() : baz()); 205 * print(t > 3 ? bar() : baz()); 206 * print(t > 4 ? bar() : baz()); 207 * print(t > 5 ? bar() : baz()); 208 * 209 * if (m > n) { // multiplies complexity by 2 210 * baz(); 211 * } else { 212 * bar(); 213 * } 214 * } 215 * 216 * public abstract String bar(); 217 * public abstract String baz(); 218 * public abstract void print(String str); 219 * } 220 * </pre> 221 * <p> 222 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 223 * </p> 224 * <p> 225 * Violation Message Keys: 226 * </p> 227 * <ul> 228 * <li> 229 * {@code npathComplexity} 230 * </li> 231 * </ul> 232 * 233 * @since 3.4 234 */ 235// -@cs[AbbreviationAsWordInName] Can't change check name 236@FileStatefulCheck 237public final class NPathComplexityCheck extends AbstractCheck { 238 239 /** 240 * A key is pointing to the warning message text in "messages.properties" 241 * file. 242 */ 243 public static final String MSG_KEY = "npathComplexity"; 244 245 /** Default allowed complexity. */ 246 private static final int DEFAULT_MAX = 200; 247 248 /** The initial current value. */ 249 private static final BigInteger INITIAL_VALUE = BigInteger.ZERO; 250 251 /** 252 * Stack of NP values for ranges. 253 */ 254 private final Deque<BigInteger> rangeValues = new ArrayDeque<>(); 255 256 /** Stack of NP values for expressions. */ 257 private final Deque<Integer> expressionValues = new ArrayDeque<>(); 258 259 /** Stack of belongs to range values for question operator. */ 260 private final Deque<Boolean> afterValues = new ArrayDeque<>(); 261 262 /** 263 * Range of the last processed expression. Used for checking that ternary operation 264 * which is a part of expression won't be processed for second time. 265 */ 266 private final TokenEnd processingTokenEnd = new TokenEnd(); 267 268 /** NP value for current range. */ 269 private BigInteger currentRangeValue; 270 271 /** Specify the maximum threshold allowed. */ 272 private int max = DEFAULT_MAX; 273 274 /** True, when branch is visited, but not leaved. */ 275 private boolean branchVisited; 276 277 /** 278 * Setter to specify the maximum threshold allowed. 279 * 280 * @param max the maximum threshold 281 */ 282 public void setMax(int max) { 283 this.max = max; 284 } 285 286 @Override 287 public int[] getDefaultTokens() { 288 return getRequiredTokens(); 289 } 290 291 @Override 292 public int[] getAcceptableTokens() { 293 return getRequiredTokens(); 294 } 295 296 @Override 297 public int[] getRequiredTokens() { 298 return new int[] { 299 TokenTypes.CTOR_DEF, 300 TokenTypes.METHOD_DEF, 301 TokenTypes.STATIC_INIT, 302 TokenTypes.INSTANCE_INIT, 303 TokenTypes.LITERAL_WHILE, 304 TokenTypes.LITERAL_DO, 305 TokenTypes.LITERAL_FOR, 306 TokenTypes.LITERAL_IF, 307 TokenTypes.LITERAL_ELSE, 308 TokenTypes.LITERAL_SWITCH, 309 TokenTypes.CASE_GROUP, 310 TokenTypes.LITERAL_TRY, 311 TokenTypes.LITERAL_CATCH, 312 TokenTypes.QUESTION, 313 TokenTypes.LITERAL_RETURN, 314 TokenTypes.LITERAL_DEFAULT, 315 TokenTypes.COMPACT_CTOR_DEF, 316 TokenTypes.SWITCH_RULE, 317 }; 318 } 319 320 @Override 321 public void beginTree(DetailAST rootAST) { 322 rangeValues.clear(); 323 expressionValues.clear(); 324 afterValues.clear(); 325 processingTokenEnd.reset(); 326 currentRangeValue = INITIAL_VALUE; 327 branchVisited = false; 328 } 329 330 @Override 331 public void visitToken(DetailAST ast) { 332 switch (ast.getType()) { 333 case TokenTypes.LITERAL_IF: 334 case TokenTypes.LITERAL_SWITCH: 335 case TokenTypes.LITERAL_WHILE: 336 case TokenTypes.LITERAL_DO: 337 case TokenTypes.LITERAL_FOR: 338 visitConditional(ast, 1); 339 break; 340 case TokenTypes.QUESTION: 341 visitUnitaryOperator(ast, 2); 342 break; 343 case TokenTypes.LITERAL_RETURN: 344 visitUnitaryOperator(ast, 0); 345 break; 346 case TokenTypes.CASE_GROUP: 347 final int caseNumber = countCaseTokens(ast); 348 branchVisited = true; 349 pushValue(caseNumber); 350 break; 351 case TokenTypes.SWITCH_RULE: 352 final int caseConstantNumber = countCaseConstants(ast); 353 branchVisited = true; 354 pushValue(caseConstantNumber); 355 break; 356 case TokenTypes.LITERAL_ELSE: 357 branchVisited = true; 358 if (currentRangeValue.equals(BigInteger.ZERO)) { 359 currentRangeValue = BigInteger.ONE; 360 } 361 pushValue(0); 362 break; 363 case TokenTypes.LITERAL_TRY: 364 case TokenTypes.LITERAL_CATCH: 365 case TokenTypes.LITERAL_DEFAULT: 366 pushValue(1); 367 break; 368 case TokenTypes.CTOR_DEF: 369 case TokenTypes.METHOD_DEF: 370 case TokenTypes.INSTANCE_INIT: 371 case TokenTypes.STATIC_INIT: 372 case TokenTypes.COMPACT_CTOR_DEF: 373 pushValue(0); 374 break; 375 default: 376 break; 377 } 378 } 379 380 @Override 381 public void leaveToken(DetailAST ast) { 382 switch (ast.getType()) { 383 case TokenTypes.LITERAL_WHILE: 384 case TokenTypes.LITERAL_DO: 385 case TokenTypes.LITERAL_FOR: 386 case TokenTypes.LITERAL_IF: 387 case TokenTypes.LITERAL_SWITCH: 388 leaveConditional(); 389 break; 390 case TokenTypes.LITERAL_TRY: 391 leaveMultiplyingConditional(); 392 break; 393 case TokenTypes.LITERAL_RETURN: 394 case TokenTypes.QUESTION: 395 leaveUnitaryOperator(); 396 break; 397 case TokenTypes.LITERAL_CATCH: 398 leaveAddingConditional(); 399 break; 400 case TokenTypes.LITERAL_DEFAULT: 401 leaveBranch(); 402 break; 403 case TokenTypes.LITERAL_ELSE: 404 case TokenTypes.CASE_GROUP: 405 case TokenTypes.SWITCH_RULE: 406 leaveBranch(); 407 branchVisited = false; 408 break; 409 case TokenTypes.CTOR_DEF: 410 case TokenTypes.METHOD_DEF: 411 case TokenTypes.INSTANCE_INIT: 412 case TokenTypes.STATIC_INIT: 413 case TokenTypes.COMPACT_CTOR_DEF: 414 leaveMethodDef(ast); 415 break; 416 default: 417 break; 418 } 419 } 420 421 /** 422 * Visits if, while, do-while, for and switch tokens - all of them have expression in 423 * parentheses which is used for calculation. 424 * 425 * @param ast visited token. 426 * @param basicBranchingFactor default number of branches added. 427 */ 428 private void visitConditional(DetailAST ast, int basicBranchingFactor) { 429 int expressionValue = basicBranchingFactor; 430 DetailAST bracketed; 431 for (bracketed = ast.findFirstToken(TokenTypes.LPAREN); 432 bracketed.getType() != TokenTypes.RPAREN; 433 bracketed = bracketed.getNextSibling()) { 434 expressionValue += countConditionalOperators(bracketed); 435 } 436 processingTokenEnd.setToken(bracketed); 437 pushValue(expressionValue); 438 } 439 440 /** 441 * Visits ternary operator (?:) and return tokens. They differ from those processed by 442 * visitConditional method in that their expression isn't bracketed. 443 * 444 * @param ast visited token. 445 * @param basicBranchingFactor number of branches inherently added by this token. 446 */ 447 private void visitUnitaryOperator(DetailAST ast, int basicBranchingFactor) { 448 final boolean isAfter = processingTokenEnd.isAfter(ast); 449 afterValues.push(isAfter); 450 if (!isAfter) { 451 processingTokenEnd.setToken(getLastToken(ast)); 452 final int expressionValue = basicBranchingFactor + countConditionalOperators(ast); 453 pushValue(expressionValue); 454 } 455 } 456 457 /** 458 * Leaves ternary operator (?:) and return tokens. 459 */ 460 private void leaveUnitaryOperator() { 461 if (Boolean.FALSE.equals(afterValues.pop())) { 462 final Values valuePair = popValue(); 463 BigInteger basicRangeValue = valuePair.getRangeValue(); 464 BigInteger expressionValue = valuePair.getExpressionValue(); 465 if (expressionValue.equals(BigInteger.ZERO)) { 466 expressionValue = BigInteger.ONE; 467 } 468 if (basicRangeValue.equals(BigInteger.ZERO)) { 469 basicRangeValue = BigInteger.ONE; 470 } 471 currentRangeValue = currentRangeValue.add(expressionValue).multiply(basicRangeValue); 472 } 473 } 474 475 /** Leaves while, do, for, if, ternary (?::), return or switch. */ 476 private void leaveConditional() { 477 final Values valuePair = popValue(); 478 final BigInteger expressionValue = valuePair.getExpressionValue(); 479 BigInteger basicRangeValue = valuePair.getRangeValue(); 480 if (currentRangeValue.equals(BigInteger.ZERO)) { 481 currentRangeValue = BigInteger.ONE; 482 } 483 if (basicRangeValue.equals(BigInteger.ZERO)) { 484 basicRangeValue = BigInteger.ONE; 485 } 486 currentRangeValue = currentRangeValue.add(expressionValue).multiply(basicRangeValue); 487 } 488 489 /** Leaves else, default or case group tokens. */ 490 private void leaveBranch() { 491 final Values valuePair = popValue(); 492 final BigInteger basicRangeValue = valuePair.getRangeValue(); 493 final BigInteger expressionValue = valuePair.getExpressionValue(); 494 if (branchVisited && currentRangeValue.equals(BigInteger.ZERO)) { 495 currentRangeValue = BigInteger.ONE; 496 } 497 currentRangeValue = currentRangeValue.subtract(BigInteger.ONE) 498 .add(basicRangeValue) 499 .add(expressionValue); 500 } 501 502 /** 503 * Process the end of a method definition. 504 * 505 * @param ast the token type representing the method definition 506 */ 507 private void leaveMethodDef(DetailAST ast) { 508 final BigInteger bigIntegerMax = BigInteger.valueOf(max); 509 if (currentRangeValue.compareTo(bigIntegerMax) > 0) { 510 log(ast, MSG_KEY, currentRangeValue, bigIntegerMax); 511 } 512 popValue(); 513 currentRangeValue = INITIAL_VALUE; 514 } 515 516 /** Leaves catch. */ 517 private void leaveAddingConditional() { 518 currentRangeValue = currentRangeValue.add(popValue().getRangeValue().add(BigInteger.ONE)); 519 } 520 521 /** 522 * Pushes the current range value on the range value stack. Pushes this token expression value 523 * on the expression value stack. 524 * 525 * @param expressionValue value of expression calculated for current token. 526 */ 527 private void pushValue(Integer expressionValue) { 528 rangeValues.push(currentRangeValue); 529 expressionValues.push(expressionValue); 530 currentRangeValue = INITIAL_VALUE; 531 } 532 533 /** 534 * Pops values from both stack of expression values and stack of range values. 535 * 536 * @return pair of head values from both of the stacks. 537 */ 538 private Values popValue() { 539 final int expressionValue = expressionValues.pop(); 540 return new Values(rangeValues.pop(), BigInteger.valueOf(expressionValue)); 541 } 542 543 /** Leaves try. */ 544 private void leaveMultiplyingConditional() { 545 currentRangeValue = currentRangeValue.add(BigInteger.ONE) 546 .multiply(popValue().getRangeValue().add(BigInteger.ONE)); 547 } 548 549 /** 550 * Calculates number of conditional operators, including inline ternary operator, for a token. 551 * 552 * @param ast inspected token. 553 * @return number of conditional operators. 554 * @see <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.23"> 555 * Java Language Specification, §15.23</a> 556 * @see <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.24"> 557 * Java Language Specification, §15.24</a> 558 * @see <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25"> 559 * Java Language Specification, §15.25</a> 560 */ 561 private static int countConditionalOperators(DetailAST ast) { 562 int number = 0; 563 for (DetailAST child = ast.getFirstChild(); child != null; 564 child = child.getNextSibling()) { 565 final int type = child.getType(); 566 if (type == TokenTypes.LOR || type == TokenTypes.LAND) { 567 number++; 568 } 569 else if (type == TokenTypes.QUESTION) { 570 number += 2; 571 } 572 number += countConditionalOperators(child); 573 } 574 return number; 575 } 576 577 /** 578 * Finds a leaf, which is the most distant from the root. 579 * 580 * @param ast the root of tree. 581 * @return the leaf. 582 */ 583 private static DetailAST getLastToken(DetailAST ast) { 584 final DetailAST lastChild = ast.getLastChild(); 585 final DetailAST result; 586 if (lastChild.getFirstChild() == null) { 587 result = lastChild; 588 } 589 else { 590 result = getLastToken(lastChild); 591 } 592 return result; 593 } 594 595 /** 596 * Counts number of case tokens subject to a case group token. 597 * 598 * @param ast case group token. 599 * @return number of case tokens. 600 */ 601 private static int countCaseTokens(DetailAST ast) { 602 int counter = 0; 603 for (DetailAST iterator = ast.getFirstChild(); iterator != null; 604 iterator = iterator.getNextSibling()) { 605 if (iterator.getType() == TokenTypes.LITERAL_CASE) { 606 counter++; 607 } 608 } 609 return counter; 610 } 611 612 /** 613 * Counts number of case constants (EXPR) tokens in a switch labeled rule. 614 * 615 * @param ast switch rule token. 616 * @return number of case constant (EXPR) tokens. 617 */ 618 private static int countCaseConstants(DetailAST ast) { 619 final AtomicInteger counter = new AtomicInteger(); 620 final DetailAST literalCase = ast.getFirstChild(); 621 622 TokenUtil.forEachChild(literalCase, 623 TokenTypes.EXPR, node -> counter.getAndIncrement()); 624 625 return counter.get(); 626 } 627 628 /** 629 * Coordinates of token end. Used to prevent inline ternary 630 * operator from being processed twice. 631 */ 632 private static final class TokenEnd { 633 634 /** End line of token. */ 635 private int endLineNo; 636 637 /** End column of token. */ 638 private int endColumnNo; 639 640 /** 641 * Sets end coordinates from given token. 642 * 643 * @param endToken token. 644 */ 645 public void setToken(DetailAST endToken) { 646 if (!isAfter(endToken)) { 647 endLineNo = endToken.getLineNo(); 648 endColumnNo = endToken.getColumnNo(); 649 } 650 } 651 652 /** Sets end token coordinates to the start of the file. */ 653 public void reset() { 654 endLineNo = 0; 655 endColumnNo = 0; 656 } 657 658 /** 659 * Checks if saved coordinates located after given token. 660 * 661 * @param ast given token. 662 * @return true, if saved coordinates located after given token. 663 */ 664 public boolean isAfter(DetailAST ast) { 665 final int lineNo = ast.getLineNo(); 666 final int columnNo = ast.getColumnNo(); 667 return lineNo <= endLineNo 668 && (lineNo != endLineNo 669 || columnNo <= endColumnNo); 670 } 671 672 } 673 674 /** 675 * Class that store range value and expression value. 676 */ 677 private static final class Values { 678 679 /** NP value for range. */ 680 private final BigInteger rangeValue; 681 682 /** NP value for expression. */ 683 private final BigInteger expressionValue; 684 685 /** 686 * Constructor that assigns all of class fields. 687 * 688 * @param valueOfRange NP value for range 689 * @param valueOfExpression NP value for expression 690 */ 691 private Values(BigInteger valueOfRange, BigInteger valueOfExpression) { 692 rangeValue = valueOfRange; 693 expressionValue = valueOfExpression; 694 } 695 696 /** 697 * Returns NP value for range. 698 * 699 * @return NP value for range 700 */ 701 public BigInteger getRangeValue() { 702 return rangeValue; 703 } 704 705 /** 706 * Returns NP value for expression. 707 * 708 * @return NP value for expression 709 */ 710 public BigInteger getExpressionValue() { 711 return expressionValue; 712 } 713 714 } 715 716}