/*
* Copyright 2017 OmniFaces
*
* Licensed 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.
*/
package org.omnifaces.facesviews;
import static javax.servlet.RequestDispatcher.FORWARD_SERVLET_PATH;
import static org.omnifaces.facesviews.FacesViews.FACES_VIEWS_ORIGINAL_SERVLET_PATH;
import static org.omnifaces.facesviews.FacesViews.getFacesServletExtensions;
import static org.omnifaces.facesviews.FacesViews.getMappedResources;
import static org.omnifaces.facesviews.FacesViews.getViewHandlerMode;
import static org.omnifaces.facesviews.FacesViews.isScannedViewsAlwaysExtensionless;
import static org.omnifaces.facesviews.FacesViews.stripWelcomeFilePrefix;
import static org.omnifaces.facesviews.ViewHandlerMode.BUILD_WITH_PARENT_QUERY_PARAMETERS;
import static org.omnifaces.util.Faces.getServletContext;
import static org.omnifaces.util.FacesLocal.getRequestAttribute;
import static org.omnifaces.util.FacesLocal.getRequestContextPath;
import static org.omnifaces.util.FacesLocal.getRequestPathInfo;
import static org.omnifaces.util.FacesLocal.getServletContext;
import static org.omnifaces.util.ResourcePaths.getExtension;
import static org.omnifaces.util.ResourcePaths.isExtensionless;
import static org.omnifaces.util.ResourcePaths.stripExtension;
import static org.omnifaces.util.ResourcePaths.stripTrailingSlash;
import static org.omnifaces.util.Utils.coalesce;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.faces.application.ViewHandler;
import javax.faces.application.ViewHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
/**
* View handler that renders an action URL extensionless if a resource is a mapped one, and faces views has been set to
* always render extensionless or if the current request is extensionless, otherwise as-is.
* <p>
* For a guide on FacesViews, please see the <a href="package-summary.html">package summary</a>.
*
* @author Arjan Tijms
* @since 1.3
* @see FacesViews
* @see FacesViewsViewHandlerInstaller
* @see ViewHandlerMode
*/
public class FacesViewsViewHandler extends ViewHandlerWrapper {
private final ViewHandler wrapped;
private final ViewHandlerMode mode;
private final boolean extensionless;
public FacesViewsViewHandler(ViewHandler viewHandler) {
wrapped = viewHandler;
mode = getViewHandlerMode(getServletContext());
extensionless = isScannedViewsAlwaysExtensionless(getServletContext());
}
@Override
public String deriveLogicalViewId(FacesContext context, String viewId) {
Map<String, String> mappedResources = getMappedResources(getServletContext(context));
return mappedResources.containsKey(viewId) ? super.deriveLogicalViewId(context, viewId) : viewId;
}
@Override
public String getActionURL(FacesContext context, String viewId) {
String actionURL = super.getActionURL(context, viewId);
ServletContext servletContext = getServletContext(context);
Map<String, String> mappedResources = getMappedResources(servletContext);
if (mappedResources.containsKey(viewId) && (extensionless || isOriginalViewExtensionless(context))) {
// User has requested to always render extensionless, or the requested viewId was mapped and the current
// request is extensionless; render the action URL extensionless as well.
String pathInfo = context.getViewRoot().getViewId().equals(viewId) ? coalesce(getRequestPathInfo(context), "") : "";
if (mode == BUILD_WITH_PARENT_QUERY_PARAMETERS) {
return getRequestContextPath(context) + stripExtension(viewId) + pathInfo + getQueryString(actionURL);
}
else {
actionURL = removeExtension(servletContext, actionURL, viewId);
return pathInfo.isEmpty() ? actionURL : (stripTrailingSlash(actionURL) + pathInfo + getQueryString(actionURL));
}
}
// Not a resource we mapped or not a forwarded one, take the version from the parent view handler.
return actionURL;
}
@Override
public ViewHandler getWrapped() {
return wrapped;
}
private static boolean isOriginalViewExtensionless(FacesContext context) {
String originalViewId = getRequestAttribute(context, FORWARD_SERVLET_PATH);
if (originalViewId == null) {
originalViewId = getRequestAttribute(context, FACES_VIEWS_ORIGINAL_SERVLET_PATH);
}
return isExtensionless(originalViewId);
}
private static String removeExtension(ServletContext servletContext, String actionURL, String viewId) {
Set<String> extensions = getFacesServletExtensions(servletContext);
if (!isExtensionless(viewId)) {
String viewIdExtension = getExtension(viewId);
// TODO Is this necessary? Which cases does this cover?
if (!extensions.contains(viewIdExtension)) {
extensions = new HashSet<>(extensions);
extensions.add(viewIdExtension);
}
}
String resource = actionURL.split("\\?", 2)[0];
for (String extension : extensions) {
if (resource.endsWith(extension)) {
return stripWelcomeFilePrefix(servletContext, resource.substring(0, resource.length() - extension.length()));
}
}
return actionURL;
}
private static String getQueryString(String resource) {
int questionMarkPos = resource.indexOf('?');
return (questionMarkPos != -1) ? resource.substring(questionMarkPos) : "";
}
}