diff --git a/webapp/src/freemarker/ext/dump/BaseDumpDirective.java b/webapp/src/freemarker/ext/dump/BaseDumpDirective.java index 09f90ad8b..adae023d6 100644 --- a/webapp/src/freemarker/ext/dump/BaseDumpDirective.java +++ b/webapp/src/freemarker/ext/dump/BaseDumpDirective.java @@ -5,7 +5,6 @@ package freemarker.ext.dump; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; @@ -13,6 +12,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; @@ -253,7 +254,7 @@ public abstract class BaseDumpDirective implements TemplateDirectiveModel { private Map getMapData(TemplateHashModelEx model) throws TemplateModelException { Map map = new HashMap(); map.put(Key.TYPE.toString(), Type.HASH_EX); - Map items = new HashMap(); + SortedMap items = new TreeMap(); // keys() gets only values visible to template TemplateCollectionModel keys = model.keys(); TemplateModelIterator iModel = keys.iterator(); @@ -262,6 +263,7 @@ public abstract class BaseDumpDirective implements TemplateDirectiveModel { TemplateModel value = model.get(key); items.put(key, getData(value)); } + // *** RY SORT keys alphabetically map.put(Key.VALUE.toString(), items); return map; } @@ -293,7 +295,9 @@ public abstract class BaseDumpDirective implements TemplateDirectiveModel { } else { methods.put(methodDisplayName, ""); // or null ? } - } + } + + // *** SORT methods alphabetically map.put(Key.VALUE.toString(), methods); return map; } @@ -316,8 +320,7 @@ public abstract class BaseDumpDirective implements TemplateDirectiveModel { methodName = methodName.substring(0, 1).toLowerCase() + methodName.substring(1); } - return methodName; - + return methodName; } private Set getMethodsAvailableToTemplate(TemplateHashModelEx model, Object object) throws TemplateModelException { @@ -357,6 +360,11 @@ public abstract class BaseDumpDirective implements TemplateDirectiveModel { // Include only methods included in keys(). This factors in visibility // defined by the model's BeansWrapper. + + //*** This has to be method.getMethodDisplayName() **** +// SO: we have to get the display name now +// and we should get the value now too +// so method => { displayName => ..., value => ... } if (keySet.contains(method.getName())) { // if the key has a value, we could add it here rather than invoking the // method later diff --git a/webapp/test/freemarker/ext/dump/DumpDirectiveTest.java b/webapp/test/freemarker/ext/dump/DumpDirectiveTest.java index d893ef581..53c2e5d32 100644 --- a/webapp/test/freemarker/ext/dump/DumpDirectiveTest.java +++ b/webapp/test/freemarker/ext/dump/DumpDirectiveTest.java @@ -12,12 +12,15 @@ import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; 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 java.util.SortedMap; +import java.util.TreeMap; import org.apache.log4j.Level; import org.apache.log4j.Logger; @@ -40,12 +43,20 @@ import freemarker.template.TemplateMethodModel; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; +/** + * Unit tests of dump directive. The tests follow the same basic pattern: + * 1. Create the data model + * 2. Create the expected dump data structure + * 3. Create the actual dump data structure by running the data model through a processing environment + * 4. Compare expected and actual dump data structures + * + * @author rjy7 + * + */ public class DumpDirectiveTest { private Template template; - - @Before public void setUp() { Configuration config = new Configuration(); @@ -292,23 +303,23 @@ public class DumpDirectiveTest { String varName = "fruit"; Map dataModel = new HashMap(); - List list = new ArrayList(); - list.add("apples"); - list.add("bananas"); - list.add("oranges"); - dataModel.put(varName, list); + List myList = new ArrayList(); + myList.add("apples"); + myList.add("bananas"); + myList.add("oranges"); + dataModel.put(varName, myList); 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) { + List> myListExpectedDump = new ArrayList>(myList.size()); + for ( String str : myList) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.STRING); itemDump.put(Key.VALUE.toString(), str); - listDump.add(itemDump); + myListExpectedDump.add(itemDump); } - expectedDump.put(Key.VALUE.toString(), listDump); + expectedDump.put(Key.VALUE.toString(), myListExpectedDump); test(varName, dataModel, expectedDump); } @@ -319,20 +330,20 @@ public class DumpDirectiveTest { String varName = "fruit"; Map dataModel = new HashMap(); - String[] arr = { "apples", "bananas", "oranges" }; - dataModel.put(varName, arr); + String[] myArray = { "apples", "bananas", "oranges" }; + dataModel.put(varName, myArray); 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) { + List> myArrayExpectedDump = new ArrayList>(myArray.length); + for ( String str : myArray) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.STRING); itemDump.put(Key.VALUE.toString(), str); - arrDump.add(itemDump); + myArrayExpectedDump.add(itemDump); } - expectedDump.put(Key.VALUE.toString(), arrDump); + expectedDump.put(Key.VALUE.toString(), myArrayExpectedDump); test(varName, dataModel, expectedDump); } @@ -366,36 +377,36 @@ public class DumpDirectiveTest { expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.SEQUENCE); - List> mixedListDump = new ArrayList>(mixedList.size()); + List> mixedListExpectedDump = 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 myStringExpectedDump = new HashMap(); + myStringExpectedDump.put(Key.TYPE.toString(), Type.STRING); + myStringExpectedDump.put(Key.VALUE.toString(), myString); + mixedListExpectedDump.add(myStringExpectedDump); - Map numberDump = new HashMap(); - numberDump.put(Key.TYPE.toString(), Type.NUMBER); - numberDump.put(Key.VALUE.toString(), myInt); - mixedListDump.add(numberDump); + Map myIntExpectedDump = new HashMap(); + myIntExpectedDump.put(Key.TYPE.toString(), Type.NUMBER); + myIntExpectedDump.put(Key.VALUE.toString(), myInt); + mixedListExpectedDump.add(myIntExpectedDump); - Map booleanDump = new HashMap(); - booleanDump.put(Key.TYPE.toString(), Type.BOOLEAN); - booleanDump.put(Key.VALUE.toString(), myBool); - mixedListDump.add(booleanDump); + Map myBoolExpectedDump = new HashMap(); + myBoolExpectedDump.put(Key.TYPE.toString(), Type.BOOLEAN); + myBoolExpectedDump.put(Key.VALUE.toString(), myBool); + mixedListExpectedDump.add(myBoolExpectedDump); - Map myListDump = new HashMap(); - myListDump.put(Key.TYPE.toString(), Type.SEQUENCE); - List> itemsDump = new ArrayList>(myList.size()); + Map myListExpectedDump = new HashMap(); + myListExpectedDump.put(Key.TYPE.toString(), Type.SEQUENCE); + List> myListItemsExpectedDump = 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); + myListItemsExpectedDump.add(itemDump); } - myListDump.put(Key.VALUE.toString(), itemsDump); - mixedListDump.add(myListDump); + myListExpectedDump.put(Key.VALUE.toString(), myListItemsExpectedDump); + mixedListExpectedDump.add(myListExpectedDump); - expectedDump.put(Key.VALUE.toString(), mixedListDump); + expectedDump.put(Key.VALUE.toString(), mixedListExpectedDump); test(varName, dataModel, expectedDump); } @@ -406,25 +417,25 @@ public class DumpDirectiveTest { String varName = "oddNums"; Map dataModel = new HashMap(); - Set odds = new HashSet(); + Set myIntSet = new HashSet(); for (int i=0; i <= 10; i++) { if (i % 2 == 1) { - odds.add(i); + myIntSet.add(i); } } - dataModel.put(varName, odds); + dataModel.put(varName, myIntSet); 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 ) { + List> myIntSetExpectedDump = new ArrayList>(myIntSet.size()); + for ( int i : myIntSet ) { Map itemDump = new HashMap(); itemDump.put(Key.TYPE.toString(), Type.NUMBER); itemDump.put(Key.VALUE.toString(), i); - sequenceDump.add(itemDump); + myIntSetExpectedDump.add(itemDump); } - expectedDump.put(Key.VALUE.toString(), sequenceDump); + expectedDump.put(Key.VALUE.toString(), myIntSetExpectedDump); test(varName, dataModel, expectedDump); } @@ -441,20 +452,20 @@ public class DumpDirectiveTest { odds.add(i); } } - TemplateCollectionModel collection = new SimpleCollection(odds); - dataModel.put(varName, collection); + TemplateCollectionModel myCollection = new SimpleCollection(odds); + dataModel.put(varName, myCollection); Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.COLLECTION); - List> collectionDump = new ArrayList>(odds.size()); + List> myCollectionExpectedDump = 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); + myCollectionExpectedDump.add(itemDump); } - expectedDump.put(Key.VALUE.toString(), collectionDump); + expectedDump.put(Key.VALUE.toString(), myCollectionExpectedDump); test(varName, dataModel, expectedDump); } @@ -481,16 +492,25 @@ public class DumpDirectiveTest { Map expectedDump = new HashMap(); expectedDump.put(Key.NAME.toString(), varName); expectedDump.put(Key.TYPE.toString(), Type.HASH_EX); - Map myMapDump = new HashMap(myMap.size()); + SortedMap myMapExpectedDump = new TreeMap(); + 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); + myMapExpectedDump.put(key, itemDump); } - expectedDump.put(Key.VALUE.toString(), myMapDump); + expectedDump.put(Key.VALUE.toString(), (myMapExpectedDump)); - test(varName, dataModel, expectedDump); + Map dump = getDump(varName, dataModel); + assertEquals(expectedDump, dump); + + // Test the sorting of the map + List expectedKeys = new ArrayList(myMapExpectedDump.keySet()); + @SuppressWarnings("unchecked") + SortedMap myMapActualDump = (SortedMap) dump.get(Key.VALUE.toString()); + List actualKeys = new ArrayList(myMapActualDump.keySet()); + assertEquals(expectedKeys, actualKeys); } @Test @@ -510,8 +530,8 @@ public class DumpDirectiveTest { int myInt = 4; mixedMap.put("myNumber", myInt); - Date now = new Date(); - mixedMap.put("myDate", now); + Date myDate = new Date(); + mixedMap.put("myDate", myDate); List myList = new ArrayList(); myList.add("apples"); @@ -531,56 +551,68 @@ public class DumpDirectiveTest { 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 mixedMapExpectedDump = 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 myStringExpectedDump = new HashMap(); + myStringExpectedDump.put(Key.TYPE.toString(), Type.STRING); + myStringExpectedDump.put(Key.VALUE.toString(), myString); + mixedMapExpectedDump.put("myString", myStringExpectedDump); - Map myBooleanDump = new HashMap(); - myBooleanDump.put(Key.TYPE.toString(), Type.BOOLEAN); - myBooleanDump.put(Key.VALUE.toString(), myBool); - mixedMapDump.put("myBoolean", myBooleanDump); + Map myBooleanExpectedDump = new HashMap(); + myBooleanExpectedDump.put(Key.TYPE.toString(), Type.BOOLEAN); + myBooleanExpectedDump.put(Key.VALUE.toString(), myBool); + mixedMapExpectedDump.put("myBoolean", myBooleanExpectedDump); - Map myNumberDump = new HashMap(); - myNumberDump.put(Key.TYPE.toString(), Type.NUMBER); - myNumberDump.put(Key.VALUE.toString(), myInt); - mixedMapDump.put("myNumber", myNumberDump); + Map myIntExpectedDump = new HashMap(); + myIntExpectedDump.put(Key.TYPE.toString(), Type.NUMBER); + myIntExpectedDump.put(Key.VALUE.toString(), myInt); + mixedMapExpectedDump.put("myNumber", myIntExpectedDump); - 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 myDateExpectedDump = new HashMap(); + myDateExpectedDump.put(Key.TYPE.toString(), Type.DATE); + myDateExpectedDump.put(Key.DATE_TYPE.toString(), DateType.UNKNOWN); + myDateExpectedDump.put(Key.VALUE.toString(), myDate); + mixedMapExpectedDump.put("myDate", myDateExpectedDump); - Map myListDump = new HashMap(); - myListDump.put(Key.TYPE.toString(), Type.SEQUENCE); - List> listItemsDump = new ArrayList>(myList.size()); + Map myListExpectedDump = new HashMap(); + myListExpectedDump.put(Key.TYPE.toString(), Type.SEQUENCE); + List> myListItemsExpectedDump = 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); + myListItemsExpectedDump.add(itemDump); } - myListDump.put(Key.VALUE.toString(), listItemsDump); - mixedMapDump.put("myList", myListDump); + myListExpectedDump.put(Key.VALUE.toString(), myListItemsExpectedDump); + mixedMapExpectedDump.put("myList", myListExpectedDump); - Map myMapDump = new HashMap(); - myMapDump.put(Key.TYPE.toString(), Type.HASH_EX); - Map mapItemsDump = new HashMap(myMap.size()); + Map myMapExpectedDump = new HashMap(); + myMapExpectedDump.put(Key.TYPE.toString(), Type.HASH_EX); + SortedMap myMapItemsExpectedDump = new TreeMap(); 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); + myMapItemsExpectedDump.put(key, itemDump); } - myMapDump.put(Key.VALUE.toString(), mapItemsDump); - mixedMapDump.put("myMap", myMapDump); + myMapExpectedDump.put(Key.VALUE.toString(), myMapItemsExpectedDump); + mixedMapExpectedDump.put("myMap", myMapExpectedDump); - expectedDump.put(Key.VALUE.toString(), mixedMapDump); + expectedDump.put(Key.VALUE.toString(), mixedMapExpectedDump); - test(varName, dataModel, expectedDump); + Map dump = getDump(varName, dataModel); + assertEquals(expectedDump, dump); + + // Test the sorting of the myMap dump + List expectedKeys = new ArrayList(myMapItemsExpectedDump.keySet()); + @SuppressWarnings("unchecked") + Map mixedMapActualDump = (Map) dump.get(Key.VALUE.toString()); + @SuppressWarnings("unchecked") + Map myMapActualDump = (Map) mixedMapActualDump.get("myMap"); + @SuppressWarnings("unchecked") + Map myMapItemsActualDump = (SortedMap) myMapActualDump.get(Key.VALUE.toString()); + List actualKeys = new ArrayList(myMapItemsActualDump.keySet()); + assertEquals(expectedKeys, actualKeys); } @Test @@ -609,7 +641,7 @@ public class DumpDirectiveTest { String varName = "employee"; Map dataModel = new HashMap(); BeansWrapper wrapper = new BeansWrapper(); - wrapper.setExposureLevel(BeansWrapper.EXPOSE_NOTHING); + wrapper.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY); try { dataModel.put("employee", wrapper.wrap(getEmployee())); } catch (TemplateModelException e) { @@ -619,8 +651,20 @@ public class DumpDirectiveTest { 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); + + Map properties = new HashMap(); + properties.put("fullName", "John Doe"); + properties.put("nickname", ""); + properties.put("id", 34523); + properties.put("supervisor", ""); + properties.put("supervisees", ""); + Calendar c = Calendar.getInstance(); + c.set(75, Calendar.MAY, 5); + properties.put("birthdate", c.getTime()); + + expectedDump.put(Key.VALUE.toString(), properties); + + //test(varName, dataModel, expectedDump); } @Test @@ -640,6 +684,7 @@ public class DumpDirectiveTest { 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); } @@ -662,16 +707,23 @@ public class DumpDirectiveTest { 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 { + Map dump = getDump(varName, dataModel); + assertEquals(expectedDump, dump); + } + + private Map getDump(String varName, Map dataModel) { + try { Environment env = template.createProcessingEnvironment(dataModel, new StringWriter()); - Map dumpData = new DumpDirective().getTemplateVariableData(varName, env); - assertEquals(expectedDump, dumpData); + return new DumpDirective().getTemplateVariableData(varName, env); } catch (Exception e) { fail(e.getMessage()); - } + return null; + } } private class HelplessMethod implements TemplateMethodModel {