1 package org.exolab.castor.builder.factory;
2
3 import org.apache.commons.lang.StringUtils;
4 import org.castor.xml.JavaNaming;
5 import org.exolab.castor.builder.AnnotationBuilder;
6 import org.exolab.castor.builder.SGTypes;
7 import org.exolab.castor.builder.info.CollectionInfo;
8 import org.exolab.castor.builder.info.FieldInfo;
9 import org.exolab.castor.builder.info.nature.XMLInfoNature;
10 import org.exolab.castor.builder.types.XSType;
11 import org.exolab.javasource.JArrayType;
12 import org.exolab.javasource.JClass;
13 import org.exolab.javasource.JCollectionType;
14 import org.exolab.javasource.JDocComment;
15 import org.exolab.javasource.JDocDescriptor;
16 import org.exolab.javasource.JMethod;
17 import org.exolab.javasource.JParameter;
18 import org.exolab.javasource.JSourceCode;
19 import org.exolab.javasource.JType;
20
21
22
23
24
25 public class CollectionMemberAndAccessorFactory extends FieldMemberAndAccessorFactory {
26
27
28
29
30
31
32
33 public CollectionMemberAndAccessorFactory(final JavaNaming naming) {
34 super(naming);
35 }
36
37
38
39
40 public void generateInitializerCode(final FieldInfo fieldInfo,
41 final JSourceCode sourceCode) {
42 CollectionInfo collectionInfo = (CollectionInfo) fieldInfo;
43 sourceCode.add("this.");
44 sourceCode.append(fieldInfo.getName());
45 sourceCode.append(" = new ");
46 JType jType = collectionInfo.getXSList().getJType();
47 sourceCode.append(((JCollectionType) jType).getInstanceName());
48 sourceCode.append("();");
49
50 if (!StringUtils.isEmpty(fieldInfo.getDefaultValue())) {
51 StringBuffer buffer = new StringBuffer();
52 buffer.append(fieldInfo.getName());
53 buffer.append(".add(");
54 buffer.append(fieldInfo.getDefaultValue());
55 buffer.append(");");
56 sourceCode.add(buffer.toString());
57 }
58 }
59
60
61
62
63 public final void createAccessMethods(final FieldInfo fieldInfo,
64 final JClass jClass, final boolean useJava50,
65 final AnnotationBuilder[] annotationBuilders) {
66 CollectionInfo collectionInfo = (CollectionInfo) fieldInfo;
67 this.createAddAndRemoveMethods(collectionInfo, jClass);
68 this.createGetAndSetMethods(collectionInfo, jClass, useJava50, annotationBuilders);
69 this.createGetCountMethod(collectionInfo, jClass);
70 this.createCollectionIterationMethods(collectionInfo, jClass, useJava50);
71 }
72
73
74
75
76
77
78
79 protected void createAddMethod(final CollectionInfo fieldInfo,
80 final JClass jClass) {
81 JMethod method = new JMethod(fieldInfo.getWriteMethodName());
82 method.addException(SGTypes.INDEX_OUT_OF_BOUNDS_EXCEPTION,
83 "if the index given is outside the bounds of the collection");
84 final JParameter parameter = new JParameter(
85 fieldInfo.getContentType().getJType(), fieldInfo.getContentName());
86 method.addParameter(parameter);
87
88 JSourceCode sourceCode = method.getSourceCode();
89 this.addMaxSizeCheck(fieldInfo, method.getName(), sourceCode);
90
91 sourceCode.add("this.");
92 sourceCode.append(fieldInfo.getName());
93 sourceCode.append(".addElement(");
94 sourceCode.append(fieldInfo.getContentType().createToJavaObjectCode(parameter.getName()));
95 sourceCode.append(");");
96
97 if (fieldInfo.isBound()) {
98 this.createBoundPropertyCode(fieldInfo, sourceCode);
99 }
100
101 jClass.addMethod(method);
102 }
103
104
105
106
107
108
109
110 protected void createBoundPropertyCode(final CollectionInfo fieldInfo,
111 final JSourceCode sourceCode) {
112 sourceCode.add("notifyPropertyChangeListeners(\"");
113 String fieldName = fieldInfo.getName();
114 if (fieldName.startsWith("_")) {
115 sourceCode.append(fieldName.substring(1));
116 } else {
117 sourceCode.append(fieldName);
118 }
119 sourceCode.append("\", null, ");
120 sourceCode.append(fieldName);
121 sourceCode.append(");");
122 }
123
124
125
126
127
128
129
130
131 protected void createEnumerateMethod(final CollectionInfo fieldInfo,
132 final JClass jClass, final boolean useJava50) {
133 JMethod method = new JMethod("enumerate" + fieldInfo.getMethodSuffix(),
134 SGTypes.createEnumeration(fieldInfo.getContentType().getJType(), useJava50, true),
135 "an Enumeration over all " + fieldInfo.getContentType().getJType() + " elements");
136
137 JSourceCode sourceCode = method.getSourceCode();
138 sourceCode.add("return this.");
139 sourceCode.append(fieldInfo.getName());
140 sourceCode.append(".elements();");
141
142 jClass.addMethod(method);
143 }
144
145
146
147
148
149
150
151
152
153 private boolean createExtraMethods(final CollectionInfo fieldInfo) {
154 return fieldInfo.isExtraMethods();
155 }
156
157
158
159
160
161
162
163
164
165 private void createGetAsArrayMethod(final CollectionInfo fieldInfo,
166 final JClass jClass, final boolean useJava50,
167 AnnotationBuilder[] annotationBuilders) {
168 JType baseType = fieldInfo.getContentType().getJType();
169 JType arrayType = new JArrayType(baseType, useJava50);
170 JMethod method = new JMethod(fieldInfo.getReadMethodName(), arrayType,
171 "this collection as an Array");
172
173 JSourceCode sourceCode = method.getSourceCode();
174
175
176 JDocComment comment = method.getJDocComment();
177 comment.appendComment("Returns the contents of the collection in an Array. ");
178
179 if (!(baseType.isPrimitive())) {
180
181 comment.appendComment("<p>");
182 comment.appendComment("Note: Just in case the collection contents are changing in ");
183 comment.appendComment("another thread, we pass a 0-length Array of the correct type ");
184 comment.appendComment("into the API call. This way we <i>know</i> that the Array ");
185 comment.appendComment("returned is of exactly the correct length.");
186
187 String baseTypeName = baseType.toString();
188 if (baseType.isArray()) {
189 sourceCode.add(arrayType.toString() + " array = new ");
190 sourceCode.append(baseTypeName.substring(0, baseTypeName.length() - 2) + "[0][];");
191 } else {
192 sourceCode.add(arrayType.toString() + " array = new ");
193 sourceCode.append(baseTypeName + "[0];");
194 }
195
196 sourceCode.add("return ");
197
198 if (!useJava50) {
199 sourceCode.add("(");
200 sourceCode.add(arrayType.toString());
201 sourceCode.add(") ");
202 }
203 sourceCode.append("this." + fieldInfo.getName() + ".toArray(array);");
204 } else {
205
206 sourceCode.add("int size = this.");
207 sourceCode.append(fieldInfo.getName());
208 sourceCode.append(".size();");
209
210 sourceCode.add(arrayType.toString());
211 sourceCode.append(" array = new ");
212
213 int brackets = arrayType.toString().indexOf("[]");
214 sourceCode.append(arrayType.toString().substring(0, brackets));
215 sourceCode.append("[size]");
216 sourceCode.append(";");
217 sourceCode.add("java.util.Iterator iter = " + fieldInfo.getName() + ".iterator();");
218
219 String value = "iter.next()";
220 sourceCode.add("for (int index = 0; index < size; index++) {");
221 sourceCode.indent();
222 sourceCode.add("array[index] = ");
223 if (fieldInfo.getContentType().getType() == XSType.CLASS) {
224 sourceCode.append("(");
225 sourceCode.append(arrayType.getName());
226 sourceCode.append(") ");
227 sourceCode.append(value);
228 } else {
229 sourceCode.append(fieldInfo.getContentType().createFromJavaObjectCode(value));
230 }
231 sourceCode.append(";");
232 sourceCode.unindent();
233 sourceCode.add("}");
234
235 sourceCode.add("return array;");
236 }
237
238
239 for (int i = 0; i < annotationBuilders.length; i++) {
240 AnnotationBuilder annotationBuilder = annotationBuilders[i];
241 annotationBuilder.addFieldGetterAnnotations(fieldInfo, method);
242 }
243
244 jClass.addMethod(method);
245 }
246
247
248
249
250
251
252
253 private void createGetAsReferenceMethod(final CollectionInfo fieldInfo,
254 final JClass jClass) {
255 JMethod method = new JMethod(fieldInfo.getReadMethodName()
256 + fieldInfo.getReferenceMethodSuffix(),
257 fieldInfo.getXSList().getJType(),
258 "a reference to the Vector backing this class");
259
260
261 JDocComment comment = method.getJDocComment();
262 comment.appendComment("Returns a reference to '");
263 comment.appendComment(fieldInfo.getName());
264 comment.appendComment("'. No type checking is performed on any ");
265 comment.appendComment("modifications to the Vector.");
266
267
268 JSourceCode sourceCode = method.getSourceCode();
269 sourceCode.add("return this.");
270 sourceCode.append(fieldInfo.getName());
271 sourceCode.append(";");
272
273 jClass.addMethod(method);
274 }
275
276
277
278
279
280
281
282
283 protected void createGetByIndexMethod(final CollectionInfo fieldInfo,
284 final JClass jClass, boolean useJava50) {
285 XSType contentType = fieldInfo.getContentType();
286 JMethod method = new JMethod(fieldInfo.getReadMethodName(), contentType.getJType(),
287 "the value of the " + contentType.getJType().toString() + " at the given index");
288
289 method.addException(SGTypes.INDEX_OUT_OF_BOUNDS_EXCEPTION,
290 "if the index given is outside the bounds of the collection");
291 method.addParameter(new JParameter(JType.INT, "index"));
292
293 JSourceCode sourceCode = method.getSourceCode();
294 this.addIndexCheck(fieldInfo, sourceCode, method.getName());
295
296 String value = fieldInfo.getName() + ".get(index)";
297 sourceCode.add("return ");
298 if (contentType.getType() == XSType.CLASS) {
299 if (!useJava50) {
300 sourceCode.append("(");
301 sourceCode.append(method.getReturnType().toString());
302 sourceCode.append(") ");
303 }
304 sourceCode.append(value);
305 } else {
306 sourceCode.append(contentType.createFromJavaObjectCode(value));
307 }
308 sourceCode.append(";");
309
310 jClass.addMethod(method);
311 }
312
313
314
315
316
317
318
319 private void createAddAndRemoveMethods(final CollectionInfo fieldInfo,
320 final JClass jClass) {
321
322 this.createAddMethod(fieldInfo, jClass);
323 this.createAddByIndexMethod(fieldInfo, jClass);
324
325
326 this.createRemoveObjectMethod(fieldInfo, jClass);
327 this.createRemoveByIndexMethod(fieldInfo, jClass);
328 this.createRemoveAllMethod(fieldInfo,jClass);
329 }
330
331
332
333
334
335
336
337
338
339 private void createGetAndSetMethods(final CollectionInfo fieldInfo,
340 final JClass jClass, final boolean useJava50,
341 final AnnotationBuilder[] annotationBuilders) {
342
343 this.createGetByIndexMethod(fieldInfo, jClass, useJava50);
344 this.createGetAsArrayMethod(fieldInfo, jClass, useJava50, annotationBuilders);
345 if (this.createExtraMethods(fieldInfo)) {
346 this.createGetAsReferenceMethod(fieldInfo, jClass);
347 }
348
349
350 this.createSetByIndexMethod(fieldInfo, jClass);
351 this.createSetAsArrayMethod(fieldInfo, jClass, useJava50);
352 if (this.createExtraMethods(fieldInfo)) {
353 this.createSetAsCopyMethod(fieldInfo, jClass);
354 this.createSetAsReferenceMethod(fieldInfo, jClass, useJava50);
355 }
356 }
357
358
359
360
361
362
363
364 private void createGetCountMethod(final CollectionInfo fieldInfo,
365 final JClass jClass) {
366 JMethod method = new JMethod(fieldInfo.getReadMethodName() + "Count", JType.INT,
367 "the size of this collection");
368
369 JSourceCode sourceCode = method.getSourceCode();
370 sourceCode.add("return this.");
371 sourceCode.append(fieldInfo.getName());
372 sourceCode.append(".size();");
373
374 jClass.addMethod(method);
375 }
376
377
378
379
380
381
382
383
384
385
386
387
388 protected void createCollectionIterationMethods(final CollectionInfo fieldInfo,
389 final JClass jClass, final boolean useJava50) {
390 this.createEnumerateMethod(fieldInfo, jClass, useJava50);
391 }
392
393
394
395
396
397
398
399
400 protected void createAddByIndexMethod(final CollectionInfo fieldInfo,
401 final JClass jClass) {
402 JMethod method = new JMethod(fieldInfo.getWriteMethodName());
403 method.addException(SGTypes.INDEX_OUT_OF_BOUNDS_EXCEPTION,
404 "if the index given is outside the bounds of the collection");
405 method.addParameter(new JParameter(JType.INT, "index"));
406 final JParameter parameter = new JParameter(
407 fieldInfo.getContentType().getJType(), fieldInfo.getContentName());
408 method.addParameter(parameter);
409
410 JSourceCode sourceCode = method.getSourceCode();
411 this.addMaxSizeCheck(fieldInfo, method.getName(), sourceCode);
412
413 sourceCode.add("this.");
414 sourceCode.append(fieldInfo.getName());
415 sourceCode.append(".add(index, ");
416 sourceCode.append(fieldInfo.getContentType().createToJavaObjectCode(parameter.getName()));
417 sourceCode.append(");");
418
419 if (fieldInfo.isBound()) {
420 this.createBoundPropertyCode(fieldInfo, sourceCode);
421 }
422
423 jClass.addMethod(method);
424 }
425
426
427
428
429
430
431
432
433 protected void createIteratorMethod(final CollectionInfo fieldInfo,
434 final JClass jClass, final boolean useJava50) {
435 JMethod method = new JMethod("iterate" + fieldInfo.getMethodSuffix(),
436 SGTypes.createIterator(fieldInfo.getContentType().getJType(), useJava50, true),
437 "an Iterator over all possible elements in this collection");
438
439 JSourceCode sourceCode = method.getSourceCode();
440 sourceCode.add("return this.");
441 sourceCode.append(fieldInfo.getName());
442 sourceCode.append(".iterator();");
443
444 jClass.addMethod(method);
445 }
446
447
448
449
450
451
452
453 private void createRemoveAllMethod(final CollectionInfo fieldInfo,
454 final JClass jClass) {
455 JMethod method = new JMethod("removeAll" + fieldInfo.getMethodSuffix());
456
457 JSourceCode sourceCode = method.getSourceCode();
458 sourceCode.add("this.");
459 sourceCode.append(fieldInfo.getName());
460 sourceCode.append(".clear();");
461
462 if (fieldInfo.isBound()) {
463 this.createBoundPropertyCode(fieldInfo, sourceCode);
464 }
465
466 jClass.addMethod(method);
467 }
468
469
470
471
472
473
474
475 protected void createRemoveByIndexMethod(final CollectionInfo fieldInfo,
476 final JClass jClass) {
477 JMethod method = new JMethod("remove" + fieldInfo.getMethodSuffix() + "At",
478 fieldInfo.getContentType().getJType(),
479 "the element removed from the collection");
480
481 method.addParameter(new JParameter(JType.INT, "index"));
482
483 JSourceCode sourceCode = method.getSourceCode();
484 sourceCode.add("java.lang.Object obj = this.");
485 sourceCode.append(fieldInfo.getName());
486 sourceCode.append(".remove(index);");
487
488 if (fieldInfo.isBound()) {
489 this.createBoundPropertyCode(fieldInfo, sourceCode);
490 }
491
492 sourceCode.add("return ");
493 if (fieldInfo.getContentType().getType() == XSType.CLASS) {
494 sourceCode.append("(");
495 sourceCode.append(method.getReturnType().getName());
496 sourceCode.append(") obj;");
497 } else {
498 sourceCode.append(fieldInfo.getContentType().createFromJavaObjectCode("obj"));
499 sourceCode.append(";");
500 }
501
502 jClass.addMethod(method);
503 }
504
505
506
507
508
509
510
511 private void createRemoveObjectMethod(final CollectionInfo fieldInfo,
512 final JClass jClass) {
513 JMethod method = new JMethod("remove" + fieldInfo.getMethodSuffix(), JType.BOOLEAN,
514 "true if the object was removed from the collection.");
515
516 final JParameter parameter = new JParameter(fieldInfo.getContentType().getJType(),
517 fieldInfo.getContentName());
518 method.addParameter(parameter);
519
520 JSourceCode sourceCode = method.getSourceCode();
521 sourceCode.add("boolean removed = ");
522 sourceCode.append(fieldInfo.getName());
523 sourceCode.append(".remove(");
524 sourceCode.append(fieldInfo.getContentType().createToJavaObjectCode(parameter.getName()));
525 sourceCode.append(");");
526
527 if (fieldInfo.isBound()) {
528 this.createBoundPropertyCode(fieldInfo, sourceCode);
529 }
530
531 sourceCode.add("return removed;");
532
533 jClass.addMethod(method);
534 }
535
536
537
538
539
540
541
542
543 private void createSetAsArrayMethod(final CollectionInfo fieldInfo,
544 final JClass jClass, final boolean useJava50) {
545 JMethod method = new JMethod("set" + fieldInfo.getMethodSuffix());
546 final JParameter parameter = new JParameter(new JArrayType(
547 fieldInfo.getContentType().getJType(), useJava50),
548 fieldInfo.getContentName() + "Array");
549 method.addParameter(parameter);
550
551 JSourceCode sourceCode = method.getSourceCode();
552 String index = "i";
553 if (parameter.getName().equals(index)) {
554 index = "j";
555 }
556
557 sourceCode.add("//-- copy array");
558 sourceCode.add(fieldInfo.getName());
559 sourceCode.append(".clear();");
560 sourceCode.add("");
561 sourceCode.add("for (int ");
562 sourceCode.append(index);
563 sourceCode.append(" = 0; ");
564 sourceCode.append(index);
565 sourceCode.append(" < ");
566 sourceCode.append(parameter.getName());
567 sourceCode.append(".length; ");
568 sourceCode.append(index);
569 sourceCode.append("++) {");
570 sourceCode.indent();
571 sourceCode.addIndented("this.");
572 sourceCode.append(fieldInfo.getName());
573 sourceCode.append(".add(");
574 sourceCode.append(fieldInfo.getContentType().createToJavaObjectCode(
575 parameter.getName() + "[" + index + "]"));
576 sourceCode.append(");");
577 sourceCode.unindent();
578 sourceCode.add("}");
579
580 if (fieldInfo.isBound()) {
581 this.createBoundPropertyCode(fieldInfo, sourceCode);
582 }
583
584 jClass.addMethod(method);
585 }
586
587
588
589
590
591
592
593 private void createSetAsCopyMethod(final CollectionInfo fieldInfo,
594 final JClass jClass) {
595 JMethod method = new JMethod("set" + fieldInfo.getMethodSuffix());
596 JParameter parameter = new JParameter(fieldInfo.getXSList().getJType(),
597 fieldInfo.getContentName() + "List");
598 method.addParameter(parameter);
599
600
601 JDocComment comment = method.getJDocComment();
602 comment.appendComment("Sets the value of '");
603 comment.appendComment(fieldInfo.getName());
604 comment.appendComment(
605 "' by copying the given Vector. All elements will be checked for type safety.");
606 JDocDescriptor jDesc = comment.getParamDescriptor(parameter.getName());
607 jDesc.setDescription("the Vector to copy.");
608
609
610 JSourceCode sourceCode = method.getSourceCode();
611
612 sourceCode.add("// copy vector");
613 sourceCode.add("this.");
614 sourceCode.append(fieldInfo.getName());
615 sourceCode.append(".clear();");
616 sourceCode.add("");
617
618 sourceCode.add("this.");
619 sourceCode.append(fieldInfo.getName());
620 sourceCode.append(".addAll(");
621 sourceCode.append(parameter.getName());
622 sourceCode.append(");");
623
624 if (fieldInfo.isBound()) {
625 this.createBoundPropertyCode(fieldInfo, sourceCode);
626 }
627
628 jClass.addMethod(method);
629 }
630
631
632
633
634
635
636
637
638 private void createSetAsReferenceMethod(final CollectionInfo fieldInfo,
639 final JClass jClass, final boolean useJava50) {
640 JMethod method = new JMethod("set" + fieldInfo.getMethodSuffix()
641 + fieldInfo.getReferenceSuffix());
642 final JType collectionJType = new XMLInfoNature(fieldInfo).getSchemaType().getJType();
643 JParameter parameter = new JParameter(
644 collectionJType, fieldInfo.getParameterPrefix() + collectionJType.getLocalName());
645 method.addParameter(parameter);
646
647
648 JDocComment comment = method.getJDocComment();
649 comment.appendComment("Sets the value of '");
650 comment.appendComment(fieldInfo.getName());
651 comment.appendComment("' by setting it to the given Vector.");
652 comment.appendComment(" No type checking is performed.");
653 comment.appendComment("\n@deprecated");
654 JDocDescriptor jDesc = comment.getParamDescriptor(parameter.getName());
655 jDesc.setDescription("the Vector to set.");
656
657
658 JSourceCode sourceCode = method.getSourceCode();
659 sourceCode.add("this.");
660 sourceCode.append(fieldInfo.getName());
661 sourceCode.append(" = ");
662 sourceCode.append(parameter.getName());
663 sourceCode.append(";");
664
665 if (fieldInfo.isBound()) {
666 this.createBoundPropertyCode(fieldInfo, sourceCode);
667 }
668
669 jClass.addMethod(method);
670 }
671
672
673
674
675
676
677
678 protected void createSetByIndexMethod(final CollectionInfo fieldInfo,
679 final JClass jClass) {
680 JMethod method = new JMethod("set" + fieldInfo.getMethodSuffix());
681
682 method.addException(SGTypes.INDEX_OUT_OF_BOUNDS_EXCEPTION,
683 "if the index given is outside the bounds of the collection");
684 method.addParameter(new JParameter(JType.INT, "index"));
685 method.addParameter(new JParameter(fieldInfo.getContentType().getJType(),
686 fieldInfo.getContentName()));
687
688 JSourceCode sourceCode = method.getSourceCode();
689 this.addIndexCheck(fieldInfo, sourceCode, method.getName());
690
691 sourceCode.add("this.");
692 sourceCode.append(fieldInfo.getName());
693 sourceCode.append(".set(index, ");
694 sourceCode.append(fieldInfo.getContentType().createToJavaObjectCode(
695 fieldInfo.getContentName()));
696 sourceCode.append(");");
697
698 if (fieldInfo.isBound()) {
699 this.createBoundPropertyCode(fieldInfo, sourceCode);
700 }
701
702 jClass.addMethod(method);
703 }
704
705
706
707
708
709
710
711
712 protected void addMaxSizeCheck(final CollectionInfo fieldInfo,
713 final String methodName, final JSourceCode sourceCode) {
714 if (fieldInfo.getXSList().getMaximumSize() > 0) {
715 final String size = Integer.toString(fieldInfo.getXSList().getMaximumSize());
716
717 sourceCode.add("// check for the maximum size");
718 sourceCode.add("if (this.");
719 sourceCode.append(fieldInfo.getName());
720 sourceCode.append(".size() >= ");
721 sourceCode.append(size);
722 sourceCode.append(") {");
723 sourceCode.indent();
724 sourceCode.add("throw new IndexOutOfBoundsException(\"");
725 sourceCode.append(methodName);
726 sourceCode.append(" has a maximum of ");
727 sourceCode.append(size);
728 sourceCode.append("\");");
729 sourceCode.unindent();
730 sourceCode.add("}");
731 sourceCode.add("");
732 }
733 }
734
735
736
737
738
739
740
741
742 private void addIndexCheck(final CollectionInfo fieldInfo,
743 final JSourceCode sourceCode, final String methodName) {
744 sourceCode.add("// check bounds for index");
745 sourceCode.add("if (index < 0 || index >= this.");
746 sourceCode.append(fieldInfo.getName());
747 sourceCode.append(".size()) {");
748
749 sourceCode.indent();
750 sourceCode.add("throw new IndexOutOfBoundsException(\"");
751 sourceCode.append(methodName);
752 sourceCode.append(": Index value '\" + index + \"' not in range [0..\" + (this.");
753 sourceCode.append(fieldInfo.getName());
754 sourceCode.append(".size() - 1) + \"]\");");
755 sourceCode.unindent();
756 sourceCode.add("}");
757 sourceCode.add("");
758 }
759
760
761 }