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