1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.student.jpa.mojo;
17  
18  import org.kuali.student.contract.model.ServiceContractModel;
19  import org.kuali.student.contract.model.ServiceMethod;
20  import org.kuali.student.contract.model.ServiceMethodError;
21  import org.kuali.student.contract.model.ServiceMethodParameter;
22  import org.kuali.student.contract.model.XmlType;
23  import org.kuali.student.contract.model.util.ModelFinder;
24  import org.kuali.student.contract.writer.JavaClassWriter;
25  import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
26  import org.kuali.student.contract.writer.service.MessageStructureTypeCalculator;
27  import org.kuali.student.contract.writer.service.ServiceExceptionWriter;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  import java.util.*;
32  import javax.persistence.CascadeType;
33  import javax.persistence.Column;
34  import javax.persistence.Entity;
35  import javax.persistence.FetchType;
36  import javax.persistence.NamedQueries;
37  import javax.persistence.NamedQuery;
38  import javax.persistence.OneToMany;
39  import javax.persistence.Table;
40  import javax.persistence.Temporal;
41  import javax.persistence.TemporalType;
42  import org.apache.commons.lang.StringUtils;
43  import org.kuali.student.contract.model.MessageStructure;
44  
45  
46  
47  
48  
49  public class JpaEntityWriter extends JavaClassWriter {
50  
51      private static final Logger log = LoggerFactory.getLogger(JpaEntityWriter.class);
52  
53      
54      
55      
56      
57  
58  
59      protected static enum MethodType {
60  
61          VALIDATE,
62          CREATE,
63          CREATE_BULK,
64          ADD,
65          UPDATE,
66          UPDATE_OTHER,
67          DELETE,
68          REMOVE,
69          DELETE_OTHER,
70          GET_CREATE,
71          GET_BY_ID,
72          GET_BY_IDS,
73          RICE_GET_BY_NAMESPACE_AND_NAME,
74          GET_IDS_BY_TYPE,
75          GET_IDS_BY_OTHER,
76          GET_INFOS_BY_OTHER,
77          GET_TYPE,
78          GET_TYPES,
79          SEARCH_FOR_IDS,
80          SEARCH_FOR_INFOS,
81          UNKNOWN
82      };
83  
84      
85      
86      
87      protected ServiceContractModel model;
88      protected ModelFinder finder;
89      private String directory;
90      private XmlType xmlType;
91      
92  
93  
94  
95  
96  
97  
98      private String rootPackage;
99  
100     
101 
102 
103 
104 
105 
106 
107     protected String servKey;
108     
109 
110 
111     private boolean isR1;
112     private List<ServiceMethod> methods;
113 
114     
115     
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     
157     
158     
159 
160 
161 
162 
163 
164 
165     public static String calcPackage(String servKey, String rootPackage) {
166         String pack = rootPackage + ".";
167 
168 
169 
170 
171 
172 
173 
174 
175 
176 
177 
178 
179 
180 
181 
182 
183 
184         pack = pack + "service.impl.jpa." + servKey.toLowerCase();
185         return pack;
186     }
187 
188     
189 
190 
191 
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 
209 
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 
244 
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 
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     
374     
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             
395 
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             
404 
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     
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         
550         
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         
571         
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 
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 
619 
620 
621 
622 
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             
730             
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 
748 
749 
750 
751 
752 
753 
754 
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 
773 
774 
775 
776 
777 
778 
779 
780 
781 
782 
783 
784 
785 
786 
787 
788 
789 
790 
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             
838             String upperName = initUpper(ms.getShortName());
839             indentPrintln("set" + upperName + "(dto.get" + upperName + "());");
840         }
841         decrementIndent();
842         indentPrintln("}");
843 
844 
845 
846 
847 
848 
849 
850 
851 
852 
853 
854 
855 
856 
857 
858 
859 
860 
861 
862 
863 
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             
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         
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         
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         
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         
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 
1223 
1224 
1225 
1226 
1227 
1228         
1229         if (!method.getReturnValue().getType().equals("StringList")) {
1230             if (name.startsWith("s")) {
1231                 name = name.substring("s".length());
1232             }
1233         }
1234         
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         
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         
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 }