You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by nb...@apache.org on 2008/06/05 21:36:06 UTC
svn commit: r663715 - in /velocity/tools/trunk:
src/main/java/org/apache/velocity/tools/generic/
src/main/java/org/apache/velocity/tools/view/
src/test/java/org/apache/velocity/tools/ xdocs/
Author: nbubna
Date: Thu Jun 5 12:36:05 2008
New Revision: 663715
URL: http://svn.apache.org/viewvc?rev=663715&view=rev
Log:
add generic version of LinkTool
Added:
velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java (with props)
velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LinkToolTests.java (with props)
Modified:
velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/tools.xml
velocity/tools/trunk/src/main/java/org/apache/velocity/tools/view/LinkTool.java
velocity/tools/trunk/xdocs/changes.xml
velocity/tools/trunk/xdocs/dependencies.xml
velocity/tools/trunk/xdocs/generic.project.xml
velocity/tools/trunk/xdocs/generic.xml
Added: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java?rev=663715&view=auto
==============================================================================
--- velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java (added)
+++ velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java Thu Jun 5 12:36:05 2008
@@ -0,0 +1,1125 @@
+package org.apache.velocity.tools.generic;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.velocity.runtime.log.Log;
+import org.apache.velocity.tools.Scope;
+import org.apache.velocity.tools.ToolContext;
+import org.apache.velocity.tools.config.DefaultKey;
+import org.apache.velocity.tools.config.ValidScope;
+
+/**
+ * <p>The LinkTool provides many methods to work with URIs and can help you:
+ * <ul>
+ * <li>construct full URIs (opaque, absolute or relative)</li>
+ * <li>encode and decode URLs (part or whole)</li>
+ * <li>retrieve path info for the current request</li>
+ * <li>and more..</li>
+ * </ul></p>
+ *
+ * <p>This GenericTools (i.e. non-servlet based) version of LinkTool
+ * is largely based upon the same API and behavior as the older
+ * VelocityView version, with a few differences, particularly in
+ * internal representation and query handling. You can expect that
+ * in the future work will be done to more closely align the APIs.
+ * It is likely that the VelocityView version will become a subclass
+ * of this version that adds on servlet-awareness and related features.
+ * For now, though, they are entirely separate but similar tools.
+ * </p>
+ *
+ * <p>The LinkTool is somewhat special in that nearly all public methods return
+ * a new instance of LinkTool. This facilitates greatly the repeated use
+ * of the LinkTool in Velocity and leads to an elegant syntax.</p>
+ *
+ * <p><pre>
+ * Template example(s):
+ * #set( $base = $link.relative('MyPage.vm').anchor('view') )
+ * <a href="$base.param('select','this')">this</a>
+ * <a href="$base.param('select','that')">that</a>
+ *
+ * Toolbox configuration:
+ * <tools>
+ * <toolbox scope="request">
+ * <tool class="org.apache.velocity.tools.generic.LinkTool"
+ * uri="http://velocity.apache.org/tools/devel/"/>
+ * </toolbox>
+ * </tools>
+ * </pre></p>
+ *
+ * @author Nathan Bubna
+ * @since VelocityTools 2.0
+ * @version $Id: LinkTool.java 601976 2007-12-07 03:50:51Z nbubna $
+ */
+@DefaultKey("link")
+@ValidScope(Scope.REQUEST)
+public class LinkTool extends AbstractLockConfig implements Cloneable
+{
+ /** Standard HTML delimiter for query data ('&') */
+ public static final String HTML_QUERY_DELIMITER = "&";
+
+ /** XHTML delimiter for query data ('&amp;') */
+ public static final String XHTML_QUERY_DELIMITER = "&";
+
+ public static final String DEFAULT_CHARSET = "UTF-8";
+ public static final String DEFAULT_SCHEME = "http";
+ public static final String SECURE_SCHEME = "https";
+
+ public static final String URI_KEY = "uri";
+ public static final String SCHEME_KEY = "scheme";
+ public static final String USER_KEY = "user";
+ public static final String HOST_KEY = "host";
+ public static final String PORT_KEY = "port";
+ public static final String PATH_KEY = ToolContext.PATH_KEY;
+ public static final String QUERY_KEY = "params";
+ public static final String FRAGMENT_KEY = "anchor";
+ public static final String CHARSET_KEY = "charset";
+ public static final String XHTML_MODE_KEY = "xhtml";
+
+ protected Log LOG;
+ protected String scheme;
+ protected String user;
+ protected String host;
+ protected int port;
+ protected String path;
+ protected String query;
+ protected String fragment;
+ protected String charset;
+ protected String queryDelim;
+
+ private boolean opaque;
+ private final LinkTool self;
+
+
+ /**
+ * Default constructor. Tool must be initialized before use.
+ */
+ public LinkTool()
+ {
+ scheme = null;
+ user = null;
+ host = null;
+ port = -1;
+ path = null;
+ query = null;
+ fragment = null;
+ charset = DEFAULT_CHARSET;
+ queryDelim = XHTML_QUERY_DELIMITER;
+ opaque = false;
+ self = this;
+ }
+
+ private void logError(String msg, Throwable t)
+ {
+ if (this.LOG != null)
+ {
+ this.LOG.error("LinkTool: "+msg, t);
+ }
+ }
+
+
+ // --------------------------------------- Setup Methods -------------
+
+ protected void configure(ValueParser props)
+ {
+ this.LOG = (Log)props.getValue(ToolContext.LOG_KEY);
+
+ String link = props.getString(URI_KEY);
+ if (link != null)
+ {
+ setFromURI(link);
+ }
+
+ String schm = props.getString(SCHEME_KEY);
+ if (schm != null)
+ {
+ setScheme(schm);
+ }
+ String info = props.getString(USER_KEY);
+ if (info != null)
+ {
+ setUserInfo(info);
+ }
+ String hst = props.getString(HOST_KEY);
+ if (hst != null)
+ {
+ setHost(hst);
+ }
+ Integer prt = props.getInteger(PORT_KEY);
+ if (prt != null)
+ {
+ setPort(prt.intValue());
+ }
+ String pth = props.getString(PATH_KEY);
+ if (pth != null)
+ {
+ setPath(pth);
+ }
+ String params = props.getString(QUERY_KEY);
+ if (params != null)
+ {
+ setQuery(params);
+ }
+ String anchor = props.getString(FRAGMENT_KEY);
+ if (anchor != null)
+ {
+ setFragment(anchor);
+ }
+
+ String chrst = props.getString(CHARSET_KEY);
+ if (chrst != null)
+ {
+ this.charset = chrst;
+ }
+
+ Boolean xhtml = props.getBoolean(XHTML_MODE_KEY);
+ if (xhtml != null)
+ {
+ setXHTML(xhtml);
+ }
+ }
+
+ /**
+ * <p>Controls the delimiter used for separating query data pairs.
+ * By default, the standard '&' character is used.</p>
+ * <p>This is not exposed to templates as this decision is best not
+ * made at that level.</p>
+ * <p>Subclasses may easily override the init() method to set this
+ * appropriately and then call super.init()</p>
+ *
+ * @param useXhtml if true, the XHTML query data delimiter ('&amp;')
+ * will be used. if false, then '&' will be used.
+ * @see <a href="http://www.w3.org/TR/xhtml1/#C_12">Using Ampersands in Attribute Values (and Elsewhere)</a>
+ */
+ protected void setXHTML(boolean xhtml)
+ {
+ queryDelim = (xhtml) ? XHTML_QUERY_DELIMITER : HTML_QUERY_DELIMITER;
+ }
+
+ /**
+ * Equivalent to clone, but with no checked exceptions.
+ * If for some unfathomable reason clone() doesn't work,
+ * this will throw a RuntimeException.
+ */
+ protected LinkTool duplicate()
+ {
+ try
+ {
+ return (LinkTool)this.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ String msg = "Could not properly clone " + getClass();
+ logError(msg, e);
+ throw new RuntimeException(msg, e);
+ }
+ }
+
+ protected void setScheme(Object obj)
+ {
+ if (obj == null)
+ {
+ this.scheme = null;
+ }
+ else
+ {
+ this.scheme = String.valueOf(obj);
+ if (scheme.endsWith(":"))
+ {
+ scheme = scheme.substring(0, scheme.length() - 1);
+ }
+ }
+ }
+
+ protected void setUserInfo(Object obj)
+ {
+ this.user = obj == null ? null : String.valueOf(obj);
+ }
+
+ protected void setHost(Object obj)
+ {
+ this.host = obj == null ? null : String.valueOf(obj);
+ }
+
+ protected void setPort(Object obj)
+ {
+ if (obj == null)
+ {
+ this.port = -1;
+ }
+ else if (obj instanceof Number)
+ {
+ this.port = ((Number)obj).intValue();
+ }
+ else
+ {
+ try
+ {
+ this.port = Integer.parseInt(String.valueOf(obj));
+ }
+ catch (NumberFormatException nfe)
+ {
+ logError("Could convert '"+obj+"' to int", nfe);
+ this.port = -2; // use this to mean error
+ }
+ }
+ }
+
+ protected void setPath(Object obj)
+ {
+ if (obj == null)
+ {
+ this.path = null;
+ }
+ else
+ {
+ this.path = String.valueOf(obj);
+ if (!this.opaque && !path.startsWith("/"))
+ {
+ this.path = '/' + this.path;
+ }
+ }
+ }
+
+ protected void appendPath(Object obj)
+ {
+ if (obj != null && !this.opaque)
+ {
+ setPath(combinePath(getPath(), String.valueOf(obj)));
+ }
+ }
+
+ protected String combinePath(String start, String end)
+ {
+ if (end == null)
+ {
+ return start;
+ }
+ if (start == null)
+ {
+ return end;
+ }
+
+ // make sure we don't get // or nothing between start and end
+ boolean startEnds = start.endsWith("/");
+ boolean endStarts = end.startsWith("/");
+ if (startEnds ^ endStarts) //one
+ {
+ return start + end;
+ }
+ else if (startEnds & endStarts) //both
+ {
+ return start + end.substring(1, end.length());
+ }
+ else //neither
+ {
+ return start + '/' + end;
+ }
+ }
+
+ protected void setQuery(Object obj)
+ {
+ if (obj == null)
+ {
+ this.query = null;
+ }
+ else
+ {
+ this.query = String.valueOf(obj);
+ if (query.startsWith("?"))
+ {
+ this.query = query.substring(1, query.length());
+ }
+ // if we have multiple pairs...
+ if (query.contains("&"))
+ {
+ // ensure the delimeters match the xhtml setting
+ // this impl is not at all efficient, but it's easy
+ this.query = query.replaceAll("&(amp;)?", queryDelim);
+ }
+ }
+ }
+
+ protected void appendQuery(Object obj)
+ {
+ if (obj != null)
+ {
+ setQuery(combineQuery(getQuery(), String.valueOf(obj)));
+ }
+ }
+
+ protected String combineQuery(String current, String add)
+ {
+ if (add == null || add.length() == 0)
+ {
+ return current;
+ }
+ if (add.startsWith("?"))
+ {
+ add = add.substring(1, add.length());
+ }
+ if (current == null)
+ {
+ return add;
+ }
+ if (current.endsWith(queryDelim))
+ {
+ current = current.substring(0, current.length() - queryDelim.length());
+ }
+ else if (current.endsWith("&"))
+ {
+ current = current.substring(0, current.length() - 1);
+ }
+ if (add.startsWith(queryDelim))
+ {
+ return current + add;
+ }
+ else if (add.startsWith("&"))
+ {
+ // drop the html delim in favor of the xhtml one
+ add = add.substring(1, add.length());
+ }
+ return current + queryDelim + add;
+ }
+
+ protected String toQuery(Object key, Object value)
+ {
+ StringBuilder out = new StringBuilder();
+ if (value == null)
+ {
+ out.append(encode(key));
+ out.append('=');
+ /* Interpret null as "no value" */
+ }
+ else if (value instanceof List)
+ {
+ appendAsArray(out, key, ((List)value).toArray());
+ }
+ else if (value instanceof Object[])
+ {
+ appendAsArray(out, key, (Object[])value);
+ }
+ else
+ {
+ out.append(encode(key));
+ out.append('=');
+ out.append(encode(value));
+ }
+ return out.toString();
+ }
+
+ /* Utility method to avoid logic duplication in toQuery() */
+ private void appendAsArray(StringBuilder out, Object key, Object[] arr)
+ {
+ String encKey = encode(key);
+ for (int i=0; i < arr.length; i++)
+ {
+ out.append(encKey);
+ out.append('=');
+ if (arr[i] != null)
+ {
+ out.append(encode(arr[i]));
+ }
+ if (i+1 < arr.length)
+ {
+ out.append(queryDelim);
+ }
+ }
+ }
+
+ protected Map<String,Object> parseQuery(String query)
+ {
+ if (query.startsWith("?"))
+ {
+ query = query.substring(1, query.length());
+ }
+ String[] pairs = query.split(queryDelim);
+ if (pairs.length == 0)
+ {
+ return null;
+ }
+ Map<String,Object> params = new LinkedHashMap<String,Object>(pairs.length);
+ for (String pair : pairs)
+ {
+ String[] kv = pair.split("=");
+ String key = kv[0];
+ Object value = kv.length > 1 ? kv[1] : null;
+ if (params.containsKey(kv[0]))
+ {
+ Object oldval = params.get(key);
+ if (oldval instanceof List)
+ {
+ ((List)oldval).add((String)value);
+ value = oldval;
+ }
+ else
+ {
+ List<String> list = new ArrayList<String>();
+ list.add((String)oldval);
+ list.add((String)value);
+ value = list;
+ }
+ }
+ params.put(key, value);
+ }
+ return params;
+ }
+
+ protected void setFragment(Object obj)
+ {
+ if (obj == null)
+ {
+ this.fragment = null;
+ }
+ else
+ {
+ this.fragment = String.valueOf(obj);
+ if (this.fragment.length() == 0)
+ {
+ this.fragment = null;
+ }
+ }
+ }
+
+ protected boolean setFromURI(Object obj)
+ {
+ if (obj == null)
+ {
+ // clear everything out...
+ setScheme(null);
+ setUserInfo(null);
+ setHost(null);
+ setPort(null);
+ setPath(null);
+ setQuery(null);
+ setFragment(null);
+ return true;
+ }
+
+ URI uri;
+ if (obj instanceof URI)
+ {
+ uri = (URI)obj;
+ }
+ else
+ {
+ try
+ {
+ uri = new URI(String.valueOf(obj));
+ }
+ catch (Exception e)
+ {
+ logError("Could convert '"+obj+"' to URI", e);
+ return false;
+ }
+ }
+ setScheme(uri.getScheme());
+ if (uri.isOpaque())
+ {
+ this.opaque = true;
+ // path is used as scheme-specific part
+ setPath(uri.getSchemeSpecificPart());
+ }
+ else
+ {
+ setUserInfo(uri.getUserInfo());
+ setHost(uri.getHost());
+ setPort(uri.getPort());
+ String pth = uri.getPath();
+ if (pth.equals("/") || pth.length() == 0)
+ {
+ pth = null;
+ }
+ setPath(pth);
+ setQuery(uri.getQuery());
+ }
+ setFragment(uri.getFragment());
+ return true;
+ }
+
+ protected URI createURI()
+ {
+ try
+ {
+ // fail if there was an error in setting the port
+ if (port > -2)
+ {
+ String anchor = this.fragment;
+ if (anchor != null)
+ {
+ anchor = encode(anchor);
+ }
+ if (opaque)
+ {
+ // path is used as scheme-specific part
+ return new URI(scheme, path, anchor);
+ }
+ else
+ {
+ // only create the URI if we have some values besides a port
+ if (scheme == null && user == null && host == null
+ && path == null && query == null && fragment == null)
+ {
+ return null;
+ }
+ return new URI(scheme, user, host, port, path, query, fragment);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ logError("Could not create URI", e);
+ }
+ return null;
+ }
+
+ // --------------------------------------------- Template Methods -----------
+
+ public LinkTool scheme(Object scheme)
+ {
+ LinkTool copy = duplicate();
+ copy.setScheme(scheme);
+ return copy;
+ }
+
+ public LinkTool secure()
+ {
+ return scheme(SECURE_SCHEME);
+ }
+
+ public LinkTool insecure()
+ {
+ return scheme(DEFAULT_SCHEME);
+ }
+
+ public String getScheme()
+ {
+ return scheme;
+ }
+
+ public boolean isSecure()
+ {
+ return SECURE_SCHEME.equalsIgnoreCase(getScheme());
+ }
+
+ public boolean isAbsolute()
+ {
+ if (this.scheme == null)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isOpaque()
+ {
+ return this.opaque;
+ }
+
+ public LinkTool user(Object info)
+ {
+ LinkTool copy = duplicate();
+ copy.setUserInfo(info);
+ return copy;
+ }
+
+ public String getUser()
+ {
+ return this.user;
+ }
+
+ public LinkTool host(Object host)
+ {
+ LinkTool copy = duplicate();
+ copy.setHost(host);
+ // if we have host but no scheme
+ if (copy.getHost() != null && !copy.isAbsolute())
+ {
+ // use default scheme
+ copy.setScheme(DEFAULT_SCHEME);
+ }
+ return copy;
+ }
+
+ public String getHost()
+ {
+ return this.host;
+ }
+
+ public LinkTool port(Object port)
+ {
+ LinkTool copy = duplicate();
+ copy.setPort(port);
+ return copy;
+ }
+
+ public Integer getPort()
+ {
+ if (this.port < 0)
+ {
+ return null;
+ }
+ return this.port;
+ }
+
+ public LinkTool path(Object pth)
+ {
+ LinkTool copy = duplicate();
+ copy.setPath(pth);
+ return copy;
+ }
+
+ public String getPath()
+ {
+ return this.path;
+ }
+
+ public LinkTool append(Object pth)
+ {
+ LinkTool copy = duplicate();
+ copy.appendPath(pth);
+ return copy;
+ }
+
+ /**
+ * At this level, this method returns all "folders"
+ * in the set {@link #getPath()} value, by just trimming
+ * of the last "/" and all that follows.
+ */
+ public String getContextPath()
+ {
+ if (this.path == null || this.opaque)
+ {
+ return null;
+ }
+ int lastSlash = this.path.lastIndexOf('/');
+ if (lastSlash <= 0)
+ {
+ return "";
+ }
+ return this.path.substring(0, lastSlash);
+ }
+
+ /**
+ * At this level, this method returns the last section
+ * of the path, from the final "/" onward.
+ */
+ public String getRequestPath()
+ {
+ if (this.path == null || this.opaque)
+ {
+ return null;
+ }
+ int lastSlash = this.path.lastIndexOf('/');
+ if (lastSlash <= 0)
+ {
+ return this.path;
+ }
+ return this.path.substring(lastSlash, this.path.length());
+ }
+
+ /**
+ * Returns the "root" for this URI, if it has one.
+ * This does not stick close to URI dogma and will
+ * try to insert the default scheme if there is none,
+ * and will return null if there is no host. It
+ * is also unfriendly to opaque URIs.
+ */
+ public String getRoot()
+ {
+ if (host == null)
+ {
+ return null;
+ }
+ if (scheme == null)
+ {
+ scheme = DEFAULT_SCHEME;
+ }
+ StringBuilder out = new StringBuilder();
+ out.append(scheme);
+ out.append("://");
+ out.append(host);
+ // if we have a port that's not a default for the scheme
+ if (port >= 0 &&
+ ((scheme.equals(DEFAULT_SCHEME) && port != 80) ||
+ (isSecure() && port != 443)))
+ {
+ out.append(':');
+ out.append(port);
+ }
+ return out.toString();
+ }
+
+
+ /**
+ * <p>Returns the URI that addresses this web application. E.g.
+ * <code>http://myserver.net/myapp</code>. This string does not end
+ * with a "/". Note! This will not represent any URI reference or
+ * query data set for this LinkTool.</p>
+ */
+ public String getContextURL()
+ {
+ String root = getRoot();
+ if (root == null)
+ {
+ return null;
+ }
+ return combinePath(root, getContextPath());
+ }
+
+ /**
+ * <p>Returns a copy of the link with the specified context-relative
+ * URI reference converted to a server-relative URI reference. This
+ * method will overwrite any previous URI reference settings but will
+ * copy the query string.</p>
+ *
+ * Example:<br>
+ * <code><a href='$link.relative("/templates/login/index.vm")'>Login Page</a></code><br>
+ * produces something like</br>
+ * <code><a href="/myapp/templates/login/index.vm">Login Page</a></code><br>
+ *
+ * @param path A context-relative URI reference. A context-relative URI
+ * is a URI that is relative to the root of this web application.
+ * @return a new instance of LinkTool with the specified URI
+ */
+ public LinkTool relative(Object obj)
+ {
+ if (obj == null)
+ {
+ return path(getContextPath());
+ }
+ String pth = String.valueOf(obj);
+ LinkTool copy = duplicate();
+ // prepend relative paths with context path
+ copy.setPath(combinePath(getContextPath(), pth));
+ return copy;
+ }
+
+ /**
+ * <p>Returns a copy of the link with the specified URI reference
+ * either used as or converted to an absolute (non-relative)
+ * URI reference. This method will overwrite any previous URI
+ * reference settings but will copy the query string.</p>
+ *
+ * Example:<br>
+ * <code><a href='$link.absolute("/templates/login/index.vm")'>Login Page</a></code><br>
+ * produces something like<br/>
+ * <code><a href="http://myserver.net/myapp/templates/login/index.vm">Login Page</a></code><br>
+ * and<br>
+ * <code><a href='$link.absolute("http://theirserver.com/index.jsp")'>Their, Inc.</a></code><br>
+ * produces something like<br/>
+ * <code><a href="http://theirserver.net/index.jsp">Their, Inc.</a></code><br>
+ *
+ * @param uri A context-relative URI reference or absolute URL.
+ * @return a new instance of LinkTool with the specified URI
+ * @see #uri(Object uri)
+ * @since VelocityTools 1.3
+ */
+ public LinkTool absolute(Object obj)
+ {
+ if (obj == null)
+ {
+ // use uri's null handling
+ return uri(obj);
+ }
+ String pth = String.valueOf(obj);
+ if (pth.startsWith(DEFAULT_SCHEME))
+ {
+ // looks absolute already
+ return uri(pth);
+ }
+
+ // assume it's relative and try to make it absolute
+ String root = getRoot();
+ if (root != null)
+ {
+ return uri(combinePath(root, pth));
+ }
+ // ugh. this is about all we can do here w/o a host
+ LinkTool copy = duplicate();
+ copy.setScheme(DEFAULT_SCHEME);
+ copy.setPath(pth);
+ return copy;
+ }
+
+ /**
+ * <p>Returns a copy of the link with the given URI reference set.
+ * No conversions are applied to the given URI reference. The URI
+ * reference can be absolute, server-relative, relative and may
+ * contain query parameters. This method will overwrite any
+ * previous URI reference settings but will copy the query
+ * string.</p>
+ *
+ * @param uri URI reference to set
+ * @return a new instance of LinkTool
+ * @since VelocityTools 1.3
+ */
+ public LinkTool uri(Object uri)
+ {
+ LinkTool copy = duplicate();
+ if (copy.setFromURI(uri))
+ {
+ return copy;
+ }
+ return null;
+ }
+
+ public URI getUri()
+ {
+ if (!isSafeMode())
+ {
+ return createURI();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the full URI of this template without any query data.
+ * e.g. <code>http://myserver.net/myapp/stuff/View.vm</code>
+ * Note! The returned String will not represent any URI reference
+ * or query data set for this LinkTool. A typical application of
+ * this method is with the HTML base tag. For example:
+ * <code><base href="$link.baseRef"></code>
+ */
+ public String getBaseRef()
+ {
+ LinkTool copy = duplicate();
+ copy.setQuery(null);
+ copy.setFragment(null);
+ return copy.toString();
+ }
+
+ public LinkTool query(Object query)
+ {
+ LinkTool copy = duplicate();
+ copy.setQuery(query);
+ return copy;
+ }
+
+ public String getQuery()
+ {
+ return this.query;
+ }
+
+ /**
+ * <p>Adds a key=value pair to the query data. This returns a new LinkTool
+ * containing both a copy of this LinkTool's query data and the new data.
+ * Query data is URL encoded before it is appended.</p>
+ *
+ * @param key key of new query parameter
+ * @param value value of new query parameter
+ *
+ * @return a new instance of LinkTool
+ * @since VelocityTools 1.3
+ */
+ public LinkTool param(Object key, Object value)
+ {
+ LinkTool copy = duplicate();
+ copy.appendQuery(toQuery(key, value));
+ return copy;
+ }
+
+ /**
+ * <p>Adds multiple key=value pairs to the query data.
+ * This returns a new LinkTool containing both a copy of
+ * this LinkTool's query data and the new data.
+ * Query data is URL encoded before it is appended.</p>
+ *
+ * @param parameters map of new query data keys to values
+ * @return a new instance of LinkTool
+ * @since VelocityTools 1.3
+ */
+ public LinkTool params(Map parameters)
+ {
+ // don't waste time with null/empty data
+ if (parameters == null || parameters.isEmpty())
+ {
+ return this;
+ }
+ LinkTool copy = duplicate();
+ StringBuilder query = new StringBuilder();
+ for (Object e : parameters.entrySet())
+ {
+ Map.Entry entry = (Map.Entry)e;
+ //add new pair to this LinkTool's query data
+ if (query.length() > 0)
+ {
+ query.append(queryDelim);
+ }
+ query.append(toQuery(entry.getKey(), entry.getValue()));
+ }
+ copy.appendQuery(query);
+ return copy;
+ }
+
+ public Map getParams()
+ {
+ if (this.query == null || this.query.isEmpty())
+ {
+ return null;
+ }
+ return parseQuery(this.query);
+ }
+
+ /**
+ * <p>Returns a copy of the link with the specified anchor to be
+ * added to the end of the generated hyperlink.</p>
+ *
+ * Example:<br>
+ * <code><a href='$link.setAnchor("foo")'>Foo</a></code><br>
+ * produces something like</br>
+ * <code><a href="#foo">Foo</a></code><br>
+ *
+ * @param anchor an internal document reference
+ * @return a new instance of LinkTool with the set anchor
+ * @since VelocityTools 1.3
+ */
+ public LinkTool anchor(Object anchor)
+ {
+ LinkTool copy = duplicate();
+ copy.setFragment(anchor);
+ return copy;
+ }
+
+ /**
+ * Returns the anchor (internal document reference) set for this link.
+ */
+ public String getAnchor()
+ {
+ return this.fragment;
+ }
+
+ public LinkTool getSelf()
+ {
+ // there are no self-params to bother with at this level,
+ return self;
+ }
+
+ /**
+ * Returns the full URI reference that's been built with this tool,
+ * including the query string and anchor, e.g.
+ * <code>http://myserver.net/myapp/stuff/View.vm?id=42&type=blue#foo</code>.
+ * Typically, it is not necessary to call this method explicitely.
+ * Velocity will call the toString() method automatically to obtain
+ * a representable version of an object.
+ */
+ public String toString()
+ {
+ URI uri = createURI();
+ if (uri == null)
+ {
+ return null;
+ }
+ return uri.toString();
+ }
+
+ /**
+ * This instance is considered equal to any
+ * LinkTool instance whose toString() method returns a
+ * String equal to that returned by this instance's toString()
+ * @see #toString()
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null || !(obj instanceof LinkTool))
+ {
+ return false;
+ }
+ // string value is all that ultimately matters
+ String that = obj.toString();
+ if (that == null && toString() == null)
+ {
+ return true;
+ }
+ return that.equals(toString());
+ }
+
+ /**
+ * Returns the hash code for the result of toString().
+ * If toString() returns {@code null} (yes, we do break that contract),
+ * this will return {@code -1}.
+ */
+ @Override
+ public int hashCode()
+ {
+ String hashme = toString();
+ if (hashme == null)
+ {
+ return -1;
+ }
+ return hashme.hashCode();
+ }
+
+
+ /**
+ * Delegates encoding of the specified url content to
+ * {@link URLEncoder#encode} using the configured character encoding.
+ *
+ * @return String - the encoded url.
+ */
+ public String encode(Object obj)
+ {
+ if (obj == null)
+ {
+ return null;
+ }
+ try
+ {
+ return URLEncoder.encode(String.valueOf(obj), charset);
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ logError("Character encoding '"+charset+"' is unsupported", uee);
+ return null;
+ }
+ }
+
+ /**
+ * Delegates decoding of the specified url content to
+ * {@link URLDecoder#decode} using the configured character encoding.
+ *
+ * @return String - the decoded url.
+ */
+ public String decode(Object obj)
+ {
+ if (obj == null)
+ {
+ return null;
+ }
+ try
+ {
+ return URLDecoder.decode(String.valueOf(obj), charset);
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ logError("Character encoding '"+charset+"' is unsupported", uee);
+ return null;
+ }
+ }
+
+}
Propchange: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java
------------------------------------------------------------------------------
svn:executable = *
Propchange: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java
------------------------------------------------------------------------------
svn:keywords = Revision
Propchange: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/tools.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/tools.xml?rev=663715&r1=663714&r2=663715&view=diff
==============================================================================
--- velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/tools.xml (original)
+++ velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/tools.xml Thu Jun 5 12:36:05 2008
@@ -38,6 +38,7 @@
</toolbox>
<toolbox scope="request">
<tool class="org.apache.velocity.tools.generic.ContextTool"/>
+ <tool class="org.apache.velocity.tools.generic.LinkTool"/>
<tool class="org.apache.velocity.tools.generic.LoopTool"/>
<tool class="org.apache.velocity.tools.generic.RenderTool"/>
<!--
Modified: velocity/tools/trunk/src/main/java/org/apache/velocity/tools/view/LinkTool.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/main/java/org/apache/velocity/tools/view/LinkTool.java?rev=663715&r1=663714&r2=663715&view=diff
==============================================================================
--- velocity/tools/trunk/src/main/java/org/apache/velocity/tools/view/LinkTool.java (original)
+++ velocity/tools/trunk/src/main/java/org/apache/velocity/tools/view/LinkTool.java Thu Jun 5 12:36:05 2008
@@ -45,6 +45,12 @@
* <li>and more..</li>
* </ul></p>
*
+ * <p>This VelocityView version of LinkTool is not currently a subclass of the
+ * newer, GenericTools' version. This is likely, however, to happen in the
+ * future. To best future proof your use of this tool against deprecations,
+ * try not to rely upon differences between the two versions, except of course
+ * where this tool provides additional servlet-related functions.</p>
+ *
* <p>The LinkTool is somewhat special in that nearly all public methods return
* a new instance of LinkTool. This facilitates greatly the repeated use
* of the LinkTool in Velocity and leads to an elegant syntax.</p>
Added: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LinkToolTests.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LinkToolTests.java?rev=663715&view=auto
==============================================================================
--- velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LinkToolTests.java (added)
+++ velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LinkToolTests.java Thu Jun 5 12:36:05 2008
@@ -0,0 +1,651 @@
+package org.apache.velocity.tools.generic;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import org.junit.*;
+import static org.junit.Assert.*;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.velocity.runtime.log.Log;
+import org.apache.velocity.tools.generic.ValueParser;
+
+/**
+ * <p>Tests for generic version of LinkTool</p>
+ *
+ * @author Nathan Bubna
+ * @since VelocityTools 2.0
+ * @version $Id$
+ */
+public class LinkToolTests {
+
+ public static final Map DEFAULT_PROPS = new HashMap();
+ static
+ {
+ // don't lock configure() for testing
+ DEFAULT_PROPS.put(LinkTool.SAFE_MODE_KEY, false);
+ DEFAULT_PROPS.put(LinkTool.LOCK_CONFIG_KEY, false);
+ }
+
+ /**
+ * Returns a new instance configured with the
+ * default testing properties.
+ */
+ public LinkTool newInstance()
+ {
+ LinkTool link = new LinkTool();
+ link.configure(DEFAULT_PROPS);
+ return link;
+ }
+
+ public LinkTool newInstance(String uri)
+ {
+ return newInstance(LinkTool.URI_KEY, uri);
+ }
+
+ /**
+ * Returns a new instance configured with the
+ * default testing properties and the specified
+ * non-default property.
+ */
+ public LinkTool newInstance(String key, Object value)
+ {
+ LinkTool link = new LinkTool();
+ Map props = new HashMap(DEFAULT_PROPS);
+ props.put(key, value);
+ link.configure(props);
+ return link;
+ }
+
+ public @Test void ctorLinkTool() throws Exception
+ {
+ try
+ {
+ new LinkTool();
+ }
+ catch (Exception e)
+ {
+ fail("Constructor 'LinkTool()' failed due to: " + e);
+ }
+ }
+
+ public @Test void methodConfigure_ValueParser() throws Exception
+ {
+ LinkTool link = newInstance("mailto:nbubna@apache.org");
+ assertEquals("mailto", link.getScheme());
+ assertEquals("mailto:nbubna@apache.org", link.toString());
+ assertTrue(link.getUri().isOpaque());
+ assertTrue(link.isAbsolute());
+ }
+
+ public @Test void methodDuplicate() throws Exception
+ {
+ LinkTool link = newInstance("http://apache.org/foo.html");
+ LinkTool result = link.duplicate();
+ assertFalse(link == result);
+ assertSame(link.getScheme(), result.getScheme());
+ assertSame(link.getUser(), result.getUser());
+ assertSame(link.getHost(), result.getHost());
+ assertSame(link.getPath(), result.getPath());
+ assertSame(link.getQuery(), result.getQuery());
+ assertSame(link.getAnchor(), result.getAnchor());
+ assertSame(link.getSelf(), result.getSelf());
+ }
+
+ public @Test void methodEncode_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals("this+has+spaces", link.encode("this has spaces"));
+ assertEquals("%40%2F+", link.encode("@/ "));
+ }
+
+ public @Test void methodDecode_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals("this has spaces", link.decode("this+has+spaces"));
+ assertEquals("@/ ", link.decode("%40%2F+"));
+ }
+
+ public @Test void methodSetScheme_Object() throws Exception
+ {
+ LinkTool link = newInstance(LinkTool.SCHEME_KEY, LinkTool.DEFAULT_SCHEME);
+ assertEquals(LinkTool.DEFAULT_SCHEME, link.getScheme());
+ link.setScheme(null);
+ assertEquals(null, link.getScheme());
+ link.setScheme("foo:");
+ assertEquals("foo", link.getScheme());
+ }
+
+ public @Test void methodScheme_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ LinkTool result = link.scheme(null);
+ assertEquals(null, result.getScheme());
+ link = newInstance("https://apache.org");
+ assertEquals(null, link.getPath());
+ assertEquals("https://apache.org", link.toString());
+ assertEquals(LinkTool.SECURE_SCHEME, link.getScheme());
+ assertTrue(link.isSecure());
+ result = link.scheme(LinkTool.DEFAULT_SCHEME);
+ assertEquals("http://apache.org", result.toString());
+ assertFalse(result.isSecure());
+ }
+
+ public @Test void methodGetScheme() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals(null, link.getScheme());
+ assertEquals("mailto", link.scheme("mailto").getScheme());
+ }
+
+ public @Test void methodSecure() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertFalse(link.isSecure());
+ LinkTool result = link.secure();
+ assertTrue(result.isSecure());
+ }
+
+ public @Test void methodInsecure() throws Exception
+ {
+ LinkTool link = newInstance("https://apache.org");
+ assertTrue(link.isSecure());
+ LinkTool result = link.insecure();
+ assertFalse(result.isSecure());
+ }
+
+ public @Test void methodIsAbsolute() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertFalse(link.isAbsolute());
+ LinkTool result = link.absolute("http://apache.org");
+ assertTrue(result.isAbsolute());
+ }
+
+ public @Test void methodSetUserInfo_Object() throws Exception
+ {
+ LinkTool link = newInstance(LinkTool.USER_KEY, "nbubna");
+ assertEquals("nbubna", link.getUser());
+ link.setUserInfo(null);
+ assertEquals(null, link.getUser());
+ // no encoding should happen here
+ link.setUserInfo("@#$ /@!");
+ assertEquals("@#$ /@!", link.getUser());
+ }
+
+ public @Test void methodGetUser() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals(null, link.getUser());
+ link = newInstance("http://nbubna@apache.org");
+ assertEquals("nbubna", link.getUser());
+ }
+
+ public @Test void methodUser_Object() throws Exception
+ {
+ LinkTool link = newInstance("http://nbubna@apache.org");
+ assertEquals(null, link.user(null).getUser());
+ assertEquals("nbubna", link.user("nbubna").getUser());
+ assertEquals("@#$ /!", link.user("@#$ /!").getUser());
+ assertEquals("http://%40%23$%20%2F!@apache.org", link.user("@#$ /!").toString());
+ }
+
+ public @Test void methodGetHost() throws Exception
+ {
+ LinkTool link = newInstance("http://apache.org");
+ assertEquals("apache.org", link.getHost());
+ link.setFromURI("http://velocity.apache.org/tools/devel/");
+ assertEquals("velocity.apache.org", link.getHost());
+ }
+
+ public @Test void methodHost_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals("apache.org", link.host("apache.org").getHost());
+ link = newInstance("https://nbubna@www.apache.org");
+ assertEquals("https://nbubna@people.apache.org", link.host("people.apache.org").toString());
+ }
+
+ public @Test void methodSetHost_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ link.setHost("foo.com");
+ assertEquals("foo.com", link.getHost());
+ }
+
+ public @Test void methodGetPort() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertNull(link.getPort());
+ link = newInstance(LinkTool.PORT_KEY, 42);
+ assertEquals(42, link.getPort());
+ }
+
+ public @Test void methodPort_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertNull(link.port(null).getPort());
+ assertNull(link.port(":asd").getPort());
+ assertEquals(1, link.port(1).getPort());
+ assertEquals(42, link.port("42").getPort());
+ }
+
+ public @Test void methodSetPort_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ link.setPort(42);
+ assertEquals(42, link.getPort());
+ }
+
+ public @Test void methodGetPath() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertNull(link.getPath());
+ link = newInstance("http://velocity.apache.org/tools/devel");
+ assertEquals("/tools/devel", link.getPath());
+ }
+
+ public @Test void methodSetPath_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertNull(link.getPath());
+ link.setPath("foo");
+ assertEquals("/foo", link.getPath());
+ link.setPath("/foo");
+ assertEquals("/foo", link.getPath());
+ link.setPath("/foo/");
+ assertEquals("/foo/", link.getPath());
+ link.setPath("/foo/");
+ assertEquals("/foo/", link.getPath());
+ }
+
+ public @Test void methodPath_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertNull(link.getPath());
+ assertEquals("/bar", link.path("bar").getPath());
+ assertEquals("/bar", link.path("/bar").getPath());
+ assertEquals("/bar/", link.path("bar/").getPath());
+ assertEquals("/bar/", link.path("/bar/").getPath());
+ link = newInstance("http://foo.com/this/that.vm");
+ assertEquals("http://foo.com/this/that.vm", link.toString());
+ assertEquals("http://foo.com/bar.vm", link.path("bar.vm").toString());
+ }
+
+ public @Test void methodCombinePath_StringString() throws Exception
+ {
+ LinkTool link = newInstance();
+ String none = null;
+ String empty = "";
+ String test = "test";
+ String starts = "/this";
+ String ends = "that/";
+ String both = "/these/";
+ assertNull(link.combinePath(none, none));
+ assertSame(empty, link.combinePath(none, empty));
+ assertSame(empty, link.combinePath(empty, none));
+ assertEquals("/test", link.combinePath(empty, test));
+ assertEquals("test/", link.combinePath(test, empty));
+ assertEquals("/this/this", link.combinePath(starts, starts));
+ assertEquals("that/that/", link.combinePath(ends, ends));
+ assertEquals("/this/that/", link.combinePath(starts, ends));
+ assertEquals("that/this", link.combinePath(ends, starts));
+ assertEquals("/these/these/", link.combinePath(both, both));
+ }
+
+ public @Test void methodAppendPath_Object() throws Exception
+ {
+ LinkTool link = newInstance(LinkTool.PATH_KEY, "/foo");
+ assertEquals("/foo", link.getPath());
+ link.appendPath("bar");
+ assertEquals("/foo/bar", link.getPath());
+ link.appendPath("/bar");
+ assertEquals("/foo/bar/bar", link.getPath());
+ link.setPath("/foo/");
+ link.appendPath("bar/");
+ assertEquals("/foo/bar/", link.getPath());
+ link.appendPath("/bar");
+ assertEquals("/foo/bar/bar", link.getPath());
+ }
+
+ public @Test void methodAppend_Object() throws Exception
+ {
+ LinkTool link = newInstance(LinkTool.PATH_KEY, "/foo");
+ assertEquals("/foo", link.append(null).getPath());
+ link.setPath(null);
+ assertNull(link.getPath());
+ link = link.append("bar");
+ assertEquals("/bar", link.getPath());
+ assertEquals("/bar/foo", link.append("foo").getPath());
+ }
+
+ public @Test void methodGetContextPath() throws Exception
+ {
+ LinkTool link = newInstance("http://foo.com/ctx/request.vm?this=that#anc");
+ assertEquals("/ctx", link.getContextPath());
+ link = newInstance("http://foo.com/foo/bar/request.vm?this=that#anc");
+ assertEquals("/foo/bar", link.getContextPath());
+ }
+
+ public @Test void methodGetRequestPath() throws Exception
+ {
+ LinkTool link = newInstance("http://foo.com/ctx/request.vm?this=that#anc");
+ assertEquals("/request.vm", link.getRequestPath());
+ link = newInstance("http://foo.com/foo/bar/request.vm?this=that#anc");
+ assertEquals("/request.vm", link.getRequestPath());
+ }
+
+ public @Test void methodGetRoot() throws Exception
+ {
+ LinkTool link = newInstance("http://foo.com/ctx/request.vm?this=that#anc");
+ assertEquals("http://foo.com", link.getRoot());
+ link.setHost("apache.org");
+ assertEquals("https://apache.org", link.secure().getRoot());
+ }
+
+ public @Test void methodGetContextURL() throws Exception
+ {
+ LinkTool link = newInstance("http://foo.com/ctx/request.vm?this=that#anc");
+ assertEquals("http://foo.com/ctx", link.getContextURL());
+ link = newInstance("http://foo.com");
+ assertEquals("http://foo.com", link.getContextURL());
+ }
+
+ public @Test void methodRelative_Object() throws Exception
+ {
+ LinkTool link = newInstance("/ctx/request.vm?this=that#anc");
+ assertEquals("/ctx/request.vm", link.getPath());
+ assertEquals("/ctx", link.getContextPath());
+ assertEquals("this=that", link.getQuery());
+ assertEquals("anc", link.getAnchor());
+ assertEquals("/ctx/request.vm?this=that#anc", link.toString());
+ assertEquals("/ctx?this=that#anc", link.relative(null).toString());
+ assertEquals("/ctx/other.vm?this=that#anc", link.relative("other.vm").toString());
+ link = newInstance("http://foo.com/bar/");
+ assertEquals("http://foo.com/bar/woogie.vm", link.relative("woogie.vm").toString());
+ link = newInstance("/bar/");
+ assertEquals("/bar/foo/woogie.vm", link.relative("foo/woogie.vm").toString());
+ assertEquals("/bar/yo", link.relative("yo").toString());
+ }
+
+ public @Test void methodAbsolute_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ LinkTool result = link.absolute(null);
+ // nulls should be handled by uri()
+ assertEquals(link.uri(null), result);
+
+ result = link.absolute("http://apache.org");
+ assertEquals(link.uri("http://apache.org"), result);
+ assertTrue(result.isAbsolute());
+ assertEquals("http://apache.org", result.toString());
+ assertEquals(LinkTool.DEFAULT_SCHEME, result.getScheme());
+ assertEquals("apache.org", result.getHost());
+
+ assertFalse(link.isAbsolute());
+ result = link.absolute("/test/foo.vm");
+ assertTrue(result.isAbsolute());
+ assertEquals("/test/foo.vm", result.getPath());
+ assertEquals(null, result.getHost());
+ assertEquals("http:/test/foo.vm", result.toString());
+ result = result.host("apache.org");
+ assertEquals("http://apache.org/test/foo.vm", result.toString());
+ result = result.absolute("bar.vm");
+ assertEquals("http://apache.org/bar.vm", result.toString());
+ }
+
+ public @Test void methodGetBaseRef() throws Exception
+ {
+ LinkTool link = newInstance("http://foo.com/ctx/request.vm?this=that#anc");
+ assertEquals("http://foo.com/ctx/request.vm", link.getBaseRef());
+ assertEquals(null, newInstance().getBaseRef());
+ }
+
+ public @Test void methodGetUri() throws Exception
+ {
+ LinkTool link = newInstance(LinkTool.SAFE_MODE_KEY, true);
+ link.setFromURI("http://velocity.apache.org");
+ assertNull(link.getUri());
+ link = newInstance();
+ assertNull(link.getUri());
+ link = link.secure().user("nbubna").host("people.apache.org");
+ assertNotNull(link.getUri());
+ assertEquals("nbubna", link.getUri().getUserInfo());
+ }
+
+ public @Test void methodSetFromURI_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertNull(link.toString());
+ link.setFromURI("*%&$^%#$*&^!");
+ assertNull(link.toString());
+ link.setFromURI("http://velocity.apache.org");
+ assertNotNull(link.toString());
+ assertEquals("velocity.apache.org", link.getHost());
+ }
+
+ public @Test void methodCreateURI() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertNull(link.createURI());
+ link.setFromURI("http://velocity.apache.org");
+ assertNotNull(link.createURI());
+ link.setPort("foo");
+ assertNull(link.createURI());
+ link.setPort(null);
+ assertTrue(link.setFromURI("mailto:nbubna@apache.org"));
+ assertTrue(link.isOpaque());
+ assertNotNull(link.createURI());
+ assertTrue(link.createURI().isOpaque());
+ assertEquals(link.createURI(), link.createURI());
+ assertFalse(link.createURI() == link.createURI());
+ }
+
+ public @Test void methodUri_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals(null, link.uri(null).toString());
+ assertEquals("http://apache.org?a=b#c", link.uri("http://apache.org?a=b#c").toString());
+ link.setFromURI("https://nbubna@people.apache.org");
+ assertEquals("people.apache.org", link.getHost());
+ assertEquals("https://nbubna@people.apache.org", link.uri(link.createURI()).toString());
+ URI uri = new URI("mailto:nbubna@apache.org");
+ assertEquals("mailto:nbubna@apache.org", link.uri(uri).toString());
+ }
+
+ public @Test void methodSetQuery_Object() throws Exception
+ {
+ LinkTool link = newInstance("/bar?a=b");
+ assertNotNull(link.getQuery());
+ assertEquals("a=b", link.getQuery());
+ link.setQuery("c=d&e=f");
+ assertEquals("c=d&e=f", link.getQuery());
+ link.setXHTML(false);
+ link.setQuery("x=1&y=2");
+ assertEquals("x=1&y=2", link.getQuery());
+ link.setQuery(null);
+ assertEquals(null, link.getQuery());
+ }
+
+ public @Test void methodGetQuery() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals(null, link.getQuery());
+ assertEquals("this=that", link.query("this=that").getQuery());
+ }
+
+ public @Test void methodQuery_Object() throws Exception
+ {
+ LinkTool link = newInstance("https://gmail.com");
+ assertEquals("https://gmail.com", link.query(null).toString());
+ assertEquals("https://gmail.com?v=2", link.query("v=2").toString());
+ link = newInstance("http://go.com?foo=bar");
+ assertEquals("foo=wog", link.query("foo=wog").getQuery());
+ assertEquals("http://go.com", link.query(null).toString());
+ }
+
+ // this method also tests setXHTML
+ public @Test void methodCombineQuery_StringString() throws Exception
+ {
+ LinkTool link = newInstance();
+ String none = null;
+ String empty = "";
+ String test = "test=1";
+ String test2 = "a=b";
+ assertSame(none, link.combineQuery(none, none));
+ assertSame(none, link.combineQuery(none, empty));
+ assertSame(test, link.combineQuery(test, none));
+ assertEquals("test=1", link.combineQuery(test, empty));
+ assertEquals("test=1&a=b", link.combineQuery(test, test2));
+ link.setXHTML(false);
+ assertEquals("a=b&test=1", link.combineQuery(test2, test));
+ }
+
+ public @Test void methodAppendQuery_Object() throws Exception
+ {
+ LinkTool link = newInstance("/foo?bar=woogie");
+ link.appendQuery("x=1");
+ assertEquals("bar=woogie&x=1", link.getQuery());
+ link.appendQuery("y=2");
+ assertEquals("bar=woogie&x=1&y=2", link.getQuery());
+ link.setQuery(null);
+ assertEquals(null, link.getQuery());
+ link.appendQuery("z=3");
+ assertEquals("z=3", link.getQuery());
+ }
+
+ public @Test void methodToQuery_ObjectObject() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals("null=", link.toQuery(null, null));
+ assertEquals("a+b=c", link.toQuery("a b", "c"));
+ assertEquals("x=1", link.toQuery('x', 1));
+ assertEquals("true=false", link.toQuery(true, false));
+ assertEquals("path=%2Ffoo+bar%2Fnew", link.toQuery("path", "/foo bar/new"));
+ }
+
+ public @Test void methodParam_ObjectObject() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals("null=", link.param(null ,null).getQuery());
+ assertEquals("x=1", link.param("x",1).getQuery());
+ assertEquals("x=1&y=2", link.param("x",1).param("y",2).getQuery());
+ link = newInstance("/hee/haa.vm?a=b");
+ assertEquals("/hee/haa.vm?a=b&b=true", link.param('b', true).toString());
+ }
+
+ public @Test void methodParams_Map() throws Exception
+ {
+ LinkTool link = newInstance("http://go.com");
+ Map params = new LinkedHashMap();
+ params.put("this", "that");
+ params.put('x', 1);
+ params.put(true, false);
+ assertEquals("http://go.com?this=that&x=1&true=false", link.params(params).toString());
+ assertEquals("http://go.com", link.params(null).toString());
+ assertEquals("http://go.com", link.params(new HashMap()).toString());
+ }
+
+ public @Test void methodParseQuery_String() throws Exception
+ {
+ LinkTool link = newInstance();
+ Map result = link.parseQuery("a=b&x=1");
+ assertEquals("b", result.get("a"));
+ assertEquals("1", result.get("x"));
+ link.setXHTML(false);
+ result = link.parseQuery("true=false");
+ assertEquals("false", result.get("true"));
+ }
+
+ public @Test void methodGetParams() throws Exception
+ {
+ LinkTool link = newInstance("/foo?a=b&x=true");
+ Map result = link.getParams();
+ assertEquals("b", result.get("a"));
+ assertEquals("true", result.get("x"));
+ result = link.param('y',false).getParams();
+ assertEquals("b", result.get("a"));
+ assertEquals("false", result.get("y"));
+ }
+
+ public @Test void methodSetFragment_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ link.setFragment("foo");
+ assertEquals("#foo", link.toString());
+ link.setFragment(null);
+ assertEquals(null, link.toString());
+ link = newInstance("/foo#bar");
+ link.setFragment("woo gie");
+ assertEquals("/foo#woo%20gie", link.toString());
+ }
+
+ public @Test void methodGetAnchor() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals(null, link.getAnchor());
+ link.setFragment("foo");
+ assertEquals("foo", link.getAnchor());
+ link = newInstance("http://go.com#espn");
+ assertEquals("espn", link.getAnchor());
+ link = newInstance(LinkTool.FRAGMENT_KEY, "foo");
+ assertEquals("foo", link.getAnchor());
+ }
+
+ public @Test void methodAnchor_Object() throws Exception
+ {
+ LinkTool link = newInstance();
+ // here are possible string values to test:
+ String none = null;
+ String empty = "";
+ String space = "a b";
+ String test = "test";
+ assertEquals(null, link.anchor(none).getAnchor());
+ assertEquals(null, link.anchor(empty).getAnchor());
+ assertEquals(test, link.anchor(test).getAnchor());
+ assertEquals("a b", link.anchor(space).getAnchor());
+ link = newInstance("http://go.com#foo");
+ assertEquals("http://go.com#true", link.anchor(true).toString());
+ assertEquals("http://go.com#a%20b", link.anchor(space).toString());
+ }
+
+ public @Test void methodGetSelf() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertSame(link, link.getSelf());
+ assertSame(link, link.uri("http://go.com").getSelf());
+ assertSame(link, link.path("foo").param(1,true).anchor('a').getSelf());
+ }
+
+ public @Test void methodToString() throws Exception
+ {
+ LinkTool link = newInstance();
+ assertEquals(null, link.toString());
+ assertEquals(null, link.secure().toString());
+ assertEquals("http://go.com", link.host("go.com").toString());
+ assertEquals(null, link.port(42).toString());
+ assertEquals("/foo", link.path("foo").toString());
+ assertEquals("?a=1", link.param('a',1).toString());
+ assertEquals("#42", link.anchor(42).toString());
+ }
+
+}
+
\ No newline at end of file
Propchange: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LinkToolTests.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LinkToolTests.java
------------------------------------------------------------------------------
svn:executable = *
Propchange: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LinkToolTests.java
------------------------------------------------------------------------------
svn:keywords = Revision
Propchange: velocity/tools/trunk/src/test/java/org/apache/velocity/tools/LinkToolTests.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: velocity/tools/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/xdocs/changes.xml?rev=663715&r1=663714&r2=663715&view=diff
==============================================================================
--- velocity/tools/trunk/xdocs/changes.xml (original)
+++ velocity/tools/trunk/xdocs/changes.xml Thu Jun 5 12:36:05 2008
@@ -35,7 +35,7 @@
<li>New configuration formats (more concise/flexible/powerful xml, properties, java)</li>
<li>Entirely new core infrastructure (lazy-loading tools, easier access, standalone support etc)</li>
<li>Added VelocityViewTag for JSP integration</li>
- <li>Added DisplayTool, ConversionTool, ClassTool, LoopTool, FieldTool and more</li>
+ <li>Added DisplayTool, ConversionTool, ClassTool, LoopTool, FieldTool, a generic version of LinkTool and more</li>
<li>Refactored and enhanced a number of existing tools</li>
<li>Improved documentation</li>
<li>Deprecated lots of outdated things</li>
Modified: velocity/tools/trunk/xdocs/dependencies.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/xdocs/dependencies.xml?rev=663715&r1=663714&r2=663715&view=diff
==============================================================================
--- velocity/tools/trunk/xdocs/dependencies.xml (original)
+++ velocity/tools/trunk/xdocs/dependencies.xml Thu Jun 5 12:36:05 2008
@@ -65,8 +65,9 @@
<td>commons-logging</td>
<td>1.1</td>
<td>Yes</td>
- <td>No</td>
- <td></td>
+ <td>Yes</td>
+ <td>Required for
+ <a href="javadoc/org/apache/velocity/tools/generic/LinkTool.html">LinkTool</a></td>
</tr>
<tr>
<td>velocity</td>
Modified: velocity/tools/trunk/xdocs/generic.project.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/xdocs/generic.project.xml?rev=663715&r1=663714&r2=663715&view=diff
==============================================================================
--- velocity/tools/trunk/xdocs/generic.project.xml (original)
+++ velocity/tools/trunk/xdocs/generic.project.xml Thu Jun 5 12:36:05 2008
@@ -35,6 +35,7 @@
<item name="DisplayTool" href="javadoc/org/apache/velocity/tools/generic/DisplayTool.html"/>
<item name="EscapeTool" href="javadoc/org/apache/velocity/tools/generic/EscapeTool.html"/>
<item name="FieldTool" href="javadoc/org/apache/velocity/tools/generic/FieldTool.html"/>
+ <item name="LinkTool" href="javadoc/org/apache/velocity/tools/generic/LinkTool.html"/>
<item name="ListTool" href="javadoc/org/apache/velocity/tools/generic/ListTool.html"/>
<item name="LoopTool" href="javadoc/org/apache/velocity/tools/generic/LoopTool.html"/> />
<item name="MathTool" href="javadoc/org/apache/velocity/tools/generic/MathTool.html"/>
Modified: velocity/tools/trunk/xdocs/generic.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/xdocs/generic.xml?rev=663715&r1=663714&r2=663715&view=diff
==============================================================================
--- velocity/tools/trunk/xdocs/generic.xml (original)
+++ velocity/tools/trunk/xdocs/generic.xml Thu Jun 5 12:36:05 2008
@@ -94,6 +94,9 @@
- A convenience tool to use with #foreach loops. It wraps a list
with a custom iterator to provide greater control, allowing loops
to end early, skip ahead and more.</li>
+ <li><a href="javadoc/org/apache/velocity/tools/generic/LinkTool.html">LinkTool</a>
+ - For creating and manipulating URIs and URLs. The API for this tool is
+ designed to closely resemble that of the VelocityView tool of the same name.</li>
<li><a href="javadoc/org/apache/velocity/tools/generic/ListTool.html">ListTool</a>
- For working with arrays and lists, treats both transparently
the same.</li>