Coverage Report - org.kuali.rice.kew.xml.export.DocumentTypeXmlExporter
 
Classes in this File Line Coverage Branch Coverage Complexity
DocumentTypeXmlExporter
0%
0/240
0%
0/124
4.2
DocumentTypeXmlExporter$1
N/A
N/A
4.2
DocumentTypeXmlExporter$DocumentTypeParentComparator
0%
0/10
0%
0/2
4.2
DocumentTypeXmlExporter$SplitJoinContext
0%
0/3
N/A
4.2
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kew.xml.export;
 17  
 
 18  
 import org.apache.commons.lang.StringUtils;
 19  
 import org.jdom.Document;
 20  
 import org.jdom.Element;
 21  
 import org.jdom.JDOMException;
 22  
 import org.jdom.input.SAXBuilder;
 23  
 import org.kuali.rice.core.api.impex.ExportDataSet;
 24  
 import org.kuali.rice.core.api.util.xml.XmlException;
 25  
 import org.kuali.rice.core.api.util.xml.XmlHelper;
 26  
 import org.kuali.rice.core.api.util.xml.XmlRenderer;
 27  
 import org.kuali.rice.core.framework.impex.xml.XmlExporter;
 28  
 import org.kuali.rice.kew.api.WorkflowRuntimeException;
 29  
 import org.kuali.rice.kew.doctype.DocumentTypeAttribute;
 30  
 import org.kuali.rice.kew.doctype.DocumentTypePolicy;
 31  
 import org.kuali.rice.kew.doctype.bo.DocumentType;
 32  
 import org.kuali.rice.kew.engine.node.BranchPrototype;
 33  
 import org.kuali.rice.kew.engine.node.NodeType;
 34  
 import org.kuali.rice.kew.engine.node.ProcessDefinitionBo;
 35  
 import org.kuali.rice.kew.engine.node.RouteNode;
 36  
 import org.kuali.rice.kew.api.exception.ResourceUnavailableException;
 37  
 import org.kuali.rice.kew.export.KewExportDataSet;
 38  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 39  
 import org.kuali.rice.kew.api.KewApiConstants;
 40  
 import org.kuali.rice.kim.api.group.Group;
 41  
 
 42  
 import java.io.IOException;
 43  
 import java.io.StringReader;
 44  
 import java.util.Collection;
 45  
 import java.util.Collections;
 46  
 import java.util.Comparator;
 47  
 import java.util.Iterator;
 48  
 import java.util.List;
 49  
 
 50  
 import static org.kuali.rice.core.api.impex.xml.XmlConstants.*;
 51  
 
 52  
 /**
 53  
  * Exports {@link DocumentType}s to XML.
 54  
  *
 55  
  * @see DocumentType
 56  
  *
 57  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 58  
  */
 59  0
 public class DocumentTypeXmlExporter implements XmlExporter {
 60  
 
 61  0
     protected final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(getClass());
 62  
 
 63  0
     private XmlRenderer renderer = new XmlRenderer(DOCUMENT_TYPE_NAMESPACE);
 64  
 
 65  
         @Override
 66  
         public boolean supportPrettyPrint() {
 67  0
                 return true;
 68  
         }
 69  
 
 70  
         public Element export(ExportDataSet exportDataSet) {
 71  0
             KewExportDataSet dataSet = KewExportDataSet.fromExportDataSet(exportDataSet);
 72  0
         if (!dataSet.getDocumentTypes().isEmpty()) {
 73  0
             Collections.sort(dataSet.getDocumentTypes(), new DocumentTypeParentComparator());
 74  0
             Element rootElement = renderer.renderElement(null, DOCUMENT_TYPES);
 75  0
             rootElement.setAttribute(SCHEMA_LOCATION_ATTR, DOCUMENT_TYPE_SCHEMA_LOCATION, SCHEMA_NAMESPACE);
 76  0
             for (Iterator iterator = dataSet.getDocumentTypes().iterator(); iterator.hasNext();) {
 77  0
                 DocumentType documentType = (DocumentType) iterator.next();
 78  0
                 exportDocumentType(rootElement, documentType);
 79  0
             }
 80  0
             return rootElement;
 81  
         }
 82  0
         return null;
 83  
     }
 84  
 
 85  
     private void exportDocumentType(Element parent, DocumentType documentType) {
 86  0
         Element docTypeElement = renderer.renderElement(parent, DOCUMENT_TYPE);
 87  0
         List flattenedNodes = KEWServiceLocator.getRouteNodeService().getFlattenedNodes(documentType, false);
 88  
         // derive a default exception workgroup by looking at how the nodes are configured
 89  0
         boolean hasDefaultExceptionWorkgroup = hasDefaultExceptionWorkgroup(flattenedNodes);
 90  0
         renderer.renderTextElement(docTypeElement, NAME, documentType.getName());
 91  0
         if (documentType.getParentDocType() != null) {
 92  0
             renderer.renderTextElement(docTypeElement, PARENT, documentType.getParentDocType().getName());
 93  
         }
 94  0
         renderer.renderTextElement(docTypeElement, DESCRIPTION, documentType.getDescription());
 95  0
         renderer.renderTextElement(docTypeElement, LABEL, documentType.getLabel());
 96  0
         if (!StringUtils.isBlank(documentType.getActualApplicationId())) {
 97  0
             renderer.renderTextElement(docTypeElement, APPLICATION_ID, documentType.getActualApplicationId());
 98  
         }
 99  0
         renderer.renderTextElement(docTypeElement, POST_PROCESSOR_NAME, documentType.getPostProcessorName());
 100  0
         Group superUserWorkgroup = documentType.getSuperUserWorkgroupNoInheritence();
 101  0
         if (superUserWorkgroup != null) {
 102  0
                 Element superUserGroupElement = renderer.renderTextElement(docTypeElement, SUPER_USER_GROUP_NAME, superUserWorkgroup.getName().trim());
 103  0
                 superUserGroupElement.setAttribute(NAMESPACE, superUserWorkgroup.getNamespaceCode().trim());
 104  
         }
 105  0
         Group blanketWorkgroup = documentType.getBlanketApproveWorkgroup();
 106  0
         if (blanketWorkgroup != null){
 107  0
                 Element blanketGroupElement = renderer.renderTextElement(docTypeElement, BLANKET_APPROVE_GROUP_NAME, blanketWorkgroup.getName().trim());
 108  0
                 blanketGroupElement.setAttribute(NAMESPACE, blanketWorkgroup.getNamespaceCode().trim());
 109  
         }
 110  0
         if (documentType.getBlanketApprovePolicy() != null){
 111  0
                 renderer.renderTextElement(docTypeElement, BLANKET_APPROVE_POLICY, documentType.getBlanketApprovePolicy());
 112  
         }
 113  0
         Group reportingWorkgroup = documentType.getReportingWorkgroup();
 114  0
         if (reportingWorkgroup != null) {
 115  0
                 Element reportingGroupElement = renderer.renderTextElement(docTypeElement, REPORTING_GROUP_NAME, reportingWorkgroup.getName().trim());
 116  0
                 reportingGroupElement.setAttribute(NAMESPACE, reportingWorkgroup.getNamespaceCode().trim());
 117  
         }
 118  0
         if (!flattenedNodes.isEmpty() && hasDefaultExceptionWorkgroup) {
 119  0
                 Group exceptionWorkgroup = ((RouteNode)flattenedNodes.get(0)).getExceptionWorkgroup();
 120  0
                 if (exceptionWorkgroup != null) {
 121  0
                         Element exceptionGroupElement = renderer.renderTextElement(docTypeElement, DEFAULT_EXCEPTION_GROUP_NAME, exceptionWorkgroup.getName().trim());
 122  0
                         exceptionGroupElement.setAttribute(NAMESPACE, exceptionWorkgroup.getNamespaceCode().trim());
 123  
                 }
 124  
         }
 125  0
         if (StringUtils.isNotBlank(documentType.getUnresolvedDocHandlerUrl())) {
 126  0
             renderer.renderTextElement(docTypeElement, DOC_HANDLER, documentType.getUnresolvedDocHandlerUrl());
 127  
         }
 128  0
         if (!StringUtils.isBlank(documentType.getUnresolvedHelpDefinitionUrl())) {
 129  0
             renderer.renderTextElement(docTypeElement, HELP_DEFINITION_URL, documentType.getUnresolvedHelpDefinitionUrl());
 130  
         }
 131  0
         if (!StringUtils.isBlank(documentType.getUnresolvedDocSearchHelpUrl())) {
 132  0
             renderer.renderTextElement(docTypeElement, DOC_SEARCH_HELP_URL, documentType.getUnresolvedDocSearchHelpUrl());
 133  
         }
 134  0
         if (!StringUtils.isBlank(documentType.getActualNotificationFromAddress())) {
 135  0
                 renderer.renderTextElement(docTypeElement, NOTIFICATION_FROM_ADDRESS, documentType.getActualNotificationFromAddress());
 136  
         }
 137  0
         renderer.renderBooleanElement(docTypeElement, ACTIVE, documentType.getActive(), true);
 138  0
         exportPolicies(docTypeElement, documentType.getDocumentTypePolicies());
 139  0
       exportAttributes(docTypeElement, documentType.getDocumentTypeAttributes());
 140  0
       exportSecurity(docTypeElement, documentType.getDocumentTypeSecurityXml());
 141  0
               if (!StringUtils.isBlank(documentType.getRoutingVersion())) {
 142  0
                       renderer.renderTextElement(docTypeElement, ROUTING_VERSION, documentType.getRoutingVersion());
 143  
               }
 144  0
               ProcessDefinitionBo process = null;
 145  0
               if (documentType.getProcesses().size() > 0) {
 146  0
                   process = (ProcessDefinitionBo)documentType.getProcesses().get(0);
 147  
               }
 148  0
               if (process != null && process.getInitialRouteNode() != null) {
 149  0
                   exportRouteData(docTypeElement, documentType, flattenedNodes, hasDefaultExceptionWorkgroup);
 150  
               } else {
 151  0
                   renderer.renderElement(docTypeElement, ROUTE_PATHS);
 152  
               }
 153  0
     }
 154  
 
 155  
     private void exportPolicies(Element parent, Collection policies) {
 156  0
         if (!policies.isEmpty()) {
 157  0
             Element policiesElement = renderer.renderElement(parent, POLICIES);
 158  0
             for (Iterator iterator = policies.iterator(); iterator.hasNext();) {
 159  0
                 DocumentTypePolicy policy = (DocumentTypePolicy) iterator.next();
 160  0
                 Element policyElement = renderer.renderElement(policiesElement, POLICY);
 161  0
                 renderer.renderTextElement(policyElement, NAME, policy.getPolicyName());
 162  0
                 renderer.renderBooleanElement(policyElement, VALUE, policy.getPolicyValue(), false);
 163  0
             }
 164  
         }
 165  0
     }
 166  
 
 167  
     private void exportAttributes(Element parent, List attributes) {
 168  0
         if (!attributes.isEmpty()) {
 169  0
             Element attributesElement = renderer.renderElement(parent, ATTRIBUTES);
 170  0
             for (Iterator iterator = attributes.iterator(); iterator.hasNext();) {
 171  0
                 DocumentTypeAttribute attribute = (DocumentTypeAttribute) iterator.next();
 172  0
                 Element attributeElement = renderer.renderElement(attributesElement, ATTRIBUTE);
 173  0
                 renderer.renderTextElement(attributeElement, NAME, attribute.getRuleAttribute().getName());
 174  0
             }
 175  
         }
 176  0
     }
 177  
 
 178  
     private void exportSecurity(Element parent, String securityXML) {
 179  0
       if (!org.apache.commons.lang.StringUtils.isEmpty(securityXML)) {
 180  
         try {
 181  0
           org.jdom.Document securityDoc = new SAXBuilder().build(new StringReader(securityXML));
 182  0
           XmlHelper.propagateNamespace(securityDoc.getRootElement(), DOCUMENT_TYPE_NAMESPACE);
 183  0
           parent.addContent(securityDoc.getRootElement().detach());
 184  0
         } catch (IOException e) {
 185  0
           throw new WorkflowRuntimeException("Error parsing doctype security XML.");
 186  0
         } catch (JDOMException e) {
 187  0
           throw new WorkflowRuntimeException("Error parsing doctype security XML.");
 188  0
         }
 189  
       }
 190  0
     }
 191  
 
 192  
     private void exportRouteData(Element parent, DocumentType documentType, List flattenedNodes, boolean hasDefaultExceptionWorkgroup) {
 193  0
         if (!flattenedNodes.isEmpty()) {
 194  0
             Element routePathsElement = renderer.renderElement(parent, ROUTE_PATHS);
 195  0
             for (Iterator iterator = documentType.getProcesses().iterator(); iterator.hasNext();) {
 196  0
                 ProcessDefinitionBo process = (ProcessDefinitionBo) iterator.next();
 197  0
                 Element routePathElement = renderer.renderElement(routePathsElement, ROUTE_PATH);
 198  0
                 if (!process.isInitial()) {
 199  0
                     renderer.renderAttribute(routePathElement, INITIAL_NODE, process.getInitialRouteNode().getRouteNodeName());
 200  0
                     renderer.renderAttribute(routePathElement, PROCESS_NAME, process.getName());
 201  
                 }
 202  0
                 exportProcess(routePathElement, process);
 203  0
             }
 204  
 
 205  0
             Element routeNodesElement = renderer.renderElement(parent, ROUTE_NODES);
 206  0
             for (Iterator iterator = flattenedNodes.iterator(); iterator.hasNext();) {
 207  0
                 RouteNode node = (RouteNode) iterator.next();
 208  0
                 exportRouteNode(routeNodesElement, node, hasDefaultExceptionWorkgroup);
 209  0
             }
 210  
         }
 211  0
     }
 212  
 
 213  
     /* default exception workgroup is not stored independently in db, so derive
 214  
      * one from the definition itself: if all nodes have the *same* exception workgroup name
 215  
      * defined, then this is tantamount to a *default* exception workgroup and can be
 216  
      * used as such.
 217  
      */
 218  
     private boolean hasDefaultExceptionWorkgroup(List flattenedNodes) {
 219  0
         boolean hasDefaultExceptionWorkgroup = true;
 220  0
         String exceptionWorkgroupName = null;
 221  0
         for (Iterator iterator = flattenedNodes.iterator(); iterator.hasNext();) {
 222  0
             RouteNode node = (RouteNode) iterator.next();
 223  0
             if (exceptionWorkgroupName == null) {
 224  0
                 exceptionWorkgroupName = node.getExceptionWorkgroupName();
 225  
             }
 226  0
             if (exceptionWorkgroupName == null || !exceptionWorkgroupName.equals(node.getExceptionWorkgroupName())) {
 227  0
                 hasDefaultExceptionWorkgroup = false;
 228  0
                 break;
 229  
             }
 230  0
         }
 231  0
         return hasDefaultExceptionWorkgroup;
 232  
     }
 233  
 
 234  
     private void exportProcess(Element parent, ProcessDefinitionBo process) {
 235  0
             exportNodeGraph(parent, process.getInitialRouteNode(), null);
 236  0
     }
 237  
 
 238  
     private void exportNodeGraph(Element parent, RouteNode node, SplitJoinContext splitJoinContext) {
 239  0
         NodeType nodeType = null;
 240  
 
 241  0
         String contentFragment = node.getContentFragment();
 242  
         // some of the older versions of rice do not have content fragments
 243  0
         if(contentFragment == null || "".equals(contentFragment)){
 244  0
                 nodeType = getNodeTypeForNode(node);
 245  
         }else{
 246  
                 // I'm not sure if this should be the default implementation because
 247  
                 // it uses a string comparison instead of a classpath check.
 248  0
                 nodeType = this.getNodeTypeForNodeFromFragment(node);
 249  
         }
 250  
 
 251  0
         if (nodeType.isAssignableFrom(NodeType.SPLIT)) {
 252  0
             exportSplitNode(parent, node, nodeType, splitJoinContext);
 253  0
         } else if (nodeType.isAssignableFrom(NodeType.JOIN)) {
 254  0
             exportJoinNode(parent, node, nodeType, splitJoinContext);
 255  
         } else {
 256  0
             exportSimpleNode(parent, node, nodeType, splitJoinContext);
 257  
         }
 258  0
     }
 259  
 
 260  
     private void exportSimpleNode(Element parent, RouteNode node, NodeType nodeType, SplitJoinContext splitJoinContext) {
 261  0
         Element simpleElement = renderNodeElement(parent, node, nodeType);
 262  0
         if (node.getNextNodes().size() > 1) {
 263  0
             throw new WorkflowRuntimeException("Simple node cannot have more than one next node: " + node.getRouteNodeName());
 264  
         }
 265  0
         if (node.getNextNodes().size() == 1) {
 266  0
             RouteNode nextNode = (RouteNode)node.getNextNodes().get(0);
 267  0
             renderer.renderAttribute(simpleElement, NEXT_NODE, nextNode.getRouteNodeName());
 268  0
             exportNodeGraph(parent, nextNode, splitJoinContext);
 269  
         }
 270  0
     }
 271  
 
 272  
     private void exportSplitNode(Element parent, RouteNode node, NodeType nodeType, SplitJoinContext splitJoinContext) {
 273  0
         Element splitElement = renderNodeElement(parent, node, nodeType);
 274  0
         SplitJoinContext newSplitJoinContext = new SplitJoinContext(node);
 275  0
         for (Iterator iterator = node.getNextNodes().iterator(); iterator.hasNext();) {
 276  0
             RouteNode nextNode = (RouteNode) iterator.next();
 277  0
             BranchPrototype branch = nextNode.getBranch();
 278  0
             if (branch == null) {
 279  0
                 throw new WorkflowRuntimeException("Found a split next node with no associated branch prototype: " + nextNode.getRouteNodeName());
 280  
             }
 281  0
             exportBranch(splitElement, nextNode, branch, newSplitJoinContext);
 282  0
         }
 283  0
         RouteNode joinNode = newSplitJoinContext.joinNode;
 284  0
         if (joinNode == null) {
 285  0
             if (node.getNextNodes().size() > 0) {
 286  0
                 throw new WorkflowRuntimeException("Could not locate the join node for the given split node " + node.getRouteNodeName());
 287  
             }
 288  
         } else {
 289  0
             renderNodeElement(splitElement, joinNode, newSplitJoinContext.joinNodeType);
 290  0
             if (joinNode.getNextNodes().size() > 1) {
 291  0
                 throw new WorkflowRuntimeException("Join node cannot have more than one next node: " + joinNode.getRouteNodeName());
 292  
             }
 293  0
             if (joinNode.getNextNodes().size() == 1) {
 294  0
                 RouteNode nextNode = (RouteNode)joinNode.getNextNodes().get(0);
 295  0
                 renderer.renderAttribute(splitElement, NEXT_NODE, nextNode.getRouteNodeName());
 296  0
                 exportNodeGraph(parent, nextNode, splitJoinContext);
 297  
             }
 298  
         }
 299  0
     }
 300  
 
 301  
     private void exportBranch(Element parent, RouteNode node, BranchPrototype branch, SplitJoinContext splitJoinContext) {
 302  0
         Element branchElement = renderer.renderElement(parent, BRANCH);
 303  0
         renderer.renderAttribute(branchElement, NAME, branch.getName());
 304  0
         exportNodeGraph(branchElement, node, splitJoinContext);
 305  0
     }
 306  
 
 307  
     private void exportJoinNode(Element parent, RouteNode node, NodeType nodeType, SplitJoinContext splitJoinContext) {
 308  0
         if (splitJoinContext == null) {
 309  
             // this is the case where a join node is defined as part of a sub process to be used by a dynamic node, in this case it is
 310  
             // not associated with a proper split node.
 311  0
             if (!node.getNextNodes().isEmpty()) {
 312  0
                 throw new WorkflowRuntimeException("Could not export join node with next nodes that is not contained within a split.");
 313  
             }
 314  0
             renderNodeElement(parent, node, nodeType);
 315  0
         } else if (splitJoinContext.joinNode == null) {
 316  
             // this is the case where we are "inside" the split node in the XML, by setting up this context, the calling code knows that
 317  
             // when it renders all of the branches of the split node, it can then use this context info to render the join node before
 318  
             // closing the split
 319  0
             splitJoinContext.joinNode = node;
 320  0
             splitJoinContext.joinNodeType = nodeType;
 321  
         }
 322  0
     }
 323  
 
 324  
     private Element renderNodeElement(Element parent, RouteNode node, NodeType nodeType) {
 325  0
         String nodeName = nodeType.getName();
 326  
         // if it's a request activation node, be sure to export it as a simple node
 327  0
         if (nodeType.equals(NodeType.REQUEST_ACTIVATION)) {
 328  0
             nodeName = NodeType.SIMPLE.getName();
 329  
         }
 330  0
         Element nodeElement = renderer.renderElement(parent, nodeName);
 331  0
         renderer.renderAttribute(nodeElement, NAME, node.getRouteNodeName());
 332  0
         return nodeElement;
 333  
     }
 334  
 
 335  
     /**
 336  
      * Exists for backward compatibility for nodes which don't have a content fragment.
 337  
      */
 338  
     private void exportRouteNodeOld(Element parent, RouteNode node, boolean hasDefaultExceptionWorkgroup) {
 339  0
         NodeType nodeType = getNodeTypeForNode(node);
 340  0
         Element nodeElement = renderer.renderElement(parent, nodeType.getName());
 341  0
         renderer.renderAttribute(nodeElement, NAME, node.getRouteNodeName());
 342  0
         if (!hasDefaultExceptionWorkgroup) {
 343  0
                 if (!StringUtils.isBlank(node.getExceptionWorkgroupName())) {
 344  0
                         Element exceptionGroupElement = renderer.renderTextElement(nodeElement, EXCEPTION_GROUP_NAME, node.getExceptionWorkgroupName());
 345  0
                         exceptionGroupElement.setAttribute(NAMESPACE, node.getExceptionWorkgroup().getNamespaceCode());
 346  
                 }
 347  
         }
 348  0
         if (supportsActivationType(nodeType) && !StringUtils.isBlank(node.getActivationType())) {
 349  0
             renderer.renderTextElement(nodeElement, ACTIVATION_TYPE, node.getActivationType());
 350  
         }
 351  0
         if (supportsRouteMethod(nodeType)) {
 352  0
             exportRouteMethod(nodeElement, node);
 353  0
             renderer.renderBooleanElement(nodeElement, MANDATORY_ROUTE, node.getMandatoryRouteInd(), false);
 354  0
             renderer.renderBooleanElement(nodeElement, FINAL_APPROVAL, node.getFinalApprovalInd(), false);
 355  
         }
 356  0
         if (nodeType.isCustomNode(node.getNodeType())) {
 357  0
             renderer.renderTextElement(nodeElement, TYPE, node.getNodeType());
 358  
         }
 359  0
     }
 360  
 
 361  
     private void exportRouteNode(Element parent, RouteNode node, boolean hasDefaultExceptionWorkgroup) {
 362  0
         String contentFragment = node.getContentFragment();
 363  0
         if (StringUtils.isBlank(contentFragment)) {
 364  0
             exportRouteNodeOld(parent, node, hasDefaultExceptionWorkgroup);
 365  
         } else {
 366  
             try {
 367  0
                 Document document = XmlHelper.buildJDocument(new StringReader(contentFragment));
 368  0
                 Element rootElement = document.detachRootElement();
 369  0
                 XmlHelper.propagateNamespace(rootElement, parent.getNamespace());
 370  0
                 parent.addContent(rootElement);
 371  0
             } catch (XmlException e) {
 372  0
                 throw new WorkflowRuntimeException("Failed to load the content fragment.", e);
 373  0
             }
 374  
         }
 375  0
     }
 376  
 
 377  
 
 378  
     private NodeType getNodeTypeForNode(RouteNode node) {
 379  0
         NodeType nodeType = null;
 380  0
         String errorMessage = "Could not determine proper XML element for the given node type: " + node.getNodeType();
 381  
 
 382  
         try {
 383  0
             nodeType = NodeType.fromClassName(node.getNodeType());
 384  0
         } catch (ResourceUnavailableException e) {
 385  0
             throw new WorkflowRuntimeException(errorMessage, e);
 386  0
         }
 387  0
         if (nodeType == null) {
 388  0
             throw new WorkflowRuntimeException(errorMessage);
 389  
         }
 390  0
         return nodeType;
 391  
     }
 392  
 
 393  
     /**
 394  
      *
 395  
      * This method will find the base node type via the content fragment of the node.
 396  
      * Basically, it reads the node type, start, split, join, etc and then assigns
 397  
      * the base type to it.  This is necessary because there are cases where the
 398  
      * passed in nodeType will no be in the classpath. It should, in theory do
 399  
      * the same thing as getNodeTypeForNode.
 400  
      *
 401  
      * @param node
 402  
      * @return
 403  
      */
 404  
     private NodeType getNodeTypeForNodeFromFragment(RouteNode node) {
 405  0
         NodeType nodeType = null;
 406  0
         String contentFragment = node.getContentFragment();
 407  0
         String errorMessage = "Could not determine proper XML element for the given node type: " + node.getNodeType();
 408  
 
 409  0
         for (Iterator<NodeType> iterator = NodeType.getTypeList().iterator(); iterator.hasNext();) {
 410  0
                 NodeType nType = iterator.next();
 411  
                 // checks for something like <start
 412  
                 // or <split
 413  
                 // we may want to switch this out for something a little more robust.
 414  0
                 if(contentFragment.startsWith("<" + nType.getName())){
 415  0
                            nodeType = nType;
 416  
                    }
 417  0
         }
 418  
 
 419  0
         if (nodeType == null) {
 420  0
             throw new WorkflowRuntimeException(errorMessage);
 421  
         }
 422  0
         return nodeType;
 423  
     }
 424  
 
 425  
     /**
 426  
      * Any node can support activation type, this use to not be the case but now it is.
 427  
      */
 428  
     private boolean supportsActivationType(NodeType nodeType) {
 429  0
         return true;
 430  
     }
 431  
 
 432  
     /**
 433  
      * Any node can support route methods, this use to not be the case but now it is.
 434  
      */
 435  
     private boolean supportsRouteMethod(NodeType nodeType) {
 436  0
         return true;
 437  
     }
 438  
 
 439  
     private void exportRouteMethod(Element parent, RouteNode node) {
 440  0
         if (!StringUtils.isBlank(node.getRouteMethodName())) {
 441  0
             String routeMethodCode = node.getRouteMethodCode();
 442  0
             String elementName = null;
 443  0
             if (KewApiConstants.ROUTE_LEVEL_FLEX_RM.equals(routeMethodCode)) {
 444  0
                 elementName = RULE_TEMPLATE;
 445  0
             } else if (KewApiConstants.ROUTE_LEVEL_ROUTE_MODULE.equals(routeMethodCode)) {
 446  0
                 elementName = ROUTE_MODULE;
 447  
             } else {
 448  0
                 throw new WorkflowRuntimeException("Invalid route method code '"+routeMethodCode+"' for node " + node.getRouteNodeName());
 449  
             }
 450  0
             renderer.renderTextElement(parent, elementName, node.getRouteMethodName());
 451  
         }
 452  0
     }
 453  
 
 454  0
     private class DocumentTypeParentComparator implements Comparator {
 455  
 
 456  
         public int compare(Object object1, Object object2) {
 457  0
             DocumentType docType1 = (DocumentType)object1;
 458  0
             DocumentType docType2 = (DocumentType)object2;
 459  0
             Integer depth1 = getDepth(docType1);
 460  0
             Integer depth2 = getDepth(docType2);
 461  0
             return depth1.compareTo(depth2);
 462  
         }
 463  
 
 464  
         private Integer getDepth(DocumentType docType) {
 465  0
                 int depth = 0;
 466  0
                 while ((docType = docType.getParentDocType()) != null) {
 467  0
                         depth++;
 468  
                 }
 469  0
                 return new Integer(depth);
 470  
         }
 471  
 
 472  
     }
 473  
 
 474  0
     private class SplitJoinContext {
 475  
         public RouteNode splitNode;
 476  
         public RouteNode joinNode;
 477  
         public NodeType joinNodeType;
 478  0
         public SplitJoinContext(RouteNode splitNode) {
 479  0
             this.splitNode = splitNode;
 480  0
         }
 481  
     }
 482  
 
 483  
 
 484  
 }