View Javadoc

1   /**
2    * Copyright 2004-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.student.remote.impl.mojo;
17  
18  import java.util.ArrayList;
19  import java.util.HashSet;
20  import java.util.List;
21  import java.util.Set;
22  import java.util.Stack;
23  import org.kuali.rice.core.api.criteria.Predicate;
24  import org.kuali.rice.core.api.criteria.PredicateFactory;
25  import org.kuali.rice.core.api.criteria.QueryByCriteria;
26  import org.kuali.student.admin.ui.mojo.AdminUiLookupableWriter;
27  import org.kuali.student.contract.model.MessageStructure;
28  import org.kuali.student.contract.model.Service;
29  
30  import org.kuali.student.contract.model.ServiceContractModel;
31  import org.kuali.student.contract.model.ServiceMethod;
32  import org.kuali.student.contract.model.ServiceMethodError;
33  import org.kuali.student.contract.model.ServiceMethodParameter;
34  import org.kuali.student.contract.model.XmlType;
35  import org.kuali.student.contract.model.util.ModelFinder;
36  import org.kuali.student.contract.writer.JavaClassWriter;
37  import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
38  import org.kuali.student.contract.writer.service.MessageStructureTypeCalculator;
39  import org.kuali.student.contract.writer.service.ServiceExceptionWriter;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  /**
44   *
45   * @author nwright
46   */
47  public class RemoteImplServiceTestWriter extends JavaClassWriter {
48  
49      private static final Logger log = LoggerFactory.getLogger(RemoteImplServiceTestWriter.class);
50      private ServiceContractModel model;
51      private ModelFinder finder;
52      private String directory;
53      private String rootPackage;
54      private String servKey;
55      private List<ServiceMethod> methods;
56      private Service service;
57  
58      public RemoteImplServiceTestWriter(ServiceContractModel model,
59              String directory,
60              String rootPackage,
61              String servKey,
62              List<ServiceMethod> methods) {
63          super(directory + "/test/java", calcPackage(servKey, rootPackage), calcClassName(servKey));
64          this.model = model;
65          this.finder = new ModelFinder(model);
66          this.directory = directory;
67          this.rootPackage = rootPackage;
68          this.servKey = servKey;
69          this.service = finder.findService(servKey);
70          this.methods = methods;
71      }
72  
73      public static String calcPackage(String servKey, String rootPackage) {
74  //        String pack = rootPackage + "." + servKey.toLowerCase() + ".";
75  //  StringBuffer buf = new StringBuffer (service.getVersion ().length ());
76  //  for (int i = 0; i < service.getVersion ().length (); i ++)
77  //  {
78  //   char c = service.getVersion ().charAt (i);
79  //   c = Character.toLowerCase (c);
80  //   if (Character.isLetter (c))
81  //   {
82  //    buf.append (c);
83  //    continue;
84  //   }
85  //   if (Character.isDigit (c))
86  //   {
87  //    buf.append (c);
88  //   }
89  //  }
90  //  pack = pack + buf.toString ();
91  //        pack = pack + "service.decorators";
92  //        return pack;
93          return rootPackage;
94      }
95  
96      public static String calcClassName(String servKey) {
97          String name = GetterSetterNameCalculator.calcInitUpper(servKey + "ServiceRemoteImplTest");
98          if (name.startsWith("RICE.")) {
99              name = name.substring("RICE.".length());
100         }
101         return name;
102     }
103 
104     private static enum MethodType {
105 
106         VALIDATE, CREATE, UPDATE
107     };
108 
109     private MethodType calcMethodType(ServiceMethod method) {
110         if (method.getName().startsWith("validate")) {
111             return MethodType.VALIDATE;
112         }
113         if (method.getName().startsWith("create")) {
114             return MethodType.CREATE;
115         }
116         if (method.getName().startsWith("update")) {
117             return MethodType.UPDATE;
118         }
119         return null;
120     }
121 
122     /**
123      * Write out the entire file
124      *
125      * @param out
126      */
127     public void write() {
128         String serviceName = service.getName();
129         indentPrintln("//@Ignore");
130         indentPrintln("public class " + calcClassName(servKey));
131         // TODO: figure out how to add import for the decorator
132         openBrace();
133         importsAdd("org.junit.*");
134         XmlType contextInfo = finder.findXmlType("contextInfo");
135         importsAdd(contextInfo.getJavaPackage() + "." + contextInfo.getName());
136         indentPrintln("private static ContextInfo contextInfo;");
137 
138         indentPrintln("private static " + serviceName + "RemoteImpl service;");
139         indentPrintln("");
140         indentPrintln("");
141         indentPrintln("@BeforeClass");
142         indentPrintln("public static void setUpClass() throws Exception");
143         openBrace();
144         indentPrintln("service = new " + serviceName + "RemoteImpl();");
145         indentPrintln("service.setHostUrl(RemoteServiceConstants.ENV2_URL);");
146         indentPrintln("//service.setHostUrl(RemoteServiceConstants.LOCAL_HOST_EMBEDDED_URL);");
147         indentPrintln("contextInfo = new ContextInfo();");
148         indentPrintln("contextInfo.setPrincipalId(\"TESTUSER\");");
149         closeBrace();
150         indentPrintln("");
151         indentPrintln("@AfterClass");
152         indentPrintln("public static void tearDownClass() throws Exception");
153         openBrace();
154         closeBrace();
155         indentPrintln("");
156         indentPrintln("@Before");
157         indentPrintln("public void setUp()");
158         openBrace();
159         closeBrace();
160         indentPrintln("");
161         indentPrintln("@After");
162         indentPrintln("public void tearDown()");
163         openBrace();
164         closeBrace();
165         indentPrintln("");
166 
167 
168         Set<XmlType> types = this.getMainXmlTypesUsedByService(methods);
169         if (types.isEmpty()) {
170             log.warn("No types defined for servKey: " + servKey);
171             return;
172         }
173         // the main servKey
174         log.info("Generating search by criteria tests for " + types.size() + " in " + servKey);
175         for (XmlType type : types) {
176             this.writeTestMethodsForXmlType(type);
177         }
178         closeBrace();
179 
180         this.writeJavaClassAndImportsOutToFile();
181         this.getOut().close();
182     }
183 
184     private void writeTestMethodsForXmlType(XmlType xmlType) {
185         ServiceMethod searchMethod = AdminUiLookupableWriter.findSearchMethod(xmlType, methods);
186         if (searchMethod == null) {
187             log.warn("No search method found for " + this.servKey + "." + xmlType.getName());
188             return;
189 //            throw new NullPointerException (this.servKey + "." + xmlType.getName());
190         }
191         String infoClass = GetterSetterNameCalculator.calcInitUpper(xmlType.getName());
192         importsAdd(xmlType.getJavaPackage() + "." + xmlType.getName());
193         indentPrintln("");
194         indentPrintln("@Test");
195         indentPrintln("public void testSearch" + infoClass + "All () throws Exception");
196         openBrace();
197         importsAdd(QueryByCriteria.class.getName());
198         importsAdd(Predicate.class.getName());
199         importsAdd(PredicateFactory.class.getName());
200         indentPrintln("QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();");
201         indentPrintln("qBuilder.setMaxResults (30);");
202         importsAdd(List.class.getName());
203         String returnType = "List<" + infoClass + ">";
204         if (searchMethod.getReturnValue().getType().endsWith("QueryResults")) {
205             returnType = searchMethod.getReturnValue().getType();
206             XmlType returnXmlType = finder.findXmlType(returnType);
207             importsAdd (returnXmlType.getJavaPackage() + "." + returnType);
208         }
209         indentPrint(returnType + " infos = service." + searchMethod.getName());
210         String comma = "(";
211         for (ServiceMethodParameter param : searchMethod.getParameters()) {
212             print(comma);
213             comma = ", ";
214             if (param.getType().equals("QueryByCriteria")) {
215                 print("qBuilder.build()");
216                 continue;
217             }
218             if (param.getType().equals("ContextInfo")) {
219                 print("contextInfo");
220                 continue;
221             }
222             log.warn(servKey + "." + searchMethod.getName() + " param=" + param.getName() + " " + param.getType() + " cannot be specified in search");
223         }
224         println(");");
225         closeBrace();
226 
227 
228         indentPrintln("");
229         indentPrintln("@Test");
230         indentPrintln("public void testSearch" + infoClass + "KeywordSearch () throws Exception");
231         openBrace();
232         importsAdd(QueryByCriteria.class.getName());
233         importsAdd(Predicate.class.getName());
234         importsAdd(PredicateFactory.class.getName());
235         indentPrintln("QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();");
236         importsAdd(ArrayList.class.getName());
237         indentPrintln("List<Predicate> pList = new ArrayList<Predicate>();");
238         indentPrintln("pList.add(PredicateFactory.equal(\"keywordSearch\", \"xyzzysomethingnothingmatches\"));");
239         indentPrintln("qBuilder.setPredicates(PredicateFactory.and(pList.toArray(new Predicate[pList.size()])));");
240         indentPrintln("qBuilder.setMaxResults (30);");
241         importsAdd(List.class.getName());
242         indentPrint(returnType + " infos = service." + searchMethod.getName());
243         comma = "(";
244         for (ServiceMethodParameter param : searchMethod.getParameters()) {
245             print(comma);
246             comma = ", ";
247             if (param.getType().equals("QueryByCriteria")) {
248                 print("qBuilder.build()");
249                 continue;
250             }
251             if (param.getType().equals("ContextInfo")) {
252                 print("contextInfo");
253                 continue;
254             }
255             log.warn(servKey + "." + searchMethod.getName() + " param=" + param.getName() + " " + param.getType() + " cannot be specified in search");
256         }
257         println(");");
258         closeBrace();
259 
260         this.writeFieldTests(infoClass, returnType, searchMethod, xmlType, new Stack<XmlType>(), "");
261     }
262 
263     private void writeFieldTests(String infoClass, String returnType, ServiceMethod searchMethod, XmlType type, Stack<XmlType> parents, String prefix) {
264         // avoid recursion
265         if (parents.contains(type)) {
266             return;
267         }
268         parents.push(type);
269         for (MessageStructure ms : finder.findMessageStructures(type.getName())) {
270             String fieldName = GetterSetterNameCalculator.calcInitLower(ms.getShortName());
271             if (!prefix.isEmpty()) {
272                 fieldName = prefix + "." + fieldName;
273             }
274             String fieldNameCamel = GetterSetterNameCalculator.dot2Camel(fieldName);
275             if (ms.getShortName().equalsIgnoreCase("versionInd")) {
276                 indentPrintln("// TODO: deal with seaching on the version indicator which is a string in the contract but a number in the database");
277                 continue;
278             }
279             if (ms.getType().equalsIgnoreCase("AttributeInfoList")) {
280                 indentPrintln("// TODO: deal with dynamic attributes");
281                 continue;
282             }
283             if (ms.getType().endsWith("List")) {
284                 indentPrintln("// TODO: deal with  " + fieldName + " which is a List");
285                 continue;
286             }
287             XmlType fieldType = finder.findXmlType(ms.getType());
288             if (fieldType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
289                 // complex sub-types such as rich text 
290                 this.writeFieldTests(infoClass, returnType, searchMethod, fieldType, parents, fieldName);
291                 continue;
292             }
293             if (!ms.getType().equalsIgnoreCase("String")) {
294                 indentPrintln("// TODO: deal with  " + fieldName + " which is a " + ms.getType());
295                 continue;
296             }
297 
298             indentPrintln("");
299             indentPrintln("@Test");
300             indentPrintln("public void testSearch" + infoClass + "By" + fieldNameCamel + " () throws Exception");
301             openBrace();
302             indentPrintln("QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();");
303             indentPrintln("List<Predicate> pList = new ArrayList<Predicate>();");
304             indentPrintln("pList.add(PredicateFactory.equal(\"" + fieldName + "\", \"xyzzysomethingnothingmatches\"));");
305             indentPrintln("qBuilder.setPredicates(PredicateFactory.and(pList.toArray(new Predicate[pList.size()])));");
306             indentPrintln("qBuilder.setMaxResults (30);");
307             indentPrint(returnType + " infos = service." + searchMethod.getName());
308             String comma = "(";
309             for (ServiceMethodParameter param : searchMethod.getParameters()) {
310                 print(comma);
311                 comma = ", ";
312                 if (param.getType().equals("QueryByCriteria")) {
313                     print("qBuilder.build()");
314                     continue;
315                 }
316                 if (param.getType().equals("ContextInfo")) {
317                     print("contextInfo");
318                     continue;
319                 }
320                 log.warn(servKey + "." + searchMethod.getName() + " param=" + param.getName() + " " + param.getType() + " cannot be specified in search");
321             }
322             println(");");
323             closeBrace();
324 
325         }
326         parents.pop();
327     }
328 
329     private String calcType(String type, String realType) {
330         XmlType t = finder.findXmlType(this.stripList(type));
331         return MessageStructureTypeCalculator.calculate(this, model, type, realType,
332                 t.getJavaPackage());
333     }
334 
335     private String stripList(String str) {
336         return GetterSetterNameCalculator.stripList(str);
337     }
338 
339     private String calcExceptionClassName(ServiceMethodError error) {
340         if (error.getClassName() == null) {
341             return ServiceExceptionWriter.calcClassName(error.getType());
342         }
343         return error.getClassName();
344     }
345 
346     private String calcExceptionPackageName(ServiceMethodError error) {
347         if (error.getClassName() == null) {
348             return ServiceExceptionWriter.calcPackage(rootPackage);
349         }
350         return error.getPackageName();
351     }
352 
353     private Set<XmlType> getMainXmlTypesUsedByService(List<ServiceMethod> methods) {
354         Set<XmlType> set = new HashSet();
355         for (ServiceMethod method : methods) {
356             if (method.getReturnValue().getType().endsWith("List")) {
357                 continue;
358             }
359             XmlType returnType = finder.findXmlType(method.getReturnValue().getType());
360             if (returnType == null) {
361                 continue;
362             }
363             if (!returnType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
364                 continue;
365             }
366             // TYPE only should show up on type service
367             if (returnType.getName().equalsIgnoreCase("TypeInfo")) {
368                 if (!servKey.equalsIgnoreCase("type")) {
369                     continue;
370                 }
371             }
372             // State only should show up on type service
373             if (returnType.getName().equalsIgnoreCase("StateInfo")) {
374                 if (!servKey.equalsIgnoreCase("state")) {
375                     continue;
376                 }
377             }
378 //            if (method.getName().startsWith("create")) {
379 //                set.add(returnType);
380 //                continue;
381 //            }
382 //            if (method.getName().startsWith("update")) {
383 //                set.add(returnType);
384 //                continue;
385 //            }
386             // presume a get with a single parameter is getting by primary key
387             if (method.getName().startsWith("get")) {
388                 List<ServiceMethodParameter> params = this.getNonContextParameters(method.getParameters());
389                 if (params.size() == 1) {
390                     if (params.get(0).getType().equalsIgnoreCase("String")) {
391                         set.add(returnType);
392                         continue;
393                     }
394                 }
395             }
396         }
397         return set;
398     }
399 
400     
401     private List<ServiceMethodParameter>  getNonContextParameters (List <ServiceMethodParameter> params) {
402          List<ServiceMethodParameter> list = new ArrayList<ServiceMethodParameter> ();
403          for (ServiceMethodParameter param : params) {
404              if (param.getType().equalsIgnoreCase("ContextInfo")) {
405                  continue;
406              }
407              list.add (param);
408          }
409          return list;
410     }
411     private List<MessageStructure> getFieldsToSearchOn(XmlType xmlType) {
412         List<MessageStructure> list = new ArrayList<MessageStructure>();
413         for (MessageStructure ms : finder.findMessageStructures(xmlType.getName())) {
414             XmlType msType = finder.findXmlType(ms.getType());
415             // TODO: dive down into complex sub-types 
416             if (msType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
417                 continue;
418             }
419             list.add(ms);
420         }
421         return list;
422     }
423 }