Package er.directtoweb.delegates

Source Code of er.directtoweb.delegates.ERDBranchDelegate

/*
* Copyright (C) NetStruxr, Inc. All rights reserved.
*
* This software is published under the terms of the NetStruxr
* Public Software License version 0.5, a copy of which has been
* included with this distribution in the LICENSE.NPL file.  */
package er.directtoweb.delegates;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Enumeration;
import java.util.Iterator;

import org.apache.log4j.Logger;

import com.webobjects.appserver.WOComponent;
import com.webobjects.directtoweb.D2WContext;
import com.webobjects.directtoweb.NextPageDelegate;
import com.webobjects.eocontrol.EOEnterpriseObject;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSForwardException;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;

import er.directtoweb.ERDirectToWeb;
import er.directtoweb.interfaces.ERDMessagePageInterface;
import er.directtoweb.pages.ERD2WPage;
import er.extensions.foundation.ERXArrayUtilities;
import er.extensions.foundation.ERXDictionaryUtilities;
import er.extensions.localization.ERXLocalizer;

/**
* The branch delegate is used in conjunction with the
* {@link ERDMessagePageInterface ERDMessagePageInterface} to allow
* flexible branching for message pages. Branch delegates can
* only be used with templates that implement the
* {@link ERDBranchInterface ERDBranchInterface}.
*/
public abstract class ERDBranchDelegate implements ERDBranchDelegateInterface {
  /**
   * 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;

  /**
   * Runtime flags for the delegate, so you can have one delegate for all tasks.
   */
  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.METHOD)
  public @interface D2WDelegate {
    /**
     * Returns the names of the scope where you can have this method. One of "selection,object"
     */
    public String scope() default "";

    /**
     * Returns the names of the tasks where you can have this method. Example "query,select"
     */
    public String availableTasks() default "";
   
    /**
     * Returns the names of the pages where you can have this method. Example "ListWebMaster,QueryWebMaster"
     */
    public String availablePages() default "";
  }
 
    /** logging support */
    public final static Logger log = Logger.getLogger(ERDBranchDelegate.class);

    /** holds the WOComponent class array used to lookup branch delegate methods */
    // MOVEME: Should belong in a WO constants class
    public final static Class[] WOComponentClassArray = new Class[] { WOComponent.class };
   
    public static final String BRANCH_CHOICES = "branchChoices";
    public static final String BRANCH_BUTTON_ID = "branchButtonID";
    public static final String BRANCH_NAME = "branchName";
    public static final String BRANCH_LABEL = "branchButtonLabel";
    public static final String BRANCH_PREFIX = "Button";
   
    /**
     * Implementation of the {@link NextPageDelegate NextPageDelegate}
     * interface. This method provides the dynamic dispatch based on
     * the selected branch provided by the sender. Will call the
     * method <branchName>(WOComponent) on itself returning the
     * result.
     * @param sender template invoking the branch delegate
     * @return result of dynamic method lookup and execution on itself.
     */
    public final WOComponent nextPage(WOComponent sender) {
        WOComponent nextPage = null;
        if (sender instanceof ERDBranchInterface) {
            String branchName = ((ERDBranchInterface)sender).branchName();
            if( branchName != null ) {
                if (log.isDebugEnabled())
                    log.debug("Branching to branch: " + branchName);
                try {
                    Method m = getClass().getMethod(branchName, WOComponentClassArray);
                    nextPage = (WOComponent)m.invoke(this, new Object[] { sender });
                } catch (InvocationTargetException ite) {
                    log.error("Invocation exception occurred in ERBranchDelegate: " + ite.getTargetException() + " for branch name: " + branchName, ite.getTargetException());
                    throw new NSForwardException(ite.getTargetException());
                } catch (Exception e) {
                    log.error("Exception occurred in ERBranchDelegate: " + e.toString() + " for branch name: " + branchName);
                    throw new NSForwardException(e);
                }
            }
        } else {
            log.warn("Branch delegate being used with a component that does not implement the ERBranchInterface");
        }
        return nextPage;
    }
   
    /**
     * Utility to build branch choice dictionaries in code.
     * @param method name of the method in question
     * @param label label for the button, a beautified method name will be used if set to null.
     * @return NSDictionary suitable as a branch choice.
     */
    protected NSDictionary branchChoiceDictionary(String method, String label) {
      if(label == null) {
        label = ERXLocalizer.currentLocalizer().localizedDisplayNameForKey(BRANCH_PREFIX, method);
      }
      return ERXDictionaryUtilities.dictionaryWithObjectsAndKeys(new Object [] { method, BRANCH_NAME, label, BRANCH_LABEL, method +  "Action", BRANCH_BUTTON_ID});
    }
   
    /**
     * Calculates which branches to show in the display first
     * asking the context for the key <b>branchChoices</b>. If
     * this returns null then
     * @param context current D2W context
     * @return array of branch names.
     */
    public NSArray branchChoicesForContext(D2WContext context) {
        NSArray choices = (NSArray)context.valueForKey(BRANCH_CHOICES);
        if (choices == null) {
            choices = defaultBranchChoices(context);
        } else {
          NSMutableArray translatedChoices = new NSMutableArray();
          for (Iterator iter = choices.iterator(); iter.hasNext();) {
            Object o = iter.next();
        String method = null;
        String label = null;
             NSMutableDictionary entry = new NSMutableDictionary();
            if (o instanceof NSDictionary) {
              entry.addEntriesFromDictionary((NSDictionary) o);
              method = (String) entry.objectForKey(BRANCH_NAME);
              label = (String) entry.objectForKey(BRANCH_LABEL);
            } else if (o instanceof String) {
                    method = (String) o;
                    entry.setObjectForKey(method, BRANCH_NAME);
                }
                if (label == null) {
              label = ERXLocalizer.currentLocalizer().localizedDisplayNameForKey(BRANCH_PREFIX, method);
               } else if(label.startsWith(BRANCH_PREFIX + ".")){
                 String localizerKey = label;
              String localized = ERXLocalizer.currentLocalizer().localizedStringForKey(label);
              if(localized == null) {
                label = ERXLocalizer.currentLocalizer().localizedDisplayNameForKey(BRANCH_PREFIX, method);
                  ERXLocalizer.currentLocalizer().takeValueForKey(label, localizerKey);
                } else {
                  label = localized;
              }
               } else {
                 // assume it's a user-provided value. If we have an entry in the localizer, use it
                 // otherwise just return it.
              label = ERXLocalizer.currentLocalizer().localizedStringForKeyWithDefault(label);
            }
            entry.setObjectForKey(label, BRANCH_LABEL);
            entry.setObjectForKey(method +  "Action", BRANCH_BUTTON_ID);
            translatedChoices.addObject(entry);
          }
          choices = translatedChoices;
        }
        return choices;
    }

    /**
     * Uses reflection to find all of the public methods that don't start with
     * an underscore and take a single WOComponent as a parameter are returned.
     * The methods are sorted by this key.
     * @param context current D2W context
     */
    protected NSArray defaultBranchChoices(D2WContext context) {
        NSArray choices = NSArray.EmptyArray;
        try {
          String task = context.task();
          String pageName = context.dynamicPage();
            NSMutableArray methodChoices = new NSMutableArray();
            Method methods[] = getClass().getMethods();
            for (Enumeration e = new NSArray(methods).objectEnumerator(); e.hasMoreElements();) {
                Method method = (Method)e.nextElement();
                if (method.getParameterTypes().length == 1
                        &&  method.getParameterTypes()[0] == WOComponent.class
                        && !method.getName().equals("nextPage")
                        && method.getName().charAt(0) != '_'
                        && ((method.getModifiers() & Modifier.PUBLIC) == Modifier.PUBLIC)
                ) {
                    boolean isAllowed = true;
                    if(method.isAnnotationPresent(D2WDelegate.class)) {
                      D2WDelegate info = method.getAnnotation(D2WDelegate.class);
                      String scope = info.scope();
                      String availableTasks = info.availableTasks();
                      String availablePages = info.availablePages();
            if(scope.length() > 0) {
              if("object".equals(scope) && context.valueForKey("object") == null) {
                isAllowed = false;
              }
              if(!"object".equals(scope) && context.valueForKey("object") != null) {
                isAllowed = false;
              }
            }
            if(availableTasks.length() > 0 && !availableTasks.contains(task)) {
              isAllowed = false;
            }
            if(availablePages.length() > 0 && !availablePages.contains(pageName)) {
              isAllowed = false;
            }
                    }
                    if(isAllowed) {
                      NSDictionary branch = branchChoiceDictionary(method.getName(), null);
                      methodChoices.addObject(branch);     
                    }
                }
            }
            choices = ERXArrayUtilities.sortedArraySortedWithKey(methodChoices, BRANCH_LABEL);
        } catch (SecurityException e) {
            log.error("Caught security exception while calculating the branch choices for delegate: "
                    + this + " exception: " + e.getMessage());
        }
        return choices;
    }
    /**
     * Gets the D2W context from the innermost enclosing D2W component of the sender.
     * @param sender
     */
    protected D2WContext d2wContext(WOComponent sender) {
      if(ERDirectToWeb.D2WCONTEXT_SELECTOR.implementedByObject(sender)) {
            return (D2WContext) sender.valueForKey(ERDirectToWeb.D2WCONTEXT_SELECTOR.name());
      }
        throw new IllegalStateException("Can't figure out d2wContext from: " + sender);
    }
   
    /**
     * return the innermost object which might be of interest
     * @param sender
     */
    protected EOEnterpriseObject object(WOComponent sender) {
        return object(d2wContext(sender));
    }
   
    /**
     * Returns the current object form the d2w context
     * @param context
     */
    protected EOEnterpriseObject object(D2WContext context) {
        return (EOEnterpriseObject) context.valueForKey(ERD2WPage.Keys.object);
    }

    /**
     * Utility to remove entries based on an array of keys
     * @param keys
     * @param choices
     */
    protected NSArray choiceByRemovingKeys(NSArray keys, NSArray choices) {
        NSMutableArray result = new NSMutableArray(choices.count());
        for (Enumeration e = choices.objectEnumerator(); e.hasMoreElements();) {
            NSDictionary choice = (NSDictionary) e.nextElement();
            if(!keys.containsObject(choice.objectForKey(BRANCH_NAME))) {
                result.addObject(choice);
            }
        }
        return result;
    }

    /**
     * Utility to leave entries based on an array of keys
     * @param keys
     * @param choices
     */
    protected NSArray choiceByLeavingKeys(NSArray keys, NSArray choices) {
        NSMutableArray result = new NSMutableArray(choices.count());
        for (Enumeration e = choices.objectEnumerator(); e.hasMoreElements();) {
            NSDictionary choice = (NSDictionary) e.nextElement();
            if(keys.containsObject(choice.objectForKey(BRANCH_NAME))) {
                result.addObject(choice);
            }
        }
        return result;
    }

}
TOP

Related Classes of er.directtoweb.delegates.ERDBranchDelegate

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.