001    /**
002     * Copyright 2004-2014 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.student.contract.model.impl;
017    
018    import com.thoughtworks.qdox.JavaDocBuilder;
019    import com.thoughtworks.qdox.model.*;
020    import com.thoughtworks.qdox.model.Type;
021    import com.thoughtworks.qdox.model.annotation.AnnotationValue;
022    import org.kuali.student.contract.model.*;
023    import org.kuali.student.contract.model.util.JavaClassAnnotationUtils;
024    import org.slf4j.Logger;
025    import org.slf4j.LoggerFactory;
026    
027    import javax.xml.bind.annotation.XmlEnum;
028    import java.io.File;
029    import java.math.BigDecimal;
030    import java.util.*;
031    
032    /**
033     *
034     * @author nwright
035     */
036    public class ServiceContractModelQDoxLoader implements
037            ServiceContractModel {
038            
039            private static final Logger log = LoggerFactory.getLogger(ServiceContractModelQDoxLoader.class);
040    
041        private static final String LOCALE_KEY_LIST = "LocaleKeyList";
042        private static final String MESSAGE_GROUP_KEY_LIST = "MessageGroupKeyList";
043        private static final JavaClass STRING_JAVA_CLASS = new JavaClass(
044                "java.lang.String");
045        private List<String> sourceDirectories = null;
046        private List<Service> services = null;
047        private List<ServiceMethod> serviceMethods = null;
048        private Map<String, XmlType> xmlTypeMap = null;
049        private List<MessageStructure> messageStructures;
050        private boolean validateKualiStudent;
051    
052        public ServiceContractModelQDoxLoader(List<String> sourceDirectories) {
053            this (sourceDirectories, true);
054        }
055    
056        public ServiceContractModelQDoxLoader(List<String> sourceDirectories, boolean validateKualiStudent) {
057            this.sourceDirectories = sourceDirectories;
058            this.setValidateKualiStudent(validateKualiStudent);
059        }
060    
061        public boolean isValidateKualiStudent() {
062            return validateKualiStudent;
063        }
064    
065        public void setValidateKualiStudent(boolean validateKualiStudent) {
066            this.validateKualiStudent = validateKualiStudent;
067        }
068    
069        @Override
070        public List<ServiceMethod> getServiceMethods() {
071            if (this.serviceMethods == null) {
072                this.parse();
073            }
074            return this.serviceMethods;
075        }
076    
077        @Override
078        public List<String> getSourceNames() {
079            List<String> list = new ArrayList<String>(this.sourceDirectories.size());
080            for (String javaFile : this.sourceDirectories) {
081                list.add(javaFile);
082            }
083            return list;
084        }
085    
086        @Override
087        public List<Service> getServices() {
088            if (services == null) {
089                this.parse();
090            }
091            return services;
092        }
093    
094        @Override
095        public List<XmlType> getXmlTypes() {
096            if (xmlTypeMap == null) {
097                this.parse();
098            }
099            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    }