001    /*
002     * Copyright 2009 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may      obtain a copy of the License at
007     *
008     *      http://www.osedu.org/licenses/ECL-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.student.mock.mojo;
017    
018    import org.kuali.student.contract.model.Service;
019    import org.kuali.student.contract.model.ServiceContractModel;
020    import org.kuali.student.contract.model.ServiceMethod;
021    import org.kuali.student.contract.model.ServiceMethodError;
022    import org.kuali.student.contract.model.ServiceMethodParameter;
023    import org.kuali.student.contract.model.XmlType;
024    import org.kuali.student.contract.model.impl.ServiceContractModelPescXsdLoader;
025    import org.kuali.student.contract.model.util.ModelFinder;
026    import org.kuali.student.contract.writer.JavaClassWriter;
027    import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
028    import org.kuali.student.contract.writer.service.MessageStructureTypeCalculator;
029    import org.kuali.student.contract.writer.service.ServiceExceptionWriter;
030    import org.slf4j.Logger;
031    import org.slf4j.LoggerFactory;
032    
033    import java.util.*;
034    
035    /**
036     *
037     * @author nwright
038     */
039    public class MockImplServiceWriter extends JavaClassWriter {
040        
041        private static Logger log = LoggerFactory.getLogger(MockImplServiceWriter.class);
042    
043        //////////////////////////////
044        // Constants
045        //////////////////////////////
046    
047        /**
048         * The standard type of methods used in our Service contract.
049         */
050        protected static enum MethodType {
051    
052            VALIDATE,
053            CREATE,
054            CREATE_BULK,
055            ADD,
056            UPDATE,
057            UPDATE_OTHER,
058            DELETE,
059            REMOVE,
060            DELETE_OTHER,
061            GET_CREATE,
062            GET_BY_ID,
063            GET_BY_IDS,
064            RICE_GET_BY_NAMESPACE_AND_NAME,
065            GET_IDS_BY_TYPE,
066            GET_IDS_BY_OTHER,
067            GET_INFOS_BY_OTHER,
068            GET_TYPE,
069            GET_TYPES,
070            UNKNOWN
071        };
072    
073        //////////////////////////////
074        // Data Variables
075        //////////////////////////////
076    
077        protected ServiceContractModel model;
078        protected ModelFinder finder;
079        private String directory;
080        /**
081         * The package name is stored in the service object itself (the package spec kept
082         * moving around so I assumed the actual service name was unique but ran into a problem
083         * when we included rice because they have a StateService  meaning US states and we have
084         * a StateService meaning the state of the object so I added logic to detect rice and
085         * prepend that "RICE." to it
086         */
087        private String rootPackage;
088    
089        /**
090         * Name of the service being operated on.
091         * If it is a RICE service it is prefixed with RICE.
092         * [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.
093         * [11:32:24 AM] Norman Wright: so OrganizationService becomes organization
094         */
095        protected String servKey;
096    
097        protected List<ServiceMethod> methods;
098    
099        /**
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    }