Skip to content

Instantly share code, notes, and snippets.

@benjchristensen
Created January 3, 2012 06:11
Show Gist options
  • Save benjchristensen/1553750 to your computer and use it in GitHub Desktop.
Save benjchristensen/1553750 to your computer and use it in GitHub Desktop.
TEXTUtility
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