View Javadoc

1   /*
2    * Copyright 2009 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.osedu.org/licenses/ECL-2.0
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                 print(calcType(type, realType));
373                 print(" ");
374                 print(param.getName());
375                 comma = ", ";
376             }
377             println(")");
378             // now do exceptions
379             comma = "throws ";
380             incrementIndent();
381             for (ServiceMethodError error : method.getErrors()) {
382                 indentPrint(comma);
383                 String exceptionClassName = calcExceptionClassName(error);
384                 String exceptionPackageName = this.calcExceptionPackageName(error);
385                 println(exceptionClassName);
386                 this.importsAdd(exceptionPackageName + "." + exceptionClassName);
387                 comma = "      ,";
388             }
389             decrementIndent();
390             openBrace();
391             indentPrintln("// " + methodType);
392             switch (methodType) {
393                 case VALIDATE:
394                     writeValidate(method);
395                     break;
396                 case CREATE:
397                     writeCreate(method);
398                     break;
399                 case ADD:
400                     writeAdd(method);
401                     break;
402                 case UPDATE:
403                     writeUpdate(method);
404                     break;
405                 case DELETE:
406                     writeDelete(method);
407                     break;
408                 case REMOVE:
409                     writeRemove(method);
410                     break;
411                 case GET_BY_ID:
412                     writeGetById(method);
413                     break;
414                 case GET_BY_IDS:
415                     writeGetByIds(method);
416                     break;
417                 case GET_IDS_BY_TYPE:
418                     writeGetIdsByType(method);
419                     break;
420                 case GET_IDS_BY_OTHER:
421                     writeGetIdsByOther(method);
422                     break;
423                 case GET_INFOS_BY_OTHER:
424                     writeGetInfosByOther(method);
425                     break;
426                 case GET_TYPE:
427                     writeGetType(method);
428                     break;
429                 case GET_TYPES:
430                     writeGetTypes(method);
431                     break;
432                 case RICE_GET_BY_NAMESPACE_AND_NAME:
433                     writeRiceGetByNamespaceAndName(method);
434                     break;
435                 default:
436                     writeThrowsNotImplemented(method);
437             }
438             closeBrace();
439         }
440 
441         writeBoilerPlate();
442         closeBrace();
443 
444         this.writeJavaClassAndImportsOutToFile();
445         this.getOut().close();
446     }
447 
448     private String getInvalidParameterException() {
449         if (this.isRice()) {
450             return "RiceIllegalArgumentException";
451         }
452         return "InvalidParameterException";
453     }
454 
455     private String getOperationFailedException() {
456         if (this.isRice()) {
457             return "RiceIllegalArgumentException";
458         }
459         return "OperationFailedException";
460     }
461 
462     private String getDoesNotExistException() {
463         if (this.isRice()) {
464             return "RiceIllegalArgumentException";
465         }
466         return "DoesNotExistException";
467     }
468 
469     private String getVersionMismatchException() {
470         if (this.isRice()) {
471             return "RiceIllegalStateException";
472         }
473         return "VersionMismatchException";
474     }
475 
476     private void writeThrowsNotImplemented(ServiceMethod method) {
477         indentPrintln("throw new " + this.getOperationFailedException() + " (\"" + method.getName() + " has not been implemented\");");
478     }
479 
480     protected String initLower(String str) {
481         return str.substring(0, 1).toLowerCase() + str.substring(1);
482     }
483 
484     private void writeBoilerPlate() {
485         if (this.isRice()) {
486             return;
487         }
488         indentPrintln("");
489         indentPrintln("private StatusInfo newStatus() {");
490         indentPrintln("     StatusInfo status = new StatusInfo();");
491         indentPrintln("     status.setSuccess(Boolean.TRUE);");
492         indentPrintln("     return status;");
493         indentPrintln("}");
494         if (isR1) {
495             this.writeBoilerPlateR1();
496         } else {
497             this.writeBoilerPlateR2();
498         }
499     }
500 
501     private void writeBoilerPlateR1() {
502         importsAdd("org.kuali.student.common.dto.MetaInfo");
503         indentPrintln("");
504 
505         indentPrintln("private MetaInfo newMeta() {");
506         indentPrintln("     MetaInfo meta = new MetaInfo();");
507         indentPrintln("     meta.setCreateId(\"MOCKUSER\");");
508         importsAdd(Date.class.getName());
509         indentPrintln("     meta.setCreateTime(new Date());");
510         indentPrintln("     meta.setUpdateId(\"MOCKUSER\");");
511         indentPrintln("     meta.setUpdateTime(meta.getCreateTime());");
512         indentPrintln("     meta.setVersionInd(\"0\");");
513         indentPrintln("     return meta;");
514         indentPrintln("}");
515         indentPrintln("");
516         indentPrintln("private MetaInfo updateMeta(MetaInfo meta) {");
517         indentPrintln("     meta.setUpdateId(\"MOCKUSER\");");
518         indentPrintln("     meta.setUpdateTime(new Date());");
519         indentPrintln("     meta.setVersionInd((Integer.parseInt(meta.getVersionInd()) + 1) + \"\");");
520         indentPrintln("     return meta;");
521         indentPrintln("}");
522         indentPrintln("");
523 
524     }
525 
526     private void writeBoilerPlateR2() {
527         importsAdd("org.kuali.student.r2.common.dto.MetaInfo");
528         indentPrintln("");
529 
530         indentPrintln("private MetaInfo newMeta(ContextInfo context) {");
531         indentPrintln("     MetaInfo meta = new MetaInfo();");
532         indentPrintln("     meta.setCreateId(context.getPrincipalId());");
533         importsAdd(Date.class.getName());
534         indentPrintln("     meta.setCreateTime(new Date());");
535         indentPrintln("     meta.setUpdateId(context.getPrincipalId());");
536         indentPrintln("     meta.setUpdateTime(meta.getCreateTime());");
537         indentPrintln("     meta.setVersionInd(\"0\");");
538         indentPrintln("     return meta;");
539         indentPrintln("}");
540         indentPrintln("");
541         indentPrintln("private MetaInfo updateMeta(MetaInfo old, ContextInfo context) {");
542         indentPrintln("     MetaInfo meta = new MetaInfo(old);");
543         indentPrintln("     meta.setUpdateId(context.getPrincipalId());");
544         indentPrintln("     meta.setUpdateTime(new Date());");
545         indentPrintln("     meta.setVersionInd((Integer.parseInt(meta.getVersionInd()) + 1) + \"\");");
546         indentPrintln("     return meta;");
547         indentPrintln("}");
548         indentPrintln("");
549 
550     }
551 
552     private void writeValidate(ServiceMethod method) {
553         indentPrintln("return new ArrayList<ValidationResultInfo> ();");
554         this.importsAdd(ArrayList.class.getName());
555     }
556     private Set<String> cacheVariablesWritten = new HashSet<String>();
557 
558     private void writeCacheVariable(ServiceMethod method) {
559         String objectName = calcObjectName(method);
560         if (!this.isRice()) {
561             objectName = objectName + "Info";
562         }
563         String mapName = calcMapName(method);
564         if (cacheVariablesWritten.add(mapName)) {
565             indentPrintln("private Map<String, " + objectName + "> " + mapName + " = new LinkedHashMap<String, " + objectName + ">();");
566             importsAdd(Map.class.getName());
567             importsAdd(LinkedHashMap.class.getName());
568         }
569     }
570 
571     private void writeCacheVariableClear(ServiceMethod method) {
572         String objectName = calcObjectName(method);
573         if (!this.isRice()) {
574             objectName = objectName + "Info";
575         }
576         String mapName = calcMapName(method);
577         if (cacheVariablesWritten.add(mapName)) {
578             indentPrintln("this." + mapName + ".clear ();");
579         }
580     }
581 
582     private void writeCreate(ServiceMethod method) {
583         ServiceMethodParameter typeParam = this.findTypeParameter(method);
584         ServiceMethodParameter infoParam = this.findInfoParameter(method);
585         ServiceMethodParameter contextParam = this.findContextParameter(method);
586         String objectName = calcObjectName(method);
587         String infoName = objectName;
588         if (!this.isRice()) {
589             infoName = infoName + "Info";
590         }
591         String mapName = calcMapName(method);
592 
593         if (this.isRice()) {
594             indentPrintln(infoName + " orig = this.get" + infoName + "ByNamespaceCodeAndName(" + infoParam.getName() + ".getNamespaceCode(), " + infoParam.getName() + ".getName());");
595             indentPrintln("if (orig != null)");
596             openBrace();
597             indentPrintln("throw new RiceIllegalArgumentException (" + infoParam.getName() + ".getNamespaceCode() + \".\" + " + infoParam.getName() + ".getName());");
598             closeBrace();
599         }
600         if (typeParam != null) {
601             indentPrintln("if (!" + typeParam.getName() + ".equals (" + infoParam.getName() + ".getTypeKey())) {");
602             indentPrintln("    throw new " + this.getInvalidParameterException() + " (\"The type parameter does not match the type on the info object\");");
603             indentPrintln("}");
604         }
605         if (method.getParameters().size() > 3) {
606             indentPrintln("// TODO: check the rest of the readonly fields that are specified on the create to make sure they match the info object");
607         }
608         if (this.isR1) {
609             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
610             indentPrintln(infoName + " copy = " + infoParam.getName() + ";");
611         } else if (this.isRice()) {
612             indentPrintln(infoName + ".Builder copy = " + infoName + ".Builder.create (" + infoParam.getName() + ");");
613         } else {
614             indentPrintln(infoName + " copy = new " + infoName + "(" + infoParam.getName() + ");");
615         }
616         indentPrintln("if (copy.getId() == null) {");
617         // indentPrintln("   copy.setId(" + mapName + ".size() + \"\");");
618         importsAdd("org.kuali.student.common.util.UUIDHelper");
619         indentPrintln("   copy.setId(UUIDHelper.genStringUUID());");
620         indentPrintln("}");
621         if (contextParam != null) {
622             indentPrintln("copy.setMeta(newMeta(" + contextParam.getName() + "));");
623         }
624         if (isR1) {
625             indentPrintln(mapName + ".put(copy.getId(), copy);");
626             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
627             indentPrintln("return copy;");
628         } else if (this.isRice()) {
629             indentPrintln (infoParam.getName() + " = copy.build ();");
630             indentPrintln(mapName + ".put(" + infoParam.getName() + ".getId(), " + infoParam.getName() + ");");
631             indentPrintln("return " + infoParam.getName() + ";");
632         } else {
633             indentPrintln(mapName + ".put(copy.getId(), copy);");
634             indentPrintln("return new " + infoName + "(copy);");
635         }
636     }
637 
638     private void writeAdd(ServiceMethod method) {
639         ServiceMethodParameter typeParam = this.findTypeParameter(method);
640         ServiceMethodParameter infoParam = this.findInfoParameter(method);
641         ServiceMethodParameter contextParam = this.findContextParameter(method);
642         String objectName = calcObjectName(method);
643         String infoName = objectName;
644         if (!this.isRice()) {
645             infoName = infoName + "Info";
646         }
647         String mapName = calcMapName(method);
648 
649         if (typeParam != null) {
650             indentPrintln("if (!" + typeParam.getName() + ".equals (" + infoParam.getName() + ".getTypeKey())) {");
651             indentPrintln("    throw new " + this.getInvalidParameterException() + " (\"The type parameter does not match the type on the info object\");");
652             indentPrintln("}");
653         }
654         if (method.getParameters().size() > 3) {
655             indentPrintln("// TODO: check the rest of the readonly fields that are specified on the create to make sure they match the info object");
656         }
657         if (isR1) {
658             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
659             indentPrintln(infoName + " copy = " + infoParam.getName() + ";");
660         } else if (this.isRice()) {
661             indentPrintln(infoName + ".Builder copy = " + infoName + ".Builder.create (" + infoParam.getName() + ");");
662         } else {
663             indentPrintln(infoName + " copy = new " + infoName + "(" + infoParam.getName() + ");");
664         }
665         indentPrintln("if (copy.getId() == null) {");
666         // indentPrintln("   copy.setId(" + mapName + ".size() + \"\");");
667         importsAdd("org.kuali.student.common.util.UUIDHelper");
668         indentPrintln("   copy.setId(UUIDHelper.genStringUUID());");
669         indentPrintln("}");
670         if (contextParam != null) {
671             indentPrintln("copy.setMeta(newMeta(" + contextParam.getName() + "));");
672         }
673         if (isR1) {
674             indentPrintln(mapName + ".put(copy.getId(), copy);");
675             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
676             indentPrintln("return copy;");
677         } else if (this.isRice()) {
678             indentPrintln (infoParam.getName() + " = copy.build ();");
679             indentPrintln(mapName + ".put(" + infoParam.getName() + ".getId(), " + infoParam.getName() + ");");
680             indentPrintln("return " + infoParam.getName() + ";");
681         } else {
682             indentPrintln(mapName + ".put(copy.getId(), copy);");
683             indentPrintln("return new " + infoName + "(copy);");
684         }
685     }
686 
687     private ServiceMethodParameter findIdParameter(ServiceMethod method) {
688         String idFieldName = calcObjectName(method) + "Id";
689         for (ServiceMethodParameter parameter : method.getParameters()) {
690             if (parameter.getType().equals("String")) {
691                 if (parameter.getName().equals(idFieldName)) {
692                     return parameter;
693                 }
694             }
695         }
696 
697         // if only one parameter and it is a string then grab that
698         if (method.getParameters().size() == 1) {
699             for (ServiceMethodParameter parameter : method.getParameters()) {
700                 if (parameter.getType().equals("String")) {
701                     return parameter;
702                 }
703             }
704         }
705         // can't find name exactly 
706         for (ServiceMethodParameter parameter : method.getParameters()) {
707             if (parameter.getType().equals("String")) {
708                 if (parameter.getName().endsWith("Id")) {
709                     return parameter;
710                 }
711             }
712         }
713         // can't find name exactly try key 
714         for (ServiceMethodParameter parameter : method.getParameters()) {
715             if (parameter.getType().equals("String")) {
716                 if (!parameter.getName().endsWith("TypeKey")) {
717                     if (parameter.getName().endsWith("Key")) {
718                         return parameter;
719                     }
720                 }
721             }
722         }
723         log.warn("Could not find the Id paramter for " + method.getService() + "." + method.getName() + " so returning the first one");
724         return method.getParameters().get(0);
725     }
726 
727     private ServiceMethodParameter findContextParameter(ServiceMethod method) {
728         for (ServiceMethodParameter parameter : method.getParameters()) {
729             if (parameter.getType().equals("ContextInfo")) {
730                 return parameter;
731             }
732         }
733         return null;
734     }
735 
736     private ServiceMethodParameter findInfoParameter(ServiceMethod method) {
737         String objectName = calcObjectName(method);
738         if (!this.isRice()) {
739             objectName = objectName + "Info";
740         }
741         for (ServiceMethodParameter parameter : method.getParameters()) {
742             if (parameter.getType().equals(objectName)) {
743                 return parameter;
744             }
745         }
746         if (method.getParameters().size() >= 1) {
747             return method.getParameters().get(0);
748         }
749         return null;
750     }
751 
752     private ServiceMethodParameter findTypeParameter(ServiceMethod method) {
753         for (ServiceMethodParameter parameter : method.getParameters()) {
754             if (parameter.getType().equals("String")) {
755                 if (parameter.getName().endsWith("TypeKey")) {
756                     return parameter;
757                 }
758                 if (parameter.getName().endsWith("Type")) {
759                     return parameter;
760                 }
761             }
762         }
763         return null;
764     }
765 
766     private String calcMapName(ServiceMethod method) {
767         String mapName = this.calcObjectName(method);
768         mapName = this.initLower(mapName) + "Map";
769         return mapName;
770     }
771 
772     protected String calcObjectName(ServiceMethod method) {
773         if (method.getName().startsWith("create")) {
774             return method.getName().substring("create".length());
775         }
776         if (method.getName().startsWith("update")) {
777             return method.getName().substring("update".length());
778         }
779         if (method.getName().startsWith("validate")) {
780             return method.getName().substring("validate".length());
781         }
782         if (method.getName().startsWith("delete")) {
783             return method.getName().substring("delete".length());
784         }
785         if (method.getName().startsWith("get")) {
786             if (method.getReturnValue().getType().equals("StringList")) {
787                 if (method.getName().contains("IdsBy")) {
788                     return method.getName().substring("get".length(),
789                             method.getName().indexOf("IdsBy"));
790                 }
791                 if (method.getName().contains("IdsFor")) {
792                     return method.getName().substring("get".length(),
793                             method.getName().indexOf("IdsFor"));
794                 }
795                 return method.getName().substring("get".length());
796             }
797             String name = method.getReturnValue().getType();
798             if (name.endsWith("List")) {
799                 name = name.substring(0, name.length() - "List".length());
800             }
801             if (name.endsWith("Info")) {
802                 name = name.substring(0, name.length() - "Info".length());
803             }
804             return name;
805         }
806         if (method.getName().startsWith("add")) {
807             return method.getName().substring("add".length());
808         }
809         if (method.getName().startsWith("remove")) {
810             return method.getName().substring("remove".length());
811         }
812         String returnType = this.stripList(method.getReturnValue().getType());
813         XmlType type = this.finder.findXmlType(returnType);
814         if (type.getPrimitive().equals(XmlType.COMPLEX)) {
815             return returnType;
816         }
817         throw new IllegalArgumentException(method.getName());
818     }
819 
820     private void writeUpdate(ServiceMethod method) {
821         ServiceMethodParameter idParam = this.findIdParameter(method);
822         ServiceMethodParameter infoParam = this.findInfoParameter(method);
823         ServiceMethodParameter contextParam = this.findContextParameter(method);
824         if (infoParam == null) {
825             throw new NullPointerException(method.getName());
826         }
827         String objectName = calcObjectName(method);
828         String infoName = objectName;
829         if (!this.isRice()) {
830             infoName = infoName + "Info";
831         }
832         String mapName = calcMapName(method);
833         if (idParam != null) {
834             if (!this.isRice()) {
835                 indentPrintln("if (!" + idParam.getName() + ".equals (" + infoParam.getName() + ".getId())) {");
836                 indentPrintln("    throw new " + this.getInvalidParameterException() + " (\"The id parameter does not match the id on the info object\");");
837                 indentPrintln("}");
838             }
839         }
840         if (isR1) {
841             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
842             indentPrintln(infoName + " copy = " + infoParam.getName() + ";");
843         } else if (this.isRice()) {
844             indentPrintln(infoName + ".Builder copy = " + infoName + ".Builder.create (" + infoParam.getName() + ");");
845         } else {
846             indentPrintln(infoName + " copy = new " + infoName + "(" + infoParam.getName() + ");");
847         }
848         if (contextParam != null) {
849             indentPrintln(infoName + " old = this.get" + objectName + "(" + infoParam.getName() + ".getId(), " + contextParam.getName() + ");");
850         } else {
851             indentPrintln(infoName + " old = this.get" + objectName + "(" + infoParam.getName() + ".getId());");
852         }
853         if (isR1) {
854             indentPrintln("if (!old.getMetaInfo().getVersionInd().equals(copy.getMetaInfo().getVersionInd())) {");
855             indentPrintln("    throw new " + this.getVersionMismatchException() + "(old.getMetaInfo().getVersionInd());");
856             indentPrintln("}");
857             if (contextParam != null) {
858                 indentPrintln("copy.setMeta(updateMeta(copy.getMetaInfo()));");
859             }
860         } else if (this.isRice()) {
861             indentPrintln("if (!old.getVersionNumber().equals(copy.getVersionNumber())) {");
862             indentPrintln("    throw new " + this.getVersionMismatchException() + "(\"\" + old.getVersionNumber());");
863             indentPrintln("}");
864             indentPrintln("copy.setVersionNumber(copy.getVersionNumber() + 1);");
865         } else {
866             indentPrintln("if (!old.getMeta().getVersionInd().equals(copy.getMeta().getVersionInd())) {");
867             indentPrintln("    throw new " + this.getVersionMismatchException() + "(old.getMeta().getVersionInd());");
868             indentPrintln("}");
869             if (contextParam != null) {
870                 indentPrintln("copy.setMeta(updateMeta(copy.getMeta(), contextInfo));");
871             }
872         }
873         if (isR1) {
874             indentPrintln("this." + mapName + " .put(" + infoParam.getName() + ".getId(), copy);");
875             indentPrintln("// don't have deep copy in R1 contracts so just use the object");
876             indentPrintln("return copy;");
877         } else if (this.isRice()) {
878             indentPrintln (infoParam.getName() + " = copy.build ();");
879             indentPrintln("this." + mapName + " .put(" + infoParam.getName() + ".getId(), " + infoParam.getName() + ");");
880             indentPrintln("return " + infoParam.getName() + ";");
881         } else {
882             indentPrintln("this." + mapName + " .put(" + infoParam.getName() + ".getId(), copy);");
883             indentPrintln("return new " + infoName + "(copy);");
884         }
885 
886     }
887 
888     private void writeDelete(ServiceMethod method) {
889         ServiceMethodParameter idParam = this.findIdParameter(method);
890         String mapName = calcMapName(method);
891         indentPrintln("if (this." + mapName + ".remove(" + idParam.getName() + ") == null) {");
892         indentPrintln("   throw new " + this.getOperationFailedException() + "(" + idParam.getName() + ");");
893         indentPrintln("}");
894         indentPrintln("return newStatus();");
895     }
896 
897     private void writeRemove(ServiceMethod method) {
898         ServiceMethodParameter idParam = this.findIdParameter(method);
899         String mapName = calcMapName(method);
900         indentPrintln("if (this." + mapName + ".remove(" + idParam.getName() + ") == null) {");
901         indentPrintln("   throw new " + this.getOperationFailedException() + "(" + idParam.getName() + ");");
902         indentPrintln("}");
903         indentPrintln("return newStatus();");
904     }
905 
906     private void writeGetById(ServiceMethod method) {
907         ServiceMethodParameter idParam = this.findIdParameter(method);
908         String mapName = calcMapName(method);
909         indentPrintln("if (!this." + mapName + ".containsKey(" + idParam.getName() + ")) {");
910         indentPrintln("   throw new " + this.getOperationFailedException() + "(" + idParam.getName() + ");");
911         indentPrintln("}");
912         String objectName = calcObjectName(method);
913         String infoName = objectName;
914         if (!this.isRice()) {
915             infoName = infoName + "Info";
916         }
917         if (isR1) {
918             indentPrintln("// r1 objects do not have deep cody");
919             indentPrintln("return this." + mapName + ".get (" + idParam.getName() + ");");
920         } else if (this.isRice()) {
921             indentPrintln("return this." + mapName + ".get (" + idParam.getName() + ");");
922         } else {
923             indentPrintln("return new " + infoName + "(this." + mapName + ".get (" + idParam.getName() + "));");
924         }
925 
926     }
927 
928     private void writeGetByIds(ServiceMethod method) {
929         String objectName = this.calcObjectName(method);
930         String infoName = objectName;
931         if (!this.isRice()) {
932             infoName = infoName + "Info";
933         }
934         ServiceMethodParameter idListParam = this.findIdListParameter(method);
935         ServiceMethodParameter contextParam = this.findContextParameter(method);
936         this.importsAdd(ArrayList.class.getName());
937         indentPrintln("List<" + infoName + "> list = new ArrayList<" + infoName + "> ();");
938 
939         indentPrintln("for (String id: " + idListParam.getName() + ") {");
940         if (this.isRice()) {
941             indentPrintln("    list.add (this.get" + objectName + "(id));");
942         } else {
943             indentPrintln("    list.add (this.get" + objectName + "(id, " + contextParam.getName() + "));");
944         }
945         indentPrintln("}");
946         indentPrintln("return list;");
947     }
948 
949     private void writeGetIdsByOther(ServiceMethod method) {
950         String objectName = this.calcObjectName(method);
951         String infoName = objectName;
952         if (!this.isRice()) {
953             infoName = infoName + "Info";
954         }
955         String mapName = calcMapName(method);
956         this.importsAdd(ArrayList.class.getName());
957         indentPrintln("List<String> list = new ArrayList<String> ();");
958         indentPrintln("for (" + infoName + " info: " + mapName + ".values ()) {");
959         for (ServiceMethodParameter parameter : method.getParameters()) {
960             if (parameter.getType().equals("ContextInfo")) {
961                 continue;
962             }
963             incrementIndent();
964             indentPrintln("if (" + parameter.getName() + ".equals(info.get" + initUpper(parameter.getName()) + "())) {");
965         }
966         indentPrintln("    list.add (info.getId ());");
967         for (ServiceMethodParameter parameter : method.getParameters()) {
968             if (parameter.getType().equals("ContextInfo")) {
969                 continue;
970             }
971             indentPrintln("}");
972             decrementIndent();
973         }
974         indentPrintln("}");
975         indentPrintln("return list;");
976     }
977 
978     private void writeGetIdsByType(ServiceMethod method) {
979         String objectName = this.calcObjectName(method);
980         String infoName = objectName;
981         if (!this.isRice()) {
982             infoName = infoName + "Info";
983         }
984         String mapName = calcMapName(method);
985         this.importsAdd(ArrayList.class.getName());
986         indentPrintln("List<String> list = new ArrayList<String> ();");
987         indentPrintln("for (" + infoName + " info: " + mapName + ".values ()) {");
988         for (ServiceMethodParameter parameter : method.getParameters()) {
989             if (parameter.getType().equals("ContextInfo")) {
990                 continue;
991             }
992             incrementIndent();
993             indentPrintln("if (" + parameter.getName() + ".equals(info.getTypeKey())) {");
994         }
995         indentPrintln("    list.add (info.getId ());");
996         for (ServiceMethodParameter parameter : method.getParameters()) {
997             if (parameter.getType().equals("ContextInfo")) {
998                 continue;
999             }
1000             indentPrintln("}");
1001             decrementIndent();
1002         }
1003         indentPrintln("}");
1004         indentPrintln("return list;");
1005     }
1006 
1007     private void writeRiceGetByNamespaceAndName(ServiceMethod method) {
1008         String objectName = this.calcObjectName(method);
1009         String infoName = objectName;
1010         if (!this.isRice()) {
1011             infoName = infoName + "Info";
1012         }
1013         String mapName = calcMapName(method);
1014         indentPrintln("for (" + infoName + " info: " + mapName + ".values ()) {");
1015         for (ServiceMethodParameter parameter : method.getParameters()) {
1016             incrementIndent();
1017             indentPrintln("if (" + parameter.getName() + ".equals(info.get" + initUpper(parameter.getName()) + "())) {");
1018         }
1019         indentPrintln("    return " + infoName + ".Builder.create (info).build ();");
1020         for (ServiceMethodParameter parameter : method.getParameters()) {
1021             indentPrintln("}");
1022             decrementIndent();
1023         }
1024         indentPrintln("}");
1025         indentPrintln("throw new RiceIllegalArgumentException ();");
1026     }
1027 
1028     private void writeGetInfosByOther(ServiceMethod method) {
1029         String objectName = this.calcObjectName(method);
1030         String infoName = objectName;
1031         if (!this.isRice()) {
1032             infoName = infoName + "Info";
1033         }
1034         String mapName = calcMapName(method);
1035         this.importsAdd(ArrayList.class.getName());
1036         indentPrintln("List<" + infoName + "> list = new ArrayList<" + infoName + "> ();");
1037         indentPrintln("for (" + infoName + " info: " + mapName + ".values ()) {");
1038         for (ServiceMethodParameter parameter : method.getParameters()) {
1039             if (parameter.getType().equals("ContextInfo")) {
1040                 continue;
1041             }
1042             incrementIndent();
1043             indentPrintln("if (" + parameter.getName() + ".equals(info.get" + initUpper(parameter.getName()) + "())) {");
1044         }
1045         indentPrintln("    list.add (new " + infoName + "(info));");
1046         for (ServiceMethodParameter parameter : method.getParameters()) {
1047             if (parameter.getType().equals("ContextInfo")) {
1048                 continue;
1049             }
1050             indentPrintln("}");
1051             decrementIndent();
1052         }
1053         indentPrintln("}");
1054         indentPrintln("return list;");
1055     }
1056 
1057     private void writeGetType(ServiceMethod method) {
1058         ServiceMethodParameter idParam = this.findIdParameter(method);
1059         String mapName = calcMapName(method);
1060         indentPrintln("if (!this." + mapName + ".containsKey(" + idParam.getName() + ")) {");
1061         indentPrintln("   throw new " + this.getOperationFailedException() + "(" + idParam.getName() + ");");
1062         indentPrintln("}");
1063         indentPrintln("return this." + mapName + ".get (" + idParam.getName() + ");");
1064     }
1065 
1066     private void writeGetTypes(ServiceMethod method) {
1067         String mapName = calcMapName(method);
1068         String objectName = this.calcObjectName(method);
1069         String infoName = objectName;
1070         if (!this.isRice()) {
1071             infoName = infoName + "Info";
1072         }
1073         this.importsAdd(ArrayList.class.getName());
1074         indentPrintln("return new ArrayList<" + infoName + ">(" + mapName + ".values ());");
1075     }
1076 
1077     private String initUpper(String str) {
1078         return str.substring(0, 1).toUpperCase() + str.substring(1);
1079     }
1080 
1081     private ServiceMethodParameter findIdListParameter(ServiceMethod method) {
1082         String idFieldName = calcObjectName(method) + "Ids";
1083         if (this.isRice()) {
1084             idFieldName = "ids";
1085         }
1086         for (ServiceMethodParameter parameter : method.getParameters()) {
1087             if (parameter.getType().equals("StringList")) {
1088                 if (parameter.getName().equals(idFieldName)) {
1089                     return parameter;
1090                 }
1091             }
1092         }
1093         // can't find name exactly 
1094         for (ServiceMethodParameter parameter : method.getParameters()) {
1095             if (parameter.getType().equals("StringList")) {
1096                 if (parameter.getName().endsWith("Ids")) {
1097                     return parameter;
1098                 }
1099             }
1100         }
1101         // can't find name exactly try key 
1102         for (ServiceMethodParameter parameter : method.getParameters()) {
1103             if (parameter.getType().equals("StringList")) {
1104                 if (parameter.getName().endsWith("Keys")) {
1105                     return parameter;
1106                 }
1107             }
1108         }
1109         return null;
1110     }
1111 
1112     private String stripList(String str) {
1113         return GetterSetterNameCalculator.stripList(str);
1114     }
1115 
1116     private String calcExceptionClassName(ServiceMethodError error) {
1117         if (error.getClassName() == null) {
1118             return ServiceExceptionWriter.calcClassName(error.getType());
1119         }
1120         return error.getClassName();
1121     }
1122 
1123     private String calcExceptionPackageName(ServiceMethodError error) {
1124         if (error.getClassName() == null) {
1125             return ServiceExceptionWriter.calcPackage(rootPackage);
1126         }
1127         return error.getPackageName();
1128     }
1129 
1130     private String calcType(String type, String realType) {
1131         XmlType t = finder.findXmlType(this.stripList(type));
1132         String retType = MessageStructureTypeCalculator.calculate(this, model, type, realType,
1133                 t.getJavaPackage());
1134         if (this.isRice()) {
1135             if (retType.equals("Boolean")) {
1136                 retType = "boolean";
1137             }
1138             if (retType.equals("Void")) {
1139                 retType = "void";
1140             }
1141         }
1142         return retType;
1143     }
1144 }