/* * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net> * Distributed under the terms of either: * - the common development and distribution license (CDDL), v1.0; or * - the GNU Lesser General Public License, v2.1 or later */ package winstone; /** * Encapsulates the parsing of URL patterns, as well as the mapping of a * url pattern to a servlet instance * * @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a> * @version $Id: Mapping.java,v 1.9 2007/04/23 02:55:35 rickknowles Exp $ */ public class Mapping implements java.util.Comparator { public static final int EXACT_PATTERN = 1; public static final int FOLDER_PATTERN = 2; public static final int EXTENSION_PATTERN = 3; public static final int DEFAULT_SERVLET = 4; public static final String STAR = "*"; public static final String SLASH = "/"; private String urlPattern; private String linkName; // used to map filters to a specific servlet by // name private String mappedTo; private int patternType; private boolean isPatternFirst; // ie is this a blah* pattern, not *blah // (extensions only) protected Mapping(String mappedTo) { this.mappedTo = mappedTo; } /** * Factory constructor method - this parses the url pattern into pieces we can use to match * against incoming URLs. */ public static Mapping createFromURL(String mappedTo, String pattern) { if ((pattern == null) || (mappedTo == null)) throw new WinstoneException(Launcher.RESOURCES.getString( "Mapping.InvalidMount", new String[] { mappedTo, pattern })); // Compatibility hacks - add a leading slash if one is not found and not // an extension mapping if (!pattern.equals("") && !pattern.startsWith(STAR) && !pattern.startsWith(SLASH)) { pattern = SLASH + pattern; } else if (pattern.equals(STAR)) { Logger.log(Logger.WARNING, Launcher.RESOURCES, "Mapping.RewritingStarMount"); pattern = SLASH + STAR; } Mapping me = new Mapping(mappedTo); int firstStarPos = pattern.indexOf(STAR); int lastStarPos = pattern.lastIndexOf(STAR); int patternLength = pattern.length(); // check for default servlet, ie mapping = exactly / if (pattern.equals(SLASH)) { me.urlPattern = ""; me.patternType = DEFAULT_SERVLET; } else if (firstStarPos == -1) { me.urlPattern = pattern; me.patternType = EXACT_PATTERN; } // > 1 star = error else if (firstStarPos != lastStarPos) throw new WinstoneException(Launcher.RESOURCES.getString( "Mapping.InvalidMount", new String[] { mappedTo, pattern })); // check for folder style mapping (ends in /*) else if (pattern.indexOf(SLASH + STAR) == (patternLength - (SLASH + STAR).length())) { me.urlPattern = pattern.substring(0, pattern.length() - (SLASH + STAR).length()); me.patternType = FOLDER_PATTERN; } // check for non-extension match else if (pattern.indexOf(SLASH) != -1) throw new WinstoneException(Launcher.RESOURCES.getString( "Mapping.InvalidMount", new String[] { mappedTo, pattern })); // check for extension match at the beginning (eg *blah) else if (firstStarPos == 0) { me.urlPattern = pattern.substring(STAR.length()); me.patternType = EXTENSION_PATTERN; me.isPatternFirst = false; } // check for extension match at the end (eg blah*) else if (firstStarPos == (patternLength - STAR.length())) { me.urlPattern = pattern.substring(0, patternLength - STAR.length()); me.patternType = EXTENSION_PATTERN; me.isPatternFirst = true; } else throw new WinstoneException(Launcher.RESOURCES.getString( "Mapping.InvalidMount", new String[] { mappedTo, pattern })); Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES, "Mapping.MappedPattern", new String[] { mappedTo, pattern }); return me; } /** * Factory constructor method - this turns a servlet name into a mapping element */ public static Mapping createFromLink(String mappedTo, String linkName) { if ((linkName == null) || (mappedTo == null)) throw new WinstoneException(Launcher.RESOURCES.getString( "Mapping.InvalidLink", new String[] { mappedTo, linkName })); Mapping me = new Mapping(mappedTo); me.linkName = linkName; return me; } public int getPatternType() { return this.patternType; } public String getUrlPattern() { return this.urlPattern; } public String getMappedTo() { return this.mappedTo; } public String getLinkName() { return this.linkName; } /** * Try to match this pattern against the incoming url * * @param inputPattern The URL we want to check for a match * @param servletPath An empty stringbuffer for the servletPath of a successful match * @param pathInfo An empty stringbuffer for the pathInfo of a successful match * @return true if the match is successful */ public boolean match(String inputPattern, StringBuffer servletPath, StringBuffer pathInfo) { switch (this.patternType) { case FOLDER_PATTERN: if (inputPattern.startsWith(this.urlPattern + '/') || inputPattern.equals(this.urlPattern)) { if (servletPath != null) servletPath.append(WinstoneRequest.decodeURLToken(this.urlPattern)); if (pathInfo != null) pathInfo.append(WinstoneRequest.decodeURLToken(inputPattern.substring(this.urlPattern.length()))); return true; } else return false; case EXTENSION_PATTERN: // Strip down to the last item in the path int slashPos = inputPattern.lastIndexOf(SLASH); if ((slashPos == -1) || (slashPos == inputPattern.length() - 1)) return false; String fileName = inputPattern.substring(slashPos + 1); if ((this.isPatternFirst && fileName.startsWith(this.urlPattern)) || (!this.isPatternFirst && fileName.endsWith(this.urlPattern))) { if (servletPath != null) servletPath.append(WinstoneRequest.decodeURLToken(inputPattern)); return true; } else return false; case EXACT_PATTERN: if (inputPattern.equals(this.urlPattern)) { if (servletPath != null) servletPath.append(WinstoneRequest.decodeURLToken(inputPattern)); return true; } else return false; case DEFAULT_SERVLET: if (servletPath != null) servletPath.append(WinstoneRequest.decodeURLToken(inputPattern)); return true; default: return false; } } /** * Used to compare two url patterns. Always sorts so that lowest pattern * type then longest path come first. */ public int compare(Object objOne, Object objTwo) { Mapping one = (Mapping) objOne; Mapping two = (Mapping) objTwo; Integer intOne = new Integer(one.getPatternType()); Integer intTwo = new Integer(two.getPatternType()); int order = -1 * intOne.compareTo(intTwo); if (order != 0) { return order; } if (one.getLinkName() != null) { // servlet name mapping - just alphabetical sort return one.getLinkName().compareTo(two.getLinkName()); } else { return -1 * one.getUrlPattern().compareTo(two.getUrlPattern()); } } public String toString() { return this.linkName != null ? "Link:" + this.linkName : "URLPattern:type=" + this.patternType + ",pattern=" + this.urlPattern; } }