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.CascadeType;
033import javax.persistence.Column;
034import javax.persistence.Entity;
035import javax.persistence.FetchType;
036import javax.persistence.NamedQueries;
037import javax.persistence.NamedQuery;
038import javax.persistence.OneToMany;
039import javax.persistence.Table;
040import javax.persistence.Temporal;
041import javax.persistence.TemporalType;
042import org.apache.commons.lang.StringUtils;
043import org.kuali.student.contract.model.MessageStructure;
044
045/**
046 *
047 * @author nwright
048 */
049public class JpaEntityWriter extends JavaClassWriter {
050
051    private static final Logger log = LoggerFactory.getLogger(JpaEntityWriter.class);
052
053    //////////////////////////////
054    // Constants
055    //////////////////////////////
056    /**
057     * The standard type of methods used in our Service contract.
058     */
059    protected static enum MethodType {
060
061        VALIDATE,
062        CREATE,
063        CREATE_BULK,
064        ADD,
065        UPDATE,
066        UPDATE_OTHER,
067        DELETE,
068        REMOVE,
069        DELETE_OTHER,
070        GET_CREATE,
071        GET_BY_ID,
072        GET_BY_IDS,
073        RICE_GET_BY_NAMESPACE_AND_NAME,
074        GET_IDS_BY_TYPE,
075        GET_IDS_BY_OTHER,
076        GET_INFOS_BY_OTHER,
077        GET_TYPE,
078        GET_TYPES,
079        SEARCH_FOR_IDS,
080        SEARCH_FOR_INFOS,
081        UNKNOWN
082    };
083
084    //////////////////////////////
085    // Data Variables
086    //////////////////////////////
087    protected ServiceContractModel model;
088    protected ModelFinder finder;
089    private String directory;
090    private XmlType xmlType;
091    /**
092     * The package name is stored in the service object itself (the package spec
093     * kept moving around so I assumed the actual service name was unique but
094     * ran into a problem when we included rice because they have a StateService
095     * meaning US states and we have a StateService meaning the state of the
096     * object so I added logic to detect rice and prepend that "RICE." to it
097     */
098    private String rootPackage;
099
100    /**
101     * Name of the service being operated on. If it is a RICE service it is
102     * prefixed with RICE. [11:32:18 AM] Norman Wright: short name... I think it
103     * gets it by taking the java class SimpleName and stripping off the word
104     * "Service" and I think making it lower case. [11:32:24 AM] Norman Wright:
105     * so OrganizationService becomes organization
106     */
107    protected String servKey;
108    /**
109     * A flag that holds if the service is an R1 service.
110     */
111    private boolean isR1;
112    private List<ServiceMethod> methods;
113
114    //////////////////////////
115    // Constructor
116    //////////////////////////
117    public JpaEntityWriter(ServiceContractModel model,
118            String directory,
119            String rootPackage,
120            String servKey,
121            List<ServiceMethod> methods,
122            XmlType xmlType,
123            boolean isR1) {
124        super(directory, calcPackage(servKey, rootPackage), calcClassName(servKey, xmlType));
125        this.model = model;
126        this.finder = new ModelFinder(model);
127        this.directory = directory;
128        this.rootPackage = rootPackage;
129        this.servKey = servKey;
130        this.methods = methods;
131        this.xmlType = xmlType;
132        this.isR1 = isR1;
133    }
134
135    public JpaEntityWriter(ServiceContractModel model,
136            String directory,
137            String rootPackage,
138            String servKey,
139            List<ServiceMethod> methods,
140            XmlType xmlType,
141            boolean isR1,
142            String packageName,
143            String className) {
144        super(directory, packageName, className);
145        this.model = model;
146        this.finder = new ModelFinder(model);
147        this.directory = directory;
148        this.rootPackage = rootPackage;
149        this.servKey = servKey;
150        this.methods = methods;
151        this.xmlType = xmlType;
152        this.isR1 = isR1;
153    }
154
155    /////////////////////////
156    // Functional Methods
157    /////////////////////////
158    /**
159     * Returns the jpa implementation package name.
160     *
161     * @param servKey
162     * @param rootPackage
163     * @return
164     */
165    public static String calcPackage(String servKey, String rootPackage) {
166        String pack = rootPackage + ".";
167//        String pack = rootPackage + "." + servKey.toLowerCase() + ".";
168//  StringBuffer buf = new StringBuffer (service.getVersion ().length ());
169//  for (int i = 0; i < service.getVersion ().length (); i ++)
170//  {
171//   char c = service.getVersion ().charAt (i);
172//   c = Character.toLowerCase (c);
173//   if (Character.isLetter (c))
174//   {
175//    buf.append (c);
176//    continue;
177//   }
178//   if (Character.isDigit (c))
179//   {
180//    buf.append (c);
181//   }
182//  }
183//  pack = pack + buf.toString ();
184        pack = pack + "service.impl.jpa." + servKey.toLowerCase();
185        return pack;
186    }
187
188    /**
189     * Checks if this is a RICE service.
190     *
191     * @return true if this is a RICE service.
192     */
193    private boolean isRice() {
194        if (this.servKey.startsWith("RICE.")) {
195            return true;
196        }
197        return false;
198    }
199
200    protected static String fixServKey(String servKey) {
201        if (servKey.startsWith("RICE.")) {
202            return servKey.substring("RICE.".length());
203        }
204        return servKey;
205    }
206
207    /**
208     * Given the service key (name), returns a calculated class name for the jpa
209     * impl.
210     */
211    public static String calcClassName(String servKey, XmlType xmlType) {
212        String name = calcInfcName(xmlType) + "Entity";
213        return name;
214    }
215
216    private String calcInfcName() {
217        String name = calcInfcName(xmlType);
218        String pkg = xmlType.getJavaPackage();
219        if (pkg.endsWith(".dto")) {
220            pkg = pkg.substring(0, pkg.length() - ".dto".length()) + ".infc";
221        }
222        importsAdd(pkg + "." + name);
223        return name;
224    }
225
226    private String calcInfoName() {
227        String name = initUpper(xmlType.getName());
228        String pkg = xmlType.getJavaPackage();
229        importsAdd(pkg + "." + name);
230        return name;
231    }
232
233    public static String calcInfcName(XmlType xmlType) {
234        String name = xmlType.getName();
235        if (name.endsWith("Info")) {
236            name = name.substring(0, name.length() - "Info".length());
237        }
238        name = GetterSetterNameCalculator.calcInitUpper(name);
239        return name;
240    }
241
242    /**
243     * Analyses the method and returns a MethodType enum that describes what
244     * type of method this is.
245     */
246    protected MethodType calcMethodType(ServiceMethod method) {
247        if (this.isRice()) {
248            if (method.getName().contains("ByNamespaceCodeAndName")) {
249                return MethodType.RICE_GET_BY_NAMESPACE_AND_NAME;
250            }
251            if (method.getName().contains("ByNameAndNamespace")) {
252                return MethodType.RICE_GET_BY_NAMESPACE_AND_NAME;
253            }
254            if (method.getName().startsWith("get")) {
255                if (method.getParameters().size() == 1) {
256                    if (!method.getReturnValue().getType().endsWith("List")) {
257                        if (method.getParameters().get(0).getName().equals("id")) {
258                            return MethodType.GET_BY_ID;
259                        }
260
261                    } else {
262                        if (method.getParameters().get(0).getName().equals("ids")) {
263                            return MethodType.GET_BY_IDS;
264                        }
265                    }
266                }
267            }
268        }
269        if (method.getName().startsWith("validate")) {
270            return MethodType.VALIDATE;
271        }
272        if (method.getName().startsWith("create")) {
273            if (this.findInfoParameter(method) != null) {
274                return MethodType.CREATE;
275            }
276            return MethodType.CREATE_BULK;
277        }
278        if (method.getName().startsWith("add")) {
279            return MethodType.ADD;
280        }
281        if (method.getName().startsWith("update")) {
282            if (this.findInfoParameter(method) != null) {
283                return MethodType.UPDATE;
284            }
285            return MethodType.UPDATE_OTHER;
286        }
287        if (method.getName().startsWith("delete")) {
288            if (method.getName().contains("By")) {
289                if (!method.getName().startsWith("deleteBy")) {
290                    return MethodType.DELETE_OTHER;
291                }
292            }
293            if (method.getName().contains("For")) {
294                if (!method.getName().startsWith("deleteFor")) {
295                    return MethodType.DELETE_OTHER;
296                }
297            }
298            return MethodType.DELETE;
299        }
300        if (method.getName().startsWith("remove")) {
301            return MethodType.REMOVE;
302        }
303
304        if (method.getName().startsWith("getCreate")) {
305            return MethodType.GET_CREATE;
306        }
307
308        if (method.getName().startsWith("get")) {
309            if (method.getName().endsWith("ByIds")) {
310                return MethodType.GET_BY_IDS;
311            }
312            if (method.getName().endsWith("ByKeys")) {
313                return MethodType.GET_BY_IDS;
314            }
315            if (method.getName().endsWith("ByType")) {
316                return MethodType.GET_IDS_BY_TYPE;
317            }
318            if (method.getReturnValue().getType().endsWith("TypeInfo")) {
319                return MethodType.GET_TYPE;
320            }
321            if (method.getReturnValue().getType().endsWith("TypeInfoList")) {
322                return MethodType.GET_TYPES;
323            }
324            if (method.getName().endsWith("ByType")) {
325                return MethodType.GET_IDS_BY_TYPE;
326            }
327            if (method.getParameters().size() >= 1 && method.getParameters().size() <= 2) {
328                if (!method.getReturnValue().getType().endsWith("List")) {
329//                    if (method.getParameters().get(0).getName().endsWith("Id")) {
330                    return MethodType.GET_BY_ID;
331//                    }
332                }
333            }
334            if (method.getName().contains("By")) {
335                if (method.getReturnValue().getType().equals("StringList")) {
336                    return MethodType.GET_IDS_BY_OTHER;
337                }
338                if (method.getReturnValue().getType().endsWith("InfoList")) {
339                    return MethodType.GET_INFOS_BY_OTHER;
340                }
341            }
342        }
343        if (method.getName().startsWith("searchFor")) {
344            if (method.getName().endsWith("Ids")) {
345                return MethodType.SEARCH_FOR_IDS;
346            }
347            if (method.getName().endsWith("Keys")) {
348                return MethodType.SEARCH_FOR_IDS;
349            }
350            return MethodType.SEARCH_FOR_INFOS;
351        }
352
353        return MethodType.UNKNOWN;
354    }
355
356    private String calcTableName(XmlType xmlType) {
357        String tableName = xmlType.getTableName();
358        if (tableName != null) {
359            return tableName;
360        }
361        return calcTableName(xmlType.getName());
362    }
363
364    private String calcTableName(String name) {
365        if (name.endsWith("Info")) {
366            name = name.substring(0, name.length() - "Info".length());
367        }
368        String tableName = "KSEN_" + splitCamelCase(name).toUpperCase();
369        tableName = applyAbbreviations(tableName.toUpperCase());
370        return tableName;
371    }
372
373    // got this from
374    // http://stackoverflow.com/questions/2559759/how-do-i-convert-camelcase-into-human-readable-names-in-java
375    private static String splitCamelCase(String s) {
376        if (s == null) {
377            return null;
378        }
379        return s.replaceAll(String.format("%s|%s|%s",
380                "(?<=[A-Z])(?=[A-Z][a-z])", "(?<=[^A-Z])(?=[A-Z])",
381                "(?<=[A-Za-z])(?=[^A-Za-z])"), "_");
382    }
383
384    private String calcColumnName(MessageStructure ms) {
385        String name = ms.getColumnName();
386        if (name != null) {
387            return name;
388        }
389        if (ms.getShortName().equalsIgnoreCase("TypeKey")) {
390            String prefix = calcTableName(xmlType);
391            if (prefix.startsWith("KSEN_")) {
392                prefix = prefix.substring("KSEN_".length());
393            }
394            // already applied
395//            prefix = applyAbbreviations (prefix.toUpperCase());
396            return prefix + "_TYPE";
397        }
398        if (ms.getShortName().equalsIgnoreCase("StateKey")) {
399            String prefix = calcTableName(xmlType);
400            if (prefix.startsWith("KSEN_")) {
401                prefix = prefix.substring("KSEN_".length());
402            }
403            // already applied
404//            prefix = applyAbbreviations (prefix.toUpperCase());
405            return prefix + "_STATE";
406        }
407        name = splitCamelCase(ms.getShortName());
408        name = applyAbbreviations(name.toUpperCase());
409        return name;
410    }
411
412    private static final Map<String, String> ABBREVIATIONS = new LinkedHashMap<String, String>();
413
414    // known abbreviations see https://wiki.kuali.org/display/STUDENT/Database+Abbreviations+for+R2
415    static {
416        ABBREVIATIONS.put("ABBREVIATION", "ABBREV");
417        ABBREVIATIONS.put("ACCOUNT", "ACCT");
418        ABBREVIATIONS.put("ACCOUNTING", "ACCT");
419        ABBREVIATIONS.put("ACCREDITING", "ACCRED");
420        ABBREVIATIONS.put("ACCREDIT", "ACCRED");
421        ABBREVIATIONS.put("ACCREDITATION", "ACCRED");
422        ABBREVIATIONS.put("ACTION", "ACTN");
423        ABBREVIATIONS.put("AFFILIATION", "AFFIL");
424        ABBREVIATIONS.put("AMOUNT", "AMT");
425        ABBREVIATIONS.put("APPLIED", "APPLD");
426        ABBREVIATIONS.put("APPLICATION", "APP");
427        ABBREVIATIONS.put("ALLOWABLE", "ALOW");
428        ABBREVIATIONS.put("ALTERNATE", "ALT");
429        ABBREVIATIONS.put("APPOINTMENT", "APPT");
430        ABBREVIATIONS.put("ASSOCIATED", "ASSO");
431        ABBREVIATIONS.put("ATTRIBUTE", "ATTR");
432        ABBREVIATIONS.put("CAMPUS", "CAMP");
433        ABBREVIATIONS.put("CANONICAL LEARNING UNIT", "CLU");
434        ABBREVIATIONS.put("CATEGORY", "CAT");
435        ABBREVIATIONS.put("CODE", "CD");
436        ABBREVIATIONS.put("COMPLETE", "COMP");
437        ABBREVIATIONS.put("COMPONENT", "CMP");
438        ABBREVIATIONS.put("CONTEXT", "CTX");
439        ABBREVIATIONS.put("CONTINUE", "CONT");
440        ABBREVIATIONS.put("COUNT", "CNT");
441        ABBREVIATIONS.put("CREDIT", "CR");
442        ABBREVIATIONS.put("CRITERIA", "CRIT");
443        ABBREVIATIONS.put("DATE", "DT");
444        ABBREVIATIONS.put("DEADLINE", "DEDLN");
445        ABBREVIATIONS.put("DEFAULT", "DEF");
446        ABBREVIATIONS.put("DEFINABLE", "DEFNBL");
447        ABBREVIATIONS.put("DEFINITION", "DEFN");
448        ABBREVIATIONS.put("DESCRIPTION", "DESCR");
449        ABBREVIATIONS.put("DISABLED", "DISBLD");
450        ABBREVIATIONS.put("DATE OVERRIDE", "DO");
451        ABBREVIATIONS.put("DOCUMENT", "DOC");
452        ABBREVIATIONS.put("DURATION", "DUR");
453        ABBREVIATIONS.put("DIVISION", "DIV");
454        ABBREVIATIONS.put("EFFECTIVE", "EFF");
455        ABBREVIATIONS.put("ENROLL", "ENRL");
456        ABBREVIATIONS.put("ENROLLED", "ENRL");
457        ABBREVIATIONS.put("ENROLLMENT", "ENRL");
458        ABBREVIATIONS.put("ENROLLABLE", "ENRL");
459        ABBREVIATIONS.put("ESTIMATE", "EST");
460        ABBREVIATIONS.put("EXEMPTION", "EXEMPT");
461        ABBREVIATIONS.put("EXPENDITURE", "EXPEND");
462        ABBREVIATIONS.put("EXPIRATION", "EXP");
463        ABBREVIATIONS.put("EXPIRED", "EXP");
464        ABBREVIATIONS.put("FLEXIBLE", "FLEX");
465        ABBREVIATIONS.put("FREQUENCY", "FREQ");
466        ABBREVIATIONS.put("GROUP", "GRP");
467        ABBREVIATIONS.put("HAZARD", "HAZR");
468        ABBREVIATIONS.put("HAZARDOUS", "HAZR");
469        ABBREVIATIONS.put("HEADER", "HDR");
470        ABBREVIATIONS.put("HIERARCHY", "HIRCHY");
471        ABBREVIATIONS.put("IDENTIFIER", "ID");
472        ABBREVIATIONS.put("INACTIVE", "INACV");
473        ABBREVIATIONS.put("INDEPENDENT", "INDPT");
474        ABBREVIATIONS.put("INDICATOR", "IND");
475        ABBREVIATIONS.put("INSTRUCTOR", "INSTR");
476        ABBREVIATIONS.put("INSTRUCTION", "INSTRN");
477        ABBREVIATIONS.put("INSTRUCTIONAL", "INSTRN");
478        ABBREVIATIONS.put("INTENSITY", "INTSTY");
479        ABBREVIATIONS.put("INTERVAL", "INTVL");
480        ABBREVIATIONS.put("LEARNING OBJECTIVE", "LO");
481        ABBREVIATIONS.put("LEARNING RESULT", "LR");
482        ABBREVIATIONS.put("LEARNING UNIT", "LU");
483        ABBREVIATIONS.put("LOCATION", "LOC");
484        ABBREVIATIONS.put("LONG", "LNG");
485        ABBREVIATIONS.put("LEVEL", "LVL");
486        ABBREVIATIONS.put("LIFECYCLE", "LIFCYC");
487        ABBREVIATIONS.put("MARKETING", "MKTG");
488        ABBREVIATIONS.put("MAXIMUM", "MAX");
489        ABBREVIATIONS.put("MEMBER", "MBR");
490        ABBREVIATIONS.put("MESSAGE", "MSG");
491        ABBREVIATIONS.put("MILESTONE", "MSTONE");
492        ABBREVIATIONS.put("MILLISECONDS", "MS");
493        ABBREVIATIONS.put("MINIMUM", "MIN");
494        ABBREVIATIONS.put("NAMESPACE", "NMSPC");
495        ABBREVIATIONS.put("NEXT", "NEXT");
496        ABBREVIATIONS.put("NUMBER", "NUM");
497        ABBREVIATIONS.put("OFFICIAL", "OFFIC");
498        ABBREVIATIONS.put("ORG ORG RELATION TYPE", "OORT");
499        ABBREVIATIONS.put("OPTION", "OPT");
500        ABBREVIATIONS.put("ORGANIZATION", "ORG");
501        ABBREVIATIONS.put("PARAMETER", "PARM");
502        ABBREVIATIONS.put("PARAMETER VALUE", "PV");
503        ABBREVIATIONS.put("PERCENT", "PERCT");
504        ABBREVIATIONS.put("PERIOD", "PRD");
505        ABBREVIATIONS.put("PERSON", "PERS");
506        ABBREVIATIONS.put("POSITION", "POS");
507        ABBREVIATIONS.put("PRIMARY", "PRI");
508        ABBREVIATIONS.put("PROCESS", "PROC");
509        ABBREVIATIONS.put("PUBLISH", "PUBL");
510        ABBREVIATIONS.put("PUBLISHING", "PUBL");
511        ABBREVIATIONS.put("QUANTITY", "QTY");
512        ABBREVIATIONS.put("RECOGNITIION", "RECOG");
513        ABBREVIATIONS.put("RECORD", "REC");
514        ABBREVIATIONS.put("REFERENCE", "REF");
515        ABBREVIATIONS.put("REGISTRATION", "REG");
516        ABBREVIATIONS.put("RELATION", "RELN");
517        ABBREVIATIONS.put("RELATIONSHIP", "RELN");
518        ABBREVIATIONS.put("ROLLOVER RESULT", "ROR");
519        ABBREVIATIONS.put("REQUEST", "RQST");
520        ABBREVIATIONS.put("REQUIRED", "REQ");
521        ABBREVIATIONS.put("REPONSIBLE", "RESP");
522        ABBREVIATIONS.put("RESOURCE", "RSRC");
523        ABBREVIATIONS.put("RESTRICTION", "RESTR");
524        ABBREVIATIONS.put("RESULT VALUE GROUP", "RVG");
525        ABBREVIATIONS.put("REVIEW", "REVIEW");
526        ABBREVIATIONS.put("RICH TEXT", "RT");
527        ABBREVIATIONS.put("SCHEDULE", "SCHED");
528        ABBREVIATIONS.put("SHORT", "SHRT");
529        ABBREVIATIONS.put("SLOT RULE", "SR");
530        ABBREVIATIONS.put("STANDARD", "STD");
531        ABBREVIATIONS.put("STATE", "ST");
532        ABBREVIATIONS.put("STATEMENT", "STMNT");
533        ABBREVIATIONS.put("STUDENT", "STU");
534        ABBREVIATIONS.put("STUDY", "STDY");
535        ABBREVIATIONS.put("SUBJECT", "SUBJ");
536        ABBREVIATIONS.put("SUFFIX", "SUFX");
537        ABBREVIATIONS.put("TEMPLATE", "TMPLT");
538        ABBREVIATIONS.put("TIME", "TM");
539        ABBREVIATIONS.put("TITLE", "TTL");
540        ABBREVIATIONS.put("TOTAL", "TOT");
541        ABBREVIATIONS.put("TRANSACTION", "TRANS");
542        ABBREVIATIONS.put("TYPE", "TYP");
543        ABBREVIATIONS.put("VARIATION", "VARTN");
544        ABBREVIATIONS.put("VERSION NUMBER", "VER_NBR");
545        ABBREVIATIONS.put("", "");
546    }
547
548    private String applyAbbreviations(String name) {
549        // THIS DOESN'T HANDLE THE MULTI-WORD ABBREVIATIONS!
550        // good enough for now
551        String[] parts = name.split("_");
552        for (int i = 0; i < parts.length; i++) {
553            String abbrev = ABBREVIATIONS.get(parts[i]);
554            if (abbrev != null) {
555                System.out.println("Replacing abbreviation " + parts[i] + " with " + abbrev);
556                parts[i] = abbrev;
557            }
558        }
559        name = StringUtils.join(parts, '_');
560        return name;
561    }
562
563    private boolean isNullable(MessageStructure ms) {
564        if (ms.getRequired() == null) {
565            return true;
566        }
567        if (ms.getRequired().equalsIgnoreCase("Required")) {
568            return false;
569        }
570        // figure out what to do if it is qualified like
571        // "required on update"
572        return true;
573    }
574
575    private String keyOrId() {
576
577        if (finder.findMessageStructure(xmlType.getName(), "id") != null) {
578            return "Id";
579        }
580        if (finder.findMessageStructure(xmlType.getName(), "key") != null) {
581            return "Key";
582        }
583        return "Id";
584    }
585
586    private boolean hasAttributes() {
587        for (MessageStructure ms : finder.findMessageStructures(xmlType.getName())) {
588            if (ms.getShortName().equals("attributes")) {
589                return true;
590            }
591        }
592        return false;
593    }
594
595    private boolean isMethodForThisType(XmlType xmlType, ServiceMethod method) {
596        String objectName = calcObjectName(method);
597        if (objectName == null) {
598            return false;
599        }
600        if (xmlType.getName().equalsIgnoreCase(objectName + "Info")) {
601            return true;
602        }
603        return false;
604    }
605
606    /**
607     * Write out the entire file
608     */
609    public void write() {
610        indentPrintln("@Entity");
611        importsAdd(Entity.class.getName());
612        String tableName = calcTableName(xmlType);
613        if (xmlType.getTableName() == null) {
614            indentPrintln("//TODO: JPAIMPL double check table name because it was calculated based on heuristics and may not match the actual table name");
615        }
616        indentPrintln("@Table(name = \"" + tableName + "\")");
617        importsAdd(Table.class.getName());
618//@NamedQueries({
619//    @NamedQuery(name = "HoldIssueEntity.getIdsByType",
620//    query = "select id from HoldIssueEntity where holdIssueType = :type"),
621//    @NamedQuery(name = "HoldIssueEntity.getByOrganization",
622//    query = "select ISSUE from HoldIssueEntity ISSUE where ISSUE.organizationId = :organizationId")
623//})
624        String className = calcClassName(servKey, xmlType);
625        String attributeEntity = JpaAttributeEntityWriter.calcClassName(servKey, xmlType);
626        indentPrintln("@NamedQueries({");
627        String comma = "";
628        for (ServiceMethod method : methods) {
629            if (!isMethodForThisType(xmlType, method)) {
630                continue;
631            }
632            MethodType methodType = calcMethodType(method);
633            switch (methodType) {
634                case GET_IDS_BY_TYPE:
635                    indentPrint(comma);
636                    comma = ",";
637                    indentPrintln("@NamedQuery(name = \"" + className + ".getIdsByType\",");
638                    indentPrintln("    query = \"select id from " + className + " where typeKey = :type\")");
639                    break;
640                case GET_IDS_BY_OTHER:
641                    indentPrintln("//TODO: JPAIMPL double check this JPQL queries to make sure they match the intention of the service method");
642                    indentPrint(comma);
643                    comma = ",";
644                    String namedQuery = calcNamedQuery(method);
645                    indentPrintln("@NamedQuery(name = \"" + className + "." + namedQuery + "\",");
646
647                    indentPrint("    query = \"select id from " + className);
648                    String and = " where ";
649                    for (ServiceMethodParameter param : method.getParameters()) {
650                        if (param.getType().equals("ContextInfo")) {
651                            continue;
652                        }
653                        print(and);
654                        and = " and ";
655                        String paramName = initLower(param.getName());
656                        print(paramName + " = :" + paramName);
657                    }
658                    println("\")");
659                    break;
660                case GET_INFOS_BY_OTHER:
661                    indentPrintln("//TODO: JPAIMPL double check this JPQL queries to make sure they match the intention of the service method");
662                    indentPrint(comma);
663                    comma = ",";
664                    namedQuery = calcNamedQuery(method);
665                    indentPrintln("@NamedQuery(name = \"" + className + "." + namedQuery + "\",");
666
667                    indentPrint("    query = \"select " + className + " from " + className);
668                    and = " where ";
669                    for (ServiceMethodParameter param : method.getParameters()) {
670                        if (param.getType().equals("ContextInfo")) {
671                            continue;
672                        }
673                        print(and);
674                        and = " and ";
675                        String paramName = initLower(param.getName());
676                        print(paramName + " = :" + paramName);
677                    }
678                    println("\")");
679                    break;
680            }
681        }
682        indentPrintln("})");
683        importsAdd(NamedQueries.class.getName());
684        importsAdd(NamedQuery.class.getName());
685        indentPrint("public class " + calcClassName(servKey, xmlType));
686        println(" extends MetaEntity");
687        importsAdd("org.kuali.student.r2.common.entity.MetaEntity");
688
689        boolean hasAttributes = hasAttributes();
690        String attributeEntityName = JpaAttributeEntityWriter.calcClassName(servKey, xmlType);
691        if (hasAttributes) {
692            indentPrintln("    implements AttributeOwner<" + attributeEntityName + ">");
693            importsAdd("org.kuali.student.r2.common.entity.AttributeOwner");
694        }
695        openBrace();
696
697        for (MessageStructure ms : finder.findMessageStructures(xmlType.getName())) {
698            if (ms.getShortName().equalsIgnoreCase("id")) {
699                continue;
700            }
701            if (ms.getShortName().equalsIgnoreCase("meta")) {
702                continue;
703            }
704            if (ms.getShortName().equalsIgnoreCase("key")) {
705                continue;
706            }
707            if (ms.getShortName().equalsIgnoreCase("descr")) {
708                importsAdd(Column.class.getName());
709                importsAdd("org.kuali.student.r1.common.entity.KSEntityConstants");
710                indentPrintln("@Column(name = \"DESCR_PLAIN\", length = KSEntityConstants.EXTRA_LONG_TEXT_LENGTH, nullable = false)");
711                indentPrintln("private String descrPlain;");
712                indentPrintln("@Column(name = \"DESCR_FORMATTED\", length = KSEntityConstants.EXTRA_LONG_TEXT_LENGTH)");
713                indentPrintln("private String descrFormatted;");
714                continue;
715            }
716            if (ms.getShortName().equalsIgnoreCase("attributes")) {
717                importsAdd(OneToMany.class.getName());
718                importsAdd(CascadeType.class.getName());
719                importsAdd(FetchType.class.getName());
720                importsAdd(Set.class.getName());
721                importsAdd(HashSet.class.getName());
722                indentPrintln("@OneToMany(cascade = CascadeType.ALL, mappedBy = \"owner\", fetch = FetchType.EAGER, orphanRemoval = true)");
723                indentPrintln("private final Set<" + attributeEntity + "> attributes = new HashSet<" + attributeEntity + ">();");
724                JpaAttributeEntityWriter attrEntityWriter
725                        = new JpaAttributeEntityWriter(model, directory, rootPackage, servKey, methods, xmlType, isR1, className);
726                attrEntityWriter.write();
727                continue;
728            }
729            //    @Column(name = "NAME")
730            //    private String name;
731            importsAdd(Column.class.getName());
732            String columnName = calcColumnName(ms);
733            if (ms.getType().equalsIgnoreCase("Date")) {
734                importsAdd(Temporal.class.getName());
735                importsAdd(TemporalType.class.getName());
736                indentPrintln("@Temporal(TemporalType.TIMESTAMP)");
737            }
738            if (shouldAddJpaImplTodo(ms)) {
739                indentPrintln("//TODO: JPAIMPL double check column name it was calculated based on heuristics and may not match actual table column");
740            }
741            indentPrintln("@Column(name= \"" + columnName + "\", nullable=" + this.isNullable(ms) + ")");
742            String javaClass = this.calcType(ms.getType(), this.stripList(ms.getType()));
743            indentPrintln("private " + javaClass + " " + initLower(ms.getShortName()) + ";");
744        }
745
746        println("");
747//    public HoldIssueEntity() {
748//    }
749//
750//    public HoldIssueEntity(HoldIssue dto) {
751//        super(dto);
752//        this.setId(dto.getId());
753//        this.setHoldIssueType(dto.getTypeKey());
754//        this.fromDto(dto);
755//    }
756        String infcName = this.calcInfcName();
757        println("");
758        indentPrintln("public " + className + "() {");
759        indentPrintln("}");
760        println("");
761        indentPrintln("public " + className + "(" + infcName + " dto) {");
762        incrementIndent();
763        indentPrintln("super(dto);");
764        String keyOrId = this.keyOrId();
765        indentPrintln("this.setId(dto.get" + keyOrId + "());");
766        indentPrintln("this.setTypeKey(dto.getTypeKey());");
767        indentPrintln("// TODO: JPAIMPL insert other fields that should only be specified on create");
768        indentPrintln("this.fromDto(dto);");
769        decrementIndent();
770        indentPrintln("}");
771
772//        public void fromDto(HoldIssue dto) {
773//        super.fromDTO(dto);
774//        
775//        setName(dto.getName());
776//        setHoldIssueState(dto.getStateKey());
777//        setOrganizationId(dto.getOrganizationId());
778//        if (dto.getDescr() != null) {
779//            this.setDescrFormatted(dto.getDescr().getFormatted());
780//            this.setDescrPlain(dto.getDescr().getPlain());
781//        } else {
782//            this.setDescrFormatted(null);
783//            this.setDescrPlain(null);
784//        }
785//
786//        // dynamic attributes
787//        this.getAttributes().clear();
788//        for (Attribute att : dto.getAttributes()) {
789//            HoldIssueAttributeEntity attEntity = new HoldIssueAttributeEntity(att, this);
790//            this.getAttributes().add(attEntity);
791//        }
792//    }
793        println("");
794        indentPrintln("public void fromDto(" + infcName + " dto) {");
795        incrementIndent();
796        indentPrintln("super.fromDTO(dto);");
797        indentPrintln("// TODO: JPAIMPL move all fields that are only supposed to be only specified on create up to the constructor section (like type is)");
798        for (MessageStructure ms : finder.findMessageStructures(xmlType.getName())) {
799            if (ms.getShortName().equalsIgnoreCase("id")) {
800                continue;
801            }
802            if (ms.getShortName().equalsIgnoreCase("meta")) {
803                continue;
804            }
805            if (ms.getShortName().equalsIgnoreCase("key")) {
806                continue;
807            }
808            if (ms.getShortName().equalsIgnoreCase("typeKey")) {
809                continue;
810            }
811            if (ms.getShortName().equalsIgnoreCase("typeKey")) {
812                continue;
813            }
814            if (ms.getShortName().equalsIgnoreCase("descr")) {
815                println("");
816                indentPrintln("if (dto.getDescr() != null) {");
817                indentPrintln("    this.setDescrFormatted(dto.getDescr().getFormatted());");
818                indentPrintln("    this.setDescrPlain(dto.getDescr().getPlain());");
819                indentPrintln("} else {");
820                indentPrintln("     this.setDescrFormatted(null);");
821                indentPrintln("     this.setDescrPlain(null);");
822                indentPrintln("}");
823                println("");
824                continue;
825            }
826            if (ms.getShortName().equalsIgnoreCase("attributes")) {
827                importsAdd("org.kuali.student.r2.common.infc.Attribute");
828                println("");
829                indentPrintln("// dynamic attributes");
830                indentPrintln("this.getAttributes().clear();");
831                indentPrintln("for (Attribute att : dto.getAttributes()) {");
832                indentPrintln("    " + attributeEntityName + " attEntity = new " + attributeEntityName + "(att, this);");
833                indentPrintln("    this.getAttributes().add(attEntity);");
834                indentPrintln("}");
835                continue;
836            }
837            // regular fields
838            String upperName = initUpper(ms.getShortName());
839            indentPrintln("set" + upperName + "(dto.get" + upperName + "());");
840        }
841        decrementIndent();
842        indentPrintln("}");
843
844//       public AppliedHoldInfo toDto() {
845//        AppliedHoldInfo info = new AppliedHoldInfo();
846//        info.setId(getId());
847//        info.setName(name);
848//        info.setEffectiveDate(effectiveDate);
849//        info.setReleasedDate(releasedDate);
850//        info.setPersonId(personId);
851//        info.setTypeKey(holdType);
852//        info.setStateKey(holdState);
853//        if (holdIssue != null) {
854//            info.setHoldIssueId(holdIssue.getId());
855//        }
856//        info.setDescr(new RichTextHelper().toRichTextInfo(getDescrPlain(), getDescrFormatted()));
857//
858//        info.setMeta(super.toDTO());
859//        for (AppliedHoldAttributeEntity att : getAttributes()) {
860//            AttributeInfo attInfo = att.toDto();
861//            info.getAttributes().add(attInfo);
862//        }
863//        return info;
864//    }
865        String infoName = this.calcInfoName();
866        println("");
867        indentPrintln("public " + infoName + " toDto() {");
868        incrementIndent();
869        indentPrintln(infoName + " info = new " + infoName + "();");
870        for (MessageStructure ms : finder.findMessageStructures(xmlType.getName())) {
871            if (ms.getShortName().equalsIgnoreCase("id")) {
872                indentPrintln("info.setId(getId());");
873                continue;
874            }
875            if (ms.getShortName().equalsIgnoreCase("meta")) {
876                indentPrintln("info.setMeta(super.toDTO());");
877                continue;
878            }
879            if (ms.getShortName().equalsIgnoreCase("key")) {
880                indentPrintln("info.setKey(getId());");
881                continue;
882            }
883            if (ms.getShortName().equalsIgnoreCase("descr")) {
884                importsAdd("org.kuali.student.r2.common.util.RichTextHelper");
885                indentPrintln("info.setDescr(new RichTextHelper().toRichTextInfo(getDescrPlain(), getDescrFormatted()));");
886                continue;
887            }
888            if (ms.getShortName().equalsIgnoreCase("attributes")) {
889                importsAdd("org.kuali.student.r2.common.dto.AttributeInfo");
890                println("");
891                indentPrintln("// dynamic attributes");
892                indentPrintln("for (" + attributeEntityName + " att : getAttributes()) {");
893                indentPrintln("    AttributeInfo attInfo = att.toDto();");
894                indentPrintln("    info.getAttributes().add(attInfo);");
895                indentPrintln("}");
896                continue;
897            }
898            // regular fields
899            String upperName = initUpper(ms.getShortName());
900            indentPrintln("info.set" + upperName + "(this.get" + upperName + "());");
901        }
902        indentPrintln("return info;");
903        decrementIndent();
904        indentPrintln("}");
905
906        // write getters and setters
907        for (MessageStructure ms : finder.findMessageStructures(xmlType.getName())) {
908            if (ms.getShortName().equalsIgnoreCase("id")) {
909                continue;
910            }
911            if (ms.getShortName().equalsIgnoreCase("meta")) {
912                continue;
913            }
914            if (ms.getShortName().equalsIgnoreCase("key")) {
915                continue;
916            }
917            if (ms.getShortName().equalsIgnoreCase("descr")) {
918                println("");
919                indentPrintln("public String getDescrPlain() {");
920                indentPrintln("    return this.descrPlain;");
921                indentPrintln("}");
922                println("");
923                indentPrintln("public void setDescrPlain(String descrPlain) {");
924                indentPrintln("    this.descrPlain = descrPlain;");
925                indentPrintln("}");
926                println("");
927                indentPrintln("public String getDescrFormatted() {");
928                indentPrintln("    return this.descrFormatted;");
929                indentPrintln("}");
930                println("");
931                indentPrintln("public void setDescrFormatted(String descrFormatted) {");
932                indentPrintln("    this.descrFormatted = descrFormatted;");
933                indentPrintln("}");
934                continue;
935            }
936            if (ms.getShortName().equalsIgnoreCase("attributes")) {
937                println("");
938                indentPrintln("@Override");
939                indentPrintln("public void setAttributes(Set<" + attributeEntityName + "> attributes) {");
940                incrementIndent();
941                indentPrintln("this.attributes.clear();");
942                indentPrintln("if (attributes != null) {");
943                indentPrintln("    this.attributes.addAll(attributes);");
944                indentPrintln("}");
945                decrementIndent();
946                indentPrintln("}");
947                println("");
948                indentPrintln("@Override");
949                indentPrintln("public Set<" + attributeEntityName + "> getAttributes() {");
950                indentPrintln("    return attributes;");
951                indentPrintln("}");
952                println("");
953                continue;
954            }
955            importsAdd(Column.class.getName());
956            String variableName = initLower(ms.getShortName());
957            String javaClass = this.calcType(ms.getType(), this.stripList(ms.getType()));
958            String upperName = initUpper(ms.getShortName());
959            println("");
960            indentPrintln("public " + javaClass + " get" + upperName + "() {");
961            indentPrintln("    return this." + variableName + ";");
962            indentPrintln("}");
963            println("");
964            indentPrintln("public void set" + upperName + "(" + javaClass + " " + variableName + ") {");
965            indentPrintln("    this." + variableName + " = " + variableName + ";");
966            indentPrintln("}");
967        }
968
969        println("");
970        closeBrace();
971
972        this.writeJavaClassAndImportsOutToFile();
973        this.getOut().close();
974    }
975
976    private boolean shouldAddJpaImplTodo(MessageStructure ms) {
977        if (ms.getColumnName() != null) {
978            return false;
979        }
980        if (ms.getShortName().equalsIgnoreCase("name")) {
981            return false;
982        }
983        if (ms.getShortName().equalsIgnoreCase("descr")) {
984            return false;
985        }
986        if (ms.getShortName().equalsIgnoreCase("effectiveDate")) {
987            return false;
988        }
989        if (ms.getShortName().equalsIgnoreCase("expirationDate")) {
990            return false;
991        }
992        return true;
993    }
994
995    private String getInvalidParameterException() {
996        if (this.isRice()) {
997            return "RiceIllegalArgumentException";
998        }
999        return "InvalidParameterException";
1000    }
1001
1002    private String getOperationFailedException() {
1003        if (this.isRice()) {
1004            return "RiceIllegalArgumentException";
1005        }
1006        return "OperationFailedException";
1007    }
1008
1009    private String getDoesNotExistException() {
1010        if (this.isRice()) {
1011            return "RiceIllegalArgumentException";
1012        }
1013        return "DoesNotExistException";
1014    }
1015
1016    private String getVersionMismatchException() {
1017        if (this.isRice()) {
1018            return "RiceIllegalStateException";
1019        }
1020        return "VersionMismatchException";
1021    }
1022
1023    private void writeThrowsNotImplemented(ServiceMethod method) {
1024        indentPrintln("throw new " + this.getOperationFailedException() + " (\"" + method.getName() + " has not been implemented\");");
1025    }
1026
1027    protected String initLower(String str) {
1028        return str.substring(0, 1).toLowerCase() + str.substring(1);
1029    }
1030
1031    private String calcCriteriaLookupServiceVariableName(ServiceMethod method) {
1032        String objectName = calcObjectName(method);
1033        String variableName = initLower(objectName) + "CriteriaLookupService";
1034        return variableName;
1035    }
1036
1037    private ServiceMethodParameter findIdParameter(ServiceMethod method) {
1038        String idFieldName = calcObjectName(method) + "Id";
1039        for (ServiceMethodParameter parameter : method.getParameters()) {
1040            if (parameter.getType().equals("String")) {
1041                if (parameter.getName().equals(idFieldName)) {
1042                    return parameter;
1043                }
1044            }
1045        }
1046
1047        // if only one parameter and it is a string then grab that
1048        if (method.getParameters().size() == 1) {
1049            for (ServiceMethodParameter parameter : method.getParameters()) {
1050                if (parameter.getType().equals("String")) {
1051                    return parameter;
1052                }
1053            }
1054        }
1055        // can't find name exactly 
1056        for (ServiceMethodParameter parameter : method.getParameters()) {
1057            if (parameter.getType().equals("String")) {
1058                if (parameter.getName().endsWith("Id")) {
1059                    return parameter;
1060                }
1061            }
1062        }
1063        // can't find name exactly try key 
1064        for (ServiceMethodParameter parameter : method.getParameters()) {
1065            if (parameter.getType().equals("String")) {
1066                if (!parameter.getName().endsWith("TypeKey")) {
1067                    if (parameter.getName().endsWith("Key")) {
1068                        return parameter;
1069                    }
1070                }
1071            }
1072        }
1073        log.warn("Could not find the Id paramter for {}.{} so returning the first one", method.getService(), method.getName());
1074        return method.getParameters().get(0);
1075    }
1076
1077    private ServiceMethodParameter findContextParameter(ServiceMethod method) {
1078        for (ServiceMethodParameter parameter : method.getParameters()) {
1079            if (parameter.getType().equals("ContextInfo")) {
1080                return parameter;
1081            }
1082        }
1083        return null;
1084    }
1085
1086    private ServiceMethodParameter findInfoParameter(ServiceMethod method) {
1087        String objectName = calcObjectName(method);
1088        if (!this.isRice()) {
1089            objectName = objectName + "Info";
1090        }
1091        for (ServiceMethodParameter parameter : method.getParameters()) {
1092            if (parameter.getType().equals(objectName)) {
1093                return parameter;
1094            }
1095        }
1096        if (method.getParameters().size() >= 1) {
1097            return method.getParameters().get(0);
1098        }
1099        return null;
1100    }
1101
1102    private ServiceMethodParameter findTypeParameter(ServiceMethod method) {
1103        for (ServiceMethodParameter parameter : method.getParameters()) {
1104            if (parameter.getType().equals("String")) {
1105                if (parameter.getName().endsWith("TypeKey")) {
1106                    return parameter;
1107                }
1108                if (parameter.getName().endsWith("Type")) {
1109                    return parameter;
1110                }
1111            }
1112        }
1113        return null;
1114    }
1115
1116    private String calcDaoVariableName(ServiceMethod method) {
1117        String daoVariableName = this.calcObjectName(method);
1118        daoVariableName = this.initLower(daoVariableName) + "Dao";
1119        return daoVariableName;
1120    }
1121
1122    private String calcEntityClassName(ServiceMethod method) {
1123        String objectName = this.calcObjectName(method);
1124        objectName = objectName + "Entity";
1125        return objectName;
1126    }
1127
1128    protected String calcObjectName(ServiceMethod method) {
1129        if (method.getName().startsWith("create")) {
1130            return method.getName().substring("create".length());
1131        }
1132        if (method.getName().startsWith("update")) {
1133            return method.getName().substring("update".length());
1134        }
1135        if (method.getName().startsWith("validate")) {
1136            return method.getName().substring("validate".length());
1137        }
1138        if (method.getName().startsWith("delete")) {
1139            return method.getName().substring("delete".length());
1140        }
1141        if (method.getName().startsWith("get")) {
1142            if (method.getReturnValue().getType().equals("StringList")) {
1143                if (method.getName().contains("IdsBy")) {
1144                    return method.getName().substring("get".length(),
1145                            method.getName().indexOf("IdsBy"));
1146                }
1147                if (method.getName().contains("KeysBy")) {
1148                    return method.getName().substring("get".length(),
1149                            method.getName().indexOf("KeysBy"));
1150                }
1151                if (method.getName().contains("IdsFor")) {
1152                    return method.getName().substring("get".length(),
1153                            method.getName().indexOf("IdsFor"));
1154                }
1155                if (method.getName().contains("With")) {
1156                    return method.getName().substring("get".length(),
1157                            method.getName().indexOf("With"));
1158                }
1159                if (method.getName().contains("By")) {
1160                    return method.getName().substring("get".length(),
1161                            method.getName().indexOf("By"));
1162                }
1163                return method.getName().substring("get".length());
1164            }
1165            String name = method.getReturnValue().getType();
1166            if (name.endsWith("List")) {
1167                name = name.substring(0, name.length() - "List".length());
1168            }
1169            if (name.endsWith("Info")) {
1170                name = name.substring(0, name.length() - "Info".length());
1171            }
1172            return name;
1173        }
1174
1175        if (method.getName().startsWith("searchFor")) {
1176            if (method.getReturnValue().getType().equals("StringList")) {
1177                if (method.getName().endsWith("Ids")) {
1178                    return method.getName().substring("searchFor".length(),
1179                            method.getName().indexOf("Ids"));
1180                }
1181                if (method.getName().endsWith("Keys")) {
1182                    return method.getName().substring("get".length(),
1183                            method.getName().indexOf("Keys"));
1184                }
1185                return method.getName().substring("searchFor".length());
1186            }
1187            String name = method.getReturnValue().getType();
1188            if (name.endsWith("List")) {
1189                name = name.substring(0, name.length() - "List".length());
1190            }
1191            if (name.endsWith("Info")) {
1192                name = name.substring(0, name.length() - "Info".length());
1193            }
1194            return name;
1195        }
1196        if (method.getName().startsWith("add")) {
1197            return method.getName().substring("add".length());
1198        }
1199        if (method.getName().startsWith("remove")) {
1200            return method.getName().substring("remove".length());
1201        }
1202        String returnType = this.stripList(method.getReturnValue().getType());
1203        XmlType type = this.finder.findXmlType(returnType);
1204        if (type.getPrimitive().equals(XmlType.COMPLEX)) {
1205            return returnType;
1206        }
1207        return null;
1208    }
1209
1210    private String calcNamedQuery(ServiceMethod method) {
1211        String objectName = calcObjectName(method);
1212        if (objectName == null) {
1213            return null;
1214        }
1215        String name = method.getName();
1216        if (name.startsWith("get")) {
1217            name = name.substring("get".length());
1218        }
1219        if (name.startsWith(objectName)) {
1220            name = name.substring(objectName.length());
1221        }
1222//        if (name.startsWith("Ids")) {
1223//            name = name.substring("Ids".length());
1224//        }
1225//        if (name.isEmpty()) {
1226//            throw new RuntimeException (method.getName());
1227//        }
1228        // remove plural
1229        if (!method.getReturnValue().getType().equals("StringList")) {
1230            if (name.startsWith("s")) {
1231                name = name.substring("s".length());
1232            }
1233        }
1234        // add back the get
1235        name = "get" + name;
1236        return name;
1237    }
1238
1239    private ServiceMethodParameter findCriteriaParam(ServiceMethod method) {
1240        for (ServiceMethodParameter param : method.getParameters()) {
1241            if (param.getType().equals("QueryByCriteria")) {
1242                return param;
1243            }
1244        }
1245        return null;
1246    }
1247
1248    private ServiceMethodParameter getTypeParameter(ServiceMethod method) {
1249        ServiceMethodParameter fallbackParam = null;
1250        for (ServiceMethodParameter parameter : method.getParameters()) {
1251            if (parameter.getName().endsWith("TypeKey")) {
1252                return parameter;
1253            }
1254            if (parameter.getType().equals("String")) {
1255                if (parameter.getName().toLowerCase().contains("type")) {
1256                    fallbackParam = parameter;
1257                }
1258            }
1259        }
1260        return fallbackParam;
1261    }
1262
1263    private String calcInfoName(ServiceMethod method) {
1264        String objectName = this.calcObjectName(method);
1265        String infoName = objectName;
1266        if (!this.isRice()) {
1267            infoName = infoName + "Info";
1268        }
1269        return infoName;
1270    }
1271
1272    private String initUpper(String str) {
1273        return str.substring(0, 1).toUpperCase() + str.substring(1);
1274    }
1275
1276    private ServiceMethodParameter findIdListParameter(ServiceMethod method) {
1277        String idFieldName = calcObjectName(method) + "Ids";
1278        if (this.isRice()) {
1279            idFieldName = "ids";
1280        }
1281        for (ServiceMethodParameter parameter : method.getParameters()) {
1282            if (parameter.getType().equals("StringList")) {
1283                if (parameter.getName().equals(idFieldName)) {
1284                    return parameter;
1285                }
1286            }
1287        }
1288        // can't find name exactly 
1289        for (ServiceMethodParameter parameter : method.getParameters()) {
1290            if (parameter.getType().equals("StringList")) {
1291                if (parameter.getName().endsWith("Ids")) {
1292                    return parameter;
1293                }
1294            }
1295        }
1296        // can't find name exactly try key 
1297        for (ServiceMethodParameter parameter : method.getParameters()) {
1298            if (parameter.getType().equals("StringList")) {
1299                if (parameter.getName().endsWith("Keys")) {
1300                    return parameter;
1301                }
1302            }
1303        }
1304        return null;
1305    }
1306
1307    private String stripList(String str) {
1308        return GetterSetterNameCalculator.stripList(str);
1309    }
1310
1311    private String calcExceptionClassName(ServiceMethodError error) {
1312        if (error.getClassName() == null) {
1313            return ServiceExceptionWriter.calcClassName(error.getType());
1314        }
1315        return error.getClassName();
1316    }
1317
1318    private String calcExceptionPackageName(ServiceMethodError error) {
1319        if (error.getClassName() == null) {
1320            return ServiceExceptionWriter.calcPackage(rootPackage);
1321        }
1322        return error.getPackageName();
1323    }
1324
1325    private String calcType(String type, String realType) {
1326        XmlType t = finder.findXmlType(this.stripList(type));
1327        String retType = MessageStructureTypeCalculator.calculate(this, model, type, realType,
1328                t.getJavaPackage());
1329        if (this.isRice()) {
1330            if (retType.equals("Boolean")) {
1331                retType = "boolean";
1332            }
1333            if (retType.equals("Void")) {
1334                retType = "void";
1335            }
1336        }
1337        return retType;
1338    }
1339}