001/**
002 * Copyright 2005-2013 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.rice.kns.workflow;
017
018import org.apache.commons.logging.Log;
019import org.apache.commons.logging.LogFactory;
020import org.junit.Ignore;
021import org.junit.Test;
022import org.kuali.rice.core.api.impex.xml.XmlConstants;
023import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
024import org.kuali.rice.core.api.reflect.ObjectDefinition;
025import org.kuali.rice.kew.api.KewApiConstants;
026import org.kuali.rice.kew.api.extension.ExtensionDefinition;
027import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
028import org.kuali.rice.krad.datadictionary.AttributeDefinition;
029import org.kuali.rice.krad.datadictionary.BusinessObjectEntry;
030import org.kuali.rice.krad.datadictionary.DocumentEntry;
031import org.kuali.rice.krad.service.DataDictionaryService;
032import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
033import org.kuali.rice.kns.workflow.attribute.KualiXmlAttribute;
034import org.kuali.rice.kns.workflow.attribute.KualiXmlAttributeHelper;
035import org.kuali.rice.krad.test.KRADTestCase;
036import org.w3c.dom.NamedNodeMap;
037import org.w3c.dom.Node;
038import org.w3c.dom.NodeList;
039import org.xml.sax.InputSource;
040
041import javax.xml.transform.Result;
042import javax.xml.transform.Source;
043import javax.xml.transform.TransformerException;
044import javax.xml.transform.TransformerFactory;
045import javax.xml.transform.dom.DOMSource;
046import javax.xml.transform.stream.StreamResult;
047import javax.xml.xpath.XPath;
048import javax.xml.xpath.XPathConstants;
049import java.io.StringReader;
050import java.io.StringWriter;
051import java.util.HashSet;
052import java.util.Set;
053
054import org.junit.Assert;
055
056
057/**
058 * KualiXMLAttributeImplTest tests the {@link KualiXmlAttributeHelper} operations of getting data from the data dictionary for workflow
059 * attributes
060 * 
061 * @author Kuali Rice Team (rice.collab@kuali.org)
062 */
063@Ignore
064public class KualiXMLAttributeImplTest extends KRADTestCase {
065    private static Log LOG = LogFactory.getLog(KualiXMLAttributeImplTest.class);
066
067    private static final String RULE_ATTRIBUTE_CONFIG_NODE_NAME = XmlConstants.ROUTING_CONFIG;
068    private static final String SEARCH_ATTRIBUTE_CONFIG_NODE_NAME = XmlConstants.SEARCHING_CONFIG;
069
070    XPath myXPath = XPathHelper.newXPath();
071    String ruleAttributeXml = "";
072    String searchAttributeXml = "";
073//    private boolean testFailed = false;
074
075    @Override
076    public void setUp() throws Exception {
077        super.setUp();
078        // read in the file and make it a string
079
080//        InputStream is = new FileInputStream(getBaseDir() + "/src/test/resources/org/kuali/rice/kew/batch/data/RuleAttributeContent.xml");
081//        is.
082//        if ((StringUtils.isNotBlank(ruleAttributeXml)) && (StringUtils.isNotBlank(searchAttributeXml))) {
083//            return;
084//        }
085//
086//        DataSource mySource = KEWServiceLocator.getDataSource();
087//        ruleAttributeXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<data >\n<ruleAttributes>\n";
088//        searchAttributeXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<data >\n<ruleAttributes>\n";
089//        Connection dbCon = null;
090//        try {
091//
092//            dbCon = mySource.getConnection();
093//            Statement dbAsk = dbCon.createStatement();
094//            ResultSet dbAnswer = dbAsk.executeQuery("select * from EN_RULE_ATTRIB_T");
095//            // ResultSet dbAnswer = dbAsk.executeQuery("select * from EN_RULE_ATTRIB_T where RULE_ATTRIB_NM =
096//            // 'SystemParameterRoutingAttribute'");
097//
098//            while (dbAnswer.next()) {
099//                String className = dbAnswer.getString("RULE_ATTRIB_CLS_NM");
100//                if (StringUtils.isNotBlank(className)) {
101//                    try {
102//                        if (KualiXmlAttribute.class.isAssignableFrom(Class.forName(className))) {
103//                            LOG.debug("Adding attribute to test with class name " + className);
104//                            String attributeType = dbAnswer.getString("RULE_ATTRIB_TYP");
105//                            if (KewApiConstants.RULE_XML_ATTRIBUTE_TYPE.equals(attributeType)) {
106//                                ruleAttributeXml = ruleAttributeXml + "<ruleAttribute>\n\t<name>";
107//                                ruleAttributeXml = ruleAttributeXml + dbAnswer.getString("RULE_ATTRIB_NM");
108//                                ruleAttributeXml = ruleAttributeXml + "</name>\n\t<className>";
109//                                ruleAttributeXml = ruleAttributeXml + className;
110//                                ruleAttributeXml = ruleAttributeXml + "</className>\n\t<label>";
111//                                ruleAttributeXml = ruleAttributeXml + dbAnswer.getString("RULE_ATTRIB_LBL_TXT");
112//                                ruleAttributeXml = ruleAttributeXml + "</label>\n\t<description>";
113//                                ruleAttributeXml = ruleAttributeXml + dbAnswer.getString("RULE_ATTRIB_DESC");
114//                                ruleAttributeXml = ruleAttributeXml + "</description>\n\t<type>";
115//                                ruleAttributeXml = ruleAttributeXml + attributeType;
116//                                ruleAttributeXml = ruleAttributeXml + "</type>\n\t" + dbAnswer.getString("RULE_ATTRIB_XML_RTE_TXT") + "\n</ruleAttribute>\n";
117//
118//                            }
119//                            else if (KewApiConstants.SEARCHABLE_XML_ATTRIBUTE_TYPE.equals(attributeType)) {
120//                                searchAttributeXml = searchAttributeXml + "<ruleAttribute>\n\t<name>";
121//                                searchAttributeXml = searchAttributeXml + dbAnswer.getString("RULE_ATTRIB_NM");
122//                                searchAttributeXml = searchAttributeXml + "</name>\n\t<className>";
123//                                searchAttributeXml = searchAttributeXml + className;
124//                                searchAttributeXml = searchAttributeXml + "</className>\n\t<label>";
125//                                searchAttributeXml = searchAttributeXml + dbAnswer.getString("RULE_ATTRIB_LBL_TXT");
126//                                searchAttributeXml = searchAttributeXml + "</label>\n\t<description>";
127//                                searchAttributeXml = searchAttributeXml + dbAnswer.getString("RULE_ATTRIB_DESC");
128//                                searchAttributeXml = searchAttributeXml + "</description>\n\t<type>";
129//                                searchAttributeXml = searchAttributeXml + attributeType;
130//                                searchAttributeXml = searchAttributeXml + "</type>\n\t" + dbAnswer.getString("RULE_ATTRIB_XML_RTE_TXT") + "\n</ruleAttribute>\n";
131//
132//                            }
133//                        }
134//                        else {
135//                            LOG.debug("Skipping attribute with class name " + className);
136//                        }
137//                    }
138//                    catch (ClassNotFoundException cnfe) {
139//                        LOG.debug("Could not find class for name '" + className + "'");
140//                    }
141//                }
142//            }
143//            ruleAttributeXml = ruleAttributeXml + "</ruleAttributes>\n</data>\n";
144//            searchAttributeXml = searchAttributeXml + "</ruleAttributes>\n</data>\n";
145//
146//            ruleAttributeXml = ruleAttributeXml.replaceAll(" & ", " &amp; ");
147//            searchAttributeXml = searchAttributeXml.replaceAll(" & ", " &amp; ");
148//
149//            loadDataDictionaryEntries();
150//        }
151//        catch (Exception e) {
152//            e.printStackTrace();
153//        }
154//        finally {
155//            try {
156//                dbCon.close();
157//            }
158//            catch (SQLException sqle2) {
159//                sqle2.printStackTrace();
160//            }
161//        }
162    }
163
164    /**
165     * goes through all of the ruleAttributes in the inputSource and tries to get a label out of the data dictionary
166     */
167    @Test public void testConfirmLabels() {
168        boolean failed = false;
169        // test rule xml attributes
170        failed |= confirmLabels(KualiXmlAttributeHelper.notFound, ruleAttributeXml, RULE_ATTRIBUTE_CONFIG_NODE_NAME);
171
172        // test search xml attributes
173        failed |= confirmLabels(KualiXmlAttributeHelper.notFound, searchAttributeXml, SEARCH_ATTRIBUTE_CONFIG_NODE_NAME);
174        
175        Assert.assertFalse("At least one label was incorrect", failed);
176    }
177
178    /**
179     * accepts a Node, and if all goes well, returns the exact same Node with expected changes
180     *
181     * <p>The node should have the name and title attributes added to
182     * the fieldDef element. This exercises the getConfigXML method on the class under test.</p>
183     * 
184     * @param xmlNode - an input node
185     * @return the input node with attributes added
186     * @throws TransformerException
187     */
188    private Node configureRuleAttribute(Node xmlNode, KualiXmlAttribute myAttribute) throws TransformerException {
189        ExtensionDefinition.Builder extensionDefinition = ExtensionDefinition.Builder.create("fakeName", "fakeType", "fakeResourceDescriptor");
190
191        StringWriter xmlBuffer = new StringWriter();
192        Source source = new DOMSource(xmlNode);
193        Result result = new StreamResult(xmlBuffer);
194        TransformerFactory.newInstance().newTransformer().transform(source, result);
195
196        extensionDefinition.getConfiguration().put(KewApiConstants.ATTRIBUTE_XML_CONFIG_DATA, new String(xmlBuffer.getBuffer()));
197
198        if (LOG.isDebugEnabled()) {
199            LOG.debug("This is the XML that was added to the attribute");
200            LOG.debug(new String(xmlBuffer.getBuffer()));
201            StringWriter xmlBuffer2 = new StringWriter();
202            Source source2 = new DOMSource(xmlNode);
203            Result result2 = new StreamResult(xmlBuffer2);
204            TransformerFactory.newInstance().newTransformer().transform(source2, result2);
205            LOG.debug("This is the XML that was returned from the ruleAttribute");
206            LOG.debug(new String(xmlBuffer2.getBuffer()));
207        }
208        return myAttribute.getConfigXML(extensionDefinition.build());
209    }
210
211    /**
212     * compares the label from the test to the expected, or not expected, value for all of the rule attributes in the file
213     *
214     * <p>The inputSource file should be as close to the production version as possible, as described by the class comments. It
215     * accepts the string to test against as a parameter.</p>
216     * 
217     * @param testString
218     * @param attributeXml
219     * @param configNodeName
220     */
221    private boolean confirmLabels(String testString, String attributeXml, String configNodeName) {
222        boolean testFailed = false;
223        String theTitle = "";
224        String theName = "";
225        String attributeName = "";
226        try {
227            NodeList tempList = (NodeList) myXPath.evaluate("//ruleAttribute", new InputSource(new StringReader(attributeXml)), XPathConstants.NODESET);
228            for (int i = 0; i < tempList.getLength(); i++) { // loop over ruleattributes
229                Node originalNode = tempList.item(i);
230                Set ruleAttributeFieldDefNames = new HashSet();
231                Set ruleAttributeFieldDefTitles = new HashSet();
232                attributeName = (String) myXPath.evaluate(WorkflowUtils.XSTREAM_MATCH_RELATIVE_PREFIX + "name", originalNode, XPathConstants.STRING);
233                Node classNameNode = (Node) myXPath.evaluate(WorkflowUtils.XSTREAM_MATCH_RELATIVE_PREFIX + "className", originalNode, XPathConstants.NODE);
234                if ((classNameNode != null) && (classNameNode.getFirstChild() != null)) {
235                    if (LOG.isInfoEnabled()) {
236                        LOG.info("Checking attribute with name '" + attributeName + "'");
237                    }
238                    KualiXmlAttribute myAttribute = (KualiXmlAttribute) GlobalResourceLoader.getObject(new ObjectDefinition(classNameNode.getFirstChild().getNodeValue()));
239                    Node xmlNode = configureRuleAttribute(originalNode, myAttribute);
240                    NamedNodeMap fieldDefAttributes = null;
241                    String potentialFailMessage = "";
242
243                    try {
244                        NodeList xmlNodeList = (NodeList) myXPath.evaluate("//fieldDef", xmlNode, XPathConstants.NODESET);
245
246                        for (int j = 0; j < xmlNodeList.getLength(); j++) {
247                            Node fieldDefXmlNode = xmlNodeList.item(j);
248                            fieldDefAttributes = fieldDefXmlNode.getAttributes();
249
250                            theTitle = fieldDefAttributes.getNamedItem("title").getNodeValue();// Making sure they are clean
251                            theName = fieldDefAttributes.getNamedItem("name").getNodeValue();
252                            if (LOG.isDebugEnabled()) {
253                                LOG.debug(attributeName);
254                                LOG.debug("name=" + theName + "   title=" + theTitle);
255                            }
256                            if (ruleAttributeFieldDefNames.contains(theName)) {
257                                // names of fieldDefs inside a single attribute must be unique
258                                potentialFailMessage = "Each fieldDef name on a single attribute must be unique and the fieldDef name '" + theName + "' already exists on the attribute '" + attributeName + "'";
259                                Assert.fail(potentialFailMessage);
260                            }
261                            else {
262                                ruleAttributeFieldDefNames.add(theName);
263                            }
264                            if (testString.equals(KualiXmlAttributeHelper.notFound)) {
265                                potentialFailMessage = "Each fieldDef title should be a valid value and currently the title for attribute '" + attributeName + "' is '" + theTitle + "'";
266                                Assert.assertFalse(potentialFailMessage, theTitle.equals(testString));
267                                if (ruleAttributeFieldDefTitles.contains(theTitle)) {
268                                    /*
269                                     * Titles of fieldDefs inside a single attribute should be unique in the normal case. Having two
270                                     * fields with the same label would certainly confuse the user. However, due to the way the
271                                     * confirmSource test works, all the titles/labels must be the same. So only run this check when
272                                     * not in the confirmSource test.
273                                     */
274                                    potentialFailMessage = "Each fieldDef title on a single attribute must be unique and the fieldDef title '" + theTitle + "' already exists on the attribute '" + attributeName + "'";
275                                    Assert.fail(potentialFailMessage);
276                                }
277                                else {
278                                    ruleAttributeFieldDefTitles.add(theTitle);
279                                }
280                            }
281                            else {
282                                potentialFailMessage = "For attribute '" + attributeName + "' the title should have been '" + testString + "' but was actually '" + theTitle + "'";
283                                Assert.assertEquals(potentialFailMessage, testString, theTitle);
284                            }
285                        }
286                    }
287                    catch (AssertionError afe) {
288                        LOG.warn("Assertion Failed for attribute '" + attributeName + "' with error " + potentialFailMessage, afe);
289                        testFailed = true;
290                    }
291                    finally {
292                        attributeName = "";
293                    }
294                }
295                else {
296                    throw new RuntimeException("Could not find class for attribute named '" + attributeName + "'");
297                }
298            }
299        }
300        catch (Exception e) {
301            LOG.error("General Exception thrown for attribute '" + attributeName + "'", e);
302            testFailed = true;
303        }
304        return testFailed;
305    }
306
307    /**
308     * confirms that the labels are coming from the data dictionary
309     *
310     * <p>This is done by modifying all the dictionary values
311     * programatically to a nonsense value. It then rebuilds the Hash Table and runs confirmLabels() to make sure the labels have
312     * changed.</p>
313     */
314    @Test public void testLabelSource() {
315        DataDictionaryService myDDService = KRADServiceLocatorWeb.getDataDictionaryService();
316        XPath xpath = XPathHelper.newXPath();
317        String nonsenseString = "BananaRama";
318        for (Object tempEntity : myDDService.getDataDictionary().getBusinessObjectEntries().values()) {
319
320            for ( AttributeDefinition attribute : ((BusinessObjectEntry) tempEntity).getAttributes() ) {
321                attribute.setLabel(nonsenseString);
322                attribute.setShortLabel(nonsenseString);
323            }
324
325        }
326        for (Object tempEntity : myDDService.getDataDictionary().getDocumentEntries().values()) {
327
328            for ( AttributeDefinition attribute : ((DocumentEntry) tempEntity).getAttributes() ) {
329                attribute.setLabel(nonsenseString);
330                attribute.setShortLabel(nonsenseString);
331            }
332
333        }
334        // KualiXmlAttributeHelper.buildDictionaryHash();
335
336        boolean failed = false;
337        Assert.assertFalse("At least one label was incorrect", failed);
338        // test rule xml attributes
339        failed |= confirmLabels(nonsenseString, ruleAttributeXml, RULE_ATTRIBUTE_CONFIG_NODE_NAME);
340
341        // test search xml attributes
342        failed |= confirmLabels(nonsenseString, searchAttributeXml, SEARCH_ATTRIBUTE_CONFIG_NODE_NAME);
343
344        Assert.assertFalse("At least one label was incorrect", failed);
345    }
346
347    /*
348    unused method
349
350    private void loadDataDictionaryEntries() throws Exception {
351        KualiXmlRuleAttributeImpl myAttribute = new KualiXmlRuleAttributeImpl();
352        NamedNodeMap fieldDefAttributes = null;
353        NodeList tempList = (NodeList) myXPath.evaluate("//ruleAttribute", new InputSource(new StringReader(ruleAttributeXml)), XPathConstants.NODESET);
354        for (int i = 0; i < tempList.getLength(); i++) {
355            Node xmlNode = configureRuleAttribute(tempList.item(i), myAttribute);
356        }
357        KualiXmlSearchableAttributeImpl mySearchAttribute = new KualiXmlSearchableAttributeImpl();
358        fieldDefAttributes = null;
359        tempList = (NodeList) myXPath.evaluate("//ruleAttribute", new InputSource(new StringReader(searchAttributeXml)), XPathConstants.NODESET);
360        for (int i = 0; i < tempList.getLength(); i++) {
361            Node xmlNode = configureRuleAttribute(tempList.item(i), mySearchAttribute);
362        }
363    }*/
364}