001    /*
002     * Copyright 2010 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.osedu.org/licenses/ECL-2.0
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 java.io.File;
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.Collections;
022    import java.util.Date;
023    import java.util.LinkedHashMap;
024    import java.util.LinkedHashSet;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Set;
028    
029    import org.kuali.student.contract.model.MessageStructure;
030    import org.kuali.student.contract.model.Service;
031    import org.kuali.student.contract.model.ServiceContractModel;
032    import org.kuali.student.contract.model.ServiceMethod;
033    import org.kuali.student.contract.model.ServiceMethodError;
034    import org.kuali.student.contract.model.ServiceMethodParameter;
035    import org.kuali.student.contract.model.ServiceMethodReturnValue;
036    import org.kuali.student.contract.model.XmlType;
037    import org.slf4j.Logger;
038    import org.slf4j.LoggerFactory;
039    
040    import com.thoughtworks.qdox.JavaDocBuilder;
041    import com.thoughtworks.qdox.model.Annotation;
042    import com.thoughtworks.qdox.model.DefaultDocletTagFactory;
043    import com.thoughtworks.qdox.model.DocletTag;
044    import com.thoughtworks.qdox.model.JavaClass;
045    import com.thoughtworks.qdox.model.JavaField;
046    import com.thoughtworks.qdox.model.JavaMethod;
047    import com.thoughtworks.qdox.model.JavaParameter;
048    import com.thoughtworks.qdox.model.Type;
049    import com.thoughtworks.qdox.model.annotation.AnnotationValue;
050    
051    /**
052     *
053     * @author nwright
054     */
055    public class ServiceContractModelQDoxLoader implements
056            ServiceContractModel {
057            
058            private static final Logger log = LoggerFactory.getLogger(ServiceContractModelQDoxLoader.class);
059    
060        private static final String LOCALE_KEY_LIST = "LocaleKeyList";
061        private static final String MESSAGE_GROUP_KEY_LIST = "MessageGroupKeyList";
062        private static final JavaClass STRING_JAVA_CLASS = new JavaClass(
063                "java.lang.String");
064        private List<String> sourceDirectories = null;
065        private List<Service> services = null;
066        private List<ServiceMethod> serviceMethods = null;
067        private Map<String, XmlType> xmlTypeMap = null;
068        private List<MessageStructure> messageStructures;
069        private boolean validateKualiStudent = true;
070    
071        public ServiceContractModelQDoxLoader(List<String> sourceDirectories) {
072            this.sourceDirectories = sourceDirectories;
073        }
074    
075        public ServiceContractModelQDoxLoader(List<String> sourceDirectories, boolean validateKualiStudent) {
076            this.sourceDirectories = sourceDirectories;
077            this.setValidateKualiStudent(validateKualiStudent);
078        }
079    
080        public boolean isValidateKualiStudent() {
081            return validateKualiStudent;
082        }
083    
084        public void setValidateKualiStudent(boolean validateKualiStudent) {
085            this.validateKualiStudent = validateKualiStudent;
086        }
087    
088        @Override
089        public List<ServiceMethod> getServiceMethods() {
090            if (this.serviceMethods == null) {
091                this.parse();
092            }
093            return this.serviceMethods;
094        }
095    
096        @Override
097        public List<String> getSourceNames() {
098            List<String> list = new ArrayList<String>(this.sourceDirectories.size());
099            for (String javaFile : this.sourceDirectories) {
100                list.add(javaFile);
101            }
102            return list;
103        }
104    
105        @Override
106        public List<Service> getServices() {
107            if (services == null) {
108                this.parse();
109            }
110            return services;
111        }
112    
113        @Override
114        public List<XmlType> getXmlTypes() {
115            if (xmlTypeMap == null) {
116                this.parse();
117            }
118            return new ArrayList<XmlType>(xmlTypeMap.values());
119        }
120    
121        @Override
122        public List<MessageStructure> getMessageStructures() {
123            if (messageStructures == null) {
124                this.parse();
125            }
126            return this.messageStructures;
127        }
128    
129        private void checkIfExists(String sourceDirectory) {
130            File file = new File(sourceDirectory);
131            if (!file.isDirectory()) {
132                throw new IllegalArgumentException(sourceDirectory + " is not a directory on disk");
133            }
134        }
135    
136        @SuppressWarnings("unchecked")
137            private void parse() {
138    //  System.out.println ("ServiceContractModelQDoxLoader: Starting parse");
139            services = new ArrayList<Service>();
140            serviceMethods = new ArrayList<ServiceMethod>();
141            xmlTypeMap = new LinkedHashMap<String, XmlType>();
142            messageStructures = new ArrayList<MessageStructure>();
143            DefaultDocletTagFactory dtf = new DefaultDocletTagFactory();
144            JavaDocBuilder builder = new JavaDocBuilder(dtf);
145            for (String sourceDirectory : sourceDirectories) {
146                checkIfExists(sourceDirectory);
147                builder.addSourceTree(new File(sourceDirectory));
148            }
149            Set<JavaClass> mergedClasses = new LinkedHashSet<JavaClass>();
150            
151            for (JavaClass javaClass : builder.getClasses()) {
152                
153                if (!javaClass.getPackageName().contains("r1"))
154                    mergedClasses.add(javaClass);
155                else
156                    log.warn("excluding r1 class: " + javaClass.getFullyQualifiedName());
157                
158            }
159            
160            List<JavaClass>sortedClasses = new ArrayList<JavaClass>(mergedClasses);
161            
162            Collections.sort(sortedClasses);
163            
164            for (JavaClass javaClass : sortedClasses) {
165                if (!this.isServiceToProcess(javaClass)) {
166                    continue;
167                }
168    //   System.out.println ("processing service=" + javaClass.getName ());
169                Service service = new Service();
170                services.add(service);
171                service.setKey(javaClass.getName().substring(0, javaClass.getName().length()
172                        - "Service".length()));
173                service.setName(javaClass.getName());
174                service.setComments(this.calcComment(javaClass));
175                service.setUrl(this.calcServiceUrl(javaClass));
176                service.setVersion(this.calcVersion(javaClass));
177                service.setStatus("???");
178                service.setIncludedServices(calcIncludedServices(javaClass));
179                service.setImplProject(javaClass.getPackageName());
180    
181    //   for (DocletTag tag : javaClass.getTags ())
182    //   {
183    //    System.out.println ("ServiceContractModelQDoxLoader: Class: "
184    //                        + javaClass.getName () + " has tag=" + dump (
185    //      tag));
186    //   }
187                
188                JavaMethod[]  methods = getServiceMethods (javaClass);  
189                for (JavaMethod javaMethod : methods) {
190    
191                    ServiceMethod serviceMethod = new ServiceMethod();
192                    serviceMethods.add(serviceMethod);
193                    serviceMethod.setService(service.getKey());
194                    serviceMethod.setName(javaMethod.getName());
195                    serviceMethod.setDescription(calcMissing(javaMethod.getComment()));
196                    serviceMethod.setParameters(new ArrayList<ServiceMethodParameter>());
197                    serviceMethod.setImplNotes(calcImplementationNotes(javaMethod));
198                    serviceMethod.setDeprecated(isDeprecated(javaMethod));
199    //    for (DocletTag tag : javaMethod.getTags ())
200    //    {
201    //     System.out.println ("ServiceContractModelQDoxLoader: Method: "
202    //                         + service.getName () + "."
203    //                         + javaMethod.getName ()
204    //                         + " has tag=" + dump (tag));
205    //    }
206                    // parameters
207                    for (JavaParameter parameter : javaMethod.getParameters()) {
208                        ServiceMethodParameter param = new ServiceMethodParameter();
209                        serviceMethod.getParameters().add(param);
210                        param.setName(parameter.getName());
211                        param.setType(calcType(parameter.getType()));
212                        param.setDescription(calcMissing(
213                                calcParameterDescription(javaMethod,
214                                param.getName())));
215                        try {
216                                                    addXmlTypeAndMessageStructure(calcRealJavaClass(parameter.getType()),
217                                                            serviceMethod.getService());
218                                            } catch (Exception e) {
219                                                    String message= "failed to parameter message structure: " + serviceMethod.getService() + " : " + parameter.getType();
220                                                    
221                                                    log.error (message + " : " + e.getMessage());
222                                                    log.debug(message, e);
223                                            }
224                    }
225                    // errors
226                    serviceMethod.setErrors(new ArrayList<ServiceMethodError>());
227                    for (Type exception : javaMethod.getExceptions()) {
228                        ServiceMethodError error = new ServiceMethodError();
229                        error.setType(this.calcType(exception.getJavaClass()));
230                        error.setDescription(calcMissing(
231                                calcExceptionDescription(javaMethod,
232                                error.getType())));
233                        error.setPackageName(exception.getJavaClass().getPackageName());
234                        error.setClassName(exception.getJavaClass().getName());
235                        serviceMethod.getErrors().add(error);
236                    }
237                    // return values
238                    ServiceMethodReturnValue rv = new ServiceMethodReturnValue();
239                    serviceMethod.setReturnValue(rv);
240                    Type returnType = null;
241                    try {
242                        returnType = javaMethod.getReturnType();
243                    } catch (NullPointerException ex) {
244                        System.out.println("Nullpinter getting return type: " + javaMethod.getCallSignature());
245                        returnType = null;
246                    }
247    
248                    rv.setType(calcType(returnType));
249                    rv.setDescription(calcMissing(this.calcReturnDescription(javaMethod)));
250                    if (returnType != null) {
251                        try {
252                            
253                            
254                                                    addXmlTypeAndMessageStructure(calcRealJavaClass(returnType),
255                                                            serviceMethod.getService());
256                                            } catch (Exception e) {
257                                                    String message = "failed to parse return type message structure: " + serviceMethod.getService() + " : " + returnType;
258                                                    
259                                                    log.error (message + " : " + e.getMessage());
260                                                    log.debug(message, e);
261                                            }
262                    }
263                }
264            }
265        }
266    
267        private JavaMethod[] getServiceMethods(JavaClass javaClass) {
268                    
269            Set<JavaMethod>methods = new LinkedHashSet<JavaMethod>();
270                    
271                    /*
272                     * As inheritence is useful from a technical level but not as much from a business level
273                     * This lets the union of the methods from all of the interfaces be listed in the contract.
274                     */
275                    JavaClass[] interfaces = javaClass.getImplementedInterfaces();
276                    
277                    for (JavaClass intfc : interfaces) {
278                            
279                            if (!isAService(intfc)) {
280                                    // only add the methods if this is not a service
281                                    // e.g. extends ServiceBusinessLogic
282                                    for (JavaMethod javaMethod : intfc.getMethods()) {
283                                    
284                                            methods.add(javaMethod);
285                                    }
286                            }
287                            
288                    }
289                    
290                    // add the main service methods last incase we override any from the parent interfaces.
291                    // note the heirarchy is only guaranteed relative to the target service class (if there are two levels or more of 
292                    // heirarchy there is no guarantee the method ordering will be correct).
293                    for (JavaMethod javaMethod : javaClass.getMethods()) {
294                            
295                            methods.add(javaMethod);
296                    }
297                    
298                    return methods.toArray(new JavaMethod[] {});
299            }
300    
301            private boolean isServiceToProcess(JavaClass javaClass) {
302    //  System.out.println ("looking if javaClass is a service to process=" + javaClass.getName () + "=" + javaClass.getPackageName ());
303            
304            if (!javaClass.getName().endsWith("Service")) {
305                return false;
306            }
307            if (javaClass.getPackageName().contains(".old.")) {
308    
309                return false;
310            }
311            if (javaClass.getPackageName().endsWith(".old")) {
312                return false;
313            }
314            for (Annotation annotation : javaClass.getAnnotations()) {
315    //   System.out.println ("looking for webservice tag=" + annotation.getType ().getJavaClass ().getName ());
316                if (annotation.getType().getJavaClass().getName().equals("WebService")) {
317    //    System.out.println ("Processing web service=" + javaClass.getPackageName ()
318    //                        + "." + javaClass.getName ());
319                    return true;
320                }
321            }
322            // This includes RICE's business object services even though they are not web services
323            // because often they are the only real service they have exposed and it helps to document
324            // them to see what the data really is like
325            if (javaClass.getName().endsWith("BoService")) {
326                return true;
327            }
328    //  System.out.println ("skipping service because it is not a web service="
329    //                      + javaClass.getPackageName () + "." + javaClass.getName ());
330            return false;
331        }
332    
333        private List<String> calcIncludedServices(JavaClass javaClass) {
334            List<String> includedServices = new ArrayList<String>();
335            for (JavaClass interfaceClass : javaClass.getImplementedInterfaces()) {
336                if (isAService(interfaceClass)) {
337    //            System.out.println("ServiceContractModelQDoxLoader:" + javaClass.getName()
338    //                    + " implements " + interfaceClass.getName());
339                    includedServices.add(interfaceClass.getName());
340                }
341            }
342            return includedServices;
343        }
344    
345        private boolean isAService(JavaClass interfaceClass) {
346            if (interfaceClass.getName().endsWith("Service")) {
347                return true;
348            }
349            return false;
350        }
351    
352        private String calcParameterDescription(JavaMethod method,
353                String parameterName) {
354            for (DocletTag tag : method.getTags()) {
355                if (tag.getName().equals("param")) {
356                    if (tag.getValue().startsWith(parameterName + " ")) {
357                        return tag.getValue().substring(parameterName.length() + 1);
358                    }
359                }
360            }
361            return null;
362        }
363    
364        private String calcExceptionDescription(JavaMethod serviceMethod,
365                String exceptionType) {
366            for (DocletTag tag : serviceMethod.getTags()) {
367                if (tag.getName().equals("throws")) {
368                    if (tag.getValue().startsWith(exceptionType + " ")) {
369                        return tag.getValue().substring(exceptionType.length() + 1);
370                    }
371                }
372            }
373            return null;
374        }
375    
376        private String calcReturnDescription(JavaMethod serviceMethod) {
377            for (DocletTag tag : serviceMethod.getTags()) {
378                if (tag.getName().equals("return")) {
379                    return tag.getValue();
380                }
381            }
382            return null;
383        }
384    
385        private String calcServiceUrl(JavaClass serviceClass) {
386            for (DocletTag tag : serviceClass.getTags()) {
387                if (tag.getName().equals("See")) {
388                    return tag.getValue();
389                }
390            }
391            return null;
392        }
393    
394        private void addXmlTypeAndMessageStructure(JavaClass messageStructureJavaClass,
395                String serviceKey) {
396            String name = calcType(messageStructureJavaClass);
397            XmlType xmlType = xmlTypeMap.get(name);
398            if (xmlType == null) {
399                xmlType = new XmlType();
400                xmlTypeMap.put(name, xmlType);
401                xmlType.setName(name);
402                xmlType.setDesc(this.calcMessageStructureDesc(messageStructureJavaClass));
403                xmlType.setDeprecated(isDeprecated (messageStructureJavaClass));
404                xmlType.setService(serviceKey);
405                xmlType.setVersion("IGNORE -- SAME AS SERVICE");
406                xmlType.setPrimitive(calcPrimitive(messageStructureJavaClass));
407                xmlType.setJavaPackage(calcJavaPackage(messageStructureJavaClass));
408                if (xmlType.getPrimitive().equals(XmlType.COMPLEX)) {
409                    addMessageStructure(messageStructureJavaClass, serviceKey);
410                }
411    
412            } else {
413                addServiceToList(xmlType, serviceKey);
414            }
415        }
416    
417        private boolean isDeprecated(JavaClass javaClass) {
418            for (Annotation annotation : javaClass.getAnnotations()) {
419                if (annotation.getType().getJavaClass().getName().equals(
420                        "Deprecated")) {
421                    return true;
422                }
423            }
424            return false;
425        }
426    
427        private String calcJavaPackage(JavaClass javaClass) {
428            String packageName = javaClass.getPackageName();
429            return packageName;
430        }
431    
432        private String calcMessageStructureDesc(JavaClass javaClass) {
433            {
434                String desc = javaClass.getComment();
435                if (desc != null) {
436                    if (!desc.isEmpty()) {
437                        return desc;
438                    }
439                }
440                JavaClass infcClass = this.getMatchingInfc(javaClass);
441                if (infcClass == null) {
442                    return null;
443                }
444                return infcClass.getComment();
445            }
446        }
447    
448        private JavaClass getMatchingInfc(JavaClass javaClass) {
449            // ks uses this pattern
450            String nameInfc = javaClass.getName();
451            if (nameInfc.endsWith("Info")) {
452                nameInfc = nameInfc.substring(0, nameInfc.length() - "Info".length())
453                        + "Infc";
454            }
455            String nameWithOutInfo = javaClass.getName();
456            // rice uses this pattern
457            if (nameWithOutInfo.endsWith("Info")) {
458                nameWithOutInfo = nameWithOutInfo.substring(0, nameWithOutInfo.length()
459                        - "Info".length());
460            }
461            for (JavaClass infc : javaClass.getImplementedInterfaces()) {
462                if (infc.getName().equals(nameInfc)) {
463    //                System.out.println("found matching interface " + infc.getName());
464                    return infc;
465                }
466                if (infc.getName().equals(nameWithOutInfo)) {
467                    return infc;
468                }
469            }
470            return null;
471        }
472    
473        private String calcPrimitive(JavaClass javaClass) {
474            if (this.isComplex(javaClass)) {
475                return XmlType.COMPLEX;
476            }
477            return "Primitive";
478        }
479    
480        private String initLower(String str) {
481            if (str == null) {
482                return null;
483            }
484            if (str.length() == 0) {
485                return str;
486            }
487            if (str.length() == 1) {
488                return str.toLowerCase();
489            }
490            return str.substring(0, 1).toLowerCase() + str.substring(1);
491        }
492    
493        private String initUpper(String str) {
494            if (str == null) {
495                return null;
496            }
497            if (str.length() == 0) {
498                return str;
499            }
500            if (str.length() == 1) {
501                return str.toUpperCase();
502            }
503            return str.substring(0, 1).toUpperCase() + str.substring(1);
504        }
505    
506        private Set<String> getShortNames(JavaClass messageStructureJavaClass) {
507            Set<String> fields = getFieldsUsingPropOrder(messageStructureJavaClass);
508            if (fields != null) {
509                return fields;
510            }
511            fields = new LinkedHashSet<String>();
512            for (JavaMethod method : messageStructureJavaClass.getMethods(true)) {
513                if (isSetterMethodToProcess(method, messageStructureJavaClass.getName())) {
514                    String shortName = this.calcShortNameFromSetter(method);
515                    fields.add(shortName);
516                    continue;
517                }
518                if (isGetterMethodToProcess(method, messageStructureJavaClass.getName())) {
519                    String shortName = this.calcShortNameFromGetter(method);
520                    fields.add(shortName);
521                    continue;
522                }
523            }
524            return fields;
525        }
526    
527        private Set<String> getFieldsUsingPropOrder(
528                JavaClass messageStructureJavaClass) {
529            for (Annotation annotation : messageStructureJavaClass.getAnnotations()) {
530                if (annotation.getType().getJavaClass().getName().equals("XmlType")) {
531                    AnnotationValue propOrderParam = annotation.getProperty("propOrder");
532                    if (propOrderParam == null) {
533                        continue;
534                    }
535                    Object propOrderValue = propOrderParam.getParameterValue();
536                    if (!(propOrderValue instanceof List)) {
537                        continue;
538                    }
539                    Set<String> fields = new LinkedHashSet<String>();
540                    for (Object value : (List<?>) propOrderValue) {
541                        if (value instanceof String) {
542                            String shortName = (String) value;
543                            shortName = this.stripQuotes(shortName);
544                            if (shortName.contains(".Elements.")) {
545                                String newShortName = getShortNameFromElements(shortName, messageStructureJavaClass);
546                                if (newShortName == null) {
547                                    continue;
548                                }
549                                shortName = newShortName;
550                            } else if (shortName.startsWith("CoreConstants.CommonElements.")) {
551                                String newShortName = getCoreConstants(shortName);
552                                if (newShortName == null) {
553                                    continue;
554                                }
555                                shortName = newShortName;
556                            }
557                            if (shortName.equals("_futureElements")) {
558                                continue;
559                            }
560                            shortName = this.initUpper(shortName);
561                            fields.add(shortName);
562                        }
563                    }
564                    return fields;
565                }
566            }
567            return null;
568        }
569    
570        private String getShortNameFromElements(String shortName, JavaClass messageStructureJavaClass) {
571            JavaClass elementsJavaClass = messageStructureJavaClass.getNestedClassByName("Elements");
572            if (elementsJavaClass == null) {
573                return null;
574            }
575            String fieldName = shortName.substring(shortName.indexOf(".Elements.") + ".Elements.".length());
576            JavaField field = elementsJavaClass.getFieldByName(fieldName);
577            String initExpr = field.getInitializationExpression();
578            return stripQuotes(initExpr);
579        }
580    
581        private String getCoreConstants(String shortName) {
582            if (shortName.endsWith("VERSION_NUMBER")) {
583                return "versionNumber";
584            }
585            if (shortName.endsWith("OBJECT_ID")) {
586                return "objectId";
587            }
588            if (shortName.endsWith("ACTIVE")) {
589                return "active";
590            }
591            if (shortName.endsWith("ACTIVE_FROM_DATE")) {
592                return "activeFromDate";
593            }
594            if (shortName.endsWith("ACTIVE_TO_DATE")) {
595                return "activeToDate";
596            }
597            if (shortName.endsWith("ATTRIBUTES")) {
598                return "attributes";
599            }
600            if (shortName.endsWith("FUTURE_ELEMENTS")) {
601                return "_futureElements";
602            }
603            throw new RuntimeException("Unknown shortName " + shortName);
604        }
605    
606        private void addMessageStructure(JavaClass messageStructureJavaClass,
607                String serviceKey) {
608            Set<JavaClass> subObjectsToAdd = new LinkedHashSet<JavaClass>();
609            for (String shortName : this.getShortNames(messageStructureJavaClass)) {
610                JavaMethod setterMethod = findSetterMethod(messageStructureJavaClass,
611                        shortName);
612                JavaMethod getterMethod = findGetterMethod(messageStructureJavaClass,
613                        shortName);
614                if (getterMethod == null) {
615                    if (this.validateKualiStudent) {
616                        throw new IllegalArgumentException("shortName has no corresponding getter method: "
617                                + messageStructureJavaClass.getFullyQualifiedName()
618                                + "." + shortName);
619                    }
620                }
621                JavaField beanField = this.findField(messageStructureJavaClass,
622                        shortName, setterMethod);
623                if (beanField == null) {
624                    String accessorType = getAccessorType(getterMethod);
625                    if ("XmlAccessType.FIELD".equals(accessorType)) {
626                        throw new IllegalArgumentException("Setter method has no corresponding bean field: "
627                                + messageStructureJavaClass.getName()
628                                + "." + getterMethod.getName());
629                    }
630                }
631                // overide the shortName if the bean field has an XmlAttribute name=xxx
632                // this catches the key=id switch
633                if (beanField != null) {
634                    for (Annotation annotation : beanField.getAnnotations()) {
635                        if (annotation.getType().getJavaClass().getName().equals("XmlAttribute")) {
636                            Object nameValue = annotation.getNamedParameter("name");
637                            if (nameValue != null) {
638                                shortName = stripQuotes(nameValue.toString());
639                            }
640                        }
641                    }
642                }
643                shortName = initLower(shortName);
644                MessageStructure ms = new MessageStructure();
645                messageStructures.add(ms);
646                ms.setXmlObject(messageStructureJavaClass.getName());
647                ms.setShortName(shortName);
648                ms.setId(ms.getXmlObject() + "." + ms.getShortName());
649                ms.setName(calcMissing(calcName(messageStructureJavaClass, getterMethod, setterMethod,
650                        beanField, shortName)));
651                ms.setType(calcType(messageStructureJavaClass, getterMethod, setterMethod, beanField, shortName));
652                if (ms.getType().equals("Object")) {
653                    System.out.println("WARNING " + ms.getId()
654                            + " has Object as it's type ==> Changing to String");
655                    ms.setType("String");
656                } else if (ms.getType().equals("ObjectList")) {
657                    System.out.println(
658                            "WARNING " + ms.getId()
659                            + " has a list of Objects as it's type ==> Changing to List of String");
660                    ms.setType("StringList");
661                }
662                ms.setXmlAttribute(this.calcXmlAttribute(beanField));
663                ms.setRequired(calcRequired(getterMethod, setterMethod, beanField));
664                ms.setReadOnly(calcReadOnly(getterMethod, setterMethod, beanField));
665                ms.setCardinality(this.calcCardinality(messageStructureJavaClass, getterMethod, setterMethod, beanField, shortName));
666                ms.setDescription(calcMissing(calcDescription(messageStructureJavaClass, getterMethod, setterMethod,
667                        beanField)));
668                ms.setImplNotes(calcImplementationNotes(getterMethod, setterMethod, beanField));
669                ms.setDeprecated(isDeprecated(getterMethod));
670                ms.setStatus("???");
671    //            if (ms.getId().equals("AcademicCalendarInfo.typeKey")) {
672    //                System.out.println("debug from here");
673    //            }
674                ms.setOverriden(this.calcOverridden(messageStructureJavaClass, getterMethod));
675                JavaClass subObjToAdd = this.calcRealJavaClassOfGetterReturn(getterMethod);
676                if (subObjToAdd != null) {
677    //                if (!subObjToAdd.isEnum()) {
678                    if (!subObjToAdd.getName().equals("Object")) {
679                        if (!subObjToAdd.getName().equals("LocaleKeyList")) {
680                            if (!subObjToAdd.getName().equals("MessageGroupKeyList")) {
681                                subObjectsToAdd.add(subObjToAdd);
682                            }
683                        }
684                    }
685    //                }
686                }
687            }
688            // now add all it's complex sub-objects if they haven't already been added
689            for (JavaClass subObjectToAdd : subObjectsToAdd) {
690                XmlType xmlType = xmlTypeMap.get(calcType(subObjectToAdd));
691                if (xmlType == null) {
692                    try {
693                                            addXmlTypeAndMessageStructure(subObjectToAdd, serviceKey);
694                                    } catch (Exception e) {
695                                            String message = "failed to parse subobject structure: " + subObjectToAdd + " : " + serviceKey;
696                                            // log into message
697                                            log.error (message + " : " + e.getMessage());
698                                            // log into debug log
699                                            log.debug(message, e);
700                                    }
701                } else {
702                    addServiceToList(xmlType, serviceKey);
703                }
704            }
705            return;
706        }
707    
708        private boolean calcOverridden(JavaClass mainClass, JavaMethod getterMethod) {
709            if (getterMethod == null) {
710                return false;
711            }
712            JavaMethod infcGetter = null;
713            if (getterMethod.getParentClass().isInterface()) {
714                infcGetter = getterMethod;
715            }
716            if (infcGetter == null) {
717                infcGetter = findInterfaceMethod(mainClass, getterMethod, false);
718            }
719            if (infcGetter == null) {
720                return false;
721            }
722            Annotation annotation = this.getAnnotation(infcGetter, null, null, "Override");
723            if (annotation != null) {
724                return true;
725            }
726            return false;
727        }
728    
729        private String calcComment(JavaClass javaClass) {
730            return this.calcComment(javaClass.getComment());
731        }
732    
733        private String calcComment(String comment) {
734            return this.parseCommentVersion(comment)[0];
735        }
736    
737        private String calcVersion(JavaClass javaClass) {
738            DocletTag tag = javaClass.getTagByName("version", true);
739            if (tag != null) {
740                return tag.getValue();
741            }
742            return this.calcVersion(javaClass.getComment());
743        }
744    
745        private String calcVersion(String comment) {
746            return this.parseCommentVersion(comment)[1];
747        }
748    
749        private String[] parseCommentVersion(String commentVersion) {
750            String[] parsed = new String[2];
751            if (commentVersion == null) {
752                return parsed;
753            }
754            commentVersion = commentVersion.trim();
755            int i = commentVersion.toLowerCase().indexOf("\nversion:");
756            if (i == -1) {
757                parsed[0] = commentVersion;
758                return parsed;
759            }
760            parsed[1] = commentVersion.substring(i + "\nversion:".length()).trim();
761            parsed[0] = commentVersion.substring(0, i).trim();
762    
763            return parsed;
764        }
765    
766        private Annotation getAnnotation(JavaMethod getterMethod,
767                JavaMethod setterMethod, JavaField beanField, String type) {
768            if (beanField != null) {
769    
770                for (Annotation annotation : beanField.getAnnotations()) {
771                    if (annotation.getType().getJavaClass().getName().equals(type)) {
772                        return annotation;
773                    }
774                }
775            }
776            if (getterMethod != null) {
777    
778                for (Annotation annotation : getterMethod.getAnnotations()) {
779                    if (annotation.getType().getJavaClass().getName().equals(type)) {
780                        return annotation;
781                    }
782                }
783            }
784            if (setterMethod != null) {
785    
786                for (Annotation annotation : setterMethod.getAnnotations()) {
787                    if (annotation.getType().getJavaClass().getName().equals(type)) {
788                        return annotation;
789                    }
790                }
791            }
792            return null;
793        }
794    
795        private String calcRequired(JavaMethod getterMethod,
796                JavaMethod setterMethod, JavaField beanField) {
797            Annotation annotation = this.getAnnotation(getterMethod, setterMethod, beanField, "XmlElement");
798            if (annotation == null) {
799                annotation = this.getAnnotation(getterMethod, setterMethod, beanField, "XmlAttribute");
800            }
801            if (annotation != null) {
802                Object required = annotation.getNamedParameter("required");
803                if (required != null) {
804                    if (required.toString().equalsIgnoreCase("true")) {
805                        return "Required";
806                    }
807                }
808            }
809            if (getterMethod != null) {
810                DocletTag tag = getterMethod.getTagByName("required", true);
811                if (tag != null) {
812                    if (tag.getValue() == null) {
813                        return "Required";
814                    }
815                    String required = "Required " + tag.getValue();
816                    return required.trim();
817                }
818            }
819            return null;
820        }
821    
822        private String calcReadOnly(JavaMethod getterMethod,
823                JavaMethod setterMethod, JavaField beanField) {
824            if (getterMethod != null) {
825                DocletTag tag = getterMethod.getTagByName("readOnly", true);
826                if (tag != null) {
827                    if (tag.getValue() == null) {
828                        return "Read only";
829                    }
830                    String readOnly = "Read only " + tag.getValue();
831                    return readOnly.trim();
832                }
833            }
834            return null;
835        }
836    
837        private String calcImplementationNotes(JavaMethod serviceMethod) {
838            StringBuilder bldr = new StringBuilder();
839            String newLine = "";
840            for (DocletTag tag : serviceMethod.getTagsByName("impl", true)) {
841                bldr.append(newLine);
842                newLine = "\n";
843                String value = tag.getValue();
844                bldr.append(value);
845            }
846            if (hasOverride(serviceMethod)) {
847                boolean matchJustOnName = true;
848                JavaMethod overriddenMethod = findInterfaceMethod(serviceMethod.getParentClass(), serviceMethod, matchJustOnName);
849                if (overriddenMethod == null) {
850                    // do it again so we can debug
851                    findInterfaceMethod(serviceMethod.getParentClass(), serviceMethod, true);
852                    throw new NullPointerException("could not find overridden method or method that has @Override annotation " + serviceMethod.getCallSignature());
853                }
854                bldr.append(newLine);
855                newLine = "\n";
856                bldr.append("Overridden method should be implemented in helper: ");
857                bldr.append(overriddenMethod.getParentClass().getName());
858            }
859            if (bldr.length() == 0) {
860                return null;
861            }
862            return bldr.toString();
863        }
864    
865        private boolean hasOverride(JavaMethod serviceMethod) {
866            for (Annotation annotation : serviceMethod.getAnnotations()) {
867                if (annotation.getType().getJavaClass().getName().equals(
868                        "Override")) {
869                    return true;
870                }
871            }
872            return false;
873        }
874    
875        private boolean isDeprecated(JavaMethod serviceMethod) {
876            for (Annotation annotation : serviceMethod.getAnnotations()) {
877                if (annotation.getType().getJavaClass().getName().equals(
878                        "Deprecated")) {
879                    return true;
880                }
881            }
882            return false;
883        }
884    
885        private String calcImplementationNotes(JavaMethod getterMethod,
886                JavaMethod setterMethod, JavaField beanField) {
887            if (getterMethod != null) {
888                DocletTag tag = getterMethod.getTagByName("impl", true);
889                if (tag != null) {
890                    return tag.getValue();
891                }
892            }
893            return null;
894        }
895    
896        private String calcNameFromShortName(String shortName) {
897            StringBuilder bldr = new StringBuilder(shortName.length() + 3);
898            char c = shortName.charAt(0);
899            bldr.append(Character.toUpperCase(c));
900            boolean lastWasUpper = true;
901            for (int i = 1; i < shortName.length(); i++) {
902                c = shortName.charAt(i);
903                if (Character.isUpperCase(c)) {
904                    if (!lastWasUpper) {
905                        bldr.append(" ");
906                    }
907                } else {
908                    lastWasUpper = false;
909                }
910                bldr.append(c);
911            }
912            return bldr.toString();
913        }
914    
915        private String calcName(JavaClass mainClass, JavaMethod getterMethod,
916                JavaMethod setterMethod, JavaField beanField, String shortName) {
917            String name = this.calcNameFromTag(getterMethod, setterMethod, beanField);
918            if (name != null) {
919                return name;
920            }
921            name = this.calcNameFromNameEmbeddedInDescription(mainClass, getterMethod, setterMethod, beanField);
922            if (name != null) {
923                return name;
924            }
925            return this.calcNameFromShortName(shortName);
926        }
927    
928        private String calcNameFromTag(JavaMethod getterMethod,
929                JavaMethod setterMethod, JavaField beanField) {
930            if (getterMethod != null) {
931                DocletTag tag = getterMethod.getTagByName("name", true);
932                if (tag != null) {
933                    return tag.getValue();
934                }
935            }
936            return null;
937        }
938    
939        private String calcNameFromNameEmbeddedInDescription(JavaClass mainClass, JavaMethod getterMethod,
940                JavaMethod setterMethod, JavaField beanField) {
941            String nameDesc = this.calcMethodComment(mainClass, getterMethod, setterMethod,
942                    beanField);
943            String[] parsed = parseNameDesc(nameDesc);
944            return parsed[0];
945        }
946    
947        private String[] parseNameDesc(String nameDesc) {
948            String[] parsed = new String[2];
949            if (nameDesc == null) {
950                return parsed;
951            }
952            nameDesc = nameDesc.trim();
953            if (!nameDesc.startsWith("Name:")) {
954                parsed[1] = nameDesc;
955                return parsed;
956            }
957            nameDesc = nameDesc.substring("Name:".length()).trim();
958            int i = nameDesc.indexOf("\n");
959            if (i == -1) {
960                parsed[0] = nameDesc.trim();
961                return parsed;
962            }
963            parsed[0] = nameDesc.substring(0, i).trim();
964            parsed[1] = nameDesc.substring(i).trim();
965            return parsed;
966        }
967    
968        private String calcDescription(JavaClass mainClass, JavaMethod getterMethod,
969                JavaMethod setterMethod, JavaField beanField) {
970            String nameDesc = this.calcMethodComment(mainClass, getterMethod, setterMethod,
971                    beanField);
972            String[] parsed = parseNameDesc(nameDesc);
973            return parsed[1];
974        }
975    
976        private String calcMethodComment(JavaClass mainClass, JavaMethod getterMethod,
977                JavaMethod setterMethod,
978                JavaField beanField) {
979            String desc = null;
980            if (getterMethod != null) {
981                desc = getterMethod.getComment();
982                if (isCommentNotEmpty(desc)) {
983                    return desc;
984                }
985            }
986            if (setterMethod != null) {
987                desc = setterMethod.getComment();
988                if (isCommentNotEmpty(desc)) {
989                    return desc;
990                }
991            }
992            if (beanField != null) {
993                desc = beanField.getComment();
994                if (isCommentNotEmpty(desc)) {
995                    return desc;
996                }
997            }
998            desc = calcMethodCommentRecursively(mainClass, getterMethod);
999            if (isCommentNotEmpty(desc)) {
1000                return desc;
1001            }
1002            desc = calcMethodCommentRecursively(mainClass, setterMethod);
1003            if (isCommentNotEmpty(desc)) {
1004                return desc;
1005            }
1006            return null;
1007        }
1008    
1009        private String calcMethodCommentRecursively(JavaClass mainClass, JavaMethod method) {
1010            if (method == null) {
1011                return null;
1012            }
1013            String desc = method.getComment();
1014            if (isCommentNotEmpty(desc)) {
1015                return desc;
1016            }
1017            JavaMethod infcMethod = findInterfaceMethod(mainClass, method, false);
1018            if (infcMethod != null) {
1019                desc = infcMethod.getComment();
1020                if (isCommentNotEmpty(desc)) {
1021                    return desc;
1022                }
1023            }
1024            JavaMethod superMethod = findSuperMethod(method);
1025            if (superMethod != null) {
1026                desc = superMethod.getComment();
1027                if (isCommentNotEmpty(desc)) {
1028                    return desc;
1029                }
1030            }
1031            return null;
1032        }
1033    
1034        private JavaMethod findSuperMethod(JavaMethod method) {
1035    //        System.out.println("Searching for super method for "
1036    //                + method.getParentClass().getName() + "."
1037    //                + method.getCallSignature());
1038            for (JavaMethod superMethod : method.getParentClass().getMethods(true)) {
1039                if (method.equals(superMethod)) {
1040                    continue;
1041                }
1042                if (method.getCallSignature().equals(superMethod.getCallSignature())) {
1043                    return superMethod;
1044                }
1045            }
1046            return null;
1047        }
1048    
1049        private JavaMethod findInterfaceMethod(JavaClass mainClass, JavaMethod method, boolean matchJustOnName) {
1050            String callSig = method.getCallSignature();
1051            if (matchJustOnName) {
1052                callSig = method.getName();
1053            }
1054            JavaClass classToSearch = mainClass;
1055    //        log ("Searching mainClass " + classToSearch.getName() + " for " + callSig, callSig);
1056            while (true) {
1057                for (JavaClass infcClass : classToSearch.getImplementedInterfaces()) {
1058                    JavaMethod meth = this.findMethodOnInterfaceRecursively(infcClass, callSig, matchJustOnName);
1059                    if (meth != null) {
1060    //                    recursionCntr = 0;
1061                        return meth;
1062                    }
1063                }
1064                JavaClass superClass = classToSearch.getSuperJavaClass();
1065                if (superClass == null) {
1066    //                recursionCntr = 0;                
1067    //                log ("Did not find " + callSig + " on " + mainClass, callSig); 
1068                    return null;
1069                }
1070                classToSearch = superClass;
1071    //            log ("Searching superClass " + classToSearch.getName() + " for " + callSig, callSig);                
1072            }
1073        }
1074    
1075    //    private void log (String message, String callSig) {
1076    //        if (callSig.equalsIgnoreCase("getTypeKey()")) {
1077    //            for (int i = 0; i < this.recursionCntr; i++) {
1078    //                System.out.print (" ");
1079    //            }
1080    //            System.out.println (message);
1081    //        }
1082    //    }
1083    //    private int recursionCntr = 0;
1084        private JavaMethod findMethodOnInterfaceRecursively(JavaClass infcClass, String callSig, boolean matchJustOnName) {
1085    //        recursionCntr++;
1086    //        log ("Searching interface " + infcClass.getName() + " for " + callSig, callSig);
1087            for (JavaMethod infcMethod : infcClass.getMethods()) {
1088                if (callSig.equals(infcMethod.getCallSignature())) {
1089    //                log (callSig + " found on " + infcClass.getName() + "!!!!!!!!!!!!!!!!", callSig); 
1090    //                recursionCntr--;
1091                    return infcMethod;
1092                }
1093                if (matchJustOnName) {
1094                    if (callSig.equals(infcMethod.getName())) {
1095                        return infcMethod;
1096                    }
1097                }
1098            }
1099            for (JavaClass subInfc : infcClass.getImplementedInterfaces()) {
1100    //            log ("Searching  sub-interface " + subInfc.getName() + " for " + callSig, callSig);
1101                JavaMethod infcMethod = findMethodOnInterfaceRecursively(subInfc, callSig, matchJustOnName);
1102                if (infcMethod != null) {
1103    //                recursionCntr--;
1104                    return infcMethod;
1105                }
1106            }
1107    //        log (callSig + " not found on " + infcClass.getName(), callSig);
1108    //        this.recursionCntr--;
1109            return null;
1110        }
1111    
1112        private boolean isCommentNotEmpty(String desc) {
1113            if (desc == null) {
1114                return false;
1115            }
1116            if (desc.trim().isEmpty()) {
1117                return false;
1118            }
1119            if (desc.contains("@inheritDoc")) {
1120                return false;
1121            }
1122            return true;
1123        }
1124    
1125        private String getAccessorType(JavaMethod method) {
1126            String accessorType = getAccessorType(method.getAnnotations());
1127            if (accessorType != null) {
1128                return accessorType;
1129            }
1130            accessorType = getAccessorType(method.getParentClass().getAnnotations());
1131            return accessorType;
1132        }
1133    
1134        private String getAccessorType(Annotation[] annotations) {
1135            for (Annotation annotation : annotations) {
1136                if (annotation.getType().getJavaClass().getName().equals(
1137                        "XmlAccessorType")) {
1138    //    System.out.println ("Looking for XmlAccessorType annotation = "
1139    //                        + annotation.getParameterValue ());
1140                    return annotation.getParameterValue().toString();
1141                }
1142            }
1143            return null;
1144        }
1145    
1146        private String stripQuotes(String str) {
1147            if (str.startsWith("\"")) {
1148                str = str.substring(1);
1149            }
1150            if (str.endsWith("\"")) {
1151                str = str.substring(0, str.length() - 1);
1152            }
1153            return str;
1154        }
1155    
1156        private String calcMissing(String str) {
1157            if (str == null) {
1158                return "???";
1159            }
1160            if (str.trim().isEmpty()) {
1161                return "???";
1162            }
1163            return str;
1164        }
1165    
1166        private void addServiceToList(XmlType xmlType, String serviceKey) {
1167            if (!xmlType.getService().contains(serviceKey)) {
1168                xmlType.setService(xmlType.getService() + ", " + serviceKey);
1169            }
1170        }
1171    
1172        private String calcXmlAttribute(JavaField beanField) {
1173            if (beanField == null) {
1174                // TODO: worry about checking for this annotation on the method for non-field based AccessorTypes
1175                return "No";
1176            }
1177            for (Annotation annotation : beanField.getAnnotations()) {
1178                if (annotation.getType().getJavaClass().getName().equals("XmlAttribute")) {
1179                    return "Yes";
1180                }
1181            }
1182            return "No";
1183        }
1184    
1185        private JavaField findField(JavaClass javaClass, String shortName,
1186                JavaMethod setterMethod) {
1187            JavaField field = findField(javaClass, shortName);
1188            if (field != null) {
1189                return field;
1190            }
1191            if (setterMethod != null) {
1192                String paramName = setterMethod.getParameters()[0].getName();
1193                if (paramName.equalsIgnoreCase(shortName)) {
1194                    return null;
1195                }
1196                return findField(javaClass, paramName);
1197            }
1198            return null;
1199        }
1200    
1201        private JavaField findField(JavaClass javaClass, String name) {
1202            if (name == null) {
1203                return null;
1204            }
1205            for (JavaField field : javaClass.getFields()) {
1206                if (field.getName().equalsIgnoreCase(name)) {
1207                    return field;
1208                }
1209                // TODO: check for shortNames that already start with is so we don't check for isIsEnrollable
1210                if (field.getName().equals("is" + name)) {
1211                    return field;
1212                }
1213            }
1214            JavaClass superClass = javaClass.getSuperJavaClass();
1215            if (superClass == null) {
1216                return null;
1217            }
1218            return findField(superClass, name);
1219        }
1220    
1221        private JavaMethod findGetterMethod(JavaClass msClass, String shortName) {
1222            for (JavaMethod method : msClass.getMethods(true)) {
1223                if (method.getName().equalsIgnoreCase("get" + shortName)) {
1224                    return method;
1225                }
1226                if (method.getName().toLowerCase().startsWith("is")) {
1227                    if (method.getName().equalsIgnoreCase("is" + shortName)) {
1228                        return method;
1229                    }
1230                    // shortName already has "is" in it
1231                    if (method.getName().equalsIgnoreCase(shortName)) {
1232                        return method;
1233                    }
1234                }
1235                // TODO: followup on KimEntityResidencyInfo.getInState
1236                if (method.getName().equalsIgnoreCase("getInState") && shortName.equalsIgnoreCase(
1237                        "InStateFlag")) {
1238                    return method;
1239                }
1240            }
1241            return null;
1242        }
1243    
1244        private JavaMethod findSetterMethod(JavaClass msClass, String shortName) {
1245            for (JavaMethod method : msClass.getMethods(true)) {
1246                if (method.getName().equals("set" + shortName)) {
1247                    return method;
1248                }
1249                // TODO: check for shortNames that already start with is so we don't check for isIsEnrollable
1250                if (method.getName().equals("setIs" + shortName)) {
1251                    return method;
1252                }
1253                // TODO: followup on KimEntityResidencyInfo.getInState
1254                if (method.getName().equals("setInStateFlag") && shortName.equals(
1255                        "InState")) {
1256                    return method;
1257                }
1258            }
1259            return null;
1260        }
1261        private static final String[] SETTER_METHODS_TO_SKIP = {
1262            // Somebody put "convenience" methods on the validation result info
1263            "ValidationResultInfo.setWarning",
1264            "ValidationResultInfo.setError",
1265            // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1266            "CredentialProgramInfo.setDiplomaTitle",
1267            // synonym for the official of setCredentialType
1268            "CredentialProgramInfo.setType",
1269            // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1270            "CredentialProgramInfo.setHegisCode",
1271            "CredentialProgramInfo.setCip2000Code",
1272            "CredentialProgramInfo.setCip2010Code",
1273            "CredentialProgramInfo.setSelectiveEnrollmentCode",
1274            "CoreProgramInfo.setDiplomaTitle",
1275            // synonym for the official of setCredentialType
1276            //  "CoreProgramInfo.setType",
1277            // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1278            "CoreProgramInfo.setHegisCode",
1279            "CoreProgramInfo.setCip2000Code",
1280            "CoreProgramInfo.setCip2010Code",
1281            "CoreProgramInfo.setSelectiveEnrollmentCode",
1282            "WhenConstraint.setValue"
1283        };
1284        private static final String[] GETTER_METHODS_TO_SKIP = {
1285            // Somebody put "convenience" methods on the validation result info
1286            "ValidationResultInfo.getWarning",
1287            "ValidationResultInfo.getError",
1288            // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1289            "CredentialProgramInfo.getDiplomaTitle",
1290            // synonym for the official of setCredentialType
1291            "CredentialProgramInfo.getType",
1292            // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1293            "CredentialProgramInfo.getHegisCode",
1294            "CredentialProgramInfo.getCip2000Code",
1295            "CredentialProgramInfo.getCip2010Code",
1296            "CredentialProgramInfo.getSelectiveEnrollmentCode",
1297            "CoreProgramInfo.getDiplomaTitle",
1298            // synonym for the official of setCredentialType
1299            //  "CoreProgramInfo.setType",
1300            // not on original wiki but still defined as a method but not backed by a field so not in wsdl
1301            "CoreProgramInfo.getHegisCode",
1302            "CoreProgramInfo.getCip2000Code",
1303            "CoreProgramInfo.getCip2010Code",
1304            "CoreProgramInfo.getSelectiveEnrollmentCode",
1305            "WhenConstraint.getValue"
1306        };
1307    
1308        private boolean isSetterMethodToProcess(JavaMethod method, String className) {
1309            if (!method.getName().startsWith("set")) {
1310                return false;
1311            }
1312            if (method.getParameters().length != 1) {
1313                return false;
1314            }
1315            if (method.isPrivate()) {
1316                return false;
1317            }
1318            if (method.isProtected()) {
1319                return false;
1320            }
1321            if (method.isStatic()) {
1322                return false;
1323            }
1324            if (method.getParentClass().getPackageName().startsWith("java")) {
1325                return false;
1326            }
1327            String fullName = className + "." + method.getName();
1328            for (String skip : SETTER_METHODS_TO_SKIP) {
1329                if (skip.equals(fullName)) {
1330                    return false;
1331                }
1332            }
1333    //  if (method.getParentClass ().isInterface ())
1334    //  {
1335    //   return false;
1336    //  }
1337            for (Annotation annotation : method.getAnnotations()) {
1338                if (annotation.getType().getJavaClass().getName().equals("XmlTransient")) {
1339                    return false;
1340                }
1341            }
1342            return true;
1343        }
1344    
1345        private boolean isGetterMethodToProcess(JavaMethod method, String className) {
1346            if (!method.getName().startsWith("get")) {
1347                if (!method.getName().startsWith("is")) {
1348                    return false;
1349                }
1350            }
1351            if (method.getParameters().length != 0) {
1352                return false;
1353            }
1354            if (method.isPrivate()) {
1355                return false;
1356            }
1357            if (method.isProtected()) {
1358                return false;
1359            }
1360            if (method.isStatic()) {
1361                return false;
1362            }
1363            if (method.getParentClass().getPackageName().startsWith("java")) {
1364                return false;
1365            }
1366            String fullName = className + "." + method.getName();
1367            for (String skip : GETTER_METHODS_TO_SKIP) {
1368                if (skip.equals(fullName)) {
1369                    return false;
1370                }
1371            }
1372    //  if (method.getParentClass ().isInterface ())
1373    //  {
1374    //   return false;
1375    //  }
1376            for (Annotation annotation : method.getAnnotations()) {
1377                if (annotation.getType().getJavaClass().getName().equals("XmlTransient")) {
1378                    return false;
1379                }
1380            }
1381            return true;
1382        }
1383    
1384        private String calcShortNameFromSetter(JavaMethod method) {
1385            return method.getName().substring(3);
1386        }
1387    
1388        private String calcShortNameFromGetter(JavaMethod method) {
1389            if (method.getName().startsWith("get")) {
1390                return method.getName().substring(3);
1391            }
1392            if (method.getName().startsWith("is")) {
1393                return method.getName().substring(2);
1394            }
1395            throw new IllegalArgumentException(method.getName()
1396                    + " does not start with is or get");
1397        }
1398    
1399        private String calcCardinality(JavaClass mainClass, JavaMethod getterMethod,
1400                JavaMethod setterMethod, JavaField beanField, String shortName) {
1401            if (isReturnACollection(mainClass, getterMethod, setterMethod, beanField, shortName)) {
1402                return "Many";
1403            }
1404            return "One";
1405        }
1406    
1407        private boolean isReturnACollection(JavaClass mainClass, JavaMethod getterMethod,
1408                JavaMethod setterMethod, JavaField beanField, String shortName) {
1409            if (getterMethod != null) {
1410                return isCollection(getterMethod.getReturnType());
1411            }
1412            if (beanField != null) {
1413                return isCollection(beanField.getType());
1414            }
1415            // TODO: check setterMethod
1416            return false;
1417        }
1418    
1419        private boolean isCollection(Type type) {
1420            JavaClass javaClass = type.getJavaClass();
1421            return this.isCollection(javaClass);
1422        }
1423    
1424        private boolean isCollection(JavaClass javaClass) {
1425            if (javaClass.getName().equals("LocalKeyList")) {
1426                return true;
1427            }
1428            if (javaClass.getName().equals("MessageGroupKeyList")) {
1429                return true;
1430            }
1431            if (javaClass.getName().equals(List.class.getSimpleName())) {
1432                return true;
1433            }
1434            if (javaClass.getName().equals(ArrayList.class.getSimpleName())) {
1435                return true;
1436            }
1437            if (javaClass.getName().equals(Collection.class.getSimpleName())) {
1438                return true;
1439            }
1440            if (javaClass.getName().equals(Set.class.getSimpleName())) {
1441                return true;
1442            }
1443            return false;
1444        }
1445    
1446        private String calcType(JavaClass mainClass, JavaMethod getterMethod,
1447                JavaMethod setterMethod, JavaField beanField, String shortName) {
1448            if (getterMethod != null) {
1449                return calcTypeOfGetterMethodReturn(getterMethod);
1450            }
1451            if (beanField != null) {
1452                Type type = beanField.getType();
1453                return calcType(type);
1454            }
1455            // TODO: calc type based on the setterMethod
1456            return null;
1457        }
1458    
1459        private String calcTypeOfGetterMethodReturn(JavaMethod getterMethod) {
1460            Type type = getterMethod.getReturnType();
1461            return calcType(type);
1462        }
1463    
1464        private String calcType(Type type) {
1465            if (type == null) {
1466                return "void";
1467            }
1468            if (isCollection(type.getJavaClass())) {
1469                return calcType(calcRealJavaClass(type)) + "List";
1470            }
1471            return calcType(calcRealJavaClass(type));
1472        }
1473    
1474        private String calcType(JavaClass javaClass) {
1475            if (javaClass.isEnum()) {
1476                // TODO: instead of hand mapping this take it based on the class in the @XmlEnum(String.class) tag
1477                if (javaClass.getName().equals("ErrorLevel")) {
1478                    return "Integer";
1479                }
1480                if (javaClass.getName().equals("StatementOperatorTypeKey")) {
1481                    return "String";
1482                }
1483                if (javaClass.getName().equals("WriteAccess")) {
1484                    return "String";
1485                }
1486                if (javaClass.getName().equals("Widget")) {
1487                    return "String";
1488                }
1489                if (javaClass.getName().equals("DataType")) {
1490                    return "String";
1491                }
1492                if (javaClass.getName().equals("SortDirection")) {
1493                    return "String";
1494                }
1495                if (javaClass.getName().equals("Usage")) {
1496                    return "String";
1497                }
1498                if (javaClass.getName().equals("StatementOperator")) {
1499                    return "String";
1500                }
1501            }
1502            // this is messed up instead of list of strings it is an object with a list of strings
1503            if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1504                return "StringList";
1505            }
1506            if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1507                return "StringList";
1508            }
1509            // TODO: figure out why rice stuff translates like this junk?
1510            if (javaClass.getName().equals("java$util$Map")) {
1511                return "Map<String, String>";
1512            }
1513            if (javaClass.getName().equals("Map")) {
1514                // TODO: make sure it is in fact a String,String map
1515                return "Map<String, String>";
1516            }
1517            return javaClass.getName();
1518        }
1519    
1520        private JavaClass calcRealJavaClassOfGetterReturn(JavaMethod getterMethod) {
1521            if (getterMethod == null) {
1522                return null;
1523            }
1524            Type type = getterMethod.getReturnType();
1525            return this.calcRealJavaClass(type);
1526        }
1527    
1528        private JavaClass calcRealJavaClass(Type type) {
1529            if (type == null) {
1530                return null;
1531            }
1532            JavaClass javaClass = type.getJavaClass();
1533            if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1534                return STRING_JAVA_CLASS;
1535            }
1536            if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1537                return STRING_JAVA_CLASS;
1538            }
1539            if (!this.isCollection(javaClass)) {
1540                return javaClass;
1541            }
1542    
1543    //  for (Type t : type.getActualTypeArguments ())
1544    //  {
1545    //   System.out.println ("ServiceContractModelQDoxLoader: type arguments = "
1546    //                       + t.toString ());
1547    //  }
1548    
1549            Type[]collectionTypeArguments = type.getActualTypeArguments();
1550            
1551            if (collectionTypeArguments == null)
1552                    return new JavaClass (Object.class.getName());
1553            else
1554                    return collectionTypeArguments[0].getJavaClass();
1555        }
1556    
1557        private boolean isComplex(JavaClass javaClass) {
1558            if (javaClass.isEnum()) {
1559                return false;
1560            }
1561            if (javaClass.getName().equals(String.class.getSimpleName())) {
1562                return false;
1563            }
1564            if (javaClass.getName().equals(Integer.class.getSimpleName())) {
1565                return false;
1566            }
1567            if (javaClass.getName().equals(Date.class.getSimpleName())) {
1568                return false;
1569            }
1570            if (javaClass.getName().equals(Long.class.getSimpleName())) {
1571                return false;
1572            }
1573            if (javaClass.getName().equals(Boolean.class.getSimpleName())) {
1574                return false;
1575            }
1576            if (javaClass.getName().equals(Double.class.getSimpleName())) {
1577                return false;
1578            }
1579            if (javaClass.getName().equals(Float.class.getSimpleName())) {
1580                return false;
1581            }
1582            if (javaClass.getName().equals(int.class.getSimpleName())) {
1583                return false;
1584            }
1585            if (javaClass.getName().equals(long.class.getSimpleName())) {
1586                return false;
1587            }
1588            if (javaClass.getName().equals(boolean.class.getSimpleName())) {
1589                return false;
1590            }
1591            if (javaClass.getName().equals(double.class.getSimpleName())) {
1592                return false;
1593            }
1594            if (javaClass.getName().equals(float.class.getSimpleName())) {
1595                return false;
1596            }
1597            if (javaClass.getName().equals(Map.class.getSimpleName())) {
1598                return false;
1599            }
1600    
1601            if (javaClass.getName().equals(String.class.getName())) {
1602                return false;
1603            }
1604            if (javaClass.getName().equals(Integer.class.getName())) {
1605                return false;
1606            }
1607            if (javaClass.getName().equals(Date.class.getName())) {
1608                return false;
1609            }
1610            if (javaClass.getName().equals(Long.class.getName())) {
1611                return false;
1612            }
1613            if (javaClass.getName().equals(Boolean.class.getName())) {
1614                return false;
1615            }
1616            if (javaClass.getName().equals(Double.class.getName())) {
1617                return false;
1618            }
1619            if (javaClass.getName().equals(Float.class.getName())) {
1620                return false;
1621            }
1622            if (javaClass.getName().equals(int.class.getName())) {
1623                return false;
1624            }
1625            if (javaClass.getName().equals(long.class.getName())) {
1626                return false;
1627            }
1628            if (javaClass.getName().equals(boolean.class.getName())) {
1629                return false;
1630            }
1631            if (javaClass.getName().equals(double.class.getName())) {
1632                return false;
1633            }
1634            if (javaClass.getName().equals(float.class.getName())) {
1635                return false;
1636            }
1637            if (javaClass.getName().equals(Map.class.getName())) {
1638                return false;
1639            }
1640            if (javaClass.getName().equals(LOCALE_KEY_LIST)) {
1641                return false;
1642            }
1643            if (javaClass.getName().equals(MESSAGE_GROUP_KEY_LIST)) {
1644                return false;
1645            }
1646            if (javaClass.getName().equals("java$util$Map")) {
1647                return false;
1648            }
1649            return true;
1650        }
1651    }