Coverage Report - org.kuali.rice.kns.rules.PromptBeforeValidationBase
 
Classes in this File Line Coverage Branch Coverage Complexity
PromptBeforeValidationBase
7%
3/38
0%
0/18
3
PromptBeforeValidationBase$1
N/A
N/A
3
PromptBeforeValidationBase$ContextSession
75%
22/29
66%
8/12
3
PromptBeforeValidationBase$IsAskingException
0%
0/1
N/A
3
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kns.rules;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.apache.struts.action.ActionForm;
 20  
 import org.kuali.rice.core.api.util.RiceConstants;
 21  
 import org.kuali.rice.kns.rule.PromptBeforeValidation;
 22  
 import org.kuali.rice.kns.rule.event.PromptBeforeValidationEvent;
 23  
 import org.kuali.rice.kns.web.struts.form.KualiForm;
 24  
 import org.kuali.rice.krad.document.Document;
 25  
 import org.kuali.rice.krad.question.ConfirmationQuestion;
 26  
 import org.kuali.rice.krad.util.KRADConstants;
 27  
 
 28  
 import javax.servlet.http.HttpServletRequest;
 29  
 import java.util.Arrays;
 30  
 import java.util.Iterator;
 31  
 import java.util.NoSuchElementException;
 32  
 
 33  
 /**
 34  
  * 
 35  
  * This class simplifies requesting clarifying user input prior to applying business rules. It mostly shields the classes that
 36  
  * extend it from being aware of the web layer, even though the input is collected via a series of one or more request/response
 37  
  * cycles.
 38  
  * 
 39  
  * Beware: method calls with side-effects will have unusual results. While it looks like the doRules method is executed
 40  
  * sequentially, in fact, it is more of a geometric series: if n questions are asked, then the code up to and including the first
 41  
  * question is executed n times, the second n-1 times, ..., the last question only one time.
 42  
  * 
 43  
  * 
 44  
  */
 45  
 public abstract class PromptBeforeValidationBase implements PromptBeforeValidation {
 46  
 
 47  1
     protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PromptBeforeValidationBase.class);
 48  
 
 49  
     protected String question;
 50  
     protected String buttonClicked;
 51  
     protected PromptBeforeValidationEvent event;
 52  
     protected KualiForm form;
 53  
 
 54  0
     private class IsAskingException extends RuntimeException {
 55  
     }
 56  
 
 57  
     /**
 58  
      * 
 59  
      * This class acts similarly to HTTP session, but working inside a REQUEST parameter
 60  
      * 
 61  
      * 
 62  
      */
 63  
     /**
 64  
      * This is a description of what this class does - wliang don't forget to fill this in. 
 65  
      * 
 66  
      * @author Kuali Rice Team (rice.collab@kuali.org)
 67  
      *
 68  
      */
 69  
     public class ContextSession {
 70  
         private final static String DELIMITER = ".";
 71  
         PromptBeforeValidationEvent event;
 72  
 
 73  1
         public ContextSession(String context, PromptBeforeValidationEvent event) {
 74  1
             this.event = event;
 75  
 
 76  1
             this.event.setQuestionContext(context);
 77  1
             if (this.event.getQuestionContext() == null) {
 78  0
                 this.event.setQuestionContext("");
 79  
             }
 80  
 
 81  1
         }
 82  
 
 83  
         /**
 84  
          * Whether a question with a given ID has already been asked
 85  
          * 
 86  
          * @param id the ID of the question, an arbitrary value, but must be consistent
 87  
          * @return
 88  
          */
 89  
         public boolean hasAsked(String id) {
 90  0
             return StringUtils.contains(event.getQuestionContext(), id);
 91  
         }
 92  
 
 93  
         /**
 94  
          * Invoked to indicate that the user should be prompted a question
 95  
          * 
 96  
          * @param id the ID of the question, an arbitrary value, but must be consistent
 97  
          * @param text the question text, to be displayed to the user
 98  
          */
 99  
         public void askQuestion(String id, String text) {
 100  1
             event.setQuestionId(id);
 101  1
             event.setQuestionType(KRADConstants.CONFIRMATION_QUESTION);
 102  1
             event.setQuestionText(text);
 103  1
             event.setPerformQuestion(true);
 104  1
         }
 105  
 
 106  
         public void setAttribute(String name, String value) {
 107  3
             if (LOG.isDebugEnabled()) {
 108  0
                 LOG.debug("setAttribute(" + name + "," + value + ")");
 109  
             }
 110  3
             event.setQuestionContext(event.getQuestionContext() + DELIMITER + name + DELIMITER + value);
 111  
 
 112  3
         }
 113  
 
 114  
         public String getAttribute(String name) {
 115  3
             if (LOG.isDebugEnabled()) {
 116  0
                 LOG.debug("getAttribute(" + name + ")");
 117  
             }
 118  3
             String result = null;
 119  
 
 120  3
             Iterator values = Arrays.asList(event.getQuestionContext().split("\\" + DELIMITER)).iterator();
 121  
 
 122  21
             while (values.hasNext()) {
 123  18
                 if (values.next().equals(name)) {
 124  
                     try {
 125  3
                         result = (String) values.next();
 126  
                     }
 127  0
                     catch (NoSuchElementException e) {
 128  0
                         result = null;
 129  3
                     }
 130  
                 }
 131  
             }
 132  3
             if (LOG.isDebugEnabled()) {
 133  0
                 LOG.debug("returning " + result);
 134  
             }
 135  3
             return result;
 136  
         }
 137  
 
 138  
     }
 139  
 
 140  
     /**
 141  
      * Implementations will override this method to do perform the actual prompting and/or logic
 142  
      * 
 143  
      * They are able to utilize the following methods:
 144  
      * <li> {@link PromptBeforeValidationBase#abortRulesCheck()}
 145  
      * <li> {@link PromptBeforeValidationBase#askOrAnalyzeYesNoQuestion(String, String)}
 146  
      * <li> {@link #hasAsked(String)}
 147  
      * 
 148  
      * @param document
 149  
      * @return
 150  
      */
 151  
     public abstract boolean doPrompts(Document document);
 152  
 
 153  
     private boolean isAborting;
 154  
 
 155  
     ContextSession session;
 156  
 
 157  1
     public PromptBeforeValidationBase() {
 158  1
     }
 159  
 
 160  
 
 161  
     public boolean processPrompts(ActionForm form, HttpServletRequest request, PromptBeforeValidationEvent event) {
 162  0
         question = request.getParameter(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME);
 163  0
         buttonClicked = request.getParameter(KRADConstants.QUESTION_CLICKED_BUTTON);
 164  0
         this.event = event;
 165  0
         this.form = (KualiForm) form;
 166  
 
 167  0
         if (LOG.isDebugEnabled()) {
 168  0
             LOG.debug("Question is: " + question);
 169  0
             LOG.debug("ButtonClicked: " + buttonClicked);
 170  0
             LOG.debug("QuestionContext() is: " + event.getQuestionContext());
 171  
         }
 172  
 
 173  0
         session = new ContextSession(request.getParameter(KRADConstants.QUESTION_CONTEXT), event);
 174  
 
 175  0
         boolean result = false;
 176  
 
 177  
         try {
 178  0
             result = doPrompts(event.getDocument());
 179  
         }
 180  0
         catch (IsAskingException e) {
 181  0
             return false;
 182  0
         }
 183  
 
 184  0
         if (isAborting) {
 185  0
             return false;
 186  
         }
 187  
 
 188  0
         return result;
 189  
     }
 190  
 
 191  
     /**
 192  
      * This bounces the user back to the document as if they had never tried to routed it. (Business rules are not invoked
 193  
      * and the action is not executed.)
 194  
      * 
 195  
      */
 196  
     public void abortRulesCheck() {
 197  0
         event.setActionForwardName(RiceConstants.MAPPING_BASIC);
 198  0
         isAborting = true;
 199  0
     }
 200  
 
 201  
     /**
 202  
      * This method poses a Y/N question to the user.  If the user has already answered the question, then it returns whether
 203  
      * the answer to the question was yes or no
 204  
      * 
 205  
      * Code that invokes this method will behave a bit strangely, so you should try to keep it as simple as possible.
 206  
      * 
 207  
      * @param id an ID for the question
 208  
      * @param text the text of the question, to be displayed on the screen
 209  
      * @return true if the user answered Yes, false if the user answers no
 210  
      * @throws IsAskingException if the user needs to be prompted the question
 211  
      */
 212  
     public boolean askOrAnalyzeYesNoQuestion(String id, String text) throws IsAskingException {
 213  
 
 214  0
         if (LOG.isDebugEnabled()) {
 215  0
             LOG.debug("Entering askOrAnalyzeYesNoQuestion(" + id + "," + text + ")");
 216  
         }
 217  
 
 218  0
         String cached = (String) session.getAttribute(id);
 219  0
         if (cached != null) {
 220  0
             LOG.debug("returning cached value: " + id + "=" + cached);
 221  0
             return new Boolean(cached).booleanValue();
 222  
         }
 223  
 
 224  0
         if (id.equals(question)) {
 225  0
             session.setAttribute(id, Boolean.toString(!ConfirmationQuestion.NO.equals(buttonClicked)));
 226  0
             return !ConfirmationQuestion.NO.equals(buttonClicked);
 227  
         }
 228  0
         else if (!session.hasAsked(id)) {
 229  0
             if (LOG.isDebugEnabled()) {
 230  0
                 LOG.debug("Forcing question to be asked: " + id);
 231  
             }
 232  0
             session.askQuestion(id, text);
 233  
         }
 234  
 
 235  0
         LOG.debug("Throwing Exception to force return to Action");
 236  0
         throw new IsAskingException();
 237  
     }
 238  
 
 239  
 }