/* * gvNIX is an open source tool for rapid application development (RAD). * Copyright (C) 2010 Generalitat Valenciana * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package __TOP_LEVEL_PACKAGE__.web.menu; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.TreeSet; import java.util.regex.Pattern; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; /** * Base class for gvNIX Context menu Strategy based on URL matching. * <p> * This Abstract class provides the way to locate the menu item by comparing * current request URL with menu items. * <p> * This takes care if current URL if from internal forwarding. This method could be * used by layouts libraries (like apache Tiles). * <p> * If a item has a URL based in an <i>EL</i> Expression this will be evaluated * before compare it. * * @author <a href="http://www.disid.com">DISID Corporation S.L.</a> made for * <a href="http://www.dgti.gva.es">General Directorate for Information Technologies (DGTI)</a> */ public abstract class BaseURLContextMenuStrategy implements ContextMenuStrategy { private static final Pattern EL_PATTERN = Pattern.compile(".*[$][{].*[}]"); /** * Get current {@link MenuItem} based on the <code>request</code> URL * * @param request * @param jspContext * @param menu * @return current {@link MenuItem} or <code>null</code> if not match found. */ protected MenuItem getItemFromCurrentURL(HttpServletRequest request, ServletContext jspContext, Menu menu) { // If request comes from a internal server forward we'll need original // request URI // See Servlert 2.4 specification, section SRV.8.4.2 for more // information String path = (String) request .getAttribute("javax.servlet.forward.request_uri"); String contextPath; if (path == null) { // If isn't a forwarded request so we'll use the original URI path = request.getRequestURI(); contextPath = request.getContextPath(); } else { contextPath = (String) request .getAttribute("javax.servlet.forward.context_path"); } if (path.startsWith(contextPath)) { path = path.substring(contextPath.length()); } Enumeration<String> paramNamesEnum = request.getParameterNames(); TreeSet<String> paramNames = new TreeSet<String>(); // We create a new Map because request.getParameterMap() is a map of // String[]. // In java 1.5 application will throw a ClassCastException if you try // set // ParameterMap's value into a String variable. // In java 1.6 works fine. Map<String, String> paramValues = new HashMap<String, String>(request .getParameterMap().size()); String paramName; while (paramNamesEnum.hasMoreElements()) { paramName = paramNamesEnum.nextElement(); paramNames.add(paramName); paramValues.put(paramName, request.getParameter(paramName)); } class StackItem { Iterator<MenuItem> iterator; public StackItem(Iterator<MenuItem> childIterator) { this.iterator = childIterator; } } Stack<StackItem> stack = new Stack<StackItem>(); stack.push(new StackItem(menu.getChildren().iterator())); StackItem curStackItem; MenuItem curItem; while (!stack.isEmpty()) { curStackItem = stack.pop(); while (curStackItem.iterator.hasNext()) { curItem = curStackItem.iterator.next(); if (isSameDestination(request, jspContext, path, paramNames, paramValues, curItem.getUrl())) { return curItem; } if (curItem.hasChildren()) { stack.push(curStackItem); stack.push(new StackItem(curItem.getChildren().iterator())); break; } } } return null; } /** * Compares request URL with {@link MenuItem}'s destination. This will ignore * parameters values if {@link MenuItem}'s destination if based on <i>EL</i> * expression. Inherit class could override this method to change compare * behavior. * * @param request * @param jspContext * @param path1 * @param paramNames * @param paramValues * @param destination2 {@link MenuItem}'s destination * @return <code>true</code> if match */ protected boolean isSameDestination(HttpServletRequest request, ServletContext jspContext, String path1, Set<String> paramNames, Map<String, String> paramValues, String destination2) { if (destination2 == null || destination2.isEmpty()) { return false; } int questionCharIndes2 = destination2.indexOf('?'); // Url's files have the same size. String path2; String query2; if (questionCharIndes2 < 0) { path2 = destination2; query2 = null; } else { path2 = destination2.substring(0, questionCharIndes2); query2 = destination2.substring(questionCharIndes2 + 1); } if (path1.length() != path2.length()) { return false; } if (!path1.equalsIgnoreCase(path2)) { // Url's files are different return false; } // Compare parameters without values if (paramNames.isEmpty() && query2 != null) { return false; } else if (query2 == null && !paramNames.isEmpty()) { return false; } String[] paramTokens2 = new String[] {}; if (query2 != null) { paramTokens2 = query2.split("[&]"); } Set<String> paramNames2 = new TreeSet<String>(); Map<String, String> paramValues2 = new HashMap<String, String>(); String paramName; String paramValue; int equalCharIndex; for (String paramToken : paramTokens2) { equalCharIndex = paramToken.indexOf('='); if (equalCharIndex < 0) { paramName = paramToken; paramValue = null; } else { paramName = paramToken.substring(0, equalCharIndex); paramValue = paramToken.substring(equalCharIndex + 1, paramToken.length()); } // TODO case insensitive compare: check servlet specification paramNames2.add(paramName.toLowerCase()); paramValues2.put(paramName, paramValue); } // check for duplicated parameters if (paramNames.size() != paramNames2.size()) { return false; } Iterator<String> iterParamNames1 = paramNames.iterator(); Iterator<String> iterParamNames2 = paramNames2.iterator(); String paramName2; String paramValue2; while (iterParamNames1.hasNext()) { paramName = iterParamNames1.next(); paramName2 = iterParamNames2.next(); if (!paramName.equals(paramName2)) { return false; } paramValue = paramValues.get(paramName); paramValue2 = paramValues2.get(paramName2); if (paramValue2 != null && EL_PATTERN.matcher(paramValue2).matches()) { continue; } if (!hasText(paramValue)) { if (hasText(paramValue2)) { return false; } continue; } if (!paramValue.equals(paramValue2)) { return false; } } return true; } private boolean hasText(String var) { if (var == null) { return false; } return var.trim().isEmpty(); } }