001    /**
002     * Copyright 2004-2014 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.student.jpa.mojo;
017    
018    import org.kuali.student.contract.model.ServiceContractModel;
019    import org.kuali.student.contract.model.ServiceMethod;
020    import org.kuali.student.contract.model.ServiceMethodError;
021    import org.kuali.student.contract.model.ServiceMethodParameter;
022    import org.kuali.student.contract.model.XmlType;
023    import org.kuali.student.contract.model.util.ModelFinder;
024    import org.kuali.student.contract.writer.JavaClassWriter;
025    import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
026    import org.kuali.student.contract.writer.service.MessageStructureTypeCalculator;
027    import org.kuali.student.contract.writer.service.ServiceExceptionWriter;
028    import org.slf4j.Logger;
029    import org.slf4j.LoggerFactory;
030    
031    import java.util.*;
032    import javax.persistence.CascadeType;
033    import javax.persistence.Column;
034    import javax.persistence.Entity;
035    import javax.persistence.FetchType;
036    import javax.persistence.NamedQueries;
037    import javax.persistence.NamedQuery;
038    import javax.persistence.OneToMany;
039    import javax.persistence.Table;
040    import javax.persistence.Temporal;
041    import javax.persistence.TemporalType;
042    import org.apache.commons.lang.StringUtils;
043    import org.kuali.student.contract.model.MessageStructure;
044    
045    /**
046     *
047     * @author nwright
048     */
049    public class JpaEntityWriter extends JavaClassWriter {
050    
051        private static 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 " + method.getService() + "." + method.getName() + " so returning the first one");
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    }