Package er.directtoweb.assignments.delayed

Source Code of er.directtoweb.assignments.delayed.ERDDelayedSelectorInvocationAssignment

//
// jbl: 13 april 2006
//

package er.directtoweb.assignments.delayed;

import org.apache.log4j.Logger;

import com.webobjects.directtoweb.Assignment;
import com.webobjects.directtoweb.D2WContext;
import com.webobjects.eocontrol.EOKeyValueUnarchiver;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSSelector;

import er.directtoweb.assignments.ERDComputingAssignmentInterface;
import er.extensions.foundation.ERXSelectorUtilities;

/**
* Similar in nature to a key-value assignment, but allows you to construct arbitrary method invocations to
* resolve rules.  As a somewhat contrived example, assume we're inferring on the componentName rule:
* <P>
* <code>entity.name = 'Person' and propertyKey = 'username' -> componentName = (object, componentForKey, propertyKey)</code>
*
* <P>
*
* Resolving the rule for componentName, would end up invoking the <code>componentForKey(Object)</code> method on the current object
* from the rule context, passing the current propertyKey through for the argument.  This would boil down to
* <code>object.componentForKey("username")</code>.
*
* <P>
*
* The array in the value for this assignment must have two or more objects.  The first object is a key path evaluated
* on the rule context to find the target of the selector.  The second object is the selector name, it is a constant
* and is not evaluated on the rule context.  All subsequent objects in the array are treated as key paths to resolve
* on the rule context to get the arguments for the selector.
*
* <P>
*
* Assumptions:
* <ul>
* <li>The arguments to the invoked method must all be Objects.  This isn't strictly speaking necessary, but the way the
*     assignment is currently coded, it's required.</li>
* </ul>
*/
public class ERDDelayedSelectorInvocationAssignment extends ERDDelayedAssignment implements ERDComputingAssignmentInterface {
  /**
   * Do I need to update serialVersionUID?
   * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
   * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
   */
  private static final long serialVersionUID = 1L;

    public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver eokeyvalueunarchiver)  {
        return new ERDDelayedSelectorInvocationAssignment(eokeyvalueunarchiver);
    }

    public ERDDelayedSelectorInvocationAssignment(EOKeyValueUnarchiver u) {
        super(u);
    }

    public ERDDelayedSelectorInvocationAssignment(String key, Object value) {
        super(key,value);
    }

    public NSArray dependentKeys(String keyPath) {
        return DefaultImplementation.dependentKeys(this, keyPath);
    }

    @Override
    public Object fireNow(D2WContext c) {
        return DefaultImplementation.fire(this, c);
    }

    public static class DefaultImplementation {

        private static final Logger _log = Logger.getLogger(ERDDelayedSelectorInvocationAssignment.class);

        // we cache 0 - 5 arguments
        private static Class[][] _parameterTypesArrays = new Class[5 + 1][];

        static {
            for ( int i = 0; i < _parameterTypesArrays.length; i++ ) {
                Class[] types = null;

                if ( i > 0 ) {
                    types = new Class[i];

                    for ( int j = 0; j < types.length; j++ )
                        types[j] = Object.class;
                }

                _parameterTypesArrays[i] = types;
            }
        }

        private static Class[] _parameterTypesForNumberOfArguments(int numberOfArguments) {
            final Class[] result;

            if ( numberOfArguments < _parameterTypesArrays.length ) {
                result = _parameterTypesArrays[numberOfArguments];
            }
            else {
                result = new Class[numberOfArguments];

                for ( int i = 0; i < numberOfArguments; i++ )
                    result[i] = Object.class;
            }

            return result;
        }

        public static NSArray dependentKeys(Assignment assignment, String keyPath) {
            final NSArray value = (NSArray)assignment.value();
            NSArray result = value;

            if ( result != null && result.count() > 1 ) {
                NSMutableArray a = value.mutableClone();

                a.removeObjectAtIndex(1)// selector name is constant
                result = a;
            }

            return result != null ? result : NSArray.EmptyArray;
        }

        public static Object fire(Assignment assignment, D2WContext c) {
            final NSArray value = (NSArray)assignment.value();
            final int valueCount = value.count();
            final Object target;
            Object result = null;

            if ( valueCount < 2 )
                throw new RuntimeException("Must have at least 2 components in value: " + value);

            target = c.valueForKeyPath((String)value.objectAtIndex(0));
            if ( target != null ) {
                final int numberOfArguments = valueCount - 2;
                final String selectorName = (String)value.objectAtIndex(1);
                final NSSelector selector;
                Object[] arguments = null;

                if ( numberOfArguments > 0 ) {
                    arguments = new Object[numberOfArguments];

                    for ( int i = 2; i < valueCount; i++ )
                        arguments[i-2] = c.valueForKeyPath((String)value.objectAtIndex(i));
                }

                if ( _log.isDebugEnabled() ) {
                    final StringBuilder sb = new StringBuilder('(');

                    if ( arguments != null ) {
                        for ( int i = 0; i < arguments.length; i++ ) {
                            if ( i > 0)
                                sb.append(", ");
                            sb.append(arguments[i]);
                        }
                    }
                    sb.append(')');

                    _log.debug("Going to fire " + selectorName + " on object " + target + " with " + numberOfArguments + " arguments: " + sb);
                }

                selector = new NSSelector(selectorName, _parameterTypesForNumberOfArguments(numberOfArguments));

                result = ERXSelectorUtilities.invoke(selector, target, arguments);
            }

            return result;
        }
    }

}
TOP

Related Classes of er.directtoweb.assignments.delayed.ERDDelayedSelectorInvocationAssignment

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.