package er.extensions.eof;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Enumeration;
import com.webobjects.eoaccess.EOAttribute;
import com.webobjects.eoaccess.EOEntity;
import com.webobjects.eoaccess.EOEntityClassDescription;
import com.webobjects.eoaccess.EOModel;
import com.webobjects.eoaccess.EOModelGroup;
import com.webobjects.eocontrol.EOArrayDataSource;
import com.webobjects.eocontrol.EOClassDescription;
import com.webobjects.eocontrol.EOCustomObject;
import com.webobjects.eocontrol.EOEditingContext;
import com.webobjects.eocontrol.EOEnterpriseObject;
import com.webobjects.eocontrol.EOGlobalID;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
import er.extensions.foundation.ERXStringUtilities;
/**
* Put POJOs into EOF (sort of). This class is mainly usefull when used with
* D2W, when you don't want to create components for non-persistent objects.
* Should be regarded as experimental :)
* Thing to watch out for: <ul>
* <li> you can't create new objects
* <li> reverting the EC doesn't revert the objects
* <li> you should use a new EC with these objects
* <li> auto-discovery of attributes is very lame
* </ul>
* Here's a usage example, showing how to
* call up an edit page for a single "object" and a list page for an array. Note
* that the list-inspect-edit workflow and sorting, batching etc work out of the box.
* <pre><code>
* public class Main extends WOComponent {
*
* public static class Test {
*
* public String string;
* public Number number;
* public Boolean flag;
*
* public Test(String string, Number number, Boolean flag) {
* this.string = string;
* this.number = number;
* this.flag = flag;
* }
* }
*
* public Test object;
* public NSArray list;
*
* public Main(WOContext context) {
* super(context);
* ERXDummyRecord.registerDescriptionForClass(Test.class, null);
* NSMutableArray l = new NSMutableArray();
* for (int i = 0; i > 5; i++) {
* Test o = new Test("Foo "+ i, Integer.valueOf(i^i % (i+1)), i % 2 == 0? Boolean.TRUE : Boolean.FALSE);
* l.addObject(o);
* }
* object = (Test) l.lastObject();
* list = l.immutableClone();
* }
*
* public WOComponent editObject() {
* EOEnterpriseObject eo = ERXDummyRecord.recordForObject(session().defaultEditingContext(), object);
* WOComponent result = D2W.factory().pageForTaskAndEntityNamed("edit", eo.entityName(), session());
* result.takeValueForKey(eo, "object");
* result.takeValueForKey(context().page(), "nextPage");
* return result;
* }
*
* public WOComponent showList() {
* EOArrayDataSource ds = ERXDummyRecord.dataSourceForObjects(session().defaultEditingContext(), list);
* ds.setArray(objects);
* WOComponent result = D2W.factory().pageForTaskAndEntityNamed("list", ds.classDescriptionForObjects().entityName(), session());
* result.takeValueForKey(ds, "dataSource");
* result.takeValueForKey(context().page(), "nextPage");
* return result;
* }
* }</code></pre>
* @author ak
*/
public class ERXDummyRecord extends EOCustomObject {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
private Object object;
protected ERXDummyRecord(Object o) {
object = o;
EOClassDescription classDescription = classDescriptionForObject(object);
__setClassDescription(classDescription);
}
public Object object() {
return object;
}
public static class GlobalID extends EOGlobalID {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
private Object object;
public GlobalID(Object o) {
object = o;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof GlobalID) {
GlobalID gid = (GlobalID) obj;
return gid.object == object;
}
return false;
}
@Override
public int hashCode() {
return System.identityHashCode(object);
}
@Override
public boolean isTemporary() {
return true;
}
}
public static class ProxyBinding extends NSKeyValueCoding._KeyBinding {
public ProxyBinding(String key) {
super(null, key);
}
@Override
public Object valueInObject(Object object) {
ERXDummyRecord eo = (ERXDummyRecord) object;
return NSKeyValueCoding.Utility.valueForKey(eo.object(), _key);
}
@Override
public void setValueInObject(Object value, Object object) {
ERXDummyRecord eo = (ERXDummyRecord) object;
NSKeyValueCoding.Utility.takeValueForKey(eo.object(), value, _key);
}
}
@Override
public NSKeyValueCoding._KeyBinding _otherStorageBinding(String key) {
NSKeyValueCoding._KeyBinding result = new ProxyBinding(key);
return result;
}
private static EOModel pojoModel;
public static EOClassDescription classDescriptionForObject(Object object) {
return EOClassDescription.classDescriptionForClass(object.getClass());
}
private static NSArray fieldNamesFromClass(Class clazz) {
NSMutableArray fieldNames = new NSMutableArray();
Field f[] = clazz.getDeclaredFields();
for (int i = 0; i < f.length; i++) {
Field field = f[i];
fieldNames.addObject(field.getName().replaceFirst("^_", ""));
}
return fieldNames;
}
private static Field _fieldForName(Class clazz, String name) {
try {
return clazz.getDeclaredField(name);
}
catch (SecurityException e) {
}
catch (NoSuchFieldException e) {
}
return null;
}
private static Field fieldForName(Class clazz, String name) {
Field result = _fieldForName(clazz, name);
if(result == null) {
result = _fieldForName(clazz, "_" + name);
}
return result;
}
private static Method _methodForName(Class clazz, String name) {
try {
return clazz.getDeclaredMethod(name, (Class[])null);
}
catch (SecurityException e) {
}
catch (NoSuchMethodException e) {
}
return null;
}
private static Method methodForName(Class clazz, String name) {
Method result = _methodForName(clazz, "get" + ERXStringUtilities.capitalize(name));
if(result == null) {
result = _methodForName(clazz, name);
}
if(result == null) {
result = _methodForName(clazz, "_" + name);
}
return result;
}
public static synchronized void registerDescriptionForClass(Class clazz, NSArray keys) {
String entityName = "Pojo" + ERXStringUtilities.lastPropertyKeyInKeyPath(clazz.getName().replaceAll("\\$", ""));
if (pojoModel == null) {
pojoModel = new EOModel();
pojoModel.setName("PojoModel");
//ak: fake name for possible NPE
pojoModel.setAdaptorName("JDBCAdaptor");
//ak: fake dict for possible NPE
pojoModel.setConnectionDictionary(new NSMutableDictionary());
EOModelGroup.defaultGroup().addModel(pojoModel);
}
EOEntity entity = EOModelGroup.defaultGroup().entityNamed(entityName);
/*if (entity != null) {
pojoModel.removeEntity(entity);
entity = null;
}*/
EOClassDescription classDescription;
if (entity == null) {
entity = new EOEntity();
entity.setName(entityName);
keys = (keys == null ? fieldNamesFromClass(clazz) : keys);
for (Enumeration iter = keys.objectEnumerator(); iter.hasMoreElements();) {
String name = (String) iter.nextElement();
EOAttribute attribute = new EOAttribute();
attribute.setName(name);
Method m = methodForName(clazz, name);
if(m != null) {
attribute.setClassName(m.getReturnType().getName());
} else {
Field f = fieldForName(clazz, name);
if(f != null) {
String type = f.getType().getName();
if("boolean".equals(type)) {
type = "java.lang.Boolean";
} else if("int".equals(type)) {
type = "java.lang.Number";
} else if("long".equals(type)) {
type = "java.lang.Number";
} else if("short".equals(type)) {
type = "java.lang.Number";
}
attribute.setClassName(type);
}
}
entity.addAttribute(attribute);
}
classDescription = new EOEntityClassDescription(entity);
NSKeyValueCoding.Utility.takeValueForKey(entity, classDescription, "classDescription");
EOClassDescription.registerClassDescription(classDescription, clazz);
pojoModel.addEntity(entity);
} else {
// classDescription = entity.classDescriptionForInstances();
}
}
public static EOArrayDataSource dataSourceForObjects(EOEditingContext ec, NSArray list) {
EOClassDescription cd = null;
NSMutableArray objects = new NSMutableArray();
for (Enumeration iter = list.objectEnumerator(); iter.hasMoreElements();) {
Object o = iter.nextElement();
EOEnterpriseObject eo = recordForObject(ec, o);
ec = eo.editingContext();
cd = eo.classDescription();
objects.addObject(eo);
}
EOArrayDataSource ds = new EOArrayDataSource(cd, ec);
ds.setArray(objects);
return ds;
}
public static EOEnterpriseObject recordForObject(EOEditingContext ec, Object o) {
EOGlobalID gid = new GlobalID(o);
EOEnterpriseObject eo = ec.objectForGlobalID(gid);
if(eo == null) {
eo = new ERXDummyRecord(o);
ec.recordObject(eo, gid);
}
return eo;
}
}