Author: nbubna
Date: Thu Sep 25 17:09:35 2008
New Revision: 699136

URL: http://svn.apache.org/viewvc?rev=699136&view=rev
Log:
add docs, make more things public, skip setters on config, other misc 
improvements

Modified:
    
velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java

Modified: 
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=699136&r1=699135&r2=699136&view=diff
==============================================================================
--- 
velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java
 (original)
+++ 
velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LinkTool.java
 Thu Sep 25 17:09:35 2008
@@ -31,6 +31,7 @@
 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.SkipSetters;
 import org.apache.velocity.tools.config.ValidScope;
 
 /**
@@ -76,6 +77,7 @@
  * @version $Id: LinkTool.java 601976 2007-12-07 03:50:51Z nbubna $
  */
 @DefaultKey("link")
[EMAIL PROTECTED]
 @ValidScope(Scope.REQUEST)
 public class LinkTool extends SafeConfig implements Cloneable
 {
@@ -133,11 +135,11 @@
         self = this;
     }
 
-    private void logError(String msg, Throwable t)
+    protected final void debug(String msg, Throwable t, Object... args)
     {
-        if (this.LOG != null)
+        if (LOG != null && LOG.isDebugEnabled())
         {
-            this.LOG.error("LinkTool: "+msg, t);
+            LOG.debug("LinkTool: "+String.format(msg, args), t);
         }
     }
 
@@ -193,7 +195,7 @@
         String chrst = props.getString(CHARSET_KEY);
         if (chrst != null)
         {
-            this.charset = chrst;
+            setCharacterEncoding(chrst);
         }
 
         Boolean xhtml = props.getBoolean(XHTML_MODE_KEY);
@@ -204,23 +206,6 @@
     }
 
     /**
-     * <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 xhtml if true, the XHTML query data delimiter ('&amp;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.
@@ -234,12 +219,41 @@
         catch (CloneNotSupportedException e)
         {
             String msg = "Could not properly clone " + getClass();
-            logError(msg, e);
+            if (LOG != null)
+            {
+                LOG.error(msg, e);
+            }
             throw new RuntimeException(msg, e);
         }
     }
 
-    protected void setScheme(Object obj)
+    public void setCharacterEncoding(String chrst)
+    {
+        this.charset = chrst;
+    }
+
+    /**
+     * <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 xhtml if true, the XHTML query data delimiter ('&amp;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>
+     */
+    public void setXHTML(boolean xhtml)
+    {
+        queryDelim = (xhtml) ? XHTML_QUERY_DELIMITER : HTML_QUERY_DELIMITER;
+    }
+
+    /**
+     * This will treat empty strings like null values
+     * and will trim any trailing ':' character.
+     */
+    public void setScheme(Object obj)
     {
         if (obj == null)
         {
@@ -259,17 +273,22 @@
         }
     }
 
-    protected void setUserInfo(Object obj)
+    public void setUserInfo(Object obj)
     {
         this.user = obj == null ? null : String.valueOf(obj);
     }
 
-    protected void setHost(Object obj)
+    public void setHost(Object obj)
     {
         this.host = obj == null ? null : String.valueOf(obj);
     }
 
-    protected void setPort(Object obj)
+    /**
+     * If the specified object is null, this will set the port value
+     * to -1 to indicate that.  If it is non-null and cannot be converted
+     * to an integer, then it will be set to -2 to indicate an error.
+     */
+    public void setPort(Object obj)
     {
         if (obj == null)
         {
@@ -287,13 +306,18 @@
             }
             catch (NumberFormatException nfe)
             {
-                logError("Could convert '"+obj+"' to int", nfe);
+                debug("Could convert '%s' to int", nfe, obj);
                 this.port = -2; // use this to mean error
             }
         }
     }
 
-    protected void setPath(Object obj)
+    /**
+     * If this instance is not opaque and the specified value does
+     * not start with a '/' character, then that will be prepended
+     * automatically.
+     */
+    public void setPath(Object obj)
     {
         if (obj == null)
         {
@@ -309,7 +333,13 @@
         }
     }
 
-    protected void appendPath(Object obj)
+    /**
+     * Uses [EMAIL PROTECTED] #combinePath} to add the specified value
+     * to the current [EMAIL PROTECTED] #getPath} value.  If the specified
+     * value is null or this instance is opaque, then this is
+     * a no-op.
+     */
+    public void appendPath(Object obj)
     {
         if (obj != null && !this.opaque)
         {
@@ -317,6 +347,12 @@
         }
     }
 
+    /**
+     * If end is null, this will return start and vice versa.
+     * If neither is null, this will append the end to the start,
+     * making sure that there is only one '/' character between
+     * the two values.
+     */
     protected String combinePath(String start, String end)
     {
         if (end == null)
@@ -345,7 +381,15 @@
         }
     }
 
-    protected void setQuery(Object obj)
+    /**
+     * This will trim any '?' character at the start of the
+     * specified value.  It will also check the value to see if
+     * it already includes multiple query pairs by checking for
+     * any '&amp;' characters in the string.  If there are some, then
+     * the delimiters between the pairs will all be replaced
+     * with this instance's configured query delimiter.
+     */
+    public void setQuery(Object obj)
     {
         if (obj == null)
         {
@@ -368,7 +412,32 @@
         }
     }
 
-    protected void appendQuery(Object obj)
+    /**
+     * Converts the map of keys to values into a query string
+     * and uses [EMAIL PROTECTED] #appendQuery(Object)} to add it to the
+     * current [EMAIL PROTECTED] #getQuery} value.
+     */
+    public void appendQuery(Map parameters)
+    {
+        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()));
+        }
+        appendQuery(query);
+    }
+
+    /**
+     * Uses [EMAIL PROTECTED] #combineQuery} to append the specified value
+     * to the current [EMAIL PROTECTED] #getQuery} value.
+     */
+    public void appendQuery(Object obj)
     {
         if (obj != null)
         {
@@ -376,6 +445,13 @@
         }
     }
 
+    /**
+     * If the second param is null or empty, this will simply return the first
+     * and vice versa.  Otherwise, it will trim any '?'
+     * at the start of the second param and any '&amp;' or '&amp;amp;' at the
+     * end of the first one, then combine the two, making sure that they
+     * are separated by only one delimiter.
+     */
     protected String combineQuery(String current, String add)
     {
         if (add == null || add.length() == 0)
@@ -386,7 +462,7 @@
         {
             add = add.substring(1, add.length());
         }
-        if (current == null)
+        if (current == null || current.length() == 0)
         {
             return add;
         }
@@ -410,6 +486,12 @@
         return current + queryDelim + add;
     }
 
+    /**
+     * Turns the specified key and value into a properly encoded
+     * query pair string.  If the value is an array or List, then
+     * this will create a delimited string of query pairs, reusing 
+     * the same key for each of the values separately.
+     */
     protected String toQuery(Object key, Object value)
     {
         StringBuilder out = new StringBuilder();
@@ -437,7 +519,7 @@
     }
 
     /* Utility method to avoid logic duplication in toQuery() */
-    private void appendAsArray(StringBuilder out, Object key, Object[] arr)
+    protected void appendAsArray(StringBuilder out, Object key, Object[] arr)
     {
         String encKey = encode(key);
         for (int i=0; i < arr.length; i++)
@@ -455,8 +537,24 @@
         }
     }
 
+    /**
+     * Uses [EMAIL PROTECTED] #parseQuery(String,String)} to parse the 
specified
+     * query string using this instance's current query delimiter.
+     */
     protected Map<String,Object> parseQuery(String query)
     {
+        return parseQuery(query, this.queryDelim);
+    }
+
+    /**
+     * This will use the specified query delimiter to parse the specified
+     * query string into a map of keys to values.
+     * If there are multiple query pairs in the string that have the same
+     * key, then the values will be combined into a single List value
+     * associated with that key.
+     */
+    protected Map<String,Object> parseQuery(String query, String queryDelim)
+    {
         if (query.startsWith("?"))
         {
             query = query.substring(1, query.length());
@@ -493,7 +591,10 @@
         return params;
     }
 
-    protected void setFragment(Object obj)
+    /**
+     * Sets the anchor for this instance and treats empty strings like null.
+     */
+    public void setFragment(Object obj)
     {
         if (obj == null)
         {
@@ -509,6 +610,13 @@
         }
     }
 
+    /**
+     * If the specified value is null, this will set the scheme, userInfo,
+     * host, port, path, query, and fragment all to their null-equivalent
+     * values.  Otherwise, this will
+     * convert the specified object into a [EMAIL PROTECTED] URI}, then those 
same
+     * values from the URI object to this instance.
+     */
     protected boolean setFromURI(Object obj)
     {
         if (obj == null)
@@ -537,7 +645,7 @@
             }
             catch (Exception e)
             {
-                logError("Could convert '"+obj+"' to URI", e);
+                debug("Could convert '%s' to URI", e, obj);
                 return false;
             }
         }
@@ -565,6 +673,11 @@
         return true;
     }
 
+    /**
+     * Tries to create a URI from the current port, opacity, scheme,
+     * userInfo, host, path, query and fragment set for this instance,
+     * using the [EMAIL PROTECTED] URI} constructor that is appropriate to the 
opacity.
+     */
     protected URI createURI()
     {
         try
@@ -596,7 +709,7 @@
         }
         catch (Exception e)
         {
-            logError("Could not create URI", e);
+            debug("Could not create URI", e);
         }
         return null;
     }
@@ -604,6 +717,26 @@
     // --------------------------------------------- Template Methods 
-----------
 
     /**
+     * Returns the configured charset used by the [EMAIL PROTECTED] #encode} 
and
+     * [EMAIL PROTECTED] #decode} methods.
+     */
+    public String getCharacterEncoding()
+    {
+        return this.charset;
+    }
+
+    /**
+     * Returns true if the query delimiter used by this instance is
+     * using <code>&amp;amp;</code> as the delimiter for query data pairs
+     * or just using <code>&amp;</code>.
+     */
+    public boolean isXHTML()
+    {
+        return queryDelim.equals(XHTML_QUERY_DELIMITER);
+    }
+
+
+    /**
      * Returns a new instance with the specified value set as its scheme.
      */
     public LinkTool scheme(Object scheme)
@@ -809,13 +942,14 @@
      * 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, as that
-     * is the core piece of the "root". It thus will
-     * return null for any opaque URLs as well.
+     * and will return null if there is no host or if there
+     * was an error when the port value was last set. It will
+     * return null for any opaque URLs as well, as those have
+     * no host or port.
      */
     public String getRoot()
     {
-        if (host == null)
+        if (host == null || opaque || port == -2)
         {
             return null;
         }
@@ -1037,18 +1171,7 @@
             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);
+        copy.appendQuery(parameters);
         return copy;
     }
 
@@ -1170,7 +1293,7 @@
         }
         catch (UnsupportedEncodingException uee)
         {
-            logError("Character encoding '"+charset+"' is unsupported", uee);
+            debug("Character encoding '%s' is unsupported", uee, charset);
             return null;
         }
     }
@@ -1193,7 +1316,7 @@
         }
         catch (UnsupportedEncodingException uee)
         {
-            logError("Character encoding '"+charset+"' is unsupported", uee);
+            debug("Character encoding '%s' is unsupported", uee, charset);
             return null;
         }
     }


Reply via email to