someObject.setProperty(prop-name, prop-value). I write my own NewObject class:package my;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import my.annotation.ClassInfo;
import my.annotation.ClassInfoList;
import my.annotation.Property;
import my.annotation.PropertyAccess;
/**
* We just set/get values as Object type. End users know the exact type of the
* property, and they can do the conversion themselves.
*/
public class NewObject {
private static String makeGetPropertyName(Field field) {
String fieldName = field.getName();
if (fieldName == null || fieldName.equals("")) {
return null;
}
return "get" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
}
private static String makeSetPropertyName(Field field) {
String fieldName = field.getName();
if (fieldName == null || fieldName.equals("")) {
return null;
}
return "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
}
/**
* Get property
* @param name
* @return
*/
public Object getProperty(String name) {
Class klass = this.getClass();
Field field = null;
while (!klass.getName().equals(NewObject.class.getName())) {
try {
field = klass.getDeclaredField(name);
break; // found
} catch (NoSuchFieldException e) {
// noop
}
klass = klass.getSuperclass();
}
if (field != null) {
Property property = field.getAnnotation(Property.class);
if (property.value() == PropertyAccess.WRITE
|| property.value() == PropertyAccess.READWRITE) {
String methodName = makeGetPropertyName(field);
try {
/**
* We can also get the value directly as below, but this
* bypass the getter function which is wrong.
* <code>
* field.setAccessible(true);
* Object value = field.get(this);
* field.setAccessible(false);
* return value;
* </code>
*/
Method getMethod = klass.getMethod(methodName);
return getMethod.invoke(this);
} catch (IllegalAccessException e) {
// noop
} catch (IllegalArgumentException e) {
// noop
} catch (InvocationTargetException e) {
// noop
} catch (NoSuchMethodException e) {
// noop
} catch (SecurityException e) {
// noop
}
}
}
return null;
}
/**
* Set property
* @param name
* @param value
*/
public void setProperty(String name, Object value) {
Class klass = this.getClass();
Field field = null;
while (!klass.getName().equals(NewObject.class.getName())) {
try {
field = klass.getDeclaredField(name);
break; // found
} catch (NoSuchFieldException e) {
// noop
}
klass = klass.getSuperclass();
}
if (field != null) {
Property property = field.getAnnotation(Property.class);
if (property.value() == PropertyAccess.WRITE
|| property.value() == PropertyAccess.READWRITE) {
String methodName = makeSetPropertyName(field);
try {
Method setMethod = klass.getMethod(methodName, field.getType());
setMethod.invoke(this, value);
} catch (IllegalAccessException e) {
// noop
} catch (IllegalArgumentException e) {
// noop
} catch (InvocationTargetException e) {
// noop
} catch (NoSuchMethodException e) {
// noop
} catch (SecurityException e) {
// noop
}
}
}
}
/**
* Dump class info by given class
* @param klass
*/
public static void dumpClassInfo(Class klass) {
System.out.println(klass.getCanonicalName() + "(");
ClassInfo[] klassInfos = klass.getAnnotation(ClassInfoList.class).value();
for (int i = 0; i < klassInfos.length; i++) {
System.out.println(klassInfos[i].name() + "=" + klassInfos[i].value());
}
System.out.println(")");
}
/**
* Dump class info of current object
*/
public void dumpClassInfo() {
Class klass = this.getClass();
System.out.println(klass.getCanonicalName() + "(");
ClassInfo[] klassInfos = klass.getAnnotation(ClassInfoList.class).value();
for (int i = 0; i < klassInfos.length; i++) {
System.out.println(klassInfos[i].name() + "=" + klassInfos[i].value());
}
System.out.println(")");
}
/**
* Get class info by given name
* @param name
* @return
*/
public String getClassInfo(String name) {
Class klass = this.getClass();
ClassInfo[] klassInfos = klass.getAnnotation(ClassInfoList.class).value();
for (int i = 0; i < klassInfos.length; i++) {
if (klassInfos[i].name().equals(name)) {
return klassInfos[i].value();
}
}
return null;
}
}
To use our setProperty()/getProperty() method, all classes should derive from the NewObject class. To be consistent with the JavaBean convention, we assume that the getter/setter function to be "get"/"set" + capitalize_first_letter_of(member-variable-name). Property annotation and PropertyAccess enum are defined to indicate properties:// PropertyAccess.java
package my.annotation;
public enum PropertyAccess {
READ, WRITE, READWRITE
}
// Property.java
package my.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Property {
/* "value" seems to be a magic name */
public PropertyAccess value();
}
ClassInfo and ClassInfoList annotation are defined to indicate class meta info:// ClassInfo.java
package my.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassInfo {
public String name();
public String value();
}
// ClassInfoList.java
package my.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassInfoList {
ClassInfo[] value();
}
Let's see how to use them, our Base is defined as:// Base.java
package fake;
import java.util.LinkedList;
import java.util.List;
import my.NewObject;
import my.annotation.ClassInfo;
import my.annotation.ClassInfoList;
import my.annotation.Property;
import my.annotation.PropertyAccess;
@ClassInfoList({
@ClassInfo(name = "author", value = "gonwan"),
@ClassInfo(name = "version", value = "1.0.0")
})
public class Base extends NewObject {
@Property(PropertyAccess.READWRITE)
private int id;
@Property(PropertyAccess.READWRITE)
private String name;
private List<IPrintInt> basePrintIntListeners;
private List<IPrintString> basePrintStringListeners;
public Base() {
basePrintIntListeners = new LinkedList<IPrintInt>();
basePrintStringListeners = new LinkedList<IPrintString>();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void virtualDump() {
System.out.printf("Base(virtual): id=%d, name=\"%s\"\n", getId(), getName());
}
final public void nonvirtualDump() {
System.out.printf("Base(nonvirtual): id=%d, name=\"%s\"\n", getId(), getName());
}
public void addBasePrintIntListener(IPrintInt listener) {
basePrintIntListeners.add(listener);
}
public void addBasePrintStringListener(IPrintString listener) {
basePrintStringListeners.add(listener);
}
public void fireBasePrintIntEvent(int i) {
for (IPrintInt listener : basePrintIntListeners) {
listener.printInt(i);
}
}
public void fireBasePrintStringEvent(String str) {
for (IPrintString listener : basePrintStringListeners) {
listener.PrintString(str);
}
}
}
Since our implementation of properties are simply methods, they can be inherited by subclasses. But the class meta info cannot be retrieved in subclasses. They just get their own. I do not want to demo events/listeners code here, just find them in source code in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestJavaObject-{date}.zip file.

0 COMMENTS:
Post a Comment