001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2016 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.Deque; 023import java.util.Map; 024import java.util.Queue; 025import java.util.Set; 026 027import com.google.common.collect.ImmutableSet; 028import com.google.common.collect.Lists; 029import com.google.common.collect.Maps; 030import com.google.common.collect.Sets; 031import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 032import com.puppycrawl.tools.checkstyle.api.DetailAST; 033import com.puppycrawl.tools.checkstyle.api.TokenTypes; 034import com.puppycrawl.tools.checkstyle.utils.ScopeUtils; 035 036/** 037 * <p>Checks that code doesn't rely on the "this" default. 038 * That is references to instance variables and methods of the present 039 * object are explicitly of the form "this.varName" or 040 * "this.methodName(args)". 041 * </p> 042 * 043 * <p>Warning: the Check is very controversial and not that actual nowadays.</p> 044 * 045 * <p>Examples of use: 046 * <pre> 047 * <module name="RequireThis"/> 048 * </pre> 049 * An example of how to configure to check {@code this} qualifier for 050 * methods only: 051 * <pre> 052 * <module name="RequireThis"> 053 * <property name="checkFields" value="false"/> 054 * <property name="checkMethods" value="true"/> 055 * </module> 056 * </pre> 057 * 058 * <p>Rationale:</p> 059 * <ol> 060 * <li> 061 * The same notation/habit for C++ and Java (C++ have global methods, so having 062 * "this." do make sense in it to distinguish call of method of class 063 * instead of global). 064 * </li> 065 * <li> 066 * Non-IDE development (ease of refactoring, some clearness to distinguish 067 * static and non-static methods). 068 * </li> 069 * </ol> 070 * 071 * <p>Limitations: Nothing is currently done about static variables 072 * or catch-blocks. Static methods invoked on a class name seem to be OK; 073 * both the class name and the method name have a DOT parent. 074 * Non-static methods invoked on either this or a variable name seem to be 075 * OK, likewise.</p> 076 * 077 * @author Stephen Bloch 078 * @author o_sukhodolsky 079 */ 080public class RequireThisCheck extends AbstractCheck { 081 082 /** 083 * A key is pointing to the warning message text in "messages.properties" 084 * file. 085 */ 086 public static final String MSG_METHOD = "require.this.method"; 087 088 /** 089 * A key is pointing to the warning message text in "messages.properties" 090 * file. 091 */ 092 public static final String MSG_VARIABLE = "require.this.variable"; 093 /** 094 * Set of all declaration tokens. 095 */ 096 private static final ImmutableSet<Integer> DECLARATION_TOKENS = ImmutableSet.of( 097 TokenTypes.VARIABLE_DEF, 098 TokenTypes.CTOR_DEF, 099 TokenTypes.METHOD_DEF, 100 TokenTypes.CLASS_DEF, 101 TokenTypes.ENUM_DEF, 102 TokenTypes.INTERFACE_DEF, 103 TokenTypes.PARAMETER_DEF, 104 TokenTypes.TYPE_ARGUMENT 105 ); 106 107 /** 108 * Tree of all the parsed frames. 109 */ 110 private Map<DetailAST, AbstractFrame> frames; 111 112 /** 113 * Frame for the currently processed AST. 114 */ 115 private AbstractFrame current; 116 117 /** Whether we should check fields usage. */ 118 private boolean checkFields = true; 119 /** Whether we should check methods usage. */ 120 private boolean checkMethods = true; 121 122 /** 123 * Setter for checkFields property. 124 * @param checkFields should we check fields usage or not. 125 */ 126 public void setCheckFields(boolean checkFields) { 127 this.checkFields = checkFields; 128 } 129 130 /** 131 * Setter for checkMethods property. 132 * @param checkMethods should we check methods usage or not. 133 */ 134 public void setCheckMethods(boolean checkMethods) { 135 this.checkMethods = checkMethods; 136 } 137 138 @Override 139 public int[] getDefaultTokens() { 140 return getAcceptableTokens(); 141 } 142 143 @Override 144 public int[] getRequiredTokens() { 145 return getAcceptableTokens(); 146 } 147 148 @Override 149 public int[] getAcceptableTokens() { 150 return new int[] { 151 TokenTypes.CLASS_DEF, 152 TokenTypes.INTERFACE_DEF, 153 TokenTypes.ENUM_DEF, 154 TokenTypes.CTOR_DEF, 155 TokenTypes.METHOD_DEF, 156 TokenTypes.SLIST, 157 TokenTypes.IDENT, 158 }; 159 } 160 161 @Override 162 public void beginTree(DetailAST rootAST) { 163 final Deque<AbstractFrame> frameStack = Lists.newLinkedList(); 164 165 frames = Maps.newHashMap(); 166 167 DetailAST curNode = rootAST; 168 while (curNode != null) { 169 collectDeclarations(frameStack, curNode); 170 DetailAST toVisit = curNode.getFirstChild(); 171 while (curNode != null && toVisit == null) { 172 endCollectingDeclarations(frameStack, curNode); 173 toVisit = curNode.getNextSibling(); 174 if (toVisit == null) { 175 curNode = curNode.getParent(); 176 } 177 } 178 curNode = toVisit; 179 } 180 } 181 182 @Override 183 public void visitToken(DetailAST ast) { 184 switch (ast.getType()) { 185 case TokenTypes.IDENT : 186 processIdent(ast); 187 break; 188 case TokenTypes.CLASS_DEF : 189 case TokenTypes.INTERFACE_DEF : 190 case TokenTypes.ENUM_DEF : 191 case TokenTypes.ANNOTATION_DEF : 192 case TokenTypes.SLIST : 193 case TokenTypes.METHOD_DEF : 194 case TokenTypes.CTOR_DEF : 195 current = frames.get(ast); 196 break; 197 default : 198 // do nothing 199 } 200 } 201 202 /** 203 * Checks if a given IDENT is method call or field name which 204 * require explicit {@code this} qualifier. 205 * 206 * @param ast IDENT to check. 207 */ 208 private void processIdent(DetailAST ast) { 209 final int parentType = ast.getParent().getType(); 210 switch (parentType) { 211 case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR: 212 case TokenTypes.ANNOTATION: 213 case TokenTypes.ANNOTATION_FIELD_DEF: 214 // no need to check annotations content 215 break; 216 case TokenTypes.METHOD_CALL: 217 // let's check method calls 218 if (checkMethods) { 219 final AbstractFrame frame = checkMethod(ast); 220 if (frame != null) { 221 logViolation(MSG_METHOD, ast, frame); 222 } 223 } 224 break; 225 default: 226 if (checkFields) { 227 final AbstractFrame frame = processField(ast, parentType); 228 if (frame != null) { 229 logViolation(MSG_VARIABLE, ast, frame); 230 } 231 } 232 break; 233 } 234 } 235 236 /** 237 * Helper method to log a LocalizedMessage. 238 * @param ast a node to get line id column numbers associated with the message. 239 * @param msgKey key to locale message format. 240 * @param frame the frame, where the violation is found. 241 */ 242 private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) { 243 if (frame.getFrameName().equals(getNearestClassFrameName())) { 244 log(ast, msgKey, ast.getText(), ""); 245 } 246 else { 247 log(ast, msgKey, ast.getText(), frame.getFrameName() + '.'); 248 } 249 } 250 251 /** 252 * Process validation of Field. 253 * @param ast field definition ast token 254 * @param parentType type of the parent 255 * @return frame, where the field is declared, if the violation is found and null otherwise 256 */ 257 private AbstractFrame processField(DetailAST ast, int parentType) { 258 final boolean importOrPackage = ScopeUtils.getSurroundingScope(ast) == null; 259 final boolean methodNameInMethodCall = parentType == TokenTypes.DOT 260 && ast.getPreviousSibling() != null; 261 final boolean typeName = parentType == TokenTypes.TYPE 262 || parentType == TokenTypes.LITERAL_NEW; 263 AbstractFrame frame = null; 264 265 if (!importOrPackage 266 && !methodNameInMethodCall 267 && !typeName 268 && !isDeclarationToken(parentType)) { 269 frame = checkField(ast); 270 } 271 return frame; 272 } 273 274 /** 275 * Parse the next AST for declarations. 276 * 277 * @param frameStack Stack containing the FrameTree being built 278 * @param ast AST to parse 279 */ 280 private static void collectDeclarations(Deque<AbstractFrame> frameStack, 281 DetailAST ast) { 282 final AbstractFrame frame = frameStack.peek(); 283 switch (ast.getType()) { 284 case TokenTypes.VARIABLE_DEF : 285 collectVariableDeclarations(ast, frame); 286 break; 287 case TokenTypes.PARAMETER_DEF : 288 final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT); 289 frame.addIdent(parameterIdent); 290 break; 291 case TokenTypes.CLASS_DEF : 292 case TokenTypes.INTERFACE_DEF : 293 case TokenTypes.ENUM_DEF : 294 case TokenTypes.ANNOTATION_DEF : 295 final DetailAST classIdent = ast.findFirstToken(TokenTypes.IDENT); 296 frameStack.addFirst(new ClassFrame(frame, classIdent.getText())); 297 break; 298 case TokenTypes.SLIST : 299 frameStack.addFirst(new BlockFrame(frame)); 300 break; 301 case TokenTypes.METHOD_DEF : 302 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); 303 if (frame.getType() == FrameType.CLASS_FRAME) { 304 final DetailAST mods = 305 ast.findFirstToken(TokenTypes.MODIFIERS); 306 if (mods.branchContains(TokenTypes.LITERAL_STATIC)) { 307 ((ClassFrame) frame).addStaticMethod(ident); 308 } 309 else { 310 ((ClassFrame) frame).addInstanceMethod(ident); 311 } 312 } 313 frameStack.addFirst(new MethodFrame(frame)); 314 break; 315 case TokenTypes.CTOR_DEF : 316 frameStack.addFirst(new MethodFrame(frame)); 317 break; 318 default: 319 // do nothing 320 } 321 } 322 323 /** 324 * Collect Variable Declarations. 325 * @param ast variable token 326 * @param frame current frame 327 */ 328 private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) { 329 final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT); 330 if (frame.getType() == FrameType.CLASS_FRAME) { 331 final DetailAST mods = 332 ast.findFirstToken(TokenTypes.MODIFIERS); 333 if (ScopeUtils.isInInterfaceBlock(ast) 334 || mods.branchContains(TokenTypes.LITERAL_STATIC)) { 335 ((ClassFrame) frame).addStaticMember(ident); 336 } 337 else { 338 ((ClassFrame) frame).addInstanceMember(ident); 339 } 340 } 341 else { 342 frame.addIdent(ident); 343 } 344 } 345 346 /** 347 * End parsing of the AST for declarations. 348 * 349 * @param frameStack Stack containing the FrameTree being built 350 * @param ast AST that was parsed 351 */ 352 private void endCollectingDeclarations(Queue<AbstractFrame> frameStack, 353 DetailAST ast) { 354 switch (ast.getType()) { 355 case TokenTypes.CLASS_DEF : 356 case TokenTypes.INTERFACE_DEF : 357 case TokenTypes.ENUM_DEF : 358 case TokenTypes.ANNOTATION_DEF : 359 case TokenTypes.SLIST : 360 case TokenTypes.METHOD_DEF : 361 case TokenTypes.CTOR_DEF : 362 frames.put(ast, frameStack.poll()); 363 break; 364 default : 365 // do nothing 366 } 367 } 368 369 /** 370 * Check if given name is a name for class field in current environment. 371 * @param ast an IDENT ast to check 372 * @return frame, where the field is declared, if the violation is found and null otherwise 373 */ 374 private AbstractFrame checkField(DetailAST ast) { 375 final AbstractFrame frame = findFrame(ast, false); 376 if (frame != null 377 && frame.getType() == FrameType.CLASS_FRAME 378 && ((ClassFrame) frame).hasInstanceMember(ast)) { 379 return frame; 380 } 381 return null; 382 } 383 384 /** 385 * Check if given name is a name for class method in current environment. 386 * @param ast the IDENT ast of the name to check 387 * @return frame, where the method is declared, if the violation is found and null otherwise 388 */ 389 private AbstractFrame checkMethod(DetailAST ast) { 390 final AbstractFrame frame = findFrame(ast, true); 391 if (frame != null 392 && ((ClassFrame) frame).hasInstanceMethod(ast) 393 && !((ClassFrame) frame).hasStaticMethod(ast)) { 394 return frame; 395 } 396 return null; 397 } 398 399 /** 400 * Find frame containing declaration. 401 * @param name IDENT ast of the declaration to find. 402 * @param lookForMethod whether we are looking for a method name. 403 * @return AbstractFrame containing declaration or null. 404 */ 405 private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) { 406 if (current == null) { 407 return null; 408 } 409 else { 410 return current.getIfContains(name, lookForMethod); 411 } 412 } 413 414 /** 415 * Check that token is related to Definition tokens. 416 * @param parentType token Type 417 * @return true if token is related to Definition Tokens 418 */ 419 private static boolean isDeclarationToken(int parentType) { 420 return DECLARATION_TOKENS.contains(parentType); 421 } 422 423 /** 424 * Get the name of the nearest parent ClassFrame. 425 * @return the name of the nearest parent ClassFrame. 426 */ 427 private String getNearestClassFrameName() { 428 AbstractFrame frame = current; 429 while (frame.getType() != FrameType.CLASS_FRAME) { 430 frame = frame.getParent(); 431 } 432 return frame.getFrameName(); 433 } 434 435 /** An AbstractFrame type. */ 436 private enum FrameType { 437 /** Class frame type. */ 438 CLASS_FRAME, 439 /** Method frame type. */ 440 METHOD_FRAME, 441 /** Block frame type. */ 442 BLOCK_FRAME, 443 } 444 445 /** 446 * A declaration frame. 447 * @author Stephen Bloch 448 */ 449 private abstract static class AbstractFrame { 450 /** Set of name of variables declared in this frame. */ 451 private final Set<DetailAST> varIdents; 452 453 /** 454 * Parent frame. 455 */ 456 private final AbstractFrame parent; 457 458 /** 459 * Frame name. 460 */ 461 private final String frameName; 462 463 /** 464 * Constructor -- invokable only via super() from subclasses. 465 * 466 * @param parent parent frame 467 * @param frameName frame name 468 */ 469 protected AbstractFrame(AbstractFrame parent, String frameName) { 470 this.parent = parent; 471 this.frameName = frameName; 472 varIdents = Sets.newHashSet(); 473 } 474 475 /** 476 * Get the type of the frame. 477 * @return a FrameType. 478 */ 479 protected abstract FrameType getType(); 480 481 /** 482 * Add a name to the frame. 483 * @param identToAdd the name we're adding 484 */ 485 private void addIdent(DetailAST identToAdd) { 486 varIdents.add(identToAdd); 487 } 488 489 protected AbstractFrame getParent() { 490 return parent; 491 } 492 493 protected String getFrameName() { 494 return frameName; 495 } 496 497 /** Check whether the frame contains a field or a variable with the given name. 498 * @param nameToFind the IDENT ast of the name we're looking for 499 * @return whether it was found 500 */ 501 protected boolean containsFieldOrVariable(DetailAST nameToFind) { 502 return containsFieldOrVariableDef(varIdents, nameToFind); 503 } 504 505 /** Check whether the frame contains a given name. 506 * @param nameToFind IDENT ast of the name we're looking for. 507 * @param lookForMethod whether we are looking for a method name. 508 * @return whether it was found. 509 */ 510 protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) { 511 final AbstractFrame frame; 512 513 if (!lookForMethod 514 && containsFieldOrVariable(nameToFind)) { 515 frame = this; 516 } 517 else { 518 frame = parent.getIfContains(nameToFind, lookForMethod); 519 } 520 return frame; 521 } 522 523 /** 524 * Whether the set contains a declaration with the text of the specified 525 * IDENT ast and it is declared in a proper position. 526 * @param set the set of declarations. 527 * @param ident the specified IDENT ast 528 * @return true if the set contains a declaration with the text of the specified 529 * IDENT ast and it is declared in a proper position. 530 */ 531 protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) { 532 boolean result = false; 533 for (DetailAST ast: set) { 534 if (isProperDefinition(ident, ast)) { 535 result = true; 536 break; 537 } 538 } 539 return result; 540 } 541 542 /** 543 * Whether the definition is correspondent to the IDENT. 544 * @param ident the IDENT ast to check. 545 * @param ast the IDENT ast of the definition to check. 546 * @return true if ast is correspondent to ident. 547 */ 548 protected boolean isProperDefinition(DetailAST ident, DetailAST ast) { 549 final String nameToFind = ident.getText(); 550 return nameToFind.equals(ast.getText()) 551 && checkPosition(ast, ident); 552 } 553 554 /** 555 * Whether the declaration is located before the checked ast. 556 * @param ast1 the IDENT ast of the declaration. 557 * @param ast2 the IDENT ast to check. 558 * @return true, if the declaration is located before the checked ast. 559 */ 560 private static boolean checkPosition(DetailAST ast1, DetailAST ast2) { 561 boolean result = false; 562 if (ast1.getLineNo() < ast2.getLineNo() 563 || ast1.getLineNo() == ast2.getLineNo() 564 && ast1.getColumnNo() < ast2.getColumnNo()) { 565 result = true; 566 } 567 return result; 568 } 569 } 570 571 /** 572 * A frame initiated at method definition; holds parameter names. 573 * @author Stephen Bloch 574 */ 575 private static class MethodFrame extends AbstractFrame { 576 /** 577 * Creates method frame. 578 * @param parent parent frame 579 */ 580 protected MethodFrame(AbstractFrame parent) { 581 super(parent, null); 582 } 583 584 @Override 585 protected FrameType getType() { 586 return FrameType.METHOD_FRAME; 587 } 588 } 589 590 /** 591 * A frame initiated at class< enum or interface definition; holds instance variable names. 592 * @author Stephen Bloch 593 */ 594 private static class ClassFrame extends AbstractFrame { 595 /** Set of idents of instance members declared in this frame. */ 596 private final Set<DetailAST> instanceMembers; 597 /** Set of idents of instance methods declared in this frame. */ 598 private final Set<DetailAST> instanceMethods; 599 /** Set of idents of variables declared in this frame. */ 600 private final Set<DetailAST> staticMembers; 601 /** Set of idents of static methods declared in this frame. */ 602 private final Set<DetailAST> staticMethods; 603 604 /** 605 * Creates new instance of ClassFrame. 606 * @param parent parent frame 607 * @param frameName frame name 608 */ 609 ClassFrame(AbstractFrame parent, String frameName) { 610 super(parent, frameName); 611 instanceMembers = Sets.newHashSet(); 612 instanceMethods = Sets.newHashSet(); 613 staticMembers = Sets.newHashSet(); 614 staticMethods = Sets.newHashSet(); 615 } 616 617 @Override 618 protected FrameType getType() { 619 return FrameType.CLASS_FRAME; 620 } 621 622 /** 623 * Adds static member's ident. 624 * @param ident an ident of static member of the class 625 */ 626 public void addStaticMember(final DetailAST ident) { 627 staticMembers.add(ident); 628 } 629 630 /** 631 * Adds static method's name. 632 * @param ident an ident of static method of the class 633 */ 634 public void addStaticMethod(final DetailAST ident) { 635 staticMethods.add(ident); 636 } 637 638 /** 639 * Adds instance member's ident. 640 * @param ident an ident of instance member of the class 641 */ 642 public void addInstanceMember(final DetailAST ident) { 643 instanceMembers.add(ident); 644 } 645 646 /** 647 * Adds instance method's name. 648 * @param ident an ident of instance method of the class 649 */ 650 public void addInstanceMethod(final DetailAST ident) { 651 instanceMethods.add(ident); 652 } 653 654 /** 655 * Checks if a given name is a known instance member of the class. 656 * @param ident the IDENT ast of the name to check 657 * @return true is the given name is a name of a known 658 * instance member of the class 659 */ 660 public boolean hasInstanceMember(final DetailAST ident) { 661 return containsFieldOrVariableDef(instanceMembers, ident); 662 } 663 664 /** 665 * Checks if a given name is a known instance method of the class. 666 * @param ident the IDENT ast of the method call to check 667 * @return true if the given ast is correspondent to a known 668 * instance method of the class 669 */ 670 public boolean hasInstanceMethod(final DetailAST ident) { 671 return containsMethodDef(instanceMethods, ident); 672 } 673 674 /** 675 * Checks if a given name is a known static method of the class. 676 * @param ident the IDENT ast of the method call to check 677 * @return true is the given ast is correspondent to a known 678 * instance method of the class 679 */ 680 public boolean hasStaticMethod(final DetailAST ident) { 681 return containsMethodDef(staticMethods, ident); 682 } 683 684 @Override 685 protected boolean containsFieldOrVariable(DetailAST nameToFind) { 686 return containsFieldOrVariableDef(instanceMembers, nameToFind) 687 || containsFieldOrVariableDef(staticMembers, nameToFind); 688 } 689 690 @Override 691 protected boolean isProperDefinition(DetailAST ident, DetailAST ast) { 692 final String nameToFind = ident.getText(); 693 return nameToFind.equals(ast.getText()); 694 } 695 696 @Override 697 protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) { 698 AbstractFrame frame = null; 699 700 if (lookForMethod && containsMethod(nameToFind) 701 || containsFieldOrVariable(nameToFind)) { 702 frame = this; 703 } 704 else if (getParent() != null) { 705 frame = getParent().getIfContains(nameToFind, lookForMethod); 706 } 707 return frame; 708 } 709 710 /** 711 * Check whether the frame contains a given method. 712 * @param methodToFind the AST of the method to find. 713 * @return true, if a method with the same name and number of parameters is found. 714 */ 715 private boolean containsMethod(DetailAST methodToFind) { 716 return containsMethodDef(instanceMethods, methodToFind) 717 || containsMethodDef(staticMethods, methodToFind); 718 } 719 720 /** 721 * Whether the set contains a method definition with the 722 * same name and number of parameters. 723 * @param set the set of definitions. 724 * @param ident the specified method call IDENT ast. 725 * @return true if the set contains a definition with the 726 * same name and number of parameters. 727 */ 728 private boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) { 729 boolean result = false; 730 for (DetailAST ast: set) { 731 if (isSimilarSignature(ident, ast)) { 732 result = true; 733 break; 734 } 735 } 736 return result; 737 } 738 739 /** 740 * Whether the method definition has the same name and number of parameters. 741 * @param ident the specified method call IDENT ast. 742 * @param ast the ast of a method definition to compare with. 743 * @return true if a method definition has the same name and number of parameters 744 * as the method call. 745 */ 746 private boolean isSimilarSignature(DetailAST ident, DetailAST ast) { 747 boolean result = false; 748 if (ident.getText().equals(ast.getText())) { 749 final int paramsNumber = ast.getParent().findFirstToken(TokenTypes.PARAMETERS) 750 .getChildCount(); 751 final int argsNumber = ident.getParent().findFirstToken(TokenTypes.ELIST) 752 .getChildCount(); 753 result = paramsNumber == argsNumber; 754 } 755 return result; 756 } 757 } 758 759 /** 760 * A frame initiated on entering a statement list; holds local variable names. 761 * @author Stephen Bloch 762 */ 763 private static class BlockFrame extends AbstractFrame { 764 765 /** 766 * Creates block frame. 767 * @param parent parent frame 768 */ 769 protected BlockFrame(AbstractFrame parent) { 770 super(parent, null); 771 } 772 773 @Override 774 protected FrameType getType() { 775 return FrameType.BLOCK_FRAME; 776 } 777 } 778}