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 */
016package org.kuali.student.jpa.mojo;
017
018import org.kuali.student.contract.model.ServiceContractModel;
019import org.kuali.student.contract.model.ServiceMethod;
020import org.kuali.student.contract.model.ServiceMethodError;
021import org.kuali.student.contract.model.ServiceMethodParameter;
022import org.kuali.student.contract.model.XmlType;
023import org.kuali.student.contract.model.util.ModelFinder;
024import org.kuali.student.contract.writer.JavaClassWriter;
025import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
026import org.kuali.student.contract.writer.service.MessageStructureTypeCalculator;
027import org.kuali.student.contract.writer.service.ServiceExceptionWriter;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031import java.util.*;
032import javax.persistence.Query;
033import org.kuali.student.contract.model.MessageStructure;
034
035/**
036 *
037 * @author nwright
038 */
039public class JpaDaoWriter extends JavaClassWriter {
040
041    private static final 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 {}.{} so returning the first one", method.getService(), method.getName());
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}