r6998 - in dumbhippo/trunk/server: src/com/dumbhippo/dm/annotations src/com/dumbhippo/dm/schema tests/com/dumbhippo/dm tests/com/dumbhippo/dm/dm



Author: otaylor
Date: 2007-12-10 11:15:15 -0600 (Mon, 10 Dec 2007)
New Revision: 6998

Added:
   dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/MetaConstruct.java
Modified:
   dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/DMO.java
   dumbhippo/trunk/server/src/com/dumbhippo/dm/schema/DMClassHolder.java
   dumbhippo/trunk/server/tests/com/dumbhippo/dm/InheritanceTests.java
   dumbhippo/trunk/server/tests/com/dumbhippo/dm/dm/TestSuperUserDMO.java
Log:
DMO InheritanceTests TestSuperUserDMO DMClassHolder: Make resourceBase property 
 of the @DMO annotation optional; require it on base classes, require it not to
 be present on subclasses.

MetaConstruct DMClassHolder: Add @MetaConstruct annotation that marks a property
 on a DMO base class as meaning "this method maps from a key object to the class
 to use to construct an instance corresponding to that key"

DMClassHolder: Track whether the DMClassHolder is subclassed (not curently
 used)


Modified: dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/DMO.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/DMO.java	2007-12-10 04:54:11 UTC (rev 6997)
+++ dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/DMO.java	2007-12-10 17:15:15 UTC (rev 6998)
@@ -9,5 +9,5 @@
 @Target(ElementType.TYPE)
 public @interface DMO {
 	String classId();
-	String resourceBase();
+	String resourceBase() default "";
 }

Added: dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/MetaConstruct.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/MetaConstruct.java	2007-12-10 04:54:11 UTC (rev 6997)
+++ dumbhippo/trunk/server/src/com/dumbhippo/dm/annotations/MetaConstruct.java	2007-12-10 17:15:15 UTC (rev 6998)
@@ -0,0 +1,16 @@
+package com.dumbhippo.dm.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation applied to a method of a baseclass of an inheritance heirarchy of
+ * DMObject's to indicate that the method takes a key as an argument, and
+ * returns the class of the DMObject to construct for the key.   
+ */
+ Retention(RetentionPolicy.RUNTIME)
+ Target(ElementType.METHOD)
+public @interface MetaConstruct {
+}

Modified: dumbhippo/trunk/server/src/com/dumbhippo/dm/schema/DMClassHolder.java
===================================================================
--- dumbhippo/trunk/server/src/com/dumbhippo/dm/schema/DMClassHolder.java	2007-12-10 04:54:11 UTC (rev 6997)
+++ dumbhippo/trunk/server/src/com/dumbhippo/dm/schema/DMClassHolder.java	2007-12-10 17:15:15 UTC (rev 6998)
@@ -3,6 +3,7 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
@@ -37,6 +38,7 @@
 import com.dumbhippo.dm.annotations.DMFilter;
 import com.dumbhippo.dm.annotations.DMO;
 import com.dumbhippo.dm.annotations.Inject;
+import com.dumbhippo.dm.annotations.MetaConstruct;
 import com.dumbhippo.dm.filter.CompiledFilter;
 import com.dumbhippo.dm.filter.CompiledItemFilter;
 import com.dumbhippo.dm.filter.CompiledListFilter;
@@ -55,8 +57,11 @@
 	
 	private DataModel model;
 	private Class<T> dmoClass;
+	DMClassHolder<K,? extends DMObject<K>> baseClassHolder;
+	private boolean subclassed = false;
 	private Class<K> keyClass;
 	private Constructor<K> keyStringConstructor;
+	private Method metaConstructor;
 	private Constructor<? extends T> wrapperConstructor;
 	private DMPropertyHolder<K,T,?>[] properties;
 	private boolean[] mustQualify;
@@ -104,6 +109,44 @@
 			throw new RuntimeException(dmoClass.getName() + ": classId '" + annotation.classId() + "' is not a valid URI");
 		}
 		
+		Class<?> parentClass = dmoClass.getSuperclass();
+		while (parentClass != null) {
+			if (parentClass.isAnnotationPresent(DMO.class)) {
+				DMClassHolder<?,?> classHolder = model.getClassHolder(parentClass);
+				if (classHolder.getKeyClass() != keyClass) {
+					throw new RuntimeException(dmoClass.getName() + ": parent class " + parentClass.getName() + " has a different key type");
+				}
+				@SuppressWarnings("unchecked")
+				DMClassHolder<K,T> tmpHolder = (DMClassHolder<K,T>)classHolder;
+				baseClassHolder = tmpHolder;
+				baseClassHolder.subclassed = true;
+			}
+			
+			parentClass = parentClass.getSuperclass();
+		}
+		
+		if ("".equals(annotation.resourceBase())) {
+			if (baseClassHolder == null)
+				throw new RuntimeException(dmoClass.getName() + ": resourceBase must be specified for base class in DMO inheritance heirarchy");
+		} else {
+			if (baseClassHolder != null)
+				throw new RuntimeException(dmoClass.getName() + ": resourceBase must not be specified for derived DMO class");
+		}
+		
+		for (Method method : dmoClass.getDeclaredMethods()) {
+			if (method.isAnnotationPresent(MetaConstruct.class)) {
+				if (metaConstructor != null)
+					throw new RuntimeException(dmoClass.getName() + ": Two @MetaConstruct method");
+				if (!(Class.class.isAssignableFrom(method.getReturnType())))
+					throw new RuntimeException(dmoClass.getName() + ": @MetaConstruct method must return a class");
+				if (method.getParameterTypes().length != 1)
+					throw new RuntimeException(dmoClass.getName() + ": @MetaConstruct method must take a single parameter");
+				if (!method.getParameterTypes()[0].isAssignableFrom(keyClass))
+					throw new RuntimeException(dmoClass.getName() + ": @MetaConstruct method parameter must match key type");
+				
+				metaConstructor = method;
+			}
+		}
 
 		buildWrapperClass();
 		
@@ -137,7 +180,10 @@
 	}
 	
 	public String getResourceBase() {
-		return annotation.resourceBase();
+		if (baseClassHolder != null)
+			return baseClassHolder.getResourceBase();
+		else
+			return annotation.resourceBase();
 	}
 	
 	public String getClassId() {
@@ -195,11 +241,10 @@
 	public boolean mustQualifyProperty(int propertyIndex) {
 		return mustQualify[propertyIndex];
 	}
-
-	public T createInstance(Object key, DMSession session) {
+	
+	public T createInstance(K key, DMSession session) {
 		try {
-			T result = wrapperConstructor.newInstance(key, session);
-			return result;
+			return wrapperConstructor.newInstance(key, session);
 		} catch (Exception e) {
 			throw new RuntimeException("Error creating instance of class " + dmoClass.getName(), e);
 		}
@@ -233,20 +278,41 @@
 	}
 
 	public String makeResourceId(K key) {
-		return model.getBaseUrl() + annotation.resourceBase() + "/" + key.toString();
+		return model.getBaseUrl() + getResourceBase() + "/" + key.toString();
 	}
 	
 	public String makeRelativeId(K key) {
-		return annotation.resourceBase() + "/" + key.toString();
+		return getResourceBase() + "/" + key.toString();
 	}
 
-	public StoreKey<K,T> makeStoreKey(String string) throws BadIdException {
+	@SuppressWarnings("unchecked")
+	private DMClassHolder<K,? extends T> getClassHolderForKey(K key) {
+		if (metaConstructor != null) {
+			try {
+				Class<?> clazz = (Class<?>)metaConstructor.invoke(null, new Object[] { key });
+				return (DMClassHolder<K,? extends T>)model.getClassHolder(clazz);
+			} catch (Exception e) {
+				throw new RuntimeException("Error calling metaconstructor of class " + dmoClass.getName(), e);
+			}
+		} else {
+			return this;
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	public StoreKey<K,? extends T> makeStoreKey(K key) {
+		DMClassHolder subClassHolder = getClassHolderForKey(key);
+		return new StoreKey(subClassHolder, key);
+	}
+	
+	public StoreKey<K,? extends T> makeStoreKey(String string) throws BadIdException {
+		
 		if (keyClass == Guid.class) {
 			try {
 				Guid guid = new Guid(string);
 				
 				@SuppressWarnings("unchecked")
-				StoreKey<K,T> key = new StoreKey(this, guid);
+				StoreKey<K,? extends T> key = makeStoreKey((K)guid);
 				return key;
 			} catch (ParseException e) {
 				throw new BadIdException("Invalid GUID in resourceId");
@@ -254,25 +320,23 @@
 			
 		} else if (keyClass == String.class) {
 			@SuppressWarnings("unchecked")
-			StoreKey<K,T> key = new StoreKey(this, string);
+			StoreKey<K,? extends T> key = makeStoreKey((K)string);
 			return key;
 		} else if (keyClass == Long.class) {
 			try {
 				Long l = Long.parseLong(string);
 				
 				@SuppressWarnings("unchecked")
-				StoreKey<K,T> key = new StoreKey(this, l);
+				StoreKey<K,? extends T> key = makeStoreKey((K)l);
 				return key;
 			} catch (NumberFormatException e) {
 				throw new BadIdException("Invalid long in resourceId");
 			}
 		} else {
 			try {
-				Object keyObject = keyStringConstructor.newInstance(string);
+				K keyObject = keyStringConstructor.newInstance(string);
 				
-				@SuppressWarnings("unchecked")
-				StoreKey<K,T> key = new StoreKey(this, keyObject);
-				return key;
+				return makeStoreKey(keyObject);
 			} catch (InstantiationException e) {
 				throw new RuntimeException("Error creating key object from string", e);
 			} catch (IllegalAccessException e) {

Modified: dumbhippo/trunk/server/tests/com/dumbhippo/dm/InheritanceTests.java
===================================================================
--- dumbhippo/trunk/server/tests/com/dumbhippo/dm/InheritanceTests.java	2007-12-10 04:54:11 UTC (rev 6997)
+++ dumbhippo/trunk/server/tests/com/dumbhippo/dm/InheritanceTests.java	2007-12-10 17:15:15 UTC (rev 6998)
@@ -39,7 +39,7 @@
 		superUserDMO = session.find(TestSuperUserDMO.class, guid);
 		assertTrue(superUserDMO != null);
 		assertEquals(guid.toString(), superUserDMO.getKey().toString());
-		assertEquals("http://mugshot.org/o/test/superUser/"; + guid, superUserDMO.getResourceId());
+		assertEquals("http://mugshot.org/o/test/user/"; + guid, superUserDMO.getResourceId());
 		assertEquals("*The Nose*", superUserDMO.getName());
 		assertEquals("The ability to tell if leftovers have gone bad", superUserDMO.getSuperPower());
 

Modified: dumbhippo/trunk/server/tests/com/dumbhippo/dm/dm/TestSuperUserDMO.java
===================================================================
--- dumbhippo/trunk/server/tests/com/dumbhippo/dm/dm/TestSuperUserDMO.java	2007-12-10 04:54:11 UTC (rev 6997)
+++ dumbhippo/trunk/server/tests/com/dumbhippo/dm/dm/TestSuperUserDMO.java	2007-12-10 17:15:15 UTC (rev 6998)
@@ -8,7 +8,12 @@
 import com.dumbhippo.dm.persistence.TestSuperUser;
 import com.dumbhippo.identity20.Guid;
 
- DMO(classId="http://mugshot.org/p/o/test/superUser";, resourceBase="/o/test/superUser")
+// FIXME: This is not actually valid inheritance since it must be possible to
+// determine the class in the inheritance heirarchy from the key; but it works
+// for what we are testing at the moment because we explicitly 
+// em.find(TestSuperUser.DMO.class)
+
+ DMO(classId="http://mugshot.org/p/o/test/superUser";)
 public abstract class TestSuperUserDMO extends TestUserDMO {
 	@SuppressWarnings("unused")
 	static private final Logger logger = GlobalSetup.getLogger(TestSuperUserDMO.class);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]