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 final 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            indentPrint("public class " + calcClassName(servKey));
368            println(" implements " + calcServiceInterfaceClassName(servKey));
369            Service serv = finder.findService(servKey);
370            importsAdd(serv.getImplProject() + "." + serv.getName());
371            openBrace();
372            // put all the cache variables at the top
373            indentPrintln("// dao variables ");
374            jpaEntitiesWritten.clear();
375            for (ServiceMethod method : methods) {
376                MethodType methodType = calcMethodType(method);
377                switch (methodType) {
378                    case CREATE:
379                    case GET_TYPE:
380                    case GET_BY_ID:
381                        writeDaoVariable(method);
382                }
383            }
384    
385            for (ServiceMethod method : methods) {
386                MethodType methodType = calcMethodType(method);
387                println("");
388    //            indentPrintln("/**");
389    //            indentPrintWrappedComment(method.getDescription());
390    //            indentPrintln("* ");
391    //            for (ServiceMethodParameter param : method.getParameters()) {
392    //                indentPrintWrappedComment("@param " + param.getName() + " - "
393    //                        + param.getType() + " - "
394    //                        + param.getDescription());
395    //            }
396    //            indentPrintWrappedComment("@return " + method.getReturnValue().
397    //                    getDescription());
398    //            indentPrintln("*/");
399                indentPrintln("@Override");
400                switch (methodType) {
401                    case CREATE:
402                        indentPrintln("@Transactional(readOnly = false, rollbackFor = {Throwable.class})");
403                        importsAdd(Transactional.class.getName());
404                        break;
405                    case ADD:
406                        indentPrintln("@Transactional(readOnly = false, rollbackFor = {Throwable.class})");
407                        importsAdd(Transactional.class.getName());
408                        break;
409                    case UPDATE:
410                        indentPrintln("@Transactional(readOnly = false, rollbackFor = {Throwable.class})");
411                        importsAdd(Transactional.class.getName());
412                        break;
413                    case DELETE:
414                        indentPrintln("@Transactional(readOnly = false, rollbackFor = {Throwable.class})");
415                        importsAdd(Transactional.class.getName());
416                        break;
417                    case REMOVE:
418                        indentPrintln("@Transactional(readOnly = false, rollbackFor = {Throwable.class})");
419                        importsAdd(Transactional.class.getName());
420                        break;
421                    case GET_BY_ID:
422                        indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
423                        importsAdd(Transactional.class.getName());
424                        break;
425                    case GET_BY_IDS:
426                        indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
427                        importsAdd(Transactional.class.getName());
428                        break;
429                    case GET_IDS_BY_TYPE:
430                        indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
431                        importsAdd(Transactional.class.getName());
432                        break;
433                    case GET_IDS_BY_OTHER:
434                        indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
435                        importsAdd(Transactional.class.getName());
436                        break;
437                    case GET_INFOS_BY_OTHER:
438                        indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
439                        importsAdd(Transactional.class.getName());
440                        break;
441                    case GET_TYPE:
442                        indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
443                        importsAdd(Transactional.class.getName());
444                        break;
445                    case GET_TYPES:
446                        indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
447                        importsAdd(Transactional.class.getName());
448                        break;
449                    case RICE_GET_BY_NAMESPACE_AND_NAME:
450                        indentPrintln("@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
451                        importsAdd(Transactional.class.getName());
452                        break;
453                    default:
454                        indentPrintln("//TODO: JPAIMPL check and set the right transactional values for this method");
455                        indentPrintln("//@Transactional(readOnly = true, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class}))");
456                        importsAdd(Transactional.class.getName());
457                }
458                String type = method.getReturnValue().getType();
459                String realType = stripList(type);
460                indentPrint("public " + calcType(type, realType) + " " + method.getName()
461                        + "(");
462                // now do parameters
463                String comma = "";
464                for (ServiceMethodParameter param : method.getParameters()) {
465                    type = param.getType();
466                    realType = stripList(type);
467                    print(comma);
468                    print(calcType(type, realType));
469                    print(" ");
470                    print(param.getName());
471                    comma = ", ";
472                }
473                println(")");
474                // now do exceptions
475                comma = "throws ";
476                incrementIndent();
477                for (ServiceMethodError error : method.getErrors()) {
478                    indentPrint(comma);
479                    String exceptionClassName = calcExceptionClassName(error);
480                    String exceptionPackageName = this.calcExceptionPackageName(error);
481                    println(exceptionClassName);
482                    this.importsAdd(exceptionPackageName + "." + exceptionClassName);
483                    comma = "      ,";
484                }
485                decrementIndent();
486                openBrace();
487                indentPrintln("// " + methodType);
488                switch (methodType) {
489                    case VALIDATE:
490                        writeValidate(method);
491                        break;
492                    case CREATE:
493                        writeCreate(method);
494                        break;
495                    case ADD:
496                        writeAdd(method);
497                        break;
498                    case UPDATE:
499                        writeUpdate(method);
500                        break;
501                    case DELETE:
502                        writeDelete(method);
503                        break;
504                    case REMOVE:
505                        writeRemove(method);
506                        break;
507                    case GET_BY_ID:
508                        writeGetById(method);
509                        break;
510                    case GET_BY_IDS:
511                        writeGetByIds(method);
512                        break;
513                    case GET_IDS_BY_TYPE:
514                        writeGetIdsByType(method);
515                        break;
516                    case GET_IDS_BY_OTHER:
517                        writeGetIdsByOther(method);
518                        break;
519                    case GET_INFOS_BY_OTHER:
520                        writeGetInfosByOther(method);
521                        break;
522                    case SEARCH_FOR_IDS:
523                        writeSearchForIds(method);
524                        break;
525                    case SEARCH_FOR_INFOS:
526                        writeSearchForInfos(method);
527                        break;
528                    case GET_TYPE:
529                        writeGetType(method);
530                        break;
531                    case GET_TYPES:
532                        writeGetTypes(method);
533                        break;
534                    case RICE_GET_BY_NAMESPACE_AND_NAME:
535                        writeRiceGetByNamespaceAndName(method);
536                        break;
537                    default:
538                        writeThrowsNotImplemented(method);
539                }
540                closeBrace();
541            }
542    
543            closeBrace();
544    
545            this.writeJavaClassAndImportsOutToFile();
546            this.getOut().close();
547        }
548    
549        private String getInvalidParameterException() {
550            if (this.isRice()) {
551                return "RiceIllegalArgumentException";
552            }
553            return "InvalidParameterException";
554        }
555    
556        private String getOperationFailedException() {
557            if (this.isRice()) {
558                return "RiceIllegalArgumentException";
559            }
560            return "OperationFailedException";
561        }
562    
563        private String getDoesNotExistException() {
564            if (this.isRice()) {
565                return "RiceIllegalArgumentException";
566            }
567            return "DoesNotExistException";
568        }
569    
570        private String getVersionMismatchException() {
571            if (this.isRice()) {
572                return "RiceIllegalStateException";
573            }
574            return "VersionMismatchException";
575        }
576    
577        private void writeThrowsNotImplemented(ServiceMethod method) {
578            indentPrintln("throw new " + this.getOperationFailedException() + " (\"" + method.getName() + " has not been implemented\");");
579        }
580    
581        protected String initLower(String str) {
582            return str.substring(0, 1).toLowerCase() + str.substring(1);
583        }
584    
585        private String calcCriteriaLookupServiceVariableName(ServiceMethod method) {
586            String objectName = calcObjectName(method);
587            String variableName = initLower(objectName) + "CriteriaLookupService";
588            return variableName;
589        }
590    
591        private void writeValidate(ServiceMethod method) {
592            indentPrintln("return new ArrayList<ValidationResultInfo> ();");
593            this.importsAdd(ArrayList.class.getName());
594        }
595        private final Set<String> jpaEntitiesWritten = new HashSet<String>();
596    
597        private void writeDaoVariable(ServiceMethod method) {
598            String objectName = calcObjectName(method);
599            String className = objectName + "Dao";
600            String variableName = calcDaoVariableName(method);
601            if (jpaEntitiesWritten.add(variableName)) {
602                XmlType xmlType = finder.findXmlType(objectName + "Info");
603                if (xmlType == null) {
604                    System.out.println("Cannot write DAO because the object does not follow patterns.  ObjectName="
605                            + objectName + " method=" + method);
606                    return;
607                }
608                new JpaEntityWriter(model, directory, rootPackage, servKey, methods, xmlType, isR1).write();
609                new JpaDaoWriter(model, directory, rootPackage, servKey, methods, xmlType, isR1).write();
610                println("");
611                indentPrintln("private " + className + " " + variableName + ";");
612                println("");
613                indentPrintln("public void set" + className + "(" + className + " " + variableName + ") {");
614                incrementIndent();
615                indentPrintln("this." + variableName + " = " + variableName + ";");
616                decrementIndent();
617                indentPrintln("}");
618                println("");
619                indentPrintln("public " + className + " get" + className + "() {");
620                incrementIndent();
621                indentPrintln("return this." + variableName + ";");
622                decrementIndent();
623                indentPrintln("}");
624    
625                variableName = calcCriteriaLookupServiceVariableName(method);
626    
627                importsAdd(CriteriaLookupService.class.getName());
628                className = "CriteriaLookupService";
629                println("");
630                indentPrintln("// Criteria Lookup for this object");
631                indentPrintln("private " + className + " " + variableName + ";");
632                println("");
633                indentPrintln("public void set" + initUpper(variableName) + "(" + className + " " + variableName + ") {");
634                incrementIndent();
635                indentPrintln("this." + variableName + " = " + variableName + ";");
636                decrementIndent();
637                indentPrintln("}");
638                println("");
639                indentPrintln("public " + className + " get" + initUpper(variableName) + "() {");
640                incrementIndent();
641                indentPrintln("return this." + variableName + ";");
642                decrementIndent();
643                indentPrintln("}");
644    
645            }
646        }
647    
648        private void writeCreate(ServiceMethod method) {
649    
650    //        holdInfo.setPersonId(personId);
651    //        holdInfo.setHoldIssueId(issueId);
652    //        holdInfo.setTypeKey(holdTypeKey);
653    //
654    //        HoldIssueEntity holdIssueEntity = holdIssueDao.find(issueId);
655    //        if (holdIssueEntity == null) {
656    //            throw new InvalidParameterException(issueId);
657    //        }
658    //        AppliedHoldEntity entity = new AppliedHoldEntity(holdInfo);
659    //        entity.setHoldIssue(holdIssueEntity);
660    //        entity.setEntityCreated(context);
661    //        appliedHoldDao.persist(entity);
662    //        appliedHoldDao.getEm().flush();
663    //        return entity.toDto();
664            String daoVariable = calcDaoVariableName(method);
665            String entityClassName = calcEntityClassName(method);
666            ServiceMethodParameter typeParam = this.findTypeParameter(method);
667            ServiceMethodParameter infoParam = this.findInfoParameter(method);
668            ServiceMethodParameter contextParam = this.findContextParameter(method);
669            String objectName = calcObjectName(method);
670            String infoName = objectName;
671            if (!this.isRice()) {
672                infoName = infoName + "Info";
673            }
674            String daoName = calcDaoVariableName(method);
675            if (typeParam != null) {
676                indentPrintln(infoParam.getName() + ".setTypeKey (" + typeParam.getName() + ");");
677            }
678            if (method.getParameters().size() > 3) {
679                indentPrintln("//TODO: JPAIMPL overwrite the rest of the readonly fields that are specified on the create in the info object");
680            }
681            indentPrintln(entityClassName + " entity = new " + entityClassName + "(" + infoParam.getName() + ");");
682            indentPrintln("entity.setEntityCreated(" + contextParam.getName() + ");");
683            indentPrintln(daoVariable + ".persist(entity);");
684            indentPrintln(daoVariable + ".getEm().flush();");
685            indentPrintln("return entity.toDto();");
686        }
687    
688        private void writeAdd(ServiceMethod method) {
689            indentPrintln("//TODO: JPAIMPL this needs to be implemented");
690            indentPrintln("throw new OperationFailedException (\"Not implemented\");");
691        }
692    
693        private ServiceMethodParameter findIdParameter(ServiceMethod method) {
694            String idFieldName = calcObjectName(method) + "Id";
695            for (ServiceMethodParameter parameter : method.getParameters()) {
696                if (parameter.getType().equals("String")) {
697                    if (parameter.getName().equals(idFieldName)) {
698                        return parameter;
699                    }
700                }
701            }
702    
703            // if only one parameter and it is a string then grab that
704            if (method.getParameters().size() == 1) {
705                for (ServiceMethodParameter parameter : method.getParameters()) {
706                    if (parameter.getType().equals("String")) {
707                        return parameter;
708                    }
709                }
710            }
711            // can't find name exactly 
712            for (ServiceMethodParameter parameter : method.getParameters()) {
713                if (parameter.getType().equals("String")) {
714                    if (parameter.getName().endsWith("Id")) {
715                        return parameter;
716                    }
717                }
718            }
719            // can't find name exactly try key 
720            for (ServiceMethodParameter parameter : method.getParameters()) {
721                if (parameter.getType().equals("String")) {
722                    if (!parameter.getName().endsWith("TypeKey")) {
723                        if (parameter.getName().endsWith("Key")) {
724                            return parameter;
725                        }
726                    }
727                }
728            }
729            log.warn("Could not find the Id paramter for {}.{} so returning the first one", method.getService(), method.getName());
730            return method.getParameters().get(0);
731        }
732    
733        private ServiceMethodParameter findContextParameter(ServiceMethod method) {
734            for (ServiceMethodParameter parameter : method.getParameters()) {
735                if (parameter.getType().equals("ContextInfo")) {
736                    return parameter;
737                }
738            }
739            return null;
740        }
741    
742        private ServiceMethodParameter findInfoParameter(ServiceMethod method) {
743            String objectName = calcObjectName(method);
744            if (!this.isRice()) {
745                objectName = objectName + "Info";
746            }
747            for (ServiceMethodParameter parameter : method.getParameters()) {
748                if (parameter.getType().equals(objectName)) {
749                    return parameter;
750                }
751            }
752            if (method.getParameters().size() >= 1) {
753                return method.getParameters().get(0);
754            }
755            return null;
756        }
757    
758        private ServiceMethodParameter findTypeParameter(ServiceMethod method) {
759            for (ServiceMethodParameter parameter : method.getParameters()) {
760                if (parameter.getType().equals("String")) {
761                    if (parameter.getName().endsWith("TypeKey")) {
762                        return parameter;
763                    }
764                    if (parameter.getName().endsWith("Type")) {
765                        return parameter;
766                    }
767                }
768            }
769            return null;
770        }
771    
772        private String calcDaoVariableName(ServiceMethod method) {
773            String daoVariableName = this.calcObjectName(method);
774            daoVariableName = this.initLower(daoVariableName) + "Dao";
775            return daoVariableName;
776        }
777    
778        private String calcEntityClassName(ServiceMethod method) {
779            String objectName = this.calcObjectName(method);
780            objectName = objectName + "Entity";
781            return objectName;
782        }
783    
784        protected String calcObjectName(ServiceMethod method) {
785            if (method.getName().startsWith("create")) {
786                return method.getName().substring("create".length());
787            }
788            if (method.getName().startsWith("update")) {
789                return method.getName().substring("update".length());
790            }
791            if (method.getName().startsWith("validate")) {
792                return method.getName().substring("validate".length());
793            }
794            if (method.getName().startsWith("delete")) {
795                return method.getName().substring("delete".length());
796            }
797            if (method.getName().startsWith("get")) {
798                if (method.getReturnValue().getType().equals("StringList")) {
799                    if (method.getName().contains("IdsBy")) {
800                        return method.getName().substring("get".length(),
801                                method.getName().indexOf("IdsBy"));
802                    }
803                    if (method.getName().contains("KeysBy")) {
804                        return method.getName().substring("get".length(),
805                                method.getName().indexOf("KeysBy"));
806                    }
807                    if (method.getName().contains("IdsFor")) {
808                        return method.getName().substring("get".length(),
809                                method.getName().indexOf("IdsFor"));
810                    }
811                    if (method.getName().contains("With")) {
812                        return method.getName().substring("get".length(),
813                                method.getName().indexOf("With"));
814                    }
815                    if (method.getName().contains("By")) {
816                        return method.getName().substring("get".length(),
817                                method.getName().indexOf("By"));
818                    }
819                    return method.getName().substring("get".length());
820                }
821                String name = method.getReturnValue().getType();
822                if (name.endsWith("List")) {
823                    name = name.substring(0, name.length() - "List".length());
824                }
825                if (name.endsWith("Info")) {
826                    name = name.substring(0, name.length() - "Info".length());
827                }
828                return name;
829            }
830    
831            if (method.getName().startsWith("searchFor")) {
832                if (method.getReturnValue().getType().equals("StringList")) {
833                    if (method.getName().endsWith("Ids")) {
834                        return method.getName().substring("searchFor".length(),
835                                method.getName().indexOf("Ids"));
836                    }
837                    if (method.getName().endsWith("Keys")) {
838                        return method.getName().substring("get".length(),
839                                method.getName().indexOf("Keys"));
840                    }
841                    return method.getName().substring("searchFor".length());
842                }
843                String name = method.getReturnValue().getType();
844                if (name.endsWith("List")) {
845                    name = name.substring(0, name.length() - "List".length());
846                }
847                if (name.endsWith("Info")) {
848                    name = name.substring(0, name.length() - "Info".length());
849                }
850                return name;
851            }
852            if (method.getName().startsWith("add")) {
853                return method.getName().substring("add".length());
854            }
855            if (method.getName().startsWith("remove")) {
856                return method.getName().substring("remove".length());
857            }
858            String returnType = this.stripList(method.getReturnValue().getType());
859            XmlType type = this.finder.findXmlType(returnType);
860            if (type.getPrimitive().equals(XmlType.COMPLEX)) {
861                return returnType;
862            }
863            throw new IllegalArgumentException(method.getName());
864        }
865    
866        private void writeUpdate(ServiceMethod method) {
867    //        if (!holdId.equals(holdInfo.getId())) {
868    //            throw new InvalidParameterException(holdId + " does not match the id in the object " + holdInfo.getId());
869    //        }
870    //        AppliedHoldEntity entity = appliedHoldDao.find(holdId);
871    //        if (null == entity) {
872    //            throw new DoesNotExistException(holdId);
873    //        }
874    //        entity.fromDto(holdInfo);
875    //        entity.setEntityUpdated(context);
876    //        entity = appliedHoldDao.merge(entity);
877    //        appliedHoldDao.getEm().flush(); // need to flush to get the version indicator updated
878    //        return entity.toDto();
879            ServiceMethodParameter idParam = this.findIdParameter(method);
880            ServiceMethodParameter infoParam = this.findInfoParameter(method);
881            ServiceMethodParameter contextParam = this.findContextParameter(method);
882            String daoVariable = calcDaoVariableName(method);
883            String entityClassName = calcEntityClassName(method);
884            if (infoParam == null) {
885                throw new NullPointerException(method.getName());
886            }
887            String objectName = calcObjectName(method);
888            String infoName = objectName;
889            if (!this.isRice()) {
890                infoName = infoName + "Info";
891            }
892            if (idParam != null) {
893                if (!this.isRice()) {
894                    indentPrintln("if (!" + idParam.getName() + ".equals (" + infoParam.getName() + ".getId())) {");
895                    indentPrintln("    throw new " + this.getInvalidParameterException() + " (\"The id parameter does not match the id on the info object\");");
896                    indentPrintln("}");
897                }
898            }
899            indentPrintln(entityClassName + " entity = " + daoVariable + ".find(" + idParam.getName() + ");");
900            indentPrintln("if (entity == null) {");
901            incrementIndent();
902            indentPrintln("throw new DoesNotExistException(" + idParam.getName() + ");");
903            decrementIndent();
904            indentPrintln("}");
905            indentPrintln("entity.fromDto(" + infoParam.getName() + ");");
906            indentPrintln("entity.setEntityUpdated(" + contextParam.getName() + ");");
907            indentPrintln("entity = " + daoVariable + ".merge(entity);");
908            indentPrintln("return entity.toDto();");
909        }
910    
911        private void writeDelete(ServiceMethod method) {
912    //        AppliedHoldEntity entity = appliedHoldDao.find(holdId);
913    //        if (null == entity) {
914    //            throw new DoesNotExistException(holdId);
915    //        }
916    //        appliedHoldDao.remove(entity);
917    //        StatusInfo status = new StatusInfo();
918    //        status.setSuccess(Boolean.TRUE);
919    //        return status;
920            ServiceMethodParameter idParam = this.findIdParameter(method);
921            String daoVariable = calcDaoVariableName(method);
922            String entityClassName = calcEntityClassName(method);
923            indentPrintln(entityClassName + " entity = " + daoVariable + ".find(" + idParam.getName() + ");");
924            indentPrintln("if (entity == null) {");
925            incrementIndent();
926            indentPrintln("throw new DoesNotExistException(" + idParam.getName() + ");");
927            decrementIndent();
928            indentPrintln("}");
929            indentPrintln(daoVariable + ".remove(entity);");
930            indentPrintln("StatusInfo status = new StatusInfo();");
931            importsAdd("org.kuali.student.r2.common.dto.StatusInfo");
932            indentPrintln("status.setSuccess(Boolean.TRUE);");
933            indentPrintln("return status;");
934        }
935    
936        private void writeRemove(ServiceMethod method) {
937            indentPrintln("//TODO: JPAIMPL this needs to be implemented");
938            indentPrintln("throw new OperationFailedException (\"Not implemented\");");
939        }
940    
941        private void writeGetById(ServiceMethod method) {
942    //        AppliedHoldEntity entity = appliedHoldDao.find(holdId);
943    //        if (entity == null) {
944    //            throw new DoesNotExistException(holdId);
945    //        }
946    //        return entity.toDto();
947            ServiceMethodParameter idParam = this.findIdParameter(method);
948            String daoVariable = calcDaoVariableName(method);
949            String entityClassName = calcEntityClassName(method);
950            indentPrintln(entityClassName + " entity = " + daoVariable + ".find(" + idParam.getName() + ");");
951            indentPrintln("if (entity == null) {");
952            incrementIndent();
953            indentPrintln("throw new DoesNotExistException(" + idParam.getName() + ");");
954            decrementIndent();
955            indentPrintln("}");
956            indentPrintln("return entity.toDto();");
957        }
958    
959        private void writeGetByIds(ServiceMethod method) {
960    //        List<HoldIssueEntity> holdIssues = holdIssueDao.findByIds(issueIds);
961    //        List<HoldIssueInfo> result = new ArrayList<HoldIssueInfo>(holdIssues.size());
962    //        for (HoldIssueEntity entity : holdIssues) {
963    //            if (entity == null) {
964    //                // if one of the entities from "findByIds" is returned as null, then one of the keys in the list was not found
965    //                throw new DoesNotExistException();
966    //            }
967    //            result.add(entity.toDto());
968    //        }
969    //        return result;
970            String daoVariable = calcDaoVariableName(method);
971            String entityClassName = calcEntityClassName(method);
972            String objectName = this.calcObjectName(method);
973            ServiceMethodParameter idListParam = this.findIdListParameter(method);
974            String infoName = objectName;
975            infoName = infoName + "Info";
976            this.importsAdd(ArrayList.class.getName());
977            indentPrintln("List<" + entityClassName + "> entities = " + daoVariable + ".findByIds(" + idListParam.getName() + ");");
978            indentPrintln("List<" + infoName + "> list = new ArrayList<" + infoName + "> (entities.size());");
979            indentPrintln("for (" + entityClassName + " entity : entities) {");
980            incrementIndent();
981            indentPrintln("if (entity == null) {");
982            incrementIndent();
983            indentPrintln("throw new DoesNotExistException();");
984            decrementIndent();
985            indentPrintln("}");
986            indentPrintln("list.add(entity.toDto());");
987            decrementIndent();
988            indentPrintln("}");
989            indentPrintln("return list;");
990        }
991    
992        private String calcNamedQuery(ServiceMethod method) {
993            String objectName = calcObjectName(method);
994            String name = method.getName();
995            if (name.startsWith("get")) {
996                name = name.substring("get".length());
997            }
998            if (name.startsWith(objectName)) {
999                name = name.substring(objectName.length());
1000            }
1001    //        if (name.startsWith("Ids")) {
1002    //            name = name.substring("Ids".length());
1003    //        }
1004    //        if (name.isEmpty()) {
1005    //            throw new RuntimeException (method.getName());
1006    //        }
1007            // remove plural
1008            if (!method.getReturnValue().getType().equals("StringList")) {
1009                if (name.startsWith("s")) {
1010                    name = name.substring("s".length());
1011                }
1012            }
1013            // add back the get
1014            name = "get" + name;
1015            return name;
1016        }
1017    
1018        private void writeGetIdsByOther(ServiceMethod method) {
1019    //      return appliedHoldDao.getIdsByIssue(issueId);
1020            String objectName = this.calcObjectName(method);
1021            String infoName = objectName;
1022            if (!this.isRice()) {
1023                infoName = infoName + "Info";
1024            }
1025            String daoVariableName = calcDaoVariableName(method);
1026            String namedQuery = calcNamedQuery(method);
1027            indentPrint("return " + daoVariableName + "." + namedQuery + "(");
1028            String comma = "";
1029            for (ServiceMethodParameter param : method.getParameters()) {
1030                if (param.getType().equals("ContextInfo")) {
1031                    continue;
1032                }
1033                print(comma);
1034                comma = ", ";
1035                print(param.getName());
1036            }
1037            println(");");
1038        }
1039    
1040        private ServiceMethodParameter findCriteriaParam(ServiceMethod method) {
1041            for (ServiceMethodParameter param : method.getParameters()) {
1042                if (param.getType().equals("QueryByCriteria")) {
1043                    return param;
1044                }
1045            }
1046            return null;
1047        }
1048    
1049        private void writeSearchForIds(ServiceMethod method) {
1050    //        List<String> results = new ArrayList<String>();
1051    //        GenericQueryResults<AppliedHoldEntity> appliedHolds = criteriaLookupService.lookup(AppliedHoldEntity.class, criteria);
1052    //        if (null != appliedHolds && appliedHolds.getResults().size() > 0) {
1053    //            for (AppliedHoldEntity appliedHold : appliedHolds.getResults()) {
1054    //                results.add(appliedHold.getId());
1055    //            }
1056    //        }
1057    //        return results;
1058            String objectName = this.calcObjectName(method);
1059            String infoName = objectName;
1060            if (!this.isRice()) {
1061                infoName = infoName + "Info";
1062            }
1063            String criteriaLookupVariableName = calcCriteriaLookupServiceVariableName(method);
1064            String entityClassName = this.calcEntityClassName(method);
1065            ServiceMethodParameter criteriaParam = findCriteriaParam(method);
1066            importsAdd(CriteriaLookupService.class.getName());
1067            importsAdd(ArrayList.class.getName());
1068            importsAdd(List.class.getName());
1069            indentPrintln("List<String> results = new ArrayList<String>();");
1070            importsAdd(GenericQueryResults.class.getName());
1071            indentPrintln("GenericQueryResults<" + entityClassName + "> entities");
1072            indentPrintln("    = " + criteriaLookupVariableName + ".lookup(" + entityClassName + ".class, " + criteriaParam.getName() + ");");
1073            indentPrintln("if (null != entities && !entities.getResults().isEmpty()) {");
1074            incrementIndent();
1075            indentPrintln("for (" + entityClassName + " entity : entities.getResults()) {");
1076            incrementIndent();
1077            indentPrintln("results.add(entity.getId());");
1078            decrementIndent();
1079            indentPrintln("}");
1080            decrementIndent();
1081            indentPrintln("}");
1082            indentPrintln("return results;");
1083        }
1084    
1085        private void writeSearchForInfos(ServiceMethod method) {
1086    //        List<AppliedHoldInfo> results = new ArrayList<AppliedHoldInfo>();
1087    //        GenericQueryResults<AppliedHoldEntity> appliedHolds = criteriaLookupService.lookup(AppliedHoldEntity.class, criteria);
1088    //        if (null != appliedHolds && appliedHolds.getResults().size() > 0) {
1089    //            for (AppliedHoldEntity appliedHold : appliedHolds.getResults()) {
1090    //                results.add(appliedHold.toDto());
1091    //            }
1092    //        }
1093    //        return results;
1094            String objectName = this.calcObjectName(method);
1095            String criteriaLookupVariableName = calcCriteriaLookupServiceVariableName(method);
1096            String entityClassName = this.calcEntityClassName(method);
1097            String infoName = this.calcInfoName(method);
1098            ServiceMethodParameter criteriaParam = findCriteriaParam(method);
1099            importsAdd(CriteriaLookupService.class.getName());
1100            importsAdd(ArrayList.class.getName());
1101            importsAdd(List.class.getName());
1102            indentPrintln("List<" + infoName + "> results = new ArrayList<" + infoName + ">();");
1103            importsAdd(GenericQueryResults.class.getName());
1104            indentPrintln("GenericQueryResults<" + entityClassName + "> entities");
1105            indentPrintln("    = " + criteriaLookupVariableName + ".lookup(" + entityClassName + ".class, " + criteriaParam.getName() + ");");
1106            indentPrintln("if (null != entities && !entities.getResults().isEmpty()) {");
1107            incrementIndent();
1108            indentPrintln("for (" + entityClassName + " entity : entities.getResults()) {");
1109            incrementIndent();
1110            indentPrintln("results.add(entity.toDto());");
1111            decrementIndent();
1112            indentPrintln("}");
1113            decrementIndent();
1114            indentPrintln("}");
1115            indentPrintln("return results;");
1116        }
1117    
1118        private ServiceMethodParameter getTypeParameter(ServiceMethod method) {
1119            ServiceMethodParameter fallbackParam = null;
1120            for (ServiceMethodParameter parameter : method.getParameters()) {
1121                if (parameter.getName().endsWith("TypeKey")) {
1122                    return parameter;
1123                }
1124                if (parameter.getType().equals("String")) {
1125                    if (parameter.getName().toLowerCase().contains("type")) {
1126                        fallbackParam = parameter;
1127                    }
1128                }
1129            }
1130            return fallbackParam;
1131        }
1132    
1133        private String calcInfoName(ServiceMethod method) {
1134            String objectName = this.calcObjectName(method);
1135            String infoName = objectName;
1136            if (!this.isRice()) {
1137                infoName = infoName + "Info";
1138            }
1139            return infoName;
1140        }
1141    
1142        private void writeGetIdsByType(ServiceMethod method) {
1143            String objectName = this.calcObjectName(method);
1144            String infoName = this.calcInfoName(method);
1145            String daoVariable = calcDaoVariableName(method);
1146            ServiceMethodParameter typeParam = this.getTypeParameter(method);
1147            if (typeParam == null) {
1148    
1149            }
1150            indentPrintln("return " + daoVariable + ".getIdsByType(" + typeParam.getName() + ");");
1151        }
1152    
1153        private void writeRiceGetByNamespaceAndName(ServiceMethod method) {
1154            indentPrintln("//TODO: JPAIMPL this needs to be implemented");
1155            indentPrintln("throw new OperationFailedException (\"Not implemented\");");
1156        }
1157    
1158        private void writeGetInfosByOther(ServiceMethod method) {
1159    //        List<AppliedHoldEntity> entities = this.appliedHoldDao.getByPerson(personId);
1160    //        List<AppliedHoldInfo> result = new ArrayList<AppliedHoldInfo>(entities.size());
1161    //        for (AppliedHoldEntity entity : entities) {
1162    //            result.add(entity.toDto());
1163    //        }
1164    //        return result;
1165            String objectName = this.calcObjectName(method);
1166            String infoName = objectName;
1167            if (!this.isRice()) {
1168                infoName = infoName + "Info";
1169            }
1170            String daoVariableName = calcDaoVariableName(method);
1171            String namedQuery = calcNamedQuery(method);
1172            String entityClassName = calcEntityClassName(method);
1173            indentPrint("List<" + entityClassName + "> entities = " + daoVariableName + "." + namedQuery + "(");
1174            String comma = "";
1175            for (ServiceMethodParameter param : method.getParameters()) {
1176                if (param.getType().equals("ContextInfo")) {
1177                    continue;
1178                }
1179                print(comma);
1180                comma = ", ";
1181                print(param.getName());
1182            }
1183            println(");");
1184            this.importsAdd(ArrayList.class.getName());
1185            indentPrintln("List<" + infoName + "> list = new ArrayList<" + infoName + "> (entities.size());");
1186            indentPrintln("for (" + entityClassName + " entity: entities) {");
1187            indentPrintln("    list.add (entity.toDto ());");
1188            indentPrintln("}");
1189            indentPrintln("return list;");
1190        }
1191    
1192        private void writeGetType(ServiceMethod method) {
1193            indentPrintln("//TODO: JPAIMPL this needs to be implemented");
1194            indentPrintln("throw new OperationFailedException (\"Not implemented\");");
1195        }
1196    
1197        private void writeGetTypes(ServiceMethod method) {
1198            indentPrintln("//TODO: JPAIMPL this needs to be implemented");
1199            indentPrintln("throw new OperationFailedException (\"Not implemented\");");
1200        }
1201    
1202        private String initUpper(String str) {
1203            return str.substring(0, 1).toUpperCase() + str.substring(1);
1204        }
1205    
1206        private ServiceMethodParameter findIdListParameter(ServiceMethod method) {
1207            String idFieldName = calcObjectName(method) + "Ids";
1208            if (this.isRice()) {
1209                idFieldName = "ids";
1210            }
1211            for (ServiceMethodParameter parameter : method.getParameters()) {
1212                if (parameter.getType().equals("StringList")) {
1213                    if (parameter.getName().equals(idFieldName)) {
1214                        return parameter;
1215                    }
1216                }
1217            }
1218            // can't find name exactly 
1219            for (ServiceMethodParameter parameter : method.getParameters()) {
1220                if (parameter.getType().equals("StringList")) {
1221                    if (parameter.getName().endsWith("Ids")) {
1222                        return parameter;
1223                    }
1224                }
1225            }
1226            // can't find name exactly try key 
1227            for (ServiceMethodParameter parameter : method.getParameters()) {
1228                if (parameter.getType().equals("StringList")) {
1229                    if (parameter.getName().endsWith("Keys")) {
1230                        return parameter;
1231                    }
1232                }
1233            }
1234            return null;
1235        }
1236    
1237        private String stripList(String str) {
1238            return GetterSetterNameCalculator.stripList(str);
1239        }
1240    
1241        private String calcExceptionClassName(ServiceMethodError error) {
1242            if (error.getClassName() == null) {
1243                return ServiceExceptionWriter.calcClassName(error.getType());
1244            }
1245            return error.getClassName();
1246        }
1247    
1248        private String calcExceptionPackageName(ServiceMethodError error) {
1249            if (error.getClassName() == null) {
1250                return ServiceExceptionWriter.calcPackage(rootPackage);
1251            }
1252            return error.getPackageName();
1253        }
1254    
1255        private String calcType(String type, String realType) {
1256            XmlType t = finder.findXmlType(this.stripList(type));
1257            String retType = MessageStructureTypeCalculator.calculate(this, model, type, realType,
1258                    t.getJavaPackage());
1259            if (this.isRice()) {
1260                if (retType.equals("Boolean")) {
1261                    retType = "boolean";
1262                }
1263                if (retType.equals("Void")) {
1264                    retType = "void";
1265                }
1266            }
1267            return retType;
1268        }
1269    }