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 */ 016package org.kuali.student.remote.impl.mojo; 017 018import java.util.ArrayList; 019import java.util.HashSet; 020import java.util.List; 021import java.util.Set; 022import java.util.Stack; 023import org.kuali.rice.core.api.criteria.Predicate; 024import org.kuali.rice.core.api.criteria.PredicateFactory; 025import org.kuali.rice.core.api.criteria.QueryByCriteria; 026import org.kuali.student.admin.ui.mojo.AdminUiLookupableWriter; 027import org.kuali.student.contract.model.MessageStructure; 028import org.kuali.student.contract.model.Service; 029 030import org.kuali.student.contract.model.ServiceContractModel; 031import org.kuali.student.contract.model.ServiceMethod; 032import org.kuali.student.contract.model.ServiceMethodError; 033import org.kuali.student.contract.model.ServiceMethodParameter; 034import org.kuali.student.contract.model.XmlType; 035import org.kuali.student.contract.model.util.ModelFinder; 036import org.kuali.student.contract.writer.JavaClassWriter; 037import org.kuali.student.contract.writer.service.GetterSetterNameCalculator; 038import org.kuali.student.contract.writer.service.MessageStructureTypeCalculator; 039import org.kuali.student.contract.writer.service.ServiceExceptionWriter; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043/** 044 * 045 * @author nwright 046 */ 047public class RemoteImplServiceTestWriter extends JavaClassWriter { 048 049 private static final Logger log = LoggerFactory.getLogger(RemoteImplServiceTestWriter.class); 050 private ServiceContractModel model; 051 private ModelFinder finder; 052 private String directory; 053 private String rootPackage; 054 private String servKey; 055 private List<ServiceMethod> methods; 056 private Service service; 057 058 public RemoteImplServiceTestWriter(ServiceContractModel model, 059 String directory, 060 String rootPackage, 061 String servKey, 062 List<ServiceMethod> methods) { 063 super(directory + "/test/java", calcPackage(servKey, rootPackage), calcClassName(servKey)); 064 this.model = model; 065 this.finder = new ModelFinder(model); 066 this.directory = directory; 067 this.rootPackage = rootPackage; 068 this.servKey = servKey; 069 this.service = finder.findService(servKey); 070 this.methods = methods; 071 } 072 073 public static String calcPackage(String servKey, String rootPackage) { 074// String pack = rootPackage + "." + servKey.toLowerCase() + "."; 075// StringBuffer buf = new StringBuffer (service.getVersion ().length ()); 076// for (int i = 0; i < service.getVersion ().length (); i ++) 077// { 078// char c = service.getVersion ().charAt (i); 079// c = Character.toLowerCase (c); 080// if (Character.isLetter (c)) 081// { 082// buf.append (c); 083// continue; 084// } 085// if (Character.isDigit (c)) 086// { 087// buf.append (c); 088// } 089// } 090// pack = pack + buf.toString (); 091// pack = pack + "service.decorators"; 092// return pack; 093 return rootPackage; 094 } 095 096 public static String calcClassName(String servKey) { 097 String name = GetterSetterNameCalculator.calcInitUpper(servKey + "ServiceRemoteImplTest"); 098 if (name.startsWith("RICE.")) { 099 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}