Modify property of non-null field using relection/annotations - java

I have this:
package general;
import org.junit.Test;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
#Retention(RetentionPolicy.RUNTIME)
#interface SetTable {
String table();
}
class Star {
public String foo = "";
public String toString(){
return "<Star> : " + this.foo;
}
}
class Bar {
#SetTable(table = "xxx")
public Star s = new Star();
public String toString(){
return "<Bar> : " + this.s.toString();
}
}
class AnnotationInjector {
public static void inject(Object instance) {
Field[] fields = instance.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(SetTable.class)) {
SetTable set = field.getAnnotation(SetTable.class);
field.setAccessible(true); // should work on private fields
try {
field.set(instance, set.table()); // this is not what I need
// ***
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public class AnnotationTest {
#Test
public void first() {
var b = new Bar();
AnnotationInjector.inject(b);
System.out.println(b.toString());
}
}
right now the code is trying to set new Bar().s to a String, but that won't work, because new Bar().s needs to be an instance Star. But that's not what I want to do anyway. What I want to do is access s and set this:
new Bar().s.foo = "whatever"
so on the line above designated by ***
I am looking to do something like:
((Star)field).foo = "whatever";
but that's not right. Is it possible to modify a field after it's been assigned?

Counterintutively, the syntax to do this is:
Star x = (Star)field.get(instance);
this line can modify the properties of the pre-existing Star s field on Bar.

As an alternative I suggest you add an interface with method for setting the value in order to make setting the value more general instead of checking on instances and their types, and make the Star class implements it, as the following:
interface ISetValue {
void setValue(String value);
}
class Star implements ISetValue{
public String foo = "";
#Override
public void setValue(String value) {
foo = value;
}
public String toString(){
return "<Star> : " + this.foo;
}
}
And your AnnotationInjector class should check if the field implements ISetValue and onvoke setValue method for it, should be something like the following:
class AnnotationInjector {
public static void inject(Object instance) {
Field[] fields = instance.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(SetTable.class)) {
SetTable set = field.getAnnotation(SetTable.class);
field.setAccessible(true); // should work on private fields
try {
//field.set(instance, set.table()); // this is not what I need
// ***
//Change is here
Class fieldType = field.getType();
if(ISetValue.class.isAssignableFrom(fieldType) ){
Object fieldValue = field.get(instance);
Method myMethod = fieldValue.getClass().getInterfaces()[0].getDeclaredMethod("setValue", new Class[]{String.class});
myMethod.invoke(fieldValue,set.table());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
Now your output should be:
<Bar> : <Star> : xxx

Related

Finding all private fields and their corresponding getters / setters for nested classes

Situation: few apps communicate using Java DTOs.
I have classes which holds as its fields another classes and they hold another another classes (up to three levels down from top DTO).
Fields could be single DTO or as (exclusively) ArrayList of other classes (DTOs).
All classes are DTO. Just private fields and public setters and getters.
Now, when I get top DTO is there any way to inspect it and get all getters, including nested ones, read fields through getters and then do what I have to do (change some data, specifically remove/change some characters (I have method which does that, all final fields are eventually Strings or Integers), and then write data back using appropriate setter. I guess the best would be to find getter/setter pair per final field and do operation then move to next. Upon finding final (lowest level field) I should check if it is String (do the operation) and if Integer skip operation.
I know there is similar question but it doesn't deal with nested DTOs.
Java reflection get all private fields
If possible I would avoid any 3rd party library.
Any advice on this?
UPDATE: Almost there. Here is kind of demo code, I wish it is so simple, but conceptually it is more less like that:
class SymposiaDTO
import java.util.ArrayList;
public class SymposiaDTO {
private ProgramDTO programDTO;
private ArrayList<PaperDTO> papersDTO;
public ProgramDTO getProgramDTO() {
return programDTO;
}
public void setProgramDTO(ProgramDTO programDTO) {
this.programDTO = programDTO;
}
public ArrayList<PaperDTO> getPapersDTO() {
return papersDTO;
}
public void setPapersDTO(ArrayList<PaperDTO> papersDTO) {
this.papersDTO = papersDTO;
}
}
class ProgramDTO
public class ProgramDTO {
String programTitle;
Integer programID;
public String getProgramTitle() {
return programTitle;
}
public void setProgramTitle(String programTitle) {
this.programTitle = programTitle;
}
public Integer getProgramID() {
return programID;
}
public void setProgramID(Integer programID) {
this.programID = programID;
}
}
class PaperDTO
import java.util.ArrayList;
public class PaperDTO {
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public ArrayList<AuthorDTO> getAuthrosDTO() {
return authrosDTO;
}
public void setAuthrosDTO(ArrayList<AuthorDTO> authrosDTO) {
this.authrosDTO = authrosDTO;
}
private String title;
private ArrayList<AuthorDTO> authrosDTO;
}
class AuthorDTO
public class AuthorDTO {
private String address;
private String name;
private String title;
private String age;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
class Controller <--- by Carlos if I got his instructions right, this version gives no output at all, never even get's single iteration in for loop.
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Controller {
#SuppressWarnings({ "unused", "rawtypes" })
public static void main(String[] args) {
SymposiaDTO symposiaDTO = new SymposiaDTO();
ProgramDTO programDTO = new ProgramDTO();
PaperDTO paperDTO = new PaperDTO();
AuthorDTO authorDTO = new AuthorDTO();
Class<?> topClass = symposiaDTO.getClass();
for (Class<?> innerClass : topClass.getDeclaredClasses()) {
for (Field field : innerClass.getDeclaredFields()) {
if (Modifier.isPrivate(field.getModifiers())) {
String name = Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
Method getter;
try {
getter = innerClass.getDeclaredMethod("get" + name);
} catch (Exception ex) {
getter = null;
}
Method setter;
try {
setter = innerClass.getDeclaredMethod("set" + name, field.getType());
} catch (Exception ex) {
setter = null;
}
// TODO real work...
System.out.printf("%s: getter=%s, setter=%s%n", innerClass.getSimpleName(), getter, setter);
}
}
}
}
}
class Controller2 <--- slightly modified previous version, this gets into the loop, but runs twice, and it never gets deeper into nested DTOs.
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Controller2 {
#SuppressWarnings({ "unused", "rawtypes" })
public static void main(String[] args) {
SymposiaDTO symposiaDTO = new SymposiaDTO();
ProgramDTO programDTO = new ProgramDTO();
PaperDTO paperDTO = new PaperDTO();
AuthorDTO authorDTO = new AuthorDTO();
Class<?> topClass = symposiaDTO.getClass();
List<Class> classesToWalk = new ArrayList<Class>();
for (Field field : topClass.getDeclaredFields()) {
Class symposiaDTO2 = field.getDeclaringClass();
classesToWalk.add(symposiaDTO2);
}
for (Class<?> innerClass : classesToWalk) {
Field[] fields = Arrays.stream(innerClass.getDeclaredFields())
.filter(field -> Modifier.isPrivate(field.getModifiers())).toArray(Field[]::new);
for (Field field : fields) {
String name = Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
Method getter;
try {
getter = innerClass.getDeclaredMethod("get" + name);
} catch (Exception ex) {
getter = null;
}
Method setter;
try {
setter = innerClass.getDeclaredMethod("set" + name, field.getType());
} catch (Exception ex) {
setter = null;
}
// TODO real work...
System.out.printf("%s: getter=%s, setter=%s%n", innerClass.getSimpleName(), getter, setter);
}
}
}
}
This is output from Controller2:
SymposiaDTO: getter=public ProgramDTO SymposiaDTO.getProgramDTO(),
setter=public void SymposiaDTO.setProgramDTO(ProgramDTO)
SymposiaDTO: getter=public java.util.ArrayList
SymposiaDTO.getPapersDTO(), setter=public void
SymposiaDTO.setPapersDTO(java.util.ArrayList)
SymposiaDTO: getter=public ProgramDTO SymposiaDTO.getProgramDTO(),
setter=public void SymposiaDTO.setProgramDTO(ProgramDTO)
SymposiaDTO: getter=public java.util.ArrayList
SymposiaDTO.getPapersDTO(), setter=public void
SymposiaDTO.setPapersDTO(java.util.ArrayList)
You could use getDeclaredClasses to find nested classes, then find the private fields and finally the getters and setters:
Class<?> topClass = ...
for (Class<?> innerClass : topClass.getDeclaredClasses()) {
for (Field field : innerClass.getDeclaredFields()) {
if (Modifier.isPrivate(field.getModifiers())) {
String name = Character.toUpperCase(field.getName().charAt(0))
+ field.getName().substring(1);
Method getter;
try {
getter = innerClass.getDeclaredMethod("get" + name);
} catch (Exception ex) {
getter = null;
}
Method setter;
try {
setter = innerClass.getDeclaredMethod("set" + name, field.getType());
} catch (Exception ex) {
setter = null;
}
// TODO real work...
System.out.printf("%s: getter=%s, setter=%s%n",
innerClass.getSimpleName(), getter, setter);
}
}
}
Edit: above code is valid for "nested classes" as mentioned in the questions title. After the sample code was added to the question it seems like the question is about getters and setters of the fields of the class:
Use getDeclaredFields to get all fields of the class and find the corresponding getter and setter as above; use getType to get the type (class) of each field and (recursively) start over with that class.

How Cast to unknow class

I have a class:
public class Example {
public String name;
}
but this class does not have to be called Example may be different(like Example2, AnotherExample I don't know how), the problem is that I do not know how to be called.
In another class I have a method which take as parameter a list of objects.
My Example class:
public class ObjectClass{
public List<Object> doSomething(List<Object> objects) {
for(int i = 0 ; i < objects.size();i++) {
Class<?> c = objects.get(i).getClass();
System.out.println("class name " + c.getCanonicalName());// i get name my class
System.out.println(((Example) objects.get(i)).name);
}
}
}
When I change the cast Example to another class like this System.out.println(((c) objects.get(i)).name);
it doesn't work.
My class can call itself differently then cast to Example will not work.
Generally speaking I want get value of name, while not knowing how the class is named. But I select which class with a field name(or by having an annotation).
Edit: is one problem, it is my homework and i can't change class Example, and i can't change method doSomething.
If you look specifically after a field name, you can also iterate over all fields via reflection and access the right one.
public List<Object> doSomething(List<Object> objects) {
for (int i = 0; i < objects.size(); i++) {
Class<?> c = objects.get(i).getClass();
for (Field field : c.getFields()) {
if ("name".equals(field.getName())) {
try {
System.out.println("class name " + c.getCanonicalName());// i get name my class
System.out.println(field.get(objects.get(i)));
} catch (Exception e) {
}
}
}
}
return null;
}
Welcome to the wonderful world of polymorphism. Here is what you can do:
interface Named
{
String getName();
}
public class Example implements Named
{
public String name;
#Override
public String getName()
{
return name;
}
}
public class AnotherExample implements Named
{
public String name;
#Override
public String getName()
{
return name;
}
}
then you can do this:
System.out.println(((Named) objects.get(i)).getName());
then of course I would also recommend that you do the following:
public List<Named> doSomething(List<Named> objects) {
so that then you can do this:
System.out.println(objects.get(i).getName());
Edit after clarification:
If you cannot add your own interface, then try something like this:
Object foo = objects.get(i);
if( foo instanceof Example )
{
Example example = (Example)foo;
...do something with example...
}
else if( foo instanceof AnotherExample )
{
AnotherExample anotherExample = (AnotherExample)foo;
...do something with anotherExample...
}
else
{
assert false; //I do not know what class this is.
}

How can I pass the MyPojo object as reference to the map method?

I have a WsField named an annotation.
WsField.java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Documented
#Target(ElementType.FIELD)
#Inherited
#Retention(RetentionPolicy.RUNTIME)
public #interface WsField
{
String fieldName();
}
I use this WsField annotation in MyPojo class.
MyPojo.java
public class MyPojo
{
#WsField(fieldName="Column1")
private String fullName;
public String getFullName()
{
return fullName;
}
public void setFullName(String fullName)
{
this.fullName = fullName;
}
}
I want to set the value of the fields that have WsField annotations in the map method.
WsMapper.java
public class WsMapper
{
public static void map(Object instance,String attributeName, Object value)
{
Class clsMeta = instance.getClass();
for (Field field : clsMeta.getFields())
{
if (field.isAnnotationPresent(WsField.class))
{
field.setAccessible(true);
String fieldName = field.getAnnotation(WsField.class).fieldName();
if (fieldName.contains(attributeName))
{
try
{
field.set(instance, value);
} catch (IllegalAccessException e)
{
e.printStackTrace();
} catch (IllegalArgumentException e)
{
e.printStackTrace();
}
}
}
}
}
}
Application.java
import java.lang.reflect.Field;
public class Application
{
public static void main(String[] args)
{
MyPojo obj = new MyPojo();
WsMapper.map(obj,"Column1", "Test");
String fullName = obj.getFullName();
System.out.println(fullName);
}
}
How can I pass the MyPojo object as reference to the map method ?
It works in the codes below.
MyPojo obj2 = new MyPojo();
Class clsMeta = obj2.getClass();
String fieldName = "";
for (Field f : clsMeta.getDeclaredFields())
{
if (f.isAnnotationPresent(WsField.class))
{
f.setAccessible(true);
fieldName = f.getAnnotation(WsField.class).fieldName();
if (fieldName.contains("Column1"))
{
try
{
f.set(obj, "Test");
} catch (IllegalAccessException e)
{
e.printStackTrace();
} catch (IllegalArgumentException e)
{
e.printStackTrace();
}
}
}
}
Notice how in one case you have
for (Field field : clsMeta.getFields())
and in the other you have
for (Field f : clsMeta.getDeclaredFields())
Your fullName field is private. Class#getFields() does not return private fields. From the javadoc
Returns an array containing Field objects reflecting all the
accessible public fields of the class or interface represented by this
Class object.
You'll have to use Class#getDeclaredFields() in your map method.

Configurable Values in Enum

I often use this design in my code to maintain configurable values. Consider this code:
public enum Options {
REGEX_STRING("Some Regex"),
REGEX_PATTERN(Pattern.compile(REGEX_STRING.getString()), false),
THREAD_COUNT(2),
OPTIONS_PATH("options.config", false),
DEBUG(true),
ALWAYS_SAVE_OPTIONS(true),
THREAD_WAIT_MILLIS(1000);
Object value;
boolean saveValue = true;
private Options(Object value) {
this.value = value;
}
private Options(Object value, boolean saveValue) {
this.value = value;
this.saveValue = saveValue;
}
public void setValue(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
public String getString() {
return value.toString();
}
public boolean getBoolean() {
Boolean booleanValue = (value instanceof Boolean) ? (Boolean) value : null;
if (value == null) {
try {
booleanValue = Boolean.valueOf(value.toString());
}
catch (Throwable t) {
}
}
// We want a NullPointerException here
return booleanValue.booleanValue();
}
public int getInteger() {
Integer integerValue = (value instanceof Number) ? ((Number) value).intValue() : null;
if (integerValue == null) {
try {
integerValue = Integer.valueOf(value.toString());
}
catch (Throwable t) {
}
}
return integerValue.intValue();
}
public float getFloat() {
Float floatValue = (value instanceof Number) ? ((Number) value).floatValue() : null;
if (floatValue == null) {
try {
floatValue = Float.valueOf(value.toString());
}
catch (Throwable t) {
}
}
return floatValue.floatValue();
}
public static void saveToFile(String path) throws IOException {
FileWriter fw = new FileWriter(path);
Properties properties = new Properties();
for (Options option : Options.values()) {
if (option.saveValue) {
properties.setProperty(option.name(), option.getString());
}
}
if (DEBUG.getBoolean()) {
properties.list(System.out);
}
properties.store(fw, null);
}
public static void loadFromFile(String path) throws IOException {
FileReader fr = new FileReader(path);
Properties properties = new Properties();
properties.load(fr);
if (DEBUG.getBoolean()) {
properties.list(System.out);
}
Object value = null;
for (Options option : Options.values()) {
if (option.saveValue) {
Class<?> clazz = option.value.getClass();
try {
if (String.class.equals(clazz)) {
value = properties.getProperty(option.name());
}
else {
value = clazz.getConstructor(String.class).newInstance(properties.getProperty(option.name()));
}
}
catch (NoSuchMethodException ex) {
Debug.log(ex);
}
catch (InstantiationException ex) {
Debug.log(ex);
}
catch (IllegalAccessException ex) {
Debug.log(ex);
}
catch (IllegalArgumentException ex) {
Debug.log(ex);
}
catch (InvocationTargetException ex) {
Debug.log(ex);
}
if (value != null) {
option.setValue(value);
}
}
}
}
}
This way, I can save and retrieve values from files easily. The problem is that I don't want to repeat this code everywhere. Like as we know, enums can't be extended; so wherever I use this, I have to put all these methods there. I want only to declare the values and that if they should be persisted. No method definitions each time; any ideas?
Using an enum to hold configurable values like this looks like an entirely wrong design. Enums are singletons, so effectively you can only have one configuration active at any given time.
An EnumMap sounds more like what you need. It's external to the enum, so you can instantiate as many configurations as you need.
import java.util.*;
public class EnumMapExample {
static enum Options {
DEBUG, ALWAYS_SAVE, THREAD_COUNT;
}
public static void main(String[] args) {
Map<Options,Object> normalConfig = new EnumMap<Options,Object>(Options.class);
normalConfig.put(Options.DEBUG, false);
normalConfig.put(Options.THREAD_COUNT, 3);
System.out.println(normalConfig);
// prints "{DEBUG=false, THREAD_COUNT=3}"
Map<Options,Object> debugConfig = new EnumMap<Options,Object>(Options.class);
debugConfig.put(Options.DEBUG, true);
debugConfig.put(Options.THREAD_COUNT, 666);
System.out.println(debugConfig);
// prints "{DEBUG=true, THREAD_COUNT=666}"
}
}
API links
java.util.EnumMap
A specialized Map implementation for use with enum type keys. All of the keys in an enum map must come from a single enum type that is specified, explicitly or implicitly, when the map is created. Enum maps are represented internally as arrays. This representation is extremely compact and efficient.
i tried doing something similar with enum maps and properties files (please see code below). but my enums were simple and only had one value except for an embedded case. i may have something that is more type safe. i will look around for it.
package p;
import java.util.*;
import java.io.*;
public class GenericAttributes<T extends Enum<T>> {
public GenericAttributes(final Class<T> keyType) {
map = new EnumMap<T, Object>(this.keyType = keyType);
}
public GenericAttributes(final Class<T> keyType, final Properties properties) {
this(keyType);
addStringProperties(properties);
}
public Object get(final T key) {
// what does a null value mean?
// depends on P's semantics
return map.containsKey(key) ? map.get(key) : null;
}
public boolean contains(final T key) {
return map.containsKey(key);
}
public void change(final T key, final Object value) {
remove(key);
put(key, value);
}
public Object put(final T key, final Object value) {
if (map.containsKey(key))
throw new RuntimeException("map already contains: " + key);
else
return map.put(key, value);
}
public Object remove(final T key) {
if (!map.containsKey(key))
throw new RuntimeException("map does not contain: " + key);
return map.remove(key);
}
public String toString() {
return toString(defaultEquals, defaultEndOfLine);
}
// maybe we don;t need this stuff
// we have tests for it though
// it might be useful
public String toString(final String equals, final String endOfLine) {
final StringBuffer sb = new StringBuffer();
for (Map.Entry<T, Object> entry : map.entrySet())
sb.append(entry.getKey()).append(equals).append(entry.getValue()).append(endOfLine);
return sb.toString();
}
public Properties toProperties() {
final Properties p = new Properties();
for (Map.Entry<T, Object> entry : map.entrySet())
p.put(entry.getKey().toString(), entry.getValue().toString());
return p;
}
public void addStringProperties(final Properties properties) {
// keep this for strings, but mostly do work in the enum class
// i.e. static GenericAttributes<PA> fromProperties();
// which would use a fromString()
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
final String key = (String) entry.getKey();
final String value = (String) entry.getValue();
addProperty(key, value);
}
}
public void addProperty(final String key, final Object value) {
try {
final T e = Enum.valueOf(keyType, key);
map.put(e, value);
} catch (IllegalArgumentException e) {
System.err.println(key + " is not an enum from: " + keyType);
}
}
public int size() {
return map.size();
}
public static Properties load(final InputStream inputStream,final Properties defaultProperties) {
final Properties p=defaultProperties!=null?new Properties(defaultProperties):new Properties();
try {
p.load(inputStream);
} catch(IOException e) {
throw new RuntimeException(e);
}
return p;
}
public static Properties load(final File file,final Properties defaultProperties) {
Properties p=null;
try {
final InputStream is=new FileInputStream(file);
p=load(is,defaultProperties);
is.close();
} catch(IOException e) {
throw new RuntimeException(e);
}
return p;
}
public static void store(final OutputStream outputStream, final Properties properties) {
try {
properties.store(outputStream, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void store(final File file, final Properties properties) {
try {
final OutputStream os = new FileOutputStream(file);
store(os, properties);
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
final Class<T> keyType;
static final String defaultEquals = "=", defaultEndOfLine = "\n";
private final EnumMap<T, Object> map;
public static void main(String[] args) {
}
}
package p;
import static org.junit.Assert.*;
import org.junit.*;
import java.io.*;
import java.util.*;
enum A1 {
foo,bar,baz;
}
enum A2 {
x,y,z;
}
public class GenericAttributesTestCase {
#Test public void testGenericAttributes() {
new GenericAttributes<A1>(A1.class);
}
#Test public void testGenericAttributesKeyTypeProperties() {
final Properties expected=gA1.toProperties();
final GenericAttributes<A1> gA=new GenericAttributes<A1>(A1.class,expected);
final Properties actual=gA.toProperties();
assertEquals(expected,actual);
}
#Test public void testGet() {
final A1 key=A1.foo;
emptyGA1.put(key,null);
final Object actual=emptyGA1.get(key);
assertEquals(null,actual);
}
#Test public void testGetInteger() {
// attributes.add(key,integer);
// assertEquals(integer,attributes.get("key"));
}
#Test public void testContains() {
for(A1 a:A1.values())
assertFalse(emptyGA1.contains(a));
}
#Test public void testChange() {
final A1 key=A1.foo;
final Integer value=42;
emptyGA1.put(key,value);
final Integer expected=43;
emptyGA1.change(key,expected);
final Object actual=emptyGA1.get(key);
assertEquals(expected,actual);
}
#Test public void testAdd() {
final A1 key=A1.foo;
final Integer expected=42;
emptyGA1.put(key,expected);
final Object actual=emptyGA1.get(key);
assertEquals(expected,actual);
}
#Test public void testRemove() {
final A1 key=A1.foo;
final Integer value=42;
emptyGA1.put(key,value);
emptyGA1.remove(key);
assertFalse(emptyGA1.contains(key));
}
#Test public void testToString() {
final String actual=gA1.toString();
final String expected="foo=a foo value\nbar=a bar value\n";
assertEquals(expected,actual);
}
#Test public void testToStringEqualsEndOfLine() {
final String equals=",";
final String endOFLine=";";
final String actual=gA1.toString(equals,endOFLine);
final String expected="foo,a foo value;bar,a bar value;";
assertEquals(expected,actual);
}
#Test public void testEmbedded() {
final String equals=",";
final String endOfLine=";";
//System.out.println("toString(\""+equals+"\",\""+endOFLine+"\"):");
final String embedded=gA1.toString(equals,endOfLine);
GenericAttributes<A2> gA2=new GenericAttributes<A2>(A2.class);
gA2.put(A2.x,embedded);
//System.out.println("embedded:\n"+gA2);
// maybe do file={name=a.jpg;dx=1;zoom=.5}??
// no good, key must be used more than once
// so file:a.jpg={} and hack
// maybe file={name=...} will work
// since we have to treat it specially anyway?
// maybe this is better done in ss first
// to see how it grows?
}
#Test public void testFromString() {
// final Attributes a=Attributes.fromString("");
// final String expected="";
// assertEquals(expected,a.toString());
}
#Test public void testToProperties() {
final Properties expected=new Properties();
expected.setProperty("foo","a foo value");
expected.setProperty("bar","a bar value");
final Properties actual=gA1.toProperties();
assertEquals(expected,actual);
}
#Test public void testAddProperties() {
final Properties p=gA1.toProperties();
final GenericAttributes<A1> ga=new GenericAttributes<A1>(A1.class);
ga.addStringProperties(p);
// assertEquals(ga1,ga); // fails since we need to define equals!
// hack, go backwards
final Properties p2=ga.toProperties();
assertEquals(p,p2); // hack until we define equals
}
#Test public void testStore() throws Exception {
final Properties expected=gA1.toProperties();
final ByteArrayOutputStream baos=new ByteArrayOutputStream();
GenericAttributes.store(baos,expected);
baos.close();
final byte[] bytes=baos.toByteArray();
final ByteArrayInputStream bais=new ByteArrayInputStream(bytes);
final Properties actual=GenericAttributes.load(bais,null);
bais.close();
assertEquals(expected,actual);
}
#Test public void testLoad() throws Exception {
final Properties expected=gA1.toProperties();
final ByteArrayOutputStream baos=new ByteArrayOutputStream();
GenericAttributes.store(baos,expected);
baos.close();
final ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
final Properties actual=GenericAttributes.load(bais,null);
bais.close();
assertEquals(expected,actual);
}
#Test public void testMain() {
// fail("Not yet implemented");
}
GenericAttributes<A1> gA1=new GenericAttributes<A1>(A1.class);
{
gA1.put(A1.foo,"a foo value");
gA1.put(A1.bar,"a bar value");
}
GenericAttributes<A1> emptyGA1=new GenericAttributes<A1>(A1.class);
}
answering your comment:
seems like i am getting values by using the enum as the key. i am probably confused.
an enum can implement an interface and each set of enums could have an instance of that base class and delegate calls to it (see item 34 of http://java.sun.com/docs/books/effective/toc.html)
i found the other code that went with my generic attributes (please see below), but i can't find any tests for it and am not quite sure what i was doing other than perhaps to add some stronger typing.
my motivation for all of this was to store some attributes for a photo viewer like picasa, i wanted to store a bunch of attributes for a picture in a single line of a property file
package p;
import java.util.*;
public enum GA {
// like properties, seems like this wants to be constructed with a set of default values
i(Integer.class) {
Integer fromString(final String s) {
return new Integer(s);
}
Integer fromNull() {
return zero; // return empty string?
}
},
b(Boolean.class) {
Boolean fromString(final String s) {
return s.startsWith("t")?true:false;
}
Boolean fromNull() {
return false;
}
},
d(Double.class) {
Double fromString(final String s) {
return new Double(s);
}
Double fromNull() {
return new Double(zero);
}
};
GA() {
this(String.class);
}
GA(final Class clazz) {
this.clazz=clazz;
}
abstract Object fromString(String string);
abstract Object fromNull();
static GenericAttributes<GA> fromProperties(final Properties properties) {
final GenericAttributes<GA> pas=new GenericAttributes<GA>(GA.class);
for(Map.Entry<Object,Object> entry:properties.entrySet()) {
final String key=(String)entry.getKey();
final GA pa=valueOf(key);
if(pa!=null) {
final String stringValue=(String)entry.getValue();
Object value=pa.fromString(stringValue);
pas.addProperty(key,value);
} else throw new RuntimeException(key+"is not a member of "+"GA");
}
return pas;
}
// private final Object defaultValue; // lose type?; require cast?
/* private */final Class clazz;
static final Integer zero=new Integer(0);
}
If you are still looking for answers, you could give a try to Properties library which is open-source with MIT license. Using this, you won't have to specify string constants and everything will be determined by an enum defined by you. And, it has some other features too. Highlights of this library are:
All property keys are defined in a single place, i.e. a user defined enum
Property values can contain variables (starting with $ sign, e.g. $PATH) where PATH is a property key in same file
Property value can be obtained as specified data type, so no need to convert string value to required data type
Property value can be obtained as list of specified data types
Property value can be a multi-line text
Can make property keys mandatory or optional
Can specify default value for the property key if value is not available
Is thread safe
You can find sample programs here

Java Reflection getDeclaredMethod() with Class Types

I'm trying to understand Java reflecton and am encountering difficulties when working with non-Integer setter methods.
As an example, how can I resolve the "getDeclaredMethod()" call below?
import java.lang.reflect.*;
class Target {
String value;
public Target() { this.value = new String("."); }
public void setValue(String value) { this.value = value; }
public String getValue() { return this.value; }
}
class ReflectionTest {
public static void main(String args[]) {
try {
Class myTarget = Class.forName("Target");
Method myMethod;
myMethod = myTarget.getDeclaredMethod("getValue"); // Works!
System.out.println("Method Name: " + myMethod.toString());
Class params[] = new Class[1];
//params[0] = String.TYPE; // ?? What is the appropriate Class TYPE?
myMethod = myTarget.getDeclaredMethod("setValue", params); // ? Help ?
System.out.println("Method Name: " + myMethod.toString());
} catch (Exception e) {
System.out.println("ERROR");
}
}
}
params[0] = String.class;
Using class on String will return the Class<?> that is associated with the String class.

Categories

Resources