Created
January 3, 2012 06:11
-
-
Save benjchristensen/1553750 to your computer and use it in GitHub Desktop.
TEXTUtility
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.mealbalance.ws; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collection; | |
import java.util.LinkedHashMap; | |
import java.util.Map; | |
import java.util.logging.Level; | |
import java.util.logging.Logger; | |
import junit.framework.TestCase; | |
import org.junit.Test; | |
/** | |
* Utility for generating human readable Plain TEXT from Maps/Lists | |
*/ | |
public class TEXTUtility { | |
private static Logger logger = Logger.getLogger(TEXTUtility.class.getName()); | |
/** | |
* Pass in a Map and this method will return a Plain TEXT string. | |
* <p> | |
* The map can contain Objects, int[], Object[] and Collections and they will be converted into string representations. | |
* <p> | |
* Nested maps can be included as values and the TEXT will have nested object notation. | |
* <p> | |
* Arrays/Collections can have Maps in them as well. | |
* <p> | |
* See the unit tests for examples. | |
* | |
* @param mapData | |
* @return | |
*/ | |
public static String textFromMap(Map<String, Object> mapData) { | |
try { | |
TextDocument text = new TextDocument(); | |
text.startGroup(); | |
for (String key : mapData.keySet()) { | |
Object data = mapData.get(key); | |
if (data instanceof Map) { | |
/* it's a nested map, so we'll recursively add the TEXT of this map to the current TEXT */ | |
text.addGroup(key, textFromMap((Map<String, Object>) data)); | |
} else if (data instanceof Object[]) { | |
/* it's an object array, so we'll iterate the elements and put them all in here */ | |
text.addValue(key, stringArrayFromObjectArray((Object[]) data)); | |
} else if (data instanceof Collection) { | |
/* it's a collection, so we'll iterate the elements and put them all in here */ | |
text.addValue(key, stringArrayFromObjectArray(((Collection) data).toArray())); | |
} else if (data instanceof int[]) { | |
/* it's an int array, so we'll get the string representation */ | |
String intArray = Arrays.toString((int[]) data); | |
/* remove [ ] */ | |
intArray = intArray.substring(1, intArray.length() - 1); | |
/* remove whitespace */ | |
intArray = intArray.replaceAll(" ", ""); | |
text.addValue(key, intArray); | |
} else if (data instanceof TEXTCapableObject) { | |
text.addValue(key, ((TEXTCapableObject) data).getTEXT()); | |
} else { | |
/* all other objects we assume we are to just put the string value in */ | |
text.addValue(key, String.valueOf(data)); | |
} | |
} | |
text.endGroup(); | |
logger.log(Level.FINER, "created TEXT from map => " + text.toString()); | |
return text.toString(); | |
} catch (Exception e) { | |
logger.log(Level.SEVERE, "Could not create TEXT from Map. ", e); | |
return "{}"; | |
} | |
} | |
/* | |
* return a string like: "one","two","three" | |
*/ | |
private static String stringArrayFromObjectArray(Object data[]) { | |
TextDocument text = new TextDocument(); | |
text.startGroup(); | |
for (Object o : data) { | |
if (o instanceof Map) { | |
text.addGroup("", textFromMap((Map<String, Object>) o)); | |
} else if (o instanceof TEXTCapableObject) { | |
text.addGroup("", ((TEXTCapableObject) o).getTEXT()); | |
} else { | |
text.addString(String.valueOf(o)); | |
} | |
} | |
text.endGroup(); | |
return text.toString(); | |
} | |
private static class TextDocument { | |
StringBuilder text = new StringBuilder(); | |
private boolean newGroup = false; | |
public TextDocument startGroup() { | |
newGroup = true; | |
return this; | |
} | |
public TextDocument endGroup() { | |
return this; | |
} | |
public TextDocument addValue(String key, String value) { | |
String lines[] = value.split("\\n"); | |
if (lines.length > 1) { | |
// we have multiple lines in the value, so we'll add it as a sub-group instead | |
return addGroup(key, value); | |
} | |
if (!newGroup) { | |
// if this is not the first value in a group append a NewLine | |
text.append("\n"); | |
} | |
/* once we're here, the group is no longer "new" */ | |
newGroup = false; | |
/* append the key/value */ | |
text.append(key); | |
text.append(" => "); | |
text.append(value); | |
return this; | |
} | |
public TextDocument addGroup(String key, String value) { | |
// we always want a group to start on a new line (unless 'text' is empty) | |
if (text.length() > 0) { | |
text.append("\n"); | |
} | |
/* once we're here, the group is no longer "new" */ | |
newGroup = false; | |
/* append the key/value */ | |
text.append(key); | |
text.append(" => ").append("\n"); | |
String lines[] = value.split("\\n"); | |
for (int i = 0; i < lines.length; i++) { | |
text.append("\t").append(lines[i]); | |
if (i != lines.length - 1) { | |
text.append("\n"); | |
} | |
} | |
return this; | |
} | |
public TextDocument addString(String value) { | |
if (!newGroup) { | |
text.append(", "); | |
} | |
/* once we're here, the group is no longer "new" */ | |
newGroup = false; | |
text.append(value); | |
return this; | |
} | |
public String toString() { | |
return text.toString(); | |
} | |
} | |
public static class UnitTest extends TestCase { | |
// I'm using LinkedHashMap in the testing so I get consistent ordering for the expected results | |
public void testSimpleOne() { | |
Map<String, Object> jsonData = new LinkedHashMap<String, Object>(); | |
jsonData.put("myKey", "myValue"); | |
String json = textFromMap(jsonData); | |
String expected = "myKey => myValue"; | |
// assert writing | |
assertEquals(expected, json); | |
} | |
public void testSimpleTwo() { | |
Map<String, Object> jsonData = new LinkedHashMap<String, Object>(); | |
jsonData.put("myKey", "myValue"); | |
jsonData.put("myKey2", "myValue2"); | |
String json = textFromMap(jsonData); | |
String expected = "myKey => myValue\nmyKey2 => myValue2"; | |
// assert writing | |
assertEquals(expected, json); | |
} | |
public void testNestedMapOne() { | |
Map<String, Object> jsonData = new LinkedHashMap<String, Object>(); | |
jsonData.put("myKey", "myValue"); | |
Map<String, Object> jsonData2 = new LinkedHashMap<String, Object>(); | |
jsonData2.put("myNestedKey", "myNestedValue"); | |
jsonData.put("myNestedData", jsonData2); | |
String json = textFromMap(jsonData); | |
String expected = "myKey => myValue\nmyNestedData => \n\tmyNestedKey => myNestedValue"; | |
// assert writing | |
assertEquals(expected, json); | |
} | |
public void testNestedMapTwo() { | |
Map<String, Object> jsonData = new LinkedHashMap<String, Object>(); | |
jsonData.put("myKey", "myValue"); | |
Map<String, Object> jsonData2 = new LinkedHashMap<String, Object>(); | |
jsonData2.put("myNestedKey", "myNestedValue"); | |
jsonData2.put("myNestedKey2", "myNestedValue2"); | |
jsonData.put("myNestedData", jsonData2); | |
String json = textFromMap(jsonData); | |
String expected = "myKey => myValue\nmyNestedData => \n\tmyNestedKey => myNestedValue\n\tmyNestedKey2 => myNestedValue2"; | |
// assert writing | |
assertEquals(expected, json); | |
} | |
public void testArrayOne() { | |
Map<String, Object> jsonData = new LinkedHashMap<String, Object>(); | |
int[] numbers = { 1, 2, 3, 4 }; | |
jsonData.put("myKey", numbers); | |
String json = textFromMap(jsonData); | |
String expected = "myKey => 1,2,3,4"; | |
// assert writing | |
assertEquals(expected, json); | |
} | |
public void testArrayTwo() { | |
Map<String, Object> jsonData = new LinkedHashMap<String, Object>(); | |
String[] values = { "one", "two", "three", "four" }; | |
jsonData.put("myKey", values); | |
String json = textFromMap(jsonData); | |
String expected = "myKey => one, two, three, four"; | |
// assert writing | |
assertEquals(expected, json); | |
} | |
public void testCollectionOne() { | |
Map<String, Object> jsonData = new LinkedHashMap<String, Object>(); | |
ArrayList<String> values = new ArrayList<String>(); | |
values.add("one"); | |
values.add("two"); | |
values.add("three"); | |
values.add("four"); | |
jsonData.put("myKey", values); | |
String json = textFromMap(jsonData); | |
String expected = "myKey => one, two, three, four"; | |
// assert writing | |
assertEquals(expected, json); | |
} | |
public void testMapAndList() { | |
Map<String, Object> jsonData = new LinkedHashMap<String, Object>(); | |
jsonData.put("myKey", "myValue"); | |
int[] numbers = { 1, 2, 3, 4 }; | |
jsonData.put("myNumbers", numbers); | |
Map<String, Object> jsonData2 = new LinkedHashMap<String, Object>(); | |
jsonData2.put("myNestedKey", "myNestedValue"); | |
jsonData2.put("myNestedKey2", "myNestedValue2"); | |
String[] values = { "one", "two", "three", "four" }; | |
jsonData2.put("myStringNumbers", values); | |
jsonData.put("myNestedData", jsonData2); | |
String json = textFromMap(jsonData); | |
String expected = "myKey => myValue\nmyNumbers => 1,2,3,4\nmyNestedData => \n\tmyNestedKey => myNestedValue\n\tmyNestedKey2 => myNestedValue2\n\tmyStringNumbers => one, two, three, four"; | |
// assert writing | |
assertEquals(expected, json); | |
} | |
@Test | |
public void testArrayOfMaps() { | |
Map<String, Object> jsonData = new LinkedHashMap<String, Object>(); | |
ArrayList<Map<String, Object>> messages = new ArrayList<Map<String, Object>>(); | |
Map<String, Object> message1 = new LinkedHashMap<String, Object>(); | |
message1.put("a", "valueA1"); | |
message1.put("b", "valueB1"); | |
messages.add(message1); | |
Map<String, Object> message2 = new LinkedHashMap<String, Object>(); | |
message2.put("a", "valueA2"); | |
message2.put("b", "valueB2"); | |
messages.add(message2); | |
jsonData.put("messages", messages); | |
String json = textFromMap(jsonData); | |
String expected = "messages => \n\t => \n\t\ta => valueA1\n\t\tb => valueB1\n\t => \n\t\ta => valueA2\n\t\tb => valueB2"; | |
// assert writing | |
assertEquals(expected, json); | |
} | |
/** | |
* Used to compare 2 maps deeply and depending on the types. | |
* <p> | |
* This does not try to be fully comprehensive to all possible types, only to those expected by the maps created by this class | |
* | |
* @param m1 | |
* @param m2 | |
* @return | |
*/ | |
private boolean deepEquals(Map m1, Map m2) { | |
for (Object key : m1.keySet()) { | |
Object v1 = m1.get(key); | |
Object v2 = m2.get(key); | |
if (v2 == null) { | |
// missing key, we are not equal | |
return false; | |
} | |
if (!v1.getClass().equals(v2.getClass())) { | |
// they are different types | |
return false; | |
} | |
if (v1 instanceof Map) { | |
if (!deepEquals((Map) v1, (Map) v2)) { | |
// this child map is not equal | |
return false; | |
} | |
} | |
if (v1 instanceof Collection) { | |
if (!Arrays.deepEquals(((Collection) v1).toArray(), ((Collection) v2).toArray())) { | |
// collection not equal | |
return false; | |
} | |
} | |
if (v1 instanceof int[]) { | |
if (!Arrays.equals((int[]) v1, (int[]) v2)) { | |
// int[] not equal | |
return false; | |
} | |
} | |
if (v1 instanceof String[]) { | |
if (!Arrays.equals((String[]) v1, (String[]) v2)) { | |
// String[] not equal | |
return false; | |
} | |
} | |
} | |
// we passed all tests so we declare it true | |
return true; | |
} | |
} | |
public static interface TEXTCapableObject { | |
String getTEXT(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment