All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.velocity.tools.view.BreadcrumbTool Maven / Gradle / Ivy

The newest version!
package org.apache.velocity.tools.view;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.apache.velocity.tools.Scope;
import org.apache.velocity.tools.config.DefaultKey;
import org.apache.velocity.tools.config.ValidScope;
import org.apache.velocity.tools.generic.LocaleConfig;
import org.apache.velocity.tools.generic.ValueParser;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;

/**
 * 

Helper tool to display a navigation breadcrumb to the end user.

*

It relies on the asumption that your URI hierarchy corresponds to the navigation hierarchy you want to present * to your users, each level having its landing index page. Please note that this asumption is considered a *good practice*.

*

Each intermediate path element becomes a navigation element, and there is an additional ending navigation element * when the page is not an index page.

*

For instance, if the URI is `/colors/red/nuances.html`, the provided path elements will be:

*
    *
  • 'colors', pointing towards '/colors/index.html'
  • *
  • 'red', pointing towards '/colors/red/index.html'
  • *
  • 'nuances', pointing towards '/colors/red/nuances.html'
  • *
*

The filename extension (`html` in the above example) is inferred from found URIs.

*

By default, the displayed name of a navigation element is the name of its corresponding path element with all * '_' replaced by spaces, transformed to lowercase.

*

You can customize this displayed name and the URL of every path element using tools properties:

*

 *     
 * 
*

where 'colors' refers to the /colors/ path element. The name and destination of the * root path element can be changed with 'home.name' and 'home.url'.

*

If you provide a name property prefixed with '?', the breadcrumb tool will use the value of the corresponding * query parameter as navigation element name, and will keep this query parameter in the navigation element URL.

*

You can also define your own rules programmatically by subclassing BreadcrumbTool and override the * boolean customize(NavigationElement, HttpServletRequest) method, which will be called for every * navigation element. Returning false will skip this navigation element from the breadcrumb.

*

Inside a template, you would either render directly the default resulting HTML fragment with:

*
$breadcrumb
*

which would produce simething like:

*
<a href="/index.vhtml">home</a>&nbsp;&gt;&nbsp;<a href="/colors/all.vhtml">My Colors</a>&nbsp;&gt;&nbsp;nuances
*

or you would iterate through navigation elements for a better control of the output with:

*

 * 
*

And of course, you would supply the necessary CSS, for instance:

*

 * #breadcrumb { font-size: smaller; }
 * #breadcrumb a { color: #4183c4; text-decoration: none; }
 * #breadcrumb .divider { color: rgba(0, 0, 0, 0.4); vertical-align: baseline; margin: 0 0.2rem; }
 * 
* * @version $$ * @author Claude Brisson * @since 3.1 * */ @DefaultKey("breadcrumb") @ValidScope(Scope.REQUEST) public class BreadcrumbTool extends LocaleConfig implements Iterable { /** * Navigation elements for the current URI */ protected List navigationElements = null; /** * Current request */ protected HttpServletRequest request = null; /** * Class representing a navigation element */ public static class NavigationElement { /** * destination URL */ private String url; /** * Displayed name */ private String name; /** * Constructor * @param url destination URL * @param name displayed name */ public NavigationElement(String url, String name) { this.url = url; if (name.endsWith(".vhtml")) name = name.substring(0, name.indexOf(".vhtml")); this.name = name; } /** * Destination URL getter * @return destination URL */ public String getUrl() { return url; } /** * Displayed name getter * @return displayed name */ public String getName() { return name; } /** * Displayed name setter * @param name displayed name */ public void setName(String name) { this.name = name; } /** * Destination URL setter * @param url destination url */ public void setUrl(String url) { this.url = url; } } /** * Current request setter * @param request */ public void setRequest(HttpServletRequest request) { this.request = request; } /** *

Let the user customize programmatically the name and URL of a specific element.

*

For instance, one can do use query parameters to customize the displayed name or target URL.

* @param navElem navigation element * @param request initial request * @return true (default value) to include this navigation element, false to skip it */ protected boolean customize(NavigationElement navElem, HttpServletRequest request) { // default implementation does nothing return true; } /** * Automatic default formatting of URI elements */ protected String formatElementName(String elem) { return elem.replace('_', ' ').toLowerCase(getLocale()); } /** * Configuration */ @Override protected void configure(ValueParser config) { if (request == null) { getLog().warn("cannot build breadcrumb: no provided request"); return; } String encoding = Optional.ofNullable(request.getCharacterEncoding()).orElse("UTF-8"); String uri = request.getRequestURI(); try { uri = URLDecoder.decode(uri, encoding); } catch (UnsupportedEncodingException uee) { getLog().error("Cannot decode URI using encoding {}", encoding); return; } // infer extension String ext = getExtension(uri); // deduce default index page String index = "index." + ext; if ("/".equals(uri)) uri = "/" + index; String elements[] = uri.split("/"); navigationElements = new ArrayList(); StringBuilder builder = new StringBuilder(); for (String elem : elements) { // for each URI path element builder.append(elem); String currentPath = builder.toString(); if (index.equals(elem)) continue; if (elem.endsWith('.' + ext)) { elem = elem.substring(0, elem.length() - (ext.length() + 1)); } else { currentPath = currentPath + '/' + index; if (elem.length() == 0) elem = "home"; } String name = builder.length() == 0 ? "home" : formatElementName(elem); NavigationElement navElem = new NavigationElement(currentPath, name); // give a chance to subclasses to customize an item if (customize(navElem, request)) { navigationElements.add(navElem); // check configuration Object obj = config.get(elem); if (obj != null && obj instanceof ValueParser) { String queryParamName = null; String queryParamValue = null; ValueParser values = (ValueParser) obj; // customize navigation element name String newName = values.getString("name"); if (newName != null) { if (newName.startsWith("?")) { queryParamName = newName.substring(1); queryParamValue = request.getParameter(queryParamName); if (queryParamValue != null) { navElem.setName(queryParamValue); } } else { navElem.setName(newName); } } // customize navigation element URL String newURL = values.getString("url"); if (queryParamValue == null) { if (newURL != null) { navElem.setUrl(newURL); } } else { if (newURL == null) { newURL = navElem.getUrl(); } newURL += newURL.indexOf('?') == -1 ? '?' : '&'; newURL += queryParamName + '=' + queryParamValue; navElem.setUrl(newURL); } } } builder.append('/'); } } /** * Navigation elements iteration * @return Iterator over navigation elements */ @Override public Iterator iterator() { return navigationElements.iterator(); } /** * Utility method to return URI file extension * @param filename * @return */ protected static String getExtension(String filename) { if (filename == null) { return null; } int extensionPos = filename.lastIndexOf('.'); int lastSeparator = filename.lastIndexOf('/'); int index = lastSeparator > extensionPos ? -1 : extensionPos; if (index == -1) { return ""; } else { return filename.substring(index + 1); } } /** * Default concatenation of navigation elements, separated by '>' * @return string representation */ @Override public String toString() { StringBuilder builder = new StringBuilder(); int len = navigationElements.size(); for (int i = 0; i < len; ++i) { NavigationElement elem = navigationElements.get(i); if (i > 0) { builder.append(" > "); } if (i < len - 1) { builder.append(""); } builder.append(elem.getName()); if (i < len - 1) { builder.append(""); } } return builder.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy