001 /* 002 * Copyright 2009 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.osedu.org/licenses/ECL-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.student.remote.impl.mojo; 017 018 import java.util.ArrayList; 019 import java.util.HashSet; 020 import java.util.List; 021 import java.util.Set; 022 import java.util.Stack; 023 import org.kuali.rice.core.api.criteria.Predicate; 024 import org.kuali.rice.core.api.criteria.PredicateFactory; 025 import org.kuali.rice.core.api.criteria.QueryByCriteria; 026 import org.kuali.student.admin.ui.mojo.AdminUiLookupableWriter; 027 import org.kuali.student.contract.model.MessageStructure; 028 import org.kuali.student.contract.model.Service; 029 030 import org.kuali.student.contract.model.ServiceContractModel; 031 import org.kuali.student.contract.model.ServiceMethod; 032 import org.kuali.student.contract.model.ServiceMethodError; 033 import org.kuali.student.contract.model.XmlType; 034 import org.kuali.student.contract.model.util.ModelFinder; 035 import org.kuali.student.contract.writer.JavaClassWriter; 036 import org.kuali.student.contract.writer.service.GetterSetterNameCalculator; 037 import org.kuali.student.contract.writer.service.MessageStructureTypeCalculator; 038 import org.kuali.student.contract.writer.service.ServiceExceptionWriter; 039 import org.slf4j.Logger; 040 import org.slf4j.LoggerFactory; 041 042 /** 043 * 044 * @author nwright 045 */ 046 public class RemoteImplServiceTestWriter extends JavaClassWriter { 047 048 private static final Logger log = LoggerFactory.getLogger(RemoteImplServiceTestWriter.class); 049 private ServiceContractModel model; 050 private ModelFinder finder; 051 private String directory; 052 private String rootPackage; 053 private String servKey; 054 private List<ServiceMethod> methods; 055 private Service service; 056 057 public RemoteImplServiceTestWriter(ServiceContractModel model, 058 String directory, 059 String rootPackage, 060 String servKey, 061 List<ServiceMethod> methods) { 062 super(directory + "/test/java", calcPackage(servKey, rootPackage), calcClassName(servKey)); 063 this.model = model; 064 this.finder = new ModelFinder(model); 065 this.directory = directory; 066 this.rootPackage = rootPackage; 067 this.servKey = servKey; 068 this.service = finder.findService(servKey); 069 this.methods = methods; 070 } 071 072 public static String calcPackage(String servKey, String rootPackage) { 073 // String pack = rootPackage + "." + servKey.toLowerCase() + "."; 074 // StringBuffer buf = new StringBuffer (service.getVersion ().length ()); 075 // for (int i = 0; i < service.getVersion ().length (); i ++) 076 // { 077 // char c = service.getVersion ().charAt (i); 078 // c = Character.toLowerCase (c); 079 // if (Character.isLetter (c)) 080 // { 081 // buf.append (c); 082 // continue; 083 // } 084 // if (Character.isDigit (c)) 085 // { 086 // buf.append (c); 087 // } 088 // } 089 // pack = pack + buf.toString (); 090 // pack = pack + "service.decorators"; 091 // return pack; 092 return rootPackage; 093 } 094 095 public static String calcClassName(String servKey) { 096 return GetterSetterNameCalculator.calcInitUpper(servKey + "ServiceRemoteImplTest"); 097 } 098 099 public static String calcDecoratorClassName(String servKey) { 100 return GetterSetterNameCalculator.calcInitUpper(servKey + "ServiceDecorator"); 101 } 102 103 private static enum MethodType { 104 105 VALIDATE, CREATE, UPDATE 106 }; 107 108 private MethodType calcMethodType(ServiceMethod method) { 109 if (method.getName().startsWith("validate")) { 110 return MethodType.VALIDATE; 111 } 112 if (method.getName().startsWith("create")) { 113 return MethodType.CREATE; 114 } 115 if (method.getName().startsWith("update")) { 116 return MethodType.UPDATE; 117 } 118 return null; 119 } 120 121 /** 122 * Write out the entire file 123 * 124 * @param out 125 */ 126 public void write() { 127 String serviceName = service.getName(); 128 indentPrintln("//@Ignore"); 129 indentPrintln("public class " + calcClassName(servKey)); 130 // TODO: figure out how to add import for the decorator 131 openBrace(); 132 importsAdd("org.junit.*"); 133 XmlType contextInfo = finder.findXmlType("contextInfo"); 134 importsAdd(contextInfo.getJavaPackage() + "." + contextInfo.getName()); 135 indentPrintln("private static ContextInfo contextInfo;"); 136 137 indentPrintln("private static " + serviceName + "RemoteImpl service;"); 138 indentPrintln(""); 139 indentPrintln(""); 140 indentPrintln("@BeforeClass"); 141 indentPrintln("public static void setUpClass() throws Exception"); 142 openBrace(); 143 indentPrintln("service = new " + serviceName + "RemoteImpl();"); 144 indentPrintln("service.setHostUrl(RemoteServiceConstants.ENV2_URL);"); 145 indentPrintln("//service.setHostUrl(RemoteServiceConstants.LOCAL_HOST_EMBEDDED_URL);"); 146 indentPrintln("contextInfo = new ContextInfo();"); 147 indentPrintln("contextInfo.setPrincipalId(\"TESTUSER\");"); 148 closeBrace(); 149 indentPrintln(""); 150 indentPrintln("@AfterClass"); 151 indentPrintln("public static void tearDownClass() throws Exception"); 152 openBrace(); 153 closeBrace(); 154 indentPrintln(""); 155 indentPrintln("@Before"); 156 indentPrintln("public void setUp()"); 157 openBrace(); 158 closeBrace(); 159 indentPrintln(""); 160 indentPrintln("@After"); 161 indentPrintln("public void tearDown()"); 162 openBrace(); 163 closeBrace(); 164 indentPrintln(""); 165 166 167 Set<XmlType> types = this.getMainXmlTypesUsedByService(methods); 168 if (types.isEmpty()) { 169 log.warn("No types defined for servKey: " + servKey); 170 return; 171 } 172 // the main servKey 173 log.info("Generating search by criteria tests for " + types.size() + " in " + servKey); 174 for (XmlType type : types) { 175 this.writeTestMethodsForXmlType(type); 176 } 177 closeBrace(); 178 179 this.writeJavaClassAndImportsOutToFile(); 180 this.getOut().close(); 181 } 182 183 private void writeTestMethodsForXmlType(XmlType xmlType) { 184 ServiceMethod searchMethod = AdminUiLookupableWriter.findSearchMethod(xmlType, methods); 185 String infoClass = GetterSetterNameCalculator.calcInitUpper(xmlType.getName()); 186 importsAdd(xmlType.getJavaPackage() + "." + xmlType.getName()); 187 indentPrintln(""); 188 indentPrintln("@Test"); 189 indentPrintln("public void testSearch" + infoClass + "All () throws Exception"); 190 openBrace(); 191 importsAdd(QueryByCriteria.class.getName()); 192 importsAdd(Predicate.class.getName()); 193 importsAdd(PredicateFactory.class.getName()); 194 indentPrintln("QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();"); 195 indentPrintln("qBuilder.setMaxResults (30);"); 196 importsAdd(List.class.getName()); 197 indentPrintln("List<" + infoClass + "> infos = service." + searchMethod.getName() + "(qBuilder.build(), contextInfo);"); 198 closeBrace(); 199 200 201 indentPrintln(""); 202 indentPrintln("@Test"); 203 indentPrintln("public void testSearch" + infoClass + "KeywordSearch () throws Exception"); 204 openBrace(); 205 importsAdd(QueryByCriteria.class.getName()); 206 importsAdd(Predicate.class.getName()); 207 importsAdd(PredicateFactory.class.getName()); 208 indentPrintln("QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();"); 209 importsAdd (ArrayList.class.getName()); 210 indentPrintln("List<Predicate> pList = new ArrayList<Predicate>();"); 211 indentPrintln("pList.add(PredicateFactory.equal(\"keywordSearch\", \"xyzzysomethingnothingmatches\"));"); 212 indentPrintln("qBuilder.setPredicates(PredicateFactory.and(pList.toArray(new Predicate[pList.size()])));"); 213 indentPrintln("qBuilder.setMaxResults (30);"); 214 importsAdd(List.class.getName()); 215 indentPrintln("List<" + infoClass + "> infos = service." + searchMethod.getName() + "(qBuilder.build(), contextInfo);"); 216 closeBrace(); 217 218 this.writeFieldTests(infoClass, searchMethod, xmlType, new Stack<XmlType>(), ""); 219 } 220 221 private void writeFieldTests(String infoClass, ServiceMethod searchMethod, XmlType type, Stack<XmlType> parents, String prefix) { 222 // avoid recursion 223 if (parents.contains(type)) { 224 return; 225 } 226 parents.push(type); 227 for (MessageStructure ms : finder.findMessageStructures(type.getName())) { 228 String fieldName = GetterSetterNameCalculator.calcInitLower(ms.getShortName()); 229 if (!prefix.isEmpty()) { 230 fieldName = prefix + "." + fieldName; 231 } 232 String fieldNameCamel = GetterSetterNameCalculator.dot2Camel(fieldName); 233 if (ms.getShortName().equalsIgnoreCase("versionInd")) { 234 indentPrintln("// TODO: deal with seaching on the version indicator which is a string in the contract but a number in the database"); 235 continue; 236 } 237 if (ms.getType().equalsIgnoreCase("AttributeInfoList")) { 238 indentPrintln("// TODO: deal with dynamic attributes"); 239 continue; 240 } 241 if (ms.getType().endsWith("List")) { 242 indentPrintln("// TODO: deal with " + fieldName + " which is a List"); 243 continue; 244 } 245 XmlType fieldType = finder.findXmlType(ms.getType()); 246 if (fieldType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) { 247 // complex sub-types such as rich text 248 this.writeFieldTests(infoClass, searchMethod, fieldType, parents, fieldName); 249 continue; 250 } 251 if (!ms.getType().equalsIgnoreCase("String")) { 252 indentPrintln("// TODO: deal with " + fieldName + " which is a " + ms.getType()); 253 continue; 254 } 255 256 indentPrintln(""); 257 indentPrintln("@Test"); 258 indentPrintln("public void testSearch" + infoClass + "By" + fieldNameCamel + " () throws Exception"); 259 openBrace(); 260 indentPrintln("QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();"); 261 indentPrintln("List<Predicate> pList = new ArrayList<Predicate>();"); 262 indentPrintln("pList.add(PredicateFactory.equal(\"" + fieldName + "\", \"xyzzysomethingnothingmatches\"));"); 263 indentPrintln("qBuilder.setPredicates(PredicateFactory.and(pList.toArray(new Predicate[pList.size()])));"); 264 indentPrintln("qBuilder.setMaxResults (30);"); 265 indentPrintln("List<" + infoClass + "> infos = service." + searchMethod.getName() + "(qBuilder.build(), contextInfo);"); 266 closeBrace(); 267 268 } 269 parents.pop(); 270 } 271 272 private String calcType(String type, String realType) { 273 XmlType t = finder.findXmlType(this.stripList(type)); 274 return MessageStructureTypeCalculator.calculate(this, model, type, realType, 275 t.getJavaPackage()); 276 } 277 278 private String stripList(String str) { 279 return GetterSetterNameCalculator.stripList(str); 280 } 281 282 private String calcExceptionClassName(ServiceMethodError error) { 283 if (error.getClassName() == null) { 284 return ServiceExceptionWriter.calcClassName(error.getType()); 285 } 286 return error.getClassName(); 287 } 288 289 private String calcExceptionPackageName(ServiceMethodError error) { 290 if (error.getClassName() == null) { 291 return ServiceExceptionWriter.calcPackage(rootPackage); 292 } 293 return error.getPackageName(); 294 } 295 296 private Set<XmlType> getMainXmlTypesUsedByService(List<ServiceMethod> methods) { 297 Set<XmlType> set = new HashSet(); 298 for (ServiceMethod method : methods) { 299 if (method.getReturnValue().getType().endsWith("List")) { 300 continue; 301 } 302 XmlType returnType = finder.findXmlType(method.getReturnValue().getType()); 303 if (returnType == null) { 304 continue; 305 } 306 if (!returnType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) { 307 continue; 308 } 309 // TYPE only should show up on type service 310 if (returnType.getName().equalsIgnoreCase("TypeInfo")) { 311 if (!servKey.equalsIgnoreCase("type")) { 312 continue; 313 } 314 } 315 // State only should show up on type service 316 if (returnType.getName().equalsIgnoreCase("StateInfo")) { 317 if (!servKey.equalsIgnoreCase("state")) { 318 continue; 319 } 320 } 321 // if (method.getName().startsWith("create")) { 322 // set.add(returnType); 323 // continue; 324 // } 325 // if (method.getName().startsWith("update")) { 326 // set.add(returnType); 327 // continue; 328 // } 329 if (method.getName().startsWith("get")) { 330 if (method.getParameters().size() == 2) { 331 if (method.getParameters().get(0).getType().equalsIgnoreCase("String")) { 332 set.add(returnType); 333 continue; 334 } 335 } 336 } 337 } 338 return set; 339 } 340 341 private List<MessageStructure> getFieldsToSearchOn(XmlType xmlType) { 342 List<MessageStructure> list = new ArrayList<MessageStructure>(); 343 for (MessageStructure ms : finder.findMessageStructures(xmlType.getName())) { 344 XmlType msType = finder.findXmlType(ms.getType()); 345 // TODO: dive down into complex sub-types 346 if (msType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) { 347 continue; 348 } 349 list.add(ms); 350 } 351 return list; 352 } 353 }