JdbcMapper/beehive-netui-compiler/src/main/java/org/apache/beehive/netui/compiler/PageFlowChecker.java

223 lines
9.7 KiB
Java

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Header:$
*/
package org.apache.beehive.netui.compiler;
import org.apache.beehive.netui.compiler.genmodel.GenStrutsApp;
import org.apache.beehive.netui.compiler.grammar.ControllerGrammar;
import org.apache.beehive.netui.compiler.grammar.WebappPathOrActionType;
import org.apache.beehive.netui.compiler.typesystem.declaration.AnnotationInstance;
import org.apache.beehive.netui.compiler.typesystem.declaration.ClassDeclaration;
import org.apache.beehive.netui.compiler.typesystem.declaration.FieldDeclaration;
import org.apache.beehive.netui.compiler.typesystem.declaration.Modifier;
import org.apache.beehive.netui.compiler.typesystem.declaration.PackageDeclaration;
import org.apache.beehive.netui.compiler.typesystem.declaration.TypeDeclaration;
import org.apache.beehive.netui.compiler.typesystem.env.CoreAnnotationProcessorEnv;
import org.apache.beehive.netui.compiler.typesystem.type.DeclaredType;
import org.apache.beehive.netui.compiler.typesystem.type.TypeInstance;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
public class PageFlowChecker
extends FlowControllerChecker
implements JpfLanguageConstants
{
public PageFlowChecker( CoreAnnotationProcessorEnv env, Diagnostics diagnostics, FlowControllerInfo fcInfo )
{
super( env, fcInfo, diagnostics );
}
protected void checkField( FieldDeclaration field, TypeDeclaration jclass )
{
//
// Check to make sure that if this is a Shared Flow field, its type matches up with the type declared
// for the shared flow of that name.
//
AnnotationInstance sfFieldAnn = CompilerUtils.getAnnotation( field, SHARED_FLOW_FIELD_TAG_NAME );
if ( sfFieldAnn != null )
{
String sharedFlowName = CompilerUtils.getString( sfFieldAnn, NAME_ATTR, true );
// sharedFlow name is a required attribute, if not defined return and let apt error.
if (sharedFlowName == null) {
return;
}
Collection sharedFlowRefs =
getFCSourceFileInfo().getMergedControllerAnnotation().getSharedFlowRefs();
boolean foundOne = false;
if ( sharedFlowRefs != null )
{
for ( Iterator ii = sharedFlowRefs.iterator(); ii.hasNext(); )
{
AnnotationInstance sharedFlowRef = ( AnnotationInstance ) ii.next();
if ( sharedFlowName.equals( CompilerUtils.getString( sharedFlowRef, NAME_ATTR, true ) ) )
{
foundOne = true;
TypeInstance sfType = CompilerUtils.getTypeInstance( sharedFlowRef, TYPE_ATTR, true );
TypeInstance ft = field.getType();
if ( ! ( sfType instanceof DeclaredType )
|| ! CompilerUtils.isAssignableFrom( ft, ( ( DeclaredType ) sfType ).getDeclaration() ) )
{
getDiagnostics().addError(
field, "error.field-not-assignable",
CompilerUtils.getDeclaration( ( DeclaredType ) sfType ).getQualifiedName() );
}
}
}
}
if ( ! foundOne )
{
getDiagnostics().addError( sfFieldAnn, "error.no-matching-shared-flow-declared",
SHARED_FLOW_REF_TAG_NAME, sharedFlowName );
}
}
else if ( CompilerUtils.isAssignableFrom( SHARED_FLOW_BASE_CLASS, field.getType(), getEnv() )
&& ! CompilerUtils.isAssignableFrom( GLOBALAPP_BASE_CLASS, field.getType(), getEnv() ) )
{
// Output a warning if the field type extends SharedFlowController but there's no @Jpf.SharedFlowField
// annotation (in which case the field won't get auto-initialized at runtime.
getDiagnostics().addWarning( field, "warning.shared-flow-field-no-annotation",
field.getSimpleName(), SHARED_FLOW_BASE_CLASS,
ANNOTATION_INTERFACE_PREFIX + SHARED_FLOW_FIELD_TAG_NAME );
}
super.checkField( field, jclass );
}
protected void doAdditionalClassChecks( ClassDeclaration jpfClass )
{
// Make sure there are no other page flows in this package/directory.
checkForOverlappingClasses( jpfClass, JPF_BASE_CLASS, JPF_FILE_EXTENSION_DOT, "error.overlapping-pageflows" );
PackageDeclaration pkg = jpfClass.getPackage();
File jpfFile = CompilerUtils.getSourceFile( jpfClass, true );
File parentDir = jpfFile.getParentFile();
//
// Check the package name.
//
String jpfPackageName = pkg.getQualifiedName();
if ( jpfPackageName != null && jpfPackageName.length() > 0 )
{
String expectedPackage = parentDir.getAbsolutePath().replace( '\\', '/' ).replace( '/', '.' );
if ( ! expectedPackage.endsWith( jpfPackageName ) )
{
getDiagnostics().addError( jpfClass, "error.wrong-package-for-directory", parentDir.getPath() );
}
}
//
// Issue a warning if the class name is the same as the parent package name.
// This causes ambiguity when resolving inner classes.
//
if ( jpfClass.getSimpleName().equals( pkg.getQualifiedName() ) )
{
getDiagnostics().addWarning( jpfClass, "warning.classname-same-as-package" );
}
//
// Make sure every .jpf has a begin action if the class isn't abstract.
//
boolean isAbstract = jpfClass.hasModifier( Modifier.ABSTRACT );
FlowControllerInfo fcInfo = getFCSourceFileInfo();
if ( ! WebappPathOrActionType.actionExists( BEGIN_ACTION_NAME, jpfClass, null, getEnv(), fcInfo, true )
&& ! isAbstract )
{
getDiagnostics().addError( jpfClass, "error.no-begin-action" );
}
//
// Make sure every nested pageflow has a returnAction. Return actions are added by ForwardGrammar, but
// here we also need to add them for inherited Forwards and SimpleActions.
//
if ( fcInfo.isNested() )
{
MergedControllerAnnotation mca = fcInfo.getMergedControllerAnnotation();
addReturnActions( mca.getSimpleActions(), fcInfo, jpfClass, CONDITIONAL_FORWARDS_ATTR );
addReturnActions( mca.getForwards(), fcInfo, jpfClass, null );
if ( ! isAbstract && fcInfo.countReturnActions() == 0 )
{
getDiagnostics().addError( jpfClass, "error.no-return-action",
ANNOTATION_INTERFACE_PREFIX + FORWARD_TAG_NAME,
RETURN_ACTION_ATTR );
}
}
}
private void addReturnActions( Collection forwardAnnotations, FlowControllerInfo fcInfo,
TypeDeclaration outerType, String childArrayAttr )
{
for ( Iterator ii = forwardAnnotations.iterator(); ii.hasNext(); )
{
AnnotationInstance ann = ( AnnotationInstance ) ii.next();
String returnAction = CompilerUtils.getString( ann, RETURN_ACTION_ATTR, true );
if ( returnAction != null ) fcInfo.addReturnAction( returnAction, ann, outerType );
if ( childArrayAttr != null )
{
Collection children = CompilerUtils.getAnnotationArray( ann, childArrayAttr, true );
if ( children != null ) addReturnActions( children, fcInfo, outerType, null );
}
}
}
protected String getDesiredBaseClass( ClassDeclaration jclass )
{
return JPF_BASE_CLASS;
}
protected GenStrutsApp createStrutsApp( ClassDeclaration jclass )
throws IOException, FatalCompileTimeException
{
File sourceFile = CompilerUtils.getSourceFile( jclass, true );
return new GenStrutsApp( sourceFile, jclass, getEnv(), getFCSourceFileInfo(), true, getDiagnostics() );
}
protected AnnotationGrammar getControllerGrammar()
{
return new JpfControllerGrammar();
}
private class JpfControllerGrammar
extends ControllerGrammar
{
public JpfControllerGrammar()
{
super( PageFlowChecker.this.getEnv(), PageFlowChecker.this.getDiagnostics(),
PageFlowChecker.this.getRuntimeVersionChecker(), PageFlowChecker.this.getFCSourceFileInfo() );
addMemberType( NESTED_ATTR, new AnnotationMemberType( null, this ) );
addMemberType( LONGLIVED_ATTR, new AnnotationMemberType( null, this ) );
}
}
}