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.BufferedReader;
019: import java.io.StringReader;
020: import java.util.ArrayList;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.regex.Pattern;
024:
025: import javax.xml.parsers.DocumentBuilderFactory;
026: import javax.xml.xpath.XPath;
027: import javax.xml.xpath.XPathConstants;
028: import javax.xml.xpath.XPathExpressionException;
029:
030: import org.apache.commons.lang.StringUtils;
031: import org.kuali.core.service.DocumentTypeService;
032: import org.kuali.kfs.KFSPropertyConstants;
033: import org.kuali.kfs.context.SpringContext;
034: import org.kuali.kfs.document.AccountingDocument;
035: import org.kuali.workflow.KualiWorkflowUtils;
036: import org.w3c.dom.Document;
037: import org.w3c.dom.Node;
038: import org.w3c.dom.NodeList;
039: import org.xml.sax.InputSource;
040:
041: import edu.iu.uis.eden.docsearch.SearchableAttributeStringValue;
042: import edu.iu.uis.eden.docsearch.SearchableAttributeValue;
043: import edu.iu.uis.eden.routetemplate.WorkflowAttributeValidationError;
044: import edu.iu.uis.eden.routetemplate.xmlrouting.XPathHelper;
045:
046: /**
047: * This is a search attribute that lets us search on account numbers in any accounting document.
048: */
049: public class AccountNumberSearchAttributeImpl extends
050: KualiXmlSearchableAttributeImpl {
051: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
052: .getLogger(AccountNumberSearchAttributeImpl.class);
053:
054: /**
055: * Validates the inputs that the user gave to search on. In this case, we're going to make sure
056: * that the account number entered is numeric.
057: *
058: * @see edu.iu.uis.eden.docsearch.SearchableAttribute#validateUserSearchInputs(java.util.Map)
059: */
060: @Override
061: public List<WorkflowAttributeValidationError> validateUserSearchInputs(
062: Map params) {
063: String accountNumber = (String) params
064: .get(KFSPropertyConstants.ACCOUNT_NUMBER);
065: List<WorkflowAttributeValidationError> errors = new ArrayList<WorkflowAttributeValidationError>();
066: if (StringUtils.isNotBlank(accountNumber)) { // only validate if the account number isn't empty
067: if (!accountNumber.matches("^[0-9 ]{1,10}$")) {
068: errors
069: .add(new WorkflowAttributeValidationError(
070: KFSPropertyConstants.ACCOUNT_NUMBER,
071: "Invalid Account Number Value. Account numbers must be all numeric, with at least one digit"));
072: }
073: }
074: return errors;
075: }
076:
077: /**
078: * Returns searchable attributes that should be searched on for the given document content. Here,
079: * we're going to determine the type of the document and, from that, derive the correct type names of the
080: * Source and Target accounting line classes.
081: *
082: * @see edu.iu.uis.eden.docsearch.xml.StandardGenericXMLSearchableAttribute#getSearchStorageValues(java.lang.String)
083: */
084: @Override
085: public List<SearchableAttributeValue> getSearchStorageValues(
086: String docContent) {
087: List<SearchableAttributeValue> storageValues = super
088: .getSearchStorageValues(docContent);
089: Document document;
090: if (StringUtils.isBlank(docContent)) {
091: LOG.warn("Empty Document Content found '" + docContent
092: + "'");
093: return storageValues;
094: }
095: try {
096: document = DocumentBuilderFactory.newInstance()
097: .newDocumentBuilder().parse(
098: new InputSource(new BufferedReader(
099: new StringReader(docContent))));
100: } catch (Exception e) {
101: LOG.error("error parsing docContent: " + docContent, e);
102: throw new RuntimeException(
103: "Error trying to parse docContent: " + docContent,
104: e);
105: }
106: AccountingLineClassDeterminer accountingLineClassDeterminer = new AccountingLineClassDeterminer(
107: document);
108: XPath xpath = XPathHelper.newXPath(document);
109:
110: try {
111: if (accountingLineClassDeterminer
112: .getSourceAccountingLineClassName() != null) {
113: String sourceLineXPath = KualiWorkflowUtils
114: .xstreamSafeXPath(KualiWorkflowUtils.XSTREAM_MATCH_ANYWHERE_PREFIX
115: + accountingLineClassDeterminer
116: .getSourceAccountingLineClassName());
117: NodeList sourceLineNodes = (NodeList) xpath.evaluate(
118: sourceLineXPath, document,
119: XPathConstants.NODESET);
120: addSearchableValuesForNodes(sourceLineNodes,
121: storageValues, xpath);
122: }
123:
124: if (accountingLineClassDeterminer
125: .getTargetAccountingLineClassName() != null) {
126: String targetLineXPath = KualiWorkflowUtils
127: .xstreamSafeXPath(KualiWorkflowUtils.XSTREAM_MATCH_ANYWHERE_PREFIX
128: + accountingLineClassDeterminer
129: .getTargetAccountingLineClassName());
130: NodeList targetLineNodes = (NodeList) xpath.evaluate(
131: targetLineXPath, document,
132: XPathConstants.NODESET);
133: addSearchableValuesForNodes(targetLineNodes,
134: storageValues, xpath);
135: }
136: } catch (XPathExpressionException e) {
137: LOG.error("Could not parse XPath expression: " + e);
138: throw new RuntimeException(e);
139: }
140:
141: return storageValues;
142: }
143:
144: /**
145: * Loops through a NodeList (what an awful, awful class) and adds the appropriate search attribute values to store for account number searches into the given List of SearchableAttributeValues
146: * @param accountingLineNodes a list of nodes with accounting lines in them
147: * @param searchAttributeStorageValues the list of SearchableAttributeValues to put the storage attributes into
148: * @param xpath the XPath evaluator for the document
149: */
150: private void addSearchableValuesForNodes(
151: NodeList accountingLineNodes,
152: List<SearchableAttributeValue> searchAttributeStorageValues,
153: XPath xpath) throws XPathExpressionException {
154: for (int i = 0; i < accountingLineNodes.getLength(); i++) {
155: Node node = accountingLineNodes.item(i);
156: SearchableAttributeValue val = retrieveSearchableAttribute(
157: node, xpath);
158: if (val != null) {
159: searchAttributeStorageValues.add(val);
160: }
161: }
162: }
163:
164: /**
165: * Creates a new searchable attribute value with the account number from the node as the value and "accountNumber" as the key
166: * @param accountingNumberNode an XML document node with an account number in it
167: * @param xpath the XPath evaluator for the document
168: * @return the searchable attribute value derived from that node
169: */
170: private SearchableAttributeValue retrieveSearchableAttribute(
171: Node accountingLineNode, XPath xpath)
172: throws XPathExpressionException {
173: String accountNumber = xpath
174: .evaluate(
175: (KualiWorkflowUtils.XSTREAM_MATCH_RELATIVE_PREFIX + KFSPropertyConstants.ACCOUNT_NUMBER),
176: accountingLineNode);
177: if (!StringUtils.isBlank(accountNumber)) {
178: SearchableAttributeStringValue storageValue = new SearchableAttributeStringValue();
179: storageValue
180: .setSearchableAttributeKey(KFSPropertyConstants.ACCOUNT_NUMBER);
181: storageValue.setSearchableAttributeValue(accountNumber);
182: return storageValue;
183: } else {
184: return null;
185: }
186: }
187: }
|