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.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 //                if (!subObjToAdd.isEnum()) {
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         // now add all it's complex sub-objects if they haven't already been added
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 					// log into message
704 					log.error (message + " : " + e.getMessage());
705 					// log into debug log
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         // Special fix up for principal
738 //        log.warn(serviceKey + ":" + xmlObject + "." + shortName);
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         // all keys and Ids are represented as strings
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         // global ones where the name matches the object 
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         // COMMON RICE IDENTITY SERVICE
845         LOOKUP_MAPPINGS.put("principalId", new Lookup ("rice.kim.Identity", "Principal"));
846         LOOKUP_MAPPINGS.put("principalIds", new Lookup ("rice.kim.Identity", "Principal"));
847         // TODO: fix these Ids that currently maps to principal instead of the entity id
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         // Common objects 
856         // TimeAmount
857         LOOKUP_MAPPINGS.put("atpDurationTypeKey", new Lookup ("Type", "TypeInfo")); 
858         LOOKUP_MAPPINGS.put("currencyTypeKey", new Lookup ("Type", "TypeInfo"));        
859         // Context
860         LOOKUP_MAPPINGS.put("authenticatedPrincipalId", new Lookup ("rice.kim.Identity", "Principal"));
861         // meta
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         // TODO: replace or augment this special list of ones with annotations in the contract itself
876         // program service
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         // share by program and course
893         LOOKUP_MAPPINGS.put("parentRelType", new Lookup ("Type", "TypeInfo"));
894         LOOKUP_MAPPINGS.put("parentLoRelationid", new Lookup ("LearningObjective", "LoInfo"));
895         
896         // type service
897         LOOKUP_MAPPINGS.put("ownerTypeKey", new Lookup ("Type", "TypeInfo"));
898         LOOKUP_MAPPINGS.put("relatedTypeKey", new Lookup ("Type", "TypeInfo"));
899         
900         // State service (there are no special purpose ones) 
901         
902         // LRC service (there are no special purpose ones)
903         
904         // atp
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         // Lui
910         LOOKUP_MAPPINGS.put("relatedLuiTypes", new Lookup ("Type", "TypeInfo"));
911         LOOKUP_MAPPINGS.put("relatedLuiId", new Lookup ("Lui", "LuiInfo"));
912         
913         // Course Offering
914         LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
915         LOOKUP_MAPPINGS.put("", new Lookup ("", ""));
916         
917         // TODO: finish the services
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                 // do it again so we can debug
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 //            System.out.println("Searching for superclass to find description for field " + shortName);
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 //        System.out.println("Searching for super method for "
1292 //                + method.getParentClass().getName() + "."
1293 //                + method.getCallSignature());
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 //        log ("Searching mainClass " + classToSearch.getName() + " for " + callSig, callSig);
1312         while (true) {
1313             for (JavaClass infcClass : classToSearch.getImplementedInterfaces()) {
1314                 JavaMethod meth = this.findMethodOnInterfaceRecursively(infcClass, callSig, matchJustOnName);
1315                 if (meth != null) {
1316 //                    recursionCntr = 0;
1317                     return meth;
1318                 }
1319             }
1320             JavaClass superClass = classToSearch.getSuperJavaClass();
1321             if (superClass == null) {
1322 //                recursionCntr = 0;                
1323 //                log ("Did not find " + callSig + " on " + mainClass, callSig); 
1324                 return null;
1325             }
1326             classToSearch = superClass;
1327 //            log ("Searching superClass " + classToSearch.getName() + " for " + callSig, callSig);                
1328         }
1329     }
1330 
1331 //    private void log (String message, String callSig) {
1332 //        if (callSig.equalsIgnoreCase("getTypeKey()")) {
1333 //            for (int i = 0; i < this.recursionCntr; i++) {
1334 //                System.out.print (" ");
1335 //            }
1336 //            System.out.println (message);
1337 //        }
1338 //    }
1339 //    private int recursionCntr = 0;
1340     private JavaMethod findMethodOnInterfaceRecursively(JavaClass infcClass, String callSig, boolean matchJustOnName) {
1341 //        recursionCntr++;
1342 //        log ("Searching interface " + infcClass.getName() + " for " + callSig, callSig);
1343         for (JavaMethod infcMethod : infcClass.getMethods()) {
1344             if (callSig.equals(infcMethod.getCallSignature())) {
1345 //                log (callSig + " found on " + infcClass.getName() + "!!!!!!!!!!!!!!!!", callSig); 
1346 //                recursionCntr--;
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 //            log ("Searching  sub-interface " + subInfc.getName() + " for " + callSig, callSig);
1357             JavaMethod infcMethod = findMethodOnInterfaceRecursively(subInfc, callSig, matchJustOnName);
1358             if (infcMethod != null) {
1359 //                recursionCntr--;
1360                 return infcMethod;
1361             }
1362         }
1363 //        log (callSig + " not found on " + infcClass.getName(), callSig);
1364 //        this.recursionCntr--;
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 //    System.out.println ("Looking for XmlAccessorType annotation = "
1395 //                        + annotation.getParameterValue ());
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             // TODO: worry about checking for this annotation on the method for non-field based AccessorTypes
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             // TODO: check for shortNames that already start with is so we don't check for isIsEnrollable
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                 // shortName already has "is" in it
1488                 if (methodName.equalsIgnoreCase(shortName)) {
1489                     return method;
1490                 }
1491             }
1492             // TODO: followup on KimEntityResidencyInfo.getInState
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             // TODO: check for shortNames that already start with is so we don't check for isIsEnrollable
1507             if (method.getName().equals("setIs" + shortName)) {
1508                 return method;
1509             }
1510             // TODO: followup on KimEntityResidencyInfo.getInState
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         // Somebody put "convenience" methods on the validation result info
1520         "ValidationResultInfo.setWarning",
1521         "ValidationResultInfo.setError",
1522         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1523         "CredentialProgramInfo.setDiplomaTitle",
1524         // synonym for the official of setCredentialType
1525         "CredentialProgramInfo.setType",
1526         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1527         "CredentialProgramInfo.setHegisCode",
1528         "CredentialProgramInfo.setCip2000Code",
1529         "CredentialProgramInfo.setCip2010Code",
1530         "CredentialProgramInfo.setSelectiveEnrollmentCode",
1531         "CoreProgramInfo.setDiplomaTitle",
1532         // synonym for the official of setCredentialType
1533         //  "CoreProgramInfo.setType",
1534         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
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         // Somebody put "convenience" methods on the validation result info
1543         "ValidationResultInfo.getWarning",
1544         "ValidationResultInfo.getError",
1545         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1546         "CredentialProgramInfo.getDiplomaTitle",
1547         // synonym for the official of setCredentialType
1548         "CredentialProgramInfo.getType",
1549         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1550         "CredentialProgramInfo.getHegisCode",
1551         "CredentialProgramInfo.getCip2000Code",
1552         "CredentialProgramInfo.getCip2010Code",
1553         "CredentialProgramInfo.getSelectiveEnrollmentCode",
1554         "CoreProgramInfo.getDiplomaTitle",
1555         // synonym for the official of setCredentialType
1556         //  "CoreProgramInfo.setType",
1557         // not on original wiki but still defined as a method but not backed by a field so not in wsdl
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 //  if (method.getParentClass ().isInterface ())
1591 //  {
1592 //   return false;
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 //  if (method.getParentClass ().isInterface ())
1630 //  {
1631 //   return false;
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         // TODO: check setterMethod
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         // TODO: calc type based on the setterMethod
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 				// a rice or other dependency without the @XmlEnum annotation
1750 				// present
1751 				if (javaClass.getName().equals("WriteAccess")) {
1752 					// rice CommonLookupParam
1753 					return "String";
1754 				} else if (javaClass.getName().equals("Widget")) {
1755 					// rice CommonLookupParam
1756 					return "String";
1757 				} else if (javaClass.getName().equals("Usage")) {
1758 					// rice
1759 					return "String";
1760 				} else {
1761 					// this allows the types to be manually specified 
1762 					// using the full package.classname format.
1763 					return javaClass.getFullyQualifiedName();
1764 				}
1765 
1766 			}
1767     		
1768     		Class<?>annotationSpecifiedType = JavaClassAnnotationUtils.extractXmlEnumValue(javaClass);
1769     		
1770         	return annotationSpecifiedType.getSimpleName();
1771            
1772         }
1773         // this is messed up instead of list of strings it is an object with a list of strings
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         // TODO: figure out why rice stuff translates like this junk?
1781         if (javaClass.getName().equals("java$util$Map")) {
1782             return "Map<String, String>";
1783         }
1784         if (javaClass.getName().equals("Map")) {
1785             // TODO: make sure it is in fact a String,String map
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 //  for (Type t : type.getActualTypeArguments ())
1815 //  {
1816 //   System.out.println ("ServiceContractModelQDoxLoader: type arguments = "
1817 //                       + t.toString ());
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 }