1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package ch.elca.el4j.services.persistence.hibernate.dao.extent;
18
19
20 import java.lang.reflect.Method;
21 import java.lang.reflect.ParameterizedType;
22 import java.lang.reflect.Type;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Iterator;
26 import java.util.LinkedList;
27 import java.util.List;
28
29 import org.springframework.util.Assert;
30
31 import ch.elca.el4j.core.metadata.ContainedClass;
32 import ch.elca.el4j.util.codingsupport.BeanPropertyUtils;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public class ExtentEntity extends AbstractExtentPart {
53
54
55 private Class<?> m_entityClass;
56
57
58 private List<String> m_fields;
59
60
61 private List<ExtentEntity> m_childEntities;
62
63
64 private List<ExtentCollection> m_collections;
65
66
67 private String m_entityId;
68
69
70 private boolean m_root = false;
71
72
73 private boolean m_frozen;
74
75
76
77
78
79 public ExtentEntity(Class<?> c) {
80 m_name = firstCharLower(c.getSimpleName());
81 m_entityClass = c;
82 m_fields = new LinkedList<String>();
83 m_childEntities = new LinkedList<ExtentEntity>();
84 m_collections = new LinkedList<ExtentCollection>();
85 m_entityId = String.format("|%s[][][]|", m_entityClass.getName());
86 }
87
88
89
90
91
92
93 public ExtentEntity(String name, Class<?> c) {
94 m_name = name;
95 m_entityClass = c;
96 m_fields = new LinkedList<String>();
97 m_childEntities = new LinkedList<ExtentEntity>();
98 m_collections = new LinkedList<ExtentCollection>();
99 m_entityId = String.format("|%s[][][]|", m_name);
100 }
101
102
103
104
105
106
107 public ExtentEntity(Class<?> c, Method method) {
108 m_name = toFieldName(method);
109 m_entityClass = c;
110
111 m_fields = new LinkedList<String>();
112 m_childEntities = new LinkedList<ExtentEntity>();
113 m_collections = new LinkedList<ExtentCollection>();
114 m_entityId = String.format("|%s[][][]|", m_name);
115 }
116
117
118 public String getId() {
119 return m_entityId;
120 }
121
122
123 protected void updateId() {
124 rebuildId();
125 }
126
127
128
129
130 public boolean isRoot() {
131 return m_root;
132 }
133
134
135
136
137
138 public Class<?> getEntityClass() {
139 return m_entityClass;
140 }
141
142
143
144
145
146 public List<String> getFields() {
147 return new LinkedList<String>(m_fields);
148 }
149
150
151
152
153
154 public List<ExtentEntity> getChildEntities() {
155 return new LinkedList<ExtentEntity>(m_childEntities);
156 }
157
158
159
160
161
162 public List<ExtentCollection> getCollections() {
163 return new LinkedList<ExtentCollection>(m_collections);
164 }
165
166
167
168
169
170
171
172 private void rebuildId() {
173
174 String id = "|";
175
176 if (isRoot()) {
177 id += m_entityClass.getName();
178 } else {
179 id += m_name;
180 }
181 id += m_fields.toString();
182 id += m_childEntities.toString();
183 id += m_collections.toString();
184 id += "|";
185
186
187 if (!m_entityId.equals(id)) {
188 m_entityId = id;
189 if (m_parent != null && !isRoot()) {
190 m_parent.updateId();
191 }
192 }
193 }
194
195
196
197
198
199
200 private void addField(String field) {
201 if (!m_fields.contains(field)) {
202 m_fields.add(field);
203 Collections.sort(m_fields);
204 rebuildId();
205 }
206 }
207
208
209
210
211
212
213 private boolean removeField(String name) {
214 if (m_fields.remove(name)) {
215 rebuildId();
216 return true;
217 } else {
218 return false;
219 }
220 }
221
222
223
224
225
226
227
228 private void addChildEntity(ExtentEntity child) throws NoSuchMethodException {
229 if (!m_childEntities.contains(child)) {
230
231 for (ExtentEntity ent : m_childEntities) {
232 if (ent.m_name.equals(child.m_name)) {
233 try {
234 BeanPropertyUtils.getReadMethod(m_entityClass, child.getName());
235 ent.merge(child);
236 return;
237 } catch (IllegalArgumentException e) {
238 throw new NoSuchMethodException(e.getMessage());
239 }
240 }
241 }
242 try {
243 Method m = BeanPropertyUtils.getReadMethod(m_entityClass, child.getName());
244 if (m != null) {
245 child.setParent(this);
246 m_childEntities.add(child);
247 Collections.sort(m_childEntities);
248 rebuildId();
249 }
250 } catch (IllegalArgumentException e) {
251 throw new NoSuchMethodException(e.getMessage());
252 }
253 }
254 }
255
256
257
258
259
260
261 private boolean removeEntity(String name) {
262 for (ExtentEntity e : m_childEntities) {
263 if (e.getName().equals(name)) {
264 m_childEntities.remove(e);
265 rebuildId();
266 return true;
267 }
268 }
269 return false;
270 }
271
272
273
274
275
276
277 private void addCollection(ExtentCollection collection) throws NoSuchMethodException {
278 if (!m_collections.contains(collection)) {
279
280 for (ExtentCollection c : m_collections) {
281 if (c.m_name.equals(collection.m_name)) {
282 try {
283 BeanPropertyUtils.getReadMethod(m_entityClass, collection.getName());
284 c.merge(collection);
285 return;
286 } catch (IllegalArgumentException e) {
287 throw new NoSuchMethodException(e.getMessage());
288 }
289 }
290 }
291 try {
292 Method m = BeanPropertyUtils.getReadMethod(m_entityClass, collection.getName());
293 collection.setParent(this);
294 boolean consistent = false;
295
296 Type rawType = m.getGenericReturnType();
297 if (rawType instanceof ParameterizedType) {
298 Type[] pt = ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments();
299 if (pt.length > 0 && pt[0] instanceof Class<?>) {
300 if (((Class<?>) pt[0]).isAssignableFrom(collection.getContainedEntity().getEntityClass())) {
301
302 consistent = true;
303 }
304 }
305 }
306 if (consistent) {
307 m_collections.add(collection);
308 Collections.sort(m_collections);
309 rebuildId();
310 } else {
311 throw new NoSuchMethodException("Collection type ["
312 + collection.getContainedEntity().getEntityClass().getSimpleName()
313 + "] doesnt conform with class definition.");
314 }
315 } catch (IllegalArgumentException e) {
316 throw new NoSuchMethodException(e.getMessage());
317 }
318 }
319 }
320
321
322
323
324
325
326 private boolean removeCollection(String name) {
327 for (ExtentCollection e : m_collections) {
328 if (e.getName().equals(name)) {
329 m_collections.remove(e);
330 rebuildId();
331 return true;
332 }
333 }
334 return false;
335 }
336
337
338
339
340
341
342
343 private void addMethodAsName(String name) throws NoSuchMethodException {
344 try {
345 Method m = BeanPropertyUtils.getReadMethod(m_entityClass, name);
346 if (m != null) {
347 fetchMethod(m, DataExtent.DEFAULT_LOADING_DEPTH);
348 } else {
349 throw new NoSuchMethodException("Method doesn't exist.");
350 }
351 } catch (IllegalArgumentException e) {
352 throw new NoSuchMethodException(e.getMessage());
353 }
354 }
355
356
357 //
358
359
360
361
362
363
364
365 public static ExtentEntity rootEntity(Class<?> c) {
366 ExtentEntity tmp = new ExtentEntity(c);
367 tmp.m_root = true;
368 return tmp;
369
370 }
371
372
373
374
375
376
377 public static ExtentEntity entity(Class<?> c) {
378 return new ExtentEntity(c);
379 }
380
381
382
383
384
385
386
387 public static ExtentEntity entity(String name, Class<?> c) {
388 return new ExtentEntity(name, c);
389 }
390
391
392
393
394
395
396
397 public static ExtentEntity entity(Class<?> c, Method m) {
398 return new ExtentEntity(c, m);
399 }
400
401
402
403
404
405
406
407
408
409 public ExtentEntity with(String... fields) throws NoSuchMethodException {
410 Assert.state(!m_frozen, "DataExtent is frozen and cannot be changed anymore");
411 for (String s : fields) {
412 addMethodAsName(s);
413 }
414 return this;
415 }
416
417
418
419
420
421
422
423
424 public ExtentEntity withSubentities(AbstractExtentPart... entities) throws NoSuchMethodException {
425 Assert.state(!m_frozen, "DataExtent is frozen and cannot be changed anymore");
426 for (AbstractExtentPart entity : entities) {
427 if (entity instanceof ExtentEntity) {
428 addChildEntity((ExtentEntity) entity);
429 } else {
430 addCollection((ExtentCollection) entity);
431 }
432 }
433 return this;
434 }
435
436
437
438
439
440
441
442
443 public ExtentEntity without(String...fields) {
444 Assert.state(!m_frozen, "DataExtent is frozen and cannot be changed anymore");
445 for (String s : fields) {
446 if (!removeField(s)) {
447 if (!removeEntity(s)) {
448 removeCollection(s);
449 }
450 }
451 }
452 return this;
453 }
454
455
456
457
458
459
460 public ExtentEntity all(int depth) {
461 Assert.state(!m_frozen, "DataExtent is frozen and cannot be changed anymore");
462 if (depth > 0) {
463 for (Method m : m_entityClass.getMethods()) {
464 try {
465 fetchMethod(m, depth);
466 } catch (NoSuchMethodException e) {
467
468 }
469 }
470 }
471 return this;
472 }
473
474
475
476
477
478
479
480
481 public ExtentEntity merge(ExtentEntity other) {
482 Assert.state(!m_frozen, "DataExtent is frozen and cannot be changed anymore");
483 if (m_entityClass.equals(other.m_entityClass) && !this.equals(other)) {
484 mergeFields(other.m_fields);
485 mergeEntities(other.m_childEntities);
486 mergeCollections(other.m_collections);
487 rebuildId();
488 }
489 return this;
490 }
491
492
493
494
495
496 public ExtentEntity freeze() {
497 if (!m_frozen) {
498 m_frozen = true;
499 for (ExtentEntity ent : m_childEntities) {
500 ent.freeze();
501 }
502 for (ExtentCollection c : m_collections) {
503 c.freeze();
504 }
505 }
506 return this;
507 }
508
509
510
511
512
513 private void mergeFields(List<String> otherFields) {
514
515 if (m_fields.isEmpty()) {
516 m_fields.addAll(otherFields);
517 } else {
518 Iterator<String> i = otherFields.iterator();
519 String f = null;
520 if (i.hasNext()) {
521 f = i.next();
522 }
523 for (int k = 0; k < m_fields.size() && f != null; k++) {
524 int result = m_fields.get(k).compareTo(f);
525 if (result > 0) {
526
527 m_fields.add(k, f);
528 }
529 if (result >= 0) {
530
531 if (i.hasNext()) {
532 f = i.next();
533 } else {
534 f = null;
535 }
536 }
537 }
538 }
539 }
540
541
542
543
544
545 private void mergeEntities(List<ExtentEntity> otherEntities) {
546
547 if (m_childEntities.isEmpty()) {
548 m_childEntities.addAll(otherEntities);
549 } else {
550 Iterator<ExtentEntity> i = otherEntities.iterator();
551 ExtentEntity e = null;
552 if (i.hasNext()) {
553 e = i.next();
554 }
555 for (int k = 0; k < m_childEntities.size() && e != null; k++) {
556 int result = m_childEntities.get(k).compareTo(e);
557 if (result > 0) {
558
559 m_childEntities.add(k, e);
560 } else if (result == 0 && !e.equals(m_childEntities.get(k))) {
561
562
563 m_childEntities.get(k).merge(e);
564 }
565 if (result >= 0) {
566
567 if (i.hasNext()) {
568 e = i.next();
569 } else {
570 e = null;
571 }
572 }
573 }
574 }
575 }
576
577
578
579
580
581 private void mergeCollections(List<ExtentCollection> otherCollections) {
582
583 if (m_collections.isEmpty()) {
584 m_collections.addAll(otherCollections);
585 } else {
586 Iterator<ExtentCollection> i = otherCollections.iterator();
587 ExtentCollection c = null;
588 if (i.hasNext()) {
589 c = i.next();
590 }
591 for (int k = 0; k < m_collections.size() && c != null; k++) {
592 int result = m_collections.get(k).compareTo(c);
593 if (result > 0) {
594
595 m_collections.add(k, c);
596 } else if (result == 0
597 && !c.getContainedEntity().equals(m_collections.get(k).getContainedEntity())) {
598
599
600 m_collections.get(k).merge(c);
601 }
602 if (result >= 0) {
603
604 if (i.hasNext()) {
605 c = i.next();
606 } else {
607 c = null;
608 }
609 }
610 }
611 }
612 }
613
614
615
616
617
618
619
620
621
622 private void fetchMethod(Method m, int depth) throws NoSuchMethodException {
623
624
625 boolean isGetter = m.getName().startsWith("get") && m.getParameterTypes().length == 0;
626 if (isGetter && !m.getName().equals("getClass") && !m.getName().equals("get")) {
627
628 if (m.getReturnType().isPrimitive() || m.getReturnType().isEnum()
629
630
631 addField(toFieldName(m));
632 } else if (Collection.class.isAssignableFrom(m.getReturnType())) {
633 fetchCollection(m, depth);
634 } else {
635 ExtentEntity tmp = entity(m.getReturnType(), m);
636 if (depth > 1) {
637 tmp.all(depth - 1);
638 }
639 addChildEntity(tmp);
640 }
641 }
642 }
643
644
645
646
647
648
649
650
651 private void fetchCollection(Method m, int depth) throws NoSuchMethodException {
652
653 Class<?> t = null;
654
655 ContainedClass containedAnnotation = m.getAnnotation(ContainedClass.class);
656 if (containedAnnotation == null) {
657 Type rawType = m.getGenericReturnType();
658 if (rawType instanceof ParameterizedType) {
659 Type[] pt = ((ParameterizedType) m.getGenericReturnType())
660 .getActualTypeArguments();
661 if (pt.length > 0 && pt[0] instanceof Class<?>) {
662 t = (Class<?>) ((ParameterizedType) m.getGenericReturnType())
663 .getActualTypeArguments()[0];
664 }
665 }
666 } else {
667 t = containedAnnotation.value();
668 }
669 if (t != null) {
670 ExtentCollection tmp = new ExtentCollection(t, m);
671 if (depth > 1) {
672 tmp.getContainedEntity().all(depth - 1);
673 }
674 addCollection(tmp);
675 }
676
677 }
678
679
680
681 @Override
682 public String toString() {
683 if (m_parent != null && isRoot()) {
684 return super.nativeToString();
685 } else {
686 return m_entityId;
687 }
688 }
689
690 }