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.*;
20 import com.thoughtworks.qdox.model.Type;
21 import com.thoughtworks.qdox.model.annotation.AnnotationValue;
22 import org.kuali.student.contract.model.*;
23 import org.kuali.student.contract.model.util.JavaClassAnnotationUtils;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import javax.xml.bind.annotation.XmlEnum;
28 import java.io.File;
29 import java.math.BigDecimal;
30 import java.util.*;
31
32
33
34
35
36 public class ServiceContractModelQDoxLoader implements
37 ServiceContractModel {
38
39 private static final Logger log = LoggerFactory.getLogger(ServiceContractModelQDoxLoader.class);
40
41 private static final String LOCALE_KEY_LIST = "LocaleKeyList";
42 private static final String MESSAGE_GROUP_KEY_LIST = "MessageGroupKeyList";
43 private static final JavaClass STRING_JAVA_CLASS = new JavaClass(
44 "java.lang.String");
45 private List<String> sourceDirectories = null;
46 private List<Service> services = null;
47 private List<ServiceMethod> serviceMethods = null;
48 private Map<String, XmlType> xmlTypeMap = null;
49 private List<MessageStructure> messageStructures;
50 private boolean validateKualiStudent;
51
52 public ServiceContractModelQDoxLoader(List<String> sourceDirectories) {
53 this (sourceDirectories, true);
54 }
55
56 public ServiceContractModelQDoxLoader(List<String> sourceDirectories, boolean validateKualiStudent) {
57 this.sourceDirectories = sourceDirectories;
58 this.setValidateKualiStudent(validateKualiStudent);
59 }
60
61 public boolean isValidateKualiStudent() {
62 return validateKualiStudent;
63 }
64
65 public void setValidateKualiStudent(boolean validateKualiStudent) {
66 this.validateKualiStudent = validateKualiStudent;
67 }
68
69 @Override
70 public List<ServiceMethod> getServiceMethods() {
71 if (this.serviceMethods == null) {
72 this.parse();
73 }
74 return this.serviceMethods;
75 }
76
77 @Override
78 public List<String> getSourceNames() {
79 List<String> list = new ArrayList<String>(this.sourceDirectories.size());
80 for (String javaFile : this.sourceDirectories) {
81 list.add(javaFile);
82 }
83 return list;
84 }
85
86 @Override
87 public List<Service> getServices() {
88 if (services == null) {
89 this.parse();
90 }
91 return services;
92 }
93
94 @Override
95 public List<XmlType> getXmlTypes() {
96 if (xmlTypeMap == null) {
97 this.parse();
98 }
99 return new ArrayList<XmlType>(xmlTypeMap.values());
100 }
101
102 @Override
103 public List<MessageStructure> getMessageStructures() {
104 if (messageStructures == null) {
105 this.parse();
106 }
107 return this.messageStructures;
108 }
109
110 private void checkIfExists(String sourceDirectory) {
111 File file = new File(sourceDirectory);
112 if (!file.isDirectory()) {
113 throw new IllegalArgumentException(sourceDirectory + " is not a directory on disk");
114 }
115 }
116
117 @SuppressWarnings("unchecked")
118 private void parse() {
119
120 services = new ArrayList<Service>();
121 serviceMethods = new ArrayList<ServiceMethod>();
122 xmlTypeMap = new LinkedHashMap<String, XmlType>();
123 messageStructures = new ArrayList<MessageStructure>();
124
125 DefaultDocletTagFactory dtf = new DefaultDocletTagFactory();
126
127 JavaDocBuilder builder = new JavaDocBuilder(dtf);
128
129 for (String sourceDirectory : sourceDirectories) {
130 checkIfExists(sourceDirectory);
131 builder.addSourceTree(new File(sourceDirectory));
132 }
133 Set<JavaClass> mergedClasses = new LinkedHashSet<JavaClass>();
134
135 for (JavaClass javaClass : builder.getClasses()) {
136
137 if (!javaClass.getPackageName().contains("r1"))
138 mergedClasses.add(javaClass);
139 else
140 log.warn("excluding r1 class: " + javaClass.getFullyQualifiedName());
141
142 }
143
144 List<JavaClass>sortedClasses = new ArrayList<JavaClass>(mergedClasses);
145
146 Collections.sort(sortedClasses);
147
148 for (JavaClass javaClass : sortedClasses) {
149 if (!this.isServiceToProcess(javaClass)) {
150 continue;
151 }
152
153 Service service = new Service();
154 services.add(service);
155 service.setKey(calcServiceKey (javaClass));
156 service.setName(javaClass.getName());
157 service.setComments(this.calcComment(javaClass));
158 service.setUrl(this.calcServiceUrl(javaClass));
159 service.setVersion(this.calcVersion(javaClass));
160 service.setStatus("???");
161 service.setIncludedServices(calcIncludedServices(javaClass));
162 service.setImplProject(javaClass.getPackageName());
163
164
165
166
167
168
169
170
171 JavaMethod[] methods = getServiceMethods (javaClass);
172 for (JavaMethod javaMethod : methods) {
173
174 ServiceMethod serviceMethod = new ServiceMethod();
175 serviceMethods.add(serviceMethod);
176 serviceMethod.setService(service.getKey());
177 serviceMethod.setName(javaMethod.getName());
178 serviceMethod.setDescription(calcMissing(javaMethod.getComment()));
179 serviceMethod.setParameters(new ArrayList<ServiceMethodParameter>());
180 serviceMethod.setImplNotes(calcImplementationNotes(javaMethod));
181 serviceMethod.setDeprecated(isDeprecated(javaMethod));
182
183
184
185
186
187
188
189
190 for (JavaParameter parameter : javaMethod.getParameters()) {
191 ServiceMethodParameter param = new ServiceMethodParameter();
192 serviceMethod.getParameters().add(param);
193 param.setName(parameter.getName());
194 param.setType(calcType(parameter.getType()));
195 param.setDescription(calcMissing(
196 calcParameterDescription(javaMethod,
197 param.getName())));
198 try {
199 addXmlTypeAndMessageStructure(calcRealJavaClass(parameter.getType()),
200 serviceMethod.getService());
201 } catch (Exception e) {
202 String message= "failed to parameter message structure: " + serviceMethod.getService() + " : " + parameter.getType();
203
204 log.error (message + " : " + e.getMessage());
205 log.debug(message, e);
206 }
207 }
208
209 serviceMethod.setErrors(new ArrayList<ServiceMethodError>());
210 for (Type exception : javaMethod.getExceptions()) {
211 ServiceMethodError error = new ServiceMethodError();
212 error.setType(this.calcType(exception.getJavaClass()));
213 error.setDescription(calcMissing(
214 calcExceptionDescription(javaMethod,
215 error.getType())));
216 error.setPackageName(exception.getJavaClass().getPackageName());
217 error.setClassName(exception.getJavaClass().getName());
218 serviceMethod.getErrors().add(error);
219 }
220
221 ServiceMethodReturnValue rv = new ServiceMethodReturnValue();
222 serviceMethod.setReturnValue(rv);
223 Type returnType = null;
224 try {
225 returnType = javaMethod.getReturnType();
226 } catch (NullPointerException ex) {
227 log.error("Nullpointer getting return type: " + javaMethod.getCallSignature());
228 returnType = null;
229 }
230
231 rv.setType(calcType(returnType));
232 rv.setDescription(calcMissing(this.calcReturnDescription(javaMethod)));
233 if (returnType != null) {
234 try {
235
236
237 addXmlTypeAndMessageStructure(calcRealJavaClass(returnType),
238 serviceMethod.getService());
239 } catch (Exception e) {
240 String message = "failed to parse return type message structure: " + serviceMethod.getService() + " : " + returnType;
241
242 log.error (message + " : " + e.getMessage());
243 log.debug(message, e);
244 }
245 }
246 }
247 }
248 }
249
250
251 private String calcServiceKey(JavaClass javaClass) {
252 String key = javaClass.getName();
253 if (key.endsWith ("Service")) {
254 key = key.substring(0, key.length() - "Service".length());
255 }
256 if (javaClass.getPackageName().startsWith("org.kuali.rice.")) {
257 log.warn("WARNING " + " changing servkey for the RICE services to include prefix "
258 + " to disambiguate with Kuali Student StateService See Jira KSENROLL-2892");
259 key = "RICE." + key;
260 }
261 return key;
262 }
263
264 private JavaMethod[] getServiceMethods(JavaClass javaClass) {
265
266 Set<JavaMethod>methods = new LinkedHashSet<JavaMethod>();
267
268
269
270
271
272 JavaClass[] interfaces = javaClass.getImplementedInterfaces();
273
274 for (JavaClass intfc : interfaces) {
275
276 if (!isAService(intfc)) {
277
278
279 for (JavaMethod javaMethod : intfc.getMethods()) {
280
281 methods.add(javaMethod);
282 }
283 }
284
285 }
286
287
288
289
290 for (JavaMethod javaMethod : javaClass.getMethods()) {
291
292 methods.add(javaMethod);
293 }
294
295 return methods.toArray(new JavaMethod[] {});
296 }
297
298 private boolean isServiceToProcess(JavaClass javaClass) {
299
300
301 if (!javaClass.getName().endsWith("Service")) {
302 return false;
303 }
304 if (javaClass.getPackageName().contains(".old.")) {
305
306 return false;
307 }
308 if (javaClass.getPackageName().endsWith(".old")) {
309 return false;
310 }
311 for (Annotation annotation : javaClass.getAnnotations()) {
312
313 if (annotation.getType().getJavaClass().getName().equals("WebService")) {
314
315
316 return true;
317 }
318 }
319
320
321
322 if (javaClass.getName().endsWith("BoService")) {
323 return true;
324 }
325
326 if (javaClass.getPackageName().startsWith("com.sigmasys.kuali.ksa.service")) {
327 return true;
328 }
329 if (javaClass.getPackageName().startsWith("org.kuali.mobility")) {
330 return true;
331 }
332
333
334 return false;
335 }
336
337 private List<String> calcIncludedServices(JavaClass javaClass) {
338 List<String> includedServices = new ArrayList<String>();
339 for (JavaClass interfaceClass : javaClass.getImplementedInterfaces()) {
340 if (isAService(interfaceClass)) {
341
342
343 includedServices.add(interfaceClass.getName());
344 }
345 }
346 return includedServices;
347 }
348
349 private boolean isAService(JavaClass interfaceClass) {
350 if (interfaceClass.getName().endsWith("Service")) {
351 return true;
352 }
353 return false;
354 }
355
356 private String calcParameterDescription(JavaMethod method,
357 String parameterName) {
358 for (DocletTag tag : method.getTags()) {
359 if (tag.getName().equals("param")) {
360 if (tag.getValue().startsWith(parameterName + " ")) {
361 return tag.getValue().substring(parameterName.length() + 1);
362 }
363 }
364 }
365 return null;
366 }
367
368 private String calcExceptionDescription(JavaMethod serviceMethod,
369 String exceptionType) {
370 for (DocletTag tag : serviceMethod.getTags()) {
371 if (tag.getName().equals("throws")) {
372 if (tag.getValue().startsWith(exceptionType + " ")) {
373 return tag.getValue().substring(exceptionType.length() + 1);
374 }
375 }
376 }
377 return null;
378 }
379
380 private String calcReturnDescription(JavaMethod serviceMethod) {
381 for (DocletTag tag : serviceMethod.getTags()) {
382 if (tag.getName().equals("return")) {
383 return tag.getValue();
384 }
385 }
386 return null;
387 }
388
389 private String calcServiceUrl(JavaClass serviceClass) {
390 for (DocletTag tag : serviceClass.getTags()) {
391 if (tag.getName().equals("See")) {
392 return tag.getValue();
393 }
394 }
395 return null;
396 }
397
398 private void addXmlTypeAndMessageStructure(JavaClass messageStructureJavaClass,
399 String serviceKey) {
400 String name = calcType(messageStructureJavaClass);
401 if (name.endsWith("CallbackService")) {
402 return;
403 }
404 XmlType xmlType = xmlTypeMap.get(name);
405 if (xmlType == null) {
406 xmlType = new XmlType();
407 xmlTypeMap.put(name, xmlType);
408 xmlType.setName(name);
409 xmlType.setDesc(this.calcMessageStructureDesc(messageStructureJavaClass));
410 xmlType.setDeprecated(isDeprecated (messageStructureJavaClass));
411 xmlType.setService(serviceKey);
412 xmlType.setVersion("IGNORE -- SAME AS SERVICE");
413 xmlType.setPrimitive(calcPrimitive(messageStructureJavaClass));
414 xmlType.setJavaPackage(calcJavaPackage(messageStructureJavaClass));
415 if (xmlType.getPrimitive().equals(XmlType.COMPLEX)) {
416 addMessageStructure(messageStructureJavaClass, serviceKey);
417 }
418
419 } else {
420 addServiceToList(xmlType, serviceKey);
421 }
422 }
423
424 private boolean isDeprecated(JavaClass javaClass) {
425 for (Annotation annotation : javaClass.getAnnotations()) {
426 if (annotation.getType().getJavaClass().getName().equals(
427 "Deprecated")) {
428 return true;
429 }
430 }
431 return false;
432 }
433
434 private String calcJavaPackage(JavaClass javaClass) {
435 String packageName = javaClass.getPackageName();
436 return packageName;
437 }
438
439 private String calcMessageStructureDesc(JavaClass javaClass) {
440 {
441 String desc = javaClass.getComment();
442 if (desc != null) {
443 if (!desc.isEmpty()) {
444 return desc;
445 }
446 }
447 JavaClass infcClass = this.getMatchingInfc(javaClass);
448 if (infcClass == null) {
449 return null;
450 }
451 return infcClass.getComment();
452 }
453 }
454
455 private JavaClass getMatchingInfc(JavaClass javaClass) {
456
457 String nameInfc = javaClass.getName();
458 if (nameInfc.endsWith("Info")) {
459 nameInfc = nameInfc.substring(0, nameInfc.length() - "Info".length())
460 + "Infc";
461 }
462 String nameWithOutInfo = javaClass.getName();
463
464 if (nameWithOutInfo.endsWith("Info")) {
465 nameWithOutInfo = nameWithOutInfo.substring(0, nameWithOutInfo.length()
466 - "Info".length());
467 }
468 for (JavaClass infc : javaClass.getImplementedInterfaces()) {
469 if (infc.getName().equals(nameInfc)) {
470
471 return infc;
472 }
473 if (infc.getName().equals(nameWithOutInfo)) {
474 return infc;
475 }
476 }
477 return null;
478 }
479
480 private String calcPrimitive(JavaClass javaClass) {
481 if (this.isComplex(javaClass)) {
482 return XmlType.COMPLEX;
483 }
484 return "Primitive";
485 }
486
487 private String initLower(String str) {
488 if (str == null) {
489 return null;
490 }
491 if (str.length() == 0) {
492 return str;
493 }
494 if (str.length() == 1) {
495 return str.toLowerCase();
496 }
497 return str.substring(0, 1).toLowerCase() + str.substring(1);
498 }
499
500 private String initUpper(String str) {
501 if (str == null) {
502 return null;
503 }
504 if (str.length() == 0) {
505 return str;
506 }
507 if (str.length() == 1) {
508 return str.toUpperCase();
509 }
510 return str.substring(0, 1).toUpperCase() + str.substring(1);
511 }
512
513 private Set<String> getShortNames(JavaClass messageStructureJavaClass) {
514 Set<String> fields = getFieldsUsingPropOrder(messageStructureJavaClass);
515 if (fields != null) {
516 return fields;
517 }
518 fields = new LinkedHashSet<String>();
519 for (JavaMethod method : messageStructureJavaClass.getMethods(true)) {
520 if (isSetterMethodToProcess(method, messageStructureJavaClass.getName())) {
521 String shortName = this.calcShortNameFromSetter(method);
522 fields.add(shortName);
523 continue;
524 }
525 if (isGetterMethodToProcess(method, messageStructureJavaClass.getName())) {
526 String shortName = this.calcShortNameFromGetter(method);
527 fields.add(shortName);
528 continue;
529 }
530 }
531 return fields;
532 }
533
534 private Set<String> getFieldsUsingPropOrder(
535 JavaClass messageStructureJavaClass) {
536 for (Annotation annotation : messageStructureJavaClass.getAnnotations()) {
537 if (annotation.getType().getJavaClass().getName().equals("XmlType")) {
538 AnnotationValue propOrderParam = annotation.getProperty("propOrder");
539 if (propOrderParam == null) {
540 continue;
541 }
542 Object propOrderValue = propOrderParam.getParameterValue();
543 if (!(propOrderValue instanceof List)) {
544 continue;
545 }
546 Set<String> fields = new LinkedHashSet<String>();
547 for (Object value : (List<?>) propOrderValue) {
548 if (value instanceof String) {
549 String shortName = (String) value;
550 shortName = this.stripQuotes(shortName);
551 if (shortName.contains(".Elements.")) {
552 String newShortName = getShortNameFromElements(shortName, messageStructureJavaClass);
553 if (newShortName == null) {
554 continue;
555 }
556 shortName = newShortName;
557 } else if (shortName.startsWith("CoreConstants.CommonElements.")) {
558 String newShortName = getCoreConstants(shortName);
559 if (newShortName == null) {
560 continue;
561 }
562 shortName = newShortName;
563 }
564 if (shortName.equals("_futureElements")) {
565 continue;
566 }
567 shortName = this.initUpper(shortName);
568 fields.add(shortName);
569 }
570 }
571 return fields;
572 }
573 }
574 return null;
575 }
576
577 private String getShortNameFromElements(String shortName, JavaClass messageStructureJavaClass) {
578 JavaClass elementsJavaClass = messageStructureJavaClass.getNestedClassByName("Elements");
579 if (elementsJavaClass == null) {
580 return null;
581 }
582 String fieldName = shortName.substring(shortName.indexOf(".Elements.") + ".Elements.".length());
583 JavaField field = elementsJavaClass.getFieldByName(fieldName);
584 String initExpr = field.getInitializationExpression();
585 return stripQuotes(initExpr);
586 }
587
588 private String getCoreConstants(String shortName) {
589 if (shortName.endsWith("VERSION_NUMBER")) {
590 return "versionNumber";
591 }
592 if (shortName.endsWith("OBJECT_ID")) {
593 return "objectId";
594 }
595 if (shortName.endsWith("ACTIVE")) {
596 return "active";
597 }
598 if (shortName.endsWith("ACTIVE_FROM_DATE")) {
599 return "activeFromDate";
600 }
601 if (shortName.endsWith("ACTIVE_TO_DATE")) {
602 return "activeToDate";
603 }
604 if (shortName.endsWith("ATTRIBUTES")) {
605 return "attributes";
606 }
607 if (shortName.endsWith("FUTURE_ELEMENTS")) {
608 return "_futureElements";
609 }
610 throw new RuntimeException("Unknown shortName " + shortName);
611 }
612
613 private void addMessageStructure(JavaClass messageStructureJavaClass,
614 String serviceKey) {
615 Set<JavaClass> subObjectsToAdd = new LinkedHashSet<JavaClass>();
616 for (String shortName : this.getShortNames(messageStructureJavaClass)) {
617 JavaMethod setterMethod = findSetterMethod(messageStructureJavaClass,
618 shortName);
619 JavaMethod getterMethod = findGetterMethod(messageStructureJavaClass,
620 shortName);
621 if (getterMethod == null) {
622 if (this.validateKualiStudent) {
623 throw new IllegalArgumentException("shortName has no corresponding getter method: "
624 + messageStructureJavaClass.getFullyQualifiedName()
625 + "." + shortName);
626 }
627 }
628 JavaField beanField = this.findField(messageStructureJavaClass,
629 shortName, setterMethod);
630 if (beanField == null) {
631 String accessorType = getAccessorType(getterMethod);
632 if ("XmlAccessType.FIELD".equals(accessorType)) {
633 throw new IllegalArgumentException("Setter method has no corresponding bean field: "
634 + messageStructureJavaClass.getName()
635 + "." + getterMethod.getName());
636 }
637 }
638
639
640 if (beanField != null) {
641 for (Annotation annotation : beanField.getAnnotations()) {
642 if (annotation.getType().getJavaClass().getName().equals("XmlAttribute")) {
643 Object nameValue = annotation.getNamedParameter("name");
644 if (nameValue != null) {
645 shortName = stripQuotes(nameValue.toString());
646 }
647 }
648 }
649 }
650 shortName = initLower(shortName);
651 MessageStructure ms = new MessageStructure();
652 messageStructures.add(ms);
653 ms.setXmlObject(messageStructureJavaClass.getName());
654 ms.setShortName(shortName);
655 ms.setId(ms.getXmlObject() + "." + ms.getShortName());
656 ms.setName(calcMissing(calcName(messageStructureJavaClass, getterMethod, setterMethod,
657 beanField, shortName)));
658 ms.setType(calcType(messageStructureJavaClass, getterMethod, setterMethod, beanField, shortName));
659 if (ms.getType().equals("Object")) {
660 log.warn(ms.getId()
661 + " has Object as it's type ==> Changing to String");
662 ms.setType("String");
663 } else if (ms.getType().equals("ObjectList")) {
664 log.warn(ms.getId()
665 + " has a list of Objects as it's type ==> Changing to List of String");
666 ms.setType("StringList");
667 }
668 ms.setXmlAttribute(this.calcXmlAttribute(beanField));
669 ms.setRequired(calcRequired(getterMethod, setterMethod, beanField));
670 ms.setReadOnly(calcReadOnly(getterMethod, setterMethod, beanField));
671 ms.setCardinality(this.calcCardinality(messageStructureJavaClass, getterMethod, setterMethod, beanField, shortName));
672 ms.setDescription(calcMissing(calcDescription(messageStructureJavaClass, getterMethod, setterMethod,
673 beanField, shortName)));
674 ms.setImplNotes(calcImplementationNotes(getterMethod, setterMethod, beanField));
675 ms.setDeprecated(isDeprecated(getterMethod));
676 ms.setStatus("???");
677 ms.setLookup(calcLookup (messageStructureJavaClass, getterMethod, setterMethod,
678 beanField, serviceKey, ms.getXmlObject(), shortName, ms.getType()));
679 ms.setPrimaryKey(calcPrimaryKey (messageStructureJavaClass, getterMethod, setterMethod,
680 beanField, serviceKey, ms.getXmlObject(), shortName, ms.getType()));
681 ms.setOverriden(this.calcOverridden(messageStructureJavaClass, getterMethod));
682 JavaClass subObjToAdd = this.calcRealJavaClassOfGetterReturn(getterMethod);
683 if (subObjToAdd != null) {
684
685 if (!subObjToAdd.getName().equals("Object")) {
686 if (!subObjToAdd.getName().equals("LocaleKeyList")) {
687 if (!subObjToAdd.getName().equals("MessageGroupKeyList")) {
688 subObjectsToAdd.add(subObjToAdd);
689 }
690 }
691 }
692
693 }
694 }
695
696 for (JavaClass subObjectToAdd : subObjectsToAdd) {
697 XmlType xmlType = xmlTypeMap.get(calcType(subObjectToAdd));
698 if (xmlType == null) {
699 try {
700 addXmlTypeAndMessageStructure(subObjectToAdd, serviceKey);
701 } catch (Exception e) {
702 String message = "failed to parse subobject structure: " + subObjectToAdd + " : " + serviceKey;
703
704 log.error (message + " : " + e.getMessage());
705
706 log.debug(message, e);
707 }
708 } else {
709 addServiceToList(xmlType, serviceKey);
710 }
711 }
712 return;
713 }
714
715 private String stripList (String type) {
716 if (type == null) {
717 return null;
718 }
719 if (type.endsWith("List")) {
720 return type.substring(0, type.length() - "List".length());
721 }
722 return type;
723 }
724
725
726 private boolean calcPrimaryKey (JavaClass mainClass, JavaMethod getterMethod,
727 JavaMethod setterMethod, JavaField beanField, String serviceKey, String xmlObject, String shortName, String type) {
728 if (!type.equalsIgnoreCase ("String")) {
729 return false;
730 }
731 if (shortName.equalsIgnoreCase ("Id")) {
732 return true;
733 }
734 if (shortName.equalsIgnoreCase ("Key")) {
735 return true;
736 }
737
738
739 if (xmlObject.equalsIgnoreCase("Principal")) {
740 if (shortName.equalsIgnoreCase("principalId")) {
741 return true;
742 }
743 }
744 return false;
745 }
746
747
748 private Lookup calcLookup (JavaClass mainClass, JavaMethod getterMethod,
749 JavaMethod setterMethod, JavaField beanField, String serviceKey, String xmlObject, String shortName, String type) {
750 type = this.stripList(type);
751
752 if (!type.equalsIgnoreCase ("String")) {
753 return null;
754 }
755 Lookup lookup = LOOKUP_MAPPINGS.get(shortName);
756 if (lookup == null) {
757 if (this.endsWithIdOrKey(shortName)) {
758 log.warn (serviceKey + ":" + xmlObject + "." + shortName + " ends with id or key but has no lookup defined");
759 }
760 }
761 return lookup;
762 }
763
764 private boolean endsWithIdOrKey (String shortName) {
765 if (shortName.equals ("Id")) {
766 return false;
767 }
768 if (shortName.equals ("Key")) {
769 return false;
770 }
771 if (shortName.endsWith ("Id")) {
772 return true;
773 }
774 if (shortName.endsWith ("Ids")) {
775 return true;
776 }
777 if (shortName.endsWith ("Key")) {
778 return true;
779 }
780 if (shortName.endsWith ("Keys")) {
781 return true;
782 }
783 return false;
784 }
785
786 private static Map<String, Lookup> LOOKUP_MAPPINGS;
787
788 {
789
790 LOOKUP_MAPPINGS = new LinkedHashMap<String, Lookup> ();
791 LOOKUP_MAPPINGS.put("typeKey", new Lookup ("Type", "TypeInfo"));
792 LOOKUP_MAPPINGS.put("stateKey", new Lookup ("State", "StateInfo"));
793 LOOKUP_MAPPINGS.put("lifecycleKey", new Lookup ("State", "LifecycleInfo"));
794 LOOKUP_MAPPINGS.put("orgId", new Lookup ("Organization", "OrgInfo"));
795 LOOKUP_MAPPINGS.put("orgIds", new Lookup ("Organization", "OrgInfo"));
796 LOOKUP_MAPPINGS.put("organizationId", new Lookup ("Organization", "OrgInfo"));
797 LOOKUP_MAPPINGS.put("organizationIds", new Lookup ("Organization", "OrgInfo"));
798 LOOKUP_MAPPINGS.put("atpId", new Lookup ("Atp", "AtpInfo"));
799 LOOKUP_MAPPINGS.put("atpIds", new Lookup ("Atp", "AtpInfo"));
800 LOOKUP_MAPPINGS.put("termId", new Lookup ("AcademicCalendar", "TermInfo"));
801 LOOKUP_MAPPINGS.put("termIds", new Lookup ("AcademicCalendar", "TermInfo"));
802 LOOKUP_MAPPINGS.put("luiId", new Lookup ("Lui", "LuiInfo"));
803 LOOKUP_MAPPINGS.put("luiIds", new Lookup ("Lui", "LuiInfo"));
804 LOOKUP_MAPPINGS.put("cluId", new Lookup ("Clu", "CluInfo"));
805 LOOKUP_MAPPINGS.put("cluIds", new Lookup ("Clu", "CluInfo"));
806 LOOKUP_MAPPINGS.put("credentialProgramId", new Lookup ("Program", "CredentialProgramInfo"));
807 LOOKUP_MAPPINGS.put("credentialProgramIds", new Lookup ("Program", "CredentialProgramInfo"));
808 LOOKUP_MAPPINGS.put("credentialProgramId", new Lookup ("Program", "CredentialProgramInfo"));
809 LOOKUP_MAPPINGS.put("credentialProgramIds", new Lookup ("Program", "CredentialProgramInfo"));
810 LOOKUP_MAPPINGS.put("coreProgramId", new Lookup ("Program", "CoreProgramInfo"));
811 LOOKUP_MAPPINGS.put("coreProgramIds", new Lookup ("Program", "CoreProgramInfo"));
812 LOOKUP_MAPPINGS.put("resultScaleKey", new Lookup ("LRC", "ResultScaleInfo"));
813 LOOKUP_MAPPINGS.put("resultScaleKeys", new Lookup ("LRC", "ResultScaleInfo"));
814 LOOKUP_MAPPINGS.put("resultValuesGroupKey", new Lookup ("LRC", "ResultValuesGroupInfo"));
815 LOOKUP_MAPPINGS.put("resultValuesGroupKeys", new Lookup ("LRC", "ResultValuesGroupInfo"));
816 LOOKUP_MAPPINGS.put("resultValueKey", new Lookup ("LRC", "ResultValueInfo"));
817 LOOKUP_MAPPINGS.put("resultValueKeys", new Lookup ("LRC", "ResultValueInfo"));
818 LOOKUP_MAPPINGS.put("scheduleId", new Lookup ("Schedule", "ScheduleInfo"));
819 LOOKUP_MAPPINGS.put("scheduleIds", new Lookup ("Schedule", "ScheduleInfo"));
820 LOOKUP_MAPPINGS.put("courseId", new Lookup ("Course", "CourseInfo"));
821 LOOKUP_MAPPINGS.put("courseIds", new Lookup ("Course", "CourseInfo"));
822 LOOKUP_MAPPINGS.put("formatId", new Lookup ("Course", "FormatInfo"));
823 LOOKUP_MAPPINGS.put("formatIds", new Lookup ("Course", "FormatInfo"));
824 LOOKUP_MAPPINGS.put("activityId", new Lookup ("Course", "ActivityInfo"));
825 LOOKUP_MAPPINGS.put("activityIds", new Lookup ("Course", "ActivityInfo"));
826 LOOKUP_MAPPINGS.put("courseOfferingId", new Lookup ("CourseOffering", "CourseOfferingInfo"));
827 LOOKUP_MAPPINGS.put("courseOfferingIds", new Lookup ("CourseOffering", "CourseOfferingInfo"));
828 LOOKUP_MAPPINGS.put("formatOfferingId", new Lookup ("CourseOffering", "FormatOfferingInfo"));
829 LOOKUP_MAPPINGS.put("formatOfferingIds", new Lookup ("CourseOffering", "FormatOfferingInfo"));
830 LOOKUP_MAPPINGS.put("activityOfferingId", new Lookup ("CourseOffering", "ActivityOfferingInfo"));
831 LOOKUP_MAPPINGS.put("activityOfferingIds", new Lookup ("CourseOffering", "ActivityOfferingInfo"));
832 LOOKUP_MAPPINGS.put("socRolloverResultId", new Lookup ("CourseOfferingSet", "SorRolloverResultInfo"));
833 LOOKUP_MAPPINGS.put("socRolloverResultIds", new Lookup ("CourseOfferingSet", "SorRolloverResultInfo"));
834 LOOKUP_MAPPINGS.put("socRolloverResultItemId", new Lookup ("CourseOfferingSet", "SorRolloverResultItemInfo"));
835 LOOKUP_MAPPINGS.put("socRolloverResultItemIds", new Lookup ("CourseOfferingSet", "SorRolloverResultItemInfo"));
836 LOOKUP_MAPPINGS.put("buildingId", new Lookup ("Room", "BuildingInfo"));
837 LOOKUP_MAPPINGS.put("buildingIds", new Lookup ("Room", "BuildingInfo"));
838 LOOKUP_MAPPINGS.put("roomId", new Lookup ("Room", "RoomInfo"));
839 LOOKUP_MAPPINGS.put("roomIds", new Lookup ("Room", "RoomInfo"));
840 LOOKUP_MAPPINGS.put("populationId", new Lookup ("Population", "PopulationInfo"));
841 LOOKUP_MAPPINGS.put("populationIds", new Lookup ("Population", "PopulationInfo"));
842
843
844
845 LOOKUP_MAPPINGS.put("principalId", new Lookup ("rice.kim.Identity", "Principal"));
846 LOOKUP_MAPPINGS.put("principalIds", new Lookup ("rice.kim.Identity", "Principal"));
847
848 LOOKUP_MAPPINGS.put("personId", new Lookup ("rice.kim.Identity", "Principal"));
849 LOOKUP_MAPPINGS.put("personIds", new Lookup ("rice.kim.Identity", "Principal"));
850 LOOKUP_MAPPINGS.put("instructorId", new Lookup ("rice.kim.Identity", "Principal"));
851 LOOKUP_MAPPINGS.put("instructorIds", new Lookup ("rice.kim.Identity", "Principal"));
852 LOOKUP_MAPPINGS.put("studentId", new Lookup ("rice.kim.Identity", "Principal"));
853 LOOKUP_MAPPINGS.put("studentIds", new Lookup ("rice.kim.Identity", "Principal"));
854
855
856
857 LOOKUP_MAPPINGS.put("atpDurationTypeKey", new Lookup ("Type", "TypeInfo"));
858 LOOKUP_MAPPINGS.put("currencyTypeKey", new Lookup ("Type", "TypeInfo"));
859
860 LOOKUP_MAPPINGS.put("authenticatedPrincipalId", new Lookup ("rice.kim.Identity", "Principal"));
861
862 LOOKUP_MAPPINGS.put("createId", new Lookup ("rice.kim.Identity", "Principal"));
863 LOOKUP_MAPPINGS.put("updateId", new Lookup ("rice.kim.Identity", "Principal"));
864 LOOKUP_MAPPINGS.put("agendaId", new Lookup ("rice.krms.Agenda", "Agenda"));
865 LOOKUP_MAPPINGS.put("agendaIds", new Lookup ("rice.krms.Agenda", "Agenda"));
866 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
867 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
868 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
869 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
870 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
871 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
872 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
873
874
875
876
877 LOOKUP_MAPPINGS.put("unitsContentOwner", new Lookup ("Organization", "OrgInfo"));
878 LOOKUP_MAPPINGS.put("unitsDeployment", new Lookup ("Organization", "OrgInfo"));
879 LOOKUP_MAPPINGS.put("unitsStudentOversight", new Lookup ("Organization", "OrgInfo"));
880 LOOKUP_MAPPINGS.put("unitsFinancialResources", new Lookup ("Organization", "OrgInfo"));
881 LOOKUP_MAPPINGS.put("unitsFinancialControl", new Lookup ("Organization", "OrgInfo"));
882 LOOKUP_MAPPINGS.put("divisionsContentOwner", new Lookup ("Organization", "OrgInfo"));
883 LOOKUP_MAPPINGS.put("divisionsDeployment", new Lookup ("Organization", "OrgInfo"));
884 LOOKUP_MAPPINGS.put("divisionsStudentOversight", new Lookup ("Organization", "OrgInfo"));
885 LOOKUP_MAPPINGS.put("divisionsFinancialResources", new Lookup ("Organization", "OrgInfo"));
886 LOOKUP_MAPPINGS.put("divisionsFinancialControl", new Lookup ("Organization", "OrgInfo"));
887 LOOKUP_MAPPINGS.put("startTerm", new Lookup ("AcademicCalendar", "TermInfo"));
888 LOOKUP_MAPPINGS.put("endTerm", new Lookup ("AcademicCalendar", "TermInfo"));
889 LOOKUP_MAPPINGS.put("endProgramEntryTerm", new Lookup ("AcademicCalendar", "TermInfo"));
890 LOOKUP_MAPPINGS.put("resultOptions", new Lookup ("LRC", "ResultValuesGroupInfo"));
891 LOOKUP_MAPPINGS.put("programRequirements", new Lookup ("Program", "ProgramRequirementInfo"));
892
893 LOOKUP_MAPPINGS.put("parentRelType", new Lookup ("Type", "TypeInfo"));
894 LOOKUP_MAPPINGS.put("parentLoRelationid", new Lookup ("LearningObjective", "LoInfo"));
895
896
897 LOOKUP_MAPPINGS.put("ownerTypeKey", new Lookup ("Type", "TypeInfo"));
898 LOOKUP_MAPPINGS.put("relatedTypeKey", new Lookup ("Type", "TypeInfo"));
899
900
901
902
903
904
905 LOOKUP_MAPPINGS.put("adminOrgId", new Lookup ("Organization", "OrgInfo"));
906 LOOKUP_MAPPINGS.put("relatedAtpId", new Lookup ("Atp", "AtpInfo"));
907 LOOKUP_MAPPINGS.put("relativeAnchorMilestoneId", new Lookup ("Atp", "MilestoneInfo"));
908
909
910 LOOKUP_MAPPINGS.put("relatedLuiTypes", new Lookup ("Type", "TypeInfo"));
911 LOOKUP_MAPPINGS.put("relatedLuiId", new Lookup ("Lui", "LuiInfo"));
912
913
914 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
915 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
916
917
918 LOOKUP_MAPPINGS.put("unitsDeploymentOrgIds", new Lookup ("Organization", "OrgInfo"));
919 LOOKUP_MAPPINGS.put("unitsContentOwnerOrgIds", new Lookup ("Organization", "OrgInfo"));
920 LOOKUP_MAPPINGS.put("unitsContentOwnerId", new Lookup ("Organization", "OrgInfo"));
921 LOOKUP_MAPPINGS.put("jointOfferingIds", new Lookup ("CourseOffering", "CourseOfferingInfo"));
922 LOOKUP_MAPPINGS.put("gradingOptionId", new Lookup ("LRC", "ResultValuesGroupInfo"));
923 LOOKUP_MAPPINGS.put("creditOptionId", new Lookup ("LRC", "ResultValuesGroupInfo"));
924 LOOKUP_MAPPINGS.put("waitlistLevelTypeKey", new Lookup ("Type", "TypeInfo"));
925 LOOKUP_MAPPINGS.put("waitlistTypeKey", new Lookup ("Type", "TypeInfo"));
926 LOOKUP_MAPPINGS.put("activityOfferingTypeKeys", new Lookup ("Type", "TypeInfo"));
927 LOOKUP_MAPPINGS.put("gradeRosterLevelTypeKey", new Lookup ("Type", "TypeInfo"));
928 LOOKUP_MAPPINGS.put("finalExamLevelTypeKey", new Lookup ("Type", "TypeInfo"));
929 LOOKUP_MAPPINGS.put("schedulingStateKey", new Lookup ("State", "StateInfo"));
930 LOOKUP_MAPPINGS.put("gradingOptionKeys", new Lookup ("LRC", "ResultValuesGroupInfo"));
931 LOOKUP_MAPPINGS.put("creditOptionId", new Lookup ("LRC", "ResultValuesGroupInfo"));
932 LOOKUP_MAPPINGS.put("gradingOptionKeys", new Lookup ("", ""));
933 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
934 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
935 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
936 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
937 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
938 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
939 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
940 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
941 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
942 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
943 LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
944
945 }
946
947 private boolean calcOverridden(JavaClass mainClass, JavaMethod getterMethod) {
948 if (getterMethod == null) {
949 return false;
950 }
951 JavaMethod infcGetter = null;
952 if (getterMethod.getParentClass().isInterface()) {
953 infcGetter = getterMethod;
954 }
955 if (infcGetter == null) {
956 infcGetter = findInterfaceMethod(mainClass, getterMethod, false);
957 }
958 if (infcGetter == null) {
959 return false;
960 }
961 Annotation annotation = this.getAnnotation(infcGetter, null, null, "Override");
962 if (annotation != null) {
963 return true;
964 }
965 return false;
966 }
967
968 private String calcComment(JavaClass javaClass) {
969 return this.calcComment(javaClass.getComment());
970 }
971
972 private String calcComment(String comment) {
973 return this.parseCommentVersion(comment)[0];
974 }
975
976 private String calcVersion(JavaClass javaClass) {
977 DocletTag tag = javaClass.getTagByName("version", true);
978 if (tag != null) {
979 return tag.getValue();
980 }
981 return this.calcVersion(javaClass.getComment());
982 }
983
984 private String calcVersion(String comment) {
985 return this.parseCommentVersion(comment)[1];
986 }
987
988 private String[] parseCommentVersion(String commentVersion) {
989 String[] parsed = new String[2];
990 if (commentVersion == null) {
991 return parsed;
992 }
993 commentVersion = commentVersion.trim();
994 int i = commentVersion.toLowerCase().indexOf("\nversion:");
995 if (i == -1) {
996 parsed[0] = commentVersion;
997 return parsed;
998 }
999 parsed[1] = commentVersion.substring(i + "\nversion:".length()).trim();
1000 parsed[0] = commentVersion.substring(0, i).trim();
1001
1002 return parsed;
1003 }
1004
1005 private Annotation getAnnotation(JavaMethod getterMethod,
1006 JavaMethod setterMethod, JavaField beanField, String type) {
1007 if (beanField != null) {
1008
1009 for (Annotation annotation : beanField.getAnnotations()) {
1010 if (annotation.getType().getJavaClass().getName().equals(type)) {
1011 return annotation;
1012 }
1013 }
1014 }
1015 if (getterMethod != null) {
1016
1017 for (Annotation annotation : getterMethod.getAnnotations()) {
1018 if (annotation.getType().getJavaClass().getName().equals(type)) {
1019 return annotation;
1020 }
1021 }
1022 }
1023 if (setterMethod != null) {
1024
1025 for (Annotation annotation : setterMethod.getAnnotations()) {
1026 if (annotation.getType().getJavaClass().getName().equals(type)) {
1027 return annotation;
1028 }
1029 }
1030 }
1031 return null;
1032 }
1033
1034 private String calcRequired(JavaMethod getterMethod,
1035 JavaMethod setterMethod, JavaField beanField) {
1036 Annotation annotation = this.getAnnotation(getterMethod, setterMethod, beanField, "XmlElement");
1037 if (annotation == null) {
1038 annotation = this.getAnnotation(getterMethod, setterMethod, beanField, "XmlAttribute");
1039 }
1040 if (annotation != null) {
1041 Object required = annotation.getNamedParameter("required");
1042 if (required != null) {
1043 if (required.toString().equalsIgnoreCase("true")) {
1044 return "Required";
1045 }
1046 }
1047 }
1048 if (getterMethod != null) {
1049 DocletTag tag = getterMethod.getTagByName("required", true);
1050 if (tag != null) {
1051 if (tag.getValue() == null) {
1052 return "Required";
1053 }
1054 String required = "Required " + tag.getValue();
1055 return required.trim();
1056 }
1057 }
1058 return null;
1059 }
1060
1061 private String calcReadOnly(JavaMethod getterMethod,
1062 JavaMethod setterMethod, JavaField beanField) {
1063 if (getterMethod != null) {
1064 DocletTag tag = getterMethod.getTagByName("readOnly", true);
1065 if (tag != null) {
1066 if (tag.getValue() == null) {
1067 return "Read only";
1068 }
1069 String readOnly = "Read only " + tag.getValue();
1070 return readOnly.trim();
1071 }
1072 }
1073 return null;
1074 }
1075
1076 private String calcImplementationNotes(JavaMethod serviceMethod) {
1077 StringBuilder bldr = new StringBuilder();
1078 String newLine = "";
1079 for (DocletTag tag : serviceMethod.getTagsByName("impl", true)) {
1080 bldr.append(newLine);
1081 newLine = "\n";
1082 String value = tag.getValue();
1083 bldr.append(value);
1084 }
1085 if (hasOverride(serviceMethod)) {
1086 boolean matchJustOnName = true;
1087 JavaMethod overriddenMethod = findInterfaceMethod(serviceMethod.getParentClass(), serviceMethod, matchJustOnName);
1088 if (overriddenMethod == null) {
1089
1090 findInterfaceMethod(serviceMethod.getParentClass(), serviceMethod, true);
1091 throw new NullPointerException("could not find overridden method or method that has @Override annotation " + serviceMethod.getCallSignature());
1092 }
1093 bldr.append(newLine);
1094 newLine = "\n";
1095 bldr.append("Overridden method should be implemented in helper: ");
1096 bldr.append(overriddenMethod.getParentClass().getName());
1097 }
1098 if (bldr.length() == 0) {
1099 return null;
1100 }
1101 return bldr.toString();
1102 }
1103
1104 private boolean hasOverride(JavaMethod serviceMethod) {
1105 for (Annotation annotation : serviceMethod.getAnnotations()) {
1106 if (annotation.getType().getJavaClass().getName().equals(
1107 "Override")) {
1108 return true;
1109 }
1110 }
1111 return false;
1112 }
1113
1114 private boolean isDeprecated(JavaMethod serviceMethod) {
1115 for (Annotation annotation : serviceMethod.getAnnotations()) {
1116 if (annotation.getType().getJavaClass().getName().equals(
1117 "Deprecated")) {
1118 return true;
1119 }
1120 }
1121 return false;
1122 }
1123
1124 private String calcImplementationNotes(JavaMethod getterMethod,
1125 JavaMethod setterMethod, JavaField beanField) {
1126 if (getterMethod != null) {
1127 DocletTag tag = getterMethod.getTagByName("impl", true);
1128 if (tag != null) {
1129 return tag.getValue();
1130 }
1131 }
1132 return null;
1133 }
1134
1135 private String calcNameFromShortName(String shortName) {
1136 StringBuilder bldr = new StringBuilder(shortName.length() + 3);
1137 char c = shortName.charAt(0);
1138 bldr.append(Character.toUpperCase(c));
1139 boolean lastWasUpper = true;
1140 for (int i = 1; i < shortName.length(); i++) {
1141 c = shortName.charAt(i);
1142 if (Character.isUpperCase(c)) {
1143 if (!lastWasUpper) {
1144 bldr.append(" ");
1145 }
1146 } else {
1147 lastWasUpper = false;
1148 }
1149 bldr.append(c);
1150 }
1151 return bldr.toString();
1152 }
1153
1154 private String calcName(JavaClass mainClass, JavaMethod getterMethod,
1155 JavaMethod setterMethod, JavaField beanField, String shortName) {
1156 String name = this.calcNameFromTag(getterMethod, setterMethod, beanField);
1157 if (name != null) {
1158 return name;
1159 }
1160 name = this.calcNameFromNameEmbeddedInDescription(mainClass, getterMethod, setterMethod, beanField, shortName);
1161 if (name != null) {
1162 return name;
1163 }
1164 return this.calcNameFromShortName(shortName);
1165 }
1166
1167 private String calcNameFromTag(JavaMethod getterMethod,
1168 JavaMethod setterMethod, JavaField beanField) {
1169 if (getterMethod != null) {
1170 DocletTag tag = getterMethod.getTagByName("name", true);
1171 if (tag != null) {
1172 return tag.getValue();
1173 }
1174 }
1175 return null;
1176 }
1177
1178 private String calcNameFromNameEmbeddedInDescription(JavaClass mainClass, JavaMethod getterMethod,
1179 JavaMethod setterMethod, JavaField beanField, String shortName) {
1180 String nameDesc = this.calcMethodComment(mainClass, getterMethod, setterMethod,
1181 beanField, shortName);
1182 String[] parsed = parseNameDesc(nameDesc);
1183 return parsed[0];
1184 }
1185
1186 private String[] parseNameDesc(String nameDesc) {
1187 String[] parsed = new String[2];
1188 if (nameDesc == null) {
1189 return parsed;
1190 }
1191 nameDesc = nameDesc.trim();
1192 if (!nameDesc.startsWith("Name:")) {
1193 parsed[1] = nameDesc;
1194 return parsed;
1195 }
1196 nameDesc = nameDesc.substring("Name:".length()).trim();
1197 int i = nameDesc.indexOf("\n");
1198 if (i == -1) {
1199 parsed[0] = nameDesc.trim();
1200 return parsed;
1201 }
1202 parsed[0] = nameDesc.substring(0, i).trim();
1203 parsed[1] = nameDesc.substring(i).trim();
1204 return parsed;
1205 }
1206
1207 private String calcDescription(JavaClass mainClass, JavaMethod getterMethod,
1208 JavaMethod setterMethod, JavaField beanField, String shortName) {
1209 String nameDesc = this.calcMethodComment(mainClass, getterMethod, setterMethod,
1210 beanField, shortName);
1211 String[] parsed = parseNameDesc(nameDesc);
1212 return parsed[1];
1213 }
1214
1215 private String calcMethodComment(JavaClass mainClass, JavaMethod getterMethod,
1216 JavaMethod setterMethod,
1217 JavaField beanField,
1218 String shortName) {
1219 String desc = null;
1220 if (getterMethod != null) {
1221 desc = getterMethod.getComment();
1222 if (isCommentNotEmpty(desc)) {
1223 return desc;
1224 }
1225
1226 JavaClass jc = mainClass;
1227 while (true) {
1228 JavaClass superJc = jc.getSuperJavaClass();
1229 if (superJc == null) {
1230 break;
1231 }
1232 JavaMethod parentGetterMethod = this.findGetterMethod(superJc, shortName);
1233 if (parentGetterMethod != null) {
1234 desc = parentGetterMethod.getComment();
1235 if (isCommentNotEmpty(desc)) {
1236 return desc;
1237 }
1238 }
1239 jc = superJc;
1240 }
1241 }
1242 if (setterMethod != null) {
1243 desc = setterMethod.getComment();
1244 if (isCommentNotEmpty(desc)) {
1245 return desc;
1246 }
1247 }
1248 if (beanField != null) {
1249 desc = beanField.getComment();
1250 if (isCommentNotEmpty(desc)) {
1251 return desc;
1252 }
1253 }
1254 desc = calcMethodCommentRecursively(mainClass, getterMethod);
1255 if (isCommentNotEmpty(desc)) {
1256 return desc;
1257 }
1258 desc = calcMethodCommentRecursively(mainClass, setterMethod);
1259 if (isCommentNotEmpty(desc)) {
1260 return desc;
1261 }
1262 return null;
1263 }
1264
1265 private String calcMethodCommentRecursively(JavaClass mainClass, JavaMethod method) {
1266 if (method == null) {
1267 return null;
1268 }
1269 String desc = method.getComment();
1270 if (isCommentNotEmpty(desc)) {
1271 return desc;
1272 }
1273 JavaMethod infcMethod = findInterfaceMethod(mainClass, method, false);
1274 if (infcMethod != null) {
1275 desc = infcMethod.getComment();
1276 if (isCommentNotEmpty(desc)) {
1277 return desc;
1278 }
1279 }
1280 JavaMethod superMethod = findSuperMethod(method);
1281 if (superMethod != null) {
1282 desc = superMethod.getComment();
1283 if (isCommentNotEmpty(desc)) {
1284 return desc;
1285 }
1286 }
1287 return null;
1288 }
1289
1290 private JavaMethod findSuperMethod(JavaMethod method) {
1291
1292
1293
1294 for (JavaMethod superMethod : method.getParentClass().getMethods(true)) {
1295 if (method.equals(superMethod)) {
1296 continue;
1297 }
1298 if (method.getCallSignature().equals(superMethod.getCallSignature())) {
1299 return superMethod;
1300 }
1301 }
1302 return null;
1303 }
1304
1305 private JavaMethod findInterfaceMethod(JavaClass mainClass, JavaMethod method, boolean matchJustOnName) {
1306 String callSig = method.getCallSignature();
1307 if (matchJustOnName) {
1308 callSig = method.getName();
1309 }
1310 JavaClass classToSearch = mainClass;
1311
1312 while (true) {
1313 for (JavaClass infcClass : classToSearch.getImplementedInterfaces()) {
1314 JavaMethod meth = this.findMethodOnInterfaceRecursively(infcClass, callSig, matchJustOnName);
1315 if (meth != null) {
1316
1317 return meth;
1318 }
1319 }
1320 JavaClass superClass = classToSearch.getSuperJavaClass();
1321 if (superClass == null) {
1322
1323
1324 return null;
1325 }
1326 classToSearch = superClass;
1327
1328 }
1329 }
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340 private JavaMethod findMethodOnInterfaceRecursively(JavaClass infcClass, String callSig, boolean matchJustOnName) {
1341
1342
1343 for (JavaMethod infcMethod : infcClass.getMethods()) {
1344 if (callSig.equals(infcMethod.getCallSignature())) {
1345
1346
1347 return infcMethod;
1348 }
1349 if (matchJustOnName) {
1350 if (callSig.equals(infcMethod.getName())) {
1351 return infcMethod;
1352 }
1353 }
1354 }
1355 for (JavaClass subInfc : infcClass.getImplementedInterfaces()) {
1356
1357 JavaMethod infcMethod = findMethodOnInterfaceRecursively(subInfc, callSig, matchJustOnName);
1358 if (infcMethod != null) {
1359
1360 return infcMethod;
1361 }
1362 }
1363
1364
1365 return null;
1366 }
1367
1368 private boolean isCommentNotEmpty(String desc) {
1369 if (desc == null) {
1370 return false;
1371 }
1372 if (desc.trim().isEmpty()) {
1373 return false;
1374 }
1375 if (desc.contains("@inheritDoc")) {
1376 return false;
1377 }
1378 return true;
1379 }
1380
1381 private String getAccessorType(JavaMethod method) {
1382 String accessorType = getAccessorType(method.getAnnotations());
1383 if (accessorType != null) {
1384 return accessorType;
1385 }
1386 accessorType = getAccessorType(method.getParentClass().getAnnotations());
1387 return accessorType;
1388 }
1389
1390 private String getAccessorType(Annotation[] annotations) {
1391 for (Annotation annotation : annotations) {
1392 if (annotation.getType().getJavaClass().getName().equals(
1393 "XmlAccessorType")) {
1394
1395
1396 return annotation.getParameterValue().toString();
1397 }
1398 }
1399 return null;
1400 }
1401
1402 private String stripQuotes(String str) {
1403 if (str.startsWith("\"")) {
1404 str = str.substring(1);
1405 }
1406 if (str.endsWith("\"")) {
1407 str = str.substring(0, str.length() - 1);
1408 }
1409 return str;
1410 }
1411
1412 private String calcMissing(String str) {
1413 if (str == null) {
1414 return "???";
1415 }
1416 if (str.trim().isEmpty()) {
1417 return "???";
1418 }
1419 return str;
1420 }
1421
1422 private void addServiceToList(XmlType xmlType, String serviceKey) {
1423 if (!xmlType.getService().contains(serviceKey)) {
1424 xmlType.setService(xmlType.getService() + ", " + serviceKey);
1425 }
1426 }
1427
1428 private String calcXmlAttribute(JavaField beanField) {
1429 if (beanField == null) {
1430
1431 return "No";
1432 }
1433 for (Annotation annotation : beanField.getAnnotations()) {
1434 if (annotation.getType().getJavaClass().getName().equals("XmlAttribute")) {
1435 return "Yes";
1436 }
1437 }
1438 return "No";
1439 }
1440
1441 private JavaField findField(JavaClass javaClass, String shortName,
1442 JavaMethod setterMethod) {
1443 JavaField field = findField(javaClass, shortName);
1444 if (field != null) {
1445 return field;
1446 }
1447 if (setterMethod != null) {
1448 String paramName = setterMethod.getParameters()[0].getName();
1449 if (paramName.equalsIgnoreCase(shortName)) {
1450 return null;
1451 }
1452 return findField(javaClass, paramName);
1453 }
1454 return null;
1455 }
1456
1457 private JavaField findField(JavaClass javaClass, String name) {
1458 if (name == null) {
1459 return null;
1460 }
1461 for (JavaField field : javaClass.getFields()) {
1462 if (field.getName().equalsIgnoreCase(name)) {
1463 return field;
1464 }
1465
1466 if (field.getName().equals("is" + name)) {
1467 return field;
1468 }
1469 }
1470 JavaClass superClass = javaClass.getSuperJavaClass();
1471 if (superClass == null) {
1472 return null;
1473 }
1474 return findField(superClass, name);
1475 }
1476
1477 private JavaMethod findGetterMethod(JavaClass msClass, String shortName) {
1478 for (JavaMethod method : msClass.getMethods(true)) {
1479 String methodName = method.getName();
1480 if (methodName.equalsIgnoreCase("get" + shortName)) {
1481 return method;
1482 }
1483 if (methodName.toLowerCase().startsWith("is")) {
1484 if (methodName.equalsIgnoreCase("is" + shortName)) {
1485 return method;
1486 }
1487
1488 if (methodName.equalsIgnoreCase(shortName)) {
1489 return method;
1490 }
1491 }
1492
1493 if (methodName.equalsIgnoreCase("getInState") && shortName.equalsIgnoreCase(
1494 "InStateFlag")) {
1495 return method;
1496 }
1497 }
1498 return null;
1499 }
1500
1501 private JavaMethod findSetterMethod(JavaClass msClass, String shortName) {
1502 for (JavaMethod method : msClass.getMethods(true)) {
1503 if (method.getName().equals("set" + shortName)) {
1504 return method;
1505 }
1506
1507 if (method.getName().equals("setIs" + shortName)) {
1508 return method;
1509 }
1510
1511 if (method.getName().equals("setInStateFlag") && shortName.equals(
1512 "InState")) {
1513 return method;
1514 }
1515 }
1516 return null;
1517 }
1518 private static final String[] SETTER_METHODS_TO_SKIP = {
1519
1520 "ValidationResultInfo.setWarning",
1521 "ValidationResultInfo.setError",
1522
1523 "CredentialProgramInfo.setDiplomaTitle",
1524
1525 "CredentialProgramInfo.setType",
1526
1527 "CredentialProgramInfo.setHegisCode",
1528 "CredentialProgramInfo.setCip2000Code",
1529 "CredentialProgramInfo.setCip2010Code",
1530 "CredentialProgramInfo.setSelectiveEnrollmentCode",
1531 "CoreProgramInfo.setDiplomaTitle",
1532
1533
1534
1535 "CoreProgramInfo.setHegisCode",
1536 "CoreProgramInfo.setCip2000Code",
1537 "CoreProgramInfo.setCip2010Code",
1538 "CoreProgramInfo.setSelectiveEnrollmentCode",
1539 "WhenConstraint.setValue"
1540 };
1541 private static final String[] GETTER_METHODS_TO_SKIP = {
1542
1543 "ValidationResultInfo.getWarning",
1544 "ValidationResultInfo.getError",
1545
1546 "CredentialProgramInfo.getDiplomaTitle",
1547
1548 "CredentialProgramInfo.getType",
1549
1550 "CredentialProgramInfo.getHegisCode",
1551 "CredentialProgramInfo.getCip2000Code",
1552 "CredentialProgramInfo.getCip2010Code",
1553 "CredentialProgramInfo.getSelectiveEnrollmentCode",
1554 "CoreProgramInfo.getDiplomaTitle",
1555
1556
1557
1558 "CoreProgramInfo.getHegisCode",
1559 "CoreProgramInfo.getCip2000Code",
1560 "CoreProgramInfo.getCip2010Code",
1561 "CoreProgramInfo.getSelectiveEnrollmentCode",
1562 "WhenConstraint.getValue"
1563 };
1564
1565 private boolean isSetterMethodToProcess(JavaMethod method, String className) {
1566 if (!method.getName().startsWith("set")) {
1567 return false;
1568 }
1569 if (method.getParameters().length != 1) {
1570 return false;
1571 }
1572 if (method.isPrivate()) {
1573 return false;
1574 }
1575 if (method.isProtected()) {
1576 return false;
1577 }
1578 if (method.isStatic()) {
1579 return false;
1580 }
1581 if (method.getParentClass().getPackageName().startsWith("java")) {
1582 return false;
1583 }
1584 String fullName = className + "." + method.getName();
1585 for (String skip : SETTER_METHODS_TO_SKIP) {
1586 if (skip.equals(fullName)) {
1587 return false;
1588 }
1589 }
1590
1591
1592
1593
1594 for (Annotation annotation : method.getAnnotations()) {
1595 if (annotation.getType().getJavaClass().getName().equals("XmlTransient")) {
1596 return false;
1597 }
1598 }
1599 return true;
1600 }
1601
1602 private boolean isGetterMethodToProcess(JavaMethod method, String className) {
1603 if (!method.getName().startsWith("get")) {
1604 if (!method.getName().startsWith("is")) {
1605 return false;
1606 }
1607 }
1608 if (method.getParameters().length != 0) {
1609 return false;
1610 }
1611 if (method.isPrivate()) {
1612 return false;
1613 }
1614 if (method.isProtected()) {
1615 return false;
1616 }
1617 if (method.isStatic()) {
1618 return false;
1619 }
1620 if (method.getParentClass().getPackageName().startsWith("java")) {
1621 return false;
1622 }
1623 String fullName = className + "." + method.getName();
1624 for (String skip : GETTER_METHODS_TO_SKIP) {
1625 if (skip.equals(fullName)) {
1626 return false;
1627 }
1628 }
1629
1630
1631
1632
1633 for (Annotation annotation : method.getAnnotations()) {
1634 if (annotation.getType().getJavaClass().getName().equals("XmlTransient")) {
1635 return false;
1636 }
1637 }
1638 return true;
1639 }
1640
1641 private String calcShortNameFromSetter(JavaMethod method) {
1642 return method.getName().substring(3);
1643 }
1644
1645 private String calcShortNameFromGetter(JavaMethod method) {
1646 if (method.getName().startsWith("get")) {
1647 return method.getName().substring(3);
1648 }
1649 if (method.getName().startsWith("is")) {
1650 return method.getName().substring(2);
1651 }
1652 throw new IllegalArgumentException(method.getName()
1653 + " does not start with is or get");
1654 }
1655
1656 private String calcCardinality(JavaClass mainClass, JavaMethod getterMethod,
1657 JavaMethod setterMethod, JavaField beanField, String shortName) {
1658 if (isReturnACollection(mainClass, getterMethod, setterMethod, beanField, shortName)) {
1659 return "Many";
1660 }
1661 return "One";
1662 }
1663
1664 private boolean isReturnACollection(JavaClass mainClass, JavaMethod getterMethod,
1665 JavaMethod setterMethod, JavaField beanField, String shortName) {
1666 if (getterMethod != null) {
1667 return isCollection(getterMethod.getReturnType());
1668 }
1669 if (beanField != null) {
1670 return isCollection(beanField.getType());
1671 }
1672
1673 return false;
1674 }
1675
1676 private boolean isCollection(Type type) {
1677 JavaClass javaClass = type.getJavaClass();
1678 return this.isCollection(javaClass);
1679 }
1680
1681 private boolean isCollection(JavaClass javaClass) {
1682 if (javaClass.getName().equals("LocalKeyList")) {
1683 return true;
1684 }
1685 if (javaClass.getName().equals("MessageGroupKeyList")) {
1686 return true;
1687 }
1688 if (javaClass.getName().equals(List.class.getSimpleName())) {
1689 return true;
1690 }
1691 if (javaClass.getName().equals(ArrayList.class.getSimpleName())) {
1692 return true;
1693 }
1694 if (javaClass.getName().equals(Collection.class.getSimpleName())) {
1695 return true;
1696 }
1697 if (javaClass.getName().equals(Set.class.getSimpleName())) {
1698 return true;
1699 }
1700 return false;
1701 }
1702
1703 private String calcType(JavaClass mainClass, JavaMethod getterMethod,
1704 JavaMethod setterMethod, JavaField beanField, String shortName) {
1705 if (getterMethod != null) {
1706 return calcTypeOfGetterMethodReturn(getterMethod);
1707 }
1708 if (beanField != null) {
1709 Type type = beanField.getType();
1710 return calcType(type);
1711 }
1712
1713 return null;
1714 }
1715
1716 private String calcTypeOfGetterMethodReturn(JavaMethod getterMethod) {
1717 Type type = getterMethod.getReturnType();
1718 return calcType(type);
1719 }
1720
1721 private String calcType(Type type) {
1722 if (type == null) {
1723 return "void";
1724 }
1725 if (isCollection(type.getJavaClass())) {
1726 return calcType(calcRealJavaClass(type)) + "List";
1727 }
1728 return calcType(calcRealJavaClass(type));
1729 }
1730
1731 private Annotation findJavaAnnotation(String name, JavaClass clazz) {
1732
1733 Annotation[] annotations = clazz.getAnnotations();
1734
1735 for (Annotation annotation : annotations) {
1736
1737 if (annotation.getType().getJavaClass().getName().equals(name)) {
1738 return annotation;
1739 }
1740 }
1741 return null;
1742 }
1743 private String calcType(JavaClass javaClass) {
1744
1745 if (javaClass.isEnum()) {
1746
1747 if (!JavaClassAnnotationUtils.doesAnnotationExist(
1748 XmlEnum.class.getSimpleName(), javaClass)) {
1749
1750
1751 if (javaClass.getName().equals("WriteAccess")) {
1752
1753 return "String";
1754 } else if (javaClass.getName().equals("Widget")) {
1755
1756 return "String";
1757 } else if (javaClass.getName().equals("Usage")) {
1758
1759 return "String";
1760 } else {
1761
1762
1763 return javaClass.getFullyQualifiedName();
1764 }
1765
1766 }
1767
1768 Class<?>annotationSpecifiedType = JavaClassAnnotationUtils.extractXmlEnumValue(javaClass);
1769
1770 return annotationSpecifiedType.getSimpleName();
1771
1772 }
1773
1774 if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1775 return "StringList";
1776 }
1777 if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1778 return "StringList";
1779 }
1780
1781 if (javaClass.getName().equals("java$util$Map")) {
1782 return "Map<String, String>";
1783 }
1784 if (javaClass.getName().equals("Map")) {
1785
1786 return "Map<String, String>";
1787 }
1788 return javaClass.getName();
1789 }
1790
1791 private JavaClass calcRealJavaClassOfGetterReturn(JavaMethod getterMethod) {
1792 if (getterMethod == null) {
1793 return null;
1794 }
1795 Type type = getterMethod.getReturnType();
1796 return this.calcRealJavaClass(type);
1797 }
1798
1799 private JavaClass calcRealJavaClass(Type type) {
1800 if (type == null) {
1801 return null;
1802 }
1803 JavaClass javaClass = type.getJavaClass();
1804 if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1805 return STRING_JAVA_CLASS;
1806 }
1807 if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1808 return STRING_JAVA_CLASS;
1809 }
1810 if (!this.isCollection(javaClass)) {
1811 return javaClass;
1812 }
1813
1814
1815
1816
1817
1818
1819
1820 Type[]collectionTypeArguments = type.getActualTypeArguments();
1821
1822 if (collectionTypeArguments == null)
1823 return new JavaClass (Object.class.getName());
1824 else
1825 return collectionTypeArguments[0].getJavaClass();
1826 }
1827
1828 private boolean isComplex(JavaClass javaClass) {
1829 if (javaClass.getName().equals ("void")) {
1830 return false;
1831 }
1832 if (javaClass.isEnum()) {
1833 return false;
1834 }
1835 if (javaClass.getName().equals(String.class.getSimpleName())) {
1836 return false;
1837 }
1838 if (javaClass.getName().equals(Integer.class.getSimpleName())) {
1839 return false;
1840 }
1841 if (javaClass.getName().equals(Date.class.getSimpleName())) {
1842 return false;
1843 }
1844 if (javaClass.getName().equals(Long.class.getSimpleName())) {
1845 return false;
1846 }
1847 if (javaClass.getName().equals(Boolean.class.getSimpleName())) {
1848 return false;
1849 }
1850 if (javaClass.getName().equals(Double.class.getSimpleName())) {
1851 return false;
1852 }
1853 if (javaClass.getName().equals(Float.class.getSimpleName())) {
1854 return false;
1855 }
1856 if (javaClass.getName().equals(int.class.getSimpleName())) {
1857 return false;
1858 }
1859 if (javaClass.getName().equals(long.class.getSimpleName())) {
1860 return false;
1861 }
1862 if (javaClass.getName().equals(boolean.class.getSimpleName())) {
1863 return false;
1864 }
1865 if (javaClass.getName().equals(double.class.getSimpleName())) {
1866 return false;
1867 }
1868 if (javaClass.getName().equals(float.class.getSimpleName())) {
1869 return false;
1870 }
1871 if (javaClass.getName().equals(Map.class.getSimpleName())) {
1872 return false;
1873 }
1874
1875 if (javaClass.getName().equals(String.class.getName())) {
1876 return false;
1877 }
1878 if (javaClass.getName().equals(Integer.class.getName())) {
1879 return false;
1880 }
1881 if (javaClass.getName().equals(Date.class.getName())) {
1882 return false;
1883 }
1884 if (javaClass.getName().equals(Long.class.getName())) {
1885 return false;
1886 }
1887 if (javaClass.getName().equals(Boolean.class.getName())) {
1888 return false;
1889 }
1890 if (javaClass.getName().equals(Double.class.getName())) {
1891 return false;
1892 }
1893 if (javaClass.getName().equals(Float.class.getName())) {
1894 return false;
1895 }
1896 if (javaClass.getName().equals(int.class.getName())) {
1897 return false;
1898 }
1899 if (javaClass.getName().equals(long.class.getName())) {
1900 return false;
1901 }
1902 if (javaClass.getName().equals(boolean.class.getName())) {
1903 return false;
1904 }
1905 if (javaClass.getName().equals(double.class.getName())) {
1906 return false;
1907 }
1908 if (javaClass.getName().equals(float.class.getName())) {
1909 return false;
1910 }
1911 if (javaClass.getName().equals(BigDecimal.class.getSimpleName())) {
1912 return false;
1913 }
1914 if (javaClass.getName().equals(Map.class.getName())) {
1915 return false;
1916 }
1917 if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1918 return false;
1919 }
1920 if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1921 return false;
1922 }
1923 if (javaClass.getName().equals("java$util$Map")) {
1924 return false;
1925 }
1926 return true;
1927 }
1928
1929 @Override
1930 public String toString() {
1931 return "ServiceContractModelQDoxLoader{" +
1932 "sourceDirectories=" + sourceDirectories +
1933 ", services=" + services +
1934 ", serviceMethods=" + serviceMethods +
1935 ", xmlTypeMap=" + xmlTypeMap +
1936 ", messageStructures=" + messageStructures +
1937 ", validateKualiStudent=" + validateKualiStudent +
1938 '}';
1939 }
1940 }