Skip to content

Instantly share code, notes, and snippets.

@daniel-sc
Created October 2, 2014 13:31
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daniel-sc/b50b0096f6fe3adabab5 to your computer and use it in GitHub Desktop.
Save daniel-sc/b50b0096f6fe3adabab5 to your computer and use it in GitHub Desktop.
Java toString() Parser - creates object/instance from toString() output (-string)
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
/**
* Model for parsing toString() output.
*
* Caveat: if values/strings contain '[' or ']' one might get unexpected
* results.
*
* @author dschreiber
*
*/
public abstract class Item {
private static final Logger LOGGER = Logger.getLogger(Item.class);
public static class ValueItem extends Item {
public ValueItem(String stringRepresentation) {
super(stringRepresentation);
}
public boolean isNullOrEmpty() {
return Strings.isNullOrEmpty(getStringRepresentation())
|| "null".equals(getStringRepresentation());
}
}
public static class ObjectItem extends Item {
private final String type;
private final Map<String, Item> attributes = new HashMap<String, Item>();
public ObjectItem(String stringRepresentation) {
super(stringRepresentation);
Pattern typePattern = Pattern.compile("(^[A-Z]\\S*) \\[(.*)\\]$",
Pattern.DOTALL);
Matcher typeMatcher = typePattern.matcher(stringRepresentation);
if (typeMatcher.matches()) {
type = typeMatcher.group(1);
for (String attributeValue : splitOnFirstLevelCommaRespectEqualSign(typeMatcher
.group(2))) {
Iterator<String> split = Splitter.on("=").trimResults()
.limit(2).split(attributeValue).iterator();
String attributeName = split.next();
String attributeValueString = split.next();
attributes.put(attributeName,
parseString(attributeValueString));
}
} else {
throw new IllegalArgumentException(
"cannot create object from string: "
+ stringRepresentation);
}
}
public String getType() {
return type;
}
public Map<String, Item> getAttributes() {
return attributes;
}
@Override
public String toString() {
return super.toString()
+ "\n Type="
+ type
+ "\n "
+ Joiner.on("\n ").withKeyValueSeparator(" = ")
.join(attributes);
}
}
public static class ListItem extends Item {
private List<Item> values = new ArrayList<Item>();
public ListItem(String stringRepresentation) {
super(stringRepresentation);
// remove "[" and "]":
String valueString = stringRepresentation.substring(1,
stringRepresentation.length() - 1);
LOGGER.debug("no brackets - list: " + valueString);
for (String value : splitOnFirstLevelComma(valueString)) {
values.add(parseString(value));
}
}
public List<Item> getValues() {
return values;
}
@Override
public String toString() {
return super.toString() + "\n " + Joiner.on("\n ").join(values);
}
}
private final String stringRepresentation;
public Item(String stringRepresentation) {
this.stringRepresentation = stringRepresentation;
LOGGER.info("creating: " + stringRepresentation);
}
public String getStringRepresentation() {
return stringRepresentation;
}
@Override
public String toString() {
return "Item [stringRepresentation=" + stringRepresentation + "]";
}
/**
* counts occurence of {@code count} in {@code string}
*
* @param string
* @param count
* @return
*/
private static int contains(String string, char count) {
int counter = 0;
for (int i = 0; i < string.length(); i++) {
if (string.charAt(i) == count) {
counter++;
}
}
return counter;
}
/**
* only the first comma before an equal sign ('=') is used for split. (So
* that strings that contain a comma are not split.)
*
* @param string
* @return
*/
public static List<String> splitOnFirstLevelCommaRespectEqualSign(
String string) {
List<String> allSplits = splitOnFirstLevelComma(string);
List<String> result = new ArrayList<String>(allSplits.size());
for (String current : allSplits) {
if (current.contains("=")) {
result.add(current);
} else {
if (result.isEmpty()) {
throw new IllegalStateException(
"first comma must not occur before first equal sign! ("
+ string + ")");
}
result.set(result.size() - 1, result.get(result.size() - 1)
+ ", " + current);
}
}
return result;
}
/**
* ignores commas nested in square brackets ("[", "]")
*
* @param string
*/
public static List<String> splitOnFirstLevelComma(String string) {
Scanner scanner = new Scanner(string);
scanner.useDelimiter(", ");
List<String> result = new ArrayList<String>();
int openBrackets = 0;
while (scanner.hasNext()) {
String next = scanner.next();
int open = contains(next, '[');
int close = contains(next, ']');
LOGGER.debug("openBrackets: " + openBrackets + ", open: " + open
+ ", close: " + close + ", next: " + next);
if (openBrackets > 0) {
result.set(result.size() - 1, result.get(result.size() - 1)
+ ", " + next);
} else {
result.add(next);
}
openBrackets = openBrackets + open - close;
}
scanner.close();
return result;
}
public static Item parseString(String string) {
if (Strings.isNullOrEmpty(string)
|| Strings.isNullOrEmpty(string.trim())) {
return new ValueItem(string);
}
Pattern objectPattern = Pattern.compile("^[A-Z][^ ]* \\[.*",
Pattern.DOTALL);
string = string.trim();
if (string.startsWith("[")) {
return new ListItem(string);
} else if (objectPattern.matcher(string).matches()) {
return new ObjectItem(string);
} else {
return new ValueItem(string);
}
}
}
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import Item.ListItem;
import Item.ObjectItem;
import Item.ValueItem;
/**
* Creates object/instance from toString()-Model.
*
* @author dschreiber
*
*/
public class ItemObjectMapper {
private static final Logger LOGGER = Logger
.getLogger(ItemObjectMapper.class);
private final Map<String, Class<?>> classModel;
public ItemObjectMapper(Map<String, Class<?>> classModel) {
this.classModel = classModel;
}
public Object parse(Item item) {
return parse(item, null);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Object parse(Item item, Class<?> desiredType) {
LOGGER.info("desired type: " + desiredType + ", item-type: "
+ item.getClass());
try {
if (item instanceof ObjectItem) {
String type = ((ObjectItem) item).getType();
LOGGER.debug("type: " + type);
if (!classModel.containsKey(type)) {
throw new IllegalStateException("Cannot map type: " + type);
}
Class<?> clazz = classModel.get(type);
Object instance = clazz.newInstance();
for (Entry<String, Item> entry : ((ObjectItem) item)
.getAttributes().entrySet()) {
Field field = clazz.getDeclaredField(entry.getKey());
LOGGER.debug("parsing for field: " + field.getName()
+ " | " + field);
field.setAccessible(true);
field.set(instance,
parse(entry.getValue(), field.getType()));
}
return instance;
} else if (item instanceof ListItem) {
ArrayList<Object> result = new ArrayList<Object>();
for (Item value : ((ListItem) item).getValues()) {
result.add(parse(value, Object.class)); // TODO
}
return result;
} else if (item instanceof ValueItem) {
if (((ValueItem) item).isNullOrEmpty()) {
return null;
}
if (desiredType.isAssignableFrom(int.class)) {
return Integer.parseInt(item.getStringRepresentation());
}
if (desiredType.isAssignableFrom(double.class)) {
return Double.parseDouble(item.getStringRepresentation());
}
if (desiredType.isAssignableFrom(long.class)
|| desiredType.isAssignableFrom(Long.class)) {
return Long.parseLong(item.getStringRepresentation());
}
if (desiredType.isAssignableFrom(boolean.class)) {
return Boolean.parseBoolean(item.getStringRepresentation());
}
if (desiredType.isAssignableFrom(String.class)) {
return item.getStringRepresentation();
}
if (desiredType.isAssignableFrom(Date.class)) {
return null; // TODO
}
if (Enum.class.isAssignableFrom(desiredType)) {
return Enum.valueOf((Class<Enum>) desiredType,
item.getStringRepresentation());
}
throw new IllegalStateException(
"Could not assign value of type=" + desiredType
+ " (value=" + item.getStringRepresentation()
+ ")");
} else {
throw new IllegalStateException("Item of unexpected type: "
+ item);
}
} catch (Exception e) {
LOGGER.error("Unexpected exception!", e);
throw new IllegalStateException("Unexpected Exception! (item="
+ item + ", desiredType=" + desiredType + ")", e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment