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