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