\r
package eu.etaxonomy.cdm.remote.controller;\r
\r
+import java.beans.PropertyDescriptor;\r
import java.io.IOException;\r
import java.lang.reflect.InvocationTargetException;\r
import java.lang.reflect.Method;\r
+import java.lang.reflect.ParameterizedType;\r
+import java.lang.reflect.Type;\r
+import java.util.ArrayList;\r
import java.util.Arrays;\r
import java.util.Collection;\r
import java.util.List;\r
import javax.servlet.http.HttpServletRequest;\r
import javax.servlet.http.HttpServletResponse;\r
\r
+import org.apache.commons.beanutils.PropertyUtils;\r
import org.apache.commons.io.FilenameUtils;\r
import org.apache.commons.lang.StringUtils;\r
import org.hibernate.mapping.Map;\r
-import org.springframework.util.Assert;\r
+import org.springframework.beans.factory.annotation.Autowired;\r
import org.springframework.web.bind.WebDataBinder;\r
import org.springframework.web.bind.annotation.InitBinder;\r
import org.springframework.web.bind.annotation.PathVariable;\r
import org.springframework.web.bind.annotation.RequestMapping;\r
import org.springframework.web.bind.annotation.RequestMethod;\r
+import org.springframework.web.bind.annotation.RequestParam;\r
import org.springframework.web.servlet.ModelAndView;\r
\r
import eu.etaxonomy.cdm.api.service.IService;\r
+import eu.etaxonomy.cdm.api.service.pager.Pager;\r
+import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;\r
+import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
import eu.etaxonomy.cdm.model.common.CdmBase;\r
+import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;\r
import eu.etaxonomy.cdm.remote.editor.UUIDPropertyEditor;\r
\r
-//$Id$\r
/**\r
* based on org.cateproject.controller.common\r
* @author b.clark\r
\r
protected SERVICE service;\r
\r
+ protected Class<T> baseClass;\r
+ \r
public abstract void setService(SERVICE service);\r
+ \r
+ public BaseController (){\r
+ \r
+ Type superClass = this.getClass().getGenericSuperclass();\r
+ if(superClass instanceof ParameterizedType){\r
+ ParameterizedType parametrizedSuperClass = (ParameterizedType) superClass;\r
+ Type[] typeArguments = parametrizedSuperClass.getActualTypeArguments();\r
+ \r
+ if(typeArguments.length > 1 && typeArguments[0] instanceof Class<?>){\r
+ baseClass = (Class<T>) typeArguments[0];\r
+ } else {\r
+ logger.error("unable to find baseClass");\r
+ }\r
+ }\r
+ }\r
\r
@InitBinder\r
public void initBinder(WebDataBinder binder) {\r
binder.registerCustomEditor(UUID.class, new UUIDPropertyEditor());\r
}\r
\r
- @SuppressWarnings("unchecked")\r
+ //TODO implement bulk version of this method\r
@RequestMapping(method = RequestMethod.GET)\r
- public T doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {\r
- logger.info("doGet() " + request.getServletPath());\r
- T obj = (T) getCdmBase(request, response, initializationStrategy, CdmBase.class);\r
+ public T doGet(@PathVariable("uuid") UUID uuid,\r
+ HttpServletRequest request, \r
+ HttpServletResponse response) throws IOException {\r
+ if(request != null)\r
+ logger.info("doGet() " + request.getServletPath());\r
+ T obj = (T) getCdmBaseInstance(uuid, response, initializationStrategy);\r
return obj;\r
}\r
-\r
+ \r
/**\r
+ * @param uuid\r
* @param request\r
* @param response\r
- * @param obj\r
* @return\r
* @throws IOException\r
+ * \r
+ * TODO implement bulk version of this method\r
*/\r
- protected <CDM_BASE> CDM_BASE getCdmBase(HttpServletRequest request, HttpServletResponse response, \r
- List<String> initStrategy, Class<CDM_BASE> clazz) throws IOException {\r
- T obj = null;\r
- try {\r
- UUID uuid = readValueUuid(request, null);\r
- Assert.notNull(uuid, HttpStatusMessage.UUID_MISSING.toString());\r
- \r
- if(initStrategy == null){\r
- // may be null is set to null via the setter\r
- obj = service.find(uuid);\r
- } else { \r
- obj = service.load(uuid, initStrategy);\r
+ @RequestMapping(value = "*", method = RequestMethod.GET)\r
+ public ModelAndView doGetMethod(\r
+ @PathVariable("uuid") UUID uuid,\r
+ // doPage request parametes\r
+ @RequestParam(value = "pageNumber", required = false) Integer pageNumber,\r
+ @RequestParam(value = "pageSize", required = false) Integer pageSize,\r
+ // doList request parametes\r
+ @RequestParam(value = "start", required = false) Integer start,\r
+ @RequestParam(value = "limit", required = false) Integer limit,\r
+ HttpServletRequest request,\r
+ HttpServletResponse response) throws IOException {\r
+ \r
+ ModelAndView modelAndView = new ModelAndView();\r
+ \r
+ String servletPath = request.getServletPath();\r
+ String baseName = FilenameUtils.getBaseName(servletPath);\r
+ \r
+ if(request != null)\r
+ logger.info("doGetMethod()[doGet" + StringUtils.capitalize(baseName) + "] " + request.getServletPath());\r
+ \r
+ // <CUT\r
+// T instance = getCdmBaseInstance(uuid, response, (List<String>)null);\r
+ \r
+ //Class<?> propertyClass = propertyClass(instance, baseName);\r
+ \r
+ Object objectFromProperty = getCdmBaseProperty(uuid, baseName, response);// invokeProperty(instance, baseName, response);\r
+ \r
+ // CUT>\r
+ \r
+ if(objectFromProperty != null){\r
+\r
+ if( Collection.class.isAssignableFrom(objectFromProperty.getClass())){\r
+ // Map types cannot be returned as list or in a pager!\r
+ \r
+ Collection c = (Collection)objectFromProperty;\r
+ if(start != null){\r
+ // return list\r
+ limit = (limit == null ? DEFAULT_PAGE_SIZE : limit);\r
+ Collection sub_c = subCollection(c, start, limit);\r
+ modelAndView.addObject(sub_c);\r
+ \r
+ } else {\r
+ //FIXME use real paging mechanism of according service class instead of subCollection()\r
+ //FIXME use BaseListController.normalizeAndValidatePagerParameters(pageNumber, pageSize, response);\r
+ pageSize = (pageSize == null ? DEFAULT_PAGE_SIZE : pageSize);\r
+ pageNumber = (pageNumber == null ? 0 : pageNumber);\r
+ start = pageNumber * pageSize;\r
+ List sub_c = subCollection(c, start, pageSize);\r
+ Pager p = new DefaultPagerImpl(pageNumber, c.size(), pageSize, sub_c);\r
+ modelAndView.addObject(p);\r
+ }\r
+ \r
+ } else {\r
+ modelAndView.addObject(objectFromProperty); \r
}\r
- Assert.notNull(obj, HttpStatusMessage.UUID_NOT_FOUND.toString());\r
+\r
+ }\r
+\r
+ if(modelAndView.isEmpty()){\r
+ return null; \r
+ } else {\r
\r
- } catch (IllegalArgumentException iae) {\r
- HttpStatusMessage.fromString(iae.getMessage()).send(response);\r
+ return modelAndView;\r
}\r
- CDM_BASE t;\r
+ }\r
+\r
+ public Object getCdmBaseProperty(UUID uuid, String property, HttpServletResponse response) throws IOException{\r
+ \r
+ T instance = (T) HibernateProxyHelper.deproxy(getCdmBaseInstance(uuid, response, property));\r
+ \r
+ Object objectFromProperty = invokeProperty(instance, property, response);\r
+ \r
+ return objectFromProperty;\r
+ }\r
+ \r
+ private Class<?> propertyClass(T instance, String baseName) {\r
+ PropertyDescriptor propertyDescriptor = null;\r
+ Class<?> c = null;\r
try {\r
- t = (CDM_BASE)obj;\r
- return t;\r
- } catch (Exception e) {\r
+ propertyDescriptor = PropertyUtils.getPropertyDescriptor(instance, baseName);\r
+ if(propertyDescriptor != null){\r
+ c = propertyDescriptor.getClass(); \r
+ }\r
+ } catch (IllegalAccessException e) {\r
+ // TODO Auto-generated catch block\r
+ e.printStackTrace();\r
+ } catch (InvocationTargetException e) {\r
+ // TODO Auto-generated catch block\r
+ e.printStackTrace();\r
+ } catch (NoSuchMethodException e) {\r
+ // TODO Auto-generated catch block\r
+ e.printStackTrace();\r
+ }\r
+ return c;\r
+ }\r
+\r
+ /**\r
+ * @param <SUB_T>\r
+ * @param clazz\r
+ * @param uuid\r
+ * @param response\r
+ * @param pathProperties\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ protected final <SUB_T extends T> SUB_T getCdmBaseInstance(Class<SUB_T> clazz, UUID uuid, HttpServletResponse response, List<String> pathProperties)\r
+ throws IOException {\r
+\r
+ CdmBase cdmBaseObject = getCdmBaseInstance(uuid, response, pathProperties);\r
+ if(!clazz.isAssignableFrom(cdmBaseObject.getClass())){\r
HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);\r
- return null;\r
}\r
+ return (SUB_T) cdmBaseObject;\r
}\r
\r
- protected T getCdmBaseInstance(UUID uuid, \r
- HttpServletResponse response, \r
- List<String> pathProperties) throws IOException{\r
- CdmBase cdmBaseObject = service.load(uuid, pathProperties);\r
- if(cdmBaseObject == null){\r
- HttpStatusMessage.UUID_NOT_FOUND.send(response);\r
+ /**\r
+ * @param <SUB_T>\r
+ * @param clazz\r
+ * @param uuid\r
+ * @param response\r
+ * @param pathProperty\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ protected final <SUB_T extends T> SUB_T getCdmBaseInstance(Class<SUB_T> clazz, UUID uuid, HttpServletResponse response, String pathProperty)\r
+ throws IOException {\r
+ \r
+ CdmBase cdmBaseObject = getCdmBaseInstance(uuid, response, pathProperty);\r
+ if(!clazz.isAssignableFrom(cdmBaseObject.getClass())){\r
+ HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);\r
}\r
- return (T) cdmBaseObject;\r
+ return (SUB_T) cdmBaseObject;\r
}\r
\r
- protected T getCdmBaseInstance(UUID uuid, HttpServletResponse response, String pathProperty) throws IOException{\r
- return getCdmBaseInstance(uuid, response, Arrays.asList(new String[]{pathProperty}));\r
+ /**\r
+ * @param uuid\r
+ * @param response\r
+ * @param pathProperty\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ protected final T getCdmBaseInstance(UUID uuid, HttpServletResponse response, String pathProperty)\r
+ throws IOException {\r
+ return getCdmBaseInstance(baseClass, uuid, response, Arrays\r
+ .asList(new String[] { pathProperty }));\r
}\r
\r
\r
- @RequestMapping(value = "{uuid}/*", method = RequestMethod.GET)\r
- public ModelAndView doGetMethod(@PathVariable("uuid") UUID uuid,\r
- HttpServletRequest request,\r
- HttpServletResponse response) throws IOException {\r
- logger.info("doGetMethod() " + request.getServletPath());\r
- \r
- ModelAndView modelAndView = new ModelAndView();\r
- \r
- String servletPath = request.getServletPath();\r
- String baseName = FilenameUtils.getBaseName(servletPath);\r
- \r
- T instance = getCdmBaseInstance(uuid, response, Arrays.asList(new String[]{baseName + ".titleCache"}));\r
+ /**\r
+ * @param uuid\r
+ * @param response\r
+ * @param pathProperties\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ protected final T getCdmBaseInstance(UUID uuid, HttpServletResponse response, List<String> pathProperties)\r
+ throws IOException {\r
+ return getCdmBaseInstance(baseClass, service, uuid, response, pathProperties);\r
+ }\r
+\r
+ /**\r
+ * @param <CDM_BASE>\r
+ * @param clazz\r
+ * @param service\r
+ * @param uuid\r
+ * @param response\r
+ * @param pathProperties\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ protected final <CDM_BASE extends CdmBase> CDM_BASE getCdmBaseInstance(Class<CDM_BASE> clazz, IService<CDM_BASE> service, UUID uuid, HttpServletResponse response, List<String> pathProperties)\r
+ throws IOException {\r
+ \r
+ CDM_BASE cdmBaseObject = service.load(uuid, pathProperties);\r
+ if (cdmBaseObject == null) {\r
+ HttpStatusMessage.UUID_NOT_FOUND.send(response);\r
+ }\r
+ return cdmBaseObject;\r
+ }\r
+\r
+ /**\r
+ * @param instance\r
+ * @param baseName\r
+ * @param response\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ private final Object invokeProperty(T instance,\r
+ String baseName, HttpServletResponse response) throws IOException {\r
\r
+ Object result = null; \r
try {\r
- Method method = instance.getClass().getDeclaredMethod("get" + StringUtils.capitalize(baseName), null);\r
+ PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(instance, baseName);\r
+ if(propertyDescriptor == null){\r
+ throw new NoSuchMethodException("No such method: " + instance.getClass().getSimpleName() + ".get" + baseName);\r
+ }\r
+ Method method = propertyDescriptor.getReadMethod();\r
\r
Class<?> returnType = method.getReturnType();\r
\r
- if(CdmBase.class.isAssignableFrom(returnType)){\r
- CdmBase resultInstance = (CdmBase) method.invoke(instance, null);\r
- modelAndView.addObject(resultInstance);\r
- }\r
- else if(Collection.class.isAssignableFrom(returnType) || Map.class.isAssignableFrom(returnType)){\r
- // TODO\r
- logger.warn("Collections or Maps not implemented yet.");\r
+ if(CdmBase.class.isAssignableFrom(returnType) \r
+ || Collection.class.isAssignableFrom(returnType) \r
+ || Map.class.isAssignableFrom(returnType)\r
+ || INomenclaturalReference.class.isAssignableFrom(returnType)){\r
+ \r
+ result = method.invoke(instance, (Object[])null);\r
+ \r
+ result = HibernateProxyHelper.deproxy(result);\r
+ \r
}else{\r
HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);\r
}\r
} catch (InvocationTargetException e) {\r
HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);\r
}\r
+ return result;\r
+ }\r
+ \r
+ private <E> List<E> subCollection(Collection<? extends E> c, Integer start, Integer length){\r
+ List<E> sub_c = new ArrayList<E>(length);\r
+ if(c.size() > length){\r
+ E[] a = (E[]) c.toArray();\r
+ for(int i = start; i < start + length; i++){\r
+ sub_c.add(a[i]);\r
+ }\r
+ } else {\r
+ sub_c.addAll(c);\r
+ }\r
+ return sub_c;\r
\r
- return modelAndView;\r
}\r
\r
\r