001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: Annotations2ElementInfo.java 3720 2007-04-27 10:58:07Z gbevin $
007: */
008: package com.uwyn.rife.engine;
009:
010: import com.uwyn.rife.engine.annotations.*;
011: import com.uwyn.rife.engine.exceptions.*;
012:
013: import com.uwyn.rife.config.RifeConfig;
014: import com.uwyn.rife.engine.annotations.Submission;
015: import com.uwyn.rife.resources.ResourceFinder;
016: import com.uwyn.rife.resources.ResourceFinderClasspath;
017: import com.uwyn.rife.tools.BeanUtils;
018: import com.uwyn.rife.tools.ClassUtils;
019: import com.uwyn.rife.tools.JavaSpecificationUtils;
020: import com.uwyn.rife.tools.StringUtils;
021: import com.uwyn.rife.tools.exceptions.BeanUtilsException;
022: import java.beans.BeanInfo;
023: import java.beans.PropertyDescriptor;
024: import java.lang.reflect.Field;
025: import java.lang.reflect.Method;
026: import java.lang.reflect.Modifier;
027: import java.net.URL;
028: import java.util.HashMap;
029: import java.util.Map;
030: import java.util.SortedSet;
031: import java.util.Stack;
032: import java.util.TreeSet;
033:
034: class Annotations2ElementInfo implements ElementInfoProcessor {
035: public void processElementInfo(ElementInfoBuilder builder,
036: String declarationName, ResourceFinder resourceFinder)
037: throws EngineException {
038: if (!JavaSpecificationUtils.isAtLeastJdk15()) {
039: throw new Jdk15RequiredForAnnotationsException(builder
040: .getSiteBuilder().getDeclarationName(),
041: declarationName);
042: }
043: try {
044: Class klass = Class
045: .forName("com.uwyn.rife.engine.Annotations2ElementInfoProcessor");
046: ElementInfoProcessor processor = (ElementInfoProcessor) klass
047: .newInstance();
048: processor.processElementInfo(builder, declarationName,
049: resourceFinder);
050: } catch (Exception e) {
051: throw new EngineException(e);
052: }
053: }
054: }
055:
056: class Annotations2ElementInfoProcessor implements ElementInfoProcessor {
057: private URL getSourceResource(String sourceLocation,
058: boolean getElement) {
059: URL source_resource = getClass().getClassLoader().getResource(
060: sourceLocation);
061: if (null == source_resource && getElement) {
062: source_resource = getClass().getClassLoader().getResource(
063: EngineClassLoader.DEFAULT_IMPLEMENTATIONS_PATH
064: + sourceLocation);
065: }
066: return source_resource;
067: }
068:
069: public void processElementInfo(ElementInfoBuilder builder,
070: String declarationName, ResourceFinder resourceFinder)
071: throws EngineException {
072: resourceFinder = ResourceFinderClasspath.getInstance();
073: try {
074: Class element_class = ElementFactory.INSTANCE.getJavaClass(
075: declarationName, declarationName);
076: if (RifeConfig.Engine.getSiteAutoReload()) {
077: URL source_resource = getSourceResource(
078: EngineClassLoader
079: .constructSourcePath(declarationName),
080: true);
081: if (source_resource != null) {
082: builder
083: .addResourceModificationTime(
084: new UrlResource(source_resource,
085: declarationName),
086: EngineClassLoader
087: .getSourceModificationTime(source_resource));
088: }
089: }
090:
091: Stack<Class> parent_stack = new Stack<Class>();
092: while (ElementAware.class.isAssignableFrom(element_class)
093: && !(Element.class == element_class)) {
094: parent_stack.push(element_class);
095: element_class = element_class.getSuperclass();
096: }
097:
098: element_class = parent_stack.pop();
099: while (element_class != null) {
100: SubmissionBuilder submissionbuilder = null;
101: if (element_class.isAnnotationPresent(Elem.class)) {
102: // handle class annotations
103: Elem elem = (Elem) element_class
104: .getAnnotation(Elem.class);
105:
106: // only set the ID and the URL for the top level element class
107: if (0 == parent_stack.size()) {
108: if (!builder.getElementDeclaration()
109: .hasDeclaredId()) {
110: if (elem.id().equals("")) {
111: builder
112: .setId(ClassUtils
113: .simpleClassName(element_class));
114: } else {
115: builder.setId(elem.id());
116: }
117: }
118:
119: if (!builder.getElementDeclaration()
120: .hasDeclaredUrl()) {
121: if (elem.url().equals(Elem.DEFAULT_URL)) {
122: builder
123: .setUrl(ClassUtils
124: .shortenClassName(element_class));
125: } else if (elem.url().equals("")) {
126: // set no URL
127: } else {
128: builder.setUrl(elem.url());
129: }
130: }
131: }
132:
133: String inherits = getElementId(builder, elem
134: .inheritsClass(), elem
135: .inheritsClassIdPrefix(), elem.inheritsId());
136: String pre = getElementId(builder, elem.preClass(),
137: elem.preClassIdPrefix(), elem.preId());
138: if (!inherits.equals(""))
139: builder.setInherits(inherits);
140: if (!pre.equals(""))
141: builder.setInherits(pre);
142:
143: if (!elem.contentType().equals(
144: Elem.DEFAULT_CONTENT_TYPE))
145: builder.setContentType(elem.contentType());
146:
147: for (Input input : elem.inputs())
148: builder.addInput(input.name(), input
149: .defaultValues());
150: for (InBean bean : elem.inbeans())
151: builder.addInBean(bean.beanclass(), (0 == bean
152: .prefix().length() ? null : bean
153: .prefix()),
154: (0 == bean.name().length() ? null
155: : bean.name()), (0 == bean
156: .group().length() ? null : bean
157: .group()));
158: for (InCookie cookie : elem.incookies())
159: builder.addIncookie(cookie.name(), cookie
160: .defaultValue());
161:
162: for (Output output : elem.outputs())
163: builder.addOutput(output.name(), output
164: .defaultValues());
165: for (OutBean bean : elem.outbeans())
166: builder.addOutBean(bean.beanclass(), (0 == bean
167: .prefix().length() ? null : bean
168: .prefix()),
169: (0 == bean.name().length() ? null
170: : bean.name()), (0 == bean
171: .group().length() ? null : bean
172: .group()));
173: for (OutCookie cookie : elem.outcookies())
174: builder.addOutcookie(cookie.name(), cookie
175: .defaultValue());
176:
177: for (Submission submission : elem.submissions()) {
178: if (submissionbuilder != null)
179: submissionbuilder.leaveSubmission();
180: submissionbuilder = builder
181: .enterSubmission(submission.name());
182: submissionbuilder
183: .cancelContinuations(Submission.Continuations.CANCEL
184: .equals(submission
185: .continuations()));
186: submissionbuilder
187: .setScope(Scope.getScope(submission
188: .scope().toString()));
189:
190: for (Param param : submission.params())
191: submissionbuilder
192: .addParameter(param.name(), param
193: .defaultValues());
194: for (ParamRegexp param : submission
195: .paramRegexps())
196: submissionbuilder.addParameterRegexp(param
197: .value());
198: for (SubmissionBean bean : submission.beans())
199: submissionbuilder.addBean(bean.beanclass(),
200: (0 == bean.prefix().length() ? null
201: : bean.prefix()),
202: (0 == bean.name().length() ? null
203: : bean.name()), (0 == bean
204: .group().length() ? null
205: : bean.group()));
206: for (File file : submission.files())
207: submissionbuilder.addFile(file.name());
208: for (FileRegexp file : submission.fileRegexps())
209: submissionbuilder.addFileRegexp(file
210: .value());
211:
212: }
213:
214: for (Exit exit : elem.exits())
215: builder.addExit(exit.name());
216: for (ChildTrigger childtrigger : elem
217: .childTriggers())
218: builder.addChildTrigger(childtrigger.name());
219:
220: Pathinfo pathinfo = elem.pathinfo();
221: builder.setPathInfoMode(PathInfoMode
222: .getMode(pathinfo.policy().toString()));
223: for (Mapping mapping : pathinfo.mappings())
224: builder.addPathInfoMapping(mapping.value());
225:
226: for (Autolink autolink : elem.autolinks()) {
227: String dest_id = getElementId(builder, autolink
228: .destClass(), autolink
229: .destClassIdPrefix(), autolink.destId());
230: String src_exit = autolink.srcExit();
231: if (0 == src_exit.length()) {
232: src_exit = dest_id;
233: if (autolink.destClassIdPrefix() != null
234: && autolink.destClassIdPrefix()
235: .length() > 0) {
236: src_exit = src_exit
237: .substring(autolink
238: .destClassIdPrefix()
239: .length() + 1);
240: }
241: }
242: builder.addAutoLink(src_exit, dest_id, autolink
243: .inheritance().equals(
244: Flowlink.Inheritance.CANCEL),
245: autolink.embedding().equals(
246: Flowlink.Embedding.CANCEL),
247: autolink.redirect(),
248: autolink.continuations().equals(
249: Flowlink.Continuations.CANCEL));
250: }
251:
252: for (Flowlink flowlink : elem.flowlinks()) {
253: String dest_id = getElementId(builder, flowlink
254: .destClass(), flowlink
255: .destClassIdPrefix(), flowlink.destId());
256: FlowLinkBuilder flowlinkbuilder = builder
257: .enterFlowLink(flowlink.srcExit())
258: .destId(dest_id)
259: .snapback(flowlink.snapback())
260: .cancelInheritance(
261: flowlink
262: .inheritance()
263: .equals(
264: Flowlink.Inheritance.CANCEL))
265: .cancelEmbedding(
266: flowlink
267: .embedding()
268: .equals(
269: Flowlink.Embedding.CANCEL))
270: .redirect(flowlink.redirect());
271: for (Datalink datalink : flowlink.datalinks()) {
272: flowlinkbuilder.addDataLink(datalink
273: .srcOutput(),
274: datalink.srcOutbean(), datalink
275: .snapback(), datalink
276: .destInput(), datalink
277: .destInbean());
278: }
279: flowlinkbuilder.leaveFlowLink();
280: }
281:
282: for (Datalink datalink : elem.datalinks()) {
283: String dest_id = getElementId(builder, datalink
284: .destClass(), datalink
285: .destClassIdPrefix(), datalink.destId());
286: builder.addDataLink(datalink.srcOutput(),
287: datalink.srcOutbean(), dest_id,
288: datalink.snapback(), datalink
289: .destInput(), datalink
290: .destInbean());
291: }
292: }
293:
294: // process all bean property accessors, validate the annotations used on them,
295: // and keep them in a list of methods that need to be processed
296: Map<Method, String> methods_to_process = new HashMap<Method, String>();
297: // obtain the BeanInfo class
298: BeanInfo bean_info = BeanUtils
299: .getBeanInfo(element_class);
300:
301: // process the properties of the bean
302: PropertyDescriptor[] bean_properties = bean_info
303: .getPropertyDescriptors();
304: if (bean_properties.length > 0) {
305: // iterate over the properties of the bean
306: for (PropertyDescriptor descriptor : bean_properties) {
307: {
308: Method write_method = descriptor
309: .getWriteMethod();
310: if (write_method != null) {
311: // ensure that read annotations aren't used with write methods
312: if (write_method
313: .isAnnotationPresent(OutBeanProperty.class)) {
314: throw new UnsupportedElementAnnotationErrorException(
315: declarationName,
316: builder
317: .getSiteBuilder()
318: .getDeclarationName(),
319: OutBeanProperty.class,
320: "on setters ("
321: + write_method
322: .getName()
323: + ")", null);
324: }
325: if (write_method
326: .isAnnotationPresent(OutCookieProperty.class)) {
327: throw new UnsupportedElementAnnotationErrorException(
328: declarationName,
329: builder
330: .getSiteBuilder()
331: .getDeclarationName(),
332: OutCookieProperty.class,
333: "on setters ("
334: + write_method
335: .getName()
336: + ")", null);
337: }
338: if (write_method
339: .isAnnotationPresent(OutputProperty.class)) {
340: throw new UnsupportedElementAnnotationErrorException(
341: declarationName,
342: builder
343: .getSiteBuilder()
344: .getDeclarationName(),
345: OutputProperty.class,
346: "on setters ("
347: + write_method
348: .getName()
349: + ")", null);
350: }
351:
352: // ensure that the property name is correct
353: if (write_method
354: .isAnnotationPresent(InBeanProperty.class)) {
355: ensureCorrespondingPropertyName(
356: declarationName,
357: builder,
358: descriptor,
359: write_method,
360: InBeanProperty.class,
361: write_method
362: .getAnnotation(
363: InBeanProperty.class)
364: .name());
365: }
366: if (write_method
367: .isAnnotationPresent(InCookieProperty.class)) {
368: ensureCorrespondingPropertyName(
369: declarationName,
370: builder,
371: descriptor,
372: write_method,
373: InCookieProperty.class,
374: write_method
375: .getAnnotation(
376: InCookieProperty.class)
377: .name());
378: }
379: if (write_method
380: .isAnnotationPresent(InputProperty.class)) {
381: ensureCorrespondingPropertyName(
382: declarationName,
383: builder,
384: descriptor,
385: write_method,
386: InputProperty.class,
387: write_method
388: .getAnnotation(
389: InputProperty.class)
390: .name());
391: }
392: if (write_method
393: .isAnnotationPresent(ParamProperty.class)) {
394: ensureCorrespondingPropertyName(
395: declarationName,
396: builder,
397: descriptor,
398: write_method,
399: ParamProperty.class,
400: write_method
401: .getAnnotation(
402: ParamProperty.class)
403: .name());
404: }
405: if (write_method
406: .isAnnotationPresent(SubmissionBeanProperty.class)) {
407: ensureCorrespondingPropertyName(
408: declarationName,
409: builder,
410: descriptor,
411: write_method,
412: SubmissionBeanProperty.class,
413: write_method
414: .getAnnotation(
415: SubmissionBeanProperty.class)
416: .name());
417: }
418: if (write_method
419: .isAnnotationPresent(FileProperty.class)) {
420: ensureCorrespondingPropertyName(
421: declarationName, builder,
422: descriptor, write_method,
423: FileProperty.class,
424: write_method.getAnnotation(
425: FileProperty.class)
426: .name());
427: }
428:
429: // use the annotations
430: if (write_method
431: .isAnnotationPresent(InBeanProperty.class)
432: || write_method
433: .isAnnotationPresent(InCookieProperty.class)
434: || write_method
435: .isAnnotationPresent(InputProperty.class)
436: || write_method
437: .isAnnotationPresent(ParamProperty.class)
438: || write_method
439: .isAnnotationPresent(SubmissionBeanProperty.class)
440: || write_method
441: .isAnnotationPresent(FileProperty.class)) {
442: methods_to_process.put(
443: write_method, descriptor
444: .getName());
445: }
446: }
447: }
448:
449: {
450: Method read_method = descriptor
451: .getReadMethod();
452: if (read_method != null) {
453: // ensure that the write annotations aren't used with read methods
454: if (read_method
455: .isAnnotationPresent(InBeanProperty.class)) {
456: throw new UnsupportedElementAnnotationErrorException(
457: declarationName,
458: builder
459: .getSiteBuilder()
460: .getDeclarationName(),
461: InBeanProperty.class,
462: "on getters ("
463: + read_method
464: .getName()
465: + ")", null);
466: }
467: if (read_method
468: .isAnnotationPresent(InCookieProperty.class)) {
469: throw new UnsupportedElementAnnotationErrorException(
470: declarationName,
471: builder
472: .getSiteBuilder()
473: .getDeclarationName(),
474: InCookieProperty.class,
475: "on getters ("
476: + read_method
477: .getName()
478: + ")", null);
479: }
480: if (read_method
481: .isAnnotationPresent(InputProperty.class)) {
482: throw new UnsupportedElementAnnotationErrorException(
483: declarationName,
484: builder
485: .getSiteBuilder()
486: .getDeclarationName(),
487: InputProperty.class,
488: "on getters ("
489: + read_method
490: .getName()
491: + ")", null);
492: }
493: if (read_method
494: .isAnnotationPresent(ParamProperty.class)) {
495: throw new UnsupportedElementAnnotationErrorException(
496: declarationName,
497: builder
498: .getSiteBuilder()
499: .getDeclarationName(),
500: ParamProperty.class,
501: "on getters ("
502: + read_method
503: .getName()
504: + ")", null);
505: }
506: if (read_method
507: .isAnnotationPresent(SubmissionBeanProperty.class)) {
508: throw new UnsupportedElementAnnotationErrorException(
509: declarationName,
510: builder
511: .getSiteBuilder()
512: .getDeclarationName(),
513: SubmissionBeanProperty.class,
514: "on getters ("
515: + read_method
516: .getName()
517: + ")", null);
518: }
519: if (read_method
520: .isAnnotationPresent(FileProperty.class)) {
521: throw new UnsupportedElementAnnotationErrorException(
522: declarationName,
523: builder
524: .getSiteBuilder()
525: .getDeclarationName(),
526: FileProperty.class,
527: "on getters ("
528: + read_method
529: .getName()
530: + ")", null);
531: }
532:
533: // ensure that the property name is correct
534: if (read_method
535: .isAnnotationPresent(OutBeanProperty.class)) {
536: ensureCorrespondingPropertyName(
537: declarationName,
538: builder,
539: descriptor,
540: read_method,
541: OutBeanProperty.class,
542: read_method
543: .getAnnotation(
544: OutBeanProperty.class)
545: .name());
546: }
547: if (read_method
548: .isAnnotationPresent(OutBeanProperty.class)) {
549: ensureCorrespondingPropertyName(
550: declarationName,
551: builder,
552: descriptor,
553: read_method,
554: OutBeanProperty.class,
555: read_method
556: .getAnnotation(
557: OutBeanProperty.class)
558: .name());
559: }
560: if (read_method
561: .isAnnotationPresent(OutputProperty.class)) {
562: ensureCorrespondingPropertyName(
563: declarationName,
564: builder,
565: descriptor,
566: read_method,
567: OutputProperty.class,
568: read_method
569: .getAnnotation(
570: OutputProperty.class)
571: .name());
572: }
573:
574: // use the annotations
575: if (read_method
576: .isAnnotationPresent(OutBeanProperty.class)
577: || read_method
578: .isAnnotationPresent(OutCookieProperty.class)
579: || read_method
580: .isAnnotationPresent(OutputProperty.class)) {
581: methods_to_process.put(read_method,
582: descriptor.getName());
583: }
584: }
585: }
586: }
587: }
588:
589: // handle field annotation types
590: try {
591: for (Field field : element_class
592: .getDeclaredFields()) {
593: if (field.isAnnotationPresent(ExitField.class)) {
594: requireFinalString(ExitField.class, field,
595: builder, declarationName);
596: builder.addExit((String) field.get(null));
597: }
598:
599: if (field
600: .isAnnotationPresent(FlowlinkExitField.class)) {
601: requireFinalString(FlowlinkExitField.class,
602: field, builder, declarationName);
603: FlowlinkExitField flowlink = field
604: .getAnnotation(FlowlinkExitField.class);
605: String dest_id = getElementId(builder,
606: flowlink.destClass(), flowlink
607: .destClassIdPrefix(),
608: flowlink.destId());
609: FlowLinkBuilder flowlinkbuilder = builder
610: .enterFlowLink(
611: (String) field.get(null))
612: .destId(dest_id)
613: .snapback(flowlink.snapback())
614: .cancelInheritance(
615: flowlink
616: .inheritance()
617: .equals(
618: Flowlink.Inheritance.CANCEL))
619: .cancelEmbedding(
620: flowlink
621: .embedding()
622: .equals(
623: Flowlink.Embedding.CANCEL))
624: .redirect(flowlink.redirect());
625: for (Datalink datalink : flowlink
626: .datalinks()) {
627: flowlinkbuilder.addDataLink(datalink
628: .srcOutput(), datalink
629: .srcOutbean(), datalink
630: .snapback(), datalink
631: .destInput(), datalink
632: .destInbean());
633: }
634: flowlinkbuilder.leaveFlowLink();
635: }
636: }
637: } catch (IllegalAccessException e) {
638: throw new ElementAnnotationErrorException(
639: declarationName,
640: builder.getSiteBuilder()
641: .getDeclarationName(),
642: "Unexpected error while introspecting the class for field annotation types.",
643: e);
644: }
645:
646: // Create a set that is sorted according to the method priorities
647: SortedSet<PrioritizedMethod> method_set = new TreeSet<PrioritizedMethod>();
648: for (Method method : element_class.getDeclaredMethods()) {
649: int[] priority = null;
650: if (method.isAnnotationPresent(Priority.class)) {
651: priority = method.getAnnotation(Priority.class)
652: .value();
653: }
654: method_set.add(new PrioritizedMethod(method,
655: priority));
656: }
657:
658: // Process the methods according to their priorities
659: for (PrioritizedMethod prioritized_method : method_set) {
660: Method method = prioritized_method.getMethod();
661:
662: // process all the setters and getters that have been detected to have RIFE annotations
663: if (methods_to_process.containsKey(method)) {
664: // handle the InBeanProperty annotation
665: if (method
666: .isAnnotationPresent(InBeanProperty.class)) {
667: InBeanProperty bean = method
668: .getAnnotation(InBeanProperty.class);
669: builder.addInBean(method
670: .getParameterTypes()[0], (0 == bean
671: .prefix().length() ? null : bean
672: .prefix()), methods_to_process
673: .get(method), (0 == bean.group()
674: .length() ? null : bean.group()));
675: }
676:
677: // handle the InCookieProperty annotation
678: if (method
679: .isAnnotationPresent(InCookieProperty.class)) {
680: InCookieProperty cookie = method
681: .getAnnotation(InCookieProperty.class);
682: builder
683: .addIncookie(methods_to_process
684: .get(method), cookie
685: .defaultValue());
686: }
687:
688: // handle the InputProperty annotation
689: if (method
690: .isAnnotationPresent(InputProperty.class)) {
691: InputProperty input = method
692: .getAnnotation(InputProperty.class);
693: builder
694: .addInput(methods_to_process
695: .get(method), input
696: .defaultValues());
697: }
698:
699: // handle the ParamProperty annotation
700: if (method
701: .isAnnotationPresent(ParamProperty.class)) {
702: if (null == submissionbuilder) {
703: throw new SubmissionElementAnnotationNeededException(
704: declarationName, builder
705: .getSiteBuilder()
706: .getDeclarationName(),
707: ParamProperty.class, null);
708: }
709: ParamProperty param = method
710: .getAnnotation(ParamProperty.class);
711: submissionbuilder.addParameter(
712: methods_to_process.get(method),
713: param.defaultValues());
714: }
715:
716: // handle the SubmissionBeanProperty annotation
717: if (method
718: .isAnnotationPresent(SubmissionBeanProperty.class)) {
719: if (null == submissionbuilder) {
720: throw new SubmissionElementAnnotationNeededException(
721: declarationName, builder
722: .getSiteBuilder()
723: .getDeclarationName(),
724: SubmissionBeanProperty.class,
725: null);
726: }
727: SubmissionBeanProperty bean = method
728: .getAnnotation(SubmissionBeanProperty.class);
729: submissionbuilder.addBean(method
730: .getParameterTypes()[0], (0 == bean
731: .prefix().length() ? null : bean
732: .prefix()), methods_to_process
733: .get(method), (0 == bean.group()
734: .length() ? null : bean.group()));
735: }
736:
737: // handle the FileProperty annotation
738: if (method
739: .isAnnotationPresent(FileProperty.class)) {
740: if (!UploadedFile.class
741: .isAssignableFrom(method
742: .getParameterTypes()[0])) {
743: throw new InvalidFilePropertyElementAnnotationException(
744: declarationName, builder
745: .getSiteBuilder()
746: .getDeclarationName(),
747: method.getName(), null);
748: }
749: if (null == submissionbuilder) {
750: throw new SubmissionElementAnnotationNeededException(
751: declarationName, builder
752: .getSiteBuilder()
753: .getDeclarationName(),
754: FileProperty.class, null);
755: }
756: submissionbuilder
757: .addFile(methods_to_process
758: .get(method));
759: }
760:
761: // handle the OutBeanProperty annotation
762: if (method
763: .isAnnotationPresent(OutBeanProperty.class)) {
764: OutBeanProperty bean = method
765: .getAnnotation(OutBeanProperty.class);
766: builder.addOutBean(method.getReturnType(),
767: (0 == bean.prefix().length() ? null
768: : bean.prefix()),
769: methods_to_process.get(method),
770: (0 == bean.group().length() ? null
771: : bean.group()));
772: }
773:
774: // handle the OutCookieProperty annotation
775: if (method
776: .isAnnotationPresent(OutCookieProperty.class)) {
777: OutCookieProperty cookie = method
778: .getAnnotation(OutCookieProperty.class);
779: builder
780: .addOutcookie(methods_to_process
781: .get(method), cookie
782: .defaultValue());
783: }
784:
785: // handle the OutputProperty annotation
786: if (method
787: .isAnnotationPresent(OutputProperty.class)) {
788: OutputProperty output = method
789: .getAnnotation(OutputProperty.class);
790: builder.addOutput(methods_to_process
791: .get(method), output
792: .defaultValues());
793: }
794: }
795: // ensure that property annotations are only used on setters and getters
796: else if (method
797: .isAnnotationPresent(InBeanProperty.class)
798: || method
799: .isAnnotationPresent(InCookieProperty.class)
800: || method
801: .isAnnotationPresent(InputProperty.class)
802: || method
803: .isAnnotationPresent(ParamProperty.class)
804: || method
805: .isAnnotationPresent(SubmissionBeanProperty.class)
806: || method
807: .isAnnotationPresent(FileProperty.class)
808: || method
809: .isAnnotationPresent(OutBeanProperty.class)
810: || method
811: .isAnnotationPresent(OutCookieProperty.class)
812: || method
813: .isAnnotationPresent(OutputProperty.class)) {
814: throw new InvalidUseOfElementPropertyAnnotationException(
815: declarationName, builder
816: .getSiteBuilder()
817: .getDeclarationName(), method
818: .getName(), null);
819: }
820: // handle the SubmissionHandler annotation
821: else if (method
822: .isAnnotationPresent(SubmissionHandler.class)) {
823: // ensure that the handler method conforms to the convention
824: if (!method.getName().startsWith("do")
825: || method.getName().length() == 2
826: || method.getReturnType() != void.class
827: || method.getParameterTypes().length > 0) {
828: throw new InvalidUseOfElementSubmissionHandlerAnnotationException(
829: declarationName, builder
830: .getSiteBuilder()
831: .getDeclarationName(),
832: method.getName(), null);
833: }
834: // add the suitable submission
835: else {
836: if (submissionbuilder != null)
837: submissionbuilder.leaveSubmission();
838: submissionbuilder = builder
839: .enterSubmission(StringUtils
840: .uncapitalize(method
841: .getName()
842: .substring(2)));
843:
844: SubmissionHandler submission = method
845: .getAnnotation(SubmissionHandler.class);
846: submissionbuilder
847: .cancelContinuations(SubmissionHandler.Continuations.CANCEL == submission
848: .continuations());
849: submissionbuilder.setScope(Scope
850: .getScope(submission.scope()
851: .toString()));
852:
853: for (Param param : submission.params())
854: submissionbuilder.addParameter(param
855: .name(), param.defaultValues());
856: for (ParamRegexp param : submission
857: .paramRegexps())
858: submissionbuilder
859: .addParameterRegexp(param
860: .value());
861: for (SubmissionBean bean : submission
862: .beans())
863: submissionbuilder
864: .addBean(
865: bean.beanclass(),
866: (0 == bean.prefix()
867: .length() ? null
868: : bean.prefix()),
869: (0 == bean.name()
870: .length() ? null
871: : bean.name()),
872: (0 == bean.group()
873: .length() ? null
874: : bean.group()));
875: for (File file : submission.files())
876: submissionbuilder.addFile(file.name());
877: for (FileRegexp file : submission
878: .fileRegexps())
879: submissionbuilder.addFileRegexp(file
880: .value());
881: }
882: }
883: }
884: if (submissionbuilder != null)
885: submissionbuilder.leaveSubmission();
886:
887: if (0 == parent_stack.size()) {
888: element_class = null;
889: break;
890: }
891:
892: element_class = parent_stack.pop();
893: }
894: } catch (BeanUtilsException e) {
895: throw new ElementAnnotationErrorException(declarationName,
896: builder.getSiteBuilder().getDeclarationName(),
897: "Unexpected error while introspecting the class.",
898: e);
899: }
900: }
901:
902: private void ensureCorrespondingPropertyName(
903: String declarationName, ElementInfoBuilder builder,
904: PropertyDescriptor descriptor, Method write_method,
905: Class property_annotation_class,
906: String expected_property_name) {
907: if (property_annotation_class != null
908: && expected_property_name != null
909: && expected_property_name.length() > 0
910: && !descriptor.getName().equals(expected_property_name)) {
911: throw new PropertyNameMismatchErrorException(
912: declarationName, builder.getSiteBuilder()
913: .getDeclarationName(), write_method,
914: property_annotation_class, expected_property_name,
915: descriptor.getName());
916: }
917: }
918:
919: private void requireFinalString(Class annotationType, Field field,
920: ElementInfoBuilder builder, String declarationName)
921: throws UnsupportedElementAnnotationErrorException {
922: if (field.getType() != String.class) {
923: throw new UnsupportedElementAnnotationErrorException(
924: declarationName, builder.getSiteBuilder()
925: .getDeclarationName(), annotationType,
926: "on non-String field (" + field.getName() + ")",
927: null);
928: }
929: if (!Modifier.isFinal(field.getModifiers())) {
930: throw new UnsupportedElementAnnotationErrorException(
931: declarationName, builder.getSiteBuilder()
932: .getDeclarationName(), annotationType,
933: "on non-final field (" + field.getName() + ")",
934: null);
935: }
936: if (!Modifier.isStatic(field.getModifiers())) {
937: throw new UnsupportedElementAnnotationErrorException(
938: declarationName, builder.getSiteBuilder()
939: .getDeclarationName(), annotationType,
940: "on non-static field (" + field.getName() + ")",
941: null);
942: }
943: }
944:
945: private String getElementId(ElementInfoBuilder builder,
946: Class elementClass, String elementClassIdPrefix,
947: String elementId) {
948: if (elementClass != void.class) {
949: if (!elementClass.isAnnotationPresent(Elem.class)) {
950: throw new ElementAnnotationMissingException(
951: elementClass.getName(), builder
952: .getSiteBuilder().getDeclarationName(),
953: Elem.class, null);
954: }
955:
956: Elem destclass_elem = (Elem) elementClass
957: .getAnnotation(Elem.class);
958: if (destclass_elem.id().equals("")) {
959: elementId = ClassUtils.simpleClassName(elementClass);
960: } else {
961: elementId = destclass_elem.id();
962: }
963:
964: if (elementClassIdPrefix != null
965: && elementClassIdPrefix.length() > 0) {
966: elementId = elementClassIdPrefix + "." + elementId;
967: }
968: }
969:
970: return elementId;
971: }
972: }
|