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     */
016    package org.kuali.rice.kew.rule;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.kew.api.KewApiConstants;
020    import org.kuali.rice.kew.api.WorkflowRuntimeException;
021    import org.kuali.rice.kew.api.extension.ExtensionDefinition;
022    import org.kuali.rice.kew.api.identity.Id;
023    import org.kuali.rice.kew.engine.RouteContext;
024    import org.kuali.rice.kew.routeheader.DocumentContent;
025    import org.kuali.rice.kew.rule.xmlrouting.GenericXMLRuleAttribute;
026    import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
027    import org.w3c.dom.Element;
028    import org.w3c.dom.NodeList;
029    import org.xml.sax.InputSource;
030    
031    import javax.xml.xpath.XPath;
032    import javax.xml.xpath.XPathConstants;
033    import javax.xml.xpath.XPathExpressionException;
034    import java.io.StringReader;
035    import java.util.ArrayList;
036    import java.util.HashMap;
037    import java.util.List;
038    import java.util.Map;
039    
040    /**
041     * A generic Role Attribute superclass that can be used to route to an ID. Can
042     * take as configuration the label to use for the element name in the XML. This
043     * allows for re-use of this component in different contexts.
044     * 
045     * @author Kuali Rice Team (rice.collab@kuali.org)
046     */
047    public abstract class AbstractIdRoleAttribute extends AbstractRoleAttribute
048                    implements GenericXMLRuleAttribute {
049    
050            private static final String XML_ELEMENT_LABEL = "xmlElementLabel";
051            private static final String ROLE_NAME_LABEL = "roleNameLabel";
052        private static final String GROUP_TOGETHER_LABEL = "groupTogether";
053        private static final String STRING_ID_SEPERATOR = ",";
054    
055            private String idValue;
056            private Map paramMap = new HashMap();
057            private ExtensionDefinition extensionDefinition;
058    
059            protected abstract String getAttributeElementName();
060    
061            protected abstract Id resolveId(String id);
062    
063            protected abstract String getIdName();
064    
065            /**
066             * Returns qualified role names based on IDs in the XML. Each returned
067             * qualified Role contains a single ID.
068             * 
069             * @see org.kuali.rice.kew.rule.RoleAttribute#getQualifiedRoleNames(java.lang.String,
070             *      org.kuali.rice.kew.routeheader.DocumentContent)
071             */
072            public List<String> getQualifiedRoleNames(String roleName,
073                            DocumentContent documentContent) {
074                    try {
075                            readConfiguration();
076                            String elementName = (String) getParamMap().get(XML_ELEMENT_LABEL);
077                            List<String> qualifiedRoleNames = new ArrayList<String>();
078                            XPath xPath = XPathHelper.newXPath();
079                            NodeList idNodes = (NodeList) xPath.evaluate("//"
080                                            + getAttributeElementName() + "/" + elementName,
081                                            documentContent.getDocument(), XPathConstants.NODESET);
082                List<String> qualifiedRoleIds = new ArrayList<String>();  //used only for groupTogether parsing
083                for (int index = 0; index < idNodes.getLength(); index++) {
084                                    Element idElement = (Element) idNodes.item(index);
085                                    String id = idElement.getTextContent();
086                    if(isGroupTogetherRole()) {
087                        qualifiedRoleIds.add(id);
088                    } else {
089                                    qualifiedRoleNames.add(id);
090                                }
091                }
092                if(isGroupTogetherRole()){
093                    qualifiedRoleNames.add(StringUtils.join(qualifiedRoleIds, STRING_ID_SEPERATOR));
094                }
095                            return qualifiedRoleNames;
096                    } catch (XPathExpressionException e) {
097                            throw new WorkflowRuntimeException(
098                                            "Failed to evaulate XPath expression to find ids.", e);
099                    }
100            }
101    
102        private boolean isGroupTogetherRole(){
103            String value = (String) getParamMap().get(GROUP_TOGETHER_LABEL);
104            if(StringUtils.isNotBlank(value) && value.equalsIgnoreCase("true")){
105                return true;
106            }
107            return false;
108        }
109    
110            /**
111             * Takes the given qualified role which contains an ID and returns a
112             * resolved role for the entity with that id.
113             * 
114             * @see org.kuali.rice.kew.rule.RoleAttribute#resolveQualifiedRole(org.kuali.rice.kew.engine.RouteContext,
115             *      java.lang.String, java.lang.String)
116             */
117            public ResolvedQualifiedRole resolveQualifiedRole(
118                            RouteContext routeContext, String roleName, String qualifiedRole) {
119                    String roleNameLabel = (String) getParamMap().get(ROLE_NAME_LABEL);
120                    if (roleNameLabel == null) {
121                            readConfiguration();
122                            roleNameLabel = (String) getParamMap().get(ROLE_NAME_LABEL);
123                    }
124    
125            String groupTogetherLabel = (String) getParamMap().get(GROUP_TOGETHER_LABEL);
126            if (groupTogetherLabel == null) {
127                readConfiguration();
128                groupTogetherLabel = (String) getParamMap().get(GROUP_TOGETHER_LABEL);
129            }
130    
131            ResolvedQualifiedRole resolvedRole = new ResolvedQualifiedRole();
132                    resolvedRole.setQualifiedRoleLabel(roleNameLabel);
133    
134            if(isGroupTogetherRole()){
135                String[] qualifiedRoleIds = StringUtils.split(qualifiedRole, STRING_ID_SEPERATOR);
136                for (String qId : qualifiedRoleIds) {
137                    resolvedRole.getRecipients().add(resolveId(qId));
138                }
139            }else{
140                    resolvedRole.getRecipients().add(resolveId(qualifiedRole));
141            }
142            return resolvedRole;
143            }
144    
145            /**
146             * Generates XML containing the ID on this attribute.
147             * 
148             * @see org.kuali.rice.kew.rule.AbstractWorkflowAttribute#getDocContent()
149             */
150            @Override
151            public String getDocContent() {
152                    readConfiguration();
153                    if (!StringUtils.isBlank(getIdValue())) {
154                            String elementName = (String) getParamMap().get(XML_ELEMENT_LABEL);
155                            return "<" + getAttributeElementName() + "><" + elementName + ">"
156                                            + getIdValue() + "</" + elementName + "></"
157                                            + getAttributeElementName() + ">";
158                    }
159                    return "";
160            }
161    
162            /**
163             * Reads any configured values in the XML of the RuleAttribute and adds them
164             * to the paramMap.
165             * 
166             */
167            protected void readConfiguration() {
168                    String idInMap = (String) getParamMap().get(getIdName());
169                    if (getIdValue() == null) {
170                            setIdValue(idInMap);
171                    }
172                    if (getIdValue() != null) {
173                            getParamMap().put(getIdName(), getIdValue());
174                    }
175                    if (extensionDefinition != null) {
176                            String xmlConfigData = extensionDefinition.getConfiguration().get(KewApiConstants.ATTRIBUTE_XML_CONFIG_DATA);
177                            if (!StringUtils.isBlank(xmlConfigData)) {
178                                    XPath xPath = XPathHelper.newXPath();
179                                    try {
180                                            String xmlElementLabel = xPath.evaluate("/configuration/"
181                                                            + XML_ELEMENT_LABEL, new InputSource(
182                                                            new StringReader(xmlConfigData)));
183                                            String roleNameLabel = xPath.evaluate("/configuration/"
184                                                            + ROLE_NAME_LABEL, new InputSource(
185                                                            new StringReader(xmlConfigData)));
186                        String groupTogetherLabel = xPath.evaluate("/configuration/"
187                                + GROUP_TOGETHER_LABEL, new InputSource(
188                                new StringReader(xmlConfigData)));
189                                            if (!StringUtils.isBlank(xmlElementLabel)) {
190                                                    getParamMap().put(XML_ELEMENT_LABEL, xmlElementLabel);
191                                            }
192                                            if (!StringUtils.isBlank(roleNameLabel)) {
193                                                    getParamMap().put(ROLE_NAME_LABEL, roleNameLabel);
194                                            }
195                        if (!StringUtils.isBlank(groupTogetherLabel)) {
196                            getParamMap().put(GROUP_TOGETHER_LABEL, groupTogetherLabel);
197                        }
198    
199                                    } catch (XPathExpressionException e) {
200                                            throw new WorkflowRuntimeException(
201                                                            "Failed to locate Rule Attribute configuration.");
202                                    }
203                            }
204                    }
205                    // setup default values if none were defined in XML
206                    if (StringUtils.isBlank((String) getParamMap().get(XML_ELEMENT_LABEL))) {
207                            getParamMap().put(XML_ELEMENT_LABEL, getIdName());
208                    }
209                    if (getParamMap().get(ROLE_NAME_LABEL) == null) {
210                            getParamMap().put(ROLE_NAME_LABEL, "");
211                    }
212            if (StringUtils.isBlank((String) getParamMap().get(GROUP_TOGETHER_LABEL))) {
213                getParamMap().put(GROUP_TOGETHER_LABEL, "false");
214            }
215            }
216    
217            public String getIdValue() {
218                    return this.idValue;
219            }
220    
221            public void setIdValue(String idValue) {
222                    this.idValue = idValue;
223            }
224    
225            public Map getParamMap() {
226                    return paramMap;
227            }
228    
229            public void setParamMap(Map paramMap) {
230                    this.paramMap = paramMap;
231            }
232    
233            public void setExtensionDefinition(ExtensionDefinition extensionDefinition) {
234                    this.extensionDefinition = extensionDefinition;
235            }
236    
237    }