Added: 
velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/generic/XmlTool.java
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/generic/XmlTool.java?rev=1776916&view=auto
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/generic/XmlTool.java
 (added)
+++ 
velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/generic/XmlTool.java
 Mon Jan  2 10:49:55 2017
@@ -0,0 +1,691 @@
+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.Reader;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.velocity.tools.XmlUtils;
+
+import org.apache.velocity.tools.ConversionUtils;
+import org.apache.velocity.tools.config.DefaultKey;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+/**
+ * <p>Tool for reading/navigating XML files.  This uses dom4j under the
+ * covers to provide complete XPath support for traversing XML files.</p>
+ * <p>Configuration parameters:</p>
+ * <p>
+ *     <ul>
+ *         <li><code>resource</code>=<i>file or classpath resource</i></li>
+ *         <li><code>source</code>=<i>URL</i></li>
+ *     </ul>
+ * </p>
+ *
+ * <p>Here's a short example:<pre>
+ * XML file:
+ *   &lt;foo&gt;&lt;bar&gt;woogie&lt;/bar&gt;&lt;a name="test"/&gt;&lt;/foo&gt;
+ *
+ * Template:
+ *   $foo.bar.text
+ *   $foo.find('a')
+ *   $foo.a.name
+ *
+ * Output:
+ *   woogie
+ *   &lt;a name="test"/&gt;
+ *   test
+ *
+ * Configuration:
+ * &lt;tools&gt;
+ *   &lt;toolbox scope="application"&gt;
+ *     &lt;tool class="org.apache.velocity.tools.generic.XmlTool"
+ *              key="foo" source="doc.xml"/&gt;
+ *   &lt;/toolbox&gt;
+ * &lt;/tools&gt;
+ * </pre></p>
+ * <p>Note that this tool is included in the default GenericTools configuration
+ * under the key "xml". You can read  but unless you set safeMode="false" for 
it, you will
+ * only be able to parse XML strings.  Safe mode is on by default and blocks
+ * access to the {@link #read(String)} method.</p>
+ *
+ * @author Nathan Bubna
+ * @author Claude Brisson
+ * @version $Revision: 1769055 $ $Date: 2006-11-27 10:49:37 -0800 (Mon, 27 Nov 
2006) $
+ * @since VelocityTools 2.0
+ */
+
+@DefaultKey("xml")
+public class XmlTool extends SafeConfig implements Serializable
+{
+    /**
+     * ImportSupport utility which provides underlying i/o
+     */
+    protected ImportSupport importSupport = null;
+
+    /**
+     * ImportSupport initialization
+     * @param config
+     */
+    protected void initializeImportSupport(ValueParser config)
+    {
+        importSupport = new ImportSupport();
+        importSupport.configure(config);
+    }
+
+    /**
+     * Configuration.
+     * @param values
+     */
+    protected void configure(ValueParser values)
+    {
+        super.configure(values);
+        initializeImportSupport(values);
+        String resource = values.getString(ImportSupport.RESOURCE_KEY);
+        if (resource != null)
+        {
+            read(resource);
+        }
+        else
+        {
+            String url = values.getString(ImportSupport.URL_KEY);
+            if (url != null)
+            {
+                fetch(url);
+            }
+        }
+    }
+
+    /**
+     * Content nodes.
+     */
+    private List<Node> nodes = null;
+
+    /**
+     * Default constructor.
+     */
+    public XmlTool() {}
+
+    /**
+     * Builds an XmlTool around a node.
+     * @param node
+     */
+    public XmlTool(Node node)
+    {
+        this(Collections.singletonList(node));
+    }
+
+    /**
+     * Builds an XmlTool around a nodes list.
+     * @param nodes
+     */
+    public XmlTool(List<Node> nodes)
+    {
+        this.nodes = nodes;
+    }
+
+    /**
+     * Sets a singular root {@link Node} for this instance.
+     */
+    protected void setRoot(Node node)
+    {
+        if (node == null)
+        {
+            this.nodes = null;
+        }
+        else
+        {
+            if (node instanceof Document)
+            {
+                node = ((Document) node).getDocumentElement();
+            }
+            this.nodes = new ArrayList<Node>(1);
+            this.nodes.add(node);
+        }
+    }
+
+    /**
+     * Parses the given XML string and uses the resulting {@link Document}
+     * as the root {@link Node}.
+     */
+    public void parse(String xml)
+    {
+        try
+        {
+            if (xml != null)
+            {
+                setRoot(XmlUtils.parse(xml));
+            }
+        }
+        catch (Exception e)
+        {
+            getLog().error("could not parse given XML string", e);
+        }
+    }
+
+    /**
+     * Reads and parses a local resource file
+     */
+    public void read(String resource)
+    {
+        try
+        {
+            Reader reader = importSupport.getResourceReader(resource);
+            if (reader != null)
+            {
+                setRoot(XmlUtils.parse(reader));
+            }
+        }
+        catch (Exception e)
+        {
+            getLog().error("could not read XML resource {}", resource, e);
+        }
+    }
+
+    /**
+     * Reads and parses a remote or local URL
+     */
+    public void fetch(String url)
+    {
+        try
+        {
+            Reader reader = importSupport.acquireReader(url);
+            if (reader != null)
+            {
+                setRoot(XmlUtils.parse(reader));
+            }
+        }
+        catch (Exception e)
+        {
+            getLog().error("could not fetch XML content from URL {}", url, e);
+        }
+    }
+
+    /**
+     * This will first attempt to find an attribute with the
+     * specified name and return its value.  If no such attribute
+     * exists or its value is {@code null}, this will attempt to convert
+     * the given value to a {@link Number} and get the result of
+     * {@link #get(Number)}.  If the number conversion fails,
+     * then this will convert the object to a string. If that string
+     * does not contain a '/', it appends the result of {@link #getPath()}
+     * and a '/' to the front of it.  Finally, it delegates the string to the
+     * {@link #find(String)} method and returns the result of that.
+     */
+    public Object get(Object o)
+    {
+        if (isEmpty() || o == null)
+        {
+            return null;
+        }
+        String attr = attr(o);
+        if (attr != null && attr.length() > 0)
+        {
+            return attr;
+        }
+        Number i = ConversionUtils.toNumber(o);
+        if (i != null)
+        {
+            return get(i);
+        }
+        String s = String.valueOf(o);
+        if (s.length() == 0)
+        {
+            return null;
+        }
+        if (s.indexOf('/') < 0)
+        {
+            s = getPath()+'/'+s;
+        }
+        return find(s);
+    }
+
+
+    /**
+     * Asks {@link #get(Object)} for a "name" result.
+     * If none, this will return the result of {@link #getNodeName()}.
+     */
+    public Object getName()
+    {
+        // give attributes and child elements priority
+        Object name = get("name");
+        if (name != null && !"".equals(name))
+        {
+            return name;
+        }
+        return getNodeName();
+    }
+
+    /**
+     * Returns the name of the root node. If the internal {@link Node}
+     * list has more than one {@link Node}, it will only return the name
+     * of the first node in the list.
+     */
+    public String getNodeName()
+    {
+        if (isEmpty())
+        {
+            return null;
+        }
+        return node().getNodeName();
+    }
+
+    /**
+     * Returns the XPath that identifies the first/sole {@link Node}
+     * represented by this instance.
+     */
+    public String getPath()
+    {
+        if (isEmpty())
+        {
+            return null;
+        }
+        return XmlUtils.nodePath(node());
+    }
+
+    /**
+     * Returns the value of the specified attribute for the first/sole
+     * {@link Node} in the internal Node list for this instance, if that
+     * Node is an {@link Element}.  If it is a non-Element node type or
+     * there is no value for that attribute in this element, then this
+     * will return {@code null}.
+     */
+    public String attr(Object o)
+    {
+        if (o == null)
+        {
+            return null;
+        }
+        String key = String.valueOf(o);
+        Node node = node();
+        if (node instanceof Element)
+        {
+            String value = ((Element)node).getAttribute(key);
+            if (value.length() > 0)
+            {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a {@link Map} of all attributes for the first/sole
+     * {@link Node} held internally by this instance.  If that Node is
+     * not an {@link Element}, this will return null.
+     */
+    public Map<String,String> attributes()
+    {
+        Node node = node();
+        if (node instanceof Element)
+        {
+            Map<String,String> attrs = new HashMap<String,String>();
+            NamedNodeMap map = node.getAttributes();
+            for (int i = 0; i < map.getLength(); ++i)
+            {
+                Attr attr = (Attr)map.item(i);
+                attrs.put(attr.getName(), attr.getValue());
+            }
+            return attrs;
+        }
+        return null;
+    }
+
+
+    /**
+     * Returns {@code true} if there are no {@link Node}s internally held
+     * by this instance.
+     */
+    public boolean isEmpty()
+    {
+        return (nodes == null || nodes.isEmpty());
+    }
+
+    /**
+     * Returns the number of {@link Node}s internally held by this instance.
+     */
+    public int size()
+    {
+        if (isEmpty())
+        {
+            return 0;
+        }
+        return nodes.size();
+    }
+
+    /**
+     * Returns an {@link Iterator} that returns new {@link XmlTool}
+     * instances for each {@link Node} held internally by this instance.
+     */
+    public Iterator<XmlTool> iterator()
+    {
+        if (isEmpty())
+        {
+            return null;
+        }
+        return new NodeIterator(nodes.iterator());
+    }
+
+    /**
+     * Returns an {@link XmlTool} that wraps only the
+     * first {@link Node} from this instance's internal Node list.
+     */
+    public XmlTool getFirst()
+    {
+        if (size() == 1)
+        {
+            return this;
+        }
+        return new XmlTool(node());
+    }
+
+    /**
+     * Returns an {@link XmlTool} that wraps only the
+     * last {@link Node} from this instance's internal Node list.
+     */
+    public XmlTool getLast()
+    {
+        if (size() == 1)
+        {
+            return this;
+        }
+        return new XmlTool(nodes.get(size() - 1));
+    }
+
+    /**
+     * Returns an {@link XmlTool} that wraps the specified
+     * {@link Node} from this instance's internal Node list.
+     */
+    public XmlTool get(Number n)
+    {
+        if (n == null)
+        {
+            return null;
+        }
+        int i = n.intValue();
+        if (i < 0 || i > size() - 1)
+        {
+            return null;
+        }
+        return new XmlTool(nodes.get(i));
+    }
+
+    /**
+     * Returns the first/sole {@link Node} from this
+     * instance's internal Node list, if any.
+     */
+    public Node node()
+    {
+        if (isEmpty())
+        {
+            return null;
+        }
+        return nodes.get(0);
+    }
+
+
+    /**
+     * Converts the specified object to a String and calls
+     * {@link #find(String)} with that.
+     */
+    public XmlTool find(Object o)
+    {
+        if (o == null || isEmpty())
+        {
+            return null;
+        }
+        return find(String.valueOf(o));
+    }
+
+    /**
+     * Performs an XPath selection on the current set of
+     * {@link Node}s held by this instance and returns a new
+     * {@link XmlTool} instance that wraps those results.
+     * If the specified value is null or this instance does
+     * not currently hold any nodes, then this will return 
+     * {@code null}.  If the specified value, when converted
+     * to a string, does not contain a '/' character, then
+     * it has "//" prepended to it.  This means that a call to
+     * {@code $xml.find("a")} is equivalent to calling
+     * {@code $xml.find("//a")}.  The full range of XPath
+     * selectors is supported here.
+     */
+    public XmlTool find(String xpath)
+    {
+        if (xpath == null || xpath.length() == 0)
+        {
+            return null;
+        }
+        if (xpath.indexOf('/') < 0)
+        {
+            xpath = "//"+xpath;
+        }
+        List<Node> found = new ArrayList<Node>();
+        for (Node n : nodes)
+        {
+            NodeList lst = XmlUtils.search(xpath, n);
+            if (lst != null)
+            {
+                for (int i = 0; i < lst.getLength(); ++i)
+                {
+                    found.add(lst.item(i));
+                }
+            }
+        }
+        if (found.isEmpty())
+        {
+            return null;
+        }
+        return new XmlTool(found);
+    }
+
+    /**
+     * Returns a new {@link XmlTool} instance that wraps
+     * the parent {@link Element} of the first/sole {@link Node}
+     * being wrapped by this instance.
+     */
+    public XmlTool getParent()
+    {
+        if (isEmpty())
+        {
+            return null;
+        }
+        Node parent = node().getParentNode();
+        if (parent == null || parent instanceof Document)
+        {
+            return null;
+        }
+        return new XmlTool(parent);
+    }
+
+    /**
+     * Returns a new {@link XmlTool} instance that wraps
+     * the parent {@link Element}s of each of the {@link Node}s
+     * being wrapped by this instance.  This does not return
+     * all ancestors, just the immediate parents.
+     */
+    public XmlTool parents()
+    {
+        if (isEmpty())
+        {
+            return null;
+        }
+        if (size() == 1)
+        {
+            return getParent();
+        }
+        List<Node> parents = new ArrayList<Node>(size());
+        for (Node n : nodes)
+        {
+            Element parent = null;
+            if (n instanceof Element)
+            {
+                parent = (Element)n.getParentNode();
+            }
+            else if (n instanceof Attr)
+            {
+                parent = ((Attr) n).getOwnerElement();
+            }
+            if (parent != null && !parents.contains(parent))
+            {
+                parents.add(parent);
+            }
+        }
+        if (parents.isEmpty())
+        {
+            return null;
+        }
+        return new XmlTool(parents);
+    }
+
+    /**
+     * Returns a new {@link XmlTool} instance that wraps all the
+     * child {@link Element}s of all the current internally held nodes
+     * that are {@link Element}s themselves.
+     */
+    public XmlTool children()
+    {
+        if (isEmpty())
+        {
+            return null;
+        }
+        List<Node> kids = new ArrayList<Node>();
+        for (Node n : nodes)
+        {
+            if (n instanceof Element)
+            {
+                NodeList lst = n.getChildNodes();
+                for (int i = 0; i < lst.getLength(); ++i)
+                {
+                    Node child = lst.item(i);
+                    if (child instanceof Text)
+                    {
+                        String value = child.getNodeValue().trim();
+                        if (value.length() == 0)
+                        {
+                            continue;
+                        }
+                    }
+                    kids.add(child);
+                }
+            }
+        }
+        return new XmlTool(kids);
+    }
+
+    /**
+     * Returns the concatenated text content of all the internally held
+     * nodes.  Obviously, this is most useful when only one node is held.
+     */
+    public String getText()
+    {
+        if (isEmpty())
+        {
+            return null;
+        }
+        StringBuilder out = new StringBuilder();
+        for (Node n : nodes)
+        {
+            String text = n.getTextContent().trim();
+            if (text != null && text.length() > 0)
+            {
+                out.append(text);
+            }
+        }
+        String result = out.toString().trim();
+        if (result.length() > 0)
+        {
+            return result;
+        }
+        return null;
+    }
+
+
+    /**
+     * If this instance has no XML {@link Node}s, then this
+     * returns the result of {@code super.toString()}.  Otherwise, it
+     * returns the XML (as a string) of all the internally held nodes
+     * that are not {@link Attr}ibutes. For attributes, only the value
+     * is used.
+     */
+    public String toString()
+    {
+        if (isEmpty())
+        {
+            return super.toString();
+        }
+        StringBuilder out = new StringBuilder();
+        for (Node n : nodes)
+        {
+            if (n instanceof Attr)
+            {
+                out.append(((Attr)n).getValue().trim());
+            }
+            else
+            {
+                out.append(XmlUtils.nodeToString(n));
+            }
+        }
+        return out.toString().trim();
+    }
+
+    
+    /**
+     * Iterator implementation that wraps a Node list iterator
+     * to return new XmlTool instances for each item in the wrapped
+     * iterator.s
+     */
+    public static class NodeIterator implements Iterator<XmlTool>
+    {
+        private Iterator<Node> i;
+
+        public NodeIterator(Iterator<Node> i)
+        {
+            this.i = i;
+        }
+
+        public boolean hasNext()
+        {
+            return i.hasNext();
+        }
+
+        public XmlTool next()
+        {
+            return new XmlTool(i.next());
+        }
+
+        public void remove()
+        {
+            i.remove();
+        }
+    }
+}

Modified: 
velocity/tools/trunk/velocity-tools-generic/src/main/resources/org/apache/velocity/tools/generic/tools.xml
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/main/resources/org/apache/velocity/tools/generic/tools.xml?rev=1776916&r1=1776915&r2=1776916&view=diff
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-generic/src/main/resources/org/apache/velocity/tools/generic/tools.xml
 (original)
+++ 
velocity/tools/trunk/velocity-tools-generic/src/main/resources/org/apache/velocity/tools/generic/tools.xml
 Mon Jan  2 10:49:55 2017
@@ -40,9 +40,12 @@
     </toolbox>
     <toolbox scope="request">
         <tool class="org.apache.velocity.tools.generic.ContextTool"/>
+        <tool class="org.apache.velocity.tools.generic.ImportTool"/>
+        <tool class="org.apache.velocity.tools.generic.JsonTool"/>
         <tool class="org.apache.velocity.tools.generic.LinkTool"/>
         <tool class="org.apache.velocity.tools.generic.LoopTool"/>
         <tool class="org.apache.velocity.tools.generic.RenderTool"/>
+        <tool class="org.apache.velocity.tools.generic.XmlTool"/>
         <!--
         This is not useful in its default form.
         But, if it were, it'd be request-scoped.

Copied: 
velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/JsonToolTests.java
 (from r1770862, 
velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/ClassToolTests.java)
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/JsonToolTests.java?p2=velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/JsonToolTests.java&p1=velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/ClassToolTests.java&r1=1770862&r2=1776916&rev=1776916&view=diff
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/ClassToolTests.java
 (original)
+++ 
velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/JsonToolTests.java
 Mon Jan  2 10:49:55 2017
@@ -19,348 +19,31 @@ package org.apache.velocity.tools.generi
  * under the License.
  */
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import org.json.JSONArray;
+import org.junit.Test;
 
-import java.lang.annotation.Annotation;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
-import org.apache.velocity.tools.config.DefaultKey;
-import org.apache.velocity.tools.config.InvalidScope;
-import org.apache.velocity.tools.config.SkipSetters;
-import org.junit.Test;
+import static org.junit.Assert.assertEquals;
 
 /**
- * <p>Tests for {@link ClassTool}</p>
+ * <p>Tests for {@link JsonTool}</p>
  *
- * @author Nathan Bubna
- * @since VelocityTools 2.0
+ * @author Claude Brisson
+ * @since VelocityTools 3.0
  * @version $Id$
  */
-public class ClassToolTests {
-
-    public @Test void ctorClassTool() throws Exception
-    {
-        try
-        {
-            new ClassTool();
-        }
-        catch (Exception e)
-        {
-            fail("Default constructor failed");
-        }
-    }
-
-    public @Test void ctorClassTool_ClassToolClass() throws Exception
-    {
-        // null parent should fail
-        try
-        {
-            new ClassTool(null, ClassTool.class);
-            fail("Constructor 'ClassTool(null, Class)' worked but shouldn't 
have.");
-        }
-        catch (Exception e)
-        {
-        }
-
-        ClassTool parent = new ClassTool();
-        // null class should fail
-        try
-        {
-            new ClassTool(parent, null);
-            fail("Constructor 'ClassTool(ClassTool, null)' worked but 
shouldn't have.");
-        }
-        catch (Exception e)
-        {
-        }
-
-        // this one should work
-        try
-        {
-            new ClassTool(parent, ClassToolTests.class);
-        }
-        catch (Exception e)
-        {
-            fail("Constructor 'ClassTool(ClassTool, Class)' failed due to: " + 
e);
-        }
-    }
-
-    public @Test void methodConfigure_Map() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        assertEquals(Object.class, classTool.getType());
-
-        // change the inspected type to Map
-        Map<String,Object> conf = new HashMap<String,Object>();
-        conf.put(ClassTool.INSPECT_KEY, "java.util.Map");
-        classTool.configure(conf);
-        assertEquals(Map.class, classTool.getType());
-        //TODO: test other configuration settings
-    }
-
-    public @Test void methodGetAnnotations() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertTrue(classTool.getAnnotations().isEmpty());
-        classTool.setType(MyDeprecated.class);
-        assertEquals(1, classTool.getAnnotations().size());
-        classTool.setType(ValueParser.class);
-        List<Annotation> annotations = classTool.getAnnotations();
-        assertEquals(3, annotations.size());
-        Collections.sort(annotations, new Comparator<Annotation>()
-        {
-            @Override
-            public int compare(Annotation o1, Annotation o2)
-            {
-                return o1.toString().compareTo(o2.toString());
-            }
-        });
-        assertTrue(annotations.get(0).annotationType() == DefaultKey.class);
-        assertTrue(annotations.get(1).annotationType() == InvalidScope.class);
-        assertTrue(annotations.get(2).annotationType() == SkipSetters.class);
-    }
-
-    public @Test void methodGetConstructors() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        List result = classTool.getConstructors();
-        assertNotNull(result);
-        assertFalse(result.isEmpty());
-        //TODO: test contents of list?
-    }
-
-    //TODO: add ConstructorSub tests
-
-    public @Test void methodGetFields() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        List result = classTool.getFields();
-        assertNotNull(result);
-        assertTrue(result.isEmpty());
-        //TODO: test a class that does have fields
-    }
-
-    //TODO: add FieldSub tests
-
-    public @Test void methodGetMethods() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        List result = classTool.getMethods();
-        assertNotNull(result);
-        assertFalse(result.isEmpty());
-        //TODO: test contents of list?
-    }
-
-    //TODO: add MethodSub tests
-
-    public @Test void methodGetTypes() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        Set result = classTool.getTypes();
-        assertNotNull(result);
-        assertFalse(result.isEmpty());
-        //TODO: test contents of set?
-    }
-
-    public @Test void methodGetFullName() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        String result = classTool.getFullName();
-        assertEquals(result, "java.lang.Object");
-    }
-
-    public @Test void methodGetName() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        String result = classTool.getName();
-        assertEquals(classTool.getName(), "Object");
-    }
-
-    public @Test void methodGetPackage() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertEquals(classTool.getPackage(), "java.lang");
-    }
-
-    public @Test void methodGetType() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        Class result = classTool.getType();
-        assertEquals(result, Object.class);
-        classTool.setType(ClassTool.class);
-
-        result = classTool.getType();
-        assertEquals(result, ClassTool.class);
-    }
-
-    public @Test void methodGetSuper() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object which has no super
-        assertNull(classTool.getSuper());
-        classTool.setType(ClassTool.class);
-        assertEquals(classTool.getSuper().getType(), SafeConfig.class);
-    }
+public class JsonToolTests
+{
 
-    public @Test void methodInspect_Class() throws Exception
+    public @Test void testJson() throws Exception
     {
-        ClassTool classTool = new ClassTool();
-        ClassTool result = classTool.inspect(ClassTool.class);
-        assertEquals(result.getType(), ClassTool.class);
+        Map config = new HashMap();
+        config.put("resource", "foo.json");
+        JsonTool jsonTool = new JsonTool();
+        jsonTool.configure(config);
+        assertEquals(jsonTool.get("foo"), "bar");
+        assertEquals(jsonTool.get("array").getClass(), JSONArray.class);
     }
-
-    public @Test void methodInspect_Object() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        ClassTool result = classTool.inspect(classTool);
-        assertEquals(result.getType(), ClassTool.class);
-    }
-
-    public @Test void methodInspect_String() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        assertNull(classTool.inspect((String)null));
-        assertNull(classTool.inspect(""));
-        assertNull(classTool.inspect("bad"));
-        assertEquals(Map.class, classTool.inspect("java.util.Map").getType());
-    }
-
-    public @Test void methodIsAbstract() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertFalse(classTool.isAbstract());
-        classTool.setType(MyAbstract.class);
-        assertTrue(classTool.isAbstract());
-    }
-    
-    protected static abstract class MyAbstract 
-    {
-        // do nothing
-    }
-
-    public @Test void methodIsDeprecated() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertFalse(classTool.isDeprecated());
-        classTool.setType(MyDeprecated.class);
-        assertTrue(classTool.isDeprecated());
-    }
-
-    @Deprecated
-    protected static class MyDeprecated
-    {
-        // do nothing
-    }
-
-    public @Test void methodIsFinal() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertFalse(classTool.isFinal());
-        classTool.setType(String.class);
-        assertTrue(classTool.isFinal());
-    }
-
-    public @Test void methodIsInterface() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertFalse(classTool.isInterface());
-        classTool.setType(Map.class);
-        assertTrue(classTool.isInterface());
-    }
-
-    public @Test void methodIsPrivate() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertFalse(classTool.isPrivate());
-        classTool.setType(PrivateStrictStatic.class);
-        assertTrue(classTool.isPrivate());
-    }
-
-    public @Test void methodIsProtected() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertFalse(classTool.isProtected());
-        classTool.setType(ProtectedNoDefaultCtor.class);
-        assertTrue(classTool.isProtected());
-    }
-
-    public @Test void methodIsPublic() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertTrue(classTool.isPublic());
-        classTool.setType(PrivateStrictStatic.class);
-        assertFalse(classTool.isPublic());
-    }
-
-    public @Test void methodIsStatic() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertFalse(classTool.isStatic());
-        classTool.setType(PrivateStrictStatic.class);
-        assertTrue(classTool.isStatic());
-    }
-
-/*  CB - commented because on some JVM (ex: 1.6.0_02-b05 linux)
-    the strictfp modifier is lost at runtime on Classes
-
-    public @Test void methodIsStrict() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type is java.lang.Object
-        assertFalse(classTool.isStrict());
-        classTool.setType(PrivateStrictStatic.class);
-        assertTrue(classTool.isStrict());
-    }
-*/
-    public @Test void methodSetClass_Class() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        assertEquals(Object.class, classTool.getType());
-        classTool.setType(ClassTool.class);
-        assertEquals(ClassTool.class, classTool.getType());
-    }
-
-    public @Test void methodSupportsNewInstance() throws Exception
-    {
-        ClassTool classTool = new ClassTool();
-        // default type class is java.lang.Object
-        assertEquals(classTool.supportsNewInstance(), true);
-        classTool.setType(ProtectedNoDefaultCtor.class);
-        assertEquals(classTool.supportsNewInstance(), false);
-    }
-
-    private static strictfp class PrivateStrictStatic {}
-
-    protected static class ProtectedNoDefaultCtor
-    {
-        public ProtectedNoDefaultCtor(String foo)
-        {
-        }
-    }
-
 }
-        

Added: 
velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/XmlToolTests.java
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/XmlToolTests.java?rev=1776916&view=auto
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/XmlToolTests.java
 (added)
+++ 
velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/generic/XmlToolTests.java
 Mon Jan  2 10:49:55 2017
@@ -0,0 +1,318 @@
+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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.junit.Test;
+import org.w3c.dom.Node;
+
+/**
+ * <p>Tests for XmlTool</p>
+ *
+ * @author Nathan Bubna
+ * @since VelocityTools 2.0
+ * @version $Id$
+ */
+public class XmlToolTests {
+
+    private static final String XML_FILE = "file.xml";
+
+    private static final String XML_STRING =
+"<foo>\n  <bar name=\"a\"/>\n  <baz>woogie</baz>\n  <baz>wiggie</baz>\n</foo>";
+
+    public @Test void ctorXmlTool() throws Exception
+    {
+        try
+        {
+            new XmlTool();
+        }
+        catch (Exception e)
+        {
+            fail("Constructor 'XmlTool()' failed due to: " + e);
+        }
+    }
+
+    private XmlTool stringBased() throws Exception
+    {
+        XmlTool xml = new XmlTool();
+        xml.configure(new ValueParser());
+        xml.parse(XML_STRING);
+        return xml;
+    }
+
+    private XmlTool fileBased() throws Exception
+    {
+        XmlTool xml = new XmlTool();
+        xml.configure(new ValueParser());
+        xml.read(XML_FILE);
+        return xml;
+    }
+
+    public @Test void testStringFileEquals() throws Exception
+    {
+        String string = stringBased().toString();
+        String file = fileBased().toString();
+        //System.out.println("string:\n"+string+"\nfile:\n"+file);
+        assertEquals(string, file);
+    }
+
+    public @Test void methodAttr_Object() throws Exception
+    {
+        XmlTool xml = stringBased();
+        assertNull(xml.attr("href"));
+        xml = xml.find("bar");
+        assertNotNull(xml.attr("name"));
+        assertEquals("a", xml.attr("name"));
+    }
+
+    public @Test void methodAttributes() throws Exception
+    {
+        XmlTool xml = stringBased();
+        Map<String,String> result = xml.attributes();
+        assertTrue(result.isEmpty());
+        xml = xml.find("bar");
+        result = xml.attributes();
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        assertEquals("a", result.get("name"));
+    }
+
+    public @Test void methodChildren() throws Exception
+    {
+        XmlTool xml = stringBased();
+        assertEquals(1, xml.size());
+        XmlTool result = xml.children();
+        assertEquals(3, result.size());
+    }
+
+    public @Test void methodGetParent() throws Exception
+    {
+        XmlTool xml = stringBased().find("bar");
+        assertNotNull(xml);
+        XmlTool foo = xml.getParent();
+        assertNotNull(foo);
+        assertEquals(1, foo.size());
+        assertNull(foo.getParent());
+    }
+
+    public @Test void methodParents() throws Exception
+    {
+        XmlTool foo = stringBased();
+        XmlTool xml = foo.find("bar");
+        assertEquals(foo.toString(), xml.parents().toString());
+        xml = foo.children();
+        assertEquals(3, xml.size());
+        foo = xml.parents();
+        assertEquals(1, foo.size());
+    }
+
+    public @Test void methodConfigure_ValueParser() throws Exception
+    {
+        XmlTool xml = new XmlTool();
+        Map<String,String> params = new HashMap<String,String>();
+        assertEquals("resource", ImportSupport.RESOURCE_KEY);
+        params.put(ImportSupport.RESOURCE_KEY, XML_FILE);
+        xml.configure(params);
+        assertEquals(1, xml.size());
+        assertEquals("foo", xml.getName());
+    }
+
+    public @Test void methodFind_Object() throws Exception
+    {
+        XmlTool xml = stringBased();
+        XmlTool result = xml.find((Object)null);
+        assertNull(result);
+        assertEquals(xml.find("bar").toString(), xml.find("//bar").toString());
+        //TODO: test more xpath expressions?
+        //TODO: test expressions with no results
+    }
+
+    public @Test void methodGetFirst() throws Exception
+    {
+        XmlTool xml = stringBased();
+        assertSame(xml, xml.getFirst());
+        xml = xml.children();
+        assertEquals(3, xml.size());
+        xml = xml.getFirst();
+        assertEquals(1, xml.size());
+        assertEquals("a", xml.getName());
+    }
+
+    public @Test void methodGetLast() throws Exception
+    {
+        XmlTool xml = stringBased();
+        assertSame(xml, xml.getLast());
+        xml = xml.children();
+        assertEquals(3, xml.size());
+        xml = xml.getLast();
+        assertEquals(1, xml.size());
+        assertEquals("baz", xml.getName());
+        assertEquals("wiggie", xml.getText());
+    }
+
+    public @Test void methodGetName() throws Exception
+    {
+        XmlTool xml = stringBased();
+        assertEquals(1, xml.size());
+        assertEquals("foo", xml.getName());
+        xml = xml.find("bar");
+        assertEquals("a", xml.getName());
+    }
+
+    public @Test void methodGetNodeName() throws Exception
+    {
+        XmlTool xml = stringBased();
+        assertEquals("foo", xml.getNodeName());
+        xml = xml.find("baz");
+        assertEquals("baz", xml.getNodeName());
+    }
+
+    public @Test void methodGetText() throws Exception
+    {
+        XmlTool xml = stringBased();
+        //TODO: prepare the instance for testing
+        String result = xml.getText();
+        assertEquals("woogie\n  wiggie", result);
+    }
+
+    public @Test void methodGet_Number() throws Exception
+    {
+        XmlTool xml = stringBased();
+        assertEquals(xml.toString(), xml.get(0).toString());
+        xml = xml.children();
+        assertEquals("bar", xml.get(0).getNodeName());
+        assertEquals("baz", xml.get(1).getName());
+        assertEquals("baz", xml.get(2).getName());
+        assertNull(xml.get(3));
+        assertNull(xml.get(-1));
+    }
+
+    public @Test void methodGet_Object() throws Exception
+    {
+        XmlTool xml = stringBased();
+        assertNull(xml.get(null));
+        assertNull(xml.get(""));
+        assertNull(xml.get("null"));
+        Object result = xml.get("bar");
+        assertNotNull(result);
+        assertTrue(result instanceof XmlTool);
+        xml = (XmlTool)result;
+        result = null;
+        assertNull(result);
+        result = xml.get("0");
+        assertNotNull(result);
+        assertEquals(result.toString(), xml.toString());
+        result = null;
+        assertNull(result);
+        result = xml.get("name");
+        assertNotNull(result);
+        assertEquals("a", result);
+    }
+
+    public @Test void methodIsEmpty() throws Exception
+    {
+        XmlTool xml = new XmlTool();
+        assertTrue(xml.isEmpty());
+        xml.parse(XML_STRING);
+        assertFalse(xml.isEmpty());
+    }
+
+    public @Test void methodIterator() throws Exception
+    {
+        XmlTool xml = new XmlTool();
+        assertNull(xml.iterator());
+        xml.parse(XML_STRING);
+        Iterator<XmlTool> i = xml.iterator();
+        assertNotNull(i);
+        XmlTool foo = i.next();
+        assertNotNull(foo);
+        assertEquals(foo.toString(), xml.toString());
+        xml = xml.children();
+        i = xml.iterator();
+        assertEquals("a", i.next().attr("name"));
+        assertEquals("baz", i.next().getName());
+        assertEquals("wiggie", i.next().getText());
+        assertFalse(i.hasNext());
+    }
+
+    public @Test void methodNode() throws Exception
+    {
+        XmlTool xml = new XmlTool();
+        assertNull(xml.node());
+        xml.parse(XML_STRING);
+        Node n = xml.node();
+        assertNotNull(n);
+    }
+
+    public @Test void methodParse_Object() throws Exception
+    {
+        XmlTool xml = new XmlTool();
+
+        xml.parse((String)null);
+        assertTrue(xml.isEmpty());
+
+        xml.parse("><S asdf8 ~$");
+        assertTrue(xml.isEmpty());
+
+        xml.parse(XML_STRING);
+        assertFalse(xml.isEmpty());
+        //TODO: test other strings?
+    }
+
+    public @Test void methodSize() throws Exception
+    {
+        XmlTool xml = new XmlTool();
+        assertEquals(0, xml.size());
+        xml.parse(XML_STRING);
+        assertEquals(1, xml.size());
+        xml = xml.children();
+        assertEquals(3, xml.size());
+        xml = xml.getLast();
+        assertEquals(1, xml.size());
+    }
+
+    public @Test void methodToString() throws Exception
+    {
+        XmlTool xml = new XmlTool();
+        xml.configure(new ValueParser());
+        assertTrue(xml.toString().startsWith(XmlTool.class.getName()));
+        xml.read(XML_FILE);
+        assertTrue(xml.toString().startsWith("<foo>"));
+        assertTrue(xml.toString().endsWith("</foo>"));
+        XmlTool bar = xml.find("bar");
+        assertEquals("<bar name=\"a\"/>", bar.toString());
+        XmlTool baz = (XmlTool)xml.get("baz");
+        assertEquals("<baz>woogie</baz><baz>wiggie</baz>", baz.toString());
+    }
+
+
+}
+        
\ No newline at end of file

Modified: 
velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/test/whitebox/GenericToolsTests.java
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/test/whitebox/GenericToolsTests.java?rev=1776916&r1=1776915&r2=1776916&view=diff
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/test/whitebox/GenericToolsTests.java
 (original)
+++ 
velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/test/whitebox/GenericToolsTests.java
 Mon Jan  2 10:49:55 2017
@@ -38,6 +38,7 @@ import org.apache.velocity.tools.generic
 import org.apache.velocity.tools.generic.DateTool;
 import org.apache.velocity.tools.generic.EscapeTool;
 import org.apache.velocity.tools.generic.FieldTool;
+import org.apache.velocity.tools.generic.JsonTool;
 import org.apache.velocity.tools.generic.MathTool;
 import org.apache.velocity.tools.generic.NumberTool;
 import org.apache.velocity.tools.generic.ResourceTool;
@@ -155,17 +156,17 @@ public class GenericToolsTests {
         assertEquals(new Integer(5),mathTool.floor(5.1));
         assertEquals(6,mathTool.getAverage(new long[] {5,6,7}));
         /* getTotal() watches the type of its first argument, so assertEquals 
needs a long */
-        assertEquals((long)7,mathTool.getTotal(new long[] {2,2,3}));
-        assertEquals(new Integer(8),mathTool.idiv(130,16));
+        assertEquals((long)7,mathTool.getTotal(new long[]{2, 2, 3}));
+        assertEquals(new Integer(8), mathTool.idiv(130, 16));
         assertEquals(9,mathTool.max(9,-10));
-        assertEquals(10,mathTool.min(10,20));
+        assertEquals(10, mathTool.min(10, 20));
         assertEquals(new Integer(11),mathTool.mod(37,13));
-        assertEquals(12,mathTool.mul(3,4));
+        assertEquals(12, mathTool.mul(3, 4));
         assertEquals(new Integer(13),mathTool.round(12.8));
         assertEquals(new Double(14.2),mathTool.roundTo(1,14.18));
         assertEquals(new Double(-5.0),mathTool.roundTo(2,-4.999));
         assertEquals(15,mathTool.sub(30,15));
-        assertEquals(16,mathTool.pow(4,2));
+        assertEquals(16, mathTool.pow(4, 2));
         assertEquals(new Integer(17),mathTool.toInteger("17"));
         assertEquals(new Double(18.1),mathTool.toDouble("18.1"));
     }

Added: velocity/tools/trunk/velocity-tools-generic/src/test/resources/file.xml
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/test/resources/file.xml?rev=1776916&view=auto
==============================================================================
--- velocity/tools/trunk/velocity-tools-generic/src/test/resources/file.xml 
(added)
+++ velocity/tools/trunk/velocity-tools-generic/src/test/resources/file.xml Mon 
Jan  2 10:49:55 2017
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<foo>
+  <bar name="a"/>
+  <baz>woogie</baz>
+  <baz>wiggie</baz>
+</foo>
\ No newline at end of file

Added: velocity/tools/trunk/velocity-tools-generic/src/test/resources/foo.json
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/test/resources/foo.json?rev=1776916&view=auto
==============================================================================
--- velocity/tools/trunk/velocity-tools-generic/src/test/resources/foo.json 
(added)
+++ velocity/tools/trunk/velocity-tools-generic/src/test/resources/foo.json Mon 
Jan  2 10:49:55 2017
@@ -0,0 +1 @@
+{ "foo": "bar", "array": [ "foo1", "foo2"] }

Modified: velocity/tools/trunk/velocity-tools-uberjar/pom.xml
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-uberjar/pom.xml?rev=1776916&r1=1776915&r2=1776916&view=diff
==============================================================================
--- velocity/tools/trunk/velocity-tools-uberjar/pom.xml (original)
+++ velocity/tools/trunk/velocity-tools-uberjar/pom.xml Mon Jan  2 10:49:55 2017
@@ -47,7 +47,6 @@
                             <artifactSet>
                                 <includes>
                                     
<include>org.apache.velocity:velocity-tools-generic</include>
-                                    
<include>org.apache.velocity:velocity-tools-xml</include>
                                     
<include>org.apache.velocity:velocity-tools-view</include>
                                     
<include>org.apache.velocity:velocity-tools-view-jsp</include>
                                 </includes>

Modified: 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/BrowserTool.java
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/BrowserTool.java?rev=1776916&r1=1776915&r2=1776916&view=diff
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/BrowserTool.java
 (original)
+++ 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/BrowserTool.java
 Mon Jan  2 10:49:55 2017
@@ -93,6 +93,7 @@ import javax.servlet.http.HttpServletReq
  * @since VelocityTools 2.0
  * @version $Revision$ $Date$
  */
+
 @DefaultKey("browser")
 @InvalidScope(Scope.APPLICATION)
 public class BrowserTool extends BrowserToolDeprecatedMethods implements 
java.io.Serializable

Modified: 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ImportTool.java
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ImportTool.java?rev=1776916&r1=1776915&r2=1776916&view=diff
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ImportTool.java
 (original)
+++ 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ImportTool.java
 Mon Jan  2 10:49:55 2017
@@ -22,7 +22,7 @@ package org.apache.velocity.tools.view;
 import org.apache.velocity.tools.Scope;
 import org.apache.velocity.tools.config.DefaultKey;
 import org.apache.velocity.tools.config.ValidScope;
-import org.apache.velocity.tools.view.ImportSupport;
+import org.apache.velocity.tools.generic.ValueParser;
 
 /**
  * General-purpose text-importing view tool for templates.
@@ -46,8 +46,19 @@ import org.apache.velocity.tools.view.Im
 
 @DefaultKey("import")
 @ValidScope(Scope.REQUEST)
-public class ImportTool extends ImportSupport
+public class ImportTool extends org.apache.velocity.tools.generic.ImportTool
 {
+    protected void initializeImportSupport(ValueParser config)
+    {
+        importSupport = new ViewImportSupport();
+        importSupport.configure(config);
+    }
+
+    protected void configure(ValueParser values)
+    {
+        super.configure(values);
+    }
+
     /**
      * Returns the supplied URL rendered as a String.
      *
@@ -57,24 +68,23 @@ public class ImportTool extends ImportSu
     public String read(Object obj) {
         if (obj == null)
         {
-            getLog().warn("ImportTool.read(): url is null!");
+            getLog().warn("URL is null!");
             return null;
         }
         String url = String.valueOf(obj).trim();
         if (url.length() == 0)
         {
-            getLog().warn("ImportTool.read(): url is empty string!");
+            getLog().warn("URL is empty string!");
             return null;
         }
         try
         {
-            return acquireString(url);
+            return importSupport.acquireString(url);
         }
         catch (Exception ex)
         {
-            getLog().error("ImportTool.read(): Exception while aquiring '{}'", 
url, ex);
+            getLog().error("Exception while acquiring '{}'", url, ex);
             return null;
         }
     }
-
 }

Added: 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/JsonTool.java
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/JsonTool.java?rev=1776916&view=auto
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/JsonTool.java
 (added)
+++ 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/JsonTool.java
 Mon Jan  2 10:49:55 2017
@@ -0,0 +1,94 @@
+package org.apache.velocity.tools.view;
+
+/*
+ * 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 javax.servlet.ServletRequest;
+
+import org.apache.velocity.tools.generic.ImportSupport;
+import org.apache.velocity.tools.generic.ValueParser;
+
+import org.apache.velocity.tools.Scope;
+import org.apache.velocity.tools.config.DefaultKey;
+import org.apache.velocity.tools.config.ValidScope;
+
+/**
+ * View version of {@link org.apache.velocity.tools.generic.JsonTool}. It adds 
an automatic parsing of the HTTP query
+ * body content, if it is found to be of JSON type.
+ *
+ * @author Claude Brisson
+ * @since VelocityTools 3.0
+ * @version $Id:$
+ */
+
+@DefaultKey("json")
+@ValidScope(Scope.REQUEST)
+public class JsonTool extends org.apache.velocity.tools.generic.JsonTool
+{
+
+    /**
+     * Importsupport initialization
+     * @param config
+     */
+    @Override
+    protected void initializeImportSupport(ValueParser config)
+    {
+        importSupport = new ViewImportSupport();
+        importSupport.configure(config);
+    }
+
+    /**
+     * Check if a given mime type is of JSON type
+     * @param mimeType
+     * @return whether given mime type is of JSON type
+     */
+    protected static boolean isJsonMimeType(String mimeType)
+    {
+        return mimeType != null &&
+            (
+                "text/json".equals(mimeType) ||
+                "application/json".equals(mimeType) ||
+                mimeType.endsWith("+json")
+            );
+    }
+
+    /**
+     * Configuration. Parses request body if appropriate.
+     * @param parser
+     */
+    protected void configure(ValueParser parser)
+    {
+        super.configure(parser);
+        if (root() == null)
+        {
+            ServletRequest request = 
(ServletRequest)parser.get(ViewContext.REQUEST);
+            if (request.getContentLength() > 0 && 
isJsonMimeType(request.getContentType()))
+            {
+                try
+                {
+                    initJSON(request.getReader());
+                }
+                catch (Exception e)
+                {
+                    getLog().error("could not parse JSON object", e);
+                }
+            }
+        }
+    }
+}

Copied: 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ViewImportSupport.java
 (from r1776915, 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ImportSupport.java)
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ViewImportSupport.java?p2=velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ViewImportSupport.java&p1=velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ImportSupport.java&r1=1776915&r2=1776916&rev=1776916&view=diff
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ImportSupport.java
 (original)
+++ 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/ViewImportSupport.java
 Mon Jan  2 10:49:55 2017
@@ -19,11 +19,9 @@ package org.apache.velocity.tools.view;
  * under the License.
  */
 
-import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.Reader;
 import java.io.StringReader;
@@ -31,42 +29,56 @@ import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
 import java.net.HttpURLConnection;
 import java.net.URL;
-import java.net.URLConnection;
 import java.util.Locale;
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponseWrapper;
 
-import org.apache.velocity.tools.generic.SafeConfig;
+import org.apache.velocity.tools.generic.ImportSupport;
+import org.apache.velocity.tools.generic.ValueParser;
 import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * <p>Provides methods to import arbitrary local or remote resources as 
strings.</p>
  * <p>Based on ImportSupport from the JSTL taglib by Shawn Bayern</p>
  *
  * @author <a href="mailto:mari...@centrum.is";>Marino A. Jonsson</a>
- * @since VelocityTools 2.0
+ * @author Claude Brisson
+ * @since VelocityTools 3.0
  * @version $Revision$ $Date$
  */
-public abstract class ImportSupport extends SafeConfig
+public class ViewImportSupport extends ImportSupport
 {
-    protected static final String VALID_SCHEME_CHARS =
-        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-";
-
-    /** Default character encoding for response. */
-    protected static final String DEFAULT_ENCODING = "UTF-8";
-
     protected ServletContext application;
     protected HttpServletRequest request;
     protected HttpServletResponse response;
 
-
     // --------------------------------------- Setup Methods -------------
 
+    protected void configure(ValueParser values)
+    {
+        super.configure(values);
+        HttpServletRequest request = 
(HttpServletRequest)values.get(ViewContext.REQUEST);
+        if (request != null)
+        {
+            setRequest(request);
+        }
+        HttpServletResponse response = 
(HttpServletResponse)values.get(ViewContext.RESPONSE);
+        if (response != null)
+        {
+            setResponse(response);
+        }
+        ServletContext servletContext = 
(ServletContext)values.get(ViewContext.SERVLET_CONTEXT_KEY);
+        if (servletContext != null)
+        {
+            setServletContext(servletContext);
+        }
+    }
+
     /**
      * Sets the current {@link HttpServletRequest}. This is required
      * for this tool to operate and will throw a NullPointerException
@@ -120,8 +132,8 @@ public abstract class ImportSupport exte
      * methods handle the common.core logic of loading either a URL or a local
      * resource.
      *
-     * We consider the 'natural' form of absolute URLs to be Readers and
-     * relative URLs to be Strings.  Thus, to avoid doing extra work,
+     * We consider the 'natural' form of remote URLs to be Readers and
+     * local URLs to be Strings.  Thus, to avoid doing extra work,
      * acquireString() and acquireReader() delegate to one another as
      * appropriate.  (Perhaps I could have spelled things out more clearly,
      * but I thought this implementation was instructive, not to mention
@@ -130,225 +142,110 @@ public abstract class ImportSupport exte
 
     /**
      *
-     * @param url the URL resource to return as string
+     * @param url the remote URL resource to return as string
      * @return the URL resource as string
      * @throws IOException
-     * @throws java.lang.Exception
      */
-    protected String acquireString(String url) throws IOException, Exception {
-        // Record whether our URL is absolute or relative
-        if (isAbsoluteUrl(url))
-        {
-            // for absolute URLs, delegate to our peer
-            BufferedReader r = null;
-            try
-            {
-                r = new BufferedReader(acquireReader(url));
-                StringBuilder sb = new StringBuilder();
-                int i;
-                // under JIT, testing seems to show this simple loop is as fast
-                // as any of the alternatives
-                while ((i = r.read()) != -1)
-                {
-                    sb.append((char)i);
-                }
-                return sb.toString();
-            }
-            finally
-            {
-                if(r != null)
-                {
-                    try
-                    {
-                        r.close();
-                    }
-                    catch (IOException ioe)
-                    {
-                        getLog().error("Could not close reader.", ioe);
-                    }
-                }
-               }
-        }
-        else // handle relative URLs ourselves
+    protected String acquireRemoteURLString(String url) throws IOException
+    {
+        if (isSafeMode())
         {
-            // URL is relative, so we must be an HTTP request
-            if (!(request instanceof HttpServletRequest
-                  && response instanceof HttpServletResponse))
-            {
-                throw new Exception("Relative import from non-HTTP request not 
allowed");
-            }
-
-            // retrieve an appropriate ServletContext
-            // normalize the URL if we have an HttpServletRequest
-            if (!url.startsWith("/"))
-            {
-                String sp = ((HttpServletRequest)request).getServletPath();
-                url = sp.substring(0, sp.lastIndexOf('/')) + '/' + url;
-            }
-
-            // strip the session id from the url
-            url = stripSession(url);
-
-            // from this context, get a dispatcher
-            RequestDispatcher rd = application.getRequestDispatcher(url);
-            if (rd == null)
-            {
-                throw new Exception("Couldn't get a RequestDispatcher for \""
-                                    + url + "\"");
-            }
-
-            // include the resource, using our custom wrapper
-            ImportResponseWrapper irw =
-                new ImportResponseWrapper((HttpServletResponse)response);
-            try
-            {
-                rd.include(request, irw);
-            }
-            catch (IOException ex)
-            {
-                throw new Exception("Problem importing the relative URL \""
-                                    + url + "\". " + ex);
-            }
-            catch (RuntimeException ex)
-            {
-                throw new Exception("Problem importing the relative URL \""
-                                    + url + "\". " + ex);
-            }
-
-            // disallow inappropriate response codes per JSTL spec
-            if (irw.getStatus() < 200 || irw.getStatus() > 299)
-            {
-                throw new Exception("Invalid response code '" + irw.getStatus()
-                                    + "' for \"" + url + "\"");
-            }
-
-            // recover the response String from our wrapper
-            return irw.getString();
+            getLog().warn("safe mode prevented reading resource from remote 
url: {} ", url);
+            return null;
         }
+        return super.acquireRemoteURLString(url);
     }
 
     /**
      *
-     * @param url the URL to read
-     * @return a Reader for the InputStream created from the supplied URL
+     * @param url the local URL resource to return as string
+     * @return the URL resource as string
      * @throws IOException
      * @throws java.lang.Exception
      */
-    protected Reader acquireReader(String url) throws IOException, Exception
+    protected String acquireLocalURLString(String url) throws IOException
     {
-        if (!isAbsoluteUrl(url))
+        // URL is local, so we must be an HTTP request
+        if (!(request instanceof HttpServletRequest
+            && response instanceof HttpServletResponse))
         {
-            // for relative URLs, delegate to our peer
-            return new StringReader(acquireString(url));
+            throw new IOException("Local import from non-HTTP request not 
allowed");
         }
-        else
+
+        // retrieve an appropriate ServletContext
+        // normalize the URL if we have an HttpServletRequest
+        if (!url.startsWith("/"))
         {
-            // absolute URL
-            URLConnection uc = null;
-            HttpURLConnection huc = null;
-            InputStream i = null;
+            String sp = ((HttpServletRequest)request).getServletPath();
+            url = sp.substring(0, sp.lastIndexOf('/')) + '/' + url;
+        }
 
-            try
-            {
-                // handle absolute URLs ourselves, using java.net.URL
-                URL u = new URL(url);
-                // URL u = new URL("http", "proxy.hi.is", 8080, target);
-                uc = u.openConnection();
-                i = uc.getInputStream();
+        // strip the session id from the url
+        url = stripSession(url);
 
-                // check response code for HTTP URLs, per spec,
-                if (uc instanceof HttpURLConnection)
-                {
-                    huc = (HttpURLConnection)uc;
-
-                    int status = huc.getResponseCode();
-                    if (status < 200 || status > 299)
-                    {
-                        throw new Exception(status + " " + url);
-                    }
-                }
+        // from this context, get a dispatcher
+        RequestDispatcher rd = application.getRequestDispatcher(url);
+        if (rd == null)
+        {
+            throw new IOException("Couldn't get a RequestDispatcher for \""
+                + url + "\"");
+        }
 
-                // okay, we've got a stream; encode it appropriately
-                Reader r = null;
-                String charSet;
-
-                // charSet extracted according to RFC 2045, section 5.1
-                String contentType = uc.getContentType();
-                if (contentType != null)
-                {
-                    charSet = 
ImportSupport.getContentTypeAttribute(contentType, "charset");
-                    if (charSet == null)
-                    {
-                        charSet = DEFAULT_ENCODING;
-                    }
-                }
-                else
-                {
-                    charSet = DEFAULT_ENCODING;
-                }
+        // include the resource, using our custom wrapper
+        ImportResponseWrapper irw =
+            new ImportResponseWrapper((HttpServletResponse)response);
+        try
+        {
+            rd.include(request, irw);
+        }
+        catch (IOException ex)
+        {
+            throw new IOException("Problem importing the local URL \"" + url + 
"\": " + ex.getMessage(), ex);
+        }
+        catch (ServletException se)
+        {
+            throw new IOException("Problem importing the local URL \"" + url + 
"\": " + se.getMessage(), se);
 
-                try
-                {
-                    r = new InputStreamReader(i, charSet);
-                }
-                catch (UnsupportedEncodingException ueex)
-                {
-                    r = new InputStreamReader(i, DEFAULT_ENCODING);
-                }
+        }
+        /* let RuntimeExceptions go through */
 
-                if (huc == null)
-                {
-                    return r;
-                }
-                else
-                {
-                    return new SafeClosingHttpURLConnectionReader(r, huc);
-                }
-            }
-            catch (IOException ex)
-            {
-                if (i != null)
-                {
-                    try
-                    {
-                        i.close();
-                    }
-                    catch (IOException ioe)
-                    {
-                        getLog().error("Could not close InputStream", ioe);
-                    }
-                }
+        // disallow inappropriate response codes per JSTL spec
+        if (irw.getStatus() < 200 || irw.getStatus() > 299)
+        {
+            throw new IOException("Invalid response code '" + irw.getStatus()
+                + "' for \"" + url + "\"");
+        }
 
-                if (huc != null)
-                {
-                    huc.disconnect();
-                }
-                throw new Exception("Problem accessing the absolute URL \""
-                                    + url + "\". " + ex);
-            }
-            catch (RuntimeException ex)
-            {
-                if (i != null)
-                {
-                    try
-                    {
-                        i.close();
-                    }
-                    catch (IOException ioe)
-                    {
-                        getLog().error("Could not close InputStream", ioe);
-                    }
-                }
+        // recover the response String from our wrapper
+        return irw.getString();
+    }
 
-                if (huc != null)
-                {
-                    huc.disconnect();
-                }
-                // because the spec makes us
-                throw new Exception("Problem accessing the absolute URL \""
-                                    + url + "\". " + ex);
-            }
+    /**
+     *
+     * @param url the URL to read
+     * @return a Reader for the InputStream created from the supplied URL
+     * @throws IOException
+     */
+    protected Reader acquireRemoteURLReader(String url) throws IOException
+    {
+        if (isSafeMode())
+        {
+            getLog().warn("safe mode prevented reading resource from remote 
url: {}", url);
+            return null;
         }
+        return super.acquireRemoteURLReader(url);
+    }
+
+    /**
+     *
+     * @param url the URL to read
+     * @return a Reader for the InputStream created from the supplied URL
+     * @throws IOException
+     * @throws java.lang.Exception
+     */
+    protected Reader acquireLocalURLReader(String url) throws IOException
+    {
+        return new StringReader(acquireLocalURLString(url));
     }
 
     protected static class SafeClosingHttpURLConnectionReader extends Reader
@@ -564,40 +461,6 @@ public abstract class ImportSupport exte
     // Public utility methods
 
     /**
-     * Returns <tt>true</tt> if our current URL is absolute,
-     * <tt>false</tt> otherwise.
-     *
-     * @param url the url to check out
-     * @return true if the url is absolute
-     */
-    public static boolean isAbsoluteUrl(String url) {
-        // a null URL is not absolute, by our definition
-        if (url == null)
-        {
-            return false;
-        }
-
-        // do a fast, simple check first
-        int colonPos;
-        if ((colonPos = url.indexOf(':')) == -1)
-        {
-            return false;
-        }
-
-        // if we DO have a colon, make sure that every character
-        // leading up to it is a valid scheme character
-        for (int i = 0; i < colonPos; i++)
-        {
-            if (VALID_SCHEME_CHARS.indexOf(url.charAt(i)) == -1)
-            {
-                return false;
-            }
-        }
-        // if so, we've got an absolute url
-        return true;
-    }
-
-    /**
      * Strips a servlet session ID from <tt>url</tt>.  The session ID
      * is encoded as a URL "path parameter" beginning with "jsessionid=".
      * We thus remove anything we find between ";jsessionid=" (inclusive)
@@ -627,56 +490,14 @@ public abstract class ImportSupport exte
         return u.toString();
     }
 
-    /**
-     * Get the value associated with a content-type attribute.
-     * Syntax defined in RFC 2045, section 5.1.
-     *
-     * @param input the string containing the attributes
-     * @param name the name of the content-type attribute
-     * @return the value associated with a content-type attribute
-     */
-    public static String getContentTypeAttribute(String input, String name)
-    {
-        int begin;
-        int end;
-        int index = input.toUpperCase().indexOf(name.toUpperCase());
-        if (index == -1)
-        {
-            return null;
-        }
-        index = index + name.length(); // positioned after the attribute name
-        index = input.indexOf('=', index); // positioned at the '='
-        if (index == -1)
-        {
-            return null;
-        }
-        index += 1; // positioned after the '='
-        input = input.substring(index).trim();
+    //*********************************************************************
+    // Fetch local resource
 
-        if (input.charAt(0) == '"')
-        {
-            // attribute value is a quoted string
-            begin = 1;
-            end = input.indexOf('"', begin);
-            if (end == -1)
-            {
-                return null;
-            }
-        }
-        else
-        {
-            begin = 0;
-            end = input.indexOf(';');
-            if (end == -1)
-            {
-                end = input.indexOf(' ');
-            }
-            if (end == -1)
-            {
-                end = input.length();
-            }
-        }
-        return input.substring(begin, end).trim();
+    @Override
+    protected URL getFileResource(String resource) throws Exception
+    {
+        return application.getResource(resource);
     }
 
+
 }

Added: 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/XmlTool.java
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/XmlTool.java?rev=1776916&view=auto
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/XmlTool.java
 (added)
+++ 
velocity/tools/trunk/velocity-tools-view/src/main/java/org/apache/velocity/tools/view/XmlTool.java
 Mon Jan  2 10:49:55 2017
@@ -0,0 +1,76 @@
+package org.apache.velocity.tools.view;
+
+/*
+ * 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.apache.velocity.tools.XmlUtils;
+import org.apache.velocity.tools.generic.ValueParser;
+
+import javax.servlet.ServletRequest;
+
+/**
+ * View version of {@link org.apache.velocity.tools.generic.XmlTool}. It adds 
an automatic parsing of the HTTP query
+ * body content, if it is found to be of JSON type.
+ *
+ * @author Claude Brisson
+ * @since VelocityTools 3.0
+ * @version $Id:$
+ */
+
+public class XmlTool extends org.apache.velocity.tools.generic.XmlTool
+{
+    /**
+     * ImportSupport initialization.
+     * @param config
+     */
+    @Override
+    protected void initializeImportSupport(ValueParser config)
+    {
+        importSupport = new ViewImportSupport();
+        importSupport.configure(config);
+    }
+
+    /**
+     * Configuration. Parses request body if appropriate.
+     * @param values
+     */
+    protected void configure(ValueParser values)
+    {
+        super.configure(values);
+        if (isEmpty())
+        {
+            ServletRequest request = 
(ServletRequest)values.get(ViewContext.REQUEST);
+            if (request.getContentLength() > 0)
+            {
+                String mimeType = request.getContentType();
+                if (XmlUtils.isXmlMimeType(mimeType))
+                {
+                    try
+                    {
+                        setRoot(XmlUtils.parse(request.getReader()));
+                    }
+                    catch (Exception e)
+                    {
+                        getLog().error("could not parse given XML string", e);
+                    }
+                }
+            }
+        }
+    }
+}

Modified: 
velocity/tools/trunk/velocity-tools-view/src/main/resources/org/apache/velocity/tools/view/tools.xml
URL: 
http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-view/src/main/resources/org/apache/velocity/tools/view/tools.xml?rev=1776916&r1=1776915&r2=1776916&view=diff
==============================================================================
--- 
velocity/tools/trunk/velocity-tools-view/src/main/resources/org/apache/velocity/tools/view/tools.xml
 (original)
+++ 
velocity/tools/trunk/velocity-tools-view/src/main/resources/org/apache/velocity/tools/view/tools.xml
 Mon Jan  2 10:49:55 2017
@@ -25,11 +25,13 @@
         <tool class="org.apache.velocity.tools.view.CookieTool"/>
         <tool class="org.apache.velocity.tools.view.ImportTool"/>
         <tool class="org.apache.velocity.tools.view.IncludeTool"/>
+        <tool class="org.apache.velocity.tools.view.JsonTool"/>
         <tool class="org.apache.velocity.tools.view.LinkTool"/>
         <tool class="org.apache.velocity.tools.view.PagerTool"/>
         <tool class="org.apache.velocity.tools.view.ParameterTool"/>
         <tool class="org.apache.velocity.tools.view.ViewContextTool"/>
-        <!-- 
+        <tool class="org.apache.velocity.tools.view.XmlTool"/>
+        <!--
         This is not directly useable.
         <tool class="org.apache.velocity.tools.view.AbstractSearchTool"/>
         -->


Reply via email to