/* * Copyright 2001-2006 Geert Bevin * Distributed under the terms of either: * - the common development and distribution license (CDDL), v1.0; or * - the GNU Lesser General Public License, v2.1 or later * $Id: Groovy2ElementInfo.java 2930 2006-02-06 18:45:59Z gbevin $ */ package com.uwyn.rife.engine; import com.uwyn.rife.engine.annotations.*; import com.uwyn.rife.engine.exceptions.*; import com.uwyn.rife.engine.annotations.Submission; import com.uwyn.rife.resources.ResourceFinder; import com.uwyn.rife.resources.ResourceFinderClasspath; import com.uwyn.rife.tools.BeanUtils; import com.uwyn.rife.tools.ClassUtils; import com.uwyn.rife.tools.StringUtils; import com.uwyn.rife.tools.exceptions.BeanUtilsException; import java.beans.BeanInfo; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; class Annotations2ElementInfo implements ElementInfoProcessor { public void processElementInfo(ElementInfoBuilder builder, String declarationName, ResourceFinder resourceFinder) throws EngineException { if (Double.parseDouble(System.getProperty("java.specification.version")) < 1.5) { throw new Jdk15RequiredForAnnotationsException(builder.getSiteBuilder().getDeclarationName(), declarationName); } try { Class klass = Class.forName("com.uwyn.rife.engine.Annotations2ElementInfoProcessor"); ElementInfoProcessor processor = (ElementInfoProcessor)klass.newInstance(); processor.processElementInfo(builder, declarationName, resourceFinder); } catch (Exception e) { throw new EngineException(e); } } } class Annotations2ElementInfoProcessor implements ElementInfoProcessor { public void processElementInfo(ElementInfoBuilder builder, String declarationName, ResourceFinder resourceFinder) throws EngineException { resourceFinder = ResourceFinderClasspath.getInstance(); try { Class element_class = ElementFactory.INSTANCE.getJavaClass(declarationName, declarationName); if (element_class.isAnnotationPresent(Elem.class)) { // handle class annotations Elem elem = (Elem)element_class.getAnnotation(Elem.class); if (!builder.getElementDeclaration().hasDeclaredId()) { if (elem.id().equals("")) { builder.setId(ClassUtils.simpleClassName(element_class)); } else { builder.setId(elem.id()); } } if (!builder.getElementDeclaration().hasDeclaredUrl()) { if (elem.url().equals(Elem.DEFAULT_URL)) { builder.setUrl(ClassUtils.shortenClassName(element_class)); } else if (elem.url().equals("")) { // set no URL } else { builder.setUrl(elem.url()); } } String inherits = getElementId(builder, elem.inheritsClass(), elem.inheritsId()); String pre = getElementId(builder, elem.preClass(), elem.preId()); if (!inherits.equals("")) builder.setInherits(inherits); if (!pre.equals("")) builder.setInherits(pre); if (!elem.contentType().equals(Elem.DEFAULT_CONTENT_TYPE)) builder.setContentType(elem.contentType()); for (Input input : elem.inputs()) builder.addInput(input.name(), input.defaultValues()); for (InBean bean : elem.inbeans()) builder.addInBean(bean.beanclass(), bean.prefix(), bean.name(), bean.group()); for (InCookie cookie : elem.incookies()) builder.addIncookie(cookie.name(), cookie.defaultValue()); for (Output output : elem.outputs()) builder.addOutput(output.name(), output.defaultValues()); for (OutBean bean : elem.outbeans()) builder.addOutBean(bean.beanclass(), bean.prefix(), bean.name(), bean.group()); for (OutCookie cookie : elem.outcookies()) builder.addOutcookie(cookie.name(), cookie.defaultValue()); SubmissionBuilder submissionbuilder = null; for (Submission submission : elem.submissions()) { if (submissionbuilder != null) submissionbuilder.leaveSubmission(); submissionbuilder = builder.enterSubmission(submission.name()); submissionbuilder.setScope(Scope.getScope(submission.scope().toString())); for (Param param : submission.params()) submissionbuilder.addParameter(param.name(), param.defaultValues()); for (ParamRegexp param : submission.paramRegexps()) submissionbuilder.addParameterRegexp(param.value()); for (SubmissionBean bean : submission.beans()) submissionbuilder.addBean(bean.beanclass(), bean.prefix(), bean.name(), bean.group()); for (File file : submission.files()) submissionbuilder.addFile(file.name()); for (FileRegexp file : submission.fileRegexps()) submissionbuilder.addFileRegexp(file.value()); } for (Exit exit : elem.exits()) builder.addExit(exit.name()); for (ChildTrigger childtrigger : elem.childTriggers()) builder.addChildTrigger(childtrigger.name()); Pathinfo pathinfo = elem.pathinfo(); builder.setPathInfoMode(PathInfoMode.getMode(pathinfo.policy().toString())); for (Mapping mapping : pathinfo.mappings()) builder.addPathInfoMapping(mapping.value()); for (Flowlink flowlink : elem.flowlinks()) { String destId = getElementId(builder, flowlink.destClass(), flowlink.destId()); FlowLinkBuilder flowlinkbuilder = builder.enterFlowLink(flowlink.srcExit()) .destId(destId) .snapback(flowlink.snapback()) .cancelInheritance(flowlink.inheritance().equals(Flowlink.Inheritance.CANCEL)) .cancelEmbedding(flowlink.embedding().equals(Flowlink.Embedding.CANCEL)) .redirect(flowlink.redirect()); for (Datalink datalink : flowlink.datalinks()) { flowlinkbuilder.addDataLink(datalink.srcOutput(), datalink.srcOutbean(), datalink.snapback(), datalink.destInput(), datalink.destInbean()); } flowlinkbuilder.leaveFlowLink(); } for (Datalink datalink : elem.datalinks()) { String destId = getElementId(builder, datalink.destClass(), datalink.destId()); builder.addDataLink(datalink.srcOutput(), datalink.srcOutbean(), destId, datalink.snapback(), datalink.destInput(), datalink.destInbean()); } // process all bean property accessors, validate the annotations used on them, // and keep them in a list of methods that need to be processed Map methods_to_process = new HashMap(); // obtain the BeanInfo class BeanInfo bean_info = BeanUtils.getBeanInfo(element_class); // process the properties of the bean PropertyDescriptor[] bean_properties = bean_info.getPropertyDescriptors(); if (bean_properties.length > 0) { // iterate over the properties of the bean for (PropertyDescriptor descriptor : bean_properties) { Method write_method = descriptor.getWriteMethod(); if (write_method != null) { if (write_method.isAnnotationPresent(OutBeanProperty.class)) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), OutBeanProperty.class, "on setters ("+write_method.getName()+")", null); } if (write_method.isAnnotationPresent(OutCookieProperty.class)) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), OutCookieProperty.class, "on setters ("+write_method.getName()+")", null); } if (write_method.isAnnotationPresent(OutputProperty.class)) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), OutputProperty.class, "on setters ("+write_method.getName()+")", null); } if (write_method.isAnnotationPresent(InBeanProperty.class) || write_method.isAnnotationPresent(InCookieProperty.class) || write_method.isAnnotationPresent(InputProperty.class) || write_method.isAnnotationPresent(ParamProperty.class) || write_method.isAnnotationPresent(SubmissionBeanProperty.class) || write_method.isAnnotationPresent(FileProperty.class)) { methods_to_process.put(write_method, descriptor.getName()); } } Method read_method = descriptor.getReadMethod(); if (read_method != null) { if (read_method.isAnnotationPresent(InBeanProperty.class)) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), InBeanProperty.class, "on getters ("+read_method.getName()+")", null); } if (read_method.isAnnotationPresent(InCookieProperty.class)) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), InCookieProperty.class, "on getters ("+read_method.getName()+")", null); } if (read_method.isAnnotationPresent(InputProperty.class)) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), InputProperty.class, "on getters ("+read_method.getName()+")", null); } if (read_method.isAnnotationPresent(ParamProperty.class)) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), ParamProperty.class, "on getters ("+read_method.getName()+")", null); } if (read_method.isAnnotationPresent(SubmissionBeanProperty.class)) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), SubmissionBeanProperty.class, "on getters ("+read_method.getName()+")", null); } if (read_method.isAnnotationPresent(FileProperty.class)) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), FileProperty.class, "on getters ("+read_method.getName()+")", null); } if (read_method.isAnnotationPresent(OutBeanProperty.class) || read_method.isAnnotationPresent(OutCookieProperty.class) || read_method.isAnnotationPresent(OutputProperty.class)) { methods_to_process.put(read_method, descriptor.getName()); } } } } // for fields, we need a bean instance to retrieve their values. Object beanInstance = element_class.newInstance(); for (Field field : element_class.getDeclaredFields()) { if (field.isAnnotationPresent(ExitField.class)) { requireFinalString(field, builder, declarationName); builder.addExit((String)field.get(beanInstance)); } if (field.isAnnotationPresent(FlowlinkField.class)) { requireFinalString(field, builder, declarationName); FlowlinkField flowlink = field.getAnnotation(FlowlinkField.class); String destId = getElementId(builder, flowlink.destClass(), flowlink.destId()); FlowLinkBuilder flowlinkbuilder = builder.enterFlowLink((String)field.get(beanInstance)) .destId(destId) .snapback(flowlink.snapback()) .cancelInheritance(flowlink.inheritance().equals(Flowlink.Inheritance.CANCEL)) .cancelEmbedding(flowlink.embedding().equals(Flowlink.Embedding.CANCEL)) .redirect(flowlink.redirect()); for (Datalink datalink : flowlink.datalinks()) { flowlinkbuilder.addDataLink(datalink.srcOutput(), datalink.srcOutbean(), datalink.snapback(), datalink.destInput(), datalink.destInbean()); } flowlinkbuilder.leaveFlowLink(); } } for (Method method : element_class.getDeclaredMethods()) { // process all the setters and getters that have been detected to have RIFE annotations if (methods_to_process.containsKey(method)) { // handle the InBeanProperty annotation if (method.isAnnotationPresent(InBeanProperty.class)) { InBeanProperty bean = method.getAnnotation(InBeanProperty.class); builder.addInBean(method.getParameterTypes()[0], bean.prefix(), methods_to_process.get(method), bean.group()); } // handle the InCookieProperty annotation if (method.isAnnotationPresent(InCookieProperty.class)) { InCookieProperty cookie = method.getAnnotation(InCookieProperty.class); builder.addIncookie(methods_to_process.get(method), cookie.defaultValue()); } // handle the InputProperty annotation if (method.isAnnotationPresent(InputProperty.class)) { InputProperty input = method.getAnnotation(InputProperty.class); builder.addInput(methods_to_process.get(method), input.defaultValues()); } // handle the ParamProperty annotation if (method.isAnnotationPresent(ParamProperty.class)) { if (null == submissionbuilder) { throw new SubmissionElementAnnotationNeededException(declarationName, builder.getSiteBuilder().getDeclarationName(), ParamProperty.class, null); } ParamProperty param = method.getAnnotation(ParamProperty.class); submissionbuilder.addParameter(methods_to_process.get(method), param.defaultValues()); } // handle the SubmissionBeanProperty annotation if (method.isAnnotationPresent(SubmissionBeanProperty.class)) { if (null == submissionbuilder) { throw new SubmissionElementAnnotationNeededException(declarationName, builder.getSiteBuilder().getDeclarationName(), SubmissionBeanProperty.class, null); } SubmissionBeanProperty bean = method.getAnnotation(SubmissionBeanProperty.class); submissionbuilder.addBean(method.getParameterTypes()[0], bean.prefix(), methods_to_process.get(method), bean.group()); } // handle the FileProperty annotation if (method.isAnnotationPresent(FileProperty.class)) { if (!UploadedFile.class.isAssignableFrom(method.getParameterTypes()[0])) { throw new InvalidFilePropertyElementAnnotationException(declarationName, builder.getSiteBuilder().getDeclarationName(), method.getName(), null); } if (null == submissionbuilder) { throw new SubmissionElementAnnotationNeededException(declarationName, builder.getSiteBuilder().getDeclarationName(), FileProperty.class, null); } submissionbuilder.addFile(methods_to_process.get(method)); } // handle the OutBeanProperty annotation if (method.isAnnotationPresent(OutBeanProperty.class)) { OutBeanProperty bean = method.getAnnotation(OutBeanProperty.class); builder.addOutBean(method.getReturnType(), bean.prefix(), methods_to_process.get(method), bean.group()); } // handle the OutCookieProperty annotation if (method.isAnnotationPresent(OutCookieProperty.class)) { OutCookieProperty cookie = method.getAnnotation(OutCookieProperty.class); builder.addOutcookie(methods_to_process.get(method), cookie.defaultValue()); } // handle the OutputProperty annotation if (method.isAnnotationPresent(OutputProperty.class)) { OutputProperty output = method.getAnnotation(OutputProperty.class); builder.addOutput(methods_to_process.get(method), output.defaultValues()); } } // ensure that property annotations are only used on setters and getters else if (method.isAnnotationPresent(InBeanProperty.class) || method.isAnnotationPresent(InCookieProperty.class) || method.isAnnotationPresent(InputProperty.class) || method.isAnnotationPresent(ParamProperty.class) || method.isAnnotationPresent(SubmissionBeanProperty.class) || method.isAnnotationPresent(FileProperty.class) || method.isAnnotationPresent(OutBeanProperty.class) || method.isAnnotationPresent(OutCookieProperty.class) || method.isAnnotationPresent(OutputProperty.class)) { throw new InvalidUseOfElementPropertyAnnotationException(declarationName, builder.getSiteBuilder().getDeclarationName(), method.getName(), null); } // handle the SubmissionHandler annotation else if (method.isAnnotationPresent(SubmissionHandler.class)) { // ensure that the handler method conforms to the convention if (!method.getName().startsWith("do") || method.getName().length() == 2 || method.getReturnType() != void.class || method.getParameterTypes().length > 0) { throw new InvalidUseOfElementSubmissionHandlerAnnotationException(declarationName, builder.getSiteBuilder().getDeclarationName(), method.getName(), null); } // add the suitable submission else { if (submissionbuilder != null) submissionbuilder.leaveSubmission(); submissionbuilder = builder.enterSubmission(StringUtils.uncapitalize(method.getName().substring(2))); SubmissionHandler submission = method.getAnnotation(SubmissionHandler.class); submissionbuilder.setScope(Scope.getScope(submission.scope().toString())); for (Param param : submission.params()) submissionbuilder.addParameter(param.name(), param.defaultValues()); for (ParamRegexp param : submission.paramRegexps()) submissionbuilder.addParameterRegexp(param.value()); for (SubmissionBean bean : submission.beans()) submissionbuilder.addBean(bean.beanclass(), bean.prefix(), bean.name(), bean.group()); for (File file : submission.files()) submissionbuilder.addFile(file.name()); for (FileRegexp file : submission.fileRegexps()) submissionbuilder.addFileRegexp(file.value()); } } } if (submissionbuilder != null) submissionbuilder.leaveSubmission(); } } catch (BeanUtilsException e) { throw new ElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), "Unexpected error while introspecting the class.", e); } catch (IllegalAccessException e) { throw new ElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), "Unexpected error while introspecting the class.", e); } catch (InstantiationException e) { throw new ElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), "Unexpected error while introspecting the class.", e); } } private void requireFinalString(Field field, ElementInfoBuilder builder, String declarationName) throws UnsupportedElementAnnotationErrorException { if (field.getType() != String.class) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), ExitField.class, "on non-String field ("+field.getName()+")", null); } if ((field.getModifiers() & Modifier.FINAL) == 0) { throw new UnsupportedElementAnnotationErrorException(declarationName, builder.getSiteBuilder().getDeclarationName(), ExitField.class, "on non-final field ("+field.getName()+")", null); } } private String getElementId(ElementInfoBuilder builder, Class elementClass, String elementId) { if (elementClass != void.class) { if (!elementClass.isAnnotationPresent(Elem.class)) { throw new ElementAnnotationMissingException(elementClass.getName(), builder.getSiteBuilder().getDeclarationName(), Elem.class, null); } Elem destclass_elem = (Elem)elementClass.getAnnotation(Elem.class); if (destclass_elem.id().equals("")) { elementId = ClassUtils.simpleClassName(elementClass); } else { elementId = destclass_elem.id(); } } return elementId; } }