Coverage Report - org.kuali.rice.kns.workflow.attribute.KualiXmlAttributeHelper
 
Classes in this File Line Coverage Branch Coverage Complexity
KualiXmlAttributeHelper
0%
0/195
0%
0/98
7.6
 
 1  
 /*
 2  
  * Copyright 2007 The Kuali Foundation
 3  
  * 
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kns.workflow.attribute;
 17  
 
 18  
 import java.io.StringWriter;
 19  
 import java.util.ArrayList;
 20  
 import java.util.Iterator;
 21  
 import java.util.List;
 22  
 import java.util.regex.Matcher;
 23  
 import java.util.regex.Pattern;
 24  
 
 25  
 import javax.xml.transform.Result;
 26  
 import javax.xml.transform.Source;
 27  
 import javax.xml.transform.TransformerFactory;
 28  
 import javax.xml.transform.dom.DOMSource;
 29  
 import javax.xml.transform.stream.StreamResult;
 30  
 import javax.xml.xpath.XPath;
 31  
 import javax.xml.xpath.XPathConstants;
 32  
 import javax.xml.xpath.XPathExpressionException;
 33  
 
 34  
 import org.apache.commons.lang.StringUtils;
 35  
 import org.apache.commons.logging.Log;
 36  
 import org.apache.commons.logging.LogFactory;
 37  
 import org.kuali.rice.core.util.KeyLabelPair;
 38  
 import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
 39  
 import org.kuali.rice.kew.util.XmlHelper;
 40  
 import org.kuali.rice.kns.lookup.keyvalues.KeyValuesFinder;
 41  
 import org.kuali.rice.kns.service.DataDictionaryService;
 42  
 import org.kuali.rice.kns.service.KNSServiceLocator;
 43  
 import org.w3c.dom.Element;
 44  
 import org.w3c.dom.NamedNodeMap;
 45  
 import org.w3c.dom.Node;
 46  
 import org.w3c.dom.NodeList;
 47  
 
 48  
 
 49  0
 public class KualiXmlAttributeHelper {
 50  0
     private static Log LOG = LogFactory.getLog(KualiXmlRuleAttributeImpl.class);
 51  0
     private static XPath xpath = XPathHelper.newXPath();
 52  
     private static final String testVal = "\'/[^\']*\'";// get the individual xpath tests.
 53  
     private static final String testVal2 = "/[^/]+/" + "*";// have to do this or the compiler gets confused by end comment.
 54  
     private static final String cleanVal = "[^/\']+";// get rid of / and ' in the resulting term.
 55  
     private static final String ruledataVal = "ruledata[^\']*\'([^\']*)";
 56  
     // TODO - enter JIRA
 57  
     // below removes wf:xstreamsafe( and )
 58  
     // below separates each wf:xstreamsafe() section into separate 'finds'
 59  0
     private static final Pattern xPathPattern = Pattern.compile(testVal);
 60  0
     private static final Pattern termPattern = Pattern.compile(testVal2);
 61  0
     private static final Pattern cleanPattern = Pattern.compile(cleanVal);
 62  0
     private static final Pattern targetPattern = Pattern.compile(ruledataVal);
 63  
 
 64  
     public static final String ATTRIBUTE_LABEL_BO_REFERENCE_PREFIX = "kuali_dd_label(";
 65  
     public static final String ATTRIBUTE_LABEL_BO_REFERENCE_SUFFIX = ")";
 66  
     public static final String ATTRIBUTE_SHORT_LABEL_BO_REFERENCE_PREFIX = "kuali_dd_short_label(";
 67  
     public static final String ATTRIBUTE_SHORT_LABEL_BO_REFERENCE_SUFFIX = ")";
 68  
     private static final String KUALI_VALUES_FINDER_REFERENCE_PREFIX = "kuali_values_finder_class(";
 69  
     private static final String KUALI_VALUES_FINDER_REFERENCE_SUFFIX = ")";
 70  
     public static final String notFound = "Label Not Found";
 71  
 
 72  0
     private String lastXPath = "";
 73  
 
 74  
     /**
 75  
      * This method overrides the super class and modifies the XML that it operates on to put the name and the title in the place
 76  
      * where the super class expects to see them, even though they may no longer exist in the original XML.
 77  
      * 
 78  
      * @see org.kuali.rice.kew.rule.xmlrouting.StandardGenericXMLRuleAttribute#getConfigXML()
 79  
      */
 80  
 
 81  
     public Element processConfigXML(Element root) {
 82  0
         return this.processConfigXML(root, null);
 83  
     }
 84  
 
 85  
     /**
 86  
      * This method overrides the super class and modifies the XML that it operates on to put the name and the title in the place
 87  
      * where the super class expects to see them, overwriting the original title in the XML.
 88  
      * 
 89  
      * @see org.kuali.rice.kew.rule.xmlrouting.StandardGenericXMLRuleAttribute#getConfigXML()
 90  
      */
 91  
 
 92  
     public Element processConfigXML(Element root, String[] xpathExpressionElements) {
 93  
 
 94  0
         NodeList fields = root.getElementsByTagName("fieldDef");
 95  0
         Element theTag = null;
 96  0
         String docContent = "";
 97  
 
 98  
 
 99  
         /**
 100  
          * This section will check to see if document content has been defined in the configXML for the document type, by running an
 101  
          * XPath. If this is an empty list the xpath expression in the fieldDef is used to define the xml document content that is
 102  
          * added to the configXML. The xmldocument content is of this form, when in the document configXML. <xmlDocumentContent>
 103  
          * <org.kuali.rice.kns.bo.SourceAccountingLine> <amount> <value>%totaldollarAmount%</value> </amount>
 104  
          * </org.kuali.rice.kns.bo.SourceAccountingLine> </xmlDocumentContent> This class generates this on the fly, by creating an XML
 105  
          * element for each term in the XPath expression. When this doesn't apply XML can be coded in the configXML for the
 106  
          * ruleAttribute.
 107  
          * 
 108  
          * @see org.kuali.rice.kew.plugin.attributes.WorkflowAttribute#getDocContent()
 109  
          */
 110  
 
 111  
 
 112  0
         org.w3c.dom.Document xmlDoc = null;
 113  0
         if (!xmlDocumentContentExists(root)) { // XML Document content is given because the xpath is non standard
 114  0
             fields = root.getElementsByTagName("fieldDef");
 115  0
             xmlDoc = root.getOwnerDocument();
 116  
         }
 117  0
         for (int i = 0; i < fields.getLength(); i++) { // loop over each fieldDef
 118  0
             String name = null;
 119  0
             if (!xmlDocumentContentExists(root)) {
 120  0
                 theTag = (Element) fields.item(i);
 121  
 
 122  
                 /*
 123  
                  * Even though there may be multiple xpath test, for example one for source lines and one for target lines, the
 124  
                  * xmlDocumentContent only needs one, since it is used for formatting. The first one is arbitrarily selected, since
 125  
                  * they are virtually equivalent in structure, most of the time.
 126  
                  */
 127  
 
 128  0
                 List<String> xPathTerms = getXPathTerms(theTag);
 129  0
                 if (xPathTerms.size() != 0) {
 130  0
                     Node iterNode = xmlDoc.createElement("xmlDocumentContent");
 131  
 
 132  
 
 133  0
                     xmlDoc.normalize();
 134  
 
 135  0
                     iterNode.normalize();
 136  
 
 137  
                     /*
 138  
                      * Since this method is run once per attribute and there may be multiple fieldDefs, the first fieldDef is used
 139  
                      * to create the configXML.
 140  
                      */
 141  0
                     for (int j = 0; j < xPathTerms.size(); j++) {// build the configXML based on the Xpath
 142  
                         // TODO - Fix the document content element generation
 143  0
                         iterNode.appendChild(xmlDoc.createElement(xPathTerms.get(j)));
 144  0
                         xmlDoc.normalize();
 145  
 
 146  0
                         iterNode = iterNode.getFirstChild();
 147  0
                         iterNode.normalize();
 148  
 
 149  
                     }
 150  0
                     iterNode.setTextContent("%" + xPathTerms.get(xPathTerms.size() - 1) + "%");
 151  0
                     root.appendChild(iterNode);
 152  
                 }
 153  
             }
 154  0
             theTag = (Element) fields.item(i);
 155  
             // check to see if a values finder is being used to set valid values for a field
 156  0
             NodeList displayTagElements = theTag.getElementsByTagName("display");
 157  0
             if (displayTagElements.getLength() == 1) {
 158  0
                 Element displayTag = (Element) displayTagElements.item(0);
 159  0
                 List valuesElementsToAdd = new ArrayList();
 160  0
                 for (int w = 0; w < displayTag.getChildNodes().getLength(); w++) {
 161  0
                     Node displayTagChildNode = (Node) displayTag.getChildNodes().item(w);
 162  0
                     if ((displayTagChildNode != null) && ("values".equals(displayTagChildNode.getNodeName()))) {
 163  0
                         if (displayTagChildNode.getChildNodes().getLength() > 0) {
 164  0
                             String valuesNodeText = displayTagChildNode.getFirstChild().getNodeValue();
 165  0
                             String potentialClassName = getPotentialKualiClassName(valuesNodeText, KUALI_VALUES_FINDER_REFERENCE_PREFIX, KUALI_VALUES_FINDER_REFERENCE_SUFFIX);
 166  0
                             if (StringUtils.isNotBlank(potentialClassName)) {
 167  
                                 try {
 168  0
                                     Class finderClass = Class.forName((String) potentialClassName);
 169  0
                                     KeyValuesFinder finder = (KeyValuesFinder) finderClass.newInstance();
 170  0
                                     NamedNodeMap valuesNodeAttributes = displayTagChildNode.getAttributes();
 171  0
                                     Node potentialSelectedAttribute = (valuesNodeAttributes != null) ? valuesNodeAttributes.getNamedItem("selected") : null;
 172  0
                                     for (Iterator iter = finder.getKeyValues().iterator(); iter.hasNext();) {
 173  0
                                         KeyLabelPair keyLabelPair = (KeyLabelPair) iter.next();
 174  0
                                         Element newValuesElement = root.getOwnerDocument().createElement("values");
 175  0
                                         newValuesElement.appendChild(root.getOwnerDocument().createTextNode(keyLabelPair.getKey().toString()));
 176  
                                         // newValuesElement.setNodeValue(keyLabelPair.getKey().toString());
 177  0
                                         newValuesElement.setAttribute("title", keyLabelPair.getLabel());
 178  0
                                         if (potentialSelectedAttribute != null) {
 179  0
                                             newValuesElement.setAttribute("selected", potentialSelectedAttribute.getNodeValue());
 180  
                                         }
 181  0
                                         valuesElementsToAdd.add(newValuesElement);
 182  0
                                     }
 183  
                                 }
 184  0
                                 catch (ClassNotFoundException cnfe) {
 185  0
                                     String errorMessage = "Caught an exception trying to find class '" + potentialClassName + "'";
 186  0
                                     LOG.error(errorMessage, cnfe);
 187  0
                                     throw new RuntimeException(errorMessage, cnfe);
 188  
                                 }
 189  0
                                 catch (InstantiationException ie) {
 190  0
                                     String errorMessage = "Caught an exception trying to instantiate class '" + potentialClassName + "'";
 191  0
                                     LOG.error(errorMessage, ie);
 192  0
                                     throw new RuntimeException(errorMessage, ie);
 193  
                                 }
 194  0
                                 catch (IllegalAccessException iae) {
 195  0
                                     String errorMessage = "Caught an access exception trying to instantiate class '" + potentialClassName + "'";
 196  0
                                     LOG.error(errorMessage, iae);
 197  0
                                     throw new RuntimeException(errorMessage, iae);
 198  0
                                 }
 199  
                             }
 200  
                             else {
 201  0
                                 valuesElementsToAdd.add(displayTagChildNode.cloneNode(true));
 202  
                             }
 203  0
                             displayTag.removeChild(displayTagChildNode);
 204  
                         }
 205  
                     }
 206  
                 }
 207  0
                 for (Iterator iter = valuesElementsToAdd.iterator(); iter.hasNext();) {
 208  0
                     Element valuesElementToAdd = (Element) iter.next();
 209  0
                     displayTag.appendChild(valuesElementToAdd);
 210  0
                 }
 211  
             }
 212  0
             if ((xpathExpressionElements != null) && (xpathExpressionElements.length > 0)) {
 213  0
                 NodeList fieldEvaluationElements = theTag.getElementsByTagName("fieldEvaluation");
 214  0
                 if (fieldEvaluationElements.getLength() == 1) {
 215  0
                     Element fieldEvaluationTag = (Element) fieldEvaluationElements.item(0);
 216  0
                     List tagsToAdd = new ArrayList();
 217  0
                     for (int w = 0; w < fieldEvaluationTag.getChildNodes().getLength(); w++) {
 218  0
                         Node fieldEvaluationChildNode = (Node) fieldEvaluationTag.getChildNodes().item(w);
 219  0
                         Element newTagToAdd = null;
 220  0
                         if ((fieldEvaluationChildNode != null) && ("xpathexpression".equals(fieldEvaluationChildNode.getNodeName()))) {
 221  0
                             newTagToAdd = root.getOwnerDocument().createElement("xpathexpression");
 222  0
                             newTagToAdd.appendChild(root.getOwnerDocument().createTextNode(generateNewXpathExpression(fieldEvaluationChildNode.getFirstChild().getNodeValue(), xpathExpressionElements)));
 223  0
                             tagsToAdd.add(newTagToAdd);
 224  0
                             fieldEvaluationTag.removeChild(fieldEvaluationChildNode);
 225  
                         }
 226  
                     }
 227  0
                     for (Iterator iter = tagsToAdd.iterator(); iter.hasNext();) {
 228  0
                         Element elementToAdd = (Element) iter.next();
 229  0
                         fieldEvaluationTag.appendChild(elementToAdd);
 230  0
                     }
 231  
                 }
 232  
             }
 233  0
             theTag.setAttribute("title", getBusinessObjectTitle(theTag));
 234  
 
 235  
         }
 236  0
         if (LOG.isDebugEnabled()) {
 237  0
             LOG.debug(XmlHelper.jotNode(root));
 238  0
             StringWriter xmlBuffer = new StringWriter();
 239  
             try {
 240  
 
 241  0
                 root.normalize();
 242  0
                 Source source = new DOMSource(root);
 243  0
                 Result result = new StreamResult(xmlBuffer);
 244  0
                 TransformerFactory.newInstance().newTransformer().transform(source, result);
 245  
             }
 246  0
             catch (Exception e) {
 247  0
                 LOG.debug(" Exception when printing debug XML output " + e);
 248  0
             }
 249  0
             LOG.debug(xmlBuffer.getBuffer());
 250  
         }
 251  
 
 252  0
         return root;
 253  
     }
 254  
 
 255  
     private String generateNewXpathExpression(String currentXpathExpression, String[] newXpathExpressionElements) {
 256  0
         StringBuffer returnableString = new StringBuffer();
 257  0
         for (int i = 0; i < newXpathExpressionElements.length; i++) {
 258  0
             String newXpathElement = newXpathExpressionElements[i];
 259  0
             returnableString.append(newXpathElement);
 260  
 
 261  
             /*
 262  
              * Append the given xpath expression onto the end of the stringbuffer only in the following cases - if there is only one
 263  
              * element in the string array - if there is more than one element in the string array and if the current element is not
 264  
              * the last element
 265  
              */
 266  0
             if (((i + 1) != newXpathExpressionElements.length) || (newXpathExpressionElements.length == 1)) {
 267  0
                 returnableString.append(currentXpathExpression);
 268  
             }
 269  
         }
 270  0
         return returnableString.toString();
 271  
     }
 272  
 
 273  
     /**
 274  
      * This method gets all of the text from the xpathexpression element.
 275  
      * 
 276  
      * @param root
 277  
      * @return
 278  
      */
 279  
     private String getXPathText(Element root) {
 280  
         try {
 281  0
             String textContent = null;
 282  0
             Node node = (Node) xpath.evaluate(".//xpathexpression", root, XPathConstants.NODE);
 283  0
             if (node != null) {
 284  0
                 textContent = node.getTextContent();
 285  
             }
 286  0
             return textContent;
 287  
         }
 288  0
         catch (XPathExpressionException e) {
 289  0
             LOG.error("No XPath expression text found in element xpathexpression of configXML for document. " + e);
 290  0
             return null;
 291  
             // throw e; Just writing labels or doing routing report.
 292  
         }
 293  
     }
 294  
 
 295  
     /**
 296  
      * This method uses an XPath expression to determine if the content of the xmlDocumentContent is empty
 297  
      * 
 298  
      * @param root
 299  
      * @return
 300  
      */
 301  
     private boolean xmlDocumentContentExists(Element root) {
 302  
         try {
 303  0
             if (((NodeList) xpath.evaluate("//xmlDocumentContent", root, XPathConstants.NODESET)).getLength() == 0) {
 304  0
                 return false;
 305  
             }
 306  
         }
 307  0
         catch (XPathExpressionException e) {
 308  0
             LOG.error("Error parsing xmlDocumentConfig.  " + e);
 309  0
             return false;
 310  0
         }
 311  0
         return true;
 312  
     }
 313  
 
 314  
     public static String getPotentialKualiClassName(String testString, String prefixIndicator, String suffixIndicator) {
 315  0
         if ((StringUtils.isNotBlank(testString)) && (testString.startsWith(prefixIndicator)) && (testString.endsWith(suffixIndicator))) {
 316  0
             return testString.substring(prefixIndicator.length(), testString.lastIndexOf(suffixIndicator));
 317  
         }
 318  0
         return null;
 319  
     }
 320  
 
 321  
     /**
 322  
      * Method to look up the title of each fieldDef tag in the RuleAttribute xml. This method checks the following items in the
 323  
      * following order:
 324  
      * <ol>
 325  
      * <li>Check for the business object name from {@link #getBusinessObjectName(Element)}. If it is not found or blank and the
 326  
      * 'title' attribute of the fieldDef tag is specified then return the value of the 'title' attribute.
 327  
      * <li>Check for the business object name from {@link #getBusinessObjectName(Element)}. If it is found try getting the data
 328  
      * dictionary label related to the business object name and the attribute name (found in the xpath expression)
 329  
      * <li>Check for the data dictionary title value using the attribute name (found in the xpath expression) and the KFS stand in
 330  
      * business object for attributes (see {@link KFSConstants#STAND_IN_BUSINESS_OBJECT_FOR_ATTRIBUTES}
 331  
      * <li>Check for the data dictionary title value using the xpath attribute name found in the xpath expression section. Use that
 332  
      * attribute name to get the label out of the KFS stand in business object for attributes (see
 333  
      * {@link KFSConstants#STAND_IN_BUSINESS_OBJECT_FOR_ATTRIBUTES}
 334  
      * <li>Check for the data dictionary title value using the xpath attribute name found in the xpath expression in the
 335  
      * wf:ruledata() section. Use that attribute name to get the label out of the KFS stand in business object for attributes (see
 336  
      * {@link KFSConstants#STAND_IN_BUSINESS_OBJECT_FOR_ATTRIBUTES}
 337  
      * </ol>
 338  
      * 
 339  
      * @param root - the element of the fieldDef tag
 340  
      */
 341  
     private String getBusinessObjectTitle(Element root) {
 342  0
         String businessObjectName = null;
 343  0
         String businessObjectText = root.getAttribute("title");
 344  0
         String potentialClassNameLongLabel = getPotentialKualiClassName(businessObjectText, ATTRIBUTE_LABEL_BO_REFERENCE_PREFIX, ATTRIBUTE_LABEL_BO_REFERENCE_SUFFIX);
 345  0
         String potentialClassNameShortLabel = getPotentialKualiClassName(businessObjectText, ATTRIBUTE_SHORT_LABEL_BO_REFERENCE_PREFIX, ATTRIBUTE_SHORT_LABEL_BO_REFERENCE_SUFFIX);
 346  
         // we assume they want the long label... but allow for the short label
 347  0
         boolean requestedShortLabel = false;
 348  
 
 349  0
         if (StringUtils.isNotBlank(potentialClassNameLongLabel)) {
 350  0
             businessObjectName = potentialClassNameLongLabel;
 351  
         }
 352  0
         else if (StringUtils.isNotBlank(potentialClassNameShortLabel)) {
 353  0
             businessObjectName = potentialClassNameShortLabel;
 354  0
             requestedShortLabel = true;
 355  
         }
 356  0
         if (StringUtils.isNotBlank(businessObjectName)) {
 357  0
             DataDictionaryService DDService = KNSServiceLocator.getDataDictionaryService();
 358  
 
 359  0
             String title = null;
 360  0
             String targetVal = lastXPath; // Assume the attribute is the last term in the XPath expression
 361  
 
 362  0
             if (LOG.isErrorEnabled()) {
 363  0
                 LOG.debug("Finding title in BO=" + businessObjectName + " ObjectName=" + targetVal);
 364  
             }
 365  
 
 366  0
             if (StringUtils.isNotBlank(targetVal)) {
 367  
                 // try to get the label based on the bo name and xpath attribute
 368  0
                 if (requestedShortLabel) {
 369  0
                     title = DDService.getAttributeShortLabel(businessObjectName, targetVal);
 370  
                 }
 371  
                 else {
 372  0
                     title = DDService.getAttributeLabel(businessObjectName, targetVal);
 373  
                 }
 374  0
                 if (StringUtils.isNotBlank(title)) {
 375  0
                     return title;
 376  
                 }
 377  
             }
 378  
             // try to get the label based on the business object and xpath ruledata section
 379  0
             targetVal = getRuleData(root);
 380  0
             if (LOG.isErrorEnabled()) {
 381  0
                 LOG.debug("Finding title in BO=" + businessObjectName + " ObjectName=" + targetVal);
 382  
             }
 383  0
             if (StringUtils.isNotBlank(targetVal)) {
 384  0
                 title = DDService.getAttributeLabel(businessObjectName, targetVal);
 385  0
                 if (StringUtils.isNotBlank(title)) {
 386  0
                     return title;
 387  
                 }
 388  
             }
 389  
             // If haven't found a label yet, its probably because there is no xpath. Use the name attribute to determine the BO
 390  
             // attribute to use.
 391  0
             targetVal = root.getAttribute("name");
 392  0
             if (LOG.isErrorEnabled()) {
 393  0
                 LOG.debug("Finding title in BO=" + businessObjectName + " ObjectName=" + targetVal);
 394  
             }
 395  0
             title = DDService.getAttributeLabel(businessObjectName, targetVal);
 396  
 
 397  0
             if (StringUtils.isNotBlank(title)) {
 398  0
                 return title;
 399  
             }
 400  0
         }
 401  
         // return any potentially hard coded title info
 402  0
         else if ( (StringUtils.isNotBlank(businessObjectText)) && (StringUtils.isBlank(businessObjectName)) ) {
 403  0
                 return businessObjectText;
 404  
         }
 405  0
         return notFound;
 406  
 
 407  
     }
 408  
 
 409  
     /**
 410  
      * This method gets the contents of the ruledata function in the xpath statement in the XML
 411  
      * 
 412  
      * @param root
 413  
      * @return
 414  
      */
 415  
     private String getRuleData(Element root) {
 416  0
         String xPathRuleTarget = getXPathText(root);
 417  
 
 418  
         // This pattern may need to change to get the last stanza of the xpath
 419  0
         if (StringUtils.isNotBlank(xPathRuleTarget)) {
 420  0
             Matcher ruleTarget = targetPattern.matcher(xPathRuleTarget);
 421  0
             if (ruleTarget.find()) {
 422  0
                 xPathRuleTarget = ruleTarget.group(1);
 423  
             }
 424  
         }
 425  0
         return xPathRuleTarget;
 426  
     }
 427  
 
 428  
     private List<String> getXPathTerms(Element myTag) {
 429  
 
 430  
         Matcher xPathTarget;
 431  
         String firstMatch;
 432  0
         List<String> xPathTerms = new ArrayList();
 433  0
         String allText = getXPathText(myTag);// grab the whole xpath expression
 434  0
         if (StringUtils.isNotBlank(allText)) {
 435  0
             xPathTarget = xPathPattern.matcher(allText);
 436  
             Matcher termTarget;
 437  
             Matcher cleanTarget;
 438  0
             int theEnd = 0;// Have to define this or the / gets used up with the match and every other term is returned.
 439  
 
 440  0
             xPathTarget.find(theEnd);
 441  0
             theEnd = xPathTarget.end() - 1;
 442  0
             firstMatch = xPathTarget.group();
 443  
 
 444  
 
 445  0
             termTarget = termPattern.matcher(firstMatch);
 446  0
             int theEnd2 = 0;
 447  0
             while (termTarget.find(theEnd2)) { // get each term, clean them up, and add to the list.
 448  0
                 theEnd2 = termTarget.end() - 1;
 449  0
                 cleanTarget = cleanPattern.matcher(termTarget.group());
 450  0
                 cleanTarget.find();
 451  0
                 lastXPath = cleanTarget.group();
 452  0
                 xPathTerms.add(lastXPath);
 453  
 
 454  
             }
 455  
         }
 456  0
         return xPathTerms;
 457  
     }
 458  
 
 459  
     private String getLastXPath(Element root) {
 460  0
         List<String> tempList = getXPathTerms(root);
 461  0
         return tempList.get(tempList.size());
 462  
     }
 463  
 }