001/*
002 * Copyright 2002-2014 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.servlet.mvc;
018
019import java.util.Map;
020import java.util.concurrent.ConcurrentHashMap;
021import javax.servlet.http.HttpServletRequest;
022
023import org.springframework.util.StringUtils;
024import org.springframework.web.servlet.HandlerMapping;
025
026/**
027 * Simple {@code Controller} implementation that transforms the virtual
028 * path of a URL into a view name and returns that view.
029 *
030 * <p>Can optionally prepend a {@link #setPrefix prefix} and/or append a
031 * {@link #setSuffix suffix} to build the viewname from the URL filename.
032 *
033 * <p>Find some examples below:
034 * <ol>
035 * <li>{@code "/index" -> "index"}</li>
036 * <li>{@code "/index.html" -> "index"}</li>
037 * <li>{@code "/index.html"} + prefix {@code "pre_"} and suffix {@code "_suf" -> "pre_index_suf"}</li>
038 * <li>{@code "/products/view.html" -> "products/view"}</li>
039 * </ol>
040 *
041 * <p>Thanks to David Barri for suggesting prefix/suffix support!
042 *
043 * @author Alef Arendsen
044 * @author Juergen Hoeller
045 * @author Rob Harrop
046 * @see #setPrefix
047 * @see #setSuffix
048 */
049public class UrlFilenameViewController extends AbstractUrlViewController {
050
051        private String prefix = "";
052
053        private String suffix = "";
054
055        /** Request URL path String --> view name String */
056        private final Map<String, String> viewNameCache = new ConcurrentHashMap<String, String>(256);
057
058
059        /**
060         * Set the prefix to prepend to the request URL filename
061         * to build a view name.
062         */
063        public void setPrefix(String prefix) {
064                this.prefix = (prefix != null ? prefix : "");
065        }
066
067        /**
068         * Return the prefix to prepend to the request URL filename.
069         */
070        protected String getPrefix() {
071                return this.prefix;
072        }
073
074        /**
075         * Set the suffix to append to the request URL filename
076         * to build a view name.
077         */
078        public void setSuffix(String suffix) {
079                this.suffix = (suffix != null ? suffix : "");
080        }
081
082        /**
083         * Return the suffix to append to the request URL filename.
084         */
085        protected String getSuffix() {
086                return this.suffix;
087        }
088
089
090        /**
091         * Returns view name based on the URL filename,
092         * with prefix/suffix applied when appropriate.
093         * @see #extractViewNameFromUrlPath
094         * @see #setPrefix
095         * @see #setSuffix
096         */
097        @Override
098        protected String getViewNameForRequest(HttpServletRequest request) {
099                String uri = extractOperableUrl(request);
100                return getViewNameForUrlPath(uri);
101        }
102
103        /**
104         * Extract a URL path from the given request,
105         * suitable for view name extraction.
106         * @param request current HTTP request
107         * @return the URL to use for view name extraction
108         */
109        protected String extractOperableUrl(HttpServletRequest request) {
110                String urlPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
111                if (!StringUtils.hasText(urlPath)) {
112                        urlPath = getUrlPathHelper().getLookupPathForRequest(request);
113                }
114                return urlPath;
115        }
116
117        /**
118         * Returns view name based on the URL filename,
119         * with prefix/suffix applied when appropriate.
120         * @param uri the request URI; for example {@code "/index.html"}
121         * @return the extracted URI filename; for example {@code "index"}
122         * @see #extractViewNameFromUrlPath
123         * @see #postProcessViewName
124         */
125        protected String getViewNameForUrlPath(String uri) {
126                String viewName = this.viewNameCache.get(uri);
127                if (viewName == null) {
128                        viewName = extractViewNameFromUrlPath(uri);
129                        viewName = postProcessViewName(viewName);
130                        this.viewNameCache.put(uri, viewName);
131                }
132                return viewName;
133        }
134
135        /**
136         * Extract the URL filename from the given request URI.
137         * @param uri the request URI; for example {@code "/index.html"}
138         * @return the extracted URI filename; for example {@code "index"}
139         */
140        protected String extractViewNameFromUrlPath(String uri) {
141                int start = (uri.charAt(0) == '/' ? 1 : 0);
142                int lastIndex = uri.lastIndexOf('.');
143                int end = (lastIndex < 0 ? uri.length() : lastIndex);
144                return uri.substring(start, end);
145        }
146
147        /**
148         * Build the full view name based on the given view name
149         * as indicated by the URL path.
150         * <p>The default implementation simply applies prefix and suffix.
151         * This can be overridden, for example, to manipulate upper case
152         * / lower case, etc.
153         * @param viewName the original view name, as indicated by the URL path
154         * @return the full view name to use
155         * @see #getPrefix()
156         * @see #getSuffix()
157         */
158        protected String postProcessViewName(String viewName) {
159                return getPrefix() + viewName + getSuffix();
160        }
161
162}