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.admin.ui.mojo;
017
018import java.io.File;
019import java.io.FileNotFoundException;
020import java.io.FileOutputStream;
021import java.io.PrintStream;
022import java.util.ArrayList;
023import java.util.List;
024import java.util.Stack;
025import org.kuali.student.contract.model.Lookup;
026import org.kuali.student.contract.model.MessageStructure;
027import org.kuali.student.contract.model.Service;
028
029import org.kuali.student.contract.model.ServiceContractModel;
030import org.kuali.student.contract.model.ServiceMethod;
031import org.kuali.student.contract.model.XmlType;
032import org.kuali.student.contract.model.impl.ServiceContractModelQDoxLoader;
033import org.kuali.student.contract.model.util.ModelFinder;
034import org.kuali.student.contract.writer.XmlWriter;
035import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039/**
040 *
041 * @author nwright
042 */
043public class AdminUiLookupViewBeanWriter {
044
045        private static final Logger log = LoggerFactory.getLogger(AdminUiLookupViewBeanWriter.class);
046    
047    private ServiceContractModel model;
048    private ModelFinder finder;
049    private String directory;
050    private String rootPackage;
051    private String servKey;
052    private Service service;
053    private XmlType xmlType;
054    private List<ServiceMethod> methods;
055    private XmlWriter out;
056    String fileName;
057    String fullDirectoryPath;
058
059    public AdminUiLookupViewBeanWriter(ServiceContractModel model,
060            String directory,
061            String rootPackage,
062            String servKey,
063            XmlType xmlType,
064            List<ServiceMethod> methods) {
065        this.model = model;
066        this.finder = new ModelFinder(model);
067        this.directory = directory;
068        this.rootPackage = rootPackage;
069        this.servKey = servKey;
070        service = finder.findService(servKey);
071        this.xmlType = xmlType;
072        this.methods = methods;
073    }
074
075    /**
076     * Write out the entire file
077     *
078     * @param out
079     */
080    public void write() {
081        initXmlWriter();
082        writeBoilerPlate();
083        writeBean();
084    }
085
086    private void writeBean() {
087        String infoClass = GetterSetterNameCalculator.calcInitUpper(xmlType.getName());
088        String serviceClass = GetterSetterNameCalculator.calcInitUpper(service.getName());
089        String serviceVar = GetterSetterNameCalculator.calcInitLower(service.getName());
090        String serviceContract = service.getImplProject() + "." + service.getName();
091        String lookupable = AdminUiLookupableWriter.calcPackage(servKey, rootPackage, xmlType) + "."
092                + AdminUiLookupableWriter.calcClassName(servKey, xmlType);
093        String viewId = "KS-" + infoClass + "-AdminLookupView";
094        out.println("");
095        out.incrementIndent();
096        out.indentPrintln("<import resource=\"classpath:ks-" + infoClass + "-dictionary.xml\"/>");
097        out.indentPrintln("<import resource=\"classpath:UifKSDefinitions.xml\"/>");
098        out.indentPrintln("<!-- **********************************************");
099        out.indentPrintln("Paste this link below into WEB-INF ksAdminLinks.tag or ksMaintenance.tag in KS with Rice Bundled etc");
100        out.indentPrintln("");
101        out.indentPrintln("<li><portal:portalLink displayTitle=\"true\" title=\"" + xmlType.getName() + " Lookup\""
102                + " url=\"${ConfigProperties.application.url}/kr-krad/lookup?methodToCall=start"
103                + "&dataObjectClassName=" + xmlType.getJavaPackage() + "." + infoClass + ""
104                + "&viewId=" + viewId
105                + "&returnLocation=${ConfigProperties.application.url}/portal.do&hideReturnLink=true\" /></li>");
106        out.indentPrintln ("Also...");
107        out.indentPrintln("Paste bean definition below into the list of dataDictionaryPackages in StudentSpringBeans.xml ");
108        out.indentPrintln ("<value>classpath:" + fullDirectoryPath + "/" + fileName + "</value>");
109        out.indentPrintln("********************************************** -->");
110        out.indentPrintln("<!-- LookupView -->");
111        out.indentPrintln("<bean id=\"" + viewId + "\" parent=\"KS-Uif-LookupView\"");
112        out.incrementIndent();
113        out.indentPrintln("p:title=\"" + xmlType.getName() + " Lookup\"");
114        out.indentPrintln("p:header.headerText=\"" + xmlType.getName() + " Lookup\"");
115        out.indentPrintln("p:dataObjectClassName=\"" + xmlType.getJavaPackage() + "." + infoClass + "\"");
116        out.indentPrintln("p:viewHelperServiceClass=\"" + lookupable + "\">");
117        out.indentPrintln("");
118        out.indentPrintln("<property name=\"criteriaFields\">");
119        out.incrementIndent();
120        out.indentPrintln("<list>");
121        out.incrementIndent();
122        out.indentPrintln("<bean parent=\"Uif-LookupCriteriaInputField\" p:propertyName=\"keywordSearch\"");
123        out.indentPrintln("      p:label=\"Keyword(s)\"");
124        out.indentPrintln("      p:helpSummary=\"Searches fields like name and description to see if they contain the keyword\" />");
125        this.writeFieldsToSearchOn (xmlType, new Stack<XmlType>(), "");
126        out.indentPrintln("<bean parent=\"Uif-LookupCriteriaInputField\" p:propertyName=\"maxResultsToReturn\"");
127        out.indentPrintln("      p:label=\"Max. Results\"");
128        out.indentPrintln("      p:defaultValue=\"50\"");
129        out.indentPrintln("      p:helpSummary=\"The maximum number of results to return from the query, leave null to not limit the results\" />");
130        out.decrementIndent();
131        out.indentPrintln("</list>");
132        out.decrementIndent();
133        out.indentPrintln("</property>");
134        out.indentPrintln("<property name=\"resultFields\">");
135        out.indentPrintln("    <list>");
136        for (MessageStructure ms : this.getFieldsToShowOnLookup()) {
137            String fieldName = GetterSetterNameCalculator.calcInitLower(ms.getShortName());
138            out.indentPrint("        <bean parent=\"Uif-DataField\" p:propertyName=\"" + fieldName + "\"");
139            if (ms.isPrimaryKey()) {
140                out.println(">");
141                out.indentPrintln("            <property name=\"inquiry\">");
142                out.indentPrintln("                <bean parent=\"Uif-Inquiry\" p:dataObjectClassName=\"" + xmlType.getJavaPackage() + "." + xmlType.getName() + "\" p:inquiryParameters=\"" + ms.getShortName() + "\" />");
143                out.indentPrintln("            </property>");
144                out.indentPrintln("        </bean>");
145            } else if (AdminUiInquiryViewBeanWriter.shouldDoLookup (ms.getLookup())) {
146                
147                out.println(">");
148                Lookup lookup = ms.getLookup();
149                XmlType msType = finder.findXmlType(lookup.getXmlTypeName());
150                MessageStructure pk = this.getPrimaryKey(ms.getLookup().getXmlTypeName());
151                out.indentPrintln("            <property name=\"inquiry\">");
152                out.indentPrintln("                <bean parent=\"Uif-Inquiry\" p:dataObjectClassName=\""
153                        + msType.getJavaPackage() + "." + msType.getName()
154                        + "\" p:inquiryParameters=\"" + fieldName + ":" + pk.getShortName() + "\" />");
155                out.indentPrintln("            </property>");
156                out.indentPrintln("        </bean>");
157            } else {
158                out.println(" />");
159            }
160        }
161        out.indentPrintln("    </list>");
162        out.indentPrintln("</property>");
163        out.decrementIndent();
164        out.indentPrintln("</bean>");
165        out.indentPrintln("");
166        out.decrementIndent();
167        out.indentPrintln("</beans>");
168    }
169
170    
171    private MessageStructure getPrimaryKey(String xmlTypeName) {
172        for (MessageStructure ms : finder.findMessageStructures(xmlTypeName)) {
173            if (ms.isPrimaryKey()) {
174                return ms;
175            }
176        }
177        throw new NullPointerException ("could not find primary key for " + xmlTypeName);
178    }
179
180    
181    private void writeFieldsToSearchOn(XmlType type, Stack<XmlType> parents, String prefix) {
182         // avoid recursion
183        if (parents.contains(type)) {
184            return;
185        }
186        parents.push(type);
187        for (MessageStructure ms : finder.findMessageStructures(type.getName())) {
188            String fieldName = GetterSetterNameCalculator.calcInitLower(ms.getShortName());
189            if (!prefix.isEmpty()) {
190                fieldName = prefix + "." + fieldName;
191            }
192            String fieldNameCamel = GetterSetterNameCalculator.dot2Camel(fieldName);
193            if (ms.getShortName().equalsIgnoreCase("versionInd")) {
194                out.indentPrintln("<!-- TODO: deal with seaching on the version indicator which is a string in the contract but a number in the database -->");
195                continue;
196            }
197            if (ms.getType().equalsIgnoreCase("AttributeInfoList")) {
198                out.indentPrintln("<!-- TODO: deal with dynamic attributes -->");
199                continue;
200            }
201            if (ms.getShortName ().equalsIgnoreCase("name")) {
202                out.indentPrintln("<!-- skip name because keyword searching should cover it -->");
203                continue;
204            }
205            if (ms.getShortName ().equalsIgnoreCase("descr")) {
206                out.indentPrintln("<!-- skip description because keyword searching should cover it -->");
207                continue;
208            }
209            if (ms.getType().endsWith("List")) {
210                out.indentPrintln("<!-- TODO: deal with  " + fieldName + " which is a List -->");
211                continue;
212            }
213            XmlType fieldType = finder.findXmlType(ms.getType());
214            if (fieldType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
215                // complex sub-types such as rich text 
216                this.writeFieldsToSearchOn(fieldType, parents, fieldName);
217                continue;
218            }
219            if (!ms.getType().equalsIgnoreCase("String")) {
220                out.indentPrintln("<!-- TODO: deal with  " + fieldName + " which is a " + ms.getType() + " -->");
221                continue;
222            }
223
224            out.indentPrint("<bean parent=\"Uif-LookupCriteriaInputField\" p:propertyName=\"" + fieldName + "\"");
225            if (!AdminUiInquiryViewBeanWriter.shouldDoLookup (ms.getLookup())) {
226                out.println(" />");
227                continue;
228            }
229            out.println(">");
230            out.incrementIndent();
231            XmlType msType = finder.findXmlType(ms.getLookup().getXmlTypeName());
232            if (msType == null) {
233                throw new NullPointerException ("Processing lookup for: " + type.getName () 
234                        + ms.getName() 
235                        + " lookup=" + ms.getLookup().getXmlTypeName());
236            }
237            out.indentPrintln("<property name=\"quickfinder\">");
238            out.incrementIndent();
239            out.indentPrintln("<bean parent=\"Uif-QuickFinder\" p:dataObjectClassName=\"" + msType.getJavaPackage() + "." + msType.getName() + "\"");
240            MessageStructure pk = this.getPrimaryKey(ms.getLookup().getXmlTypeName());
241            if (pk == null) {
242                throw new NullPointerException("could not find primary key for " + ms.getId());
243            }
244            out.indentPrintln("p:fieldConversions=\"" + pk.getShortName() + ":" + fieldName + "\" />");
245            out.decrementIndent();
246            out.indentPrintln("</property>");
247            out.decrementIndent();
248            out.indentPrintln("</bean>");
249        }
250        parents.pop();
251    }
252    
253    private List<MessageStructure> getFieldsToSearchOn() {
254        List<MessageStructure> list = new ArrayList<MessageStructure>();
255        for (MessageStructure ms : finder.findMessageStructures(xmlType.getName())) {
256            // lists of values cannot be displayed within a list of values
257            if (ms.getType().endsWith("List")) {
258                continue;
259            }
260            if (ms.getType().endsWith("List")) {
261                continue;
262            }
263            XmlType msType = finder.findXmlType(ms.getType());
264            // just show fields on the main object for now don't dive down into complex fields
265            if (msType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
266                continue;
267            }
268        }
269        return list;
270    }
271
272    private List<MessageStructure> getFieldsToShowOnLookup() {
273        List<MessageStructure> list = new ArrayList<MessageStructure>();
274        for (MessageStructure ms : finder.findMessageStructures(xmlType.getName())) {
275            // lists of values cannot be displayed within a list of values
276            if (ms.getType().endsWith("List")) {
277                continue;
278            }
279            XmlType msType = finder.findXmlType(ms.getType());
280            // just show fields on the main object for now don't dive down into complex fields
281            if (msType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
282                continue;
283            }
284            list.add(ms);
285        }
286        return list;
287    }
288
289    private void initXmlWriter() {
290        String infoClass = GetterSetterNameCalculator.calcInitUpper(xmlType.getName());
291        String serviceClass = GetterSetterNameCalculator.calcInitUpper(service.getName());
292        String serviceVar = GetterSetterNameCalculator.calcInitLower(service.getName());
293        String serviceContract = service.getImplProject() + "." + service.getName();
294
295        fullDirectoryPath = AdminUiLookupableWriter.calcPackage(servKey, rootPackage, xmlType).replace('.', '/');
296        fileName = infoClass + "AdminLookupView.xml";
297
298        File dir = new File(this.directory);
299
300        if (!dir.exists()) {
301            if (!dir.mkdirs()) {
302                throw new IllegalStateException("Could not create directory "
303                        + this.directory);
304            }
305        }
306
307        String dirStr = this.directory + "/" + "resources" + "/" + fullDirectoryPath;
308        File dirFile = new File(dirStr);
309        if (!dirFile.exists()) {
310            if (!dirFile.mkdirs()) {
311                throw new IllegalStateException(
312                        "Could not create directory " + dirStr);
313            }
314        }
315        try {
316            PrintStream out = new PrintStream(new FileOutputStream(
317                    dirStr + "/"
318                    + fileName, false));
319            log.info("AdminUILookupViewBeanWriter: writing " + dirStr + "/" + fileName);
320            this.out = new XmlWriter(out, 0);
321        } catch (FileNotFoundException ex) {
322            throw new IllegalStateException(ex);
323        }
324    }
325
326    private void writeBoilerPlate() {
327        out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
328        out.println("<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
329        out.println("       xmlns:p=\"http://www.springframework.org/schema/p\"");
330        out.println("       xsi:schemaLocation=\"http://www.springframework.org/schema/beans");
331        out.println("                    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\">");
332        out.println("    <!--");
333        out.println("     Copyright 2007-2012 The Kuali Foundation");
334        out.println("");
335        out.println("     Licensed under the Educational Community License, Version 2.0 (the \"License\");");
336        out.println("     you may not use this file except in compliance with the License.");
337        out.println("     You may obtain a copy of the License at");
338        out.println("");
339        out.println("     http://www.opensource.org/licenses/ecl2.php");
340        out.println("");
341        out.println("     Unless required by applicable law or agreed to in writing, software");
342        out.println("     distributed under the License is distributed on an \"AS IS\" BASIS,");
343        out.println("     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.");
344        out.println("     See the License for the specific language governing permissions and");
345        out.println("     limitations under the License.");
346        out.println("    -->");
347
348    }
349}