/* $This file is distributed under the terms of the license in /doc/license.txt$ */ package freemarker.ext.dump; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Before; import org.junit.Test; import freemarker.core.Environment; import freemarker.ext.beans.BeansWrapper; import freemarker.ext.dump.BaseDumpDirective.DateType; import freemarker.ext.dump.BaseDumpDirective.Key; import freemarker.ext.dump.BaseDumpDirective.Type; import freemarker.template.Configuration; import freemarker.template.SimpleCollection; import freemarker.template.Template; import freemarker.template.TemplateCollectionModel; import freemarker.template.TemplateDirectiveBody; import freemarker.template.TemplateDirectiveModel; import freemarker.template.TemplateException; import freemarker.template.TemplateMethodModel; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; public class DumpDirectiveTest { private Template template; @Before public void setUp() { Configuration config = new Configuration(); String templateStr = ""; try { template = new Template("template", new StringReader(templateStr), config); } catch (Exception e) { fail(e.getMessage()); } // Turn off log messages to console Logger.getLogger(BaseDumpDirective.class).setLevel(Level.OFF); } @Test public void dumpString() { String varName = "dog"; Map dataModel = new HashMap(); String value = "Rover"; dataModel.put(varName, value); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.STRING); expectedDump.put(Key.VALUE.toString(), value); test(varName, dataModel, expectedDump); } @Test public void dumpBoolean() { String varName = "isLoggedIn"; Map dataModel = new HashMap(); boolean value = true; dataModel.put(varName, value); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.BOOLEAN); expectedDump.put(Key.VALUE.toString(), value); test(varName, dataModel, expectedDump); } @Test public void dumpNumber() { String varName = "tabCount"; Map dataModel = new HashMap(); int value = 7; dataModel.put(varName, value); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.NUMBER); expectedDump.put(Key.VALUE.toString(), value); test(varName, dataModel, expectedDump); } @Test public void dumpSimpleDate() { String varName = "now"; Map dataModel = new HashMap(); Date now = new Date(); dataModel.put(varName, now); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.DATE); expectedDump.put(Key.DATE_TYPE.toString(), DateType.UNKNOWN); expectedDump.put(Key.VALUE.toString(), now); test(varName, dataModel, expectedDump); } @Test public void dumpDateTime() { String varName = "timestamp"; Map dataModel = new HashMap(); Timestamp ts = new Timestamp(1302297332043L); dataModel.put(varName, ts); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.DATE); expectedDump.put(Key.DATE_TYPE.toString(), DateType.DATETIME); expectedDump.put(Key.VALUE.toString(), ts); test(varName, dataModel, expectedDump); } @Test public void dumpSqlDate() { String varName = "date"; Map dataModel = new HashMap(); java.sql.Date date = new java.sql.Date(1302297332043L); dataModel.put(varName, date); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.DATE); expectedDump.put(Key.DATE_TYPE.toString(), DateType.DATE); expectedDump.put(Key.VALUE.toString(), date); test(varName, dataModel, expectedDump); } @Test public void dumpTime() { String varName = "time"; Map dataModel = new HashMap(); Time time = new Time(1302297332043L); dataModel.put(varName, time); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.DATE); expectedDump.put(Key.DATE_TYPE.toString(), DateType.TIME); expectedDump.put(Key.VALUE.toString(), time); test(varName, dataModel, expectedDump); } // RY test method and directive types with and without help methods @Test public void dumpHelplessMethod() { String varName = "square"; Map dataModel = new HashMap(); TemplateMethodModel methodModel = new HelplessMethod(); dataModel.put(varName, methodModel); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.METHOD); expectedDump.put("help", null); test(varName, dataModel, expectedDump); } @Test public void dumpHelpfulMethod() { String varName = "square"; Map dataModel = new HashMap(); TemplateMethodModel methodModel = new HelpfulMethod(); dataModel.put(varName, methodModel); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.METHOD); expectedDump.put("help", getMethodHelp(varName)); test(varName, dataModel, expectedDump); } @Test public void dumpMethodWithBadHelp() { String varName = "square"; Map dataModel = new HashMap(); TemplateMethodModel methodModel = new MethodWithBadHelp(); dataModel.put(varName, methodModel); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.METHOD); expectedDump.put("help", null); test(varName, dataModel, expectedDump); } @Test public void dumpHelplessDirective() { String varName = "dump"; Map dataModel = new HashMap(); TemplateDirectiveModel directiveModel = new HelplessDirective(); dataModel.put(varName, directiveModel); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.DIRECTIVE); expectedDump.put("help", null); test(varName, dataModel, expectedDump); } @Test public void dumpHelpfulDirective() { String varName = "dump"; Map dataModel = new HashMap(); TemplateDirectiveModel directiveModel = new HelpfulDirective(); dataModel.put(varName, directiveModel); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.DIRECTIVE); expectedDump.put("help", getDirectiveHelp(varName)); test(varName, dataModel, expectedDump); } @Test public void dumpDirectiveWithBadHelp() { String varName = "dump"; Map dataModel = new HashMap(); TemplateDirectiveModel directiveModel = new DirectiveWithBadHelp(); dataModel.put(varName, directiveModel); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.DIRECTIVE); expectedDump.put("help", null); test(varName, dataModel, expectedDump); } @Test public void dumpStringList() { String varName = "fruit"; Map dataModel = new HashMap(); List list = new ArrayList(); list.add("apples"); list.add("bananas"); list.add("oranges"); dataModel.put(varName, list); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.SEQUENCE); List> listDump = new ArrayList>(list.size()); for ( String str : list) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.STRING); itemDump.put(Key.VALUE.toString(), str); listDump.add(itemDump); } expectedDump.put(Key.VALUE.toString(), listDump); test(varName, dataModel, expectedDump); } @Test public void dumpStringArray() { String varName = "fruit"; Map dataModel = new HashMap(); String[] arr = { "apples", "bananas", "oranges" }; dataModel.put(varName, arr); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.SEQUENCE); List> arrDump = new ArrayList>(arr.length); for ( String str : arr) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.STRING); itemDump.put(Key.VALUE.toString(), str); arrDump.add(itemDump); } expectedDump.put(Key.VALUE.toString(), arrDump); test(varName, dataModel, expectedDump); } @Test public void dumpMixedList() { String varName = "stuff"; Map dataModel = new HashMap(); List mixedList = new ArrayList(); String myString = "apples"; mixedList.add(myString); int myInt = 4; mixedList.add(myInt); boolean myBool = true; mixedList.add(myBool); List myList = new ArrayList(); myList.add("dog"); myList.add("cat"); myList.add("elephant"); mixedList.add(myList); dataModel.put(varName, mixedList); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.SEQUENCE); List> mixedListDump = new ArrayList>(mixedList.size()); Map stringDump = new HashMap(); stringDump.put(Key.TYPE.toString(), Type.STRING); stringDump.put(Key.VALUE.toString(), myString); mixedListDump.add(stringDump); Map numberDump = new HashMap(); numberDump.put(Key.TYPE.toString(), Type.NUMBER); numberDump.put(Key.VALUE.toString(), myInt); mixedListDump.add(numberDump); Map booleanDump = new HashMap(); booleanDump.put(Key.TYPE.toString(), Type.BOOLEAN); booleanDump.put(Key.VALUE.toString(), myBool); mixedListDump.add(booleanDump); Map myListDump = new HashMap(); myListDump.put(Key.TYPE.toString(), Type.SEQUENCE); List> itemsDump = new ArrayList>(myList.size()); for ( String animal : myList ) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.STRING); itemDump.put(Key.VALUE.toString(), animal); itemsDump.add(itemDump); } myListDump.put(Key.VALUE.toString(), itemsDump); mixedListDump.add(myListDump); expectedDump.put(Key.VALUE.toString(), mixedListDump); test(varName, dataModel, expectedDump); } @Test public void dumpNumberSet() { String varName = "oddNums"; Map dataModel = new HashMap(); Set odds = new HashSet(); for (int i=0; i <= 10; i++) { if (i % 2 == 1) { odds.add(i); } } dataModel.put(varName, odds); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.SEQUENCE); List> sequenceDump = new ArrayList>(odds.size()); for ( int i : odds ) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.NUMBER); itemDump.put(Key.VALUE.toString(), i); sequenceDump.add(itemDump); } expectedDump.put(Key.VALUE.toString(), sequenceDump); test(varName, dataModel, expectedDump); } @Test public void dumpNumberCollection() { String varName = "oddNums"; Map dataModel = new HashMap(); Set odds = new HashSet(); for (int i=0; i <= 10; i++) { if (i % 2 == 1) { odds.add(i); } } TemplateCollectionModel collection = new SimpleCollection(odds); dataModel.put(varName, collection); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.COLLECTION); List> collectionDump = new ArrayList>(odds.size()); for ( int i : odds ) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.NUMBER); itemDump.put(Key.VALUE.toString(), i); collectionDump.add(itemDump); } expectedDump.put(Key.VALUE.toString(), collectionDump); test(varName, dataModel, expectedDump); } @Test public void dumpHash() { } @Test public void dumpStringToStringMap() { String varName = "capitals"; Map dataModel = new HashMap(); Map myMap = new HashMap(); myMap.put("Albany", "New York"); myMap.put("St. Paul", "Minnesota"); myMap.put("Austin", "Texas"); myMap.put("Sacramento", "California"); myMap.put("Richmond", "Virginia"); dataModel.put(varName, myMap); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.HASH_EX); Map myMapDump = new HashMap(myMap.size()); for ( String key : myMap.keySet() ) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.STRING); itemDump.put(Key.VALUE.toString(), myMap.get(key)); myMapDump.put(key, itemDump); } expectedDump.put(Key.VALUE.toString(), myMapDump); test(varName, dataModel, expectedDump); } @Test public void dumpStringToObjectMap() { String varName = "stuff"; Map dataModel = new HashMap(); Map mixedMap = new HashMap(); String myString = "apples"; mixedMap.put("myString", myString); boolean myBool = true; mixedMap.put("myBoolean", myBool); int myInt = 4; mixedMap.put("myNumber", myInt); Date now = new Date(); mixedMap.put("myDate", now); List myList = new ArrayList(); myList.add("apples"); myList.add("bananas"); myList.add("oranges"); mixedMap.put("myList", myList); Map myMap = new HashMap(); myMap.put("Great Expectations", "Charles Dickens"); myMap.put("Pride and Prejudice", "Jane Austen"); myMap.put("Middlemarch", "George Eliot"); myMap.put("Jude the Obscure", "Thomas Hardy"); mixedMap.put("myMap", myMap); dataModel.put(varName, mixedMap); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.HASH_EX); Map mixedMapDump = new HashMap(mixedMap.size()); Map myStringDump = new HashMap(); myStringDump.put(Key.TYPE.toString(), Type.STRING); myStringDump.put(Key.VALUE.toString(), myString); mixedMapDump.put("myString", myStringDump); Map myBooleanDump = new HashMap(); myBooleanDump.put(Key.TYPE.toString(), Type.BOOLEAN); myBooleanDump.put(Key.VALUE.toString(), myBool); mixedMapDump.put("myBoolean", myBooleanDump); Map myNumberDump = new HashMap(); myNumberDump.put(Key.TYPE.toString(), Type.NUMBER); myNumberDump.put(Key.VALUE.toString(), myInt); mixedMapDump.put("myNumber", myNumberDump); Map myDateDump = new HashMap(); myDateDump.put(Key.TYPE.toString(), Type.DATE); myDateDump.put(Key.DATE_TYPE.toString(), DateType.UNKNOWN); myDateDump.put(Key.VALUE.toString(), now); mixedMapDump.put("myDate", myDateDump); Map myListDump = new HashMap(); myListDump.put(Key.TYPE.toString(), Type.SEQUENCE); List> listItemsDump = new ArrayList>(myList.size()); for ( String item : myList ) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.STRING); itemDump.put(Key.VALUE.toString(), item); listItemsDump.add(itemDump); } myListDump.put(Key.VALUE.toString(), listItemsDump); mixedMapDump.put("myList", myListDump); Map myMapDump = new HashMap(); myMapDump.put(Key.TYPE.toString(), Type.HASH_EX); Map mapItemsDump = new HashMap(myMap.size()); for ( String key : myMap.keySet() ) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.STRING); itemDump.put(Key.VALUE.toString(), myMap.get(key)); mapItemsDump.put(key, itemDump); } myMapDump.put(Key.VALUE.toString(), mapItemsDump); mixedMapDump.put("myMap", myMapDump); expectedDump.put(Key.VALUE.toString(), mixedMapDump); test(varName, dataModel, expectedDump); } @Test public void dumpObjectWithExposeNothingWrapper() { String varName = "employee"; Map dataModel = new HashMap(); BeansWrapper wrapper = new BeansWrapper(); wrapper.setExposureLevel(BeansWrapper.EXPOSE_NOTHING); try { dataModel.put("employee", wrapper.wrap(getEmployee())); } catch (TemplateModelException e) { // ?? } Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), "freemarker.ext.dump.DumpDirectiveTest$Employee"); expectedDump.put(Key.VALUE.toString(), new HashMap()); test(varName, dataModel, expectedDump); } @Test public void dumpObjectWithExposePropertiesOnlyWrapper() { String varName = "employee"; Map dataModel = new HashMap(); BeansWrapper wrapper = new BeansWrapper(); wrapper.setExposureLevel(BeansWrapper.EXPOSE_NOTHING); try { dataModel.put("employee", wrapper.wrap(getEmployee())); } catch (TemplateModelException e) { // ?? } Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), "freemarker.ext.dump.DumpDirectiveTest$Employee"); expectedDump.put(Key.VALUE.toString(), new HashMap()); test(varName, dataModel, expectedDump); } @Test public void dumpObjectWithExposeSafeWrapper() { String varName = "employee"; Map dataModel = new HashMap(); BeansWrapper wrapper = new BeansWrapper(); wrapper.setExposureLevel(BeansWrapper.EXPOSE_NOTHING); try { dataModel.put("employee", wrapper.wrap(getEmployee())); } catch (TemplateModelException e) { // ?? } Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), "freemarker.ext.dump.DumpDirectiveTest$Employee"); expectedDump.put(Key.VALUE.toString(), new HashMap()); test(varName, dataModel, expectedDump); } @Test public void dumpObjectWithExposeAllWrapper() { String varName = "employee"; Map dataModel = new HashMap(); BeansWrapper wrapper = new BeansWrapper(); wrapper.setExposureLevel(BeansWrapper.EXPOSE_NOTHING); try { dataModel.put("employee", wrapper.wrap(getEmployee())); } catch (TemplateModelException e) { // ?? } Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), "freemarker.ext.dump.DumpDirectiveTest$Employee"); expectedDump.put(Key.VALUE.toString(), new HashMap()); test(varName, dataModel, expectedDump); } /////////////////////////// Private stub classes and helper methods /////////////////////////// private void test(String varName, Map dataModel, Map expectedDump) { try { Environment env = template.createProcessingEnvironment(dataModel, new StringWriter()); Map dumpData = new DumpDirective().getTemplateVariableData(varName, env); assertEquals(expectedDump, dumpData); } catch (Exception e) { fail(e.getMessage()); } } private class HelplessMethod implements TemplateMethodModel { @Override public Object exec(List arg0) throws TemplateModelException { return null; } } private class HelpfulMethod implements TemplateMethodModel { @Override public Object exec(List arg0) throws TemplateModelException { return null; } public Map help(String name) { return getMethodHelp(name); } } private class MethodWithBadHelp implements TemplateMethodModel { @Override public Object exec(List arg0) throws TemplateModelException { return null; } public Map help() { return new HashMap(); } } private class HelplessDirective implements TemplateDirectiveModel { @Override public void execute(Environment arg0, Map arg1, TemplateModel[] arg2, TemplateDirectiveBody arg3) throws TemplateException, IOException { } } private class HelpfulDirective implements TemplateDirectiveModel { @Override public void execute(Environment arg0, Map arg1, TemplateModel[] arg2, TemplateDirectiveBody arg3) throws TemplateException, IOException { } public Map help(String name) { return getDirectiveHelp(name); } } private class DirectiveWithBadHelp implements TemplateDirectiveModel { @Override public void execute(Environment arg0, Map arg1, TemplateModel[] arg2, TemplateDirectiveBody arg3) throws TemplateException, IOException { } public String help(String name) { return "help"; } } private Map getDirectiveHelp(String name) { Map map = new HashMap(); map.put("effect", "Dump the contents of a template variable."); map.put("comments", "Sequences (lists and arrays) are enclosed in square brackets. Hashes are enclosed in curly braces."); Map params = new HashMap(); params.put("var", "name of variable to dump"); map.put("params", params); List examples = new ArrayList(); examples.add("<@" + name + " var=\"urls\" />"); map.put("examples", examples); return map; } private Map getMethodHelp(String name) { Map map = new HashMap(); map.put(Key.NAME.toString(), name); map.put("returns", "The square of the argument"); Listparams = new ArrayList(); params.add("Integer to square"); map.put("params", params); List examples = new ArrayList(); examples.add(name + "(4)"); map.put("examples", examples); return map; } public static class Employee { private static int count = 0; private String firstName; private String lastName; private String nickname; private Date birthdate; private int id; private Employee supervisor; private List supervisees; private float salary; Employee(String firstName, String lastName, Date birthdate, int id) { this.firstName = firstName; this.lastName = lastName; this.birthdate = birthdate; this.id = id; this.nickname = ""; count++; } void setSupervisor(Employee supervisor) { this.supervisor = supervisor; } void setSupervisees(List supervisees) { this.supervisees = supervisees; } void setSalary(float salary) { this.salary = salary; } public void setNickname(String nickname) { this.nickname = nickname; } // Not available to templates float getSalary() { return salary; } public static int getEmployeeCount() { return count; } /* Public accessor methods for templates */ public String getFullName() { return firstName + " " + lastName; } public String getName(String which) { return which == "first" ? firstName : lastName; } public String getNickname() { return nickname; } public Date getBirthdate() { return birthdate; } public int getId() { return id; } @Deprecated public int getFormerId() { return id % 10000; } public Employee getSupervisor() { return supervisor; } public List getSupervisees() { return supervisees; } } private Employee getEmployee() { Calendar c = Calendar.getInstance(); c.set(75, Calendar.MAY, 5); Employee jdoe = new Employee("John", "Doe", c.getTime(), 34523); c.set(65, Calendar.AUGUST, 10); Employee jsmith = new Employee("Jane", "Smith", c.getTime(), 78234); c.set(80, Calendar.JUNE, 20); Employee mjones = new Employee("Michael", "Jones", c.getTime(), 65432); c.set(81, Calendar.NOVEMBER, 30); Employee mturner = new Employee("Mary", "Turner", c.getTime(), 89531); List supervisees = new ArrayList(); supervisees.add(mjones); supervisees.add(mturner); jdoe.setSupervisor(jsmith); jdoe.setSupervisees(supervisees); jdoe.setSalary(65000); return jdoe; } }