Last active
March 20, 2016 21:15
-
-
Save routevegetable/657037b6ab0239bb5ac8 to your computer and use it in GitHub Desktop.
Instantiate an object graph from a property file
This file contains 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.io.BufferedReader; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.util.HashMap; | |
import java.util.Map; | |
public class PropsInstantiator { | |
private static Object getProperty(Object target, String propName) throws Exception { | |
for(Method m : target.getClass().getMethods()) { | |
String name = m.getName(); | |
if(!name.startsWith("get") || m.getParameterTypes().length != 0) | |
continue; | |
name = name.substring(3); | |
if(name.equalsIgnoreCase(propName)) { | |
return m.invoke(target); | |
} | |
} | |
for(Field f : target.getClass().getFields()) { | |
String name = f.getName(); | |
if(name.equalsIgnoreCase(propName)) { | |
return f.get(target); | |
} | |
} | |
return null; | |
} | |
private static abstract class SettableProperty { | |
public final String name; | |
public final Class<?> type; | |
public abstract void set(Object o) throws Exception; | |
public SettableProperty(String name, Class<?> type) { | |
this.name = name; | |
this.type = type; | |
} | |
public static SettableProperty getSettable(final Object target, String propName) { | |
for(Method m : target.getClass().getMethods()) { | |
String name = m.getName(); | |
if(!name.startsWith("set") || m.getParameterTypes().length != 1) | |
continue; | |
name = name.substring(3); | |
if(name.equalsIgnoreCase(propName)) { | |
final Method myM = m; | |
return new SettableProperty(propName, m.getParameterTypes()[0]) { | |
public void set(Object o) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { | |
myM.invoke(target, o); | |
} | |
}; | |
} | |
} | |
for(Field f : target.getClass().getFields()) { | |
String name = f.getName(); | |
if(name.equalsIgnoreCase(propName)) { | |
final Field myF = f; | |
return new SettableProperty(propName, f.getType()) { | |
public void set(Object o) throws IllegalArgumentException, IllegalAccessException { | |
myF.set(target, o); | |
} | |
}; | |
} | |
} | |
return null; | |
} | |
} | |
private static Object parseEnum(Class<?> enumType, String str) { | |
if(!str.contains(".")) { | |
// Single enum | |
for(Object o : enumType.getEnumConstants()) { | |
Enum e = (Enum) o; | |
if(e.name().equals(str)) | |
return e; | |
} | |
} | |
return null; | |
} | |
private static Object parseObjectReference(Object t, String path) throws Exception { | |
if(path.startsWith("@")) { | |
// Reference from root object | |
path = path.substring(1); | |
String[] parts = path.split("\\."); | |
for(int i = 0; i < parts.length && t != null; i++) { | |
t = getProperty(t, parts[i]); | |
} | |
return t; | |
} else if(path.startsWith("!")){ | |
// New object | |
path = path.substring(1); | |
return Class.forName(path).newInstance(); | |
} | |
return null; | |
} | |
public static void parse(InputStream is, Object target) throws Exception { | |
try(BufferedReader br = new BufferedReader(new InputStreamReader(is))) { | |
String line = null; | |
while((line = br.readLine()) != null) { | |
if(line.isEmpty()) | |
continue; | |
if(line.startsWith("#")) | |
continue; | |
String key = line.replaceFirst(" *=.*",""); | |
String valueString = line.replaceFirst(".*= *",""); | |
String[] keyParts = key.split("\\."); | |
Object t = target; | |
for(int i = 0; i < keyParts.length-1 && t != null; i++) { | |
t = getProperty(t, keyParts[i]); | |
} | |
SettableProperty s = SettableProperty.getSettable(t, keyParts[keyParts.length-1]); | |
Object refObj = parseObjectReference(target, valueString); | |
if(refObj != null) { | |
s.set(refObj); | |
} else if(s.type.isEnum()) { | |
s.set(parseEnum(s.type, valueString)); | |
} else if(s.type == String.class) { | |
s.set(valueString); | |
} else if(s.type == Integer.class) { | |
s.set(Integer.parseInt(valueString)); | |
} else if(s.type == Double.class) { | |
s.set(Double.parseDouble(valueString)); | |
} else if(s.type == Float.class) { | |
s.set(Float.parseFloat(valueString)); | |
} | |
} | |
} | |
} | |
} |
This file contains 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
class Test { | |
public static enum State { | |
RUNNING, | |
STOPPED; | |
} | |
private State state; | |
public Object child = null; | |
private String description = null; | |
public void setDescription(String description) { | |
this.description = description; | |
} | |
public void setState(State state) { | |
this.state = state; | |
} | |
public State getState() { | |
return this.state; | |
} | |
public static void main(String[] args) throws Exception { | |
Test t = new Test(); | |
PropsInstantiator.parse(Test.class.getResourceAsStream("/test.properties"), t); | |
} | |
} |
This file contains 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
# Set a String setter | |
description=Hello There | |
# Set a field with a new object | |
child=!Test | |
# Set a String setter | |
child.description=I am the child thing | |
# Set an Enum setter | |
child.state=RUNNING | |
# Set an Enum setter with an object from elsewhere | |
[email protected] | |
# Set a field with a new object | |
child.child=!java.lang.String |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment