blob: db2c39fe0533bbb2ebfaf6750ece9d98ef3db9eb [file] [log] [blame]
// Copyright 2006, 2007, 2008 The Apache Software Foundation
//
// 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.apache.tapestry5.internal.services;
import org.apache.tapestry5.EventContext;
import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.internal.URLEventContext;
import org.apache.tapestry5.services.*;
import java.io.IOException;
/**
* Dispatches incoming requests for render requests. Render requests consist of either just a logical page name (case
* insensitive) or a logical page name plus additional context. Because of this structure, it take a little bit of work
* to identify the split point between the page name and the context.
*/
public class PageRenderDispatcher implements Dispatcher
{
private final ComponentClassResolver componentClassResolver;
private final PageRenderRequestHandler handler;
private final ContextValueEncoder contextValueEncoder;
public PageRenderDispatcher(ComponentClassResolver componentClassResolver, PageRenderRequestHandler handler,
ContextValueEncoder contextValueEncoder)
{
this.componentClassResolver = componentClassResolver;
this.handler = handler;
this.contextValueEncoder = contextValueEncoder;
}
public boolean dispatch(Request request, final Response response) throws IOException
{
// The extended name may include a page activation context. The trick is
// to figure out where the logical page name stops and where the
// activation context begins. Further, strip out the leading slash.
String path = request.getPath();
// TAPESTRY-1343: Sometimes path is the empty string (it should always be at least a slash,
// but Tomcat may return the empty string for a root context request).
String extendedName = path.length() == 0 ? path : path.substring(1);
// Ignore trailing slashes in the path.
while (extendedName.endsWith("/"))
extendedName = extendedName.substring(0, extendedName.length() - 1);
int slashx = extendedName.length();
boolean atEnd = true;
while (slashx > 0)
{
String pageName = extendedName.substring(0, slashx);
String pageActivationContext = atEnd ? "" :
extendedName.substring(slashx + 1);
if (process(pageName, pageActivationContext)) return true;
// Work backwards, splitting at the next slash.
slashx = extendedName.lastIndexOf('/', slashx - 1);
atEnd = false;
}
// OK, maybe its all page activation context for the root Index page.
return process("", extendedName);
}
private boolean process(String pageName, String pageActivationContext) throws IOException
{
if (!componentClassResolver.isPageName(pageName)) return false;
String[] values = convertActivationContext(pageActivationContext);
EventContext activationContext
= new URLEventContext(contextValueEncoder, values);
PageRenderRequestParameters parameters = new PageRenderRequestParameters(pageName, activationContext);
handler.handle(parameters);
return true;
}
/**
* Converts the "extra path", the portion after the page name (and after the slash seperating the page name from the
* activation context) into an array of strings. LinkFactory and friends URL encode each value, so we URL decode the
* value (we assume that page names are "URL safe").
*/
private String[] convertActivationContext(String extraPath)
{
if (extraPath.length() == 0) return new String[0];
String[] context = TapestryInternalUtils.splitPath(extraPath);
for (int i = 0; i < context.length; i++)
{
context[i] = TapestryInternalUtils.unescapePercentAndSlash(context[i]);
}
return context;
}
}