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 }