Created
October 2, 2014 13:31
-
-
Save daniel-sc/b50b0096f6fe3adabab5 to your computer and use it in GitHub Desktop.
Java toString() Parser - creates object/instance from toString() output (-string)
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
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); | |
} | |
} | |
} |
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
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