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    }