001: /*
002: * Copyright 2006-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:
017: package org.kuali.workflow.attribute;
018:
019: import java.util.ArrayList;
020: import java.util.Collections;
021: import java.util.HashSet;
022: import java.util.List;
023: import java.util.Map;
024: import java.util.Set;
025:
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.apache.log4j.Logger;
032: import org.kuali.core.lookup.LookupUtils;
033: import org.kuali.kfs.KFSConstants;
034: import org.kuali.kfs.context.SpringContext;
035: import org.kuali.module.chart.bo.Chart;
036: import org.kuali.module.chart.service.ChartService;
037: import org.kuali.workflow.KualiWorkflowUtils;
038: import org.w3c.dom.Element;
039: import org.w3c.dom.NodeList;
040:
041: import edu.iu.uis.eden.WorkflowServiceErrorImpl;
042: import edu.iu.uis.eden.engine.RouteContext;
043: import edu.iu.uis.eden.exception.EdenUserNotFoundException;
044: import edu.iu.uis.eden.plugin.attributes.RoleAttribute;
045: import edu.iu.uis.eden.plugin.attributes.WorkflowAttribute;
046: import edu.iu.uis.eden.routeheader.DocumentContent;
047: import edu.iu.uis.eden.routetemplate.ResolvedQualifiedRole;
048: import edu.iu.uis.eden.routetemplate.Role;
049: import edu.iu.uis.eden.user.AuthenticationUserId;
050: import edu.iu.uis.eden.util.KeyLabelPair;
051: import edu.iu.uis.eden.util.Utilities;
052:
053: /**
054: * KualiChartAttribute which should be used when using charts to do routing
055: */
056: public class KualiChartAttribute implements RoleAttribute,
057: WorkflowAttribute {
058:
059: static final long serialVersionUID = 1000;
060:
061: private static Logger LOG = Logger
062: .getLogger(KualiChartAttribute.class);
063:
064: private static final String FIN_COA_CD_KEY = "fin_coa_cd";
065:
066: private static final String UNIVERSITY_CHART_MANAGER_ROLE_KEY = "UNIVERSITY-CHART-MANAGER";
067:
068: private static final String UNIVERSITY_CHART_MANAGER_ROLE_LABEL = "University Chart Manager";
069:
070: private static final String CHART_MANAGER_ROLE_KEY = "CHART-MANAGER";
071:
072: private static final String CHART_MANAGER_ROLE_LABEL = "Chart Manager";
073:
074: private static final String CHART_ATTRIBUTE = "KUALI_CHART_ATTRIBUTE";
075:
076: private static final String CHART_REVIEW_FIN_COA_CD_KEY = "chart_review_fin_coa_cd";
077:
078: private static final String ROLE_STRING_DELIMITER = "~!~!~";
079:
080: private static final String ORGANIZATION_DOC_TYPE = KualiWorkflowUtils.ORGANIZATION_DOC_TYPE;
081:
082: private String finCoaCd;
083:
084: private boolean required;
085:
086: private List rows;
087:
088: /**
089: * Constructs a KualiChartAttribute.java.
090: */
091: public KualiChartAttribute() {
092:
093: rows = new ArrayList();
094:
095: List fields = new ArrayList();
096: fields.add(KualiWorkflowUtils.buildTextRowWithLookup(
097: Chart.class,
098: KFSConstants.CHART_OF_ACCOUNTS_CODE_PROPERTY_NAME,
099: FIN_COA_CD_KEY));
100: }
101:
102: /**
103: * Constructs a KualiChartAttribute.java.
104: *
105: * @param finCoaCd - the chart code
106: */
107: public KualiChartAttribute(String finCoaCd) {
108:
109: this ();
110: this .finCoaCd = LookupUtils.forceUppercase(Chart.class,
111: "chartOfAccountsCode", finCoaCd);
112:
113: }
114:
115: /**
116: * Gets the finCoaCd attribute.
117: *
118: * @return Returns the finCoaCd.
119: */
120: public String getFinCoaCd() {
121: return finCoaCd;
122: }
123:
124: /**
125: * Sets the finCoaCd attribute value.
126: *
127: * @param finCoaCd The finCoaCd to set.
128: */
129: public void setFinCoaCd(String finCoaCd) {
130: this .finCoaCd = finCoaCd;
131: }
132:
133: /**
134: * @see edu.iu.uis.eden.routetemplate.WorkflowAttribute#getRuleRows()
135: */
136: public List getRuleRows() {
137: return Collections.EMPTY_LIST;
138: }
139:
140: /**
141: * @see edu.iu.uis.eden.routetemplate.WorkflowAttribute#getRoutingDataRows()
142: */
143: public List getRoutingDataRows() {
144: return rows;
145: }
146:
147: /**
148: * @see edu.iu.uis.eden.routetemplate.WorkflowAttribute#getDocContent()
149: */
150: public String getDocContent() {
151: if (Utilities.isEmpty(getFinCoaCd())) {
152: return "";
153: }
154: return KualiWorkflowUtils.XML_REPORT_DOC_CONTENT_PREFIX + "<"
155: + CHART_ATTRIBUTE + ">" + "<" + FIN_COA_CD_KEY + ">"
156: + getFinCoaCd() + "</" + FIN_COA_CD_KEY + ">" + "</"
157: + CHART_ATTRIBUTE + ">"
158: + KualiWorkflowUtils.XML_REPORT_DOC_CONTENT_SUFFIX;
159: }
160:
161: /**
162: * @see edu.iu.uis.eden.routetemplate.WorkflowAttribute#getRuleExtensionValues()
163: */
164: public List getRuleExtensionValues() {
165: return Collections.EMPTY_LIST;
166: }
167:
168: /**
169: * Validates chart using database
170: *
171: * @param finCoaCd
172: * @return true if chart is valid
173: */
174: private boolean isValidChart(String finCoaCd) {
175: return (getChart(finCoaCd) != null);
176: }
177:
178: /**
179: * Returns a Chart object from the code
180: *
181: * @param finCoaCd
182: * @return Chart
183: */
184: private Chart getChart(String finCoaCd) {
185: return SpringContext.getBean(ChartService.class)
186: .getByPrimaryId(finCoaCd);
187: }
188:
189: /**
190: * @see edu.iu.uis.eden.routetemplate.WorkflowAttribute#validateRoutingData(java.util.Map)
191: */
192: public List validateRoutingData(Map paramMap) {
193:
194: List errors = new ArrayList();
195: this .finCoaCd = LookupUtils.forceUppercase(Chart.class,
196: "chartOfAccountsCode", (String) paramMap
197: .get(CHART_REVIEW_FIN_COA_CD_KEY));
198: if (isRequired()
199: && (this .finCoaCd == null || "".equals(finCoaCd))) {
200: errors
201: .add(new WorkflowServiceErrorImpl(
202: "Chart is required.",
203: "routetemplate.chartorgattribute.chartorg.required"));
204: }
205:
206: if (this .finCoaCd != null && !"".equals(finCoaCd)) {
207: if (!isValidChart(finCoaCd)) {
208: errors
209: .add(new WorkflowServiceErrorImpl(
210: "Chart is invalid.",
211: "routetemplate.chartorgattribute.chartorg.invalid"));
212: }
213: }
214: return errors;
215: }
216:
217: /**
218: * @see edu.iu.uis.eden.routetemplate.WorkflowAttribute#validateRuleData(java.util.Map)
219: */
220: public List validateRuleData(Map paramMap) {
221: return Collections.EMPTY_LIST;
222: }
223:
224: /**
225: * @see edu.iu.uis.eden.routetemplate.WorkflowAttribute#getFieldConversions()
226: */
227: public List getFieldConversions() {
228: List fieldConversions = new ArrayList();
229: fieldConversions.add(new KeyLabelPair(FIN_COA_CD_KEY,
230: CHART_REVIEW_FIN_COA_CD_KEY));
231: return fieldConversions;
232: }
233:
234: /**
235: * @see edu.iu.uis.eden.routetemplate.WorkflowAttribute#setRequired(boolean)
236: */
237: public void setRequired(boolean required) {
238: this .required = required;
239: }
240:
241: /**
242: * @see edu.iu.uis.eden.routetemplate.WorkflowAttribute#isRequired()
243: */
244: public boolean isRequired() {
245: return required;
246: }
247:
248: /**
249: * @see edu.iu.uis.eden.routetemplate.RoleAttribute#getRoleNames()
250: */
251: public List getRoleNames() {
252: List roles = new ArrayList();
253: roles.add(new Role(this .getClass(), CHART_MANAGER_ROLE_KEY,
254: CHART_MANAGER_ROLE_LABEL));
255: roles.add(new Role(this .getClass(),
256: UNIVERSITY_CHART_MANAGER_ROLE_KEY,
257: UNIVERSITY_CHART_MANAGER_ROLE_LABEL));
258: return roles;
259: }
260:
261: /**
262: * This method will build a string representation of a qualified role a qualified role is the role, with the corresponding
263: * string values that further qualify the role to apply for a given object.
264: *
265: * @param roleName
266: * @param chart
267: * @return String
268: */
269: private String getQualifiedRoleString(String roleName, String chart) {
270: return roleName + ROLE_STRING_DELIMITER + chart;
271: }
272:
273: private static final String ACCOUNT_GLOBAL_DETAIL_XPATH = "wf:xstreamsafe('"
274: + KualiWorkflowUtils.NEW_MAINTAINABLE_PREFIX
275: + "accountGlobalDetails/org.kuali.module.chart.bo.AccountGlobalDetail/chartOfAccountsCode')";
276: private static final String SUB_OBJECT_CODE_GLOBAL_DETAIL_XPATH = "wf:xstreamsafe('"
277: + KualiWorkflowUtils.NEW_MAINTAINABLE_PREFIX
278: + "subObjCdGlobalDetails/list/org.kuali.module.chart.bo.SubObjCdGlobalDetail/chartOfAccountsCode')";
279: private static final String OBJECT_CODE_GLOBAL_DETAIL_XPATH = "wf:xstreamsafe('"
280: + KualiWorkflowUtils.NEW_MAINTAINABLE_PREFIX
281: + "objectCodeGlobalDetails/list/org.kuali.module.chart.bo.ObjectCodeGlobalDetail/chartOfAccountsCode')";
282: private static final String ORG_REVERSION_GLOBAL_DETAIL_XPATH = "wf:xstreamsafe('"
283: + KualiWorkflowUtils.NEW_MAINTAINABLE_PREFIX
284: + "organizationReversionGlobalOrganizations/list/org.kuali.module.chart.bo.OrganizationReversionGlobalOrganization/chartOfAccountsCode')";
285:
286: /**
287: * @see edu.iu.uis.eden.routetemplate.RoleAttribute#getQualifiedRoleNames(java.lang.String, java.lang.String)
288: */
289: public List getQualifiedRoleNames(String roleName,
290: DocumentContent docContent)
291: throws EdenUserNotFoundException {
292: Set qualifiedRoleNames = new HashSet();
293: if (CHART_MANAGER_ROLE_KEY.equals(roleName)) {
294: XPath xpath = KualiWorkflowUtils.getXPath(docContent
295: .getDocument());
296: List<String> chartCodes = new ArrayList<String>();
297: List<String> chartXPaths = new ArrayList<String>();
298: String docTypeName = docContent.getRouteContext()
299: .getDocument().getDocumentType().getName();
300: try {
301: // the report business is to support Routing Reports, which we
302: // need to work on Chart
303: boolean isReport = ((Boolean) xpath
304: .evaluate(
305: KualiWorkflowUtils.XSTREAM_SAFE_PREFIX
306: + KualiWorkflowUtils.XSTREAM_MATCH_ANYWHERE_PREFIX
307: + KualiWorkflowUtils.XML_REPORT_DOC_CONTENT_XPATH_PREFIX
308: + KualiWorkflowUtils.XSTREAM_SAFE_SUFFIX,
309: docContent.getDocument(),
310: XPathConstants.BOOLEAN)).booleanValue();
311: if (isReport) {
312: chartXPaths
313: .add(KualiWorkflowUtils.XSTREAM_SAFE_PREFIX
314: + KualiWorkflowUtils.XSTREAM_MATCH_ANYWHERE_PREFIX
315: + KualiWorkflowUtils.XML_REPORT_DOC_CONTENT_XPATH_PREFIX
316: + "/"
317: + CHART_ATTRIBUTE
318: + "/"
319: + FIN_COA_CD_KEY
320: + KualiWorkflowUtils.XSTREAM_SAFE_SUFFIX);
321: } else if (KualiWorkflowUtils.ACCOUNT_DELEGATE_GLOBAL_DOC_TYPE
322: .equals(docTypeName)) {
323: chartXPaths.add(ACCOUNT_GLOBAL_DETAIL_XPATH);
324: } else if (KualiWorkflowUtils.ACCOUNT_CHANGE_DOC_TYPE
325: .equals(docTypeName)) {
326: chartXPaths.add(ACCOUNT_GLOBAL_DETAIL_XPATH);
327: } else if (KualiWorkflowUtils.SUB_OBJECT_CODE_CHANGE_DOC_TYPE
328: .equals(docTypeName)) {
329: chartXPaths.add(ACCOUNT_GLOBAL_DETAIL_XPATH);
330: chartXPaths
331: .add(SUB_OBJECT_CODE_GLOBAL_DETAIL_XPATH);
332: } else if (KualiWorkflowUtils.OBJECT_CODE_CHANGE_DOC_TYPE
333: .equals(docTypeName)) {
334: chartXPaths.add(OBJECT_CODE_GLOBAL_DETAIL_XPATH);
335: } else if (KualiWorkflowUtils.ORG_REVERSION_CHANGE_DOC_TYPE
336: .equals(docTypeName)) {
337: chartXPaths.add(ORG_REVERSION_GLOBAL_DETAIL_XPATH);
338: }
339: // this is the typical path during normal workflow operation
340: else {
341: chartXPaths
342: .add(KualiWorkflowUtils.XSTREAM_SAFE_PREFIX
343: + KualiWorkflowUtils.NEW_MAINTAINABLE_PREFIX
344: + "chartOfAccountsCode"
345: + KualiWorkflowUtils.XSTREAM_SAFE_SUFFIX);
346: }
347: for (String chartXPath : chartXPaths) {
348: NodeList chartNodes = (NodeList) xpath.evaluate(
349: chartXPath, docContent.getDocument(),
350: XPathConstants.NODESET);
351: if (chartNodes != null) {
352: for (int index = 0; index < chartNodes
353: .getLength(); index++) {
354: Element chartElem = (Element) chartNodes
355: .item(index);
356: String chartOfAccountsCode = chartElem
357: .getFirstChild().getNodeValue();
358: if (!StringUtils
359: .isEmpty(chartOfAccountsCode)) {
360: chartCodes.add(chartOfAccountsCode);
361: }
362: }
363: }
364: }
365: } catch (XPathExpressionException e) {
366: throw new RuntimeException(
367: "Error evaluating xpath expression to locate chart.",
368: e);
369: } catch (Exception e) {
370: throw new RuntimeException(
371: "An unexpected error occurred while trying to locate the Chart.",
372: e);
373: }
374:
375: for (String chartCode : chartCodes) {
376: qualifiedRoleNames.add(getQualifiedRoleString(roleName,
377: chartCode));
378: }
379: } else if (UNIVERSITY_CHART_MANAGER_ROLE_KEY.equals(roleName)) {
380: qualifiedRoleNames.add(UNIVERSITY_CHART_MANAGER_ROLE_KEY);
381: }
382: return new ArrayList(qualifiedRoleNames);
383: }
384:
385: /**
386: * get the chart name from a qualified role string
387: *
388: * @param qualifiedRole
389: * @return String
390: */
391: private String getUnqualifiedChartFromString(String qualifiedRole) {
392: return qualifiedRole.split(ROLE_STRING_DELIMITER)[1];
393: }
394:
395: /**
396: * @see edu.iu.uis.eden.routetemplate.RoleAttribute#resolveQualifiedRole(edu.iu.uis.eden.routetemplate.attribute.RouteContext,
397: * java.lang.String, java.lang.String)
398: */
399: public ResolvedQualifiedRole resolveQualifiedRole(
400: RouteContext context, String roleName, String qualifiedRole)
401: throws EdenUserNotFoundException {
402: List members = new ArrayList();
403: Chart chart = null;
404: if (CHART_MANAGER_ROLE_KEY.equals(roleName)) {
405: members.add(new AuthenticationUserId(getChart(
406: getUnqualifiedChartFromString(qualifiedRole))
407: .getFinCoaManagerUniversal()
408: .getPersonUserIdentifier()));
409: } else if (UNIVERSITY_CHART_MANAGER_ROLE_KEY.equals(roleName)) {
410: members.add(new AuthenticationUserId(SpringContext.getBean(
411: ChartService.class).getUniversityChart()
412: .getFinCoaManagerUniversal()
413: .getPersonUserIdentifier()));
414: }
415: return new ResolvedQualifiedRole(roleName, members);
416: }
417:
418: public boolean isMatch(DocumentContent documentContent, List rules) {
419: return true;
420: }
421:
422: }
|