View Javadoc
1   /**
2    * Copyright 2004-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.student.jpa.mojo;
17  
18  import org.kuali.student.contract.model.Service;
19  import org.kuali.student.contract.model.ServiceContractModel;
20  import org.kuali.student.contract.model.ServiceMethod;
21  import org.kuali.student.contract.model.ServiceMethodError;
22  import org.kuali.student.contract.model.ServiceMethodParameter;
23  import org.kuali.student.contract.model.XmlType;
24  import org.kuali.student.contract.model.util.ModelFinder;
25  import org.kuali.student.contract.writer.JavaClassWriter;
26  import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
27  import org.kuali.student.contract.writer.service.MessageStructureTypeCalculator;
28  import org.kuali.student.contract.writer.service.ServiceExceptionWriter;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  import java.util.*;
33  import org.kuali.rice.core.api.criteria.CriteriaLookupService;
34  import org.kuali.rice.core.api.criteria.GenericQueryResults;
35  import org.springframework.transaction.annotation.Transactional;
36  
37  /**
38   *
39   * @author nwright
40   */
41  public class JpaImplServiceWriter extends JavaClassWriter {
42  
43      private static final Logger log = LoggerFactory.getLogger(JpaImplServiceWriter.class);
44  
45      //////////////////////////////
46      // Constants
47      //////////////////////////////
48      /**
49       * The standard type of methods used in our Service contract.
50       */
51      protected static enum MethodType {
52  
53          VALIDATE,
54          CREATE,
55          CREATE_BULK,
56          ADD,
57          UPDATE,
58          UPDATE_OTHER,
59          DELETE,
60          REMOVE,
61          DELETE_OTHER,
62          GET_CREATE,
63          GET_BY_ID,
64          GET_BY_IDS,
65          RICE_GET_BY_NAMESPACE_AND_NAME,
66          GET_IDS_BY_TYPE,
67          GET_IDS_BY_OTHER,
68          GET_INFOS_BY_OTHER,
69          GET_TYPE,
70          GET_TYPES,
71          SEARCH_FOR_IDS,
72          SEARCH_FOR_INFOS,
73          UNKNOWN
74      };
75  
76      //////////////////////////////
77      // Data Variables
78      //////////////////////////////
79      protected ServiceContractModel model;
80      protected ModelFinder finder;
81      private String directory;
82      /**
83       * The package name is stored in the service object itself (the package spec
84       * kept moving around so I assumed the actual service name was unique but
85       * ran into a problem when we included rice because they have a StateService
86       * meaning US states and we have a StateService meaning the state of the
87       * object so I added logic to detect rice and prepend that "RICE." to it
88       */
89      private String rootPackage;
90  
91      /**
92       * Name of the service being operated on. If it is a RICE service it is
93       * prefixed with RICE. [11:32:18 AM] Norman Wright: short name... I think it
94       * gets it by taking the java class SimpleName and stripping off the word
95       * "Service" and I think making it lower case. [11:32:24 AM] Norman Wright:
96       * so OrganizationService becomes organization
97       */
98      protected String servKey;
99  
100     protected List<ServiceMethod> methods;
101 
102     /**
103      * A flag that holds if the service is an R1 service.
104      */
105     private boolean isR1;
106 
107     //////////////////////////
108     // Constructor
109     //////////////////////////
110     public JpaImplServiceWriter(ServiceContractModel model,
111             String directory,
112             String rootPackage,
113             String servKey,
114             List<ServiceMethod> methods,
115             boolean isR1) {
116         super(directory, calcPackage(servKey, rootPackage), calcClassName(servKey));
117         this.model = model;
118         this.finder = new ModelFinder(model);
119         this.directory = directory;
120         this.rootPackage = rootPackage;
121         this.servKey = servKey;
122         this.methods = methods;
123         this.isR1 = isR1;
124     }
125 
126     public JpaImplServiceWriter(ServiceContractModel model,
127             String directory,
128             String rootPackage,
129             String servKey,
130             List<ServiceMethod> methods,
131             boolean isR1,
132             String packageName,
133             String className) {
134         super(directory, packageName, className);
135         this.model = model;
136         this.finder = new ModelFinder(model);
137         this.directory = directory;
138         this.rootPackage = rootPackage;
139         this.servKey = servKey;
140         this.methods = methods;
141         this.isR1 = isR1;
142     }
143 
144     /////////////////////////
145     // Functional Methods
146     /////////////////////////
147     /**
148      * Returns the jpa implementation package name.
149      *
150      * @param servKey
151      * @param rootPackage
152      * @return
153      */
154     public static String calcPackage(String servKey, String rootPackage) {
155         String pack = rootPackage + ".";
156 //        String pack = rootPackage + "." + servKey.toLowerCase() + ".";
157 //  StringBuffer buf = new StringBuffer (service.getVersion ().length ());
158 //  for (int i = 0; i < service.getVersion ().length (); i ++)
159 //  {
160 //   char c = service.getVersion ().charAt (i);
161 //   c = Character.toLowerCase (c);
162 //   if (Character.isLetter (c))
163 //   {
164 //    buf.append (c);
165 //    continue;
166 //   }
167 //   if (Character.isDigit (c))
168 //   {
169 //    buf.append (c);
170 //   }
171 //  }
172 //  pack = pack + buf.toString ();
173         pack = pack + "service.impl.jpa." + servKey.toLowerCase();
174         return pack;
175     }
176 
177     /**
178      * Checks if this is a RICE service.
179      *
180      * @return true if this is a RICE service.
181      */
182     private boolean isRice() {
183         if (this.servKey.startsWith("RICE.")) {
184             return true;
185         }
186         return false;
187     }
188 
189     protected static String fixServKey(String servKey) {
190         if (servKey.startsWith("RICE.")) {
191             return servKey.substring("RICE.".length());
192         }
193         return servKey;
194     }
195 
196     /**
197      * Given the service key (name), returns a calculated class name for the jpa
198      * impl.
199      */
200     public static String calcClassName(String servKey) {
201         return GetterSetterNameCalculator.calcInitUpper(fixServKey(servKey) + "ServiceJpaImpl");
202     }
203 
204     public static String calcServiceInterfaceClassName(String servKey) {
205         return GetterSetterNameCalculator.calcInitUpper(fixServKey(servKey) + "Service");
206     }
207 
208     /**
209      * Analyses the method and returns a MethodType enum that describes what
210      * type of method this is.
211      */
212     protected MethodType calcMethodType(ServiceMethod method) {
213 //        if (method.getName().equals("getInstructionalDaysForTerm")) {
214 //            System.out.println("debug here");
215 //        }
216         if (this.isRice()) {
217             if (method.getName().contains("ByNamespaceCodeAndName")) {
218                 return MethodType.RICE_GET_BY_NAMESPACE_AND_NAME;
219             }
220             if (method.getName().contains("ByNameAndNamespace")) {
221                 return MethodType.RICE_GET_BY_NAMESPACE_AND_NAME;
222             }
223             if (method.getName().startsWith("get")) {
224                 if (method.getParameters().size() == 1) {
225                     if (!method.getReturnValue().getType().endsWith("List")) {
226                         if (method.getParameters().get(0).getName().equals("id")) {
227                             return MethodType.GET_BY_ID;
228                         }
229 
230                     } else {
231                         if (method.getParameters().get(0).getName().equals("ids")) {
232                             return MethodType.GET_BY_IDS;
233                         }
234                     }
235                 }
236             }
237         }
238         if (method.getName().startsWith("validate")) {
239             return MethodType.VALIDATE;
240         }
241         if (method.getName().startsWith("create")) {
242             if (method.getName().startsWith("createBatch")) {
243                 return MethodType.CREATE_BULK;
244             }
245             if (method.getName().startsWith("createSocRolloverResultItems")) {
246                 return MethodType.CREATE_BULK;
247             }
248             if (method.getName().contains("FromExisting")) {
249                 return MethodType.CREATE_BULK;
250             }
251             ServiceMethodParameter infoParam = this.findInfoParameter(method);
252             if (infoParam == null) {
253                 return MethodType.CREATE_BULK;
254             }
255             if (method.getReturnValue().getType().endsWith("List")) {
256                 return MethodType.CREATE_BULK;
257             }
258             return MethodType.CREATE;
259         }
260         if (method.getName().startsWith("add")) {
261             return MethodType.ADD;
262         }
263         if (method.getName().startsWith("update")) {
264             if (this.findInfoParameter(method) != null) {
265                 return MethodType.UPDATE;
266             }
267             return MethodType.UPDATE_OTHER;
268         }
269         if (method.getName().startsWith("delete")) {
270             if (method.getName().contains("By")) {
271                 if (!method.getName().startsWith("deleteBy")) {
272                     return MethodType.DELETE_OTHER;
273                 }
274             }
275             if (method.getName().contains("For")) {
276                 if (!method.getName().startsWith("deleteFor")) {
277                     return MethodType.DELETE_OTHER;
278                 }
279             }
280             return MethodType.DELETE;
281         }
282         if (method.getName().startsWith("remove")) {
283             return MethodType.REMOVE;
284         }
285 
286         if (method.getName().startsWith("getCreate")) {
287             return MethodType.GET_CREATE;
288         }
289 
290         if (method.getName().startsWith("get")) {
291             if (method.getName().endsWith("ByIds")) {
292                 return MethodType.GET_BY_IDS;
293             }
294             if (method.getName().endsWith("ByKeys")) {
295                 return MethodType.GET_BY_IDS;
296             }
297             if (method.getName().endsWith("ByType")) {
298                 return MethodType.GET_IDS_BY_TYPE;
299             }
300             if (method.getReturnValue().getType().endsWith("TypeInfo")) {
301                 return MethodType.GET_TYPE;
302             }
303             if (method.getReturnValue().getType().endsWith("TypeInfoList")) {
304                 return MethodType.GET_TYPES;
305             }
306             if (method.getName().endsWith("ByType")) {
307                 return MethodType.GET_IDS_BY_TYPE;
308             }
309             String splitName = splitCamelCase(method.getName());
310             if (splitName.contains(" By ")) {
311                 if (method.getReturnValue().getType().equals("StringList")) {
312                     return MethodType.GET_IDS_BY_OTHER;
313                 }
314                 if (method.getReturnValue().getType().endsWith("InfoList")) {
315                     return MethodType.GET_INFOS_BY_OTHER;
316                 }
317                 return MethodType.UNKNOWN;
318             }
319             if (splitName.contains(" For ")) {
320                 if (method.getReturnValue().getType().equals("StringList")) {
321                     return MethodType.GET_IDS_BY_OTHER;
322                 }
323                 if (method.getReturnValue().getType().endsWith("InfoList")) {
324                     return MethodType.GET_INFOS_BY_OTHER;
325                 }
326                 return MethodType.UNKNOWN;
327             }
328             if (method.getParameters().size() >= 1 && method.getParameters().size() <= 2) {
329                 if (!method.getReturnValue().getType().endsWith("List")) {
330                     if (method.getParameters().get(0).getName().endsWith("Id")) {
331                         return MethodType.GET_BY_ID;
332                     }
333                     if (method.getParameters().get(0).getName().endsWith("Key")) {
334                         return MethodType.GET_BY_ID;
335                     }
336                 }
337             }
338         }
339         if (method.getName().startsWith("searchFor")) {
340             if (method.getName().endsWith("Ids")) {
341                 return MethodType.SEARCH_FOR_IDS;
342             }
343             if (method.getName().endsWith("Keys")) {
344                 return MethodType.SEARCH_FOR_IDS;
345             }
346             return MethodType.SEARCH_FOR_INFOS;
347         }
348 
349         return MethodType.UNKNOWN;
350     }
351 
352     // got this from
353     // http://stackoverflow.com/questions/2559759/how-do-i-convert-camelcase-into-human-readable-names-in-java
354     private static String splitCamelCase(String s) {
355         if (s == null) {
356             return null;
357         }
358         return s.replaceAll(String.format("%s|%s|%s",
359                 "(?<=[A-Z])(?=[A-Z][a-z])", "(?<=[^A-Z])(?=[A-Z])",
360                 "(?<=[A-Za-z])(?=[^A-Za-z])"), " ");
361     }
362 
363     /**
364      * Write out the entire file
365      */
366     public void write() {
367         indentPrint("public class " + calcClassName(servKey));
368         println(" implements " + calcServiceInterfaceClassName(servKey));
369         Service serv = finder.findService(servKey);
370         importsAdd(serv.getImplProject() + "." + serv.getName());
371         openBrace();
372         // put all the cache variables at the top
373         indentPrintln("// dao variables ");
374         jpaEntitiesWritten.clear();
375         for (ServiceMethod method : methods) {
376             MethodType methodType = calcMethodType(method);
377             switch (methodType) {
378                 case CREATE:
379                 case GET_TYPE:
380                 case GET_BY_ID:
381                     writeDaoVariable(method);
382             }
383         }
384 
385         for (ServiceMethod method : methods) {
386             MethodType methodType = calcMethodType(method);
387             println("");
388 //            indentPrintln("/**");
389 //            indentPrintWrappedComment(method.getDescription());
390 //            indentPrintln("* ");
391 //            for (ServiceMethodParameter param : method.getParameters()) {
392 //                indentPrintWrappedComment("@param " + param.getName() + " - "
393 //                        + param.getType() + " - "
394 //                        + param.getDescription());
395 //            }
396 //            indentPrintWrappedComment("@return " + method.getReturnValue().
397 //                    getDescription());
398 //            indentPrintln("*/");
399             indentPrintln("@Override");
400             switch (methodType) {
401                 case CREATE:
402                     indentPrintln("@Transactional(readOnly = false, rollbackFor = {Throwable.class})");
403                     importsAdd(Transactional.class.getName());
404                     break;
405                 case ADD:
406                     indentPrintln("@Transactional(readOnly = false, rollbackFor = {Throwable.class})");
407                     importsAdd(Transactional.class.getName());
408                     break;
409                 case UPDATE:
410                     indentPrintln("@Transactional(readOnly = false, rollbackFor = {Throwable.class})");
411                     importsAdd(Transactional.class.getName());
412                     break;
413                 case DELETE:
414                     indentPrintln("@Transactional(readOnly = false, rollbackFor = {Throwable.class})");
415                     importsAdd(Transactional.class.getName());
416                     break;
417                 case REMOVE:
418                     indentPrintln("@Transactional(readOnly = false, rollbackFor = {Throwable.class})");
419                     importsAdd(Transactional.class.getName());
420                     break;
421                 case GET_BY_ID:
422                     indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
423                     importsAdd(Transactional.class.getName());
424                     break;
425                 case GET_BY_IDS:
426                     indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
427                     importsAdd(Transactional.class.getName());
428                     break;
429                 case GET_IDS_BY_TYPE:
430                     indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
431                     importsAdd(Transactional.class.getName());
432                     break;
433                 case GET_IDS_BY_OTHER:
434                     indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
435                     importsAdd(Transactional.class.getName());
436                     break;
437                 case GET_INFOS_BY_OTHER:
438                     indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
439                     importsAdd(Transactional.class.getName());
440                     break;
441                 case GET_TYPE:
442                     indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
443                     importsAdd(Transactional.class.getName());
444                     break;
445                 case GET_TYPES:
446                     indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
447                     importsAdd(Transactional.class.getName());
448                     break;
449                 case RICE_GET_BY_NAMESPACE_AND_NAME:
450                     indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
451                     importsAdd(Transactional.class.getName());
452                     break;
453                 default:
454                     indentPrintln("//TODO: JPAIMPL check and set the right transactional values for this method");
455                     indentPrintln("//@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
456                     importsAdd(Transactional.class.getName());
457             }
458             String type = method.getReturnValue().getType();
459             String realType = stripList(type);
460             indentPrint("public " + calcType(type, realType) + " " + method.getName()
461                     + "(");
462             // now do parameters
463             String comma = "";
464             for (ServiceMethodParameter param : method.getParameters()) {
465                 type = param.getType();
466                 realType = stripList(type);
467                 print(comma);
468                 print(calcType(type, realType));
469                 print(" ");
470                 print(param.getName());
471                 comma = ", ";
472             }
473             println(")");
474             // now do exceptions
475             comma = "throws ";
476             incrementIndent();
477             for (ServiceMethodError error : method.getErrors()) {
478                 indentPrint(comma);
479                 String exceptionClassName = calcExceptionClassName(error);
480                 String exceptionPackageName = this.calcExceptionPackageName(error);
481                 println(exceptionClassName);
482                 this.importsAdd(exceptionPackageName + "." + exceptionClassName);
483                 comma = "      ,";
484             }
485             decrementIndent();
486             openBrace();
487             indentPrintln("// " + methodType);
488             switch (methodType) {
489                 case VALIDATE:
490                     writeValidate(method);
491                     break;
492                 case CREATE:
493                     writeCreate(method);
494                     break;
495                 case ADD:
496                     writeAdd(method);
497                     break;
498                 case UPDATE:
499                     writeUpdate(method);
500                     break;
501                 case DELETE:
502                     writeDelete(method);
503                     break;
504                 case REMOVE:
505                     writeRemove(method);
506                     break;
507                 case GET_BY_ID:
508                     writeGetById(method);
509                     break;
510                 case GET_BY_IDS:
511                     writeGetByIds(method);
512                     break;
513                 case GET_IDS_BY_TYPE:
514                     writeGetIdsByType(method);
515                     break;
516                 case GET_IDS_BY_OTHER:
517                     writeGetIdsByOther(method);
518                     break;
519                 case GET_INFOS_BY_OTHER:
520                     writeGetInfosByOther(method);
521                     break;
522                 case SEARCH_FOR_IDS:
523                     writeSearchForIds(method);
524                     break;
525                 case SEARCH_FOR_INFOS:
526                     writeSearchForInfos(method);
527                     break;
528                 case GET_TYPE:
529                     writeGetType(method);
530                     break;
531                 case GET_TYPES:
532                     writeGetTypes(method);
533                     break;
534                 case RICE_GET_BY_NAMESPACE_AND_NAME:
535                     writeRiceGetByNamespaceAndName(method);
536                     break;
537                 default:
538                     writeThrowsNotImplemented(method);
539             }
540             closeBrace();
541         }
542 
543         closeBrace();
544 
545         this.writeJavaClassAndImportsOutToFile();
546         this.getOut().close();
547     }
548 
549     private String getInvalidParameterException() {
550         if (this.isRice()) {
551             return "RiceIllegalArgumentException";
552         }
553         return "InvalidParameterException";
554     }
555 
556     private String getOperationFailedException() {
557         if (this.isRice()) {
558             return "RiceIllegalArgumentException";
559         }
560         return "OperationFailedException";
561     }
562 
563     private String getDoesNotExistException() {
564         if (this.isRice()) {
565             return "RiceIllegalArgumentException";
566         }
567         return "DoesNotExistException";
568     }
569 
570     private String getVersionMismatchException() {
571         if (this.isRice()) {
572             return "RiceIllegalStateException";
573         }
574         return "VersionMismatchException";
575     }
576 
577     private void writeThrowsNotImplemented(ServiceMethod method) {
578         indentPrintln("throw new " + this.getOperationFailedException() + " (\"" + method.getName() + " has not been implemented\");");
579     }
580 
581     protected String initLower(String str) {
582         return str.substring(0, 1).toLowerCase() + str.substring(1);
583     }
584 
585     private String calcCriteriaLookupServiceVariableName(ServiceMethod method) {
586         String objectName = calcObjectName(method);
587         String variableName = initLower(objectName) + "CriteriaLookupService";
588         return variableName;
589     }
590 
591     private void writeValidate(ServiceMethod method) {
592         indentPrintln("return new ArrayList<ValidationResultInfo> ();");
593         this.importsAdd(ArrayList.class.getName());
594     }
595     private final Set<String> jpaEntitiesWritten = new HashSet<String>();
596 
597     private void writeDaoVariable(ServiceMethod method) {
598         String objectName = calcObjectName(method);
599         String className = objectName + "Dao";
600         String variableName = calcDaoVariableName(method);
601         if (jpaEntitiesWritten.add(variableName)) {
602             XmlType xmlType = finder.findXmlType(objectName + "Info");
603             if (xmlType == null) {
604                 System.out.println("Cannot write DAO because the object does not follow patterns.  ObjectName="
605                         + objectName + " method=" + method);
606                 return;
607             }
608             new JpaEntityWriter(model, directory, rootPackage, servKey, methods, xmlType, isR1).write();
609             new JpaDaoWriter(model, directory, rootPackage, servKey, methods, xmlType, isR1).write();
610             println("");
611             indentPrintln("private " + className + " " + variableName + ";");
612             println("");
613             indentPrintln("public void set" + className + "(" + className + " " + variableName + ") {");
614             incrementIndent();
615             indentPrintln("this." + variableName + " = " + variableName + ";");
616             decrementIndent();
617             indentPrintln("}");
618             println("");
619             indentPrintln("public " + className + " get" + className + "() {");
620             incrementIndent();
621             indentPrintln("return this." + variableName + ";");
622             decrementIndent();
623             indentPrintln("}");
624 
625             variableName = calcCriteriaLookupServiceVariableName(method);
626 
627             importsAdd(CriteriaLookupService.class.getName());
628             className = "CriteriaLookupService";
629             println("");
630             indentPrintln("// Criteria Lookup for this object");
631             indentPrintln("private " + className + " " + variableName + ";");
632             println("");
633             indentPrintln("public void set" + initUpper(variableName) + "(" + className + " " + variableName + ") {");
634             incrementIndent();
635             indentPrintln("this." + variableName + " = " + variableName + ";");
636             decrementIndent();
637             indentPrintln("}");
638             println("");
639             indentPrintln("public " + className + " get" + initUpper(variableName) + "() {");
640             incrementIndent();
641             indentPrintln("return this." + variableName + ";");
642             decrementIndent();
643             indentPrintln("}");
644 
645         }
646     }
647 
648     private void writeCreate(ServiceMethod method) {
649 
650 //        holdInfo.setPersonId(personId);
651 //        holdInfo.setHoldIssueId(issueId);
652 //        holdInfo.setTypeKey(holdTypeKey);
653 //
654 //        HoldIssueEntity holdIssueEntity = holdIssueDao.find(issueId);
655 //        if (holdIssueEntity == null) {
656 //            throw new InvalidParameterException(issueId);
657 //        }
658 //        AppliedHoldEntity entity = new AppliedHoldEntity(holdInfo);
659 //        entity.setHoldIssue(holdIssueEntity);
660 //        entity.setEntityCreated(context);
661 //        appliedHoldDao.persist(entity);
662 //        appliedHoldDao.getEm().flush();
663 //        return entity.toDto();
664         String daoVariable = calcDaoVariableName(method);
665         String entityClassName = calcEntityClassName(method);
666         ServiceMethodParameter typeParam = this.findTypeParameter(method);
667         ServiceMethodParameter infoParam = this.findInfoParameter(method);
668         ServiceMethodParameter contextParam = this.findContextParameter(method);
669         String objectName = calcObjectName(method);
670         String infoName = objectName;
671         if (!this.isRice()) {
672             infoName = infoName + "Info";
673         }
674         String daoName = calcDaoVariableName(method);
675         if (typeParam != null) {
676             indentPrintln(infoParam.getName() + ".setTypeKey (" + typeParam.getName() + ");");
677         }
678         if (method.getParameters().size() > 3) {
679             indentPrintln("//TODO: JPAIMPL overwrite the rest of the readonly fields that are specified on the create in the info object");
680         }
681         indentPrintln(entityClassName + " entity = new " + entityClassName + "(" + infoParam.getName() + ");");
682         indentPrintln("entity.setEntityCreated(" + contextParam.getName() + ");");
683         indentPrintln(daoVariable + ".persist(entity);");
684         indentPrintln(daoVariable + ".getEm().flush();");
685         indentPrintln("return entity.toDto();");
686     }
687 
688     private void writeAdd(ServiceMethod method) {
689         indentPrintln("//TODO: JPAIMPL this needs to be implemented");
690         indentPrintln("throw new OperationFailedException (\"Not implemented\");");
691     }
692 
693     private ServiceMethodParameter findIdParameter(ServiceMethod method) {
694         String idFieldName = calcObjectName(method) + "Id";
695         for (ServiceMethodParameter parameter : method.getParameters()) {
696             if (parameter.getType().equals("String")) {
697                 if (parameter.getName().equals(idFieldName)) {
698                     return parameter;
699                 }
700             }
701         }
702 
703         // if only one parameter and it is a string then grab that
704         if (method.getParameters().size() == 1) {
705             for (ServiceMethodParameter parameter : method.getParameters()) {
706                 if (parameter.getType().equals("String")) {
707                     return parameter;
708                 }
709             }
710         }
711         // can't find name exactly 
712         for (ServiceMethodParameter parameter : method.getParameters()) {
713             if (parameter.getType().equals("String")) {
714                 if (parameter.getName().endsWith("Id")) {
715                     return parameter;
716                 }
717             }
718         }
719         // can't find name exactly try key 
720         for (ServiceMethodParameter parameter : method.getParameters()) {
721             if (parameter.getType().equals("String")) {
722                 if (!parameter.getName().endsWith("TypeKey")) {
723                     if (parameter.getName().endsWith("Key")) {
724                         return parameter;
725                     }
726                 }
727             }
728         }
729         log.warn("Could not find the Id paramter for {}.{} so returning the first one", method.getService(), method.getName());
730         return method.getParameters().get(0);
731     }
732 
733     private ServiceMethodParameter findContextParameter(ServiceMethod method) {
734         for (ServiceMethodParameter parameter : method.getParameters()) {
735             if (parameter.getType().equals("ContextInfo")) {
736                 return parameter;
737             }
738         }
739         return null;
740     }
741 
742     private ServiceMethodParameter findInfoParameter(ServiceMethod method) {
743         String objectName = calcObjectName(method);
744         if (!this.isRice()) {
745             objectName = objectName + "Info";
746         }
747         for (ServiceMethodParameter parameter : method.getParameters()) {
748             if (parameter.getType().equals(objectName)) {
749                 return parameter;
750             }
751         }
752         if (method.getParameters().size() >= 1) {
753             return method.getParameters().get(0);
754         }
755         return null;
756     }
757 
758     private ServiceMethodParameter findTypeParameter(ServiceMethod method) {
759         for (ServiceMethodParameter parameter : method.getParameters()) {
760             if (parameter.getType().equals("String")) {
761                 if (parameter.getName().endsWith("TypeKey")) {
762                     return parameter;
763                 }
764                 if (parameter.getName().endsWith("Type")) {
765                     return parameter;
766                 }
767             }
768         }
769         return null;
770     }
771 
772     private String calcDaoVariableName(ServiceMethod method) {
773         String daoVariableName = this.calcObjectName(method);
774         daoVariableName = this.initLower(daoVariableName) + "Dao";
775         return daoVariableName;
776     }
777 
778     private String calcEntityClassName(ServiceMethod method) {
779         String objectName = this.calcObjectName(method);
780         objectName = objectName + "Entity";
781         return objectName;
782     }
783 
784     protected String calcObjectName(ServiceMethod method) {
785         if (method.getName().startsWith("create")) {
786             return method.getName().substring("create".length());
787         }
788         if (method.getName().startsWith("update")) {
789             return method.getName().substring("update".length());
790         }
791         if (method.getName().startsWith("validate")) {
792             return method.getName().substring("validate".length());
793         }
794         if (method.getName().startsWith("delete")) {
795             return method.getName().substring("delete".length());
796         }
797         if (method.getName().startsWith("get")) {
798             if (method.getReturnValue().getType().equals("StringList")) {
799                 if (method.getName().contains("IdsBy")) {
800                     return method.getName().substring("get".length(),
801                             method.getName().indexOf("IdsBy"));
802                 }
803                 if (method.getName().contains("KeysBy")) {
804                     return method.getName().substring("get".length(),
805                             method.getName().indexOf("KeysBy"));
806                 }
807                 if (method.getName().contains("IdsFor")) {
808                     return method.getName().substring("get".length(),
809                             method.getName().indexOf("IdsFor"));
810                 }
811                 if (method.getName().contains("With")) {
812                     return method.getName().substring("get".length(),
813                             method.getName().indexOf("With"));
814                 }
815                 if (method.getName().contains("By")) {
816                     return method.getName().substring("get".length(),
817                             method.getName().indexOf("By"));
818                 }
819                 return method.getName().substring("get".length());
820             }
821             String name = method.getReturnValue().getType();
822             if (name.endsWith("List")) {
823                 name = name.substring(0, name.length() - "List".length());
824             }
825             if (name.endsWith("Info")) {
826                 name = name.substring(0, name.length() - "Info".length());
827             }
828             return name;
829         }
830 
831         if (method.getName().startsWith("searchFor")) {
832             if (method.getReturnValue().getType().equals("StringList")) {
833                 if (method.getName().endsWith("Ids")) {
834                     return method.getName().substring("searchFor".length(),
835                             method.getName().indexOf("Ids"));
836                 }
837                 if (method.getName().endsWith("Keys")) {
838                     return method.getName().substring("get".length(),
839                             method.getName().indexOf("Keys"));
840                 }
841                 return method.getName().substring("searchFor".length());
842             }
843             String name = method.getReturnValue().getType();
844             if (name.endsWith("List")) {
845                 name = name.substring(0, name.length() - "List".length());
846             }
847             if (name.endsWith("Info")) {
848                 name = name.substring(0, name.length() - "Info".length());
849             }
850             return name;
851         }
852         if (method.getName().startsWith("add")) {
853             return method.getName().substring("add".length());
854         }
855         if (method.getName().startsWith("remove")) {
856             return method.getName().substring("remove".length());
857         }
858         String returnType = this.stripList(method.getReturnValue().getType());
859         XmlType type = this.finder.findXmlType(returnType);
860         if (type.getPrimitive().equals(XmlType.COMPLEX)) {
861             return returnType;
862         }
863         throw new IllegalArgumentException(method.getName());
864     }
865 
866     private void writeUpdate(ServiceMethod method) {
867 //        if (!holdId.equals(holdInfo.getId())) {
868 //            throw new InvalidParameterException(holdId + " does not match the id in the object " + holdInfo.getId());
869 //        }
870 //        AppliedHoldEntity entity = appliedHoldDao.find(holdId);
871 //        if (null == entity) {
872 //            throw new DoesNotExistException(holdId);
873 //        }
874 //        entity.fromDto(holdInfo);
875 //        entity.setEntityUpdated(context);
876 //        entity = appliedHoldDao.merge(entity);
877 //        appliedHoldDao.getEm().flush(); // need to flush to get the version indicator updated
878 //        return entity.toDto();
879         ServiceMethodParameter idParam = this.findIdParameter(method);
880         ServiceMethodParameter infoParam = this.findInfoParameter(method);
881         ServiceMethodParameter contextParam = this.findContextParameter(method);
882         String daoVariable = calcDaoVariableName(method);
883         String entityClassName = calcEntityClassName(method);
884         if (infoParam == null) {
885             throw new NullPointerException(method.getName());
886         }
887         String objectName = calcObjectName(method);
888         String infoName = objectName;
889         if (!this.isRice()) {
890             infoName = infoName + "Info";
891         }
892         if (idParam != null) {
893             if (!this.isRice()) {
894                 indentPrintln("if (!" + idParam.getName() + ".equals (" + infoParam.getName() + ".getId())) {");
895                 indentPrintln("    throw new " + this.getInvalidParameterException() + " (\"The id parameter does not match the id on the info object\");");
896                 indentPrintln("}");
897             }
898         }
899         indentPrintln(entityClassName + " entity = " + daoVariable + ".find(" + idParam.getName() + ");");
900         indentPrintln("if (entity == null) {");
901         incrementIndent();
902         indentPrintln("throw new DoesNotExistException(" + idParam.getName() + ");");
903         decrementIndent();
904         indentPrintln("}");
905         indentPrintln("entity.fromDto(" + infoParam.getName() + ");");
906         indentPrintln("entity.setEntityUpdated(" + contextParam.getName() + ");");
907         indentPrintln("entity = " + daoVariable + ".merge(entity);");
908         indentPrintln("return entity.toDto();");
909     }
910 
911     private void writeDelete(ServiceMethod method) {
912 //        AppliedHoldEntity entity = appliedHoldDao.find(holdId);
913 //        if (null == entity) {
914 //            throw new DoesNotExistException(holdId);
915 //        }
916 //        appliedHoldDao.remove(entity);
917 //        StatusInfo status = new StatusInfo();
918 //        status.setSuccess(Boolean.TRUE);
919 //        return status;
920         ServiceMethodParameter idParam = this.findIdParameter(method);
921         String daoVariable = calcDaoVariableName(method);
922         String entityClassName = calcEntityClassName(method);
923         indentPrintln(entityClassName + " entity = " + daoVariable + ".find(" + idParam.getName() + ");");
924         indentPrintln("if (entity == null) {");
925         incrementIndent();
926         indentPrintln("throw new DoesNotExistException(" + idParam.getName() + ");");
927         decrementIndent();
928         indentPrintln("}");
929         indentPrintln(daoVariable + ".remove(entity);");
930         indentPrintln("StatusInfo status = new StatusInfo();");
931         importsAdd("org.kuali.student.r2.common.dto.StatusInfo");
932         indentPrintln("status.setSuccess(Boolean.TRUE);");
933         indentPrintln("return status;");
934     }
935 
936     private void writeRemove(ServiceMethod method) {
937         indentPrintln("//TODO: JPAIMPL this needs to be implemented");
938         indentPrintln("throw new OperationFailedException (\"Not implemented\");");
939     }
940 
941     private void writeGetById(ServiceMethod method) {
942 //        AppliedHoldEntity entity = appliedHoldDao.find(holdId);
943 //        if (entity == null) {
944 //            throw new DoesNotExistException(holdId);
945 //        }
946 //        return entity.toDto();
947         ServiceMethodParameter idParam = this.findIdParameter(method);
948         String daoVariable = calcDaoVariableName(method);
949         String entityClassName = calcEntityClassName(method);
950         indentPrintln(entityClassName + " entity = " + daoVariable + ".find(" + idParam.getName() + ");");
951         indentPrintln("if (entity == null) {");
952         incrementIndent();
953         indentPrintln("throw new DoesNotExistException(" + idParam.getName() + ");");
954         decrementIndent();
955         indentPrintln("}");
956         indentPrintln("return entity.toDto();");
957     }
958 
959     private void writeGetByIds(ServiceMethod method) {
960 //        List<HoldIssueEntity> holdIssues = holdIssueDao.findByIds(issueIds);
961 //        List<HoldIssueInfo> result = new ArrayList<HoldIssueInfo>(holdIssues.size());
962 //        for (HoldIssueEntity entity : holdIssues) {
963 //            if (entity == null) {
964 //                // if one of the entities from "findByIds" is returned as null, then one of the keys in the list was not found
965 //                throw new DoesNotExistException();
966 //            }
967 //            result.add(entity.toDto());
968 //        }
969 //        return result;
970         String daoVariable = calcDaoVariableName(method);
971         String entityClassName = calcEntityClassName(method);
972         String objectName = this.calcObjectName(method);
973         ServiceMethodParameter idListParam = this.findIdListParameter(method);
974         String infoName = objectName;
975         infoName = infoName + "Info";
976         this.importsAdd(ArrayList.class.getName());
977         indentPrintln("List<" + entityClassName + "> entities = " + daoVariable + ".findByIds(" + idListParam.getName() + ");");
978         indentPrintln("List<" + infoName + "> list = new ArrayList<" + infoName + "> (entities.size());");
979         indentPrintln("for (" + entityClassName + " entity : entities) {");
980         incrementIndent();
981         indentPrintln("if (entity == null) {");
982         incrementIndent();
983         indentPrintln("throw new DoesNotExistException();");
984         decrementIndent();
985         indentPrintln("}");
986         indentPrintln("list.add(entity.toDto());");
987         decrementIndent();
988         indentPrintln("}");
989         indentPrintln("return list;");
990     }
991 
992     private String calcNamedQuery(ServiceMethod method) {
993         String objectName = calcObjectName(method);
994         String name = method.getName();
995         if (name.startsWith("get")) {
996             name = name.substring("get".length());
997         }
998         if (name.startsWith(objectName)) {
999             name = name.substring(objectName.length());
1000         }
1001 //        if (name.startsWith("Ids")) {
1002 //            name = name.substring("Ids".length());
1003 //        }
1004 //        if (name.isEmpty()) {
1005 //            throw new RuntimeException (method.getName());
1006 //        }
1007         // remove plural
1008         if (!method.getReturnValue().getType().equals("StringList")) {
1009             if (name.startsWith("s")) {
1010                 name = name.substring("s".length());
1011             }
1012         }
1013         // add back the get
1014         name = "get" + name;
1015         return name;
1016     }
1017 
1018     private void writeGetIdsByOther(ServiceMethod method) {
1019 //      return appliedHoldDao.getIdsByIssue(issueId);
1020         String objectName = this.calcObjectName(method);
1021         String infoName = objectName;
1022         if (!this.isRice()) {
1023             infoName = infoName + "Info";
1024         }
1025         String daoVariableName = calcDaoVariableName(method);
1026         String namedQuery = calcNamedQuery(method);
1027         indentPrint("return " + daoVariableName + "." + namedQuery + "(");
1028         String comma = "";
1029         for (ServiceMethodParameter param : method.getParameters()) {
1030             if (param.getType().equals("ContextInfo")) {
1031                 continue;
1032             }
1033             print(comma);
1034             comma = ", ";
1035             print(param.getName());
1036         }
1037         println(");");
1038     }
1039 
1040     private ServiceMethodParameter findCriteriaParam(ServiceMethod method) {
1041         for (ServiceMethodParameter param : method.getParameters()) {
1042             if (param.getType().equals("QueryByCriteria")) {
1043                 return param;
1044             }
1045         }
1046         return null;
1047     }
1048 
1049     private void writeSearchForIds(ServiceMethod method) {
1050 //        List<String> results = new ArrayList<String>();
1051 //        GenericQueryResults<AppliedHoldEntity> appliedHolds = criteriaLookupService.lookup(AppliedHoldEntity.class, criteria);
1052 //        if (null != appliedHolds && appliedHolds.getResults().size() > 0) {
1053 //            for (AppliedHoldEntity appliedHold : appliedHolds.getResults()) {
1054 //                results.add(appliedHold.getId());
1055 //            }
1056 //        }
1057 //        return results;
1058         String objectName = this.calcObjectName(method);
1059         String infoName = objectName;
1060         if (!this.isRice()) {
1061             infoName = infoName + "Info";
1062         }
1063         String criteriaLookupVariableName = calcCriteriaLookupServiceVariableName(method);
1064         String entityClassName = this.calcEntityClassName(method);
1065         ServiceMethodParameter criteriaParam = findCriteriaParam(method);
1066         importsAdd(CriteriaLookupService.class.getName());
1067         importsAdd(ArrayList.class.getName());
1068         importsAdd(List.class.getName());
1069         indentPrintln("List<String> results = new ArrayList<String>();");
1070         importsAdd(GenericQueryResults.class.getName());
1071         indentPrintln("GenericQueryResults<" + entityClassName + "> entities");
1072         indentPrintln("    = " + criteriaLookupVariableName + ".lookup(" + entityClassName + ".class, " + criteriaParam.getName() + ");");
1073         indentPrintln("if (null != entities && !entities.getResults().isEmpty()) {");
1074         incrementIndent();
1075         indentPrintln("for (" + entityClassName + " entity : entities.getResults()) {");
1076         incrementIndent();
1077         indentPrintln("results.add(entity.getId());");
1078         decrementIndent();
1079         indentPrintln("}");
1080         decrementIndent();
1081         indentPrintln("}");
1082         indentPrintln("return results;");
1083     }
1084 
1085     private void writeSearchForInfos(ServiceMethod method) {
1086 //        List<AppliedHoldInfo> results = new ArrayList<AppliedHoldInfo>();
1087 //        GenericQueryResults<AppliedHoldEntity> appliedHolds = criteriaLookupService.lookup(AppliedHoldEntity.class, criteria);
1088 //        if (null != appliedHolds && appliedHolds.getResults().size() > 0) {
1089 //            for (AppliedHoldEntity appliedHold : appliedHolds.getResults()) {
1090 //                results.add(appliedHold.toDto());
1091 //            }
1092 //        }
1093 //        return results;
1094         String objectName = this.calcObjectName(method);
1095         String criteriaLookupVariableName = calcCriteriaLookupServiceVariableName(method);
1096         String entityClassName = this.calcEntityClassName(method);
1097         String infoName = this.calcInfoName(method);
1098         ServiceMethodParameter criteriaParam = findCriteriaParam(method);
1099         importsAdd(CriteriaLookupService.class.getName());
1100         importsAdd(ArrayList.class.getName());
1101         importsAdd(List.class.getName());
1102         indentPrintln("List<" + infoName + "> results = new ArrayList<" + infoName + ">();");
1103         importsAdd(GenericQueryResults.class.getName());
1104         indentPrintln("GenericQueryResults<" + entityClassName + "> entities");
1105         indentPrintln("    = " + criteriaLookupVariableName + ".lookup(" + entityClassName + ".class, " + criteriaParam.getName() + ");");
1106         indentPrintln("if (null != entities && !entities.getResults().isEmpty()) {");
1107         incrementIndent();
1108         indentPrintln("for (" + entityClassName + " entity : entities.getResults()) {");
1109         incrementIndent();
1110         indentPrintln("results.add(entity.toDto());");
1111         decrementIndent();
1112         indentPrintln("}");
1113         decrementIndent();
1114         indentPrintln("}");
1115         indentPrintln("return results;");
1116     }
1117 
1118     private ServiceMethodParameter getTypeParameter(ServiceMethod method) {
1119         ServiceMethodParameter fallbackParam = null;
1120         for (ServiceMethodParameter parameter : method.getParameters()) {
1121             if (parameter.getName().endsWith("TypeKey")) {
1122                 return parameter;
1123             }
1124             if (parameter.getType().equals("String")) {
1125                 if (parameter.getName().toLowerCase().contains("type")) {
1126                     fallbackParam = parameter;
1127                 }
1128             }
1129         }
1130         return fallbackParam;
1131     }
1132 
1133     private String calcInfoName(ServiceMethod method) {
1134         String objectName = this.calcObjectName(method);
1135         String infoName = objectName;
1136         if (!this.isRice()) {
1137             infoName = infoName + "Info";
1138         }
1139         return infoName;
1140     }
1141 
1142     private void writeGetIdsByType(ServiceMethod method) {
1143         String objectName = this.calcObjectName(method);
1144         String infoName = this.calcInfoName(method);
1145         String daoVariable = calcDaoVariableName(method);
1146         ServiceMethodParameter typeParam = this.getTypeParameter(method);
1147         if (typeParam == null) {
1148 
1149         }
1150         indentPrintln("return " + daoVariable + ".getIdsByType(" + typeParam.getName() + ");");
1151     }
1152 
1153     private void writeRiceGetByNamespaceAndName(ServiceMethod method) {
1154         indentPrintln("//TODO: JPAIMPL this needs to be implemented");
1155         indentPrintln("throw new OperationFailedException (\"Not implemented\");");
1156     }
1157 
1158     private void writeGetInfosByOther(ServiceMethod method) {
1159 //        List<AppliedHoldEntity> entities = this.appliedHoldDao.getByPerson(personId);
1160 //        List<AppliedHoldInfo> result = new ArrayList<AppliedHoldInfo>(entities.size());
1161 //        for (AppliedHoldEntity entity : entities) {
1162 //            result.add(entity.toDto());
1163 //        }
1164 //        return result;
1165         String objectName = this.calcObjectName(method);
1166         String infoName = objectName;
1167         if (!this.isRice()) {
1168             infoName = infoName + "Info";
1169         }
1170         String daoVariableName = calcDaoVariableName(method);
1171         String namedQuery = calcNamedQuery(method);
1172         String entityClassName = calcEntityClassName(method);
1173         indentPrint("List<" + entityClassName + "> entities = " + daoVariableName + "." + namedQuery + "(");
1174         String comma = "";
1175         for (ServiceMethodParameter param : method.getParameters()) {
1176             if (param.getType().equals("ContextInfo")) {
1177                 continue;
1178             }
1179             print(comma);
1180             comma = ", ";
1181             print(param.getName());
1182         }
1183         println(");");
1184         this.importsAdd(ArrayList.class.getName());
1185         indentPrintln("List<" + infoName + "> list = new ArrayList<" + infoName + "> (entities.size());");
1186         indentPrintln("for (" + entityClassName + " entity: entities) {");
1187         indentPrintln("    list.add (entity.toDto ());");
1188         indentPrintln("}");
1189         indentPrintln("return list;");
1190     }
1191 
1192     private void writeGetType(ServiceMethod method) {
1193         indentPrintln("//TODO: JPAIMPL this needs to be implemented");
1194         indentPrintln("throw new OperationFailedException (\"Not implemented\");");
1195     }
1196 
1197     private void writeGetTypes(ServiceMethod method) {
1198         indentPrintln("//TODO: JPAIMPL this needs to be implemented");
1199         indentPrintln("throw new OperationFailedException (\"Not implemented\");");
1200     }
1201 
1202     private String initUpper(String str) {
1203         return str.substring(0, 1).toUpperCase() + str.substring(1);
1204     }
1205 
1206     private ServiceMethodParameter findIdListParameter(ServiceMethod method) {
1207         String idFieldName = calcObjectName(method) + "Ids";
1208         if (this.isRice()) {
1209             idFieldName = "ids";
1210         }
1211         for (ServiceMethodParameter parameter : method.getParameters()) {
1212             if (parameter.getType().equals("StringList")) {
1213                 if (parameter.getName().equals(idFieldName)) {
1214                     return parameter;
1215                 }
1216             }
1217         }
1218         // can't find name exactly 
1219         for (ServiceMethodParameter parameter : method.getParameters()) {
1220             if (parameter.getType().equals("StringList")) {
1221                 if (parameter.getName().endsWith("Ids")) {
1222                     return parameter;
1223                 }
1224             }
1225         }
1226         // can't find name exactly try key 
1227         for (ServiceMethodParameter parameter : method.getParameters()) {
1228             if (parameter.getType().equals("StringList")) {
1229                 if (parameter.getName().endsWith("Keys")) {
1230                     return parameter;
1231                 }
1232             }
1233         }
1234         return null;
1235     }
1236 
1237     private String stripList(String str) {
1238         return GetterSetterNameCalculator.stripList(str);
1239     }
1240 
1241     private String calcExceptionClassName(ServiceMethodError error) {
1242         if (error.getClassName() == null) {
1243             return ServiceExceptionWriter.calcClassName(error.getType());
1244         }
1245         return error.getClassName();
1246     }
1247 
1248     private String calcExceptionPackageName(ServiceMethodError error) {
1249         if (error.getClassName() == null) {
1250             return ServiceExceptionWriter.calcPackage(rootPackage);
1251         }
1252         return error.getPackageName();
1253     }
1254 
1255     private String calcType(String type, String realType) {
1256         XmlType t = finder.findXmlType(this.stripList(type));
1257         String retType = MessageStructureTypeCalculator.calculate(this, model, type, realType,
1258                 t.getJavaPackage());
1259         if (this.isRice()) {
1260             if (retType.equals("Boolean")) {
1261                 retType = "boolean";
1262             }
1263             if (retType.equals("Void")) {
1264                 retType = "void";
1265             }
1266         }
1267         return retType;
1268     }
1269 }