1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.student.contract.model.impl;
17
18 import com.thoughtworks.qdox.JavaDocBuilder;
19 import com.thoughtworks.qdox.model.Annotation;
20 import com.thoughtworks.qdox.model.DefaultDocletTagFactory;
21 import com.thoughtworks.qdox.model.DocletTag;
22 import com.thoughtworks.qdox.model.JavaClass;
23 import com.thoughtworks.qdox.model.JavaField;
24 import com.thoughtworks.qdox.model.JavaMethod;
25 import com.thoughtworks.qdox.model.JavaParameter;
26 import com.thoughtworks.qdox.model.Type;
27 import com.thoughtworks.qdox.model.annotation.AnnotationValue;
28 import java.io.File;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.Date;
34 import java.util.LinkedHashMap;
35 import java.util.LinkedHashSet;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39
40 import org.kuali.student.contract.model.MessageStructure;
41 import org.kuali.student.contract.model.Service;
42 import org.kuali.student.contract.model.ServiceContractModel;
43 import org.kuali.student.contract.model.ServiceMethod;
44 import org.kuali.student.contract.model.ServiceMethodError;
45 import org.kuali.student.contract.model.ServiceMethodParameter;
46 import org.kuali.student.contract.model.ServiceMethodReturnValue;
47 import org.kuali.student.contract.model.XmlType;
48
49
50
51
52
53 public class ServiceContractModelQDoxLoader implements
54 ServiceContractModel {
55
56 private static final String LOCALE_KEY_LIST = "LocaleKeyList";
57 private static final String MESSAGE_GROUP_KEY_LIST = "MessageGroupKeyList";
58 private static final JavaClass STRING_JAVA_CLASS = new JavaClass(
59 "java.lang.String");
60 private List<String> sourceDirectories = null;
61 private List<Service> services = null;
62 private List<ServiceMethod> serviceMethods = null;
63 private Map<String, XmlType> xmlTypeMap = null;
64 private List<MessageStructure> messageStructures;
65 private boolean validateKualiStudent = true;
66
67 public ServiceContractModelQDoxLoader(List<String> sourceDirectories) {
68 this.sourceDirectories = sourceDirectories;
69 }
70
71 public ServiceContractModelQDoxLoader(List<String> sourceDirectories, boolean validateKualiStudent) {
72 this.sourceDirectories = sourceDirectories;
73 this.setValidateKualiStudent(validateKualiStudent);
74 }
75
76 public boolean isValidateKualiStudent() {
77 return validateKualiStudent;
78 }
79
80 public void setValidateKualiStudent(boolean validateKualiStudent) {
81 this.validateKualiStudent = validateKualiStudent;
82 }
83
84 @Override
85 public List<ServiceMethod> getServiceMethods() {
86 if (this.serviceMethods == null) {
87 this.parse();
88 }
89 return this.serviceMethods;
90 }
91
92 @Override
93 public List<String> getSourceNames() {
94 List<String> list = new ArrayList(this.sourceDirectories.size());
95 for (String javaFile : this.sourceDirectories) {
96 list.add(javaFile);
97 }
98 return list;
99 }
100
101 @Override
102 public List<Service> getServices() {
103 if (services == null) {
104 this.parse();
105 }
106 return services;
107 }
108
109 @Override
110 public List<XmlType> getXmlTypes() {
111 if (xmlTypeMap == null) {
112 this.parse();
113 }
114 return new ArrayList(xmlTypeMap.values());
115 }
116
117 @Override
118 public List<MessageStructure> getMessageStructures() {
119 if (messageStructures == null) {
120 this.parse();
121 }
122 return this.messageStructures;
123 }
124
125 private String dump(DocletTag tag) {
126 if (tag == null) {
127 return null;
128 }
129 StringBuilder bldr = new StringBuilder();
130 bldr.append(tag.getName());
131 bldr.append("=");
132 if (tag.getNamedParameterMap() == null
133 || tag.getNamedParameterMap().isEmpty()) {
134 bldr.append(tag.getValue());
135 } else {
136 for (Object key : tag.getNamedParameterMap().keySet()) {
137 Object value = tag.getNamedParameterMap().get(key);
138 bldr.append("(");
139 bldr.append(key);
140 bldr.append("=");
141 bldr.append(value);
142 bldr.append(")");
143 }
144 }
145 return bldr.toString();
146 }
147
148 private void checkIfExists(String sourceDirectory) {
149 File file = new File(sourceDirectory);
150 if (!file.isDirectory()) {
151 throw new IllegalArgumentException(sourceDirectory + " is not a directory on disk");
152 }
153 }
154
155 private void parse() {
156
157 services = new ArrayList();
158 serviceMethods = new ArrayList();
159 xmlTypeMap = new LinkedHashMap();
160 messageStructures = new ArrayList();
161 DefaultDocletTagFactory dtf = new DefaultDocletTagFactory();
162 JavaDocBuilder builder = new JavaDocBuilder(dtf);
163 for (String sourceDirectory : sourceDirectories) {
164 checkIfExists(sourceDirectory);
165 builder.addSourceTree(new File(sourceDirectory));
166 }
167 List<JavaClass> sortedClasses = Arrays.asList(builder.getClasses());
168 Collections.sort(sortedClasses);
169 for (JavaClass javaClass : sortedClasses) {
170 if (!this.isServiceToProcess(javaClass)) {
171 continue;
172 }
173
174 Service service = new Service();
175 services.add(service);
176 service.setKey(javaClass.getName().substring(0, javaClass.getName().length()
177 - "Service".length()));
178 service.setName(javaClass.getName());
179 service.setComments(this.calcComment(javaClass));
180 service.setUrl(this.calcServiceUrl(javaClass));
181 service.setVersion(this.calcVersion(javaClass));
182 service.setStatus("???");
183 service.setIncludedServices(calcIncludedServices(javaClass));
184 service.setImplProject(javaClass.getPackageName());
185
186
187
188
189
190
191
192 for (JavaMethod javaMethod : javaClass.getMethods()) {
193
194 ServiceMethod serviceMethod = new ServiceMethod();
195 serviceMethods.add(serviceMethod);
196 serviceMethod.setService(service.getKey());
197 serviceMethod.setName(javaMethod.getName());
198 serviceMethod.setDescription(calcMissing(javaMethod.getComment()));
199 serviceMethod.setParameters(new ArrayList());
200
201
202
203
204
205
206
207
208 for (JavaParameter parameter : javaMethod.getParameters()) {
209 ServiceMethodParameter param = new ServiceMethodParameter();
210 serviceMethod.getParameters().add(param);
211 param.setName(parameter.getName());
212 param.setType(calcType(parameter.getType()));
213 param.setDescription(calcMissing(
214 calcParameterDescription(javaMethod,
215 param.getName())));
216 addXmlTypeAndMessageStructure(calcRealJavaClass(parameter.getType()),
217 serviceMethod.getService());
218 }
219
220 serviceMethod.setErrors(new ArrayList());
221 for (Type exception : javaMethod.getExceptions()) {
222 ServiceMethodError error = new ServiceMethodError();
223 error.setType(this.calcType(exception.getJavaClass()));
224 error.setDescription(calcMissing(
225 calcExceptionDescription(javaMethod,
226 error.getType())));
227 error.setPackageName(exception.getJavaClass().getPackageName());
228 error.setClassName(exception.getJavaClass().getName());
229 serviceMethod.getErrors().add(error);
230 }
231
232 ServiceMethodReturnValue rv = new ServiceMethodReturnValue();
233 serviceMethod.setReturnValue(rv);
234 Type returnType = null;
235 try {
236 returnType = javaMethod.getReturnType();
237 } catch (NullPointerException ex) {
238 System.out.println("Nullpinter getting return type: " + javaMethod.getCallSignature());
239 returnType = null;
240 }
241
242 rv.setType(calcType(returnType));
243 rv.setDescription(calcMissing(this.calcReturnDescription(javaMethod)));
244 if (returnType != null) {
245 addXmlTypeAndMessageStructure(calcRealJavaClass(returnType),
246 serviceMethod.getService());
247 }
248 }
249 }
250 }
251
252 private boolean isServiceToProcess(JavaClass javaClass) {
253
254 if (!javaClass.getName().endsWith("Service")) {
255 return false;
256 }
257 if (javaClass.getPackageName().contains(".old.")) {
258
259 return false;
260 }
261 if (javaClass.getPackageName().endsWith(".old")) {
262 return false;
263 }
264 for (Annotation annotation : javaClass.getAnnotations()) {
265
266 if (annotation.getType().getJavaClass().getName().equals("WebService")) {
267
268
269 return true;
270 }
271 }
272
273
274
275 if (javaClass.getName().endsWith("BoService")) {
276 return true;
277 }
278
279
280 return false;
281 }
282
283 private List<String> calcIncludedServices(JavaClass javaClass) {
284 List<String> includedServices = new ArrayList<String>();
285 for (JavaClass interfaceClass : javaClass.getImplementedInterfaces()) {
286
287
288 includedServices.add(interfaceClass.getName());
289 }
290 return includedServices;
291 }
292
293 private String calcParameterDescription(JavaMethod method,
294 String parameterName) {
295 for (DocletTag tag : method.getTags()) {
296 if (tag.getName().equals("param")) {
297 if (tag.getValue().startsWith(parameterName + " ")) {
298 return tag.getValue().substring(parameterName.length() + 1);
299 }
300 }
301 }
302 return null;
303 }
304
305 private String calcExceptionDescription(JavaMethod serviceMethod,
306 String exceptionType) {
307 for (DocletTag tag : serviceMethod.getTags()) {
308 if (tag.getName().equals("throws")) {
309 if (tag.getValue().startsWith(exceptionType + " ")) {
310 return tag.getValue().substring(exceptionType.length() + 1);
311 }
312 }
313 }
314 return null;
315 }
316
317 private String calcReturnDescription(JavaMethod serviceMethod) {
318 for (DocletTag tag : serviceMethod.getTags()) {
319 if (tag.getName().equals("return")) {
320 return tag.getValue();
321 }
322 }
323 return null;
324 }
325
326 private String calcServiceUrl(JavaClass serviceClass) {
327 for (DocletTag tag : serviceClass.getTags()) {
328 if (tag.getName().equals("See")) {
329 return tag.getValue();
330 }
331 }
332 return null;
333 }
334
335 private void addXmlTypeAndMessageStructure(JavaClass messageStructureJavaClass,
336 String serviceKey) {
337 String name = calcType(messageStructureJavaClass);
338 XmlType xmlType = xmlTypeMap.get(name);
339 if (xmlType == null) {
340 xmlType = new XmlType();
341 xmlTypeMap.put(name, xmlType);
342 xmlType.setName(name);
343 xmlType.setDesc(this.calcMessageStructureDesc(messageStructureJavaClass));
344 xmlType.setService(serviceKey);
345 xmlType.setVersion("IGNORE -- SAME AS SERVICE");
346 xmlType.setPrimitive(calcPrimitive(messageStructureJavaClass));
347 xmlType.setJavaPackage(calcJavaPackage(messageStructureJavaClass));
348 if (xmlType.getPrimitive().equals(XmlType.COMPLEX)) {
349 addMessageStructure(messageStructureJavaClass, serviceKey);
350 }
351 } else {
352 addServiceToList(xmlType, serviceKey);
353 }
354 }
355
356 private String calcJavaPackage(JavaClass javaClass) {
357 String packageName = javaClass.getPackageName();
358 return packageName;
359 }
360
361 private String calcMessageStructureDesc(JavaClass javaClass) {
362 {
363 String desc = javaClass.getComment();
364 if (desc != null) {
365 if (!desc.isEmpty()) {
366 return desc;
367 }
368 }
369 JavaClass infcClass = this.getMatchingInfc(javaClass);
370 if (infcClass == null) {
371 return null;
372 }
373 return infcClass.getComment();
374 }
375 }
376
377 private JavaClass getMatchingInfc(JavaClass javaClass) {
378
379 String nameInfc = javaClass.getName();
380 if (nameInfc.endsWith("Info")) {
381 nameInfc = nameInfc.substring(0, nameInfc.length() - "Info".length())
382 + "Infc";
383 }
384 String nameWithOutInfo = javaClass.getName();
385
386 if (nameWithOutInfo.endsWith("Info")) {
387 nameWithOutInfo = nameWithOutInfo.substring(0, nameWithOutInfo.length()
388 - "Info".length());
389 }
390 for (JavaClass infc : javaClass.getImplementedInterfaces()) {
391 if (infc.getName().equals(nameInfc)) {
392
393 return infc;
394 }
395 if (infc.getName().equals(nameWithOutInfo)) {
396 return infc;
397 }
398 }
399 return null;
400 }
401
402 private String calcPrimitive(JavaClass javaClass) {
403 if (this.isComplex(javaClass)) {
404 return XmlType.COMPLEX;
405 }
406 return "Primitive";
407 }
408
409 private String initLower(String str) {
410 if (str == null) {
411 return null;
412 }
413 if (str.length() == 0) {
414 return str;
415 }
416 if (str.length() == 1) {
417 return str.toLowerCase();
418 }
419 return str.substring(0, 1).toLowerCase() + str.substring(1);
420 }
421
422 private String initUpper(String str) {
423 if (str == null) {
424 return null;
425 }
426 if (str.length() == 0) {
427 return str;
428 }
429 if (str.length() == 1) {
430 return str.toUpperCase();
431 }
432 return str.substring(0, 1).toUpperCase() + str.substring(1);
433 }
434
435 private Set<String> getShortNames(JavaClass messageStructureJavaClass) {
436 Set<String> fields = getFieldsUsingPropOrder(messageStructureJavaClass);
437 if (fields != null) {
438 return fields;
439 }
440 fields = new LinkedHashSet();
441 for (JavaMethod method : messageStructureJavaClass.getMethods(true)) {
442 if (isSetterMethodToProcess(method, messageStructureJavaClass.getName())) {
443 String shortName = this.calcShortNameFromSetter(method);
444 fields.add(shortName);
445 continue;
446 }
447 if (isGetterMethodToProcess(method, messageStructureJavaClass.getName())) {
448 String shortName = this.calcShortNameFromGetter(method);
449 fields.add(shortName);
450 continue;
451 }
452 }
453 return fields;
454 }
455
456 private Set<String> getFieldsUsingPropOrder(
457 JavaClass messageStructureJavaClass) {
458 for (Annotation annotation : messageStructureJavaClass.getAnnotations()) {
459 if (annotation.getType().getJavaClass().getName().equals("XmlType")) {
460 AnnotationValue propOrderParam = annotation.getProperty("propOrder");
461 if (propOrderParam == null) {
462 continue;
463 }
464 Object propOrderValue = propOrderParam.getParameterValue();
465 if (!(propOrderValue instanceof List)) {
466 continue;
467 }
468 Set<String> fields = new LinkedHashSet();
469 for (Object value : (List) propOrderValue) {
470 if (value instanceof String) {
471 String shortName = (String) value;
472 shortName = this.stripQuotes(shortName);
473 if (shortName.contains(".Elements.")) {
474 String newShortName = getShortNameFromElements(shortName, messageStructureJavaClass);
475 if (newShortName == null) {
476 continue;
477 }
478 shortName = newShortName;
479 } else if (shortName.startsWith("CoreConstants.CommonElements.")) {
480 String newShortName = getCoreConstants(shortName);
481 if (newShortName == null) {
482 continue;
483 }
484 shortName = newShortName;
485 }
486 if (shortName.equals("_futureElements")) {
487 continue;
488 }
489 shortName = this.initUpper(shortName);
490 fields.add(shortName);
491 }
492 }
493 return fields;
494 }
495 }
496 return null;
497 }
498
499 private String getShortNameFromElements(String shortName, JavaClass messageStructureJavaClass) {
500 JavaClass elementsJavaClass = messageStructureJavaClass.getNestedClassByName("Elements");
501 if (elementsJavaClass == null) {
502 return null;
503 }
504 String fieldName = shortName.substring(shortName.indexOf(".Elements.") + ".Elements.".length());
505 JavaField field = elementsJavaClass.getFieldByName(fieldName);
506 String initExpr = field.getInitializationExpression();
507 return stripQuotes(initExpr);
508 }
509
510 private String getCoreConstants(String shortName) {
511 if (shortName.endsWith("VERSION_NUMBER")) {
512 return "versionNumber";
513 }
514 if (shortName.endsWith("OBJECT_ID")) {
515 return "objectId";
516 }
517 if (shortName.endsWith("ACTIVE")) {
518 return "active";
519 }
520 if (shortName.endsWith("ACTIVE_FROM_DATE")) {
521 return "activeFromDate";
522 }
523 if (shortName.endsWith("ACTIVE_TO_DATE")) {
524 return "activeToDate";
525 }
526 if (shortName.endsWith("ATTRIBUTES")) {
527 return "attributes";
528 }
529 if (shortName.endsWith("FUTURE_ELEMENTS")) {
530 return "_futureElements";
531 }
532 throw new RuntimeException("Unknown shortName " + shortName);
533 }
534
535 private void addMessageStructure(JavaClass messageStructureJavaClass,
536 String serviceKey) {
537 Set<JavaClass> subObjectsToAdd = new LinkedHashSet();
538 for (String shortName : this.getShortNames(messageStructureJavaClass)) {
539 JavaMethod setterMethod = findSetterMethod(messageStructureJavaClass,
540 shortName);
541 JavaMethod getterMethod = findGetterMethod(messageStructureJavaClass,
542 shortName);
543 if (getterMethod == null) {
544 if (this.validateKualiStudent) {
545 throw new IllegalArgumentException("shortName has no corresponding getter method: "
546 + messageStructureJavaClass.getFullyQualifiedName()
547 + "." + shortName);
548 }
549 }
550 JavaField beanField = this.findField(messageStructureJavaClass,
551 shortName, setterMethod);
552 if (beanField == null) {
553 String accessorType = getAccessorType(getterMethod);
554 if ("XmlAccessType.FIELD".equals(accessorType)) {
555 throw new IllegalArgumentException("Setter method has no corresponding bean field: "
556 + messageStructureJavaClass.getName()
557 + "." + getterMethod.getName());
558 }
559 }
560
561
562 if (beanField != null) {
563 for (Annotation annotation : beanField.getAnnotations()) {
564 if (annotation.getType().getJavaClass().getName().equals("XmlAttribute")) {
565 Object nameValue = annotation.getNamedParameter("name");
566 if (nameValue != null) {
567 shortName = stripQuotes(nameValue.toString());
568 }
569 }
570 }
571 }
572 shortName = initLower(shortName);
573 MessageStructure ms = new MessageStructure();
574 messageStructures.add(ms);
575 ms.setXmlObject(messageStructureJavaClass.getName());
576 ms.setShortName(shortName);
577 ms.setId(ms.getXmlObject() + "." + ms.getShortName());
578 ms.setName(calcMissing(calcName(messageStructureJavaClass, getterMethod, setterMethod,
579 beanField, shortName)));
580 ms.setType(calcType(messageStructureJavaClass, getterMethod, setterMethod, beanField, shortName));
581 if (ms.getType().equals("Object")) {
582 System.out.println("WARNING " + ms.getId()
583 + " has Object as it's type ==> Changing to String");
584 ms.setType("String");
585 } else if (ms.getType().equals("ObjectList")) {
586 System.out.println(
587 "WARNING " + ms.getId()
588 + " has a list of Objects as it's type ==> Changing to List of String");
589 ms.setType("StringList");
590 }
591 ms.setXmlAttribute(this.calcXmlAttribute(beanField));
592 ms.setRequired(calcRequired(getterMethod, setterMethod, beanField));
593 ms.setReadOnly(calcReadOnly(getterMethod, setterMethod, beanField));
594 ms.setCardinality(this.calcCardinality(messageStructureJavaClass, getterMethod, setterMethod, beanField, shortName));
595 ms.setDescription(calcMissing(calcDescription(messageStructureJavaClass, getterMethod, setterMethod,
596 beanField)));
597 ms.setImplNotes(calcImplementationNotes(getterMethod, setterMethod, beanField));
598 ms.setStatus("???");
599
600
601
602 ms.setOverriden(this.calcOverridden(messageStructureJavaClass, getterMethod));
603 JavaClass subObjToAdd = this.calcRealJavaClassOfGetterReturn(getterMethod);
604 if (subObjToAdd != null) {
605
606 if (!subObjToAdd.getName().equals("Object")) {
607 if (!subObjToAdd.getName().equals("LocaleKeyList")) {
608 if (!subObjToAdd.getName().equals("MessageGroupKeyList")) {
609 subObjectsToAdd.add(subObjToAdd);
610 }
611 }
612 }
613
614 }
615 }
616
617 for (JavaClass subObjectToAdd : subObjectsToAdd) {
618 XmlType xmlType = xmlTypeMap.get(calcType(subObjectToAdd));
619 if (xmlType == null) {
620 addXmlTypeAndMessageStructure(subObjectToAdd, serviceKey);
621 } else {
622 addServiceToList(xmlType, serviceKey);
623 }
624 }
625 return;
626 }
627
628 private boolean calcOverridden(JavaClass mainClass, JavaMethod getterMethod) {
629 if (getterMethod == null) {
630 return false;
631 }
632 JavaMethod infcGetter = null;
633 if (getterMethod.getParentClass().isInterface()) {
634 infcGetter = getterMethod;
635 }
636 if (infcGetter == null) {
637 infcGetter = findInterfaceMethod(mainClass, getterMethod);
638 }
639 if (infcGetter == null) {
640 return false;
641 }
642 Annotation annotation = this.getAnnotation(infcGetter, null, null, "Override");
643 if (annotation != null) {
644 return true;
645 }
646 return false;
647 }
648
649 private String calcComment(JavaClass javaClass) {
650 return this.calcComment(javaClass.getComment());
651 }
652
653 private String calcComment(String comment) {
654 return this.parseCommentVersion(comment)[0];
655 }
656
657 private String calcVersion(JavaClass javaClass) {
658 DocletTag tag = javaClass.getTagByName("version", true);
659 if (tag != null) {
660 return tag.getValue();
661 }
662 return this.calcVersion(javaClass.getComment());
663 }
664
665 private String calcVersion(String comment) {
666 return this.parseCommentVersion(comment)[1];
667 }
668
669 private String[] parseCommentVersion(String commentVersion) {
670 String[] parsed = new String[2];
671 if (commentVersion == null) {
672 return parsed;
673 }
674 commentVersion = commentVersion.trim();
675 int i = commentVersion.toLowerCase().indexOf("\nversion:");
676 if (i == -1) {
677 parsed[0] = commentVersion;
678 return parsed;
679 }
680 parsed[1] = commentVersion.substring(i + "\nversion:".length()).trim();
681 parsed[0] = commentVersion.substring(0, i).trim();
682
683 return parsed;
684 }
685
686 private Annotation getAnnotation(JavaMethod getterMethod,
687 JavaMethod setterMethod, JavaField beanField, String type) {
688 if (beanField != null) {
689
690 for (Annotation annotation : beanField.getAnnotations()) {
691 if (annotation.getType().getJavaClass().getName().equals(type)) {
692 return annotation;
693 }
694 }
695 }
696 if (getterMethod != null) {
697
698 for (Annotation annotation : getterMethod.getAnnotations()) {
699 if (annotation.getType().getJavaClass().getName().equals(type)) {
700 return annotation;
701 }
702 }
703 }
704 if (setterMethod != null) {
705
706 for (Annotation annotation : setterMethod.getAnnotations()) {
707 if (annotation.getType().getJavaClass().getName().equals(type)) {
708 return annotation;
709 }
710 }
711 }
712 return null;
713 }
714
715 private String calcRequired(JavaMethod getterMethod,
716 JavaMethod setterMethod, JavaField beanField) {
717 Annotation annotation = this.getAnnotation(getterMethod, setterMethod, beanField, "XmlElement");
718 if (annotation == null) {
719 annotation = this.getAnnotation(getterMethod, setterMethod, beanField, "XmlAttribute");
720 }
721 if (annotation != null) {
722 Object required = annotation.getNamedParameter("required");
723 if (required != null) {
724 if (required.toString().equalsIgnoreCase("true")) {
725 return "Required";
726 }
727 }
728 }
729 if (getterMethod != null) {
730 DocletTag tag = getterMethod.getTagByName("required", true);
731 if (tag != null) {
732 if (tag.getValue() == null) {
733 return "Required";
734 }
735 String required = "Required " + tag.getValue();
736 return required.trim();
737 }
738 }
739 return null;
740 }
741
742 private String calcReadOnly(JavaMethod getterMethod,
743 JavaMethod setterMethod, JavaField beanField) {
744 if (getterMethod != null) {
745 DocletTag tag = getterMethod.getTagByName("readOnly", true);
746 if (tag != null) {
747 if (tag.getValue() == null) {
748 return "Read only";
749 }
750 String readOnly = "Read only " + tag.getValue();
751 return readOnly.trim();
752 }
753 }
754 return null;
755 }
756
757 private String calcImplementationNotes(JavaMethod getterMethod,
758 JavaMethod setterMethod, JavaField beanField) {
759 if (getterMethod != null) {
760 DocletTag tag = getterMethod.getTagByName("impl", true);
761 if (tag != null) {
762 return tag.getValue();
763 }
764 }
765 return null;
766 }
767
768 private String calcNameFromShortName(String shortName) {
769 StringBuilder bldr = new StringBuilder(shortName.length() + 3);
770 char c = shortName.charAt(0);
771 bldr.append(Character.toUpperCase(c));
772 boolean lastWasUpper = true;
773 for (int i = 1; i < shortName.length(); i++) {
774 c = shortName.charAt(i);
775 if (Character.isUpperCase(c)) {
776 if (!lastWasUpper) {
777 bldr.append(" ");
778 }
779 } else {
780 lastWasUpper = false;
781 }
782 bldr.append(c);
783 }
784 return bldr.toString();
785 }
786
787 private String calcName(JavaClass mainClass, JavaMethod getterMethod,
788 JavaMethod setterMethod, JavaField beanField, String shortName) {
789 String name = this.calcNameFromTag(getterMethod, setterMethod, beanField);
790 if (name != null) {
791 return name;
792 }
793 name = this.calcNameFromNameEmbeddedInDescription(mainClass, getterMethod, setterMethod, beanField);
794 if (name != null) {
795 return name;
796 }
797 return this.calcNameFromShortName(shortName);
798 }
799
800 private String calcNameFromTag(JavaMethod getterMethod,
801 JavaMethod setterMethod, JavaField beanField) {
802 if (getterMethod != null) {
803 DocletTag tag = getterMethod.getTagByName("name", true);
804 if (tag != null) {
805 return tag.getValue();
806 }
807 }
808 return null;
809 }
810
811 private String calcNameFromNameEmbeddedInDescription(JavaClass mainClass, JavaMethod getterMethod,
812 JavaMethod setterMethod, JavaField beanField) {
813 String nameDesc = this.calcMethodComment(mainClass, getterMethod, setterMethod,
814 beanField);
815 String[] parsed = parseNameDesc(nameDesc);
816 return parsed[0];
817 }
818
819 private String[] parseNameDesc(String nameDesc) {
820 String[] parsed = new String[2];
821 if (nameDesc == null) {
822 return parsed;
823 }
824 nameDesc = nameDesc.trim();
825 if (!nameDesc.startsWith("Name:")) {
826 parsed[1] = nameDesc;
827 return parsed;
828 }
829 nameDesc = nameDesc.substring("Name:".length()).trim();
830 int i = nameDesc.indexOf("\n");
831 if (i == -1) {
832 parsed[0] = nameDesc.trim();
833 return parsed;
834 }
835 parsed[0] = nameDesc.substring(0, i).trim();
836 parsed[1] = nameDesc.substring(i).trim();
837 return parsed;
838 }
839
840 private String calcDescription(JavaClass mainClass, JavaMethod getterMethod,
841 JavaMethod setterMethod, JavaField beanField) {
842 String nameDesc = this.calcMethodComment(mainClass, getterMethod, setterMethod,
843 beanField);
844 String[] parsed = parseNameDesc(nameDesc);
845 return parsed[1];
846 }
847
848 private String calcMethodComment(JavaClass mainClass, JavaMethod getterMethod,
849 JavaMethod setterMethod,
850 JavaField beanField) {
851 String desc = null;
852 if (getterMethod != null) {
853 desc = getterMethod.getComment();
854 if (isCommentNotEmpty(desc)) {
855 return desc;
856 }
857 }
858 if (setterMethod != null) {
859 desc = setterMethod.getComment();
860 if (isCommentNotEmpty(desc)) {
861 return desc;
862 }
863 }
864 if (beanField != null) {
865 desc = beanField.getComment();
866 if (isCommentNotEmpty(desc)) {
867 return desc;
868 }
869 }
870 desc = calcMethodCommentRecursively(mainClass, getterMethod);
871 if (isCommentNotEmpty(desc)) {
872 return desc;
873 }
874 desc = calcMethodCommentRecursively(mainClass, setterMethod);
875 if (isCommentNotEmpty(desc)) {
876 return desc;
877 }
878 return null;
879 }
880
881 private String calcMethodCommentRecursively(JavaClass mainClass, JavaMethod method) {
882 if (method == null) {
883 return null;
884 }
885 String desc = method.getComment();
886 if (isCommentNotEmpty(desc)) {
887 return desc;
888 }
889 JavaMethod infcMethod = findInterfaceMethod(mainClass, method);
890 if (infcMethod != null) {
891 desc = infcMethod.getComment();
892 if (isCommentNotEmpty(desc)) {
893 return desc;
894 }
895 }
896 JavaMethod superMethod = findSuperMethod(method);
897 if (superMethod != null) {
898 desc = superMethod.getComment();
899 if (isCommentNotEmpty(desc)) {
900 return desc;
901 }
902 }
903 return null;
904 }
905
906 private JavaMethod findSuperMethod(JavaMethod method) {
907
908
909
910 for (JavaMethod superMethod : method.getParentClass().getMethods(true)) {
911 if (method.equals(superMethod)) {
912 continue;
913 }
914 if (method.getCallSignature().equals(superMethod.getCallSignature())) {
915 return superMethod;
916 }
917 }
918 return null;
919 }
920
921 private JavaMethod findInterfaceMethod(JavaClass mainClass, JavaMethod method) {
922 String callSig = method.getCallSignature();
923 JavaClass classToSearch = mainClass;
924
925 while (true) {
926 for (JavaClass infcClass : classToSearch.getImplementedInterfaces()) {
927 JavaMethod meth = this.findMethodOnInterfaceRecursively(infcClass, callSig);
928 if (meth != null) {
929
930 return meth;
931 }
932 }
933 JavaClass superClass = classToSearch.getSuperJavaClass();
934 if (superClass == null) {
935
936
937 return null;
938 }
939 classToSearch = superClass;
940
941 }
942 }
943
944
945
946
947
948
949
950
951
952
953 private JavaMethod findMethodOnInterfaceRecursively(JavaClass infcClass, String callSig) {
954
955
956 for (JavaMethod infcMethod : infcClass.getMethods()) {
957 if (callSig.equals(infcMethod.getCallSignature())) {
958
959
960 return infcMethod;
961 }
962 }
963 for (JavaClass subInfc : infcClass.getImplementedInterfaces()) {
964
965 JavaMethod infcMethod = findMethodOnInterfaceRecursively(subInfc, callSig);
966 if (infcMethod != null) {
967
968 return infcMethod;
969 }
970 }
971
972
973 return null;
974 }
975
976 private boolean isCommentNotEmpty(String desc) {
977 if (desc == null) {
978 return false;
979 }
980 if (desc.trim().isEmpty()) {
981 return false;
982 }
983 if (desc.contains("@inheritDoc")) {
984 return false;
985 }
986 return true;
987 }
988
989 private String getAccessorType(JavaMethod method) {
990 String accessorType = getAccessorType(method.getAnnotations());
991 if (accessorType != null) {
992 return accessorType;
993 }
994 accessorType = getAccessorType(method.getParentClass().getAnnotations());
995 return accessorType;
996 }
997
998 private String getAccessorType(Annotation[] annotations) {
999 for (Annotation annotation : annotations) {
1000 if (annotation.getType().getJavaClass().getName().equals(
1001 "XmlAccessorType")) {
1002
1003
1004 return annotation.getParameterValue().toString();
1005 }
1006 }
1007 return null;
1008 }
1009
1010 private String stripQuotes(String str) {
1011 if (str.startsWith("\"")) {
1012 str = str.substring(1);
1013 }
1014 if (str.endsWith("\"")) {
1015 str = str.substring(0, str.length() - 1);
1016 }
1017 return str;
1018 }
1019
1020 private String calcMissing(String str) {
1021 if (str == null) {
1022 return "???";
1023 }
1024 if (str.trim().isEmpty()) {
1025 return "???";
1026 }
1027 return str;
1028 }
1029
1030 private void addServiceToList(XmlType xmlType, String serviceKey) {
1031 if (!xmlType.getService().contains(serviceKey)) {
1032 xmlType.setService(xmlType.getService() + ", " + serviceKey);
1033 }
1034 }
1035
1036 private String calcXmlAttribute(JavaField beanField) {
1037 if (beanField == null) {
1038
1039 return "No";
1040 }
1041 for (Annotation annotation : beanField.getAnnotations()) {
1042 if (annotation.getType().getJavaClass().getName().equals("XmlAttribute")) {
1043 return "Yes";
1044 }
1045 }
1046 return "No";
1047 }
1048
1049 private JavaField findField(JavaClass javaClass, String shortName,
1050 JavaMethod setterMethod) {
1051 JavaField field = findField(javaClass, shortName);
1052 if (field != null) {
1053 return field;
1054 }
1055 if (setterMethod != null) {
1056 String paramName = setterMethod.getParameters()[0].getName();
1057 if (paramName.equalsIgnoreCase(shortName)) {
1058 return null;
1059 }
1060 return findField(javaClass, paramName);
1061 }
1062 return null;
1063 }
1064
1065 private JavaField findField(JavaClass javaClass, String name) {
1066 if (name == null) {
1067 return null;
1068 }
1069 for (JavaField field : javaClass.getFields()) {
1070 if (field.getName().equalsIgnoreCase(name)) {
1071 return field;
1072 }
1073
1074 if (field.getName().equals("is" + name)) {
1075 return field;
1076 }
1077 }
1078 JavaClass superClass = javaClass.getSuperJavaClass();
1079 if (superClass == null) {
1080 return null;
1081 }
1082 return findField(superClass, name);
1083 }
1084
1085 private JavaMethod findGetterMethod(JavaClass msClass, String shortName) {
1086 for (JavaMethod method : msClass.getMethods(true)) {
1087 if (method.getName().equalsIgnoreCase("get" + shortName)) {
1088 return method;
1089 }
1090 if (method.getName().toLowerCase().startsWith("is")) {
1091 if (method.getName().equalsIgnoreCase("is" + shortName)) {
1092 return method;
1093 }
1094
1095 if (method.getName().equalsIgnoreCase(shortName)) {
1096 return method;
1097 }
1098 }
1099
1100 if (method.getName().equalsIgnoreCase("getInState") && shortName.equalsIgnoreCase(
1101 "InStateFlag")) {
1102 return method;
1103 }
1104 }
1105 return null;
1106 }
1107
1108 private JavaMethod findSetterMethod(JavaClass msClass, String shortName) {
1109 for (JavaMethod method : msClass.getMethods(true)) {
1110 if (method.getName().equals("set" + shortName)) {
1111 return method;
1112 }
1113
1114 if (method.getName().equals("setIs" + shortName)) {
1115 return method;
1116 }
1117
1118 if (method.getName().equals("setInStateFlag") && shortName.equals(
1119 "InState")) {
1120 return method;
1121 }
1122 }
1123 return null;
1124 }
1125 private static final String[] SETTER_METHODS_TO_SKIP = {
1126
1127 "ValidationResultInfo.setWarning",
1128 "ValidationResultInfo.setError",
1129
1130 "CredentialProgramInfo.setDiplomaTitle",
1131
1132 "CredentialProgramInfo.setType",
1133
1134 "CredentialProgramInfo.setHegisCode",
1135 "CredentialProgramInfo.setCip2000Code",
1136 "CredentialProgramInfo.setCip2010Code",
1137 "CredentialProgramInfo.setSelectiveEnrollmentCode",
1138 "CoreProgramInfo.setDiplomaTitle",
1139
1140
1141
1142 "CoreProgramInfo.setHegisCode",
1143 "CoreProgramInfo.setCip2000Code",
1144 "CoreProgramInfo.setCip2010Code",
1145 "CoreProgramInfo.setSelectiveEnrollmentCode",
1146 "WhenConstraint.setValue"
1147 };
1148 private static final String[] GETTER_METHODS_TO_SKIP = {
1149
1150 "ValidationResultInfo.getWarning",
1151 "ValidationResultInfo.getError",
1152
1153 "CredentialProgramInfo.getDiplomaTitle",
1154
1155 "CredentialProgramInfo.getType",
1156
1157 "CredentialProgramInfo.getHegisCode",
1158 "CredentialProgramInfo.getCip2000Code",
1159 "CredentialProgramInfo.getCip2010Code",
1160 "CredentialProgramInfo.getSelectiveEnrollmentCode",
1161 "CoreProgramInfo.getDiplomaTitle",
1162
1163
1164
1165 "CoreProgramInfo.getHegisCode",
1166 "CoreProgramInfo.getCip2000Code",
1167 "CoreProgramInfo.getCip2010Code",
1168 "CoreProgramInfo.getSelectiveEnrollmentCode",
1169 "WhenConstraint.getValue"
1170 };
1171
1172 private boolean isSetterMethodToProcess(JavaMethod method, String className) {
1173 if (!method.getName().startsWith("set")) {
1174 return false;
1175 }
1176 if (method.getParameters().length != 1) {
1177 return false;
1178 }
1179 if (method.isPrivate()) {
1180 return false;
1181 }
1182 if (method.isProtected()) {
1183 return false;
1184 }
1185 if (method.isStatic()) {
1186 return false;
1187 }
1188 if (method.getParentClass().getPackageName().startsWith("java")) {
1189 return false;
1190 }
1191 String fullName = className + "." + method.getName();
1192 for (String skip : SETTER_METHODS_TO_SKIP) {
1193 if (skip.equals(fullName)) {
1194 return false;
1195 }
1196 }
1197
1198
1199
1200
1201 for (Annotation annotation : method.getAnnotations()) {
1202 if (annotation.getType().getJavaClass().getName().equals("XmlTransient")) {
1203 return false;
1204 }
1205 }
1206 return true;
1207 }
1208
1209 private boolean isGetterMethodToProcess(JavaMethod method, String className) {
1210 if (!method.getName().startsWith("get")) {
1211 if (!method.getName().startsWith("is")) {
1212 return false;
1213 }
1214 }
1215 if (method.getParameters().length != 0) {
1216 return false;
1217 }
1218 if (method.isPrivate()) {
1219 return false;
1220 }
1221 if (method.isProtected()) {
1222 return false;
1223 }
1224 if (method.isStatic()) {
1225 return false;
1226 }
1227 if (method.getParentClass().getPackageName().startsWith("java")) {
1228 return false;
1229 }
1230 String fullName = className + "." + method.getName();
1231 for (String skip : GETTER_METHODS_TO_SKIP) {
1232 if (skip.equals(fullName)) {
1233 return false;
1234 }
1235 }
1236
1237
1238
1239
1240 for (Annotation annotation : method.getAnnotations()) {
1241 if (annotation.getType().getJavaClass().getName().equals("XmlTransient")) {
1242 return false;
1243 }
1244 }
1245 return true;
1246 }
1247
1248 private String calcShortNameFromSetter(JavaMethod method) {
1249 return method.getName().substring(3);
1250 }
1251
1252 private String calcShortNameFromGetter(JavaMethod method) {
1253 if (method.getName().startsWith("get")) {
1254 return method.getName().substring(3);
1255 }
1256 if (method.getName().startsWith("is")) {
1257 return method.getName().substring(2);
1258 }
1259 throw new IllegalArgumentException(method.getName()
1260 + " does not start with is or get");
1261 }
1262
1263 private String calcCardinality(JavaClass mainClass, JavaMethod getterMethod,
1264 JavaMethod setterMethod, JavaField beanField, String shortName) {
1265 if (isReturnACollection(mainClass, getterMethod, setterMethod, beanField, shortName)) {
1266 return "Many";
1267 }
1268 return "One";
1269 }
1270
1271 private boolean isReturnACollection(JavaClass mainClass, JavaMethod getterMethod,
1272 JavaMethod setterMethod, JavaField beanField, String shortName) {
1273 if (getterMethod != null) {
1274 return isCollection(getterMethod.getReturnType());
1275 }
1276 if (beanField != null) {
1277 return isCollection(beanField.getType());
1278 }
1279
1280 return false;
1281 }
1282
1283 private boolean isCollection(Type type) {
1284 JavaClass javaClass = type.getJavaClass();
1285 return this.isCollection(javaClass);
1286 }
1287
1288 private boolean isCollection(JavaClass javaClass) {
1289 if (javaClass.getName().equals("LocalKeyList")) {
1290 return true;
1291 }
1292 if (javaClass.getName().equals("MessageGroupKeyList")) {
1293 return true;
1294 }
1295 if (javaClass.getName().equals(List.class.getSimpleName())) {
1296 return true;
1297 }
1298 if (javaClass.getName().equals(ArrayList.class.getSimpleName())) {
1299 return true;
1300 }
1301 if (javaClass.getName().equals(Collection.class.getSimpleName())) {
1302 return true;
1303 }
1304 if (javaClass.getName().equals(Set.class.getSimpleName())) {
1305 return true;
1306 }
1307 return false;
1308 }
1309
1310 private String calcType(JavaClass mainClass, JavaMethod getterMethod,
1311 JavaMethod setterMethod, JavaField beanField, String shortName) {
1312 if (getterMethod != null) {
1313 return calcTypeOfGetterMethodReturn(getterMethod);
1314 }
1315 if (beanField != null) {
1316 Type type = beanField.getType();
1317 return calcType(type);
1318 }
1319
1320 return null;
1321 }
1322
1323 private String calcTypeOfGetterMethodReturn(JavaMethod getterMethod) {
1324 Type type = getterMethod.getReturnType();
1325 return calcType(type);
1326 }
1327
1328 private String calcTypeOfSetterMethodFirstParam(JavaMethod setterMethod) {
1329 JavaParameter param = setterMethod.getParameters()[0];
1330 return calcType(param);
1331 }
1332
1333 private String calcType(JavaParameter parameter) {
1334 return calcType(parameter.getType());
1335 }
1336
1337 private String calcType(Type type) {
1338 if (type == null) {
1339 return "void";
1340 }
1341 if (isCollection(type.getJavaClass())) {
1342 return calcType(calcRealJavaClass(type)) + "List";
1343 }
1344 return calcType(calcRealJavaClass(type));
1345 }
1346
1347 private String calcType(JavaClass javaClass) {
1348 if (javaClass.isEnum()) {
1349
1350 if (javaClass.getName().equals("ErrorLevel")) {
1351 return "Integer";
1352 }
1353 if (javaClass.getName().equals("StatementOperatorTypeKey")) {
1354 return "String";
1355 }
1356 if (javaClass.getName().equals("WriteAccess")) {
1357 return "String";
1358 }
1359 if (javaClass.getName().equals("Widget")) {
1360 return "String";
1361 }
1362 if (javaClass.getName().equals("DataType")) {
1363 return "String";
1364 }
1365 if (javaClass.getName().equals("SortDirection")) {
1366 return "String";
1367 }
1368 if (javaClass.getName().equals("Usage")) {
1369 return "String";
1370 }
1371 if (javaClass.getName().equals("StatementOperator")) {
1372 return "String";
1373 }
1374 }
1375
1376 if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1377 return "StringList";
1378 }
1379 if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1380 return "StringList";
1381 }
1382
1383 if (javaClass.getName().equals("java$util$Map")) {
1384 return "Map<String, String>";
1385 }
1386 if (javaClass.getName().equals("Map")) {
1387
1388 return "Map<String, String>";
1389 }
1390 return javaClass.getName();
1391 }
1392
1393 private JavaClass calcRealJavaClassOfGetterReturn(JavaMethod getterMethod) {
1394 if (getterMethod == null) {
1395 return null;
1396 }
1397 Type type = getterMethod.getReturnType();
1398 return this.calcRealJavaClass(type);
1399 }
1400
1401 private JavaClass calcRealJavaClassOfSetterFirstParam(JavaMethod setterMethod) {
1402 JavaParameter param = setterMethod.getParameters()[0];
1403 return this.calcRealJavaClass(param);
1404 }
1405
1406 private JavaClass calcRealJavaClass(JavaParameter param) {
1407 Type type = param.getType();
1408 return calcRealJavaClass(type);
1409 }
1410
1411 private JavaClass calcRealJavaClass(Type type) {
1412 if (type == null) {
1413 return null;
1414 }
1415 JavaClass javaClass = type.getJavaClass();
1416 if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1417 return STRING_JAVA_CLASS;
1418 }
1419 if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1420 return STRING_JAVA_CLASS;
1421 }
1422 if (!this.isCollection(javaClass)) {
1423 return javaClass;
1424 }
1425
1426
1427
1428
1429
1430
1431
1432 Type t = type.getActualTypeArguments()[0];
1433 return t.getJavaClass();
1434 }
1435
1436 private boolean isComplex(JavaClass javaClass) {
1437 if (javaClass.isEnum()) {
1438 return false;
1439 }
1440 if (javaClass.getName().equals(String.class.getSimpleName())) {
1441 return false;
1442 }
1443 if (javaClass.getName().equals(Integer.class.getSimpleName())) {
1444 return false;
1445 }
1446 if (javaClass.getName().equals(Date.class.getSimpleName())) {
1447 return false;
1448 }
1449 if (javaClass.getName().equals(Long.class.getSimpleName())) {
1450 return false;
1451 }
1452 if (javaClass.getName().equals(Boolean.class.getSimpleName())) {
1453 return false;
1454 }
1455 if (javaClass.getName().equals(Double.class.getSimpleName())) {
1456 return false;
1457 }
1458 if (javaClass.getName().equals(Float.class.getSimpleName())) {
1459 return false;
1460 }
1461 if (javaClass.getName().equals(int.class.getSimpleName())) {
1462 return false;
1463 }
1464 if (javaClass.getName().equals(long.class.getSimpleName())) {
1465 return false;
1466 }
1467 if (javaClass.getName().equals(boolean.class.getSimpleName())) {
1468 return false;
1469 }
1470 if (javaClass.getName().equals(double.class.getSimpleName())) {
1471 return false;
1472 }
1473 if (javaClass.getName().equals(float.class.getSimpleName())) {
1474 return false;
1475 }
1476 if (javaClass.getName().equals(Map.class.getSimpleName())) {
1477 return false;
1478 }
1479
1480 if (javaClass.getName().equals(String.class.getName())) {
1481 return false;
1482 }
1483 if (javaClass.getName().equals(Integer.class.getName())) {
1484 return false;
1485 }
1486 if (javaClass.getName().equals(Date.class.getName())) {
1487 return false;
1488 }
1489 if (javaClass.getName().equals(Long.class.getName())) {
1490 return false;
1491 }
1492 if (javaClass.getName().equals(Boolean.class.getName())) {
1493 return false;
1494 }
1495 if (javaClass.getName().equals(Double.class.getName())) {
1496 return false;
1497 }
1498 if (javaClass.getName().equals(Float.class.getName())) {
1499 return false;
1500 }
1501 if (javaClass.getName().equals(int.class.getName())) {
1502 return false;
1503 }
1504 if (javaClass.getName().equals(long.class.getName())) {
1505 return false;
1506 }
1507 if (javaClass.getName().equals(boolean.class.getName())) {
1508 return false;
1509 }
1510 if (javaClass.getName().equals(double.class.getName())) {
1511 return false;
1512 }
1513 if (javaClass.getName().equals(float.class.getName())) {
1514 return false;
1515 }
1516 if (javaClass.getName().equals(Map.class.getName())) {
1517 return false;
1518 }
1519 if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1520 return false;
1521 }
1522 if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1523 return false;
1524 }
1525 if (javaClass.getName().equals("java$util$Map")) {
1526 return false;
1527 }
1528 return true;
1529 }
1530 }