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.mock.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.impl.ServiceContractModelPescXsdLoader;
25  import org.kuali.student.contract.model.util.ModelFinder;
26  import org.kuali.student.contract.writer.JavaClassWriter;
27  import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
28  import org.kuali.student.contract.writer.service.MessageStructureTypeCalculator;
29  import org.kuali.student.contract.writer.service.ServiceExceptionWriter;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  import java.util.*;
34  
35  /**
36   *
37   * @author nwright
38   */
39  public class MockImplServiceWriter extends JavaClassWriter {
40      
41      private static Logger log = LoggerFactory.getLogger(MockImplServiceWriter.class);
42  
43      //////////////////////////////
44      // Constants
45      //////////////////////////////
46  
47      /**
48       * The standard type of methods used in our Service contract.
49       */
50      protected static enum MethodType {
51  
52          VALIDATE,
53          CREATE,
54          CREATE_BULK,
55          ADD,
56          UPDATE,
57          UPDATE_OTHER,
58          DELETE,
59          REMOVE,
60          DELETE_OTHER,
61          GET_CREATE,
62          GET_BY_ID,
63          GET_BY_IDS,
64          RICE_GET_BY_NAMESPACE_AND_NAME,
65          GET_IDS_BY_TYPE,
66          GET_IDS_BY_OTHER,
67          GET_INFOS_BY_OTHER,
68          GET_TYPE,
69          GET_TYPES,
70          UNKNOWN
71      };
72  
73      //////////////////////////////
74      // Data Variables
75      //////////////////////////////
76  
77      protected ServiceContractModel model;
78      protected ModelFinder finder;
79      private String directory;
80      /**
81       * The package name is stored in the service object itself (the package spec kept
82       * moving around so I assumed the actual service name was unique but ran into a problem
83       * when we included rice because they have a StateService  meaning US states and we have
84       * a StateService meaning the state of the object so I added logic to detect rice and
85       * prepend that "RICE." to it
86       */
87      private String rootPackage;
88  
89      /**
90       * Name of the service being operated on.
91       * If it is a RICE service it is prefixed with RICE.
92       * [11:32:18 AM] Norman Wright: short name... I think it gets it by taking the java class SimpleName and stripping off the word "Service" and I think making it lower case.
93       * [11:32:24 AM] Norman Wright: so OrganizationService becomes organization
94       */
95      protected String servKey;
96  
97      protected List<ServiceMethod> methods;
98  
99      /**
100      * A flag that holds if the service is an R1 service.
101      */
102     private boolean isR1;
103 
104     //////////////////////////
105     // Constructor
106     //////////////////////////
107 
108     public MockImplServiceWriter(ServiceContractModel model,
109             String directory,
110             String rootPackage,
111             String servKey,
112             List<ServiceMethod> methods,
113             boolean isR1) {
114         super(directory, calcPackage(servKey, rootPackage), calcClassName(servKey));
115         this.model = model;
116         this.finder = new ModelFinder(model);
117         this.directory = directory;
118         this.rootPackage = rootPackage;
119         this.servKey = servKey;
120         this.methods = methods;
121         this.isR1 = isR1;
122     }
123 
124     public MockImplServiceWriter(ServiceContractModel model,
125                                  String directory,
126                                  String rootPackage,
127                                  String servKey,
128                                  List<ServiceMethod> methods,
129                                  boolean isR1,
130                                  String packageName,
131                                  String className) {
132         super(directory, packageName, className);
133         this.model = model;
134         this.finder = new ModelFinder(model);
135         this.directory = directory;
136         this.rootPackage = rootPackage;
137         this.servKey = servKey;
138         this.methods = methods;
139         this.isR1 = isR1;
140     }
141 
142     /////////////////////////
143     // Functional Methods
144     /////////////////////////
145 
146     /**
147      * Returns the mock implementation package name.
148      * @param servKey
149      * @param rootPackage
150      * @return
151      */
152     public static String calcPackage(String servKey, String rootPackage) {
153         String pack = rootPackage + ".";
154 //        String pack = rootPackage + "." + servKey.toLowerCase() + ".";
155 //  StringBuffer buf = new StringBuffer (service.getVersion ().length ());
156 //  for (int i = 0; i < service.getVersion ().length (); i ++)
157 //  {
158 //   char c = service.getVersion ().charAt (i);
159 //   c = Character.toLowerCase (c);
160 //   if (Character.isLetter (c))
161 //   {
162 //    buf.append (c);
163 //    continue;
164 //   }
165 //   if (Character.isDigit (c))
166 //   {
167 //    buf.append (c);
168 //   }
169 //  }
170 //  pack = pack + buf.toString ();
171         pack = pack + "service.impl.mock";
172         return pack;
173     }
174 
175     /**
176      * Checks if this is a RICE service.
177      * @return true if this is a RICE service.
178      */
179     private boolean isRice() {
180         if (this.servKey.startsWith("RICE.")) {
181             return true;
182         }
183         return false;
184     }
185 
186     protected static String fixServKey(String servKey) {
187         if (servKey.startsWith("RICE.")) {
188             return servKey.substring("RICE.".length());
189         }
190         return servKey;
191     }
192 
193     /**
194      * Given the service key (name), returns a calculated class name for the mock impl.
195      */
196     public static String calcClassName(String servKey) {
197         return GetterSetterNameCalculator.calcInitUpper(fixServKey(servKey) + "ServiceMockImpl");
198     }
199 
200     public static String calcServiceInterfaceClassName(String servKey) {
201         return GetterSetterNameCalculator.calcInitUpper(fixServKey(servKey) + "Service");
202     }
203 
204     /**
205      * Analyses the method and returns a MethodType enum that describes what type of method this is.
206      */
207     protected MethodType calcMethodType(ServiceMethod method) {
208         if (this.isRice()) {
209             if (method.getName().contains("ByNamespaceCodeAndName")) {
210                 return MethodType.RICE_GET_BY_NAMESPACE_AND_NAME;
211             }
212             if (method.getName().contains("ByNameAndNamespace")) {
213                 return MethodType.RICE_GET_BY_NAMESPACE_AND_NAME;
214             }
215             if (method.getName().startsWith("get")) {
216                 if (method.getParameters().size() == 1) {
217                     if (!method.getReturnValue().getType().endsWith("List")) {
218                         if (method.getParameters().get(0).getName().equals("id")) {
219                             return MethodType.GET_BY_ID;
220                         }
221 
222                     } else {
223                         if (method.getParameters().get(0).getName().equals("ids")) {
224                             return MethodType.GET_BY_IDS;
225                         }
226                     }
227                 }
228             }
229         }
230         if (method.getName().startsWith("validate")) {
231             return MethodType.VALIDATE;
232         }
233         if (method.getName().startsWith("create")) {
234             if (this.findInfoParameter(method) != null) {
235                 return MethodType.CREATE;
236             }
237             return MethodType.CREATE_BULK;
238         }
239         if (method.getName().startsWith("add")) {
240             return MethodType.ADD;
241         }
242         if (method.getName().startsWith("update")) {
243             if (this.findInfoParameter(method) != null) {
244                 return MethodType.UPDATE;
245             }
246             return MethodType.UPDATE_OTHER;
247         }
248         if (method.getName().startsWith("delete")) {
249             if (method.getName().contains("By")) {
250                 if (!method.getName().startsWith("deleteBy")) {
251                     return MethodType.DELETE_OTHER;
252                 }
253             }
254             if (method.getName().contains("For")) {
255                 if (!method.getName().startsWith("deleteFor")) {
256                     return MethodType.DELETE_OTHER;
257                 }
258             }
259             return MethodType.DELETE;
260         }
261         if (method.getName().startsWith("remove")) {
262             return MethodType.REMOVE;
263         }
264 
265         if (method.getName().startsWith("getCreate")) {
266             return MethodType.GET_CREATE;
267         }
268 
269         if (method.getName().startsWith("get")) {
270             if (method.getName().endsWith("ByIds")) {
271                 return MethodType.GET_BY_IDS;
272             }
273             if (method.getName().endsWith("ByType")) {
274                 return MethodType.GET_IDS_BY_TYPE;
275             }
276             if (method.getReturnValue().getType().endsWith("TypeInfo")) {
277                 return MethodType.GET_TYPE;
278             }
279             if (method.getReturnValue().getType().endsWith("TypeInfoList")) {
280                 return MethodType.GET_TYPES;
281             }
282             if (method.getName().endsWith("ByType")) {
283                 return MethodType.GET_IDS_BY_TYPE;
284             }
285             if (method.getParameters().size() >= 1 && method.getParameters().size() <= 2) {
286                 if (!method.getReturnValue().getType().endsWith("List")) {
287                     if (method.getParameters().get(0).getName().endsWith("Id")) {
288                         return MethodType.GET_BY_ID;
289                     }
290                 }
291             }
292             if (method.getName().contains("By")) {
293                 if (method.getReturnValue().getType().equals("StringList")) {
294                     return MethodType.GET_IDS_BY_OTHER;
295                 }
296                 if (method.getReturnValue().getType().endsWith("InfoList")) {
297                     return MethodType.GET_INFOS_BY_OTHER;
298                 }
299             }
300         }
301 
302         return MethodType.UNKNOWN;
303     }
304 
305     /**
306      * Write out the entire file
307      */
308     public void write() {
309         indentPrint("public class " + calcClassName(servKey));
310         println(" implements MockService, " + calcServiceInterfaceClassName(servKey));
311         importsAdd ("org.kuali.student.common.mock.MockService");
312         Service serv = finder.findService(servKey);
313         importsAdd(serv.getImplProject() + "." + serv.getName());
314         openBrace();
315         // put all the cache variables at the top
316         indentPrintln("// cache variable ");
317         indentPrintln("// The LinkedHashMap is just so the values come back in a predictable order");
318         cacheVariablesWritten.clear();
319         for (ServiceMethod method : methods) {
320             MethodType methodType = calcMethodType(method);
321             switch (methodType) {
322                 case CREATE:
323                 case ADD:
324                 case GET_TYPE:
325                 case GET_BY_ID:
326                     writeCacheVariable(method);
327             }
328         }
329         println ("");
330         indentPrintln ("@Override");
331         indentPrintln ("public void clear()");
332         openBrace ();
333         cacheVariablesWritten.clear();
334          for (ServiceMethod method : methods) {
335             MethodType methodType = calcMethodType(method);
336             switch (methodType) {
337                 case CREATE:
338                 case ADD:
339                 case GET_TYPE:
340                 case GET_BY_ID:
341                     writeCacheVariableClear(method);
342             }
343         }
344         closeBrace ();
345         println ("");
346         
347         for (ServiceMethod method : methods) {
348             MethodType methodType = calcMethodType(method);
349             indentPrintln("");
350 //            indentPrintln("/**");
351 //            indentPrintWrappedComment(method.getDescription());
352 //            indentPrintln("* ");
353 //            for (ServiceMethodParameter param : method.getParameters()) {
354 //                indentPrintWrappedComment("@param " + param.getName() + " - "
355 //                        + param.getType() + " - "
356 //                        + param.getDescription());
357 //            }
358 //            indentPrintWrappedComment("@return " + method.getReturnValue().
359 //                    getDescription());
360 //            indentPrintln("*/");
361             indentPrintln("@Override");
362             String type = method.getReturnValue().getType();
363             String realType = stripList(type);
364             indentPrint("public " + calcType(type, realType) + " " + method.getName()
365                     + "(");
366             // now do parameters
367             String comma = "";
368             for (ServiceMethodParameter param : method.getParameters()) {
369                 type = param.getType();
370                 realType = stripList(type);
371                 print(comma);
372                 
373                 String calculatedType = realType;
374                 
375                 try {
376                 		
377                 	calculatedType = calcType(type, realType);
378                 	
379                 }
380                 catch (Exception e) {
381                 	log.warn(String.format("Method (%s) : failed to calculate type (%s), real type (%s)", method.getName(), type, realType), e);
382                 }
383                 
384                 print(calculatedType);
385                 
386                 print(" ");
387                 print(param.getName());
388                 comma = ", ";
389             }
390             println(")");
391             // now do exceptions
392             comma = "throws ";
393             incrementIndent();
394             for (ServiceMethodError error : method.getErrors()) {
395                 indentPrint(comma);
396                 String exceptionClassName = calcExceptionClassName(error);
397                 String exceptionPackageName = this.calcExceptionPackageName(error);
398                 println(exceptionClassName);
399                 this.importsAdd(exceptionPackageName + "." + exceptionClassName);
400                 comma = "      ,";
401             }
402             decrementIndent();
403             openBrace();
404             indentPrintln("// " + methodType);
405             switch (methodType) {
406                 case VALIDATE:
407                     writeValidate(method);
408                     break;
409                 case CREATE:
410                     writeCreate(method);
411                     break;
412                 case ADD:
413                     writeAdd(method);
414                     break;
415                 case UPDATE:
416                     writeUpdate(method);
417                     break;
418                 case DELETE:
419                     writeDelete(method);
420                     break;
421                 case REMOVE:
422                     writeRemove(method);
423                     break;
424                 case GET_BY_ID:
425                     writeGetById(method);
426                     break;
427                 case GET_BY_IDS:
428                     writeGetByIds(method);
429                     break;
430                 case GET_IDS_BY_TYPE:
431                     writeGetIdsByType(method);
432                     break;
433                 case GET_IDS_BY_OTHER:
434                     writeGetIdsByOther(method);
435                     break;
436                 case GET_INFOS_BY_OTHER:
437                     writeGetInfosByOther(method);
438                     break;
439                 case GET_TYPE:
440                     writeGetType(method);
441                     break;
442                 case GET_TYPES:
443                     writeGetTypes(method);
444                     break;
445                 case RICE_GET_BY_NAMESPACE_AND_NAME:
446                     writeRiceGetByNamespaceAndName(method);
447                     break;
448                 default:
449                     writeThrowsNotImplemented(method);
450             }
451             closeBrace();
452         	
453         }
454 
455         writeBoilerPlate();
456         closeBrace();
457 
458         this.writeJavaClassAndImportsOutToFile();
459         this.getOut().close();
460     }
461 
462     private String getInvalidParameterException() {
463         if (this.isRice()) {
464             return "RiceIllegalArgumentException";
465         }
466         return "InvalidParameterException";
467     }
468 
469     private String getOperationFailedException() {
470         if (this.isRice()) {
471             return "RiceIllegalArgumentException";
472         }
473         return "OperationFailedException";
474     }
475 
476     private String getDoesNotExistException() {
477         if (this.isRice()) {
478             return "RiceIllegalArgumentException";
479         }
480         return "DoesNotExistException";
481     }
482 
483     private String getVersionMismatchException() {
484         if (this.isRice()) {
485             return "RiceIllegalStateException";
486         }
487         return "VersionMismatchException";
488     }
489 
490     private void writeThrowsNotImplemented(ServiceMethod method) {
491         indentPrintln("throw new " + this.getOperationFailedException() + " (\"" + method.getName() + " has not been implemented\");");
492     }
493 
494     protected String initLower(String str) {
495         return str.substring(0, 1).toLowerCase() + str.substring(1);
496     }
497 
498     private void writeBoilerPlate() {
499         if (this.isRice()) {
500             return;
501         }
502         indentPrintln("");
503         indentPrintln("private StatusInfo newStatus() {");
504         indentPrintln("     StatusInfo status = new StatusInfo();");
505         indentPrintln("     status.setSuccess(Boolean.TRUE);");
506         indentPrintln("     return status;");
507         indentPrintln("}");
508         if (isR1) {
509             this.writeBoilerPlateR1();
510         } else {
511             this.writeBoilerPlateR2();
512         }
513     }
514 
515     private void writeBoilerPlateR1() {
516         importsAdd("org.kuali.student.common.dto.MetaInfo");
517         indentPrintln("");
518 
519         indentPrintln("private MetaInfo newMeta() {");
520         indentPrintln("     MetaInfo meta = new MetaInfo();");
521         indentPrintln("     meta.setCreateId(\"MOCKUSER\");");
522         importsAdd(Date.class.getName());
523         indentPrintln("     meta.setCreateTime(new Date());");
524         indentPrintln("     meta.setUpdateId(\"MOCKUSER\");");
525         indentPrintln("     meta.setUpdateTime(meta.getCreateTime());");
526         indentPrintln("     meta.setVersionInd(\"0\");");
527         indentPrintln("     return meta;");
528         indentPrintln("}");
529         indentPrintln("");
530         indentPrintln("private MetaInfo updateMeta(MetaInfo meta) {");
531         indentPrintln("     meta.setUpdateId(\"MOCKUSER\");");
532         indentPrintln("     meta.setUpdateTime(new Date());");
533         indentPrintln("     meta.setVersionInd((Integer.parseInt(meta.getVersionInd()) + 1) + \"\");");
534         indentPrintln("     return meta;");
535         indentPrintln("}");
536         indentPrintln("");
537 
538     }
539 
540     private void writeBoilerPlateR2() {
541         importsAdd("org.kuali.student.r2.common.dto.MetaInfo");
542         indentPrintln("");
543 
544         indentPrintln("private MetaInfo newMeta(ContextInfo context) {");
545         indentPrintln("     MetaInfo meta = new MetaInfo();");
546         indentPrintln("     meta.setCreateId(context.getPrincipalId());");
547         importsAdd(Date.class.getName());
548         indentPrintln("     meta.setCreateTime(new Date());");
549         indentPrintln("     meta.setUpdateId(context.getPrincipalId());");
550         indentPrintln("     meta.setUpdateTime(meta.getCreateTime());");
551         indentPrintln("     meta.setVersionInd(\"0\");");
552         indentPrintln("     return meta;");
553         indentPrintln("}");
554         indentPrintln("");
555         indentPrintln("private MetaInfo updateMeta(MetaInfo old, ContextInfo context) {");
556         indentPrintln("     MetaInfo meta = new MetaInfo(old);");
557         indentPrintln("     meta.setUpdateId(context.getPrincipalId());");
558         indentPrintln("     meta.setUpdateTime(new Date());");
559         indentPrintln("     meta.setVersionInd((Integer.parseInt(meta.getVersionInd()) + 1) + \"\");");
560         indentPrintln("     return meta;");
561         indentPrintln("}");
562         indentPrintln("");
563 
564     }
565 
566     private void writeValidate(ServiceMethod method) {
567         indentPrintln("return new ArrayList<ValidationResultInfo> ();");
568         this.importsAdd(ArrayList.class.getName());
569     }
570     private Set<String> cacheVariablesWritten = new HashSet<String>();
571 
572     private void writeCacheVariable(ServiceMethod method) {
573         String objectName = calcObjectName(method);
574         if (!this.isRice()) {
575             objectName = objectName + "Info";
576         }
577         String mapName = calcMapName(method);
578         if (cacheVariablesWritten.add(mapName)) {
579             indentPrintln("private Map<String, " + objectName + "> " + mapName + " = new LinkedHashMap<String, " + objectName + ">();");
580             importsAdd(Map.class.getName());
581             importsAdd(LinkedHashMap.class.getName());
582         }
583     }
584 
585     private void writeCacheVariableClear(ServiceMethod method) {
586         String objectName = calcObjectName(method);
587         if (!this.isRice()) {
588             objectName = objectName + "Info";
589         }
590         String mapName = calcMapName(method);
591         if (cacheVariablesWritten.add(mapName)) {
592             indentPrintln("this." + mapName + ".clear ();");
593         }
594     }
595 
596     private void writeCreate(ServiceMethod method) {
597         ServiceMethodParameter typeParam = this.findTypeParameter(method);
598         ServiceMethodParameter infoParam = this.findInfoParameter(method);
599         ServiceMethodParameter contextParam = this.findContextParameter(method);
600         String objectName = calcObjectName(method);
601         String infoName = objectName;
602         if (!this.isRice()) {
603             infoName = infoName + "Info";
604         }
605         String mapName = calcMapName(method);
606 
607         if (this.isRice()) {
608             indentPrintln(infoName + " orig = this.get" + infoName + "ByNamespaceCodeAndName(" + infoParam.getName() + ".getNamespaceCode(), " + infoParam.getName() + ".getName());");
609             indentPrintln("if (orig != null)");
610             openBrace();
611             indentPrintln("throw new RiceIllegalArgumentException (" + infoParam.getName() + ".getNamespaceCode() + \".\" + " + infoParam.getName() + ".getName());");
612             closeBrace();
613         }
614         if (typeParam != null) {
615             indentPrintln("if (!" + typeParam.getName() + ".equals (" + infoParam.getName() + ".getTypeKey())) {");
616             indentPrintln("    throw new " + this.getInvalidParameterException() + " (\"The type parameter does not match the type on the info object\");");
617             indentPrintln("}");
618         }
619         if (method.getParameters().size() > 3) {
620             indentPrintln("// TODO: check the rest of the readonly fields that are specified on the create to make sure they match the info object");
621         }
622         if (this.isR1) {
623             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
624             indentPrintln(infoName + " copy = " + infoParam.getName() + ";");
625         } else if (this.isRice()) {
626             indentPrintln(infoName + ".Builder copy = " + infoName + ".Builder.create (" + infoParam.getName() + ");");
627         } else {
628             indentPrintln(infoName + " copy = new " + infoName + "(" + infoParam.getName() + ");");
629         }
630         indentPrintln("if (copy.getId() == null) {");
631         // indentPrintln("   copy.setId(" + mapName + ".size() + \"\");");
632         importsAdd("org.kuali.student.common.util.UUIDHelper");
633         indentPrintln("   copy.setId(UUIDHelper.genStringUUID());");
634         indentPrintln("}");
635         if (contextParam != null) {
636             indentPrintln("copy.setMeta(newMeta(" + contextParam.getName() + "));");
637         }
638         if (isR1) {
639             indentPrintln(mapName + ".put(copy.getId(), copy);");
640             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
641             indentPrintln("return copy;");
642         } else if (this.isRice()) {
643             indentPrintln (infoParam.getName() + " = copy.build ();");
644             indentPrintln(mapName + ".put(" + infoParam.getName() + ".getId(), " + infoParam.getName() + ");");
645             indentPrintln("return " + infoParam.getName() + ";");
646         } else {
647             indentPrintln(mapName + ".put(copy.getId(), copy);");
648             indentPrintln("return new " + infoName + "(copy);");
649         }
650     }
651 
652     private void writeAdd(ServiceMethod method) {
653         ServiceMethodParameter typeParam = this.findTypeParameter(method);
654         ServiceMethodParameter infoParam = this.findInfoParameter(method);
655         ServiceMethodParameter contextParam = this.findContextParameter(method);
656         String objectName = calcObjectName(method);
657         String infoName = objectName;
658         if (!this.isRice()) {
659             infoName = infoName + "Info";
660         }
661         String mapName = calcMapName(method);
662 
663         if (typeParam != null) {
664             indentPrintln("if (!" + typeParam.getName() + ".equals (" + infoParam.getName() + ".getTypeKey())) {");
665             indentPrintln("    throw new " + this.getInvalidParameterException() + " (\"The type parameter does not match the type on the info object\");");
666             indentPrintln("}");
667         }
668         if (method.getParameters().size() > 3) {
669             indentPrintln("// TODO: check the rest of the readonly fields that are specified on the create to make sure they match the info object");
670         }
671         if (isR1) {
672             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
673             indentPrintln(infoName + " copy = " + infoParam.getName() + ";");
674         } else if (this.isRice()) {
675             indentPrintln(infoName + ".Builder copy = " + infoName + ".Builder.create (" + infoParam.getName() + ");");
676         } else {
677             indentPrintln(infoName + " copy = new " + infoName + "(" + infoParam.getName() + ");");
678         }
679         indentPrintln("if (copy.getId() == null) {");
680         // indentPrintln("   copy.setId(" + mapName + ".size() + \"\");");
681         importsAdd("org.kuali.student.common.util.UUIDHelper");
682         indentPrintln("   copy.setId(UUIDHelper.genStringUUID());");
683         indentPrintln("}");
684         if (contextParam != null) {
685             indentPrintln("copy.setMeta(newMeta(" + contextParam.getName() + "));");
686         }
687         if (isR1) {
688             indentPrintln(mapName + ".put(copy.getId(), copy);");
689             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
690             indentPrintln("return copy;");
691         } else if (this.isRice()) {
692             indentPrintln (infoParam.getName() + " = copy.build ();");
693             indentPrintln(mapName + ".put(" + infoParam.getName() + ".getId(), " + infoParam.getName() + ");");
694             indentPrintln("return " + infoParam.getName() + ";");
695         } else {
696             indentPrintln(mapName + ".put(copy.getId(), copy);");
697             indentPrintln("return new " + infoName + "(copy);");
698         }
699     }
700 
701     private ServiceMethodParameter findIdParameter(ServiceMethod method) {
702         String idFieldName = calcObjectName(method) + "Id";
703         for (ServiceMethodParameter parameter : method.getParameters()) {
704             if (parameter.getType().equals("String")) {
705                 if (parameter.getName().equals(idFieldName)) {
706                     return parameter;
707                 }
708             }
709         }
710 
711         // if only one parameter and it is a string then grab that
712         if (method.getParameters().size() == 1) {
713             for (ServiceMethodParameter parameter : method.getParameters()) {
714                 if (parameter.getType().equals("String")) {
715                     return parameter;
716                 }
717             }
718         }
719         // can't find name exactly 
720         for (ServiceMethodParameter parameter : method.getParameters()) {
721             if (parameter.getType().equals("String")) {
722                 if (parameter.getName().endsWith("Id")) {
723                     return parameter;
724                 }
725             }
726         }
727         // can't find name exactly try key 
728         for (ServiceMethodParameter parameter : method.getParameters()) {
729             if (parameter.getType().equals("String")) {
730                 if (!parameter.getName().endsWith("TypeKey")) {
731                     if (parameter.getName().endsWith("Key")) {
732                         return parameter;
733                     }
734                 }
735             }
736         }
737         log.warn("Could not find the Id paramter for " + method.getService() + "." + method.getName() + " so returning the first one");
738         return method.getParameters().get(0);
739     }
740 
741     private ServiceMethodParameter findContextParameter(ServiceMethod method) {
742         for (ServiceMethodParameter parameter : method.getParameters()) {
743             if (parameter.getType().equals("ContextInfo")) {
744                 return parameter;
745             }
746         }
747         return null;
748     }
749 
750     private ServiceMethodParameter findInfoParameter(ServiceMethod method) {
751         String objectName = calcObjectName(method);
752         if (!this.isRice()) {
753             objectName = objectName + "Info";
754         }
755         for (ServiceMethodParameter parameter : method.getParameters()) {
756             if (parameter.getType().equals(objectName)) {
757                 return parameter;
758             }
759         }
760         if (method.getParameters().size() >= 1) {
761             return method.getParameters().get(0);
762         }
763         return null;
764     }
765 
766     private ServiceMethodParameter findTypeParameter(ServiceMethod method) {
767         for (ServiceMethodParameter parameter : method.getParameters()) {
768             if (parameter.getType().equals("String")) {
769                 if (parameter.getName().endsWith("TypeKey")) {
770                     return parameter;
771                 }
772                 if (parameter.getName().endsWith("Type")) {
773                     return parameter;
774                 }
775             }
776         }
777         return null;
778     }
779 
780     private String calcMapName(ServiceMethod method) {
781         String mapName = this.calcObjectName(method);
782         mapName = this.initLower(mapName) + "Map";
783         return mapName;
784     }
785 
786     protected String calcObjectName(ServiceMethod method) {
787         if (method.getName().startsWith("create")) {
788             return method.getName().substring("create".length());
789         }
790         if (method.getName().startsWith("update")) {
791             return method.getName().substring("update".length());
792         }
793         if (method.getName().startsWith("validate")) {
794             return method.getName().substring("validate".length());
795         }
796         if (method.getName().startsWith("delete")) {
797             return method.getName().substring("delete".length());
798         }
799         if (method.getName().startsWith("get")) {
800             if (method.getReturnValue().getType().equals("StringList")) {
801                 if (method.getName().contains("IdsBy")) {
802                     return method.getName().substring("get".length(),
803                             method.getName().indexOf("IdsBy"));
804                 }
805                 if (method.getName().contains("IdsFor")) {
806                     return method.getName().substring("get".length(),
807                             method.getName().indexOf("IdsFor"));
808                 }
809                 return method.getName().substring("get".length());
810             }
811             String name = method.getReturnValue().getType();
812             if (name.endsWith("List")) {
813                 name = name.substring(0, name.length() - "List".length());
814             }
815             if (name.endsWith("Info")) {
816                 name = name.substring(0, name.length() - "Info".length());
817             }
818             return name;
819         }
820         if (method.getName().startsWith("add")) {
821             return method.getName().substring("add".length());
822         }
823         if (method.getName().startsWith("remove")) {
824             return method.getName().substring("remove".length());
825         }
826         String returnType = this.stripList(method.getReturnValue().getType());
827         XmlType type = this.finder.findXmlType(returnType);
828         if (type.getPrimitive().equals(XmlType.COMPLEX)) {
829             return returnType;
830         }
831         throw new IllegalArgumentException(method.getName());
832     }
833 
834     private void writeUpdate(ServiceMethod method) {
835         ServiceMethodParameter idParam = this.findIdParameter(method);
836         ServiceMethodParameter infoParam = this.findInfoParameter(method);
837         ServiceMethodParameter contextParam = this.findContextParameter(method);
838         if (infoParam == null) {
839             throw new NullPointerException(method.getName());
840         }
841         String objectName = calcObjectName(method);
842         String infoName = objectName;
843         if (!this.isRice()) {
844             infoName = infoName + "Info";
845         }
846         String mapName = calcMapName(method);
847         if (idParam != null) {
848             if (!this.isRice()) {
849                 indentPrintln("if (!" + idParam.getName() + ".equals (" + infoParam.getName() + ".getId())) {");
850                 indentPrintln("    throw new " + this.getInvalidParameterException() + " (\"The id parameter does not match the id on the info object\");");
851                 indentPrintln("}");
852             }
853         }
854         if (isR1) {
855             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
856             indentPrintln(infoName + " copy = " + infoParam.getName() + ";");
857         } else if (this.isRice()) {
858             indentPrintln(infoName + ".Builder copy = " + infoName + ".Builder.create (" + infoParam.getName() + ");");
859         } else {
860             indentPrintln(infoName + " copy = new " + infoName + "(" + infoParam.getName() + ");");
861         }
862         if (contextParam != null) {
863             indentPrintln(infoName + " old = this.get" + objectName + "(" + infoParam.getName() + ".getId(), " + contextParam.getName() + ");");
864         } else {
865             indentPrintln(infoName + " old = this.get" + objectName + "(" + infoParam.getName() + ".getId());");
866         }
867         if (isR1) {
868             indentPrintln("if (!old.getMetaInfo().getVersionInd().equals(copy.getMetaInfo().getVersionInd())) {");
869             indentPrintln("    throw new " + this.getVersionMismatchException() + "(old.getMetaInfo().getVersionInd());");
870             indentPrintln("}");
871             if (contextParam != null) {
872                 indentPrintln("copy.setMeta(updateMeta(copy.getMetaInfo()));");
873             }
874         } else if (this.isRice()) {
875             indentPrintln("if (!old.getVersionNumber().equals(copy.getVersionNumber())) {");
876             indentPrintln("    throw new " + this.getVersionMismatchException() + "(\"\" + old.getVersionNumber());");
877             indentPrintln("}");
878             indentPrintln("copy.setVersionNumber(copy.getVersionNumber() + 1);");
879         } else {
880             indentPrintln("if (!old.getMeta().getVersionInd().equals(copy.getMeta().getVersionInd())) {");
881             indentPrintln("    throw new " + this.getVersionMismatchException() + "(old.getMeta().getVersionInd());");
882             indentPrintln("}");
883             if (contextParam != null) {
884                 indentPrintln("copy.setMeta(updateMeta(copy.getMeta(), contextInfo));");
885             }
886         }
887         if (isR1) {
888             indentPrintln("this." + mapName + " .put(" + infoParam.getName() + ".getId(), copy);");
889             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
890             indentPrintln("return copy;");
891         } else if (this.isRice()) {
892             indentPrintln (infoParam.getName() + " = copy.build ();");
893             indentPrintln("this." + mapName + " .put(" + infoParam.getName() + ".getId(), " + infoParam.getName() + ");");
894             indentPrintln("return " + infoParam.getName() + ";");
895         } else {
896             indentPrintln("this." + mapName + " .put(" + infoParam.getName() + ".getId(), copy);");
897             indentPrintln("return new " + infoName + "(copy);");
898         }
899 
900     }
901 
902     private void writeDelete(ServiceMethod method) {
903         ServiceMethodParameter idParam = this.findIdParameter(method);
904         String mapName = calcMapName(method);
905         indentPrintln("if (this." + mapName + ".remove(" + idParam.getName() + ") == null) {");
906         indentPrintln("   throw new " + this.getOperationFailedException() + "(" + idParam.getName() + ");");
907         indentPrintln("}");
908         indentPrintln("return newStatus();");
909     }
910 
911     private void writeRemove(ServiceMethod method) {
912         ServiceMethodParameter idParam = this.findIdParameter(method);
913         String mapName = calcMapName(method);
914         indentPrintln("if (this." + mapName + ".remove(" + idParam.getName() + ") == null) {");
915         indentPrintln("   throw new " + this.getOperationFailedException() + "(" + idParam.getName() + ");");
916         indentPrintln("}");
917         indentPrintln("return newStatus();");
918     }
919 
920     private void writeGetById(ServiceMethod method) {
921         ServiceMethodParameter idParam = this.findIdParameter(method);
922         String mapName = calcMapName(method);
923         indentPrintln("if (!this." + mapName + ".containsKey(" + idParam.getName() + ")) {");
924         indentPrintln("   throw new " + this.getDoesNotExistException() + "(" + idParam.getName() + ");");
925         indentPrintln("}");
926         String objectName = calcObjectName(method);
927         String infoName = objectName;
928         if (!this.isRice()) {
929             infoName = infoName + "Info";
930         }
931         if (isR1) {
932             indentPrintln("// r1 objects do not have deep cody");
933             indentPrintln("return this." + mapName + ".get (" + idParam.getName() + ");");
934         } else if (this.isRice()) {
935             indentPrintln("return this." + mapName + ".get (" + idParam.getName() + ");");
936         } else {
937             indentPrintln("return new " + infoName + "(this." + mapName + ".get (" + idParam.getName() + "));");
938         }
939 
940     }
941 
942     private void writeGetByIds(ServiceMethod method) {
943         String objectName = this.calcObjectName(method);
944         String infoName = objectName;
945         if (!this.isRice()) {
946             infoName = infoName + "Info";
947         }
948         ServiceMethodParameter idListParam = this.findIdListParameter(method);
949         ServiceMethodParameter contextParam = this.findContextParameter(method);
950         this.importsAdd(ArrayList.class.getName());
951         indentPrintln("List<" + infoName + "> list = new ArrayList<" + infoName + "> ();");
952 
953         indentPrintln("for (String id: " + idListParam.getName() + ") {");
954         if (this.isRice()) {
955             indentPrintln("    list.add (this.get" + objectName + "(id));");
956         } else {
957             indentPrintln("    list.add (this.get" + objectName + "(id, " + contextParam.getName() + "));");
958         }
959         indentPrintln("}");
960         indentPrintln("return list;");
961     }
962 
963     private void writeGetIdsByOther(ServiceMethod method) {
964         String objectName = this.calcObjectName(method);
965         String infoName = objectName;
966         if (!this.isRice()) {
967             infoName = infoName + "Info";
968         }
969         String mapName = calcMapName(method);
970         this.importsAdd(ArrayList.class.getName());
971         indentPrintln("List<String> list = new ArrayList<String> ();");
972         indentPrintln("for (" + infoName + " info: " + mapName + ".values ()) {");
973         for (ServiceMethodParameter parameter : method.getParameters()) {
974             if (parameter.getType().equals("ContextInfo")) {
975                 continue;
976             }
977             incrementIndent();
978             indentPrintln("if (" + parameter.getName() + ".equals(info.get" + initUpper(parameter.getName()) + "())) {");
979         }
980         indentPrintln("    list.add (info.getId ());");
981         for (ServiceMethodParameter parameter : method.getParameters()) {
982             if (parameter.getType().equals("ContextInfo")) {
983                 continue;
984             }
985             indentPrintln("}");
986             decrementIndent();
987         }
988         indentPrintln("}");
989         indentPrintln("return list;");
990     }
991 
992     private void writeGetIdsByType(ServiceMethod method) {
993         String objectName = this.calcObjectName(method);
994         String infoName = objectName;
995         if (!this.isRice()) {
996             infoName = infoName + "Info";
997         }
998         String mapName = calcMapName(method);
999         this.importsAdd(ArrayList.class.getName());
1000         indentPrintln("List<String> list = new ArrayList<String> ();");
1001         indentPrintln("for (" + infoName + " info: " + mapName + ".values ()) {");
1002         for (ServiceMethodParameter parameter : method.getParameters()) {
1003             if (parameter.getType().equals("ContextInfo")) {
1004                 continue;
1005             }
1006             incrementIndent();
1007             indentPrintln("if (" + parameter.getName() + ".equals(info.getTypeKey())) {");
1008         }
1009         indentPrintln("    list.add (info.getId ());");
1010         for (ServiceMethodParameter parameter : method.getParameters()) {
1011             if (parameter.getType().equals("ContextInfo")) {
1012                 continue;
1013             }
1014             indentPrintln("}");
1015             decrementIndent();
1016         }
1017         indentPrintln("}");
1018         indentPrintln("return list;");
1019     }
1020 
1021     private void writeRiceGetByNamespaceAndName(ServiceMethod method) {
1022         String objectName = this.calcObjectName(method);
1023         String infoName = objectName;
1024         if (!this.isRice()) {
1025             infoName = infoName + "Info";
1026         }
1027         String mapName = calcMapName(method);
1028         indentPrintln("for (" + infoName + " info: " + mapName + ".values ()) {");
1029         for (ServiceMethodParameter parameter : method.getParameters()) {
1030             incrementIndent();
1031             indentPrintln("if (" + parameter.getName() + ".equals(info.get" + initUpper(parameter.getName()) + "())) {");
1032         }
1033         indentPrintln("    return " + infoName + ".Builder.create (info).build ();");
1034         for (ServiceMethodParameter parameter : method.getParameters()) {
1035             indentPrintln("}");
1036             decrementIndent();
1037         }
1038         indentPrintln("}");
1039         indentPrintln("throw new RiceIllegalArgumentException ();");
1040     }
1041 
1042     private void writeGetInfosByOther(ServiceMethod method) {
1043         String objectName = this.calcObjectName(method);
1044         String infoName = objectName;
1045         if (!this.isRice()) {
1046             infoName = infoName + "Info";
1047         }
1048         String mapName = calcMapName(method);
1049         this.importsAdd(ArrayList.class.getName());
1050         indentPrintln("List<" + infoName + "> list = new ArrayList<" + infoName + "> ();");
1051         indentPrintln("for (" + infoName + " info: " + mapName + ".values ()) {");
1052         for (ServiceMethodParameter parameter : method.getParameters()) {
1053             if (parameter.getType().equals("ContextInfo")) {
1054                 continue;
1055             }
1056             incrementIndent();
1057             indentPrintln("if (" + parameter.getName() + ".equals(info.get" + initUpper(parameter.getName()) + "())) {");
1058         }
1059         indentPrintln("    list.add (new " + infoName + "(info));");
1060         for (ServiceMethodParameter parameter : method.getParameters()) {
1061             if (parameter.getType().equals("ContextInfo")) {
1062                 continue;
1063             }
1064             indentPrintln("}");
1065             decrementIndent();
1066         }
1067         indentPrintln("}");
1068         indentPrintln("return list;");
1069     }
1070 
1071     private void writeGetType(ServiceMethod method) {
1072         ServiceMethodParameter idParam = this.findIdParameter(method);
1073         String mapName = calcMapName(method);
1074         indentPrintln("if (!this." + mapName + ".containsKey(" + idParam.getName() + ")) {");
1075         indentPrintln("   throw new " + this.getOperationFailedException() + "(" + idParam.getName() + ");");
1076         indentPrintln("}");
1077         indentPrintln("return this." + mapName + ".get (" + idParam.getName() + ");");
1078     }
1079 
1080     private void writeGetTypes(ServiceMethod method) {
1081         String mapName = calcMapName(method);
1082         String objectName = this.calcObjectName(method);
1083         String infoName = objectName;
1084         if (!this.isRice()) {
1085             infoName = infoName + "Info";
1086         }
1087         this.importsAdd(ArrayList.class.getName());
1088         indentPrintln("return new ArrayList<" + infoName + ">(" + mapName + ".values ());");
1089     }
1090 
1091     private String initUpper(String str) {
1092         return str.substring(0, 1).toUpperCase() + str.substring(1);
1093     }
1094 
1095     private ServiceMethodParameter findIdListParameter(ServiceMethod method) {
1096         String idFieldName = calcObjectName(method) + "Ids";
1097         if (this.isRice()) {
1098             idFieldName = "ids";
1099         }
1100         for (ServiceMethodParameter parameter : method.getParameters()) {
1101             if (parameter.getType().equals("StringList")) {
1102                 if (parameter.getName().equals(idFieldName)) {
1103                     return parameter;
1104                 }
1105             }
1106         }
1107         // can't find name exactly 
1108         for (ServiceMethodParameter parameter : method.getParameters()) {
1109             if (parameter.getType().equals("StringList")) {
1110                 if (parameter.getName().endsWith("Ids")) {
1111                     return parameter;
1112                 }
1113             }
1114         }
1115         // can't find name exactly try key 
1116         for (ServiceMethodParameter parameter : method.getParameters()) {
1117             if (parameter.getType().equals("StringList")) {
1118                 if (parameter.getName().endsWith("Keys")) {
1119                     return parameter;
1120                 }
1121             }
1122         }
1123         return null;
1124     }
1125 
1126     private String stripList(String str) {
1127         return GetterSetterNameCalculator.stripList(str);
1128     }
1129 
1130     private String calcExceptionClassName(ServiceMethodError error) {
1131         if (error.getClassName() == null) {
1132             return ServiceExceptionWriter.calcClassName(error.getType());
1133         }
1134         return error.getClassName();
1135     }
1136 
1137     private String calcExceptionPackageName(ServiceMethodError error) {
1138         if (error.getClassName() == null) {
1139             return ServiceExceptionWriter.calcPackage(rootPackage);
1140         }
1141         return error.getPackageName();
1142     }
1143 
1144     private String calcType(String type, String realType) {
1145         XmlType t = finder.findXmlType(this.stripList(type));
1146         String retType = MessageStructureTypeCalculator.calculate(this, model, type, realType,
1147                 t.getJavaPackage());
1148         if (this.isRice()) {
1149             if (retType.equals("Boolean")) {
1150                 retType = "boolean";
1151             }
1152             if (retType.equals("Void")) {
1153                 retType = "void";
1154             }
1155         }
1156         return retType;
1157     }
1158 }