Package org.apache.beehive.netui.compiler.genmodel

Source Code of org.apache.beehive.netui.compiler.genmodel.GenStrutsApp

/*
* Copyright 2004 The Apache Software Foundation.
*
* Licensed 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.genmodel;

import org.apache.beehive.netui.compiler.CompilerUtils;
import org.apache.beehive.netui.compiler.FlowControllerInfo;
import org.apache.beehive.netui.compiler.JpfLanguageConstants;
import org.apache.beehive.netui.compiler.MergedControllerAnnotation;
import org.apache.beehive.netui.compiler.Diagnostics;
import org.apache.beehive.netui.compiler.FatalCompileTimeException;
import org.apache.beehive.netui.compiler.model.ActionModel;
import org.apache.beehive.netui.compiler.model.FormBeanModel;
import org.apache.beehive.netui.compiler.model.ForwardModel;
import org.apache.beehive.netui.compiler.model.MessageResourcesModel;
import org.apache.beehive.netui.compiler.model.StrutsApp;
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.MethodDeclaration;
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.ParameterDeclaration;
import org.apache.beehive.netui.compiler.typesystem.declaration.TypeDeclaration;
import org.apache.beehive.netui.compiler.typesystem.env.AnnotationProcessorEnvironment;
import org.apache.beehive.netui.compiler.typesystem.type.DeclaredType;
import org.apache.beehive.netui.compiler.typesystem.type.TypeInstance;
import org.apache.beehive.netui.compiler.typesystem.type.ClassType;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;


public class GenStrutsApp
        extends StrutsApp
        implements JpfLanguageConstants
{
    private ClassDeclaration _jclass;
    private String _containingPackage;
    private File _strutsConfigFile;
    private File _sourceFile;
    private AnnotationProcessorEnvironment _env;
    private FlowControllerInfo _fcInfo;
    private Diagnostics _diagnostics;
   
    protected void recalculateStrutsConfigFile()
        throws XmlException, IOException, FatalCompileTimeException
    {
        _strutsConfigFile = calculateStrutsConfigFile(); // caching this
    }

    FlowControllerInfo getFlowControllerInfo()
    {
        return _fcInfo;
    }

    public GenStrutsApp( File sourceFile, ClassDeclaration jclass, AnnotationProcessorEnvironment env,
                         FlowControllerInfo fcInfo, boolean checkOnly, Diagnostics diagnostics )
        throws XmlException, IOException, FatalCompileTimeException
    {
        super( jclass.getQualifiedName() );
       
        _jclass = jclass;
        _containingPackage = jclass.getPackage().getQualifiedName();
        _sourceFile = sourceFile;
        _env = env;
        assert fcInfo != null;
        _fcInfo = fcInfo;
        _diagnostics = diagnostics;
       
        recalculateStrutsConfigFile();
       
        if ( checkOnly ) return;
       
        if ( _jclass != null )
        {
            MergedControllerAnnotation mca = fcInfo.getMergedControllerAnnotation();
            setNestedPageFlow( mca.isNested() );
            setLongLivedPageFlow( mca.isLongLived() );
            addMessageResources( mca.getMessageResources() );   // messageResources is deprecated
            addMessageBundles( mca.getMessageBundles() );   // messageBundles is not
            addSimpleActions( mca.getSimpleActions() );
            setMultipartHandler( mca.getMultipartHandler() );
            GenForwardModel.addForwards( mca.getForwards(), this, _jclass, this, null );
           
            // TODO: comment
            addForward( new ForwardModel( "_auto", "", this ) );
           
            GenExceptionModel.addCatches( mca.getCatches(), this, _jclass, this, this );
            addTilesDefinitionsConfigs( mca.getTilesDefinitionsConfigs() );
            setAdditionalValidatorConfigs( mca.getCustomValidatorConfigs() );

            addActionMethods();
            addFormBeans( _jclass );
        }
       
        if ( fcInfo != null )
        {
            setSharedFlows( fcInfo.getSharedFlowTypeNames() );
            setReturnToActionDisabled( ! fcInfo.isNavigateToActionEnabled() );
            setReturnToPageDisabled( ! fcInfo.isNavigateToPageEnabled() );
        }
    }
   
    private void addFormBeans( ClassDeclaration jclass )
    {
        Collection innerTypes = CompilerUtils.getClassNestedTypes( jclass );
       
        for ( Iterator ii = innerTypes.iterator(); ii.hasNext();
        {
            TypeDeclaration innerType = ( TypeDeclaration ) ii.next();
            if ( innerType instanceof ClassDeclaration )
            {
                ClassDeclaration innerClass = ( ClassDeclaration ) innerType;
               
                if ( innerType.hasModifier( Modifier.PUBLIC )
                     && CompilerUtils.isAssignableFrom( PAGEFLOW_FORM_CLASS_NAME, innerClass, _env ) )
                {
                    addFormBean( innerClass, null );
                }
            }
        }
       
    }   
   
    String addFormBean( TypeDeclaration formType, ActionModel usedByAction )
    {
        String formClass = CompilerUtils.getFormClassName( formType, _env );

        //
        // Use the actual type of form to create the name.
        // This avoids conflicts if there are multiple forms using the
        // ANY_FORM_CLASS_NAME type.
        //
        String actualType = CompilerUtils.getLoadableName( formType );

        //
        // See if the app already has a form-bean of this type.  If so,
        // we'll just use it; otherwise, we need to create it.
        //
        boolean usesPageFlowScopedFormBean = usedByAction != null ? usedByAction.getFormMember() != null : false;
        List existingBeans = getFormBeansByActualType( actualType, Boolean.valueOf( usesPageFlowScopedFormBean ) );
        String formBeanName;

        if ( existingBeans != null )
        {
            assert existingBeans.size() > 0;
            formBeanName = ( ( FormBeanModel ) existingBeans.get( 0 ) ).getName();
        }
        else
        {
            formBeanName = getFormNameForType( actualType, usesPageFlowScopedFormBean );
            addFormBean( new FormBeanModel( formBeanName, formClass, actualType, usesPageFlowScopedFormBean, this ) );
            getMessageResourcesFromForm( formType, usedByAction );
        }
       
        return formBeanName;
    }
   
    private void addMessageResources( Collection messageResources )
    {
        if ( messageResources != null )
        {
            for ( Iterator ii = messageResources.iterator(); ii.hasNext();
            {
                AnnotationInstance ann = ( AnnotationInstance ) ii.next();
                addMessageResources( new GenMessageBundleModel( this, ann ) );
            }
        }
    }
   
    private void addMessageBundles( Collection messageBundles )
    {
        if ( messageBundles != null )
        {
            for ( Iterator ii = messageBundles.iterator(); ii.hasNext();
            {
                AnnotationInstance ann = ( AnnotationInstance ) ii.next();
                addMessageResources( new GenMessageBundleModel( this, ann ) );
            }
        }
    }
   
    private void addSimpleActions( Collection simpleActionAnnotations )
    {
        if ( simpleActionAnnotations != null )
        {
            for ( Iterator ii = simpleActionAnnotations.iterator(); ii.hasNext();
            {
                AnnotationInstance ann = ( AnnotationInstance ) ii.next();
                addActionMapping( new GenSimpleActionModel( ann, this, _jclass ) );
            }
        }
    }
   
    private void setMultipartHandler( String mpHandler )
    {
        if ( mpHandler != null )
        {
            if ( mpHandler.equals( MULTIPART_HANDLER_MEMORY_STR ) )
            {
                setMultipartHandlerClassName( MULTIPART_HANDLER_MEMORY_CLASSNAME );
            }
            else if ( mpHandler.equals( MULTIPART_HANDLER_DISK_STR ) )
            {
                setMultipartHandlerClassName( MULTIPART_HANDLER_DISK_CLASSNAME );
            }
            else
            {
                assert mpHandler.equals( MULTIPART_HANDLER_DISABLED_STR );
                setMultipartHandlerClassName( "none" );
            }
        }
    }
   
    private void addTilesDefinitionsConfigs( List tilesDefinitionsConfigs )
    {
        if ( tilesDefinitionsConfigs == null || tilesDefinitionsConfigs.isEmpty() )
        {
            return;
        }

        List paths = new ArrayList();

        for ( Iterator ii = tilesDefinitionsConfigs.iterator(); ii.hasNext();
        {
            String definitionsConfig = ( String ) ii.next();

            if ( definitionsConfig != null && definitionsConfig.length() > 0 )
            {
                paths.add( definitionsConfig );
            }
        }

        setTilesDefinitionsConfigs( paths );
    }

    private void addActionMethods()
    {
        MethodDeclaration[] actionMethods = CompilerUtils.getClassMethods( _jclass, ACTION_TAG_NAME );
       
        for ( int i = 0; i < actionMethods.length; i++ )
        {
            MethodDeclaration actionMethod = actionMethods[i];
           
            if ( ! actionMethod.hasModifier( Modifier.ABSTRACT ) )
            {
                ActionModel actionModel = new GenActionModel( actionMethod, this, _jclass );
                addActionMapping( actionModel );
                ParameterDeclaration[] params = actionMethod.getParameters();
               
                if ( params.length > 0 )
                {
                    ParameterDeclaration param1 = params[0];
                    TypeInstance paramType = param1.getType();
                   
                    if ( paramType instanceof DeclaredType )
                    {
                        getMessageResourcesFromForm( CompilerUtils.getDeclaration( ( DeclaredType ) paramType ), actionModel );
                    }
                }
            }
        }
    }
   
    private void getMessageResourcesFromForm( TypeDeclaration formTypeDecl, ActionModel actionModel )
    {
        if ( ! ( formTypeDecl instanceof ClassDeclaration ) ) return;
       
        ClassDeclaration formClassDecl = ( ClassDeclaration ) formTypeDecl;
       
        while ( true )
        {
            AnnotationInstance ann = CompilerUtils.getAnnotation( formClassDecl, FORM_BEAN_TAG_NAME );
           
            if ( ann != null )
            {
                String defaultMessageResources = CompilerUtils.getString( ann, MESSAGE_BUNDLE_ATTR, true );
               
                if ( defaultMessageResources != null )
                {
                    for ( Iterator ii = getMessageResourcesList().iterator(); ii.hasNext();
                    {
                        MessageResourcesModel i = ( MessageResourcesModel ) ii.next();
                        if ( i.getParameter().equals( defaultMessageResources ) ) return;
                    }
                   
                    MessageResourcesModel mrm = new MessageResourcesModel( this );
                    String key = "formMessages:" + CompilerUtils.getLoadableName( formClassDecl );
                    mrm.setKey( key );
                    mrm.setParameter( defaultMessageResources );
                    mrm.setReturnNull( true );
                    addMessageResources( mrm );
                    if ( actionModel != null ) actionModel.setFormBeanMessageResourcesKey( key );
                }
            }
           
            ClassType superType = formClassDecl.getSuperclass();
            if ( superType == null ) break;
            formClassDecl = superType.getClassTypeDeclaration();
        }
    }
   
    protected String getMergeFileName()
    {
        return getFlowControllerInfo().getMergedControllerAnnotation().getStrutsMerge();
    }
   
    public void writeToFile()
        throws FileNotFoundException, IOException, XmlException, FatalCompileTimeException
    {
        writeToFile( getMergeFile( getMergeFileName() ) );
    }
   
    public boolean isStale()
            throws FatalCompileTimeException
    {
        return isStale( getMergeFile( getMergeFileName() ) );
    }
   
    protected boolean isModuleDeclaredInWebXml()
    {
        // Only the root page flow (which generates a module for path "/") is declared in web.xml
        PackageDeclaration pkg = _jclass.getPackage();
        return ! isSharedFlow() && pkg == null || pkg.getQualifiedName().length() == 0;
    }
   
    String getOutputFileURI( String filePrefix )
    {
        return getOutputFileURI( filePrefix, _containingPackage, false );
    }
   
    String getStrutsConfigURI()
    {
        return getStrutsConfigURI( _containingPackage, false );
    }

    protected String getContainingPackage()
    {
        return _containingPackage;
    }
   
    private File calculateStrutsConfigFile()
        throws XmlException, IOException, FatalCompileTimeException
    {
        String webappBuildRoot = CompilerUtils.getWebBuildRoot( getEnv() );
        File strutsConfigFile = new File( webappBuildRoot + getStrutsConfigURI() );
       
        //
        // For the root Controller.jpf and for Global.app, we have to look in web.xml to get the output location.
        // See the comment on getAlternateLocation for a rationale...
        //
        if ( isModuleDeclaredInWebXml() )
        {
            String alternateLocation = getAlternateLocation( strutsConfigFile );
            if ( alternateLocation != null ) return new File( webappBuildRoot + alternateLocation );
        }
       
        return strutsConfigFile;
    }
   
    /**
     * Tell whether the struts output file (jpf-struts-config-*.xml) is out of date, based on the
     * file times of the source file and the (optional) struts-merge file.
     */
    public boolean isStale( File mergeFile )
    {
        //
        // We always write the root-level JPF and Global.app, because the struts XML
        // config files for these modules are provided by default, and may be out of
        // date, even if the file modification times don't indicate that this is true.
        //
        if ( isModuleDeclaredInWebXml() )
        {
            return true;
        }
       
        //
        // We can write to the file if it doesn't exist yet.
        //
        if ( ! _strutsConfigFile.exists() )
        {
            return true;
        }
       
        long lastWrite = _strutsConfigFile.lastModified();
       
        if ( mergeFile != null && mergeFile.exists() && mergeFile.lastModified() > lastWrite )
        {
            return true;
        }
       
        if ( _sourceFile.lastModified() > lastWrite )
        {
            return true;
        }
       
        return false;
    }
    /**
     * In some cases, canWrite() does not guarantee that a FileNotFoundException will not
     * be thrown when trying to write to a file.  This method actually tries to overwrite
     * the file as a test to see whether it's possible.
     */
    public boolean canWrite()
    {
        if ( ! _strutsConfigFile.canWrite() )
        {
            return false;
        }
       
        try
        {
            //
            // This appears to be the only way to predict whether the file can actually be
            // written to; it may be that canWrite() returns true, but the file permissions
            // (NTFS only?) will cause an exception to be thrown.
            //
            new FileOutputStream( _strutsConfigFile, true ).close();
        }
        catch ( FileNotFoundException e )
        {
            return false;
        }
        catch ( IOException e )
        {
            return false;
        }
       
        return true;
    }
       
   
    public void writeToFile( File strutsMergeFile )
        throws FileNotFoundException, IOException, XmlException, FatalCompileTimeException
    {
        _strutsConfigFile.getParentFile().mkdirs();
        PrintStream out = new PrintStream( new FileOutputStream( _strutsConfigFile ) );
        writeXml( out, strutsMergeFile, CompilerUtils.getWebBuildRoot( getEnv() ) );
        out.close();
    }   
   
    public File getStrutsConfigFile()
    {
        return _strutsConfigFile;
    }
   
    private static boolean isAtElement( XmlCursor curs, String localName )
    {
        return curs.getName().getLocalPart().equals( localName );
    }
   
    /**
     * Two special files, the module configs for the root module and "-global", are registered in
     * web.xml explicitly.  If the user is pointing to an alternate (e.g., old) location for these
     * files, we need to compile to that location.
     */
    private String getAlternateLocation( File strutsConfigFile )
        throws XmlException, IOException, FatalCompileTimeException
    {
        String webappContentRoot = CompilerUtils.getWebContentRoot( getEnv() );
        File webXmlFile = new File( webappContentRoot + '/' + StrutsApp.WEBINF_DIR_NAME + "/web.xml" );
       
        if ( ! webXmlFile.canRead() )
        {
            _diagnostics.addWarning( _jclass, "warning.could-not-read-web-xml", webappContentRoot );
            return null;
        }
       
        String strutsConfigFileName = strutsConfigFile.getName();
       
        //
        // We're going to parse web.xml in a "loose" way, so we can accept both the Servlet 2.3 and Servlet 2.4 versions
        // (and beyond) of the schema.  We're looking for this fragment:
        //   
        //        <servlet-name>action</servlet-name>
        //            <init-param>
        //                <param-name>config</param-name>
        //                <param-value>/WEB-INF/.pageflow-struts-generated/jpf-struts-config.xml</param-value>
        //            </init-param>
        //            ...
        //
        // If this refers to the basename of our generated struts-config file, we'll use it to determine (override) the
        // location of the output file.
        //
        XmlObject webXmlDoc = XmlObject.Factory.parse( webXmlFile );
        XmlCursor curs = webXmlDoc.newCursor();
        if ( curs.toFirstChild() && curs.toFirstChild() )
        {
            do
            {
                if ( isAtElement( curs, "servlet" ) )
                {
                    XmlCursor i = curs.newCursor();
                    i.toFirstChild();
                    do
                    {
                        if ( isAtElement( i, "servlet-name" ) && i.getTextValue().equals( "action" ) )
                        {
                            XmlCursor j = curs.newCursor();
                            j.toFirstChild();
                           
                            do
                            {
                                if ( isAtElement( j, "init-param" ) )
                                {
                                    XmlCursor k = j.newCursor();
                                    k.toFirstChild();
                                    boolean isConfig = false;
                                    String alternateLocation = null;
                                   
                                    do
                                    {
                                        if ( isAtElement( k, "param-name" ) && k.getTextValue().startsWith( "config" ) )
                                        {
                                            isConfig = true;
                                        }
                                        else if ( isAtElement( k, "param-value" ) )
                                        {
                                            alternateLocation =
                                                parseAlternateLocation( k.getTextValue(), strutsConfigFileName );
                                        }
                                    } while ( k.toNextSibling() );
                                   
                                    if ( isConfig && alternateLocation != null )
                                    {
                                        return alternateLocation;
                                    }
                                }
                            } while ( j.toNextSibling() );
                           
                            //
                            // We found the action servlet, but no init-param gave an alternate location for our
                            // struts-config output file.
                            //
                            return null;
                        }
                    } while ( i.toNextSibling() );
                }
            } while ( curs.toNextSibling() );
        }
       
        return null;
    }
   
    private static String parseAlternateLocation( String paramValue, String strutsConfigFileName )
    {
        //
        // If the referenced struts-config file has the same name as the file
        // we're going to generate, use the referenced file (its location may be
        // different than our default location).
        //
        if ( paramValue.indexOf( strutsConfigFileName ) != -1 )
        {
            //
            // This may be a comma-separated list of files.  Find the right one.
            //
            if ( paramValue.indexOf( "," ) != -1 )
            {
                String[] files = paramValue.split( "," );
                for ( int k = 0; k < files.length; ++k )
                {
                    if ( files[k].indexOf( strutsConfigFileName ) != -1 )
                    {
                        return files[k].trim();
                    }
                }
            }
            else
            {
                return paramValue;
            }
        }
       
        return null;
    }
   
    public File getMergeFile( String mergeFileName )
        throws FatalCompileTimeException
    {
        if ( mergeFileName != null )
        {
            return CompilerUtils.getFileRelativeToSourceFile( _jclass, mergeFileName, getEnv() );
        }
       
        return null;
    }

    protected String getHeaderComment( File mergeFile )
        throws FatalCompileTimeException
    {
        StringBuffer comment = new StringBuffer( " Generated from " );
        comment.append( getWebappRelativePath( _sourceFile ) );
        if ( mergeFile != null )
        {
            comment.append( " and " ).append( getWebappRelativePath( mergeFile ) );
        }
        comment.append( " on " ).append( new Date().toString() ).append( ' ' );
        return comment.toString();
    }
   
    private String getWebappRelativePath( File file )
        throws FatalCompileTimeException
    {
        String filePath = file.getAbsoluteFile().getPath();
        String[] sourceRoots = CompilerUtils.getWebSourceRoots( _env );
       
        //
        // Look through the source roots.
        //
        for ( int i = 0; i < sourceRoots.length; i++ )
        {
            String sourceRoot = sourceRoots[i];
           
            if ( filePath.startsWith( sourceRoot ) )
            {
                return file.toString().substring( sourceRoot.length() ).replace( '\\', '/' );
            }
        }
       
        //
        // Look in the web content root.
        //
        String webContentRoot = CompilerUtils.getWebContentRoot( getEnv() );
       
        if ( filePath.startsWith( webContentRoot ) )
        {
            return file.toString().substring( webContentRoot.length() ).replace( '\\', '/' );
        }
       
        assert false : "could not calculate webapp-relative file from " + file;
        return file.toString();
    }
   
    AnnotationProcessorEnvironment getEnv()
    {
        return _env;
    }
}
TOP

Related Classes of org.apache.beehive.netui.compiler.genmodel.GenStrutsApp

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.