001/**
002 * Copyright 2005-2016 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 */
016package org.kuali.rice.kew.rule;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.kew.api.KewApiConstants;
020import org.kuali.rice.kew.api.WorkflowRuntimeException;
021import org.kuali.rice.kew.api.extension.ExtensionDefinition;
022import org.kuali.rice.kew.api.identity.Id;
023import org.kuali.rice.kew.engine.RouteContext;
024import org.kuali.rice.kew.routeheader.DocumentContent;
025import org.kuali.rice.kew.rule.xmlrouting.GenericXMLRuleAttribute;
026import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
027import org.w3c.dom.Element;
028import org.w3c.dom.NodeList;
029import org.xml.sax.InputSource;
030
031import javax.xml.xpath.XPath;
032import javax.xml.xpath.XPathConstants;
033import javax.xml.xpath.XPathExpressionException;
034import java.io.StringReader;
035import java.util.ArrayList;
036import java.util.HashMap;
037import java.util.List;
038import 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 */
047public 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}