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: + * <foo><bar>woogie</bar><a name="test"/></foo> + * + * Template: + * $foo.bar.text + * $foo.find('a') + * $foo.a.name + * + * Output: + * woogie + * <a name="test"/> + * test + * + * Configuration: + * <tools> + * <toolbox scope="application"> + * <tool class="org.apache.velocity.tools.generic.XmlTool" + * key="foo" source="doc.xml"/> + * </toolbox> + * </tools> + * </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"/> -->