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.jpa.mojo;
017    
018    import org.kuali.student.contract.model.ServiceContractModel;
019    import org.kuali.student.contract.model.ServiceMethod;
020    import org.kuali.student.contract.model.ServiceMethodError;
021    import org.kuali.student.contract.model.ServiceMethodParameter;
022    import org.kuali.student.contract.model.XmlType;
023    import org.kuali.student.contract.model.util.ModelFinder;
024    import org.kuali.student.contract.writer.JavaClassWriter;
025    import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
026    import org.kuali.student.contract.writer.service.MessageStructureTypeCalculator;
027    import org.kuali.student.contract.writer.service.ServiceExceptionWriter;
028    import org.slf4j.Logger;
029    import org.slf4j.LoggerFactory;
030    
031    import java.util.*;
032    import javax.persistence.Query;
033    import org.kuali.student.contract.model.MessageStructure;
034    
035    /**
036     *
037     * @author nwright
038     */
039    public class JpaDaoWriter extends JavaClassWriter {
040    
041        private static Logger log = LoggerFactory.getLogger(JpaDaoWriter.class);
042    
043        //////////////////////////////
044        // Constants
045        //////////////////////////////
046        /**
047         * The standard type of methods used in our Service contract.
048         */
049        protected static enum MethodType {
050    
051            VALIDATE,
052            CREATE,
053            CREATE_BULK,
054            ADD,
055            UPDATE,
056            UPDATE_OTHER,
057            DELETE,
058            REMOVE,
059            DELETE_OTHER,
060            GET_CREATE,
061            GET_BY_ID,
062            GET_BY_IDS,
063            RICE_GET_BY_NAMESPACE_AND_NAME,
064            GET_IDS_BY_TYPE,
065            GET_IDS_BY_OTHER,
066            GET_INFOS_BY_OTHER,
067            GET_TYPE,
068            GET_TYPES,
069            SEARCH_FOR_IDS,
070            SEARCH_FOR_INFOS,
071            UNKNOWN
072        };
073    
074        //////////////////////////////
075        // Data Variables
076        //////////////////////////////
077        protected ServiceContractModel model;
078        protected ModelFinder finder;
079        private String directory;
080        private XmlType xmlType;
081        /**
082         * The package name is stored in the service object itself (the package spec
083         * kept moving around so I assumed the actual service name was unique but
084         * ran into a problem when we included rice because they have a StateService
085         * meaning US states and we have a StateService meaning the state of the
086         * object so I added logic to detect rice and prepend that "RICE." to it
087         */
088        private String rootPackage;
089    
090        /**
091         * Name of the service being operated on. If it is a RICE service it is
092         * prefixed with RICE. [11:32:18 AM] Norman Wright: short name... I think it
093         * gets it by taking the java class SimpleName and stripping off the word
094         * "Service" and I think making it lower case. [11:32:24 AM] Norman Wright:
095         * so OrganizationService becomes organization
096         */
097        protected String servKey;
098        /**
099         * A flag that holds if the service is an R1 service.
100         */
101        private boolean isR1;
102        private List<ServiceMethod> methods;
103    
104        //////////////////////////
105        // Constructor
106        //////////////////////////
107        public JpaDaoWriter(ServiceContractModel model,
108                String directory,
109                String rootPackage,
110                String servKey,
111                List<ServiceMethod> methods,
112                XmlType xmlType,
113                boolean isR1) {
114            super(directory, calcPackage(servKey, rootPackage), calcClassName(servKey, xmlType));
115            this.model = model;
116            this.finder = new ModelFinder(model);
117            this.directory = directory;
118            this.rootPackage = rootPackage;
119            this.servKey = servKey;
120            this.methods = methods;
121            this.xmlType = xmlType;
122            this.isR1 = isR1;
123        }
124    
125        public JpaDaoWriter(ServiceContractModel model,
126                String directory,
127                String rootPackage,
128                String servKey,
129                List<ServiceMethod> methods,
130                XmlType xmlType,
131                boolean isR1,
132                String packageName,
133                String className) {
134            super(directory, packageName, className);
135            this.model = model;
136            this.finder = new ModelFinder(model);
137            this.directory = directory;
138            this.rootPackage = rootPackage;
139            this.servKey = servKey;
140            this.methods = methods;
141            this.xmlType = xmlType;
142            this.isR1 = isR1;
143        }
144    
145        /////////////////////////
146        // Functional Methods
147        /////////////////////////
148        /**
149         * Returns the jpa implementation package name.
150         *
151         * @param servKey
152         * @param rootPackage
153         * @return
154         */
155        public static String calcPackage(String servKey, String rootPackage) {
156            String pack = rootPackage + ".";
157    //        String pack = rootPackage + "." + servKey.toLowerCase() + ".";
158    //  StringBuffer buf = new StringBuffer (service.getVersion ().length ());
159    //  for (int i = 0; i < service.getVersion ().length (); i ++)
160    //  {
161    //   char c = service.getVersion ().charAt (i);
162    //   c = Character.toLowerCase (c);
163    //   if (Character.isLetter (c))
164    //   {
165    //    buf.append (c);
166    //    continue;
167    //   }
168    //   if (Character.isDigit (c))
169    //   {
170    //    buf.append (c);
171    //   }
172    //  }
173    //  pack = pack + buf.toString ();
174            pack = pack + "service.impl.jpa." + servKey.toLowerCase();
175            return pack;
176        }
177    
178        /**
179         * Checks if this is a RICE service.
180         *
181         * @return true if this is a RICE service.
182         */
183        private boolean isRice() {
184            if (this.servKey.startsWith("RICE.")) {
185                return true;
186            }
187            return false;
188        }
189    
190        protected static String fixServKey(String servKey) {
191            if (servKey.startsWith("RICE.")) {
192                return servKey.substring("RICE.".length());
193            }
194            return servKey;
195        }
196    
197        /**
198         * Given the service key (name), returns a calculated class name for the jpa
199         * impl.
200         */
201        public static String calcClassName(String servKey, XmlType xmlType) {
202            String name = calcInfcName(xmlType) + "Dao";
203            return name;
204        }
205    
206        private String calcInfcName() {
207            String name = calcInfcName(xmlType);
208            String pkg = xmlType.getJavaPackage();
209            if (pkg.endsWith(".dto")) {
210                pkg = pkg.substring(0, pkg.length() - ".dto".length()) + ".infc";
211            }
212            importsAdd(pkg + "." + name);
213            return name;
214        }
215    
216        private String calcInfoName() {
217            String name = initUpper(xmlType.getName());
218            String pkg = xmlType.getJavaPackage();
219            importsAdd(pkg + "." + name);
220            return name;
221        }
222    
223        public static String calcInfcName(XmlType xmlType) {
224            String name = xmlType.getName();
225            if (name.endsWith("Info")) {
226                name = name.substring(0, name.length() - "Info".length());
227            }
228            name = GetterSetterNameCalculator.calcInitUpper(name);
229            return name;
230        }
231    
232        protected MethodType calcMethodType(ServiceMethod method) {
233    //        if (method.getName().equals("getInstructionalDaysForTerm")) {
234    //            System.out.println("debug here");
235    //        }
236            if (this.isRice()) {
237                if (method.getName().contains("ByNamespaceCodeAndName")) {
238                    return MethodType.RICE_GET_BY_NAMESPACE_AND_NAME;
239                }
240                if (method.getName().contains("ByNameAndNamespace")) {
241                    return MethodType.RICE_GET_BY_NAMESPACE_AND_NAME;
242                }
243                if (method.getName().startsWith("get")) {
244                    if (method.getParameters().size() == 1) {
245                        if (!method.getReturnValue().getType().endsWith("List")) {
246                            if (method.getParameters().get(0).getName().equals("id")) {
247                                return MethodType.GET_BY_ID;
248                            }
249    
250                        } else {
251                            if (method.getParameters().get(0).getName().equals("ids")) {
252                                return MethodType.GET_BY_IDS;
253                            }
254                        }
255                    }
256                }
257            }
258            if (method.getName().startsWith("validate")) {
259                return MethodType.VALIDATE;
260            }
261            if (method.getName().startsWith("create")) {
262                if (method.getName().startsWith("createBatch")) {
263                    return MethodType.CREATE_BULK;
264                }
265                if (method.getName().startsWith("createSocRolloverResultItems")) {
266                    return MethodType.CREATE_BULK;
267                }
268                if (method.getName().contains("FromExisting")) {
269                    return MethodType.CREATE_BULK;
270                }
271                ServiceMethodParameter infoParam = this.findInfoParameter(method);
272                if (infoParam == null) {
273                    return MethodType.CREATE_BULK;
274                }
275                if (method.getReturnValue().getType().endsWith("List")) {
276                    return MethodType.CREATE_BULK;
277                }
278                return MethodType.CREATE;
279            }
280            if (method.getName().startsWith("add")) {
281                return MethodType.ADD;
282            }
283            if (method.getName().startsWith("update")) {
284                if (this.findInfoParameter(method) != null) {
285                    return MethodType.UPDATE;
286                }
287                return MethodType.UPDATE_OTHER;
288            }
289            if (method.getName().startsWith("delete")) {
290                if (method.getName().contains("By")) {
291                    if (!method.getName().startsWith("deleteBy")) {
292                        return MethodType.DELETE_OTHER;
293                    }
294                }
295                if (method.getName().contains("For")) {
296                    if (!method.getName().startsWith("deleteFor")) {
297                        return MethodType.DELETE_OTHER;
298                    }
299                }
300                return MethodType.DELETE;
301            }
302            if (method.getName().startsWith("remove")) {
303                return MethodType.REMOVE;
304            }
305    
306            if (method.getName().startsWith("getCreate")) {
307                return MethodType.GET_CREATE;
308            }
309    
310            if (method.getName().startsWith("get")) {
311                if (method.getName().endsWith("ByIds")) {
312                    return MethodType.GET_BY_IDS;
313                }
314                if (method.getName().endsWith("ByKeys")) {
315                    return MethodType.GET_BY_IDS;
316                }
317                if (method.getName().endsWith("ByType")) {
318                    return MethodType.GET_IDS_BY_TYPE;
319                }
320                if (method.getReturnValue().getType().endsWith("TypeInfo")) {
321                    return MethodType.GET_TYPE;
322                }
323                if (method.getReturnValue().getType().endsWith("TypeInfoList")) {
324                    return MethodType.GET_TYPES;
325                }
326                if (method.getName().endsWith("ByType")) {
327                    return MethodType.GET_IDS_BY_TYPE;
328                }
329                String splitName = splitCamelCase(method.getName());
330                if (splitName.contains("_By_")) {
331                    if (method.getReturnValue().getType().equals("StringList")) {
332                        return MethodType.GET_IDS_BY_OTHER;
333                    }
334                    if (method.getReturnValue().getType().endsWith("InfoList")) {
335                        return MethodType.GET_INFOS_BY_OTHER;
336                    }
337                    return MethodType.UNKNOWN;
338                }
339                if (splitName.contains("_For_")) {
340                    if (method.getReturnValue().getType().equals("StringList")) {
341                        return MethodType.GET_IDS_BY_OTHER;
342                    }
343                    if (method.getReturnValue().getType().endsWith("InfoList")) {
344                        return MethodType.GET_INFOS_BY_OTHER;
345                    }
346                    return MethodType.UNKNOWN;
347                }
348                if (method.getParameters().size() >= 1 && method.getParameters().size() <= 2) {
349                    if (!method.getReturnValue().getType().endsWith("List")) {
350                        if (method.getParameters().get(0).getName().endsWith("Id")) {
351                            return MethodType.GET_BY_ID;
352                        }
353                        if (method.getParameters().get(0).getName().endsWith("Key")) {
354                            return MethodType.GET_BY_ID;
355                        }
356                    }
357                }
358            }
359            if (method.getName().startsWith("searchFor")) {
360                if (method.getName().endsWith("Ids")) {
361                    return MethodType.SEARCH_FOR_IDS;
362                }
363                if (method.getName().endsWith("Keys")) {
364                    return MethodType.SEARCH_FOR_IDS;
365                }
366                return MethodType.SEARCH_FOR_INFOS;
367            }
368    
369            return MethodType.UNKNOWN;
370        }
371    
372        private String calcTableName(XmlType xmlType) {
373            String tableName = xmlType.getTableName();
374            if (tableName != null) {
375                return tableName;
376            }
377            return calcTableName(xmlType.getName());
378        }
379    
380        private String calcTableName(String name) {
381            if (name.endsWith("Info")) {
382                name = name.substring(0, name.length() - "Info".length());
383            }
384            String tableName = "KSEN_" + splitCamelCase(name).toUpperCase();
385            return tableName;
386        }
387    
388        // got this from
389        // http://stackoverflow.com/questions/2559759/how-do-i-convert-camelcase-into-human-readable-names-in-java
390        private static String splitCamelCase(String s) {
391            if (s == null) {
392                return null;
393            }
394            return s.replaceAll(String.format("%s|%s|%s",
395                    "(?<=[A-Z])(?=[A-Z][a-z])", "(?<=[^A-Z])(?=[A-Z])",
396                    "(?<=[A-Za-z])(?=[^A-Za-z])"), "_");
397        }
398    
399        private String calcColumnName(MessageStructure ms) {
400            if (ms.getShortName().equalsIgnoreCase("TypeKey")) {
401                String prefix = calcTableName(xmlType);
402                if (prefix.startsWith("KSEN_")) {
403                    prefix = prefix.substring("KSEN_".length());
404                }
405                return prefix + "_TYPE";
406            }
407            if (ms.getShortName().equalsIgnoreCase("StateKey")) {
408                String prefix = calcTableName(xmlType);
409                if (prefix.startsWith("KSEN_")) {
410                    prefix = prefix.substring("KSEN_".length());
411                }
412                return prefix + "_STATE";
413            }
414            String name = splitCamelCase(ms.getShortName());
415            // TODO: apply known abbreviations see https://wiki.kuali.org/display/STUDENT/Database+Abbreviations+for+R2
416            return name.toUpperCase();
417        }
418    
419        private boolean isNullable(MessageStructure ms) {
420            if (ms.getRequired() == null) {
421                return true;
422            }
423            if (ms.getRequired().equalsIgnoreCase("Required")) {
424                return false;
425            }
426            // figure out what to do if it is qualified like
427            // "required on update"
428            return true;
429        }
430    
431        private String keyOrId() {
432    
433            if (finder.findMessageStructure(xmlType.getName(), "id") != null) {
434                return "Id";
435            }
436            if (finder.findMessageStructure(xmlType.getName(), "key") != null) {
437                return "Key";
438            }
439            return "Id";
440        }
441    
442        private boolean hasAttributes() {
443            for (MessageStructure ms : finder.findMessageStructures(xmlType.getName())) {
444                if (ms.getShortName().equals("attributes")) {
445                    return true;
446                }
447            }
448            return false;
449        }
450    
451        private boolean isMethodForThisType(XmlType xmlType, ServiceMethod method) {
452            String objectName = calcObjectName(method);
453            if (objectName == null) {
454                return false;
455            }
456            if (xmlType.getName().equalsIgnoreCase(objectName + "Info")) {
457                return true;
458            }
459            return false;
460        }
461    
462        /**
463         * Write out the entire file
464         */
465        public void write() {
466            String className = calcClassName(servKey, xmlType);
467            String entityName = JpaEntityWriter.calcClassName(servKey, xmlType);
468            indentPrint("public class " + className);
469            println(" extends GenericEntityDao<" + entityName + "> {");
470            incrementIndent();
471            importsAdd("org.kuali.student.r2.common.dao.GenericEntityDao");
472            for (ServiceMethod method : methods) {
473                if (!isMethodForThisType(xmlType, method)) {
474                    continue;
475                }
476                MethodType methodType = calcMethodType(method);
477                String returnClass = null;
478                switch (methodType) {
479                    case GET_IDS_BY_TYPE:
480                        importsAdd(List.class.getName());
481                        println("");
482                        indentPrintln("public List<String> getIdsByType(String type) {");
483                        incrementIndent();
484                        importsAdd(Query.class.getName());
485                        indentPrintln("Query query = em.createNamedQuery(\"" + entityName + ".getIdsByType\");");
486                        indentPrintln("query.setParameter(\"type\", type);");
487                        indentPrintln("return query.getResultList();");
488                        decrementIndent();
489                        indentPrintln("}");
490                        break;
491                    case GET_IDS_BY_OTHER:
492                    case GET_INFOS_BY_OTHER:
493                        importsAdd(List.class.getName());
494                        importsAdd(Query.class.getName());
495                        if (methodType.equals(MethodType.GET_IDS_BY_OTHER)) {
496                            returnClass = "String";
497                        } else {
498                            returnClass = entityName;
499                        }
500                        println("");
501                        String namedQuery = calcNamedQuery(method);
502                        indentPrint("public List<" + returnClass + "> " + namedQuery + "(");
503                        String comma = "";
504                        for (ServiceMethodParameter param : method.getParameters()) {
505                            if (param.getType().equals("ContextInfo")) {
506                                continue;
507                            }
508                            print(comma);
509                            comma = ", ";
510                            String paramName = initLower(param.getName());
511                            String javaClass = this.calcType(param.getType(), this.stripList(param.getType()));
512                            print(javaClass + " " + paramName);
513                        }
514                        println(") {");
515                        incrementIndent();
516                        indentPrintln("Query query = em.createNamedQuery(\"" + entityName + "." + namedQuery + "\");");
517                        for (ServiceMethodParameter param : method.getParameters()) {
518                            if (param.getType().equals("ContextInfo")) {
519                                continue;
520                            }
521                            String paramName = initLower(param.getName());
522                            indentPrintln("query.setParameter(\"" + paramName + "\", " + paramName + ");");
523                        }
524                        indentPrintln("return query.getResultList();");
525                        decrementIndent();
526                        indentPrintln("}");
527                        break;
528                }
529            }
530    
531            println("");
532            closeBrace();
533    
534            this.writeJavaClassAndImportsOutToFile();
535            this.getOut().close();
536        }
537    
538        private String getInvalidParameterException() {
539            if (this.isRice()) {
540                return "RiceIllegalArgumentException";
541            }
542            return "InvalidParameterException";
543        }
544    
545        private String getOperationFailedException() {
546            if (this.isRice()) {
547                return "RiceIllegalArgumentException";
548            }
549            return "OperationFailedException";
550        }
551    
552        private String getDoesNotExistException() {
553            if (this.isRice()) {
554                return "RiceIllegalArgumentException";
555            }
556            return "DoesNotExistException";
557        }
558    
559        private String getVersionMismatchException() {
560            if (this.isRice()) {
561                return "RiceIllegalStateException";
562            }
563            return "VersionMismatchException";
564        }
565    
566        private void writeThrowsNotImplemented(ServiceMethod method) {
567            indentPrintln("throw new " + this.getOperationFailedException() + " (\"" + method.getName() + " has not been implemented\");");
568        }
569    
570        protected String initLower(String str) {
571            return str.substring(0, 1).toLowerCase() + str.substring(1);
572        }
573    
574        private String calcCriteriaLookupServiceVariableName(ServiceMethod method) {
575            String objectName = calcObjectName(method);
576            String variableName = initLower(objectName) + "CriteriaLookupService";
577            return variableName;
578        }
579    
580        private ServiceMethodParameter findIdParameter(ServiceMethod method) {
581            String idFieldName = calcObjectName(method) + "Id";
582            for (ServiceMethodParameter parameter : method.getParameters()) {
583                if (parameter.getType().equals("String")) {
584                    if (parameter.getName().equals(idFieldName)) {
585                        return parameter;
586                    }
587                }
588            }
589    
590            // if only one parameter and it is a string then grab that
591            if (method.getParameters().size() == 1) {
592                for (ServiceMethodParameter parameter : method.getParameters()) {
593                    if (parameter.getType().equals("String")) {
594                        return parameter;
595                    }
596                }
597            }
598            // can't find name exactly 
599            for (ServiceMethodParameter parameter : method.getParameters()) {
600                if (parameter.getType().equals("String")) {
601                    if (parameter.getName().endsWith("Id")) {
602                        return parameter;
603                    }
604                }
605            }
606            // can't find name exactly try key 
607            for (ServiceMethodParameter parameter : method.getParameters()) {
608                if (parameter.getType().equals("String")) {
609                    if (!parameter.getName().endsWith("TypeKey")) {
610                        if (parameter.getName().endsWith("Key")) {
611                            return parameter;
612                        }
613                    }
614                }
615            }
616            log.warn("Could not find the Id paramter for " + method.getService() + "." + method.getName() + " so returning the first one");
617            return method.getParameters().get(0);
618        }
619    
620        private ServiceMethodParameter findContextParameter(ServiceMethod method) {
621            for (ServiceMethodParameter parameter : method.getParameters()) {
622                if (parameter.getType().equals("ContextInfo")) {
623                    return parameter;
624                }
625            }
626            return null;
627        }
628    
629        private ServiceMethodParameter findInfoParameter(ServiceMethod method) {
630            String objectName = calcObjectName(method);
631            if (!this.isRice()) {
632                objectName = objectName + "Info";
633            }
634            for (ServiceMethodParameter parameter : method.getParameters()) {
635                if (parameter.getType().equals(objectName)) {
636                    return parameter;
637                }
638            }
639            if (method.getParameters().size() >= 1) {
640                return method.getParameters().get(0);
641            }
642            return null;
643        }
644    
645        private ServiceMethodParameter findTypeParameter(ServiceMethod method) {
646            for (ServiceMethodParameter parameter : method.getParameters()) {
647                if (parameter.getType().equals("String")) {
648                    if (parameter.getName().endsWith("TypeKey")) {
649                        return parameter;
650                    }
651                    if (parameter.getName().endsWith("Type")) {
652                        return parameter;
653                    }
654                }
655            }
656            return null;
657        }
658    
659        private String calcDaoVariableName(ServiceMethod method) {
660            String daoVariableName = this.calcObjectName(method);
661            daoVariableName = this.initLower(daoVariableName) + "Dao";
662            return daoVariableName;
663        }
664    
665        private String calcEntityClassName(ServiceMethod method) {
666            String objectName = this.calcObjectName(method);
667            objectName = objectName + "Entity";
668            return objectName;
669        }
670    
671        protected String calcObjectName(ServiceMethod method) {
672            if (method.getName().startsWith("create")) {
673                return method.getName().substring("create".length());
674            }
675            if (method.getName().startsWith("update")) {
676                return method.getName().substring("update".length());
677            }
678            if (method.getName().startsWith("validate")) {
679                return method.getName().substring("validate".length());
680            }
681            if (method.getName().startsWith("delete")) {
682                return method.getName().substring("delete".length());
683            }
684            if (method.getName().startsWith("get")) {
685                if (method.getReturnValue().getType().equals("StringList")) {
686                    if (method.getName().contains("IdsBy")) {
687                        return method.getName().substring("get".length(),
688                                method.getName().indexOf("IdsBy"));
689                    }
690                    if (method.getName().contains("KeysBy")) {
691                        return method.getName().substring("get".length(),
692                                method.getName().indexOf("KeysBy"));
693                    }
694                    if (method.getName().contains("IdsFor")) {
695                        return method.getName().substring("get".length(),
696                                method.getName().indexOf("IdsFor"));
697                    }
698                    if (method.getName().contains("With")) {
699                        return method.getName().substring("get".length(),
700                                method.getName().indexOf("With"));
701                    }
702                    if (method.getName().contains("By")) {
703                        return method.getName().substring("get".length(),
704                                method.getName().indexOf("By"));
705                    }
706                    return method.getName().substring("get".length());
707                }
708                String name = method.getReturnValue().getType();
709                if (name.endsWith("List")) {
710                    name = name.substring(0, name.length() - "List".length());
711                }
712                if (name.endsWith("Info")) {
713                    name = name.substring(0, name.length() - "Info".length());
714                }
715                return name;
716            }
717    
718            if (method.getName().startsWith("searchFor")) {
719                if (method.getReturnValue().getType().equals("StringList")) {
720                    if (method.getName().endsWith("Ids")) {
721                        return method.getName().substring("searchFor".length(),
722                                method.getName().indexOf("Ids"));
723                    }
724                    if (method.getName().endsWith("Keys")) {
725                        return method.getName().substring("get".length(),
726                                method.getName().indexOf("Keys"));
727                    }
728                    return method.getName().substring("searchFor".length());
729                }
730                String name = method.getReturnValue().getType();
731                if (name.endsWith("List")) {
732                    name = name.substring(0, name.length() - "List".length());
733                }
734                if (name.endsWith("Info")) {
735                    name = name.substring(0, name.length() - "Info".length());
736                }
737                return name;
738            }
739            if (method.getName().startsWith("add")) {
740                return method.getName().substring("add".length());
741            }
742            if (method.getName().startsWith("remove")) {
743                return method.getName().substring("remove".length());
744            }
745            String returnType = this.stripList(method.getReturnValue().getType());
746            XmlType type = this.finder.findXmlType(returnType);
747            if (type.getPrimitive().equals(XmlType.COMPLEX)) {
748                return returnType;
749            }
750            return null;
751        }
752    
753        private String calcNamedQuery(ServiceMethod method) {
754            String objectName = calcObjectName(method);
755            String name = method.getName();
756            if (name.startsWith("get")) {
757                name = name.substring("get".length());
758            }
759            if (name.startsWith(objectName)) {
760                name = name.substring(objectName.length());
761            }
762    //        if (name.startsWith("Ids")) {
763    //            name = name.substring("Ids".length());
764    //        }
765    //        if (name.isEmpty()) {
766    //            throw new RuntimeException (method.getName());
767    //        }
768            // remove plural
769            if (!method.getReturnValue().getType().equals("StringList")) {
770                if (name.startsWith("s")) {
771                    name = name.substring("s".length());
772                }
773            }
774            // add back the get
775            name = "get" + name;
776            return name;
777        }
778    
779        private ServiceMethodParameter findCriteriaParam(ServiceMethod method) {
780            for (ServiceMethodParameter param : method.getParameters()) {
781                if (param.getType().equals("QueryByCriteria")) {
782                    return param;
783                }
784            }
785            return null;
786        }
787    
788        private ServiceMethodParameter getTypeParameter(ServiceMethod method) {
789            ServiceMethodParameter fallbackParam = null;
790            for (ServiceMethodParameter parameter : method.getParameters()) {
791                if (parameter.getName().endsWith("TypeKey")) {
792                    return parameter;
793                }
794                if (parameter.getType().equals("String")) {
795                    if (parameter.getName().toLowerCase().contains("type")) {
796                        fallbackParam = parameter;
797                    }
798                }
799            }
800            return fallbackParam;
801        }
802    
803        private String calcInfoName(ServiceMethod method) {
804            String objectName = this.calcObjectName(method);
805            String infoName = objectName;
806            if (!this.isRice()) {
807                infoName = infoName + "Info";
808            }
809            return infoName;
810        }
811    
812        private String initUpper(String str) {
813            return str.substring(0, 1).toUpperCase() + str.substring(1);
814        }
815    
816        private ServiceMethodParameter findIdListParameter(ServiceMethod method) {
817            String idFieldName = calcObjectName(method) + "Ids";
818            if (this.isRice()) {
819                idFieldName = "ids";
820            }
821            for (ServiceMethodParameter parameter : method.getParameters()) {
822                if (parameter.getType().equals("StringList")) {
823                    if (parameter.getName().equals(idFieldName)) {
824                        return parameter;
825                    }
826                }
827            }
828            // can't find name exactly 
829            for (ServiceMethodParameter parameter : method.getParameters()) {
830                if (parameter.getType().equals("StringList")) {
831                    if (parameter.getName().endsWith("Ids")) {
832                        return parameter;
833                    }
834                }
835            }
836            // can't find name exactly try key 
837            for (ServiceMethodParameter parameter : method.getParameters()) {
838                if (parameter.getType().equals("StringList")) {
839                    if (parameter.getName().endsWith("Keys")) {
840                        return parameter;
841                    }
842                }
843            }
844            return null;
845        }
846    
847        private String stripList(String str) {
848            return GetterSetterNameCalculator.stripList(str);
849        }
850    
851        private String calcExceptionClassName(ServiceMethodError error) {
852            if (error.getClassName() == null) {
853                return ServiceExceptionWriter.calcClassName(error.getType());
854            }
855            return error.getClassName();
856        }
857    
858        private String calcExceptionPackageName(ServiceMethodError error) {
859            if (error.getClassName() == null) {
860                return ServiceExceptionWriter.calcPackage(rootPackage);
861            }
862            return error.getPackageName();
863        }
864    
865        private String calcType(String type, String realType) {
866            XmlType t = finder.findXmlType(this.stripList(type));
867            String retType = MessageStructureTypeCalculator.calculate(this, model, type, realType,
868                    t.getJavaPackage());
869            if (this.isRice()) {
870                if (retType.equals("Boolean")) {
871                    retType = "boolean";
872                }
873                if (retType.equals("Void")) {
874                    retType = "void";
875                }
876            }
877            return retType;
878        }
879    }