001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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: */
016: package org.kuali.workflow.attribute;
017:
018: import java.io.StringWriter;
019: import java.util.List;
020:
021: import javax.xml.transform.TransformerException;
022: import javax.xml.transform.TransformerFactory;
023: import javax.xml.transform.dom.DOMSource;
024: import javax.xml.transform.stream.StreamResult;
025: import javax.xml.xpath.XPath;
026: import javax.xml.xpath.XPathConstants;
027: import javax.xml.xpath.XPathExpressionException;
028:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031: import org.w3c.dom.Element;
032: import org.w3c.dom.Node;
033: import org.w3c.dom.NodeList;
034:
035: import edu.iu.uis.eden.engine.RouteContext;
036: import edu.iu.uis.eden.routeheader.DocumentContent;
037: import edu.iu.uis.eden.routeheader.DocumentRouteHeaderValue;
038: import edu.iu.uis.eden.routetemplate.xmlrouting.XPathHelper;
039: import edu.iu.uis.eden.util.Utilities;
040:
041: /**
042: * This class represents rule attributes that support kfs_*AccountingLineClass markers in the XPath
043: * expressions they have.
044: */
045: public class AccountLineReplacingXmlRuleAttribute extends
046: KualiXmlRuleAttributeImpl {
047: private static Log LOG = LogFactory
048: .getLog(AccountLineReplacingXmlRuleAttribute.class);
049:
050: /**
051: * This returns a DOM Element, which is the parent element for the XML in a route rule. This implementation
052: * replaces all kfs_*AccountingLineClass markers in XPath expressions in the XML.
053: *
054: * @see org.kuali.workflow.attribute.KualiXmlRuleAttributeImpl#getConfigXML()
055: */
056: public Element getConfigXML() {
057: Element xmlRoot = super .getConfigXML();
058: try {
059: RouteContext routeContext = RouteContext
060: .getCurrentRouteContext();
061: DocumentRouteHeaderValue docHeader = routeContext
062: .getDocument();
063: if (docHeader != null
064: && docHeader.getDocumentType() != null) {
065: String docTypeName = docHeader.getDocumentType()
066: .getName();
067:
068: AccountingLineClassDeterminer accountingLineClassDeterminer = new AccountingLineClassDeterminer(
069: docTypeName);
070: NodeList xpathNodes = getXPathExpressionNodes(xmlRoot);
071: for (int i = 0; i < xpathNodes.getLength(); i++) {
072: Node xpathExpressionNode = xpathNodes.item(i);
073: LOG
074: .debug("original node: "
075: + getStringFromElement(xpathExpressionNode));
076: // update the text in that element
077: updateNode(xpathExpressionNode,
078: accountingLineClassDeterminer);
079: }
080: LOG.debug("resultant xmlRoot = "
081: + getStringFromElement(xmlRoot));
082: }
083: } catch (TransformerException e) {
084: LOG.error("Transformer Exception: " + e);
085: throw new RuntimeException(e);
086: }
087: return xmlRoot;
088: }
089:
090: /**
091: * Writes an XML Element and all children as a String
092: * @param rootElement the XML Element, which will serve as the root of what is being written to the String
093: * @return the XML document with the given Element as a parent, written into a String
094: * @throws TransformerException thrown if, for some unholy reason, the given XML element can't be turned into a String
095: */
096: private String getStringFromElement(Node rootElement)
097: throws TransformerException {
098: DOMSource domSource = new DOMSource(rootElement);
099: StringWriter writer = new StringWriter();
100: TransformerFactory.newInstance().newTransformer().transform(
101: domSource, new StreamResult(writer));
102: return writer.toString();
103: }
104:
105: /**
106: * Finds a list in the rule attribute XML of nodes that hold XPathExpressions, which must have any
107: * kfs_*AccountingLineClass markers replaced
108: * @param rootElement the root Element of the document to search through
109: * @return a NodeList of XPathExpression nodes
110: */
111: private NodeList getXPathExpressionNodes(Element rootElement) {
112: String findXpathExpression = "//fieldEvaluation/xpathexpression";
113: NodeList xpathNodes = null;
114: XPath xpath = XPathHelper.newXPath(rootElement);
115: try {
116: xpathNodes = (NodeList) xpath.evaluate(findXpathExpression,
117: rootElement, XPathConstants.NODESET);
118: } catch (XPathExpressionException e) {
119: LOG.error("error in isMatch ", e);
120: throw new RuntimeException(
121: "Error trying to find xml content with xpath expressions: "
122: + findXpathExpression, e);
123: }
124: return xpathNodes;
125: }
126:
127: /**
128: * This updates a node with an XPath expression in it to replace the kfs_*AccountingLineClass with the actual correct class names
129: * @param node the XML node to update
130: * @param accountingLineClassDeterminer the class that figures out which child of AccountingLine is being used for this particular document
131: */
132: private void updateNode(Node node,
133: AccountingLineClassDeterminer accountingLineClassDeterminer) {
134: String extrapolatedXPathExpression = extrapolateInXPathExpression(
135: node.getTextContent(), accountingLineClassDeterminer);
136: LOG.debug("Extrapolated XPath Expression = "
137: + extrapolatedXPathExpression);
138: node.setTextContent(extrapolatedXPathExpression);
139: }
140:
141: /**
142: * This method "extrapolates" the kfs_*AccountingLineClass in XPath rules we find.
143: * @param expression the XPath expression to fix
144: * @param accountingLineClassDeterminer the class that figures out which child of AccountingLine is being used for this particular document
145: * @return the extrapolated XPath expression.
146: */
147: private String extrapolateInXPathExpression(String expression,
148: AccountingLineClassDeterminer accountingLineClassDeterminer) {
149: expression = expression.replaceAll(
150: "kfs_sourceAccountingLineClass",
151: accountingLineClassDeterminer
152: .getSourceAccountingLineClassName());
153: expression = expression.replaceAll(
154: "kfs_targetAccountingLineClass",
155: accountingLineClassDeterminer
156: .getTargetAccountingLineClassName());
157: return expression;
158: }
159: }
|