1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.portals.graffito.jcr.mapper.model;
17
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.portals.graffito.jcr.exception.JcrMappingException;
29 import org.apache.portals.graffito.jcr.reflection.ReflectionUtils;
30
31 /***
32 *
33 * ClassDescriptor is used by the mapper to read general information on a class
34 *
35 * @author <a href="mailto:christophe.lombart@sword-technologies.com">Lombart Christophe </a>
36 * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
37 */
38 public class ClassDescriptor {
39
40 private static final Log log = LogFactory.getLog(ClassDescriptor.class);
41
42 private static final String NODETYPE_PER_HIERARCHY = "nodetypeperhierarchy";
43 private static final String NODETYPE_PER_CONCRETECLASS = "nodetypeperconcreteclass";
44
45 private MappingDescriptor mappingDescriptor;
46 private ClassDescriptor superClassDescriptor;
47 private Collection descendantClassDescriptors = new ArrayList();
48
49 private String className;
50 private String jcrNodeType;
51 private String jcrSuperTypes;
52 private String[] jcrMixinTypes = new String[0];
53 private FieldDescriptor idFieldDescriptor;
54 private FieldDescriptor pathFieldDescriptor;
55
56 private Map fieldDescriptors = new HashMap();
57 private Map beanDescriptors = new HashMap();
58 private Map collectionDescriptors = new HashMap();
59
60 private Map fieldNames = new HashMap();
61
62 private String superClassName;
63 private String extendsStrategy;
64 private boolean isAbstract = false;
65 private boolean hasDescendant = false;
66 private boolean hasDiscriminator = true;
67
68
69 private boolean isInterface=false;
70 private List interfaces = new ArrayList();
71
72 public void setAbstract(boolean flag) {
73 this.isAbstract = flag;
74 }
75
76 public boolean isAbstract() {
77 return this.isAbstract;
78 }
79
80 public void setInterface(boolean flag) {
81 this.isInterface = flag;
82 }
83
84 public boolean isInterface() {
85 return isInterface;
86 }
87
88 public boolean hasInterfaces()
89 {
90 return this.interfaces.size() > 0;
91 }
92
93 public void setDiscriminator(boolean flag)
94 {
95 this.hasDiscriminator = flag;
96 }
97
98 public boolean hasDiscriminator() {
99 return this.hasDiscriminator;
100 }
101
102 public boolean usesNodeTypePerHierarchyStrategy() {
103 return NODETYPE_PER_HIERARCHY.equals(this.extendsStrategy);
104 }
105
106 public boolean usesNodeTypePerConcreteClassStrategy() {
107 return NODETYPE_PER_CONCRETECLASS.equals(this.extendsStrategy);
108 }
109 /***
110 * @return Returns the className.
111 */
112 public String getClassName() {
113 return className;
114 }
115
116 /***
117 * @param className The className to set.
118 */
119 public void setClassName(String className) {
120 this.className = className;
121 }
122
123 /***
124 * @return Returns the jcrNodeType.
125 */
126 public String getJcrNodeType() {
127 return jcrNodeType;
128 }
129
130 /***
131 * @param jcrNodeType The jcrNodeType to set.
132 */
133 public void setJcrNodeType(String jcrNodeType) {
134 this.jcrNodeType = jcrNodeType;
135 }
136
137 /***
138 * Add a new FielDescriptor
139 * @param fieldDescriptor the new field descriptor to add
140 */
141 public void addFieldDescriptor(FieldDescriptor fieldDescriptor) {
142 fieldDescriptor.setClassDescriptor(this);
143 if (fieldDescriptor.isId()) {
144 this.idFieldDescriptor = fieldDescriptor;
145 }
146 if (fieldDescriptor.isPath()) {
147 this.pathFieldDescriptor = fieldDescriptor;
148 }
149
150 fieldDescriptors.put(fieldDescriptor.getFieldName(), fieldDescriptor);
151 fieldNames.put(fieldDescriptor.getFieldName(), fieldDescriptor.getJcrName());
152 }
153
154 public void addImplementDescriptor(ImplementDescriptor implementDescriptor)
155 {
156 interfaces.add(implementDescriptor.getInterfaceName());
157 }
158
159 /***
160 * Get the FieldDescriptor to used for a specific java bean attribute
161 * @param fieldName The java bean attribute name
162 *
163 * @return the {@link FieldDescriptor} found or null
164 */
165 public FieldDescriptor getFieldDescriptor(String fieldName) {
166 return (FieldDescriptor) this.fieldDescriptors.get(fieldName);
167 }
168
169 /***
170 *
171 * @return all {@link FieldDescriptor} defined in this ClassDescriptor
172 */
173 public Collection getFieldDescriptors() {
174 return this.fieldDescriptors.values();
175 }
176
177 /***
178 * Add a new BeanDescriptor
179 * @param beanDescriptor the new bean descriptor to add
180 */
181
182 public void addBeanDescriptor(BeanDescriptor beanDescriptor) {
183 beanDescriptor.setClassDescriptor(this);
184 beanDescriptors.put(beanDescriptor.getFieldName(), beanDescriptor);
185 fieldNames.put(beanDescriptor.getFieldName(), beanDescriptor.getJcrName());
186 }
187
188 /***
189 * Get the BeanDescriptor to used for a specific java bean attribute
190 * @param fieldName The java bean attribute name
191 *
192 * @return the {@link BeanDescriptor} found or null
193 */
194 public BeanDescriptor getBeanDescriptor(String fieldName) {
195 return (BeanDescriptor) this.beanDescriptors.get(fieldName);
196 }
197
198 /***
199 * @return all {@link BeanDescriptor} defined in this ClassDescriptor
200 */
201 public Collection getBeanDescriptors() {
202 return this.beanDescriptors.values();
203 }
204
205 /***
206 * Add a new CollectionDescriptor
207 * @param collectionDescriptor the new collection descriptor to add
208 */
209
210 public void addCollectionDescriptor(CollectionDescriptor collectionDescriptor) {
211 collectionDescriptor.setClassDescriptor(this);
212 collectionDescriptors.put(collectionDescriptor.getFieldName(), collectionDescriptor);
213 fieldNames.put(collectionDescriptor.getFieldName(), collectionDescriptor.getJcrName());
214 }
215
216 /***
217 * Get the CollectionDescriptor to used for a specific java bean attribute
218 * @param fieldName The java bean attribute name
219 *
220 * @return the {@link CollectionDescriptor} found or null
221 */
222 public CollectionDescriptor getCollectionDescriptor(String fieldName) {
223 return (CollectionDescriptor) this.collectionDescriptors.get(fieldName);
224 }
225
226 /***
227 * @return all {@link BeanDescriptor} defined in this ClassDescriptor
228 */
229 public Collection getCollectionDescriptors() {
230 return this.collectionDescriptors.values();
231 }
232
233 /***
234 * @return the fieldDescriptor ID
235 */
236 public FieldDescriptor getIdFieldDescriptor() {
237 return idFieldDescriptor;
238 }
239
240 /***
241 * @return the fieldDescriptor path
242 */
243 public FieldDescriptor getPathFieldDescriptor() {
244 if (null != this.pathFieldDescriptor) {
245 return this.pathFieldDescriptor;
246 }
247
248 if (null != this.superClassDescriptor) {
249 return this.superClassDescriptor.getPathFieldDescriptor();
250 }
251
252 return null;
253 }
254
255
256 /***
257 * Check if this class has an ID
258 * @return true if the class has an ID
259 */
260 public boolean hasIdField() {
261 return (this.idFieldDescriptor != null && ! this.idFieldDescriptor.equals(""));
262 }
263
264 /***
265 * Get the JCR name used for one of the object attributes
266 * @param fieldName the object attribute name (can be an atomic field, bean field or a collection field)
267 * @return the JCR name found
268 */
269 public String getJcrName(String fieldName) {
270 String jcrName = (String) this.fieldNames.get(fieldName);
271 if (this.isInterface && jcrName == null)
272 {
273 return this.getJcrNameFromDescendants(this, fieldName);
274 }
275
276 return jcrName;
277 }
278
279 private String getJcrNameFromDescendants(ClassDescriptor classDescriptor, String fieldName )
280 {
281 Iterator descendants = classDescriptor.getDescendantClassDescriptors().iterator();
282 while (descendants.hasNext())
283 {
284 ClassDescriptor descendant = (ClassDescriptor) descendants.next();
285 String jcrName = (String) descendant.fieldNames.get(fieldName);
286 if(jcrName != null)
287 {
288 return jcrName;
289 }
290 return this.getJcrNameFromDescendants(descendant, fieldName);
291 }
292 return null;
293
294
295 }
296
297 public Map getFieldNames() {
298 return this.fieldNames;
299 }
300
301 /*** Get the JCR node super types.
302 *
303 * @return jcrSuperTypes
304 */
305 public String getJcrSuperTypes() {
306 return jcrSuperTypes;
307 }
308
309 /*** Setter for JCR super types.
310 *
311 * @param superTypes Comma separated list of JCR node super types
312 */
313 public void setJcrSuperTypes(String superTypes) {
314 this.jcrSuperTypes = superTypes;
315 }
316
317 /***
318 * Retrieve the mixin types.
319 *
320 * @return array of mixin types
321 */
322 public String[] getJcrMixinTypes() {
323 return this.jcrMixinTypes;
324 }
325
326 /***
327 * Sets a comma separated list of mixin types.
328 *
329 * @param mixinTypes command separated list of mixins
330 */
331 public void setJcrMixinTypes(String[] mixinTypes) {
332 if (null != mixinTypes && mixinTypes.length == 1) {
333 jcrMixinTypes = mixinTypes[0].split(" *, *");
334 }
335 }
336
337 /***
338 * @return Returns the mappingDescriptor.
339 */
340 public MappingDescriptor getMappingDescriptor() {
341 return mappingDescriptor;
342 }
343
344 /***
345 * @param mappingDescriptor The mappingDescriptor to set.
346 */
347 public void setMappingDescriptor(MappingDescriptor mappingDescriptor) {
348 this.mappingDescriptor = mappingDescriptor;
349 }
350
351 /***
352 * Revisit information in this descriptor and fills in more.
353 */
354 public void afterPropertiesSet() {
355 validateClassName();
356 validateBeanFields();
357 lookupSuperDescriptor();
358 lookupInheritanceSettings();
359
360 }
361
362 private void validateClassName() {
363 try {
364 ReflectionUtils.forName(this.className);
365 } catch (JcrMappingException e) {
366 throw new JcrMappingException("Class used in descriptor not found : " + className);
367 }
368 }
369
370 private void validateBeanFields()
371 {
372 Iterator beanDescriptorIterator = beanDescriptors.values().iterator();
373 while (beanDescriptorIterator.hasNext()) {
374 BeanDescriptor beanDescriptor = (BeanDescriptor) beanDescriptorIterator.next();
375 if (beanDescriptor.isProxy() && beanDescriptor.isInline())
376 {
377 throw new JcrMappingException("Bean field can not be proxy and inline - class : " + this.className + " - bean field :" + beanDescriptor.getFieldName());
378 }
379
380 }
381
382 }
383
384 private void lookupSuperDescriptor() {
385 if (null != superClassDescriptor) {
386 this.hasDiscriminator = superClassDescriptor.hasDiscriminator();
387 if (! this.isInterface)
388 {
389 this.fieldDescriptors = mergeFields(this.fieldDescriptors, this.superClassDescriptor.getFieldDescriptors());
390 this.beanDescriptors = mergeBeans(this.beanDescriptors, this.superClassDescriptor.getBeanDescriptors());
391 this.collectionDescriptors = mergeCollections(this.collectionDescriptors, this.superClassDescriptor.getCollectionDescriptors());
392 this.fieldNames.putAll(this.superClassDescriptor.getFieldNames());
393 }
394
395 }
396 }
397
398 private void lookupInheritanceSettings() {
399 if ((null != this.superClassDescriptor) || (this.hasDescendants()) || this.hasInterfaces()) {
400 if (this.hasDiscriminator()) {
401 this.extendsStrategy = NODETYPE_PER_HIERARCHY;
402 }
403 else {
404 this.extendsStrategy = NODETYPE_PER_CONCRETECLASS;
405 }
406 }
407 }
408
409
410 /***
411 * @return return the super class name if defined in mapping, or
412 * <tt>null</tt> if not set
413 */
414 public String getExtend() {
415 return this.superClassName;
416 }
417
418 /***
419 * @param className
420 */
421 public void setExtend(String className) {
422 this.superClassName = className;
423 }
424
425 /***
426 * @return Returns the superClassDescriptor.
427 */
428 public ClassDescriptor getSuperClassDescriptor() {
429 return superClassDescriptor;
430 }
431
432 public Collection getDescendantClassDescriptors() {
433 return this.descendantClassDescriptors;
434 }
435
436 /***
437 * If the node type per concrete class strategy is used, we need to find a descendant class descriptor assigned to a node type
438 * This method is not used in other situation.
439 *
440 * @param nodeType the node type for which the classdescriptor is required
441 * @return the classdescriptor found or null
442 *
443 * @todo : maybe we have to review this implementation to have better performance.
444 */
445 public ClassDescriptor getDescendantClassDescriptor(String nodeType) {
446 Iterator iterator = this.descendantClassDescriptors.iterator();
447 while (iterator.hasNext()) {
448 ClassDescriptor descendantClassDescriptor = (ClassDescriptor) iterator.next();
449
450 if (descendantClassDescriptor.getJcrNodeType().equals(nodeType)) {
451 return descendantClassDescriptor;
452 }
453
454 if (descendantClassDescriptor.hasDescendants()) {
455 ClassDescriptor classDescriptor = descendantClassDescriptor.getDescendantClassDescriptor(nodeType);
456 if (classDescriptor != null) {
457 return classDescriptor;
458 }
459 }
460 }
461 return null;
462 }
463
464 public void addDescendantClassDescriptor(ClassDescriptor classDescriptor) {
465 this.descendantClassDescriptors.add(classDescriptor);
466 this.hasDescendant = true;
467 }
468
469 public boolean hasDescendants() {
470 return this.hasDescendant;
471 }
472
473 /***
474 * @param superClassDescriptor The superClassDescriptor to set.
475 */
476 public void setSuperClassDescriptor(ClassDescriptor superClassDescriptor) {
477 this.superClassDescriptor= superClassDescriptor;
478 superClassDescriptor.addDescendantClassDescriptor(this);
479 }
480
481
482 public Collection getImplements()
483 {
484 return interfaces;
485 }
486
487 private Map mergeFields(Map existing, Collection superSource) {
488 if (null == superSource) {
489 return existing;
490 }
491
492 Map merged = new HashMap(existing);
493 for(Iterator it = superSource.iterator(); it.hasNext();) {
494 FieldDescriptor fieldDescriptor = (FieldDescriptor) it.next();
495 if (!merged.containsKey(fieldDescriptor.getFieldName())) {
496 merged.put(fieldDescriptor.getFieldName(), fieldDescriptor);
497 }
498
499
500
501 }
502
503 return merged;
504 }
505
506
507 private Map mergeBeans(Map existing, Collection superSource) {
508 if (null == superSource) {
509 return existing;
510 }
511
512 Map merged = new HashMap(existing);
513 for(Iterator it = superSource.iterator(); it.hasNext();) {
514 BeanDescriptor beanDescriptor = (BeanDescriptor) it.next();
515 if (!merged.containsKey(beanDescriptor.getFieldName())) {
516 merged.put(beanDescriptor.getFieldName(), beanDescriptor);
517 }
518
519
520
521 }
522
523 return merged;
524 }
525
526 private Map mergeCollections(Map existing, Collection superSource) {
527 if (null == superSource) {
528 return existing;
529 }
530
531 Map merged = new HashMap(existing);
532 for(Iterator it = superSource.iterator(); it.hasNext();) {
533 CollectionDescriptor collectionDescriptor = (CollectionDescriptor) it.next();
534 if (!merged.containsKey(collectionDescriptor.getFieldName())) {
535 merged.put(collectionDescriptor.getFieldName(), collectionDescriptor);
536 }
537
538
539
540 }
541
542 return merged;
543 }
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565 public String toString() {
566 return "Class Descriptor : " + this.getClassName();
567 }
568 }