All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.opensagres.xdocreport.template.formatter.AbstractFieldsMetadataClassSerializer Maven / Gradle / Ivy

There is a newer version: 2.0.6
Show newest version
/**
 * Copyright (C) 2011-2015 The XDocReport Team 
 *
 * All rights reserved.
 *
 * Permission is hereby granted, free  of charge, to any person obtaining
 * a  copy  of this  software  and  associated  documentation files  (the
 * "Software"), to  deal in  the Software without  restriction, including
 * without limitation  the rights to  use, copy, modify,  merge, publish,
 * distribute,  sublicense, and/or sell  copies of  the Software,  and to
 * permit persons to whom the Software  is furnished to do so, subject to
 * the following conditions:
 *
 * The  above  copyright  notice  and  this permission  notice  shall  be
 * included in all copies or substantial portions of the Software.
 *
 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
 * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package fr.opensagres.xdocreport.template.formatter;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import fr.opensagres.xdocreport.core.XDocReportException;
import fr.opensagres.xdocreport.template.annotations.FieldMetadata;
import fr.opensagres.xdocreport.template.annotations.ImageMetadata;
import java.lang.reflect.WildcardType;

/**
 * Abstract class for Fields metadata serializer.
 */
public abstract class AbstractFieldsMetadataClassSerializer
    implements IFieldsMetadataClassSerializer
{

    private static Class IIMAGEPROVIDER_CLASS = null;
    static
    {
        try
        {
            IIMAGEPROVIDER_CLASS = Class.forName( "fr.opensagres.xdocreport.document.images.IImageProvider" );
        }
        catch ( ClassNotFoundException e )
        {
            e.printStackTrace();
        }
    }

    private final String id;

    private final String description;

    // package name to exclude while processing
    private final List excludedPackages;

    private IPropertyDescriptorFilter filter = new AllowAllPropertyDescriptorFilter();;
    
    public AbstractFieldsMetadataClassSerializer( String id, String description )
    {
        this.id = id;
        this.description = description;
        this.excludedPackages = new ArrayList();
        this.excludedPackages.add( "java." );
    }

    public String getId()
    {
        return id;
    }

    public String getDescription()
    {
        return description;
    }

    
    public void setFilter(IPropertyDescriptorFilter filter) {
		this.filter = filter;
	}
    
    public void load( FieldsMetadata fieldsMetadata, String key, Class clazz )
        throws XDocReportException
    {
        load( fieldsMetadata, key, clazz, false );
    }

    public void load( FieldsMetadata fieldsMetadata, String key, Class clazz, boolean listType )
        throws XDocReportException
    {
        try
        {
            List path = new ArrayList();
            process( fieldsMetadata, key, clazz, path, listType );
        }
        catch ( Exception e )
        {
            throw new XDocReportException( e );
        }
    }

    private void process( FieldsMetadata fieldsMetadata, String key, Class clazz, List path,
                          boolean isList )
        throws IntrospectionException
    {

        BeanInfo infos = Introspector.getBeanInfo( clazz );
        PropertyDescriptor[] descs = infos.getPropertyDescriptors();

        for ( PropertyDescriptor propertyDescriptor : descs )
        {

            // should not be transient and should have getter method
            if ( isTransient( propertyDescriptor, clazz ) || !haveGetterMethod( propertyDescriptor ) )
                continue;

            Method method = propertyDescriptor.getReadMethod();
            Class returnTypeClass = method.getReturnType();

            // if this current property is already in path, we have to continue without it.
            boolean wasVisited = false;
            for ( PropertyDescriptor item : path )
            {
                if ( item.equals( propertyDescriptor ) )
                {
                    wasVisited = true;
                    break;
                }
            }

            if ( wasVisited )
                continue;
            
            if(filter.test(propertyDescriptor)) {
            	return;
            }

            // process the field
            if ( Iterable.class.isAssignableFrom( returnTypeClass ) )
            {
                Type collectionType = method.getGenericReturnType();
                if ( collectionType != null && ( collectionType instanceof ParameterizedType ) )
                {
                    ParameterizedType parameterizedType = (ParameterizedType) method.getGenericReturnType();
                    Type[] types = parameterizedType.getActualTypeArguments();
                    if ( types.length == 1 )
                    {
                        Class itemClazz = null;
                        if ( types[0] instanceof WildcardType )
                        {
                            // WildcardType cannot be cast to Class
                            WildcardType wildcardType = (WildcardType) types[0];
                            if ( wildcardType.getLowerBounds().length != 0 )
                            {
                                itemClazz = (Class) wildcardType.getLowerBounds()[0];
                            }
                            else if ( wildcardType.getUpperBounds().length != 0 )
                            {
                                itemClazz = (Class) wildcardType.getUpperBounds()[0];
                            }
                        }
                        else
                        {
                            itemClazz = (Class) types[0];
                        }
                        if ( itemClazz != null )
                        {
                            if ( isImageField( itemClazz ) )
                            {// add image field as is
                                addField( key, fieldsMetadata, path, propertyDescriptor, true, true );
                            }
                            else if ( isTextField( itemClazz ) )
                            {// add text field as is
                                addField( key, fieldsMetadata, path, propertyDescriptor, true, false );
                            }
                            else
                            {// continue building with this class
                                path.add( propertyDescriptor );
                                process( fieldsMetadata, key, itemClazz, path, true );
                                path.remove( propertyDescriptor );
                            }
                        }
                    }
                }
            }
            else if ( isImageField( returnTypeClass ) )
            {// add image field
                addField( key, fieldsMetadata, path, propertyDescriptor, isList, true );
            }
            else if ( isTextField( returnTypeClass ) )
            {// add text field

                addField( key, fieldsMetadata, path, propertyDescriptor, isList, false );
            }
            else
            {// continue building with this class
                path.add( propertyDescriptor );
                process( fieldsMetadata, key, returnTypeClass, path, isList );
                path.remove( propertyDescriptor );
            }
        }
    }

    private boolean isTextField( Class returnTypeClass )
    {
        return String.class.isAssignableFrom( returnTypeClass ) || isClassToExclude( returnTypeClass );
    }

    private boolean isImageField( Class returnTypeClass )
    {
        return InputStream.class.isAssignableFrom( returnTypeClass ) || byte[].class.isAssignableFrom( returnTypeClass )
            || File.class.isAssignableFrom( returnTypeClass )
            || ( IIMAGEPROVIDER_CLASS != null && IIMAGEPROVIDER_CLASS.isAssignableFrom( returnTypeClass ) );
    }

    /**
     * @param key - it is constant for whole process, this key is defined by user
     * @param fieldsMetadata - destination for fields
     * @param path - list of fields which should be accessed to read current field
     * @param currentField - field to be added to fieldsMetadata
     * @param proDesc - Property Descriptor. Used to retrieve getter annotations to be able to manage styles
     * @param isList true if field is list and false otherwise.
     * @param isImage true if field is an image and false otherwise.
     */
    private void addField( String key, FieldsMetadata fieldsMetadata, List path,
                           PropertyDescriptor currentField, Boolean isList, boolean isImage )
    {
        String fieldName = createFieldName( key, path, currentField );

        // check if field is already there
        for ( fr.opensagres.xdocreport.template.formatter.FieldMetadata fieldMetadata : fieldsMetadata.getFields() )
            if ( fieldMetadata.getFieldName().equals( fieldName ) )
                return;

        Method method = currentField.getReadMethod();
        FieldMetadata fMetadata = method.getAnnotation( FieldMetadata.class );

        if ( fMetadata != null )
        {
            //
            ImageMetadata[] images = fMetadata.images();
            if ( images.length < 1 )
            {
                addFieldAndUpdateWithAnnotation( fieldsMetadata, isList, fieldName, null, fMetadata );
            }
            else
            {
                for ( int i = 0; i < images.length; i++ )
                {
                    addFieldAndUpdateWithAnnotation( fieldsMetadata, isList, fieldName, images[i], fMetadata );
                }
            }
        }
        else
        {
            String imageName = isImage ? fieldName : null;
            fieldsMetadata.addField( fieldName, isList, imageName, null, null );
        }

    }

    private String createFieldName( String key, List path, PropertyDescriptor currentField )
    {
        StringBuilder fieldName = new StringBuilder( key );
        for ( PropertyDescriptor fieldFromPath : path )
        {
            fieldName.append( getFieldName( "", fieldFromPath.getName() ) );
        }

        // generate field path
        fieldName.append( getFieldName( "", currentField.getName() ) );
        return fieldName.toString();
    }

    private void addFieldAndUpdateWithAnnotation( FieldsMetadata fieldsMetadata, Boolean isList, String fieldName,
                                                  ImageMetadata imageData, FieldMetadata fMetadata )
    {
        String imageName = null;
        if ( imageData != null )
        {
            imageName = imageData.name();
        }
        fr.opensagres.xdocreport.template.formatter.FieldMetadata newField =
            fieldsMetadata.addField( fieldName.toString(), isList, imageName, fMetadata.syntaxKind(),
                                     fMetadata.syntaxWithDirective() );
        // description from annotation
        newField.setDescription( fMetadata.description() );
        if ( imageData != null )
        {
            newField.setBehaviour( imageData.behaviour() );
        }
    }

    /**
     * This method check if the propertyDescriptor is transient in Class clazz. It will go upper in hierarchy tree, also
     * as taking in consideration the implemented interfaces, as for clazz, as and for implemented interfaces by super
     * classes.
     * 
     * @param propertyDescriptor
     * @param clazz
     * @return true if the property from propertyDescriptor in class clazz is transient
     */
    private boolean isTransient( PropertyDescriptor propertyDescriptor, Class clazz )
    {
        try
        {
            if ( clazz != null )
            {
                Field field = clazz.getDeclaredField( propertyDescriptor.getName() );
                return Modifier.isTransient( field.getModifiers() );
            }
            else
            {// if we are here this mean we processed
             // whole tree for class and interfaces and
             // not found field, we will consider that it is just an get
             // Method which can't have transient modifier
                return false;
            }
        }
        catch ( SecurityException e )
        {// if we have no access because of
         // security, we will mark it as
         // transient, as from
         // template engine we also will not be able to access it
            return true;
        }
        catch ( NoSuchFieldException e )
        {
            // in this case we will go upper with parent and interfaces
            // check parent class
            if ( isTransient( propertyDescriptor, clazz.getSuperclass() ) )
                return true;
            // check implemented interfaces
            Class[] interfaces = clazz.getInterfaces();
            for ( Class _interfase : interfaces )
            {
                if ( isTransient( propertyDescriptor, _interfase ) )
                    return true;
            }
            return false;
        }

    }

    private boolean haveGetterMethod( PropertyDescriptor propertyDescriptor )
    {
        Method method = propertyDescriptor.getReadMethod();
        return isGetterMethod( method );
    }

    /**
     * Return true if package of the given class start with list of package to exclude and false otherwise.
     * 
     * @param clazz
     * @return
     */
    private boolean isClassToExclude( Class clazz )
    {
        if ( clazz != null && clazz.getPackage() != null )
        {
            String packageName = clazz.getPackage().getName();
            for ( String excludePackageName : excludedPackages )
            {
                if ( packageName.startsWith( excludePackageName ) )
                    return true;
            }
        }
        return false;
    }

    private boolean isGetterMethod( Method method )
    {
        if ( method == null )
        {
            return false;
        }
        String name = method.getName();
        return !name.equals( "getClass" ) && ( name.startsWith( "get" ) || name.startsWith( "is" ) );
    }

    
    
    protected abstract String getFieldName( String key, String getterName );
    

    static class AllowAllPropertyDescriptorFilter implements IPropertyDescriptorFilter {

		public boolean test(PropertyDescriptor descriptor) {
			return false;
		}
    	
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy