1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.orchestra.dynaForm.metadata.impl.ejb;
20
21 import java.lang.reflect.AccessibleObject;
22 import java.lang.reflect.Field;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Modifier;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.EnumSet;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.Stack;
31 import java.util.TreeSet;
32
33 import javax.persistence.Basic;
34 import javax.persistence.Column;
35 import javax.persistence.Embeddable;
36 import javax.persistence.Embedded;
37 import javax.persistence.EmbeddedId;
38 import javax.persistence.Entity;
39 import javax.persistence.GeneratedValue;
40 import javax.persistence.Id;
41 import javax.persistence.ManyToMany;
42 import javax.persistence.ManyToOne;
43 import javax.persistence.MappedSuperclass;
44 import javax.persistence.OneToMany;
45 import javax.persistence.OneToOne;
46 import javax.persistence.Temporal;
47 import javax.persistence.Transient;
48 import javax.persistence.Version;
49
50 import org.apache.commons.lang.StringUtils;
51 import org.apache.myfaces.orchestra.dynaForm.annot.ui.ComponentHandler;
52 import org.apache.myfaces.orchestra.dynaForm.annot.ui.DataProvider;
53 import org.apache.myfaces.orchestra.dynaForm.annot.ui.DisplayOnly;
54 import org.apache.myfaces.orchestra.dynaForm.annot.ui.DisplaySize;
55 import org.apache.myfaces.orchestra.dynaForm.annot.ui.IgnoreProperty;
56 import org.apache.myfaces.orchestra.dynaForm.annot.ui.Length;
57 import org.apache.myfaces.orchestra.dynaForm.annot.ui.Max;
58 import org.apache.myfaces.orchestra.dynaForm.annot.ui.MetaAttribute;
59 import org.apache.myfaces.orchestra.dynaForm.annot.ui.Min;
60 import org.apache.myfaces.orchestra.dynaForm.annot.ui.NotNull;
61 import org.apache.myfaces.orchestra.dynaForm.annot.ui.Range;
62 import org.apache.myfaces.orchestra.dynaForm.annot.ui.ReadOnly;
63 import org.apache.myfaces.orchestra.dynaForm.annot.ui.UIComponent;
64 import org.apache.myfaces.orchestra.dynaForm.metadata.Extractor;
65 import org.apache.myfaces.orchestra.dynaForm.metadata.MetaDataWritable;
66 import org.apache.myfaces.orchestra.dynaForm.metadata.MetaFieldWritable;
67 import org.apache.myfaces.orchestra.dynaForm.metadata.RelationType;
68 import org.apache.myfaces.orchestra.dynaForm.metadata.Selection;
69
70
71
72
73
74
75
76
77
78 public class EjbExtractor implements Extractor
79 {
80 private final static Set<String> SYSTEM_METHODS = new TreeSet<String>(
81 Arrays.asList(new String[]
82 {"hashCode", "getClass", "wait", "equals", "notify", "notifyAll",
83 "toString"}));
84
85
86 protected static class ContextInfo
87 {
88 private final Boolean accessField;
89 private final String name;
90 private final boolean embedded;
91 private final boolean id;
92
93 protected ContextInfo(final String name, final Boolean accessField, final boolean embedded, final boolean id)
94 {
95 super();
96 this.name = name;
97 this.accessField = accessField;
98 this.embedded = embedded;
99 this.id = id;
100 }
101 }
102
103
104
105 protected static class Context
106 {
107 private boolean accessField = false;
108 private Stack<ContextInfo> accessFields = new Stack<ContextInfo>();
109 private int embeddLevel = 0;
110
111
112
113 public void setAccessField(boolean accessField)
114 {
115 this.accessField = accessField;
116 }
117
118 public Boolean popAccessType()
119 {
120 return accessFields.pop().accessField;
121 }
122
123
124 public boolean getAccessField()
125 {
126 return accessField;
127 }
128
129
130 protected void startEmbedded(final String name, final boolean embedded, final boolean id)
131 {
132 embeddLevel++;
133
134 String contextName = name;
135 if (!accessFields.isEmpty())
136 {
137 contextName = accessFields.peek().name + "." + name;
138 }
139
140 accessFields.push(new ContextInfo(contextName, accessField, embedded, id));
141 }
142
143 protected void endEmbedded()
144 {
145 embeddLevel--;
146 accessField = popAccessType();
147 }
148
149 public String getContextName()
150 {
151 if (accessFields.isEmpty())
152 {
153 return null;
154 }
155
156 return accessFields.peek().name;
157 }
158
159 public boolean isEmbedded()
160 {
161 if (accessFields.isEmpty())
162 {
163 return true;
164 }
165
166 return accessFields.peek().embedded;
167 }
168
169 public boolean isId()
170 {
171 if (accessFields.isEmpty())
172 {
173 return true;
174 }
175
176 return accessFields.peek().id;
177 }
178 }
179
180 public EjbExtractor()
181 {
182 }
183
184
185
186
187
188
189
190
191
192 public void getMetaData(MetaDataWritable metaData, Object entity)
193 {
194 if (!(entity instanceof String))
195 {
196 throw new IllegalArgumentException("passed entity argument not a string: " + entity);
197 }
198
199 Class<?> entityClass;
200 try
201 {
202 entityClass = Class.forName(entity.toString());
203 }
204 catch (ClassNotFoundException e)
205 {
206 throw new RuntimeException(e);
207 }
208
209 Context context = new Context();
210
211 create(context, metaData, entityClass);
212 }
213
214
215
216
217
218
219
220
221 protected void createClassList(List<Class<?>> classes, Class<?> clazz)
222 {
223 Class<?> superClass = clazz.getSuperclass();
224
225 if (superClass != null && !superClass.equals(Object.class))
226 {
227 createClassList(classes, superClass);
228 }
229
230 classes.add(clazz);
231 }
232
233
234
235
236 @SuppressWarnings("unchecked")
237 protected void create(Context context, MetaDataWritable metaData, Class entityClass)
238 {
239
240
241
242
243
244
245
246
247 List<Class<?>> classes = new ArrayList<Class<?>>(10);
248 createClassList(classes, entityClass);
249
250 for (Class clazz : classes)
251 {
252
253
254 boolean accessByField = context.getAccessField();
255
256 Boolean determinedAccessByField = determineAccessByField(entityClass);
257 if (determinedAccessByField != null)
258 {
259 accessByField = determinedAccessByField.booleanValue();
260 }
261
262 if (accessByField)
263 {
264 context.setAccessField(true);
265 initFromFields(context, metaData, getFields(clazz));
266 }
267 else
268 {
269 context.setAccessField(false);
270 initFromMethods(context, metaData, getMethods(clazz));
271 }
272 }
273 }
274
275
276
277
278
279
280
281
282
283
284 protected Boolean determineAccessByField(Class<?> clazz)
285 {
286 Class<?> checkClass = clazz;
287 while (checkClass != null && !checkClass.equals(Object.class))
288 {
289 Method[] methods = checkClass.getDeclaredMethods();
290 for (Method method : methods)
291 {
292 if (method.isSynthetic())
293 {
294 continue;
295 }
296
297 if (method.isAnnotationPresent(Id.class) || method.isAnnotationPresent(EmbeddedId.class))
298 {
299 return Boolean.FALSE;
300 }
301 }
302
303 Field[] fields = checkClass.getDeclaredFields();
304 for (Field field : fields)
305 {
306 if (field.isSynthetic())
307 {
308 continue;
309 }
310
311 if (field.isAnnotationPresent(Id.class) || field.isAnnotationPresent(EmbeddedId.class))
312 {
313 return Boolean.TRUE;
314 }
315 }
316
317 checkClass = checkClass.getSuperclass();
318 }
319
320 return null;
321 }
322
323 protected Method[] getMethods(Class<?> entityClass)
324 {
325 return ClassHelperFactory.get().getMethods(entityClass);
326 }
327
328 protected Field[] getFields(Class<?> entityClass)
329 {
330 return ClassHelperFactory.get().getFields(entityClass);
331 }
332
333
334
335
336 protected void initFromFields(Context context, MetaDataWritable metaData, Field[] fields)
337 {
338 for (Field field : fields)
339 {
340 if (!validModifier(field.getModifiers(), false)
341 || field.isSynthetic()
342 || hasAnnotationTransient(field))
343 {
344
345
346 continue;
347 }
348 String name = field.getName();
349 Class<?> type = field.getType();
350
351 if (metaData.isWantedField(createFullName(context, name)))
352 {
353 processField(context, metaData, field, name, type, true, true);
354 }
355 }
356 }
357
358
359
360
361 protected void processField(Context context, MetaDataWritable metaData, AccessibleObject accessibleObject,
362 String name, Class<?> type, Boolean canRead, Boolean canWrite)
363 {
364 if (accessibleObject.isAnnotationPresent(IgnoreProperty.class))
365 {
366
367 return;
368 }
369
370
371 if (processEmbedded(context, metaData, accessibleObject, name, type))
372 {
373 return;
374 }
375
376 if (metaData.isParentOfWantedField(name))
377 {
378
379 embeddEntity(context, metaData, name, type);
380
381 return;
382 }
383
384 MetaFieldWritable mdField = metaData.getOrCreateField(createFullName(context, name));
385 mdField.setType(type);
386 if (canRead != null)
387 {
388 mdField.setCanRead(canRead.booleanValue());
389 }
390 if (canWrite != null)
391 {
392 mdField.setCanWrite(canWrite.booleanValue());
393 }
394 mdField.setEmbedded(context.isEmbedded());
395 initFromType(context, mdField, type);
396 initFromAnnotations(context, mdField, accessibleObject);
397 }
398
399 protected boolean processEmbedded(Context context, MetaDataWritable metaData, AccessibleObject accessibleObject,
400 String name, Class<?> type)
401 {
402 if (accessibleObject.isAnnotationPresent(Embedded.class)
403 || accessibleObject.isAnnotationPresent(Id.class)
404 || accessibleObject.isAnnotationPresent(EmbeddedId.class))
405 {
406 if (type.isAnnotationPresent(Embeddable.class) || type.isAnnotationPresent(MappedSuperclass.class))
407 {
408
409 try
410 {
411 boolean isId = context.isId()
412 || accessibleObject.isAnnotationPresent(Id.class)
413 || accessibleObject.isAnnotationPresent(EmbeddedId.class);
414
415 context.startEmbedded(name, true, isId);
416
417 create(context, metaData, type);
418 }
419 finally
420 {
421 context.endEmbedded();
422 }
423 return true;
424 }
425 }
426
427 return false;
428 }
429
430
431
432
433 protected boolean checkEmbeddEntity(Context context, MetaDataWritable metaData, String name)
434 {
435 String checkName = createFullName(context, name) + ".";
436
437 for (String fieldName : metaData.getRequestedFields())
438 {
439 if (fieldName.startsWith(checkName))
440 {
441 return true;
442 }
443 }
444
445 return false;
446 }
447
448 protected String createFullName(Context context, String name)
449 {
450 if (context.getContextName() != null)
451 {
452 return context.getContextName() + "." + name;
453 }
454 else
455 {
456 return name;
457 }
458 }
459
460
461
462
463 protected void embeddEntity(Context context, MetaDataWritable metaData, String name, Class<?> entityType)
464 {
465
466 boolean previousLock = false;
467 try
468 {
469 boolean processAll = metaData.getRequestedFields().contains(createFullName(context, name + ".*"));
470 if (!processAll)
471 {
472 previousLock = metaData.setLockFields(true);
473 }
474 context.startEmbedded(name, false, context.isId());
475 create(context, metaData, entityType);
476 }
477 finally
478 {
479 context.endEmbedded();
480 metaData.setLockFields(previousLock);
481 }
482 }
483
484
485
486
487 protected void initFromAnnotations(Context context, MetaFieldWritable mdField, AccessibleObject accessibleObject)
488 {
489 if (accessibleObject.isAnnotationPresent(DisplayOnly.class))
490 {
491
492 mdField.setDisplayOnly(true);
493 }
494 if (accessibleObject.isAnnotationPresent(ReadOnly.class))
495 {
496 ReadOnly readOnly = accessibleObject.getAnnotation(ReadOnly.class);
497
498
499 mdField.setCanWrite(false);
500 if (readOnly.disabled())
501 {
502 mdField.setDisabled(true);
503 }
504 }
505
506 if (accessibleObject.isAnnotationPresent(UIComponent.class))
507 {
508 UIComponent component = accessibleObject.getAnnotation(UIComponent.class);
509 mdField.setWantedComponentType(component.type());
510 }
511
512 if (accessibleObject.isAnnotationPresent(ComponentHandler.class))
513 {
514 ComponentHandler componentHandler = accessibleObject.getAnnotation(ComponentHandler.class);
515 String componentHandlerBeanName = componentHandler.value();
516 mdField.setComponentHandler(componentHandlerBeanName);
517 }
518
519 if (accessibleObject.isAnnotationPresent(Column.class))
520 {
521
522 Column column = accessibleObject.getAnnotation(Column.class);
523 mdField.setRequired(!column.nullable());
524 }
525 if (accessibleObject.isAnnotationPresent(Basic.class))
526 {
527
528 Basic basic = accessibleObject.getAnnotation(Basic.class);
529
530 if (!basic.optional())
531 {
532 mdField.setRequired(!basic.optional());
533 }
534 }
535
536 if (accessibleObject.isAnnotationPresent(Id.class)
537 || accessibleObject.isAnnotationPresent(EmbeddedId.class)
538 || context.isId())
539 {
540 mdField.setId(true);
541 }
542
543 if (accessibleObject.isAnnotationPresent(Id.class))
544 {
545
546 if (accessibleObject.isAnnotationPresent(GeneratedValue.class))
547 {
548 setSpecialFieldDisabled(mdField);
549 }
550 }
551
552 if (accessibleObject.isAnnotationPresent(Version.class))
553 {
554 setSpecialFieldDisabled(mdField);
555 }
556
557 if (accessibleObject.isAnnotationPresent(OneToOne.class))
558 {
559 mdField.setRelationType(RelationType.ONE_TO_ONE);
560 OneToOne relation = accessibleObject.getAnnotation(OneToOne.class);
561 if (!relation.optional())
562 {
563 mdField.setRequired(relation.optional());
564 }
565 }
566 if (accessibleObject.isAnnotationPresent(OneToMany.class))
567 {
568 mdField.setRelationType(RelationType.ONE_TO_MANY);
569 }
570 if (accessibleObject.isAnnotationPresent(ManyToOne.class))
571 {
572 mdField.setRelationType(RelationType.MANY_TO_ONE);
573
574 ManyToOne relation = accessibleObject.getAnnotation(ManyToOne.class);
575 if (!relation.optional())
576 {
577 mdField.setRequired(relation.optional());
578 }
579 }
580 if (accessibleObject.isAnnotationPresent(ManyToMany.class))
581 {
582 mdField.setRelationType(RelationType.MANY_TO_MANY);
583 }
584 if (accessibleObject.isAnnotationPresent(DataProvider.class))
585 {
586 DataProvider dataProvider = accessibleObject.getAnnotation(DataProvider.class);
587 mdField.setDataSource(dataProvider.value());
588 mdField.setDataSourceDescription(dataProvider.description());
589
590 String comparator = dataProvider.comparator();
591 if (comparator != null && comparator.length() > 0)
592 {
593 mdField.setDataComparator(dataProvider.comparator());
594 }
595 if (!StringUtils.isEmpty(dataProvider.converterBean()))
596 {
597 mdField.setConverterBean(dataProvider.converterBean());
598 }
599 }
600
601
602 if (accessibleObject.isAnnotationPresent(Temporal.class))
603 {
604 Temporal temporal = accessibleObject.getAnnotation(Temporal.class);
605 mdField.setTemporalType(temporal.value());
606 }
607
608 if (accessibleObject.isAnnotationPresent(org.apache.myfaces.orchestra.dynaForm.annot.ui.Temporal.class))
609 {
610 org.apache.myfaces.orchestra.dynaForm.annot.ui.Temporal temporal =
611 accessibleObject.getAnnotation(org.apache.myfaces.orchestra.dynaForm.annot.ui.Temporal.class);
612 mdField.setTemporalType(temporal.value());
613 }
614
615 Class<?> type = mdField.getType();
616 if (type.isAnnotationPresent(Entity.class))
617 {
618 mdField.setEntityType(true);
619 }
620
621 if (accessibleObject.isAnnotationPresent(Min.class))
622 {
623 Min annot = accessibleObject.getAnnotation(Min.class);
624 mdField.setMinValue((double) annot.value());
625 }
626 if (accessibleObject.isAnnotationPresent(Max.class))
627 {
628 Max annot = accessibleObject.getAnnotation(Max.class);
629 mdField.setMaxValue((double) annot.value());
630 }
631 if (accessibleObject.isAnnotationPresent(Length.class))
632 {
633 Length annot = accessibleObject.getAnnotation(Length.class);
634 mdField.setMinSize(annot.min());
635 mdField.setMaxSize(annot.max());
636 }
637 if (accessibleObject.isAnnotationPresent(NotNull.class))
638 {
639 mdField.setRequired(true);
640 }
641 if (accessibleObject.isAnnotationPresent(Range.class))
642 {
643 Range annot = accessibleObject.getAnnotation(Range.class);
644 mdField.setMinValue((double) annot.min());
645 mdField.setMaxValue((double) annot.max());
646 }
647
648 if (accessibleObject.isAnnotationPresent(DisplaySize.class))
649 {
650 DisplaySize displaySize = accessibleObject.getAnnotation(DisplaySize.class);
651 mdField.setDisplaySize(displaySize.value());
652 }
653
654
655 if (accessibleObject.isAnnotationPresent(MetaAttribute.class))
656 {
657 MetaAttribute attr = accessibleObject.getAnnotation(MetaAttribute.class);
658 String name = attr.name();
659 String value = attr.value();
660 mdField.setAttribute(name, value);
661 }
662 }
663
664
665
666
667 protected void setSpecialFieldDisabled(MetaFieldWritable mdField)
668 {
669 mdField.setCanWrite(false);
670 mdField.setDisabled(true);
671 }
672
673
674
675
676 protected void initFromMethods(Context context, MetaDataWritable metaData, Method[] methods)
677 {
678 for (Method method : methods)
679 {
680 if (!validModifier(method.getModifiers(), true)
681 || method.isSynthetic()
682 || hasAnnotationTransient(method)
683 || SYSTEM_METHODS.contains(method.getName()))
684 {
685 continue;
686 }
687
688
689 String methodName = method.getName();
690 String propertyName = convertMethodName(methodName);
691
692 if (!metaData.isWantedField(createFullName(context, propertyName)))
693 {
694 continue;
695 }
696
697 if (methodName.startsWith("get") || methodName.startsWith("is"))
698 {
699 Class<?>[] parameters = method.getParameterTypes();
700 if (parameters != null && parameters.length > 0)
701 {
702
703 continue;
704 }
705
706 processField(context, metaData, method, propertyName, method.getReturnType(), true, null);
707 }
708 else if (methodName.startsWith("set"))
709 {
710 if (!void.class.equals(method.getReturnType()) && !Void.class.equals(method.getReturnType()))
711 {
712
713 continue;
714 }
715
716 Class<?>[] parameters = method.getParameterTypes();
717 if (parameters != null && parameters.length != 1)
718 {
719
720 continue;
721 }
722
723 if (metaData.isWantedField(createFullName(context, propertyName)))
724 {
725 processField(context, metaData, method, propertyName, method.getParameterTypes()[0], null, true);
726 }
727 }
728 }
729 }
730
731 @SuppressWarnings("unchecked")
732 protected void initFromType(Context context, MetaFieldWritable mdField, Class type)
733 {
734 if (type.isEnum())
735 {
736 EnumSet es = EnumSet.allOf(type);
737 Object[] enums = es.toArray(new Object[]{es.size()});
738
739 Selection[] selections = new Selection[enums.length];
740 for (int i = 0; i < enums.length; i++)
741 {
742 Enum e = (Enum) enums[i];
743 selections[i] = new Selection(e.name(), e);
744 }
745
746 mdField.setAllowedSelections(selections);
747 }
748
749
750
751
752
753 }
754
755
756
757
758 protected String convertMethodName(String name)
759 {
760 if (name.startsWith("get"))
761 {
762 name = name.substring("get".length());
763 }
764 else if (name.startsWith("set"))
765 {
766 name = name.substring("set".length());
767 }
768 else if (name.startsWith("is"))
769 {
770 name = name.substring("is".length());
771 }
772 return Character.toLowerCase(name.charAt(0)) + name.substring(1);
773 }
774
775
776
777
778 protected boolean hasAnnotationTransient(AccessibleObject accessibleObject)
779 {
780 return accessibleObject.isAnnotationPresent(Transient.class);
781 }
782
783
784
785
786 protected boolean validModifier(int modifier, boolean isMethod)
787 {
788 if (isMethod && !Modifier.isPublic(modifier))
789 {
790 return false;
791 }
792 if (Modifier.isStatic(modifier))
793 {
794 return false;
795 }
796 if (Modifier.isTransient(modifier))
797 {
798 return false;
799 }
800
801 return true;
802 }
803 }