Package org.qi4j.library.struts2

Source Code of org.qi4j.library.struts2.Qi4jPropertyAccessor

package org.qi4j.library.struts2;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import ognl.MethodFailedException;
import ognl.ObjectMethodAccessor;
import ognl.ObjectPropertyAccessor;
import ognl.OgnlContext;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import org.qi4j.api.Qi4j;
import org.qi4j.api.association.Association;
import org.qi4j.api.association.ManyAssociation;
import org.qi4j.api.association.NamedAssociation;
import org.qi4j.api.constraint.ConstraintViolation;
import org.qi4j.api.constraint.ConstraintViolationException;
import org.qi4j.api.injection.scope.Structure;
import org.qi4j.api.property.Property;
import org.qi4j.library.struts2.ConstraintViolationInterceptor.FieldConstraintViolations;

import static com.opensymphony.xwork2.conversion.impl.XWorkConverter.CONVERSION_PROPERTY_FULLNAME;
import static ognl.OgnlRuntime.getConvertedType;
import static ognl.OgnlRuntime.getFieldValue;
import static org.qi4j.library.struts2.ConstraintViolationInterceptor.CONTEXT_CONSTRAINT_VIOLATIONS;

/**
* <p>An implementation of the ObjectPropertyAccessor that provides conversion for Qi4j properties.  The typical way that
* OGNL gets/sets object attributes is by finding the corresponding JavaBean getter/setter methods.  This
* ObjectPropertyAccessor checks if there is a Qi4j property on the Composite and if there is uses the properties
* get/set methods.</p>
*
* <p>When setting Property values, if a ConstraintViolationException is thrown it is added to the context so that
* it can be processed and by the ConstraintViolationInterceptor, similar to how conversion exceptions are handled by
* the ConversionErrorInterceptor</p>
*
* <p>When setting Association values, we attempt to convert the value to the association type using the normal XWork
* converter mechanisms.  If the type is an EntityComposite, we already have a converter registered
* {@link EntityCompositeConverter} to handle conversion from a string identity to an object.  If the type is not an
* EntityComposite, but the actual values are EntityComposites, you can register the {@link EntityCompositeConverter}
* for your type in your xwork-conversion.properties file.</p>
*
* <p>NOTE: We can't do this as a regular converter because Qi4j composites doesn't (nor should it be) following the
* JavaBean standard.  We might be able to only override the getProperty() method here and have regular converters for
* Property, Association and SetAssociation but I haven't tried that yet so it may not work as expected.</>
*
* <p>TODO: Doesn't yet handle ManyAssociations, but these shouldn't be too hard to add</p>
*/
public class Qi4jPropertyAccessor
    extends ObjectPropertyAccessor
{
    private static final Object[] BLANK_ARGUMENTS = new Object[0];

    private final ObjectMethodAccessor methodAccessor = new ObjectMethodAccessor();

    @Structure
    Qi4j api;

    @Override
    public final Object getProperty( Map aContext, Object aTarget, Object aPropertyName )
        throws OgnlException
    {
        String fieldName = aPropertyName.toString();
        Object qi4jField = getQi4jField( aContext, aTarget, fieldName );
        if( qi4jField != null )
        {
            Class<?> memberClass = qi4jField.getClass();
            if( Property.class.isAssignableFrom( memberClass ) )
            {
                Property<?> property = (Property) qi4jField;
                return property.get();
            }
            else if( Association.class.isAssignableFrom( memberClass ) )
            {
                Association<?> association = (Association) qi4jField;
                return association.get();
            }
            else if( ManyAssociation.class.isAssignableFrom( memberClass ) )
            {
                return qi4jField;
            }
            else if( NamedAssociation.class.isAssignableFrom( memberClass ) )
            {
                return qi4jField;
            }
        }

        return super.getProperty( aContext, aTarget, fieldName );
    }

    @SuppressWarnings( "unchecked" )
    private Object getQi4jField( Map aContext, Object aTarget, String aFieldName )
        throws OgnlException
    {
        if( aTarget != null )
        {
            // Is target#name a method? e.g. cat.name()
            try
            {
                return methodAccessor.callMethod( aContext, aTarget, aFieldName, BLANK_ARGUMENTS );
            }
            catch( MethodFailedException e )
            {
                // Means not a property/association
            }

            // Is target#name a field? e.g. action.field1, where field1 is extracted from a composite
            OgnlContext ognlContext = (OgnlContext) aContext;
            try
            {
                return getFieldValue( ognlContext, aTarget, aFieldName, true );
            }
            catch( NoSuchFieldException e )
            {
                // Means not a field
            }
        }

        return null;
    }

    @Override
    @SuppressWarnings( "unchecked" )
    public final void setProperty( Map aContext, Object aTarget, Object aPropertyName, Object aPropertyValue )
        throws OgnlException
    {
        String fieldName = aPropertyName.toString();
        Object qi4jField = getQi4jField( aContext, aTarget, fieldName );

        if( qi4jField != null )
        {
            Class<?> memberClass = qi4jField.getClass();

            if( Property.class.isAssignableFrom( memberClass ) )
            {
                Property property = (Property) qi4jField;

                OgnlContext ognlContext = (OgnlContext) aContext;
                Class<?> propertyType = (Class) api.propertyDescriptorFor( property ).type();
                Object convertedValue = getConvertedType(
                    ognlContext, aTarget, null, fieldName, aPropertyValue, propertyType );
                try
                {
                    property.set( convertedValue );
                }
                catch( ConstraintViolationException e )
                {
                    Collection<ConstraintViolation> violations = e.constraintViolations();
                    handleConstraintViolation( aContext, aTarget, fieldName, convertedValue, violations );
                }

                return;
            }
            else if( Association.class.isAssignableFrom( memberClass ) )
            {
                Association association = (Association) qi4jField;
                OgnlContext ognlContext = (OgnlContext) aContext;
                Class<?> associationType = (Class) api.associationDescriptorFor( association ).type();
                Object convertedValue = getConvertedType(
                    ognlContext, aTarget, null, fieldName, aPropertyValue, associationType );
                if( convertedValue == OgnlRuntime.NoConversionPossible )
                {
                    throw new OgnlException( "Could not convert value to association type" );
                }
                try
                {
                    association.set( convertedValue );
                }
                catch( ConstraintViolationException e )
                {
                    Collection<ConstraintViolation> violations = e.constraintViolations();
                    handleConstraintViolation( aContext, aTarget, fieldName, aPropertyValue, violations );
                }

                return;
            }
            else if( ManyAssociation.class.isAssignableFrom( memberClass ) )
            {
                throw new OgnlException( "Setting many association [" + fieldName + "] is impossible." );
            }
            else if( NamedAssociation.class.isAssignableFrom( memberClass ) )
            {
                throw new OgnlException( "Setting named association [" + fieldName + "] is impossible." );
            }
        }

        super.setProperty( aContext, aTarget, aPropertyName, aPropertyValue );
    }

    @SuppressWarnings( "unchecked" )
    protected final void handleConstraintViolation(
        Map aContext, Object aTarget, String aPropertyName, Object aPropertyValue,
        Collection<ConstraintViolation> violations
    )
    {
        Map<String, FieldConstraintViolations> allPropertyConstraintViolations
                                               = (Map<String, FieldConstraintViolations>) aContext.get( CONTEXT_CONSTRAINT_VIOLATIONS );
        if( allPropertyConstraintViolations == null )
        {
            allPropertyConstraintViolations = new HashMap<>();
            aContext.put( CONTEXT_CONSTRAINT_VIOLATIONS, allPropertyConstraintViolations );
        }

        String realFieldName = aPropertyName;
        String fieldFullName = (String) aContext.get( CONVERSION_PROPERTY_FULLNAME );
        if( fieldFullName != null )
        {
            realFieldName = fieldFullName;
        }
        // Add another violation
        allPropertyConstraintViolations.put(
            realFieldName, new FieldConstraintViolations( aTarget, aPropertyName, aPropertyValue, violations ) );
    }
}
TOP

Related Classes of org.qi4j.library.struts2.Qi4jPropertyAccessor

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.