1 18 package org.apache.beehive.netui.compiler; 19 20 import org.apache.beehive.netui.compiler.genmodel.GenStrutsApp; 21 import org.apache.beehive.netui.compiler.grammar.ActionGrammar; 22 import org.apache.beehive.netui.compiler.grammar.ExceptionHandlerGrammar; 23 import org.apache.beehive.netui.compiler.grammar.WebappPathType; 24 import org.apache.beehive.netui.compiler.typesystem.declaration.AnnotationInstance; 25 import org.apache.beehive.netui.compiler.typesystem.declaration.AnnotationValue; 26 import org.apache.beehive.netui.compiler.typesystem.declaration.ClassDeclaration; 27 import org.apache.beehive.netui.compiler.typesystem.declaration.FieldDeclaration; 28 import org.apache.beehive.netui.compiler.typesystem.declaration.MethodDeclaration; 29 import org.apache.beehive.netui.compiler.typesystem.declaration.Modifier; 30 import org.apache.beehive.netui.compiler.typesystem.declaration.PackageDeclaration; 31 import org.apache.beehive.netui.compiler.typesystem.declaration.ParameterDeclaration; 32 import org.apache.beehive.netui.compiler.typesystem.declaration.TypeDeclaration; 33 import org.apache.beehive.netui.compiler.typesystem.env.AnnotationProcessorEnvironment; 34 import org.apache.beehive.netui.compiler.typesystem.type.ClassType; 35 import org.apache.beehive.netui.compiler.typesystem.type.TypeInstance; 36 import org.apache.xmlbeans.XmlException; 37 38 import java.io.File ; 39 import java.io.FilenameFilter ; 40 import java.io.IOException ; 41 import java.util.ArrayList ; 42 import java.util.Collection ; 43 import java.util.HashMap ; 44 import java.util.HashSet ; 45 import java.util.Iterator ; 46 import java.util.List ; 47 import java.util.Map ; 48 import java.util.Set ; 49 50 51 public abstract class FlowControllerChecker 52 extends BaseChecker 53 implements JpfLanguageConstants 54 { 55 private AnnotationGrammar _controllerGrammar; 56 private AnnotationGrammar _actionGrammar; 57 private AnnotationGrammar _exceptionHandlerGrammar; 58 private FormBeanChecker _formBeanChecker; 59 private Map _checkResultMap; 60 61 protected FlowControllerChecker( AnnotationProcessorEnvironment env, FlowControllerInfo fcInfo, Diagnostics diags ) 62 { 63 super( env, fcInfo, diags ); 64 } 65 66 protected void doAdditionalClassChecks( ClassDeclaration jpfClass ) 67 { 68 } 69 70 protected Map getCheckResultMap() 71 { 72 return _checkResultMap; 73 } 74 75 protected abstract String getDesiredBaseClass( ClassDeclaration jclass ); 76 77 protected abstract AnnotationGrammar getControllerGrammar(); 78 79 public Map onCheck( ClassDeclaration jclass ) 80 throws FatalCompileTimeException 81 { 82 FlowControllerInfo fcInfo = getFCSourceFileInfo(); 83 84 _checkResultMap = new HashMap (); 85 _controllerGrammar = getControllerGrammar(); 86 _actionGrammar = new ActionGrammar( getEnv(), getDiagnostics(), getRuntimeVersionChecker(), fcInfo ); 87 _exceptionHandlerGrammar = 88 new ExceptionHandlerGrammar( getEnv(), getDiagnostics(), getRuntimeVersionChecker(), fcInfo ); 89 _formBeanChecker = new FormBeanChecker( getEnv(), getDiagnostics() ); 90 91 fcInfo.startBuild( getEnv(), jclass ); 92 93 try 94 { 95 return onCheckInternal( jclass ); 96 } 97 finally 98 { 99 fcInfo.endBuild(); 100 } 101 } 102 103 private Map onCheckInternal( ClassDeclaration jclass ) 104 throws FatalCompileTimeException 105 { 106 FlowControllerInfo fcInfo = getFCSourceFileInfo(); 107 108 String desiredBaseClass = getDesiredBaseClass( jclass ); 112 if ( desiredBaseClass != null && ! CompilerUtils.isAssignableFrom( desiredBaseClass, jclass, getEnv() ) ) 113 { 114 getDiagnostics().addError( jclass, "error.does-not-extend-base", desiredBaseClass ); 115 } 116 117 startCheckClass( jclass ); 121 122 Collection fields = CompilerUtils.getClassFields( jclass ); 126 127 for ( Iterator ii = fields.iterator(); ii.hasNext(); ) 128 { 129 FieldDeclaration field = ( FieldDeclaration ) ii.next(); 130 checkField( field, jclass ); 131 } 132 133 MethodDeclaration[] methods = CompilerUtils.getClassMethods( jclass, null ); 137 138 for ( int i = 0; i < methods.length; i++ ) 139 { 140 MethodDeclaration method = methods[i]; 141 TypeDeclaration declaringType = method.getDeclaringType(); 142 143 if ( declaringType.equals( jclass ) || declaringType.getPosition() == null ) 148 checkMethod( method, jclass ); 149 } 150 151 Collection innerTypes = CompilerUtils.getClassNestedTypes( jclass ); 155 156 for ( Iterator ii = innerTypes.iterator(); ii.hasNext(); ) 157 { 158 TypeDeclaration innerType = ( TypeDeclaration ) ii.next(); 159 if ( innerType instanceof ClassDeclaration ) checkInnerClass( ( ClassDeclaration ) innerType ); 160 } 161 162 doAdditionalClassChecks( jclass ); 166 167 enableNavigateTo( jclass, fcInfo.getMergedControllerAnnotation(), fcInfo ); 172 Map sharedFlowTypes = fcInfo.getSharedFlowTypes(); 173 174 if ( sharedFlowTypes != null ) 175 { 176 for ( Iterator ii = sharedFlowTypes.values().iterator(); ii.hasNext(); ) 177 { 178 TypeDeclaration sharedFlowType = ( TypeDeclaration ) ii.next(); 179 enableNavigateTo( sharedFlowType, new MergedControllerAnnotation( sharedFlowType ), fcInfo ); 184 } 185 } 186 187 endCheckClass( jclass ); 188 _checkResultMap.put( JpfLanguageConstants.ExtraInfoKeys.flowControllerInfo, fcInfo ); 189 return _checkResultMap; 190 } 191 192 private static void enableNavigateTo( TypeDeclaration flowControllerClass, MergedControllerAnnotation controllerAnn, 193 FlowControllerInfo fcInfo ) 194 { 195 enableNavigateTo( controllerAnn.getForwards(), fcInfo ); 199 enableNavigateTo( controllerAnn.getSimpleActions(), fcInfo ); 200 201 MethodDeclaration[] methods = CompilerUtils.getClassMethods( flowControllerClass, null ); 205 206 for ( int i = 0; i < methods.length; i++ ) 207 { 208 MethodDeclaration method = methods[i]; 209 AnnotationInstance ann = CompilerUtils.getAnnotation( method, ACTION_TAG_NAME ); 210 211 if ( ann != null ) 212 { 213 enableNavigateTo( CompilerUtils.getAnnotation( ann, VALIDATION_ERROR_FORWARD_ATTR, true ), fcInfo ); 214 } 215 216 if ( ann == null ) ann = CompilerUtils.getAnnotation( method, EXCEPTION_HANDLER_TAG_NAME ); 217 if ( ann != null ) enableNavigateTo( CompilerUtils.getAnnotationArray( ann, FORWARDS_ATTR, true ), fcInfo ); 218 } 219 } 220 221 private static void enableNavigateTo( Collection childAnnotations, FlowControllerInfo fcInfo ) 222 { 223 if ( childAnnotations != null ) 224 { 225 for ( Iterator ii = childAnnotations.iterator(); ii.hasNext(); ) 226 { 227 AnnotationInstance childAnnotation = ( AnnotationInstance ) ii.next(); 228 enableNavigateTo( childAnnotation, fcInfo ); 229 } 230 } 231 } 232 233 private static void enableNavigateTo( AnnotationInstance ann, FlowControllerInfo fcInfo ) 234 { 235 if ( ann == null ) return; 236 String val = CompilerUtils.getEnumFieldName( ann, NAVIGATE_TO_ATTR, true ); 237 238 if ( val != null ) 239 { 240 if ( val.equals( NAVIGATE_TO_CURRENT_PAGE_STR ) || val.equals( NAVIGATE_TO_PREVIOUS_PAGE_STR ) 241 || val.equals( NAVIGATE_TO_PAGE_LEGACY_STR ) ) 242 { 243 fcInfo.enableNavigateToPage(); 244 } 245 else if ( val.equals( NAVIGATE_TO_PREVIOUS_ACTION_STR ) ) 246 { 247 fcInfo.enableNavigateToAction(); 248 } 249 } 250 } 251 252 protected void endCheckClass( ClassDeclaration jclass ) 253 { 254 } 255 256 protected abstract GenStrutsApp createStrutsApp( ClassDeclaration jclass ) 257 throws XmlException, IOException , FatalCompileTimeException; 258 259 protected void startCheckClass( ClassDeclaration jclass ) 260 throws FatalCompileTimeException 261 { 262 GenStrutsApp strutsApp = null; 266 File strutsConfigFile = null; 267 268 try 272 { 273 strutsApp = createStrutsApp( jclass ); 274 strutsConfigFile = strutsApp.getStrutsConfigFile(); 275 } 276 catch ( XmlException e ) 277 { 278 } 280 catch ( IOException e ) 281 { 282 } 284 285 if ( strutsConfigFile != null ) 286 { 287 File parentDir = strutsConfigFile.getParentFile(); 288 289 getFCSourceFileInfo().addReferencedFile( strutsConfigFile ); 290 291 if ( ! parentDir.isDirectory() ) 292 { 293 if ( ! parentDir.mkdirs() && ! parentDir.isDirectory() ) 299 { 300 getDiagnostics().addError( jclass, "error.invalid-parent-directory", parentDir ); 301 } 302 } 303 304 if ( strutsConfigFile.exists() && strutsApp != null && ! strutsApp.canWrite() ) 305 { 306 getDiagnostics().addError( jclass, "error.struts-config-not-writable", strutsConfigFile ); 307 } 308 } 309 310 getRuntimeVersionChecker().checkRuntimeVersion( VERSION_8_SP2_STRING, jclass, getDiagnostics(), 311 "warning.runtime-version", new Object []{ PAGEFLOW_RUNTIME_JAR } ); 312 313 AnnotationInstance controllerAnnotation = CompilerUtils.getAnnotation( jclass, CONTROLLER_TAG_NAME ); 317 if ( controllerAnnotation != null ) _controllerGrammar.check( controllerAnnotation, null, jclass ); 318 319 checkInheritedRelativePaths( jclass ); 323 } 324 325 328 private void checkInheritedRelativePaths( ClassDeclaration jclass ) 329 throws FatalCompileTimeException 330 { 331 for ( ClassType type = jclass.getSuperclass(); 332 type != null && CompilerUtils.isAssignableFrom( FLOWCONTROLLER_BASE_CLASS, type, getEnv() ); 333 type = type.getSuperclass() ) 334 { 335 TypeDeclaration decl = CompilerUtils.getDeclaration( type ); 336 337 List simpleActions = 341 CompilerUtils.getAnnotationArrayValue( decl, CONTROLLER_TAG_NAME, SIMPLE_ACTIONS_ATTR, true ); 342 343 if ( simpleActions != null ) 344 { 345 for ( Iterator j = simpleActions.iterator(); j.hasNext(); ) 346 { 347 AnnotationInstance i = ( AnnotationInstance ) j.next(); 348 checkRelativePath( i, PATH_ATTR, jclass, decl, false ); 349 List conditionalForwards = CompilerUtils.getAnnotationArray( i, CONDITIONAL_FORWARDS_ATTR, true ); 350 351 if ( conditionalForwards != null ) 352 { 353 for ( Iterator k = conditionalForwards.iterator(); k.hasNext(); ) 354 { 355 AnnotationInstance ann = ( AnnotationInstance ) k.next(); 356 checkRelativePath( ann, PATH_ATTR, jclass, decl, false ); 357 } 358 } 359 } 360 } 361 362 List forwards = CompilerUtils.getAnnotationArrayValue( decl, CONTROLLER_TAG_NAME, FORWARDS_ATTR, true ); 366 367 if ( forwards != null ) 368 { 369 for ( Iterator ii = forwards.iterator(); ii.hasNext(); ) 370 { 371 AnnotationInstance i = ( AnnotationInstance ) ii.next(); 372 checkRelativePath( i, PATH_ATTR, jclass, decl, false ); 373 } 374 } 375 376 List catches = CompilerUtils.getAnnotationArrayValue( decl, CONTROLLER_TAG_NAME, CATCHES_ATTR, true ); 380 381 if ( catches != null ) 382 { 383 for ( Iterator j = catches.iterator(); j.hasNext(); ) 384 { 385 AnnotationInstance i = ( AnnotationInstance ) j.next(); 386 checkRelativePath( i, PATH_ATTR, jclass, decl, false ); 387 } 388 } 389 390 AnnotationInstance controllerAnnotation = CompilerUtils.getAnnotation( decl, CONTROLLER_TAG_NAME ); 394 395 if ( controllerAnnotation != null ) 396 { 397 checkRelativePath( controllerAnnotation, VALIDATOR_MERGE_ATTR, jclass, decl, true ); 398 checkRelativePath( controllerAnnotation, STRUTSMERGE_ATTR, jclass, decl, true ); 399 } 400 401 MethodDeclaration[] methods = decl.getMethods(); 405 for ( int i = 0; i < methods.length; i++ ) 406 { 407 MethodDeclaration method = methods[i]; 408 AnnotationInstance ann = CompilerUtils.getAnnotation( method, ACTION_TAG_NAME); 409 if ( ann == null ) ann = CompilerUtils.getAnnotation( method, EXCEPTION_HANDLER_TAG_NAME ); 410 411 if ( ann != null ) 412 { 413 List methodForwards = CompilerUtils.getAnnotationArray( ann, FORWARDS_ATTR, true ); 414 String methodName = method.getSimpleName(); 415 416 if ( methodForwards != null ) 417 { 418 for ( Iterator j = methodForwards.iterator(); j.hasNext(); ) 419 { 420 AnnotationInstance methodForward = ( AnnotationInstance ) j.next(); 421 checkRelativePath( methodName, methodForward, PATH_ATTR, jclass, decl, false ); 422 } 423 } 424 425 List methodCatches = CompilerUtils.getAnnotationArray( ann, CATCHES_ATTR, true ); 426 427 if ( methodCatches != null ) 428 { 429 for ( Iterator j = methodCatches.iterator(); j.hasNext(); ) 430 { 431 AnnotationInstance methodCatch = ( AnnotationInstance ) j.next(); 432 checkRelativePath( methodName, methodCatch, PATH_ATTR, jclass, decl, false ); 433 } 434 } 435 } 436 } 437 } 438 } 439 440 private void checkRelativePath( AnnotationInstance ann, String memberName, TypeDeclaration jclass, 441 TypeDeclaration baseType, boolean isError ) 442 throws FatalCompileTimeException 443 { 444 if ( ann != null ) 445 { 446 AnnotationValue pathVal = CompilerUtils.getAnnotationValue( ann, memberName, true ); 447 448 if ( pathVal != null ) 449 { 450 String path = ( String ) pathVal.getValue(); 451 452 if ( path.charAt( 0 ) != '/' && ! WebappPathType.relativePathExists( path, jclass, getEnv() ) ) 453 { 454 String [] args = { 455 path, 456 ANNOTATION_INTERFACE_PREFIX + ann.getAnnotationType().getDeclaration().getSimpleName(), 457 baseType.getQualifiedName() 458 }; 459 460 if ( isError ) 461 { 462 getDiagnostics().addErrorArrayArgs( ann, "message.inherited-file-not-found", args ); 463 } 464 else 465 { 466 getDiagnostics().addWarningArrayArgs( ann, "message.inherited-file-not-found", args ); 467 } 468 } 469 } 470 } 471 } 472 473 private void checkRelativePath( String methodName, AnnotationInstance ann, String memberName, 474 TypeDeclaration jclass, TypeDeclaration baseType, boolean isError ) 475 throws FatalCompileTimeException 476 { 477 if ( ann != null ) 478 { 479 AnnotationValue pathVal = CompilerUtils.getAnnotationValue( ann, memberName, true ); 480 481 if ( pathVal != null ) 482 { 483 String path = ( String ) pathVal.getValue(); 484 485 if ( path.charAt( 0 ) != '/' && ! WebappPathType.relativePathExists( path, jclass, getEnv() ) ) 486 { 487 String [] args = { 488 path, 489 ANNOTATION_INTERFACE_PREFIX + ann.getAnnotationType().getDeclaration().getSimpleName(), 490 methodName, 491 baseType.getQualifiedName() 492 }; 493 494 if ( isError ) 495 { 496 getDiagnostics().addErrorArrayArgs( jclass, "message.method-inherited-file-not-found", args ); 497 } 498 else 499 { 500 getDiagnostics().addWarningArrayArgs( jclass, "message.method-inherited-file-not-found", args ); 501 } 502 } 503 } 504 } 505 } 506 507 508 509 protected void checkField( FieldDeclaration field, TypeDeclaration jclass ) 510 { 511 if ( CompilerUtils.typesAreEqual( jclass, field.getDeclaringType() ) ) 515 { 516 TypeInstance type = field.getType(); 517 518 if ( ! field.hasModifier( Modifier.TRANSIENT ) && ! field.hasModifier( Modifier.STATIC ) 519 && type instanceof ClassType 520 && ! CompilerUtils.isAssignableFrom( SERIALIZABLE_CLASS_NAME, type, getEnv() ) ) 521 { 522 getDiagnostics().addWarning( field, "warning.nonserializable-member-data" ); 523 } 524 } 525 } 526 527 protected void checkMethod( MethodDeclaration method, ClassDeclaration jclass ) 528 throws FatalCompileTimeException 529 { 530 AnnotationInstance[] annotations = method.getAnnotationInstances(); 531 532 for ( int i = 0; i < annotations.length; i++ ) 533 { 534 AnnotationInstance annotation = annotations[i]; 535 String annotationName = CompilerUtils.getDeclaration( annotation.getAnnotationType() ).getSimpleName(); 536 537 if ( annotationName.equals( ACTION_TAG_NAME ) ) 538 { 539 _actionGrammar.check( annotation, null, method ); 540 541 if ( ! CompilerUtils.isAssignableFrom( FORWARD_CLASS_NAME, method.getReturnType(), getEnv() ) ) 542 { 543 getDiagnostics().addError( method, "error.method-wrong-return-type", FORWARD_CLASS_NAME ); 544 } 545 } 546 else if ( annotationName.equals( EXCEPTION_HANDLER_TAG_NAME ) ) 547 { 548 _exceptionHandlerGrammar.check( annotation, null, method ); 549 checkExceptionHandlerMethod( method ); 550 } 551 } 552 } 553 554 protected void checkInnerClass( ClassDeclaration innerClass ) 555 throws FatalCompileTimeException 556 { 557 _formBeanChecker.check( innerClass ); 558 } 559 560 private void checkExceptionHandlerMethod( MethodDeclaration method ) 561 { 562 if ( ! CompilerUtils.isAssignableFrom( FORWARD_CLASS_NAME, method.getReturnType(), getEnv() ) ) 563 { 564 getDiagnostics().addError( method, "error.method-wrong-return-type", FORWARD_CLASS_NAME ); 565 } 566 567 ParameterDeclaration[] parameters = method.getParameters(); 568 569 if ( parameters.length == 4 ) 570 { 571 if ( ! CompilerUtils.isAssignableFrom( THROWABLE_CLASS_NAME, parameters[0].getType(), getEnv() ) ) 572 { 573 getDiagnostics().addError( method, "error.exception-method-wrong-exception-arg", THROWABLE_CLASS_NAME ); 574 } 575 576 checkExceptionHandlerArgType( method, parameters, 1, STRING_CLASS_NAME ); 577 checkExceptionHandlerArgType( method, parameters, 2, STRING_CLASS_NAME ); 578 579 if ( CompilerUtils.isAssignableFrom( STRUTS_FORM_CLASS_NAME, parameters[3].getType(), getEnv() ) ) 584 { 585 getDiagnostics().addWarning( method, "warning.exception-method-deprecated-form-arg" ); 586 } 587 else 588 { 589 checkExceptionHandlerArgType( method, parameters, 3, OBJECT_CLASS_NAME ); 590 } 591 } 592 else 593 { 594 getDiagnostics().addError( method, "error.exception-method-wrong-arg-count", new Integer ( 4 ) ); 595 } 596 } 597 598 private void checkExceptionHandlerArgType( MethodDeclaration method, ParameterDeclaration[] parameters, 599 int index, String className ) 600 { 601 if ( ! CompilerUtils.isOfClass( parameters[ index ].getType(), className, getEnv() ) ) 602 { 603 getDiagnostics().addError( method, "error.exception-method-wrong-arg-type", new Integer ( index + 1 ), 604 className ); 605 } 606 } 607 608 protected void checkForOverlappingClasses( ClassDeclaration jpfClass, String baseClass, String fileExtension, 609 String errorKey ) 610 { 611 File jpfFile = CompilerUtils.getSourceFile( jpfClass, true ); 612 File parentDir = jpfFile.getParentFile(); 613 PackageDeclaration pkg = jpfClass.getPackage(); 614 ClassDeclaration[] packageClasses = pkg.getClasses(); 615 Set overlapping = new HashSet (); 616 List overlappingFiles = new ArrayList (); 617 618 for ( int i = 0; i < packageClasses.length; i++ ) 623 { 624 ClassDeclaration classDecl = packageClasses[i]; 625 if ( CompilerUtils.getAnnotation( classDecl, CONTROLLER_TAG_NAME ) != null 626 && CompilerUtils.isAssignableFrom( baseClass, classDecl, getEnv() ) ) 627 { 628 File file = CompilerUtils.getSourceFile( classDecl, false ); 629 630 if ( ! jpfFile.equals( file ) && file != null && file.exists() ) 635 { 636 overlapping.add( file.getName() ); 637 overlappingFiles.add( file ); 638 } 639 } 640 } 641 642 File [] peers = parentDir.listFiles( new ExtensionFileFilter( fileExtension ) ); 649 650 if ( peers != null ) { 652 for ( int i = 0; i < peers.length; i++ ) 653 { 654 File peer = peers[i]; 655 if ( ! peer.equals( jpfFile ) ) 656 { 657 String name = peer.getName(); 658 659 if ( ! overlapping.contains( name ) ) 660 { 661 overlapping.add( name ); 662 overlappingFiles.add( peer ); 663 } 664 } 665 } 666 } 667 668 int len = overlapping.size(); 669 if ( len > 0 ) 670 { 671 if ( len > 3 ) 672 { 673 getDiagnostics().addErrorArrayArgs( jpfClass, errorKey, overlapping.toArray() ); 674 } 675 else 676 { 677 getDiagnostics().addErrorArrayArgs( jpfClass, errorKey + len, overlapping.toArray() ); 678 } 679 } 680 681 getCheckResultMap().put( JpfLanguageConstants.ExtraInfoKeys.overlappingPageFlowFiles, overlappingFiles ); 682 } 683 684 private static class ExtensionFileFilter implements FilenameFilter 685 { 686 private String _extension; 687 688 public ExtensionFileFilter( String extension ) 689 { 690 _extension = extension; 691 } 692 693 public boolean accept( File dir, String name ) 694 { 695 return name.endsWith( _extension ); 696 } 697 } 698 699 protected FlowControllerInfo getFCSourceFileInfo() 700 { 701 return ( FlowControllerInfo ) super.getSourceFileInfo(); 702 } 703 } 704 | Popular Tags |