/* Copyright 2005-2006 Tim Fennell
*
* 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 net.sourceforge.stripes.tag;
import net.sourceforge.stripes.exception.StripesJspException;
import net.sourceforge.stripes.util.CryptoUtil;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.util.UrlBuilder;
import net.sourceforge.stripes.controller.StripesConstants;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.HashMap;
/**
* Abstract support class for generating links. Used by both the LinkTag (which generates
* regular {@literal <a href=""/>} style links) and the UrlTag which is a rough similie
* of the JSTL url tag.
*
* @author Tim Fennell
* @since Stripes 1.4
*/
public abstract class LinkTagSupport extends HtmlTagSupport implements ParameterizableTag {
private static final Log log = Log.getInstance(LinkTagSupport.class);
/** Initial value for fields to indicate they were not set by a tag attribute. */
private static final String VALUE_NOT_SET = "VALUE_NOT_SET";
private Map<String,Object> parameters = new HashMap<String,Object>();
private String event = VALUE_NOT_SET;
private Object beanclass;
private String url;
private String anchor;
private boolean addSourcePage = false;
private Boolean prependContext;
/**
* Gets the URL that is supplied by the user/developer on the page. This is the basis
* for constructing the eventual URL that the tag generates.
*
* @return the URL that was supplied by the user
*/
public String getUrl() { return url; }
/**
* Sets the URL that is supplied by the user/developer on the page. This is the basis
* for constructing the eventual URL that the tag generates.
*
* @param url the URL supplied on the page
*/
public void setUrl(String url) { this.url = url; }
/**
* Used by stripes:param tags (and possibly other tags at some distant point in
* the future) to add a parameter to the parent link tag.
*
* @param name the name of the parameter(s) to add
* @param valueOrValues
*/
public void addParameter(String name, Object valueOrValues) {
this.parameters.put(name, valueOrValues);
}
/** Retrieves the parameter values set on the tag. */
public Map<String,Object> getParameters() {
return this.parameters;
}
/**
* Clears all existing parameters. Subclasses should be careful to call this in
* doEndTag() to ensure that parameter state is cleared between pooled used of the tag.
*/
public void clearParameters() {
this.parameters.clear();
}
/** Sets the (optional) event name that the link will trigger. */
public void setEvent(String event) { this.event = event; }
/** Gets the (optional) event name that the link will trigger. */
public String getEvent() { return event; }
/**
* Sets the bean class (String FQN or Class) to generate a link for. Provides an
* alternative to using href for targeting ActionBeans.
*
* @param beanclass the name of an ActionBean class, or Class object
*/
public void setBeanclass(Object beanclass) { this.beanclass = beanclass; }
/**
* Gets the bean class (String FQN or Class) to generate a link for. Provides an
* alternative to using href for targeting ActionBeans.
*
* @return the name of an ActionBean class, or Class object
*/
public Object getBeanclass() { return beanclass; }
/**
* Gets the anchor element that is appended at the end of the URL.
*
* @return the anchor element
*/
public String getAnchor() {
return anchor;
}
/**
* Sets the anchor element that is appended at the end of the URL. If the provided URL (set
* using <code>setUrl</code> method) already contains the anchor, then the anchor specified by
* this attribute takes precedence.
*
* @param anchor the name of the anchor to set
*/
public void setAnchor(String anchor) {
this.anchor = anchor;
}
/**
* Get the flag that indicates if the _sourcePage parameter should be
* appended to the URL.
*
* @return true if _sourcePage is to be appended to the URL; false otherwise
*/
public boolean isAddSourcePage() { return addSourcePage; }
/**
* Set the flag that indicates if the _sourcePage parameter should be
* appended to the URL.
*/
public void setAddSourcePage(boolean addSourcePage) { this.addSourcePage = addSourcePage; }
/** Get the flag that indicates if the application context should be included in the URL. */
public Boolean isPrependContext() { return prependContext; }
/** Set the flag that indicates if the application context should be included in the URL. */
public void setPrependContext(Boolean prependContext) { this.prependContext = prependContext; }
/**
* Returns the base URL that should be used for building the link. This is derived from
* the 'beanclass' attribute if it is set, else from the 'url' attribute.
*
* @return the preferred base URL for the link
* @throws StripesJspException if a beanclass attribute was specified, but does not identify
* an existing ActionBean
*/
protected String getPreferredBaseUrl() throws StripesJspException {
// If the beanclass attribute was supplied we'll prefer that to an href
if (this.beanclass != null) {
String beanHref = getActionBeanUrl(beanclass);
if (beanHref == null) {
throw new StripesJspException("The value supplied for the 'beanclass' attribute "
+ "does not represent a valid ActionBean. The value supplied was '" +
this.beanclass + "'. If you're prototyping, or your bean isn't ready yet " +
"and you want this exception to go away, just use 'href' for now instead.");
}
else {
return beanHref;
}
}
else {
return getUrl();
}
}
/**
* Builds the URL based on the information currently stored in the tag. Ensures that all
* parameters are appended into the URL, along with event name if necessary and the source
* page information.
*
* @return the fully constructed URL
* @throws StripesJspException if the base URL cannot be determined
*/
protected String buildUrl() throws StripesJspException {
HttpServletRequest request = (HttpServletRequest) getPageContext().getRequest();
HttpServletResponse response = (HttpServletResponse) getPageContext().getResponse();
// Add all the parameters and reset the href attribute; pass to false here because
// the HtmlTagSupport will HtmlEncode the ampersands for us
String base = getPreferredBaseUrl();
UrlBuilder builder = new UrlBuilder(pageContext.getRequest().getLocale(), base, false);
if (this.event != VALUE_NOT_SET) {
builder.setEvent(this.event == null || this.event.length() < 1 ? null : this.event);
}
if (addSourcePage) {
builder.addParameter(StripesConstants.URL_KEY_SOURCE_PAGE,
CryptoUtil.encrypt(request.getServletPath()));
}
if (this.anchor != null) {
builder.setAnchor(anchor);
}
builder.addParameters(this.parameters);
// Prepend the context path, but only if the user didn't already
String url = builder.toString();
String contextPath = request.getContextPath();
if (contextPath.length() > 1) {
boolean prepend = prependContext != null && prependContext
|| prependContext == null && beanclass != null
|| prependContext == null && url.startsWith("/") && !url.startsWith(contextPath);
if (prepend) {
if (url.startsWith("/"))
url = contextPath + url;
else
log.warn("Use of prependContext=\"true\" is only valid with a URL that starts with \"/\"");
}
}
return response.encodeURL(url);
}
}