Coverage Report - org.kuali.student.common.ui.client.mvc.history.HistoryManager
 
Classes in this File Line Coverage Branch Coverage Complexity
HistoryManager
0%
0/93
0%
0/60
3.267
HistoryManager$1
0%
0/5
0%
0/2
3.267
HistoryManager$NavigationEventMonitor
0%
0/9
0%
0/2
3.267
HistoryManager$NavigationEventMonitor$1
0%
0/6
0%
0/2
3.267
 
 1  
 /**
 2  
  * Copyright 2010 The Kuali Foundation Licensed under the
 3  
  * Educational Community License, Version 2.0 (the "License"); you may
 4  
  * not use this file except in compliance with the License. You may
 5  
  * obtain a copy of the License at
 6  
  *
 7  
  * http://www.osedu.org/licenses/ECL-2.0
 8  
  *
 9  
  * Unless required by applicable law or agreed to in writing,
 10  
  * software distributed under the License is distributed on an "AS IS"
 11  
  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 12  
  * or implied. See the License for the specific language governing
 13  
  * permissions and limitations under the License.
 14  
  */
 15  
 
 16  
 package org.kuali.student.common.ui.client.mvc.history;
 17  
 
 18  
 import java.util.HashMap;
 19  
 import java.util.Iterator;
 20  
 import java.util.Map;
 21  
 
 22  
 import org.kuali.student.common.ui.client.application.ViewContext;
 23  
 import org.kuali.student.common.ui.client.mvc.Controller;
 24  
 import org.kuali.student.common.ui.client.mvc.breadcrumb.BreadcrumbManager;
 25  
 
 26  
 import com.google.gwt.event.logical.shared.ValueChangeEvent;
 27  
 import com.google.gwt.event.logical.shared.ValueChangeHandler;
 28  
 import com.google.gwt.user.client.DOM;
 29  
 import com.google.gwt.user.client.Element;
 30  
 import com.google.gwt.user.client.History;
 31  
 import com.google.gwt.user.client.Timer;
 32  
 import com.google.gwt.user.client.Window;
 33  
 import com.google.gwt.user.client.ui.Hidden;
 34  
 
 35  
 /**
 36  
  * The HistoryManager is responsible for handling history events and navigating within the application.  Leverages
 37  
  * GWT history class.
 38  
  * <br><br>
 39  
  * History tokens which appear in the url follow this format: 
 40  
  * <br>/HOME/CURRICULUM_HOME/VIEW_COURSE/VIEW&docId=<id>&idType=<type>/BRIEF
 41  
  * <br>In this example, the location is app -> home controller -> curriculum home controller -> view course controller ->
 42  
  * view controller with docId and idType specified for use in its view context - > brief view
 43  
  * 
 44  
  * 
 45  
  * @author Kuali Student Team
 46  
  * @see History
 47  
  */
 48  
 /**
 49  
  * @author Kuali Student Team
 50  
  *
 51  
  */
 52  0
 public class HistoryManager {
 53  0
     private static final NavigationEventMonitor monitor = new NavigationEventMonitor();
 54  0
         public static String VIEW_ATR = "view";
 55  
     private static Controller root;
 56  0
     private static boolean logNavigationHistory = true;
 57  
     private static Locations locations;
 58  
     
 59  
     /**
 60  
      * Binds a controller to this HistoryManager that specifies the top level controller for the application.
 61  
      * The locations passed in are used for determining navigable locations.
 62  
      * 
 63  
      * @param controller
 64  
      * @param views
 65  
      */
 66  
     public static void bind(Controller controller, Locations views) {
 67  0
             locations = views;
 68  0
         root = controller;
 69  0
         root.addApplicationEventHandler(NavigationEvent.TYPE, monitor);
 70  0
         History.addValueChangeHandler(new ValueChangeHandler<String>() {
 71  
 
 72  
             @Override
 73  
             public void onValueChange(ValueChangeEvent<String> event) {
 74  0
                 String token = event.getValue();
 75  0
                 if (token != null) {
 76  0
                     root.onHistoryEvent(token);
 77  
                 }
 78  0
             }
 79  
             
 80  
         });
 81  0
     }
 82  
     
 83  
     public static String[] splitHistoryStack(String historyStack){
 84  0
             return historyStack.split("/");
 85  
     }
 86  
     
 87  
     /**
 88  
      * Gets the property map from the history token passed in.  Properties specified by 
 89  
      * "&propertyName=propertyValue" in the url token.
 90  
      * 
 91  
      * @param token
 92  
      * @return
 93  
      */
 94  
     public static Map<String, String> getTokenMap(String token){
 95  0
             Map<String, String> pairs = new HashMap<String, String>();
 96  0
             String[] arr = token.split("&");
 97  0
             for (String s : arr) {
 98  0
                     if(s.contains("=")){
 99  0
                             String[] tmp = s.split("=");
 100  0
                             if(tmp.length == 2){
 101  0
                                     pairs.put(tmp[0], tmp[1]);
 102  
                             }
 103  0
                     }
 104  
                     else{
 105  
                             //view name do not have = sign required; for better readability
 106  
                             //putting a token without = sign as the view key
 107  0
                             pairs.put("view", s);
 108  
                     }
 109  
             }
 110  0
             return pairs;
 111  
     }
 112  
     
 113  
     /**
 114  
      * Gets the next piece of the history token to be interpreted split on "/"
 115  
      * 
 116  
      * @param historyStack
 117  
      * @return
 118  
      */
 119  
     public static String nextHistoryStack(String historyStack){
 120  0
             String[] arr= historyStack.split("/", 2);
 121  0
             if(arr.length == 2){
 122  0
                     return arr[1];
 123  
             }
 124  
             else{
 125  0
                     return "";
 126  
             }
 127  
     }
 128  
     
 129  
     /**
 130  
      * Reads the url history token and navigates to the appropriate location.  History following the
 131  
      * "#" takes precedence. Url can also be formatted in a parameter format following the "?" symbol using
 132  
      * the "view" parameter for the view path to navigate to, but must exist in Locations passed into this
 133  
      * HistoryManager.
 134  
      */
 135  
     public static void processWindowLocation(){
 136  0
             boolean navigateSuccess = false;
 137  0
             Element loc = DOM.getElementById("locationHash");
 138  
             //String value = loc.getValue().trim();
 139  0
             String value = loc.getAttribute("value");
 140  0
             if(value != null && !value.equals("") && value.startsWith("/")){
 141  0
                     navigate(value);
 142  
             }
 143  0
             else if(Window.Location.getQueryString() != null && 
 144  
                             !Window.Location.getQueryString().isEmpty()){
 145  0
                     String view = Window.Location.getParameter(VIEW_ATR);
 146  0
                     String docId = Window.Location.getParameter(ViewContext.ID_ATR);
 147  0
                     String idType = Window.Location.getParameter(ViewContext.ID_TYPE_ATR);
 148  0
                     if (view != null){
 149  0
                         String path = locations.getLocation(view);
 150  0
                         if(docId != null){
 151  0
                             if(path != null){
 152  0
                                 ViewContext context = new ViewContext();
 153  0
                                 if (idType != null){
 154  0
                                     context.setIdType(idType);
 155  
                                 }
 156  0
                                 context.setId(docId);   
 157  0
                                 setAttributes(context);
 158  0
                                 navigate(path, context);
 159  0
                                 navigateSuccess = true;
 160  0
                             }
 161  
                         } else {
 162  0
                             navigate(path);
 163  0
                             navigateSuccess = true;
 164  
                         }
 165  
                     }
 166  
             }
 167  0
             if(!navigateSuccess){
 168  0
                     navigate(Window.Location.getHash().trim());
 169  
             }
 170  0
     }
 171  
     
 172  
     /**
 173  
      * Add the parameters from the request on the context.
 174  
      * @param context
 175  
      */
 176  
     private static void setAttributes(ViewContext context){ 
 177  0
         for (String key : Window.Location.getParameterMap().keySet()){
 178  0
             if ((!key.equals(VIEW_ATR)) && (!key.equals(ViewContext.ID_ATR)) && (!key.equals(ViewContext.ID_TYPE_ATR))){
 179  0
                 context.setAttribute(key, Window.Location.getParameter(key));
 180  
             }
 181  
         }
 182  0
     }
 183  
     
 184  
     /**
 185  
      * Navigate to the path specified including any parameters needed by the views.  Paths must begin
 186  
      * with / and parameters following view enum names within that path in the format "&name=value"
 187  
      * @param path
 188  
      */
 189  
     public static void navigate(String path){
 190  0
             if(path != null && !path.isEmpty() && path.startsWith("/")){
 191  0
                     logNavigationHistory = false;
 192  0
                     root.onHistoryEvent(path);
 193  0
                     logHistoryChange();
 194  0
                     logNavigationHistory = true;
 195  
             }
 196  0
     }
 197  
     
 198  
     /**
 199  
      * Collects the current history stack based on user location in the app
 200  
      * 
 201  
      * @return
 202  
      */
 203  
     public static String collectHistoryStack() {
 204  0
         String result = null;
 205  0
         if (root != null){
 206  0
             result = root.collectHistory("");
 207  
         }
 208  0
                 if(result == null){
 209  0
                         result = "";
 210  
                 }
 211  0
         return result;
 212  
     }
 213  
     
 214  
     /**
 215  
      * Logs the current location to the history stack and updates the breadcrumb manager as appropriate
 216  
      * 
 217  
      */
 218  
     public static void logHistoryChange() {
 219  0
         String historyStack = collectHistoryStack();
 220  0
         if(historyStack.endsWith("/")){
 221  0
                 historyStack = historyStack.substring(0, historyStack.length()-1);
 222  
         }
 223  0
         String currentToken = History.getToken();
 224  0
         if(!currentToken.equals(historyStack)){
 225  0
                 History.newItem(historyStack, false);
 226  
         }
 227  0
         BreadcrumbManager.updateLinks(historyStack);
 228  0
     }
 229  
     
 230  0
     private static class NavigationEventMonitor implements NavigationEventHandler{
 231  
         private static final int EVENT_DELAY = 100;
 232  0
         private long lastEvent = -1;
 233  
         
 234  0
         private final Timer timer = new Timer() {
 235  
 
 236  
             @Override
 237  
             public void run() {
 238  0
                 if (lastEvent < (System.currentTimeMillis()-EVENT_DELAY)) {
 239  0
                     this.cancel();
 240  0
                     lastEvent = -1;
 241  0
                     logHistoryChange();
 242  
                 }
 243  0
             }
 244  
             
 245  
         };
 246  
         
 247  
         @Override
 248  
         public void onNavigationEvent(NavigationEvent event) {
 249  0
                 if(logNavigationHistory){
 250  0
                         logHistoryChange();
 251  
                 }
 252  
                 else{
 253  0
                         String historyStack = collectHistoryStack();
 254  0
                         BreadcrumbManager.updateLinks(historyStack);
 255  0
                         HistoryManager.setLogNavigationHistory(true);
 256  
                 }
 257  0
         }
 258  
     }
 259  
     
 260  
     /**
 261  
      * Passing in false, turns off history tracking.  History tracking is turned back on by the default 
 262  
      * controller implementation after a navigation is completed.
 263  
      * 
 264  
      * @param log
 265  
      */
 266  
     public static void setLogNavigationHistory(boolean log){
 267  0
             logNavigationHistory = log;
 268  0
     }
 269  
 
 270  
         /**
 271  
          * Navigate to the path passed in with the context passed in appended to it appropriately.  The view context
 272  
          * passed in will be appended to the url in the history token/address bar
 273  
          * 
 274  
          * @param path
 275  
          * @param context
 276  
          */
 277  
         public static void navigate(String path, ViewContext context) {
 278  0
                 path = appendContext(path, context);
 279  0
                 navigate(path);
 280  
                 
 281  0
         }
 282  
 
 283  
         /**
 284  
          * Appends the context to the path passed safely in a format the navigate command will recognize
 285  
          * @param path
 286  
          * @param context
 287  
          * @return
 288  
          */
 289  
         public static String appendContext(String path, ViewContext context) {
 290  0
                 if(context.getId() != null && !context.getId().isEmpty()){
 291  0
                         path = path + "&" + ViewContext.ID_ATR + "=" + context.getId();
 292  
                 }
 293  0
                 if(context.getIdType() != null){
 294  0
                         path = path + "&" + ViewContext.ID_TYPE_ATR + "=" + context.getIdType();
 295  
                 }
 296  0
                 if(!context.getAttributes().isEmpty()){
 297  0
                         Map<String,String> attributes = context.getAttributes();
 298  0
                         Iterator<String> it = attributes.keySet().iterator();
 299  0
                         while(it.hasNext()){
 300  0
                                 String key = it.next();
 301  0
                                 String value = attributes.get(key);
 302  0
                                 path = path + "&" + key + "=" + value;
 303  0
                         }
 304  
                 }
 305  
                 //TODO add the ability for view context to add a variety of additional attributes
 306  0
                 return path;
 307  
         }
 308  
 }