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/58
3.357
HistoryManager$1
0%
0/5
0%
0/2
3.357
HistoryManager$NavigationEventMonitor
0%
0/9
0%
0/2
3.357
HistoryManager$NavigationEventMonitor$1
0%
0/6
0%
0/2
3.357
 
 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 && docId != null && idType != null){
 149  0
                             String path = locations.getLocation(view);
 150  0
                             if(path != null){
 151  0
                                     ViewContext context = new ViewContext();
 152  0
                                     context.setIdType(idType);
 153  0
                                     context.setId(docId);
 154  0
                                     navigate(path, context);
 155  0
                                     navigateSuccess = true;
 156  
                             }
 157  0
                     }else if(view != null && docId != null){
 158  0
                             String path = locations.getLocation(view);
 159  0
                             if(path != null){
 160  0
                                     ViewContext context = new ViewContext();
 161  0
                                     context.setId(docId);
 162  0
                                     navigate(path, context);
 163  0
                                     navigateSuccess = true;
 164  
                             }
 165  0
                     }
 166  0
                     else if(view != null){
 167  0
                             String path = locations.getLocation(view);
 168  0
                             navigate(path);
 169  0
                                 navigateSuccess = true;
 170  
                     }
 171  
             }
 172  0
             if(!navigateSuccess){
 173  0
                     navigate(Window.Location.getHash().trim());
 174  
             }
 175  0
     }
 176  
     
 177  
     /**
 178  
      * Navigate to the path specified including any parameters needed by the views.  Paths must begin
 179  
      * with / and parameters following view enum names within that path in the format "&name=value"
 180  
      * @param path
 181  
      */
 182  
     public static void navigate(String path){
 183  0
             if(path != null && !path.isEmpty() && path.startsWith("/")){
 184  0
                     logNavigationHistory = false;
 185  0
                     root.onHistoryEvent(path);
 186  0
                     logHistoryChange();
 187  0
                     logNavigationHistory = true;
 188  
             }
 189  0
     }
 190  
     
 191  
     /**
 192  
      * Collects the current history stack based on user location in the app
 193  
      * 
 194  
      * @return
 195  
      */
 196  
     public static String collectHistoryStack() {
 197  0
         String result = root.collectHistory("");
 198  0
                 if(result == null){
 199  0
                         result = "";
 200  
                 }
 201  0
         return result;
 202  
     }
 203  
     
 204  
     /**
 205  
      * Logs the current location to the history stack and updates the breadcrumb manager as appropriate
 206  
      * 
 207  
      */
 208  
     public static void logHistoryChange() {
 209  0
         String historyStack = collectHistoryStack();
 210  0
         if(historyStack.endsWith("/")){
 211  0
                 historyStack = historyStack.substring(0, historyStack.length()-1);
 212  
         }
 213  0
         String currentToken = History.getToken();
 214  0
         if(!currentToken.equals(historyStack)){
 215  0
                 History.newItem(historyStack, false);
 216  
         }
 217  0
         BreadcrumbManager.updateLinks(historyStack);
 218  0
     }
 219  
     
 220  0
     private static class NavigationEventMonitor implements NavigationEventHandler{
 221  
         private static final int EVENT_DELAY = 100;
 222  0
         private long lastEvent = -1;
 223  
         
 224  0
         private final Timer timer = new Timer() {
 225  
 
 226  
             @Override
 227  
             public void run() {
 228  0
                 if (lastEvent < (System.currentTimeMillis()-EVENT_DELAY)) {
 229  0
                     this.cancel();
 230  0
                     lastEvent = -1;
 231  0
                     logHistoryChange();
 232  
                 }
 233  0
             }
 234  
             
 235  
         };
 236  
         
 237  
         @Override
 238  
         public void onNavigationEvent(NavigationEvent event) {
 239  0
                 if(logNavigationHistory){
 240  0
                         logHistoryChange();
 241  
                 }
 242  
                 else{
 243  0
                         String historyStack = collectHistoryStack();
 244  0
                         BreadcrumbManager.updateLinks(historyStack);
 245  0
                         HistoryManager.setLogNavigationHistory(true);
 246  
                 }
 247  0
         }
 248  
     }
 249  
     
 250  
     /**
 251  
      * Passing in false, turns off history tracking.  History tracking is turned back on by the default 
 252  
      * controller implementation after a navigation is completed.
 253  
      * 
 254  
      * @param log
 255  
      */
 256  
     public static void setLogNavigationHistory(boolean log){
 257  0
             logNavigationHistory = log;
 258  0
     }
 259  
 
 260  
         /**
 261  
          * Navigate to the path passed in with the context passed in appended to it appropriately.  The view context
 262  
          * passed in will be appended to the url in the history token/address bar
 263  
          * 
 264  
          * @param path
 265  
          * @param context
 266  
          */
 267  
         public static void navigate(String path, ViewContext context) {
 268  0
                 path = appendContext(path, context);
 269  0
                 navigate(path);
 270  
                 
 271  0
         }
 272  
 
 273  
         /**
 274  
          * Appends the context to the path passed safely in a format the navigate command will recognize
 275  
          * @param path
 276  
          * @param context
 277  
          * @return
 278  
          */
 279  
         public static String appendContext(String path, ViewContext context) {
 280  0
                 if(context.getId() != null && !context.getId().isEmpty()){
 281  0
                         path = path + "&" + ViewContext.ID_ATR + "=" + context.getId();
 282  
                 }
 283  0
                 if(context.getIdType() != null){
 284  0
                         path = path + "&" + ViewContext.ID_TYPE_ATR + "=" + context.getIdType();
 285  
                 }
 286  0
                 if(!context.getAttributes().isEmpty()){
 287  0
                         Map<String,String> attributes = context.getAttributes();
 288  0
                         Iterator<String> it = attributes.keySet().iterator();
 289  0
                         while(it.hasNext()){
 290  0
                                 String key = it.next();
 291  0
                                 String value = attributes.get(key);
 292  0
                                 path = path + "&" + key + "=" + value;
 293  0
                         }
 294  
                 }
 295  
                 //TODO add the ability for view context to add a variety of additional attributes
 296  0
                 return path;
 297  
         }
 298  
 }