View Javadoc
1   /**
2    * Copyright 2004-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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   * @author nwright
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 //  System.out.println ("ServiceContractModelQDoxLoader: Starting parse");
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 //   System.out.println ("processing service=" + javaClass.getName ());
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 //   for (DocletTag tag : javaClass.getTags ())
165 //   {
166 //    System.out.println ("ServiceContractModelQDoxLoader: Class: "
167 //                        + javaClass.getName () + " has tag=" + dump (
168 //      tag));
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 //    for (DocletTag tag : javaMethod.getTags ())
183 //    {
184 //     System.out.println ("ServiceContractModelQDoxLoader: Method: "
185 //                         + service.getName () + "."
186 //                         + javaMethod.getName ()
187 //                         + " has tag=" + dump (tag));
188 //    }
189                 // parameters
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                 // errors
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                 // return values
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 		 * As inheritence is useful from a technical level but not as much from a business level
270 		 * This lets the union of the methods from all of the interfaces be listed in the contract.
271 		 */
272 		JavaClass[] interfaces = javaClass.getImplementedInterfaces();
273 		
274 		for (JavaClass intfc : interfaces) {
275 			
276 			if (!isAService(intfc)) {
277 				// only add the methods if this is not a service
278 				// e.g. extends ServiceBusinessLogic
279 				for (JavaMethod javaMethod : intfc.getMethods()) {
280 				
281 					methods.add(javaMethod);
282 				}
283 			}
284 			
285 		}
286 		
287 		// add the main service methods last incase we override any from the parent interfaces.
288 		// note the heirarchy is only guaranteed relative to the target service class (if there are two levels or more of 
289 		// heirarchy there is no guarantee the method ordering will be correct).
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 //  System.out.println ("looking if javaClass is a service to process=" + javaClass.getName () + "=" + javaClass.getPackageName ());
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 //   System.out.println ("looking for webservice tag=" + annotation.getType ().getJavaClass ().getName ());
313             if (annotation.getType().getJavaClass().getName().equals("WebService")) {
314 //    System.out.println ("Processing web service=" + javaClass.getPackageName ()
315 //                        + "." + javaClass.getName ());
316                 return true;
317             }
318         }
319         // This includes RICE's business object services even though they are not web services
320         // because often they are the only real service they have exposed and it helps to document
321         // them to see what the data really is like
322         if (javaClass.getName().endsWith("BoService")) {
323             return true;
324         }
325         // This includes KSA's internal services even though they are not web services
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 //  System.out.println ("skipping service because it is not a web service="
333 //                      + javaClass.getPackageName () + "." + javaClass.getName ());
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 //            System.out.println("ServiceContractModelQDoxLoader:" + javaClass.getName()
342 //                    + " implements " + interfaceClass.getName());
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         // ks uses this pattern
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         // rice uses this pattern
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 //                System.out.println("found matching interface " + infc.getName());
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             // overide the shortName if the bean field has an XmlAttribute name=xxx
639             // this catches the key=id switch
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.setColumnName(calcImplementationColumn(getterMethod, setterMethod, beanField));
676             ms.setDeprecated(isDeprecated(getterMethod));
677             ms.setStatus("???");
678             ms.setLookup(calcLookup (messageStructureJavaClass, getterMethod, setterMethod,
679                     beanField, serviceKey, ms.getXmlObject(), shortName, ms.getType()));
680             ms.setPrimaryKey(calcPrimaryKey (messageStructureJavaClass, getterMethod, setterMethod,
681                     beanField, serviceKey, ms.getXmlObject(), shortName, ms.getType()));
682             ms.setOverriden(this.calcOverridden(messageStructureJavaClass, getterMethod));
683             JavaClass subObjToAdd = this.calcRealJavaClassOfGetterReturn(getterMethod);
684             if (subObjToAdd != null) {
685 //                if (!subObjToAdd.isEnum()) {
686                 if (!subObjToAdd.getName().equals("Object")) {
687                     if (!subObjToAdd.getName().equals("LocaleKeyList")) {
688                         if (!subObjToAdd.getName().equals("MessageGroupKeyList")) {
689                             subObjectsToAdd.add(subObjToAdd);
690                         }
691                     }
692                 }
693 //                }
694             }
695         }
696         // now add all it's complex sub-objects if they haven't already been added
697         for (JavaClass subObjectToAdd : subObjectsToAdd) {
698             XmlType xmlType = xmlTypeMap.get(calcType(subObjectToAdd));
699             if (xmlType == null) {
700                 try {
701 					addXmlTypeAndMessageStructure(subObjectToAdd, serviceKey);
702 				} catch (Exception e) {
703 					String message = "failed to parse subobject structure: " + subObjectToAdd + " : " + serviceKey;
704 					// log into message
705 					log.error (message + " : " + e.getMessage());
706 					// log into debug log
707 					log.debug(message, e);
708 				}
709             } else {
710                 addServiceToList(xmlType, serviceKey);
711             }
712         }
713         return;
714     }
715 
716     private String stripList (String type) {
717         if (type == null) {
718             return null;
719         }
720         if (type.endsWith("List")) {
721             return type.substring(0, type.length() - "List".length());
722         }
723         return type;
724     }
725     
726     
727     private boolean calcPrimaryKey (JavaClass mainClass, JavaMethod getterMethod,
728             JavaMethod setterMethod, JavaField beanField, String serviceKey, String xmlObject, String shortName, String type) {
729         if (!type.equalsIgnoreCase ("String")) {
730             return false;
731         }
732         if (shortName.equalsIgnoreCase ("Id")) {
733             return true;
734         }
735         if (shortName.equalsIgnoreCase ("Key")) {
736             return true;
737         }
738         // Special fix up for principal
739 //        log.warn(serviceKey + ":" + xmlObject + "." + shortName);
740         if (xmlObject.equalsIgnoreCase("Principal")) {
741             if (shortName.equalsIgnoreCase("principalId")) {
742                 return true;
743             }
744         }
745         return false;
746     }
747     
748 
749     private Lookup calcLookup (JavaClass mainClass, JavaMethod getterMethod,
750             JavaMethod setterMethod, JavaField beanField, String serviceKey, String xmlObject, String shortName, String type) {
751         type = this.stripList(type);
752         // all keys and Ids are represented as strings
753         if (!type.equalsIgnoreCase ("String")) {
754             return null;
755         }
756         Lookup lookup = LOOKUP_MAPPINGS.get(shortName);
757         if (lookup == null) {
758             if (this.endsWithIdOrKey(shortName)) {
759                 log.warn (serviceKey + ":" + xmlObject + "." + shortName + " ends with id or key but has no lookup defined");
760             } 
761         }
762         return lookup;
763     }
764     
765     private boolean endsWithIdOrKey (String shortName) {
766         if (shortName.equals ("Id")) {
767             return false;
768         }
769         if (shortName.equals ("Key")) {
770             return false;
771         }
772         if (shortName.endsWith ("Id")) {
773             return true;
774         }
775         if (shortName.endsWith ("Ids")) {
776             return true;
777         }
778         if (shortName.endsWith ("Key")) {
779             return true;
780         }
781         if (shortName.endsWith ("Keys")) {
782             return true;
783         }
784         return false;
785     }
786     
787     private static Map<String, Lookup> LOOKUP_MAPPINGS;
788     
789     {
790         // global ones where the name matches the object 
791         LOOKUP_MAPPINGS = new LinkedHashMap<String, Lookup> ();
792         LOOKUP_MAPPINGS.put("typeKey", new Lookup ("Type", "TypeInfo"));
793         LOOKUP_MAPPINGS.put("stateKey", new Lookup ("State", "StateInfo"));
794         LOOKUP_MAPPINGS.put("lifecycleKey", new Lookup ("State", "LifecycleInfo"));
795         LOOKUP_MAPPINGS.put("orgId", new Lookup ("Organization", "OrgInfo"));
796         LOOKUP_MAPPINGS.put("orgIds", new Lookup ("Organization", "OrgInfo"));
797         LOOKUP_MAPPINGS.put("organizationId", new Lookup ("Organization", "OrgInfo"));
798         LOOKUP_MAPPINGS.put("organizationIds", new Lookup ("Organization", "OrgInfo"));
799         LOOKUP_MAPPINGS.put("atpId", new Lookup ("Atp", "AtpInfo"));
800         LOOKUP_MAPPINGS.put("atpIds", new Lookup ("Atp", "AtpInfo"));
801         LOOKUP_MAPPINGS.put("termId", new Lookup ("AcademicCalendar", "TermInfo"));
802         LOOKUP_MAPPINGS.put("termIds", new Lookup ("AcademicCalendar", "TermInfo"));
803         LOOKUP_MAPPINGS.put("luiId", new Lookup ("Lui", "LuiInfo"));
804         LOOKUP_MAPPINGS.put("luiIds", new Lookup ("Lui", "LuiInfo"));
805         LOOKUP_MAPPINGS.put("cluId", new Lookup ("Clu", "CluInfo"));
806         LOOKUP_MAPPINGS.put("cluIds", new Lookup ("Clu", "CluInfo"));
807         LOOKUP_MAPPINGS.put("credentialProgramId", new Lookup ("Program", "CredentialProgramInfo"));
808         LOOKUP_MAPPINGS.put("credentialProgramIds", new Lookup ("Program", "CredentialProgramInfo"));
809         LOOKUP_MAPPINGS.put("credentialProgramId", new Lookup ("Program", "CredentialProgramInfo"));
810         LOOKUP_MAPPINGS.put("credentialProgramIds", new Lookup ("Program", "CredentialProgramInfo"));
811         LOOKUP_MAPPINGS.put("coreProgramId", new Lookup ("Program", "CoreProgramInfo"));
812         LOOKUP_MAPPINGS.put("coreProgramIds", new Lookup ("Program", "CoreProgramInfo"));
813         LOOKUP_MAPPINGS.put("resultScaleKey", new Lookup ("LRC", "ResultScaleInfo"));
814         LOOKUP_MAPPINGS.put("resultScaleKeys", new Lookup ("LRC", "ResultScaleInfo"));
815         LOOKUP_MAPPINGS.put("resultValuesGroupKey", new Lookup ("LRC", "ResultValuesGroupInfo"));
816         LOOKUP_MAPPINGS.put("resultValuesGroupKeys", new Lookup ("LRC", "ResultValuesGroupInfo"));
817         LOOKUP_MAPPINGS.put("resultValueKey", new Lookup ("LRC", "ResultValueInfo"));
818         LOOKUP_MAPPINGS.put("resultValueKeys", new Lookup ("LRC", "ResultValueInfo"));
819         LOOKUP_MAPPINGS.put("scheduleId", new Lookup ("Schedule", "ScheduleInfo"));
820         LOOKUP_MAPPINGS.put("scheduleIds", new Lookup ("Schedule", "ScheduleInfo"));
821         LOOKUP_MAPPINGS.put("courseId", new Lookup ("Course", "CourseInfo"));
822         LOOKUP_MAPPINGS.put("courseIds", new Lookup ("Course", "CourseInfo"));
823         LOOKUP_MAPPINGS.put("formatId", new Lookup ("Course", "FormatInfo"));
824         LOOKUP_MAPPINGS.put("formatIds", new Lookup ("Course", "FormatInfo"));
825         LOOKUP_MAPPINGS.put("activityId", new Lookup ("Course", "ActivityInfo"));
826         LOOKUP_MAPPINGS.put("activityIds", new Lookup ("Course", "ActivityInfo"));
827         LOOKUP_MAPPINGS.put("courseOfferingId", new Lookup ("CourseOffering", "CourseOfferingInfo"));
828         LOOKUP_MAPPINGS.put("courseOfferingIds", new Lookup ("CourseOffering", "CourseOfferingInfo"));
829         LOOKUP_MAPPINGS.put("formatOfferingId", new Lookup ("CourseOffering", "FormatOfferingInfo"));
830         LOOKUP_MAPPINGS.put("formatOfferingIds", new Lookup ("CourseOffering", "FormatOfferingInfo"));
831         LOOKUP_MAPPINGS.put("activityOfferingId", new Lookup ("CourseOffering", "ActivityOfferingInfo"));
832         LOOKUP_MAPPINGS.put("activityOfferingIds", new Lookup ("CourseOffering", "ActivityOfferingInfo"));
833         LOOKUP_MAPPINGS.put("socRolloverResultId", new Lookup ("CourseOfferingSet", "SorRolloverResultInfo"));
834         LOOKUP_MAPPINGS.put("socRolloverResultIds", new Lookup ("CourseOfferingSet", "SorRolloverResultInfo"));
835         LOOKUP_MAPPINGS.put("socRolloverResultItemId", new Lookup ("CourseOfferingSet", "SorRolloverResultItemInfo"));
836         LOOKUP_MAPPINGS.put("socRolloverResultItemIds", new Lookup ("CourseOfferingSet", "SorRolloverResultItemInfo"));
837         LOOKUP_MAPPINGS.put("buildingId", new Lookup ("Room", "BuildingInfo"));
838         LOOKUP_MAPPINGS.put("buildingIds", new Lookup ("Room", "BuildingInfo"));
839         LOOKUP_MAPPINGS.put("roomId", new Lookup ("Room", "RoomInfo"));
840         LOOKUP_MAPPINGS.put("roomIds", new Lookup ("Room", "RoomInfo"));
841         LOOKUP_MAPPINGS.put("populationId", new Lookup ("Population", "PopulationInfo"));
842         LOOKUP_MAPPINGS.put("populationIds", new Lookup ("Population", "PopulationInfo"));
843         
844         
845         // COMMON RICE IDENTITY SERVICE
846         LOOKUP_MAPPINGS.put("principalId", new Lookup ("rice.kim.Identity", "Principal"));
847         LOOKUP_MAPPINGS.put("principalIds", new Lookup ("rice.kim.Identity", "Principal"));
848         // TODO: fix these Ids that currently maps to principal instead of the entity id
849         LOOKUP_MAPPINGS.put("personId", new Lookup ("rice.kim.Identity", "Principal"));
850         LOOKUP_MAPPINGS.put("personIds", new Lookup ("rice.kim.Identity", "Principal"));
851         LOOKUP_MAPPINGS.put("instructorId", new Lookup ("rice.kim.Identity", "Principal"));
852         LOOKUP_MAPPINGS.put("instructorIds", new Lookup ("rice.kim.Identity", "Principal"));
853         LOOKUP_MAPPINGS.put("studentId", new Lookup ("rice.kim.Identity", "Principal"));
854         LOOKUP_MAPPINGS.put("studentIds", new Lookup ("rice.kim.Identity", "Principal"));
855         
856         // Common objects 
857         // TimeAmount
858         LOOKUP_MAPPINGS.put("atpDurationTypeKey", new Lookup ("Type", "TypeInfo")); 
859         LOOKUP_MAPPINGS.put("currencyTypeKey", new Lookup ("Type", "TypeInfo"));        
860         // Context
861         LOOKUP_MAPPINGS.put("authenticatedPrincipalId", new Lookup ("rice.kim.Identity", "Principal"));
862         // meta
863         LOOKUP_MAPPINGS.put("createId", new Lookup ("rice.kim.Identity", "Principal"));
864         LOOKUP_MAPPINGS.put("updateId", new Lookup ("rice.kim.Identity", "Principal"));
865         LOOKUP_MAPPINGS.put("agendaId", new Lookup ("rice.krms.Agenda", "Agenda"));
866         LOOKUP_MAPPINGS.put("agendaIds", new Lookup ("rice.krms.Agenda", "Agenda"));
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         LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
874         
875         
876         // TODO: replace or augment this special list of ones with annotations in the contract itself
877         // program service
878         LOOKUP_MAPPINGS.put("unitsContentOwner", new Lookup ("Organization", "OrgInfo"));
879         LOOKUP_MAPPINGS.put("unitsDeployment", new Lookup ("Organization", "OrgInfo"));
880         LOOKUP_MAPPINGS.put("unitsStudentOversight", new Lookup ("Organization", "OrgInfo"));
881         LOOKUP_MAPPINGS.put("unitsFinancialResources", new Lookup ("Organization", "OrgInfo"));
882         LOOKUP_MAPPINGS.put("unitsFinancialControl", new Lookup ("Organization", "OrgInfo"));
883         LOOKUP_MAPPINGS.put("divisionsContentOwner", new Lookup ("Organization", "OrgInfo"));
884         LOOKUP_MAPPINGS.put("divisionsDeployment", new Lookup ("Organization", "OrgInfo"));
885         LOOKUP_MAPPINGS.put("divisionsStudentOversight", new Lookup ("Organization", "OrgInfo"));
886         LOOKUP_MAPPINGS.put("divisionsFinancialResources", new Lookup ("Organization", "OrgInfo"));
887         LOOKUP_MAPPINGS.put("divisionsFinancialControl", new Lookup ("Organization", "OrgInfo"));
888         LOOKUP_MAPPINGS.put("startTerm", new Lookup ("AcademicCalendar", "TermInfo"));
889         LOOKUP_MAPPINGS.put("endTerm", new Lookup ("AcademicCalendar", "TermInfo"));
890         LOOKUP_MAPPINGS.put("endProgramEntryTerm", new Lookup ("AcademicCalendar", "TermInfo"));
891         LOOKUP_MAPPINGS.put("resultOptions", new Lookup ("LRC", "ResultValuesGroupInfo"));
892         LOOKUP_MAPPINGS.put("programRequirements", new Lookup ("Program", "ProgramRequirementInfo"));
893         // share by program and course
894         LOOKUP_MAPPINGS.put("parentRelType", new Lookup ("Type", "TypeInfo"));
895         LOOKUP_MAPPINGS.put("parentLoRelationid", new Lookup ("LearningObjective", "LoInfo"));
896         
897         // type service
898         LOOKUP_MAPPINGS.put("ownerTypeKey", new Lookup ("Type", "TypeInfo"));
899         LOOKUP_MAPPINGS.put("relatedTypeKey", new Lookup ("Type", "TypeInfo"));
900         
901         // State service (there are no special purpose ones) 
902         
903         // LRC service (there are no special purpose ones)
904         
905         // atp
906         LOOKUP_MAPPINGS.put("adminOrgId", new Lookup ("Organization", "OrgInfo"));
907         LOOKUP_MAPPINGS.put("relatedAtpId", new Lookup ("Atp", "AtpInfo"));
908         LOOKUP_MAPPINGS.put("relativeAnchorMilestoneId", new Lookup ("Atp", "MilestoneInfo"));
909         
910         // Lui
911         LOOKUP_MAPPINGS.put("relatedLuiTypes", new Lookup ("Type", "TypeInfo"));
912         LOOKUP_MAPPINGS.put("relatedLuiId", new Lookup ("Lui", "LuiInfo"));
913         
914         // Course Offering
915         LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
916         LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
917         
918         // TODO: finish the services
919         LOOKUP_MAPPINGS.put("unitsDeploymentOrgIds", new Lookup ("Organization", "OrgInfo"));
920         LOOKUP_MAPPINGS.put("unitsContentOwnerOrgIds", new Lookup ("Organization", "OrgInfo"));
921         LOOKUP_MAPPINGS.put("unitsContentOwnerId", new Lookup ("Organization", "OrgInfo"));
922         LOOKUP_MAPPINGS.put("jointOfferingIds", new Lookup ("CourseOffering", "CourseOfferingInfo"));
923         LOOKUP_MAPPINGS.put("gradingOptionId", new Lookup ("LRC", "ResultValuesGroupInfo"));
924         LOOKUP_MAPPINGS.put("creditOptionId", new Lookup ("LRC", "ResultValuesGroupInfo"));
925         LOOKUP_MAPPINGS.put("waitlistLevelTypeKey", new Lookup ("Type", "TypeInfo"));
926         LOOKUP_MAPPINGS.put("waitlistTypeKey", new Lookup ("Type", "TypeInfo"));
927         LOOKUP_MAPPINGS.put("activityOfferingTypeKeys", new Lookup ("Type", "TypeInfo"));
928         LOOKUP_MAPPINGS.put("gradeRosterLevelTypeKey", new Lookup ("Type", "TypeInfo"));
929         LOOKUP_MAPPINGS.put("finalExamLevelTypeKey", new Lookup ("Type", "TypeInfo"));
930         LOOKUP_MAPPINGS.put("schedulingStateKey", new Lookup ("State", "StateInfo"));
931         LOOKUP_MAPPINGS.put("gradingOptionKeys", new Lookup ("LRC", "ResultValuesGroupInfo"));
932         LOOKUP_MAPPINGS.put("creditOptionId", new Lookup ("LRC", "ResultValuesGroupInfo"));
933         LOOKUP_MAPPINGS.put("gradingOptionKeys", 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         LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
945         
946     }
947     
948     private boolean calcOverridden(JavaClass mainClass, JavaMethod getterMethod) {
949         if (getterMethod == null) {
950             return false;
951         }
952         JavaMethod infcGetter = null;
953         if (getterMethod.getParentClass().isInterface()) {
954             infcGetter = getterMethod;
955         }
956         if (infcGetter == null) {
957             infcGetter = findInterfaceMethod(mainClass, getterMethod, false);
958         }
959         if (infcGetter == null) {
960             return false;
961         }
962         Annotation annotation = this.getAnnotation(infcGetter, null, null, "Override");
963         if (annotation != null) {
964             return true;
965         }
966         return false;
967     }
968 
969     private String calcComment(JavaClass javaClass) {
970         return this.calcComment(javaClass.getComment());
971     }
972 
973     private String calcComment(String comment) {
974         return this.parseCommentVersion(comment)[0];
975     }
976 
977     private String calcVersion(JavaClass javaClass) {
978         DocletTag tag = javaClass.getTagByName("version", true);
979         if (tag != null) {
980             return tag.getValue();
981         }
982         return this.calcVersion(javaClass.getComment());
983     }
984 
985     private String calcVersion(String comment) {
986         return this.parseCommentVersion(comment)[1];
987     }
988 
989     private String[] parseCommentVersion(String commentVersion) {
990         String[] parsed = new String[2];
991         if (commentVersion == null) {
992             return parsed;
993         }
994         commentVersion = commentVersion.trim();
995         int i = commentVersion.toLowerCase().indexOf("\nversion:");
996         if (i == -1) {
997             parsed[0] = commentVersion;
998             return parsed;
999         }
1000         parsed[1] = commentVersion.substring(i + "\nversion:".length()).trim();
1001         parsed[0] = commentVersion.substring(0, i).trim();
1002 
1003         return parsed;
1004     }
1005 
1006     private Annotation getAnnotation(JavaMethod getterMethod,
1007             JavaMethod setterMethod, JavaField beanField, String type) {
1008         if (beanField != null) {
1009 
1010             for (Annotation annotation : beanField.getAnnotations()) {
1011                 if (annotation.getType().getJavaClass().getName().equals(type)) {
1012                     return annotation;
1013                 }
1014             }
1015         }
1016         if (getterMethod != null) {
1017 
1018             for (Annotation annotation : getterMethod.getAnnotations()) {
1019                 if (annotation.getType().getJavaClass().getName().equals(type)) {
1020                     return annotation;
1021                 }
1022             }
1023         }
1024         if (setterMethod != null) {
1025 
1026             for (Annotation annotation : setterMethod.getAnnotations()) {
1027                 if (annotation.getType().getJavaClass().getName().equals(type)) {
1028                     return annotation;
1029                 }
1030             }
1031         }
1032         return null;
1033     }
1034 
1035     private String calcRequired(JavaMethod getterMethod,
1036             JavaMethod setterMethod, JavaField beanField) {
1037         Annotation annotation = this.getAnnotation(getterMethod, setterMethod, beanField, "XmlElement");
1038         if (annotation == null) {
1039             annotation = this.getAnnotation(getterMethod, setterMethod, beanField, "XmlAttribute");
1040         }
1041         if (annotation != null) {
1042             Object required = annotation.getNamedParameter("required");
1043             if (required != null) {
1044                 if (required.toString().equalsIgnoreCase("true")) {
1045                     return "Required";
1046                 }
1047             }
1048         }
1049         if (getterMethod != null) {
1050             DocletTag tag = getterMethod.getTagByName("required", true);
1051             if (tag != null) {
1052                 if (tag.getValue() == null) {
1053                     return "Required";
1054                 }
1055                 String required = "Required " + tag.getValue();
1056                 return required.trim();
1057             }
1058         }
1059         return null;
1060     }
1061 
1062     private String calcReadOnly(JavaMethod getterMethod,
1063             JavaMethod setterMethod, JavaField beanField) {
1064         if (getterMethod != null) {
1065             DocletTag tag = getterMethod.getTagByName("readOnly", true);
1066             if (tag != null) {
1067                 if (tag.getValue() == null) {
1068                     return "Read only";
1069                 }
1070                 String readOnly = "Read only " + tag.getValue();
1071                 return readOnly.trim();
1072             }
1073         }
1074         return null;
1075     }
1076 
1077     private String calcImplementationNotes(JavaMethod serviceMethod) {
1078         StringBuilder bldr = new StringBuilder();
1079         String newLine = "";
1080         for (DocletTag tag : serviceMethod.getTagsByName("impl", true)) {
1081             bldr.append(newLine);
1082             newLine = "\n";
1083             String value = tag.getValue();
1084             bldr.append(value);
1085         }
1086         if (hasOverride(serviceMethod)) {
1087             boolean matchJustOnName = true;
1088             JavaMethod overriddenMethod = findInterfaceMethod(serviceMethod.getParentClass(), serviceMethod, matchJustOnName);
1089             if (overriddenMethod == null) {
1090                 // do it again so we can debug
1091                 findInterfaceMethod(serviceMethod.getParentClass(), serviceMethod, true);
1092                 throw new NullPointerException("could not find overridden method or method that has @Override annotation " + serviceMethod.getCallSignature());
1093             }
1094             bldr.append(newLine);
1095             newLine = "\n";
1096             bldr.append("Overridden method should be implemented in helper: ");
1097             bldr.append(overriddenMethod.getParentClass().getName());
1098         }
1099         if (bldr.length() == 0) {
1100             return null;
1101         }
1102         return bldr.toString();
1103     }
1104 
1105     private boolean hasOverride(JavaMethod serviceMethod) {
1106         for (Annotation annotation : serviceMethod.getAnnotations()) {
1107             if (annotation.getType().getJavaClass().getName().equals(
1108                     "Override")) {
1109                 return true;
1110             }
1111         }
1112         return false;
1113     }
1114 
1115     private boolean isDeprecated(JavaMethod serviceMethod) {
1116         for (Annotation annotation : serviceMethod.getAnnotations()) {
1117             if (annotation.getType().getJavaClass().getName().equals(
1118                     "Deprecated")) {
1119                 return true;
1120             }
1121         }
1122         return false;
1123     }
1124 
1125     private String calcImplementationNotes(JavaMethod getterMethod,
1126             JavaMethod setterMethod, JavaField beanField) {
1127         if (getterMethod != null) {
1128             DocletTag tag = getterMethod.getTagByName("impl", true);
1129             if (tag != null) {
1130                 return tag.getValue();
1131             }
1132         }
1133         return null;
1134     }
1135     
1136     private String calcImplementationColumn (JavaMethod getterMethod,
1137             JavaMethod setterMethod, JavaField beanField) {
1138         if (getterMethod != null) {
1139             DocletTag tag = getterMethod.getTagByName("implColumn", true);
1140             if (tag != null) {
1141                 return tag.getValue();
1142             }
1143         }
1144         return null;
1145     }
1146 
1147     private String calcNameFromShortName(String shortName) {
1148         StringBuilder bldr = new StringBuilder(shortName.length() + 3);
1149         char c = shortName.charAt(0);
1150         bldr.append(Character.toUpperCase(c));
1151         boolean lastWasUpper = true;
1152         for (int i = 1; i < shortName.length(); i++) {
1153             c = shortName.charAt(i);
1154             if (Character.isUpperCase(c)) {
1155                 if (!lastWasUpper) {
1156                     bldr.append(" ");
1157                 }
1158             } else {
1159                 lastWasUpper = false;
1160             }
1161             bldr.append(c);
1162         }
1163         return bldr.toString();
1164     }
1165 
1166     private String calcName(JavaClass mainClass, JavaMethod getterMethod,
1167             JavaMethod setterMethod, JavaField beanField, String shortName) {
1168         String name = this.calcNameFromTag(getterMethod, setterMethod, beanField);
1169         if (name != null) {
1170             return name;
1171         }
1172         name = this.calcNameFromNameEmbeddedInDescription(mainClass, getterMethod, setterMethod, beanField, shortName);
1173         if (name != null) {
1174             return name;
1175         }
1176         return this.calcNameFromShortName(shortName);
1177     }
1178 
1179     private String calcNameFromTag(JavaMethod getterMethod,
1180             JavaMethod setterMethod, JavaField beanField) {
1181         if (getterMethod != null) {
1182             DocletTag tag = getterMethod.getTagByName("name", true);
1183             if (tag != null) {
1184                 return tag.getValue();
1185             }
1186         }
1187         return null;
1188     }
1189 
1190     private String calcNameFromNameEmbeddedInDescription(JavaClass mainClass, JavaMethod getterMethod,
1191             JavaMethod setterMethod, JavaField beanField, String shortName) {
1192         String nameDesc = this.calcMethodComment(mainClass, getterMethod, setterMethod,
1193                 beanField, shortName);
1194         String[] parsed = parseNameDesc(nameDesc);
1195         return parsed[0];
1196     }
1197 
1198     private String[] parseNameDesc(String nameDesc) {
1199         String[] parsed = new String[2];
1200         if (nameDesc == null) {
1201             return parsed;
1202         }
1203         nameDesc = nameDesc.trim();
1204         if (!nameDesc.startsWith("Name:")) {
1205             parsed[1] = nameDesc;
1206             return parsed;
1207         }
1208         nameDesc = nameDesc.substring("Name:".length()).trim();
1209         int i = nameDesc.indexOf("\n");
1210         if (i == -1) {
1211             parsed[0] = nameDesc.trim();
1212             return parsed;
1213         }
1214         parsed[0] = nameDesc.substring(0, i).trim();
1215         parsed[1] = nameDesc.substring(i).trim();
1216         return parsed;
1217     }
1218 
1219     private String calcDescription(JavaClass mainClass, JavaMethod getterMethod,
1220             JavaMethod setterMethod, JavaField beanField, String shortName) {
1221         String nameDesc = this.calcMethodComment(mainClass, getterMethod, setterMethod,
1222                 beanField, shortName);
1223         String[] parsed = parseNameDesc(nameDesc);
1224         return parsed[1];
1225     }
1226  
1227     private String calcMethodComment(JavaClass mainClass, JavaMethod getterMethod,
1228             JavaMethod setterMethod,
1229             JavaField beanField,
1230             String shortName) {
1231         String desc = null;
1232         if (getterMethod != null) {
1233             desc = getterMethod.getComment();
1234             if (isCommentNotEmpty(desc)) {
1235                 return desc;
1236             }
1237 //            System.out.println("Searching for superclass to find description for field " + shortName);
1238             JavaClass jc = mainClass;
1239             while (true) {
1240                 JavaClass superJc = jc.getSuperJavaClass();
1241                 if (superJc == null) {
1242                     break;
1243                 }
1244                 JavaMethod parentGetterMethod = this.findGetterMethod(superJc, shortName);
1245                 if (parentGetterMethod != null) {
1246                     desc = parentGetterMethod.getComment();
1247                     if (isCommentNotEmpty(desc)) {
1248                         return desc;
1249                     }
1250                 }
1251                 jc = superJc;
1252             }
1253         }
1254         if (setterMethod != null) {
1255             desc = setterMethod.getComment();
1256             if (isCommentNotEmpty(desc)) {
1257                 return desc;
1258             }
1259         }
1260         if (beanField != null) {
1261             desc = beanField.getComment();
1262             if (isCommentNotEmpty(desc)) {
1263                 return desc;
1264             }
1265         }
1266         desc = calcMethodCommentRecursively(mainClass, getterMethod);
1267         if (isCommentNotEmpty(desc)) {
1268             return desc;
1269         }
1270         desc = calcMethodCommentRecursively(mainClass, setterMethod);
1271         if (isCommentNotEmpty(desc)) {
1272             return desc;
1273         }
1274         return null;
1275     }
1276 
1277     private String calcMethodCommentRecursively(JavaClass mainClass, JavaMethod method) {
1278         if (method == null) {
1279             return null;
1280         }
1281         String desc = method.getComment();
1282         if (isCommentNotEmpty(desc)) {
1283             return desc;
1284         }
1285         JavaMethod infcMethod = findInterfaceMethod(mainClass, method, false);
1286         if (infcMethod != null) {
1287             desc = infcMethod.getComment();
1288             if (isCommentNotEmpty(desc)) {
1289                 return desc;
1290             }
1291         }
1292         JavaMethod superMethod = findSuperMethod(method);
1293         if (superMethod != null) {
1294             desc = superMethod.getComment();
1295             if (isCommentNotEmpty(desc)) {
1296                 return desc;
1297             }
1298         }
1299         return null;
1300     }
1301 
1302     private JavaMethod findSuperMethod(JavaMethod method) {
1303 //        System.out.println("Searching for super method for "
1304 //                + method.getParentClass().getName() + "."
1305 //                + method.getCallSignature());
1306         for (JavaMethod superMethod : method.getParentClass().getMethods(true)) {
1307             if (method.equals(superMethod)) {
1308                 continue;
1309             }
1310             if (method.getCallSignature().equals(superMethod.getCallSignature())) {
1311                 return superMethod;
1312             }
1313         }
1314         return null;
1315     }
1316 
1317     private JavaMethod findInterfaceMethod(JavaClass mainClass, JavaMethod method, boolean matchJustOnName) {
1318         String callSig = method.getCallSignature();
1319         if (matchJustOnName) {
1320             callSig = method.getName();
1321         }
1322         JavaClass classToSearch = mainClass;
1323 //        log ("Searching mainClass " + classToSearch.getName() + " for " + callSig, callSig);
1324         while (true) {
1325             for (JavaClass infcClass : classToSearch.getImplementedInterfaces()) {
1326                 JavaMethod meth = this.findMethodOnInterfaceRecursively(infcClass, callSig, matchJustOnName);
1327                 if (meth != null) {
1328 //                    recursionCntr = 0;
1329                     return meth;
1330                 }
1331             }
1332             JavaClass superClass = classToSearch.getSuperJavaClass();
1333             if (superClass == null) {
1334 //                recursionCntr = 0;                
1335 //                log ("Did not find " + callSig + " on " + mainClass, callSig); 
1336                 return null;
1337             }
1338             classToSearch = superClass;
1339 //            log ("Searching superClass " + classToSearch.getName() + " for " + callSig, callSig);                
1340         }
1341     }
1342 
1343 //    private void log (String message, String callSig) {
1344 //        if (callSig.equalsIgnoreCase("getTypeKey()")) {
1345 //            for (int i = 0; i < this.recursionCntr; i++) {
1346 //                System.out.print (" ");
1347 //            }
1348 //            System.out.println (message);
1349 //        }
1350 //    }
1351 //    private int recursionCntr = 0;
1352     private JavaMethod findMethodOnInterfaceRecursively(JavaClass infcClass, String callSig, boolean matchJustOnName) {
1353 //        recursionCntr++;
1354 //        log ("Searching interface " + infcClass.getName() + " for " + callSig, callSig);
1355         for (JavaMethod infcMethod : infcClass.getMethods()) {
1356             if (callSig.equals(infcMethod.getCallSignature())) {
1357 //                log (callSig + " found on " + infcClass.getName() + "!!!!!!!!!!!!!!!!", callSig); 
1358 //                recursionCntr--;
1359                 return infcMethod;
1360             }
1361             if (matchJustOnName) {
1362                 if (callSig.equals(infcMethod.getName())) {
1363                     return infcMethod;
1364                 }
1365             }
1366         }
1367         for (JavaClass subInfc : infcClass.getImplementedInterfaces()) {
1368 //            log ("Searching  sub-interface " + subInfc.getName() + " for " + callSig, callSig);
1369             JavaMethod infcMethod = findMethodOnInterfaceRecursively(subInfc, callSig, matchJustOnName);
1370             if (infcMethod != null) {
1371 //                recursionCntr--;
1372                 return infcMethod;
1373             }
1374         }
1375 //        log (callSig + " not found on " + infcClass.getName(), callSig);
1376 //        this.recursionCntr--;
1377         return null;
1378     }
1379 
1380     private boolean isCommentNotEmpty(String desc) {
1381         if (desc == null) {
1382             return false;
1383         }
1384         if (desc.trim().isEmpty()) {
1385             return false;
1386         }
1387         if (desc.contains("@inheritDoc")) {
1388             return false;
1389         }
1390         return true;
1391     }
1392 
1393     private String getAccessorType(JavaMethod method) {
1394         String accessorType = getAccessorType(method.getAnnotations());
1395         if (accessorType != null) {
1396             return accessorType;
1397         }
1398         accessorType = getAccessorType(method.getParentClass().getAnnotations());
1399         return accessorType;
1400     }
1401 
1402     private String getAccessorType(Annotation[] annotations) {
1403         for (Annotation annotation : annotations) {
1404             if (annotation.getType().getJavaClass().getName().equals(
1405                     "XmlAccessorType")) {
1406 //    System.out.println ("Looking for XmlAccessorType annotation = "
1407 //                        + annotation.getParameterValue ());
1408                 return annotation.getParameterValue().toString();
1409             }
1410         }
1411         return null;
1412     }
1413 
1414     private String stripQuotes(String str) {
1415         if (str.startsWith("\"")) {
1416             str = str.substring(1);
1417         }
1418         if (str.endsWith("\"")) {
1419             str = str.substring(0, str.length() - 1);
1420         }
1421         return str;
1422     }
1423 
1424     private String calcMissing(String str) {
1425         if (str == null) {
1426             return "???";
1427         }
1428         if (str.trim().isEmpty()) {
1429             return "???";
1430         }
1431         return str;
1432     }
1433 
1434     private void addServiceToList(XmlType xmlType, String serviceKey) {
1435         if (!xmlType.getService().contains(serviceKey)) {
1436             xmlType.setService(xmlType.getService() + ", " + serviceKey);
1437         }
1438     }
1439 
1440     private String calcXmlAttribute(JavaField beanField) {
1441         if (beanField == null) {
1442             // TODO: worry about checking for this annotation on the method for non-field based AccessorTypes
1443             return "No";
1444         }
1445         for (Annotation annotation : beanField.getAnnotations()) {
1446             if (annotation.getType().getJavaClass().getName().equals("XmlAttribute")) {
1447                 return "Yes";
1448             }
1449         }
1450         return "No";
1451     }
1452 
1453     private JavaField findField(JavaClass javaClass, String shortName,
1454             JavaMethod setterMethod) {
1455         JavaField field = findField(javaClass, shortName);
1456         if (field != null) {
1457             return field;
1458         }
1459         if (setterMethod != null) {
1460             String paramName = setterMethod.getParameters()[0].getName();
1461             if (paramName.equalsIgnoreCase(shortName)) {
1462                 return null;
1463             }
1464             return findField(javaClass, paramName);
1465         }
1466         return null;
1467     }
1468 
1469     private JavaField findField(JavaClass javaClass, String name) {
1470         if (name == null) {
1471             return null;
1472         }
1473         for (JavaField field : javaClass.getFields()) {
1474             if (field.getName().equalsIgnoreCase(name)) {
1475                 return field;
1476             }
1477             // TODO: check for shortNames that already start with is so we don't check for isIsEnrollable
1478             if (field.getName().equals("is" + name)) {
1479                 return field;
1480             }
1481         }
1482         JavaClass superClass = javaClass.getSuperJavaClass();
1483         if (superClass == null) {
1484             return null;
1485         }
1486         return findField(superClass, name);
1487     }
1488 
1489     private JavaMethod findGetterMethod(JavaClass msClass, String shortName) {
1490         for (JavaMethod method : msClass.getMethods(true)) {
1491             String methodName = method.getName();
1492             if (methodName.equalsIgnoreCase("get" + shortName)) {
1493                 return method;
1494             }
1495             if (methodName.toLowerCase().startsWith("is")) {
1496                 if (methodName.equalsIgnoreCase("is" + shortName)) {
1497                     return method;
1498                 }
1499                 // shortName already has "is" in it
1500                 if (methodName.equalsIgnoreCase(shortName)) {
1501                     return method;
1502                 }
1503             }
1504             // TODO: followup on KimEntityResidencyInfo.getInState
1505             if (methodName.equalsIgnoreCase("getInState") && shortName.equalsIgnoreCase(
1506                     "InStateFlag")) {
1507                 return method;
1508             }
1509         }
1510         return null;
1511     }
1512 
1513     private JavaMethod findSetterMethod(JavaClass msClass, String shortName) {
1514         for (JavaMethod method : msClass.getMethods(true)) {
1515             if (method.getName().equals("set" + shortName)) {
1516                 return method;
1517             }
1518             // TODO: check for shortNames that already start with is so we don't check for isIsEnrollable
1519             if (method.getName().equals("setIs" + shortName)) {
1520                 return method;
1521             }
1522             // TODO: followup on KimEntityResidencyInfo.getInState
1523             if (method.getName().equals("setInStateFlag") && shortName.equals(
1524                     "InState")) {
1525                 return method;
1526             }
1527         }
1528         return null;
1529     }
1530     private static final String[] SETTER_METHODS_TO_SKIP = {
1531         // Somebody put "convenience" methods on the validation result info
1532         "ValidationResultInfo.setWarning",
1533         "ValidationResultInfo.setError",
1534         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1535         "CredentialProgramInfo.setDiplomaTitle",
1536         // synonym for the official of setCredentialType
1537         "CredentialProgramInfo.setType",
1538         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1539         "CredentialProgramInfo.setHegisCode",
1540         "CredentialProgramInfo.setCip2000Code",
1541         "CredentialProgramInfo.setCip2010Code",
1542         "CredentialProgramInfo.setSelectiveEnrollmentCode",
1543         "CoreProgramInfo.setDiplomaTitle",
1544         // synonym for the official of setCredentialType
1545         //  "CoreProgramInfo.setType",
1546         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1547         "CoreProgramInfo.setHegisCode",
1548         "CoreProgramInfo.setCip2000Code",
1549         "CoreProgramInfo.setCip2010Code",
1550         "CoreProgramInfo.setSelectiveEnrollmentCode",
1551         "WhenConstraint.setValue"
1552     };
1553     private static final String[] GETTER_METHODS_TO_SKIP = {
1554         // Somebody put "convenience" methods on the validation result info
1555         "ValidationResultInfo.getWarning",
1556         "ValidationResultInfo.getError",
1557         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1558         "CredentialProgramInfo.getDiplomaTitle",
1559         // synonym for the official of setCredentialType
1560         "CredentialProgramInfo.getType",
1561         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1562         "CredentialProgramInfo.getHegisCode",
1563         "CredentialProgramInfo.getCip2000Code",
1564         "CredentialProgramInfo.getCip2010Code",
1565         "CredentialProgramInfo.getSelectiveEnrollmentCode",
1566         "CoreProgramInfo.getDiplomaTitle",
1567         // synonym for the official of setCredentialType
1568         //  "CoreProgramInfo.setType",
1569         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1570         "CoreProgramInfo.getHegisCode",
1571         "CoreProgramInfo.getCip2000Code",
1572         "CoreProgramInfo.getCip2010Code",
1573         "CoreProgramInfo.getSelectiveEnrollmentCode",
1574         "WhenConstraint.getValue"
1575     };
1576 
1577     private boolean isSetterMethodToProcess(JavaMethod method, String className) {
1578         if (!method.getName().startsWith("set")) {
1579             return false;
1580         }
1581         if (method.getParameters().length != 1) {
1582             return false;
1583         }
1584         if (method.isPrivate()) {
1585             return false;
1586         }
1587         if (method.isProtected()) {
1588             return false;
1589         }
1590         if (method.isStatic()) {
1591             return false;
1592         }
1593         if (method.getParentClass().getPackageName().startsWith("java")) {
1594             return false;
1595         }
1596         String fullName = className + "." + method.getName();
1597         for (String skip : SETTER_METHODS_TO_SKIP) {
1598             if (skip.equals(fullName)) {
1599                 return false;
1600             }
1601         }
1602 //  if (method.getParentClass ().isInterface ())
1603 //  {
1604 //   return false;
1605 //  }
1606         for (Annotation annotation : method.getAnnotations()) {
1607             if (annotation.getType().getJavaClass().getName().equals("XmlTransient")) {
1608                 return false;
1609             }
1610         }
1611         return true;
1612     }
1613 
1614     private boolean isGetterMethodToProcess(JavaMethod method, String className) {
1615         if (!method.getName().startsWith("get")) {
1616             if (!method.getName().startsWith("is")) {
1617                 return false;
1618             }
1619         }
1620         if (method.getParameters().length != 0) {
1621             return false;
1622         }
1623         if (method.isPrivate()) {
1624             return false;
1625         }
1626         if (method.isProtected()) {
1627             return false;
1628         }
1629         if (method.isStatic()) {
1630             return false;
1631         }
1632         if (method.getParentClass().getPackageName().startsWith("java")) {
1633             return false;
1634         }
1635         String fullName = className + "." + method.getName();
1636         for (String skip : GETTER_METHODS_TO_SKIP) {
1637             if (skip.equals(fullName)) {
1638                 return false;
1639             }
1640         }
1641 //  if (method.getParentClass ().isInterface ())
1642 //  {
1643 //   return false;
1644 //  }
1645         for (Annotation annotation : method.getAnnotations()) {
1646             if (annotation.getType().getJavaClass().getName().equals("XmlTransient")) {
1647                 return false;
1648             }
1649         }
1650         return true;
1651     }
1652 
1653     private String calcShortNameFromSetter(JavaMethod method) {
1654         return method.getName().substring(3);
1655     }
1656 
1657     private String calcShortNameFromGetter(JavaMethod method) {
1658         if (method.getName().startsWith("get")) {
1659             return method.getName().substring(3);
1660         }
1661         if (method.getName().startsWith("is")) {
1662             return method.getName().substring(2);
1663         }
1664         throw new IllegalArgumentException(method.getName()
1665                 + " does not start with is or get");
1666     }
1667 
1668     private String calcCardinality(JavaClass mainClass, JavaMethod getterMethod,
1669             JavaMethod setterMethod, JavaField beanField, String shortName) {
1670         if (isReturnACollection(mainClass, getterMethod, setterMethod, beanField, shortName)) {
1671             return "Many";
1672         }
1673         return "One";
1674     }
1675 
1676     private boolean isReturnACollection(JavaClass mainClass, JavaMethod getterMethod,
1677             JavaMethod setterMethod, JavaField beanField, String shortName) {
1678         if (getterMethod != null) {
1679             return isCollection(getterMethod.getReturnType());
1680         }
1681         if (beanField != null) {
1682             return isCollection(beanField.getType());
1683         }
1684         // TODO: check setterMethod
1685         return false;
1686     }
1687 
1688     private boolean isCollection(Type type) {
1689         JavaClass javaClass = type.getJavaClass();
1690         return this.isCollection(javaClass);
1691     }
1692 
1693     private boolean isCollection(JavaClass javaClass) {
1694         if (javaClass.getName().equals("LocalKeyList")) {
1695             return true;
1696         }
1697         if (javaClass.getName().equals("MessageGroupKeyList")) {
1698             return true;
1699         }
1700         if (javaClass.getName().equals(List.class.getSimpleName())) {
1701             return true;
1702         }
1703         if (javaClass.getName().equals(ArrayList.class.getSimpleName())) {
1704             return true;
1705         }
1706         if (javaClass.getName().equals(Collection.class.getSimpleName())) {
1707             return true;
1708         }
1709         if (javaClass.getName().equals(Set.class.getSimpleName())) {
1710             return true;
1711         }
1712         return false;
1713     }
1714 
1715     private String calcType(JavaClass mainClass, JavaMethod getterMethod,
1716             JavaMethod setterMethod, JavaField beanField, String shortName) {
1717         if (getterMethod != null) {
1718             return calcTypeOfGetterMethodReturn(getterMethod);
1719         }
1720         if (beanField != null) {
1721             Type type = beanField.getType();
1722             return calcType(type);
1723         }
1724         // TODO: calc type based on the setterMethod
1725         return null;
1726     }
1727 
1728     private String calcTypeOfGetterMethodReturn(JavaMethod getterMethod) {
1729         Type type = getterMethod.getReturnType();
1730         return calcType(type);
1731     }
1732 
1733     private String calcType(Type type) {
1734         if (type == null) {
1735             return "void";
1736         }
1737         if (isCollection(type.getJavaClass())) {
1738             return calcType(calcRealJavaClass(type)) + "List";
1739         }
1740         return calcType(calcRealJavaClass(type));
1741     }
1742 
1743     private Annotation findJavaAnnotation(String name, JavaClass clazz) {
1744     	
1745     	Annotation[] annotations = clazz.getAnnotations();
1746     	
1747     	for (Annotation annotation : annotations) {
1748 	        
1749     		if (annotation.getType().getJavaClass().getName().equals(name)) {
1750     			return annotation;
1751     		}
1752         }
1753     	return null;
1754     }
1755     private String calcType(JavaClass javaClass) {
1756     	
1757         if (javaClass.isEnum()) {
1758         	
1759 			if (!JavaClassAnnotationUtils.doesAnnotationExist(
1760 			        XmlEnum.class.getSimpleName(), javaClass)) {
1761 				// a rice or other dependency without the @XmlEnum annotation
1762 				// present
1763 				if (javaClass.getName().equals("WriteAccess")) {
1764 					// rice CommonLookupParam
1765 					return "String";
1766 				} else if (javaClass.getName().equals("Widget")) {
1767 					// rice CommonLookupParam
1768 					return "String";
1769 				} else if (javaClass.getName().equals("Usage")) {
1770 					// rice
1771 					return "String";
1772 				} else {
1773 					// this allows the types to be manually specified 
1774 					// using the full package.classname format.
1775 					return javaClass.getFullyQualifiedName();
1776 				}
1777 
1778 			}
1779     		
1780     		Class<?>annotationSpecifiedType = JavaClassAnnotationUtils.extractXmlEnumValue(javaClass);
1781     		
1782         	return annotationSpecifiedType.getSimpleName();
1783            
1784         }
1785         // this is messed up instead of list of strings it is an object with a list of strings
1786         if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1787             return "StringList";
1788         }
1789         if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1790             return "StringList";
1791         }
1792         // TODO: figure out why rice stuff translates like this junk?
1793         if (javaClass.getName().equals("java$util$Map")) {
1794             return "Map<String, String>";
1795         }
1796         if (javaClass.getName().equals("Map")) {
1797             // TODO: make sure it is in fact a String,String map
1798             return "Map<String, String>";
1799         }
1800         return javaClass.getName();
1801     }
1802 
1803     private JavaClass calcRealJavaClassOfGetterReturn(JavaMethod getterMethod) {
1804         if (getterMethod == null) {
1805             return null;
1806         }
1807         Type type = getterMethod.getReturnType();
1808         return this.calcRealJavaClass(type);
1809     }
1810 
1811     private JavaClass calcRealJavaClass(Type type) {
1812         if (type == null) {
1813             return null;
1814         }
1815         JavaClass javaClass = type.getJavaClass();
1816         if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1817             return STRING_JAVA_CLASS;
1818         }
1819         if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1820             return STRING_JAVA_CLASS;
1821         }
1822         if (!this.isCollection(javaClass)) {
1823             return javaClass;
1824         }
1825 
1826 //  for (Type t : type.getActualTypeArguments ())
1827 //  {
1828 //   System.out.println ("ServiceContractModelQDoxLoader: type arguments = "
1829 //                       + t.toString ());
1830 //  }
1831 
1832         Type[]collectionTypeArguments = type.getActualTypeArguments();
1833         
1834         if (collectionTypeArguments == null)
1835         	return new JavaClass (Object.class.getName());
1836         else
1837         	return collectionTypeArguments[0].getJavaClass();
1838     }
1839 
1840     private boolean isComplex(JavaClass javaClass) {
1841         if (javaClass.getName().equals ("void")) {
1842             return false;
1843         }
1844         if (javaClass.isEnum()) {
1845             return false;
1846         }
1847         if (javaClass.getName().equals(String.class.getSimpleName())) {
1848             return false;
1849         }
1850         if (javaClass.getName().equals(Integer.class.getSimpleName())) {
1851             return false;
1852         }
1853         if (javaClass.getName().equals(Date.class.getSimpleName())) {
1854             return false;
1855         }
1856         if (javaClass.getName().equals(Long.class.getSimpleName())) {
1857             return false;
1858         }
1859         if (javaClass.getName().equals(Boolean.class.getSimpleName())) {
1860             return false;
1861         }
1862         if (javaClass.getName().equals(Double.class.getSimpleName())) {
1863             return false;
1864         }
1865         if (javaClass.getName().equals(Float.class.getSimpleName())) {
1866             return false;
1867         }
1868         if (javaClass.getName().equals(int.class.getSimpleName())) {
1869             return false;
1870         }
1871         if (javaClass.getName().equals(long.class.getSimpleName())) {
1872             return false;
1873         }
1874         if (javaClass.getName().equals(boolean.class.getSimpleName())) {
1875             return false;
1876         }
1877         if (javaClass.getName().equals(double.class.getSimpleName())) {
1878             return false;
1879         }
1880         if (javaClass.getName().equals(float.class.getSimpleName())) {
1881             return false;
1882         }
1883         if (javaClass.getName().equals(Map.class.getSimpleName())) {
1884             return false;
1885         }
1886 
1887         if (javaClass.getName().equals(String.class.getName())) {
1888             return false;
1889         }
1890         if (javaClass.getName().equals(Integer.class.getName())) {
1891             return false;
1892         }
1893         if (javaClass.getName().equals(Date.class.getName())) {
1894             return false;
1895         }
1896         if (javaClass.getName().equals(Long.class.getName())) {
1897             return false;
1898         }
1899         if (javaClass.getName().equals(Boolean.class.getName())) {
1900             return false;
1901         }
1902         if (javaClass.getName().equals(Double.class.getName())) {
1903             return false;
1904         }
1905         if (javaClass.getName().equals(Float.class.getName())) {
1906             return false;
1907         }
1908         if (javaClass.getName().equals(int.class.getName())) {
1909             return false;
1910         }
1911         if (javaClass.getName().equals(long.class.getName())) {
1912             return false;
1913         }
1914         if (javaClass.getName().equals(boolean.class.getName())) {
1915             return false;
1916         }
1917         if (javaClass.getName().equals(double.class.getName())) {
1918             return false;
1919         }
1920         if (javaClass.getName().equals(float.class.getName())) {
1921             return false;
1922         }
1923         if (javaClass.getName().equals(BigDecimal.class.getSimpleName())) {
1924             return false;
1925         }
1926         if (javaClass.getName().equals(Map.class.getName())) {
1927             return false;
1928         }
1929         if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1930             return false;
1931         }
1932         if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1933             return false;
1934         }
1935         if (javaClass.getName().equals("java$util$Map")) {
1936             return false;
1937         }
1938         return true;
1939     }
1940 
1941     @Override
1942     public String toString() {
1943         return "ServiceContractModelQDoxLoader{" +
1944                 "sourceDirectories=" + sourceDirectories +
1945                 ", services=" + services +
1946                 ", serviceMethods=" + serviceMethods +
1947                 ", xmlTypeMap=" + xmlTypeMap +
1948                 ", messageStructures=" + messageStructures +
1949                 ", validateKualiStudent=" + validateKualiStudent +
1950                 '}';
1951     }
1952 }