Coverage Report - org.kuali.rice.krad.uif.view.History
 
Classes in this File Line Coverage Branch Coverage Complexity
History
0%
0/157
0%
0/86
3.35
 
 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.krad.uif.view;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.apache.log4j.Logger;
 20  
 import org.kuali.rice.krad.service.DataObjectMetaDataService;
 21  
 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
 22  
 import org.kuali.rice.krad.uif.UifConstants;
 23  
 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
 24  
 import org.kuali.rice.krad.uif.util.ViewModelUtils;
 25  
 import org.kuali.rice.krad.web.form.UifFormBase;
 26  
 
 27  
 import javax.servlet.http.HttpServletRequest;
 28  
 import java.io.Serializable;
 29  
 import java.io.UnsupportedEncodingException;
 30  
 import java.net.URLDecoder;
 31  
 import java.net.URLEncoder;
 32  
 import java.util.ArrayList;
 33  
 import java.util.Enumeration;
 34  
 import java.util.List;
 35  
 
 36  
 /**
 37  
  * History class used to keep track of views visited so they can be displayed in the ui
 38  
  * as breadcrumbs - both as homeward path and history path interpretations
 39  
  *
 40  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 41  
  */
 42  
 public class History implements Serializable {
 43  
     private static final long serialVersionUID = -8279297694371557335L;
 44  0
     private static final Logger LOG = Logger.getLogger(History.class);
 45  
 
 46  
     public static final String ENTRY_TOKEN = "$";
 47  
     public static final String VAR_TOKEN = ",";
 48  
 
 49  
     private boolean appendHomewardPath;
 50  
     private boolean appendPassedHistory;
 51  
 
 52  
     private HistoryEntry current;
 53  
 
 54  
     private List<HistoryEntry> homewardPath;
 55  
     private List<HistoryEntry> historyEntries;
 56  
 
 57  0
     public History() {
 58  0
         historyEntries = new ArrayList<HistoryEntry>();
 59  0
     }
 60  
 
 61  
     /**
 62  
      * Gets the predetermined homeward path for this view's history.
 63  
      * This is set by the same property in the view's Breadcrumbs configuration.
 64  
      *
 65  
      * @return the homewardPath
 66  
      */
 67  
     public List<HistoryEntry> getHomewardPath() {
 68  0
         return this.homewardPath;
 69  
     }
 70  
 
 71  
     /**
 72  
      * @param homewardPath the homewardPath to set
 73  
      */
 74  
     public void setHomewardPath(List<HistoryEntry> homewardPath) {
 75  0
         this.homewardPath = homewardPath;
 76  0
     }
 77  
 
 78  
     /**
 79  
      * Gets a list of the current HistoryEntries not including the current entry.
 80  
      * This list does not include the "&history=" query parameter on each HistoryEntry's
 81  
      * url variable.  For HistoryEntries that include history information to be passed to the
 82  
      * view they are retrieving, getGeneratedBreadcrumbs is used.
 83  
      *
 84  
      * @return the history
 85  
      */
 86  
     public List<HistoryEntry> getHistoryEntries() {
 87  0
         return this.historyEntries;
 88  
     }
 89  
 
 90  
     /**
 91  
      * @param history the history to set
 92  
      */
 93  
     public void setHistoryEntries(List<HistoryEntry> history) {
 94  0
         this.historyEntries = history;
 95  0
     }
 96  
 
 97  
     /**
 98  
      * Gets the current view's HistoryEntry.
 99  
      * This does not include the "&history=" query parameter on its
 100  
      * url variable.  For the HistoryEntry that includes history information to be passed
 101  
      * on the url it is retrieving, getGeneratedCurrentBreadcrumb is used.
 102  
      *
 103  
      * @return the current
 104  
      */
 105  
     public HistoryEntry getCurrent() {
 106  0
         return this.current;
 107  
     }
 108  
 
 109  
     /**
 110  
      * @param current the current to set
 111  
      */
 112  
     private void setCurrent(String viewId, String pageId, String title, String url, String formKey) {
 113  0
         HistoryEntry entry = new HistoryEntry(viewId, pageId, title, url, formKey);
 114  0
         current = entry;
 115  0
     }
 116  
 
 117  
     /**
 118  
      * @param current the current to set
 119  
      */
 120  
     public void setCurrent(HistoryEntry current) {
 121  0
         this.current = current;
 122  0
     }
 123  
 
 124  
     /**
 125  
      * Takes in the encoded history query parameter string passed on the url and parses it to create
 126  
      * the list of historyEntries.  It will also append any homeward path if appendHomewardPath is true.  This
 127  
      * append will happen after the passedHistory entries are appended so it will not make sense to use both settings
 128  
      * in most cases.
 129  
      *
 130  
      * @param parameterString
 131  
      */
 132  
     public void buildHistoryFromParameterString(String parameterString) {
 133  0
         if (StringUtils.isNotEmpty(parameterString)) {
 134  
             try {
 135  0
                 parameterString = URLDecoder.decode(parameterString, "UTF-8");
 136  0
             } catch (UnsupportedEncodingException e) {
 137  0
                 LOG.error("Error decoding history param", e);
 138  0
             }
 139  
 
 140  0
             historyEntries = new ArrayList<HistoryEntry>();
 141  0
             if (appendPassedHistory) {
 142  0
                 String[] historyTokens = parameterString.split("\\" + ENTRY_TOKEN);
 143  0
                 for (String token : historyTokens) {
 144  0
                     String[] params = token.split(VAR_TOKEN);
 145  0
                     pushToHistory(params[0], params[1], params[2], params[3], params[4]);
 146  
                 }
 147  
             }
 148  
         }
 149  
 
 150  0
         if (appendHomewardPath) {
 151  0
             historyEntries.addAll(homewardPath);
 152  
         }
 153  0
     }
 154  
 
 155  
     /**
 156  
      * Gets the encoded and tokenized history parameter string that is representative of the HistoryEntries
 157  
      * currently in History and includes the current view's HistoryEntry.  This parameter should be appended on any
 158  
      * appropriate links which perform view swapping.
 159  
      *
 160  
      * @return
 161  
      */
 162  
     public String getHistoryParameterString() {
 163  0
         String historyString = "";
 164  0
         for (HistoryEntry e : historyEntries) {
 165  0
             if (historyEntries.indexOf(e) == 0) {
 166  0
                 historyString = historyString + e.toParam();
 167  
             } else {
 168  0
                 historyString = historyString + ENTRY_TOKEN + e.toParam();
 169  
             }
 170  
         }
 171  
 
 172  
         // add current
 173  0
         if (current != null) {
 174  0
             if (historyString.equals("")) {
 175  0
                 historyString = historyString + current.toParam();
 176  
             } else {
 177  0
                 historyString = historyString + ENTRY_TOKEN + current.toParam();
 178  
             }
 179  
         }
 180  
 
 181  
         try {
 182  0
             historyString = URLEncoder.encode(historyString, "UTF-8");
 183  0
         } catch (Exception e) {
 184  0
             LOG.error("Error encoding history param", e);
 185  0
         }
 186  
 
 187  0
         return historyString;
 188  
     }
 189  
 
 190  
     /**
 191  
      * Generates a list of HistoryEntries that can be used as breadcrumbs by the breadcrumb widget.  This
 192  
      * method appends the appropriate history information on the HistoryEntry url variables so when a view is requested
 193  
      * its history can be regenerated for use in its breadcrumbs.  It also sets the the passed showHome variable to
 194  
      * false to prevent showing the homeward path more than once (as it is passed through the history
 195  
      * variable backwards). This does not include the current HistoryEntry as a breadcrumb.
 196  
      *
 197  
      * @return
 198  
      */
 199  
     public List<HistoryEntry> getGeneratedBreadcrumbs() {
 200  0
         List<HistoryEntry> breadcrumbs = new ArrayList<HistoryEntry>();
 201  0
         for (int i = 0; i < historyEntries.size(); i++) {
 202  0
             if (i == 0) {
 203  0
                 breadcrumbs.add(copyEntry(historyEntries.get(i)));
 204  
             } else {
 205  0
                 HistoryEntry breadcrumb = copyEntry(historyEntries.get(i));
 206  0
                 String historyParam = "";
 207  0
                 for (int j = 0; j < i; j++) {
 208  0
                     historyParam = historyParam + ENTRY_TOKEN + historyEntries.get(j).toParam();
 209  
                 }
 210  0
                 historyParam = historyParam.replaceFirst("\\" + ENTRY_TOKEN, "");
 211  
                 try {
 212  0
                     historyParam = URLEncoder.encode(historyParam, "UTF-8");
 213  0
                 } catch (Exception e) {
 214  0
                     LOG.error("Error encoding history param", e);
 215  0
                 }
 216  
 
 217  0
                 String url = "";
 218  0
                 if (breadcrumb.getUrl().contains("?")) {
 219  0
                     url = breadcrumb.getUrl() + "&" + UifConstants.UrlParams.HISTORY + "=" + historyParam;
 220  
                 } else {
 221  0
                     url = breadcrumb.getUrl() + "?" + UifConstants.UrlParams.HISTORY + "=" + historyParam;
 222  
                 }
 223  
 
 224  0
                 breadcrumb.setUrl(url);
 225  0
                 breadcrumbs.add(breadcrumb);
 226  
             }
 227  
         }
 228  
 
 229  0
         return breadcrumbs;
 230  
     }
 231  
 
 232  
     /**
 233  
      * Gets the current HistoryEntry in the breadcrumb format described in getGeneratedBreadcrumbs
 234  
      *
 235  
      * @return
 236  
      */
 237  
     public HistoryEntry getGeneratedCurrentBreadcrumb() {
 238  0
         HistoryEntry breadcrumb = copyEntry(current);
 239  0
         String historyParam = "";
 240  0
         for (int j = 0; j < historyEntries.size(); j++) {
 241  0
             historyParam = historyParam + ENTRY_TOKEN + historyEntries.get(j).toParam();
 242  
         }
 243  0
         historyParam = historyParam.replaceFirst("\\" + ENTRY_TOKEN, "");
 244  
 
 245  
         try {
 246  0
             historyParam = URLEncoder.encode(historyParam, "UTF-8");
 247  0
         } catch (Exception e) {
 248  0
             LOG.error("Error encoding history param", e);
 249  0
         }
 250  
 
 251  0
         String url = "";
 252  0
         if (breadcrumb.getUrl().contains("?")) {
 253  0
             url = breadcrumb.getUrl() + "&" + UifConstants.UrlParams.HISTORY + "=" + historyParam;
 254  
         } else {
 255  0
             url = breadcrumb.getUrl() + "?" + UifConstants.UrlParams.HISTORY + "=" + historyParam;
 256  
         }
 257  0
         breadcrumb.setUrl(url);
 258  
 
 259  0
         return breadcrumb;
 260  
     }
 261  
 
 262  
     /**
 263  
      * Copies a HistoryEntry, for use during breadcrumb generation.
 264  
      *
 265  
      * @param e
 266  
      * @return
 267  
      */
 268  
     private HistoryEntry copyEntry(HistoryEntry e) {
 269  0
         return new HistoryEntry(e.getViewId(), e.getPageId(), e.getTitle(), e.getUrl(), e.getFormKey());
 270  
     }
 271  
 
 272  
     /**
 273  
      * Pushes the information passed in to history.
 274  
      * Note: currently only used internally in the class - be cautious about its external use.
 275  
      *
 276  
      * @param viewId
 277  
      * @param pageId
 278  
      * @param title
 279  
      * @param url
 280  
      * @param formKey
 281  
      */
 282  
     public void pushToHistory(String viewId, String pageId, String title, String url, String formKey) {
 283  0
         HistoryEntry entry = new HistoryEntry(viewId, pageId, title, url, formKey);
 284  0
         historyEntries.add(entry);
 285  0
     }
 286  
 
 287  
     /**
 288  
      * When this is set to true, the homeward path will be appended.
 289  
      * Note:  For most cases this should only be on during the first view load.
 290  
      * This setting is set automatically in most cases.
 291  
      *
 292  
      * @param appendHomewardPath the appendHomewardPath to set
 293  
      */
 294  
     public void setAppendHomewardPath(boolean appendHomewardPath) {
 295  0
         this.appendHomewardPath = appendHomewardPath;
 296  0
     }
 297  
 
 298  
     /**
 299  
      * @return the appendHomewardPath
 300  
      */
 301  
     public boolean isAppendHomewardPath() {
 302  0
         return appendHomewardPath;
 303  
     }
 304  
 
 305  
     /**
 306  
      * Appends the passed history as each different view is shown.  This setting should be used when displaying
 307  
      * passed history is relevant to the user (ie inquiry/lookup chains).  This setting is set automatically in
 308  
      * most cases.
 309  
      *
 310  
      * @param appendPassedHistory the appendPassedHistory to set
 311  
      */
 312  
     public void setAppendPassedHistory(boolean appendPassedHistory) {
 313  0
         this.appendPassedHistory = appendPassedHistory;
 314  0
     }
 315  
 
 316  
     /**
 317  
      * @return the appendPassedHistory
 318  
      */
 319  
     public boolean isAppendPassedHistory() {
 320  0
         return appendPassedHistory;
 321  
     }
 322  
 
 323  
     /**
 324  
      * Sets the current HistoryEntry using information from the form and the request.  This history parameter is
 325  
      * extracted out of the url inorder for a "clean" url to be used in history parameter and
 326  
      * breadcrumb generation, as passing history history through the nested urls is unnecessary.
 327  
      *
 328  
      * @param form
 329  
      * @param request
 330  
      */
 331  
     @SuppressWarnings("unchecked")
 332  
     public void setCurrent(UifFormBase form, HttpServletRequest request) {
 333  0
         if (!request.getMethod().equals("POST")) {
 334  0
             boolean showHomeValue = false;
 335  0
             boolean pageIdValue = false;
 336  0
             boolean formKeyValue = false;
 337  
 
 338  0
             String queryString = "";
 339  0
             String url = request.getRequestURL().toString();
 340  
 
 341  
             //remove history attribute
 342  0
             Enumeration<String> params = request.getParameterNames();
 343  0
             while (params.hasMoreElements()) {
 344  0
                 String key = params.nextElement();
 345  0
                 if (!key.equals(UifConstants.UrlParams.HISTORY)) {
 346  0
                     for (String value : request.getParameterValues(key)) {
 347  0
                         queryString = queryString + "&" + key + "=" + value;
 348  
                     }
 349  0
                 } else if (key.equals(UifConstants.UrlParams.PAGE_ID)) {
 350  0
                     pageIdValue = true;
 351  0
                 } else if (key.equals(UifConstants.UrlParams.SHOW_HOME)) {
 352  0
                     showHomeValue = true;
 353  0
                 } else if (key.equals(UifConstants.UrlParams.FORM_KEY)) {
 354  0
                     formKeyValue = true;
 355  
                 }
 356  0
             }
 357  
 
 358  
             //add formKey and pageId to url
 359  0
             if (StringUtils.isNotBlank(form.getFormKey()) && !formKeyValue) {
 360  0
                 queryString = queryString + "&" + UifConstants.UrlParams.FORM_KEY + "=" + form.getFormKey();
 361  
             }
 362  0
             if (StringUtils.isNotBlank(form.getPageId()) && !pageIdValue) {
 363  0
                 queryString = queryString + "&" + UifConstants.UrlParams.PAGE_ID + "=" + form.getPageId();
 364  
             }
 365  0
             if (!showHomeValue) {
 366  0
                 queryString = queryString + "&" + UifConstants.UrlParams.SHOW_HOME + "=false";
 367  
             }
 368  
 
 369  0
             queryString = queryString.replaceFirst("&", "");
 370  
 
 371  0
             if (StringUtils.isNotEmpty(queryString)) {
 372  0
                 url = url + "?" + queryString;
 373  
             }
 374  
 
 375  0
             this.setCurrent(form.getViewId(), form.getPageId(), buildViewTitle(form), url, form.getFormKey());
 376  
         }
 377  0
     }
 378  
 
 379  
     /**
 380  
      * Builds the title for the view to display in history (for example breadcrumbs)
 381  
      *
 382  
      * <p>
 383  
      * Retrieves the viewLabelFieldPropertyName from the view if configured, otherwise attempts
 384  
      * to find the title attribute for the default data object. If view label property is found the
 385  
      * corresponding property value is retrieved and appended to the title for the view
 386  
      * </p>
 387  
      *
 388  
      * TODO: Possibly move so it can be used for the actual view title, not just history
 389  
      *
 390  
      * @param form - form instance containing the view and view data
 391  
      * @return String title string to use
 392  
      */
 393  
     protected String buildViewTitle(UifFormBase form) {
 394  0
         View view = form.getView();
 395  0
         String title = view.getTitle();
 396  
 
 397  
         // may move this into view logic instead in the future if it is required for the view's title (not just breadcrumb)
 398  
         // if so remove this and just use getTitle - this logic would be in performFinalize instead
 399  0
         String viewLabelPropertyName = view.getViewLabelFieldPropertyName();
 400  
 
 401  
         // if view label property name given, try to retrieve the title attribute for the main data object
 402  0
         if (StringUtils.isBlank(viewLabelPropertyName)) {
 403  
             Class<?> dataObjectClass;
 404  0
             if (StringUtils.isNotBlank(view.getDefaultBindingObjectPath())) {
 405  0
                 dataObjectClass = ObjectPropertyUtils.getPropertyType(form, view.getDefaultBindingObjectPath());
 406  
             } else {
 407  0
                 dataObjectClass = view.getFormClass();
 408  
             }
 409  
 
 410  0
             DataObjectMetaDataService mds = KRADServiceLocatorWeb.getDataObjectMetaDataService();
 411  0
             if (dataObjectClass != null) {
 412  0
                 viewLabelPropertyName = mds.getTitleAttribute(dataObjectClass);
 413  
             }
 414  
         }
 415  
 
 416  0
         String viewLabelPropertyPath = "";
 417  0
         if (StringUtils.isNotBlank(viewLabelPropertyName)) {
 418  
             // adjust binding prefix
 419  0
             if (!viewLabelPropertyName.startsWith(UifConstants.NO_BIND_ADJUST_PREFIX)) {
 420  0
                 if (StringUtils.isNotBlank(view.getDefaultBindingObjectPath())) {
 421  0
                     viewLabelPropertyPath = view.getDefaultBindingObjectPath() + "." + viewLabelPropertyName;
 422  
                 }
 423  
             } else {
 424  0
                 viewLabelPropertyPath = StringUtils.removeStart(viewLabelPropertyName,
 425  
                         UifConstants.NO_BIND_ADJUST_PREFIX);
 426  
             }
 427  
         }
 428  
         else {
 429  
             // attempt to get title attribute
 430  
             Class<?> dataObjectClass;
 431  0
             if (StringUtils.isNotBlank(view.getDefaultBindingObjectPath())) {
 432  0
                 dataObjectClass = ViewModelUtils.getObjectClassForMetadata(view, form,
 433  
                         view.getDefaultBindingObjectPath());
 434  
             } else {
 435  0
                 dataObjectClass = view.getFormClass();
 436  
             }
 437  
 
 438  0
             DataObjectMetaDataService mds = KRADServiceLocatorWeb.getDataObjectMetaDataService();
 439  0
             if (dataObjectClass != null) {
 440  0
                 String titleAttribute = mds.getTitleAttribute(dataObjectClass);
 441  0
                 if (StringUtils.isNotBlank(titleAttribute)) {
 442  0
                   viewLabelPropertyPath = view.getDefaultBindingObjectPath() + "." + titleAttribute;
 443  
                 }
 444  
             }
 445  
         }
 446  
 
 447  0
         Object viewLabelPropertyValue = null;
 448  0
         if (StringUtils.isNotBlank(viewLabelPropertyPath)) {
 449  0
             viewLabelPropertyValue = ObjectPropertyUtils.getPropertyValue(form, viewLabelPropertyPath);
 450  
         }
 451  
 
 452  0
         String titleAppend = "";
 453  0
         if (viewLabelPropertyValue != null) {
 454  0
             titleAppend = viewLabelPropertyValue.toString();
 455  
         }
 456  
 
 457  0
         if (StringUtils.isNotBlank(titleAppend) && view.getAppendOption() != null) {
 458  0
             if (view.getAppendOption().equalsIgnoreCase(UifConstants.TitleAppendTypes.DASH)) {
 459  0
                 title = title + " - " + titleAppend;
 460  0
             } else if (view.getAppendOption().equalsIgnoreCase(UifConstants.TitleAppendTypes.PARENTHESIS)) {
 461  0
                 title = title + "(" + titleAppend + ")";
 462  0
             } else if (view.getAppendOption().equalsIgnoreCase(UifConstants.TitleAppendTypes.REPLACE)) {
 463  0
                 title = titleAppend;
 464  
             }
 465  
             //else it is none or blank so no title modification will be used
 466  
         }
 467  
 
 468  0
         return title;
 469  
     }
 470  
 }