001: /*
002: * Copyright 2005-2006 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package edu.iu.uis.eden.engine;
018:
019: import java.util.ArrayList;
020: import java.util.List;
021:
022: import edu.iu.uis.eden.EdenConstants;
023: import edu.iu.uis.eden.actionrequests.ActionRequestValue;
024: import edu.iu.uis.eden.doctype.DocumentType;
025: import edu.iu.uis.eden.engine.node.Process;
026: import edu.iu.uis.eden.engine.node.RouteNode;
027: import edu.iu.uis.eden.exception.WorkflowRuntimeException;
028: import edu.iu.uis.eden.routeheader.DocumentRouteHeaderValue;
029:
030: /**
031: * Provides utility methods for handling backwards compatibility between KEW releases.
032: * Currently, it's primary function is to handle backward compatability between the
033: * deprecated "route level" concept and the "node" concept which was introduced in
034: * KEW 2.1.
035: *
036: * @author ewestfal
037: */
038: public class CompatUtils {
039:
040: private static RouteHelper helper = new RouteHelper();
041:
042: public static Integer getLevelForNode(DocumentType documentType,
043: String nodeName) {
044: if (isRouteLevelCompatible(documentType)) {
045: return getLevelForNode(documentType.getPrimaryProcess()
046: .getInitialRouteNode(), nodeName, new Integer(0));
047: }
048: return new Integer(EdenConstants.INVALID_ROUTE_LEVEL);
049: }
050:
051: private static Integer getLevelForNode(RouteNode node,
052: String nodeName, Integer currentLevel) {
053: // TODO potential for infinite recursion here if their document type has loops in it. Should this be a concern?
054: // If their routing version is really "route level" then there should be no cycles.
055: if (node.getRouteNodeName().equals(nodeName)) {
056: return currentLevel;
057: }
058: List nextNodes = node.getNextNodes();
059: if (nextNodes.isEmpty()) {
060: throw new WorkflowRuntimeException(
061: "Could not locate node with name '" + nodeName
062: + "'");
063: }
064: if (nextNodes.size() > 1) {
065: throw new WorkflowRuntimeException(
066: "Can only determine route level for document types with no splitting");
067: }
068: RouteNode nextNode = (RouteNode) nextNodes.get(0);
069: return getLevelForNode(nextNode, nodeName, new Integer(
070: currentLevel.intValue() + 1));
071: }
072:
073: /**
074: * Returns the RouteNode at the given numerical route level for the given document type.
075: * This currently throws a WorkflowException if the document has parallel routing structures
076: * because the route level as a number becomes arbitrary in that case.
077: */
078: public static RouteNode getNodeForLevel(DocumentType documentType,
079: Integer routeLevel) {
080: Object[] node = getNodeForLevel(documentType
081: .getPrimaryProcess().getInitialRouteNode(), routeLevel,
082: new Integer(0));
083: return (RouteNode) node[0];
084: }
085:
086: private static Object[] getNodeForLevel(RouteNode node,
087: Integer routeLevel, Integer currentLevel) {
088: if (helper.isSubProcessNode(node)) {
089: Object[] result = getNodeForLevel(node.getDocumentType()
090: .getNamedProcess(node.getRouteNodeName())
091: .getInitialRouteNode(), routeLevel, currentLevel);
092: if (result[0] != null) {
093: node = (RouteNode) result[0];
094: }
095: currentLevel = (Integer) result[1];
096: }
097: if (currentLevel.equals(routeLevel)) {
098: return new Object[] { node, currentLevel };
099: }
100: List nextNodes = node.getNextNodes();
101: if (nextNodes.isEmpty()) {
102: return new Object[] { null, currentLevel };
103: }
104: if (nextNodes.size() > 1) {
105: throw new WorkflowRuntimeException(
106: "Cannot determine a route level number for documents with splitting.");
107: }
108: currentLevel = new Integer(currentLevel.intValue() + 1);
109: return getNodeForLevel((RouteNode) nextNodes.get(0),
110: routeLevel, currentLevel);
111: }
112:
113: public static boolean isRouteLevelCompatible(
114: DocumentType documentType) {
115: return EdenConstants.ROUTING_VERSION_ROUTE_LEVEL
116: .equals(documentType.getRoutingVersion());
117: }
118:
119: public static boolean isRouteLevelCompatible(
120: DocumentRouteHeaderValue document) {
121: return isRouteLevelCompatible(document.getDocumentType());
122: }
123:
124: public static boolean isNodalDocument(
125: DocumentRouteHeaderValue document) {
126: return EdenConstants.DOCUMENT_VERSION_NODAL == document
127: .getDocVersion().intValue();
128: }
129:
130: public static boolean isNodalRequest(ActionRequestValue request) {
131: return EdenConstants.DOCUMENT_VERSION_NODAL == request
132: .getDocVersion().intValue();
133: }
134:
135: public static boolean isRouteLevelDocument(
136: DocumentRouteHeaderValue document) {
137: return EdenConstants.DOCUMENT_VERSION_ROUTE_LEVEL == document
138: .getDocVersion().intValue();
139: }
140:
141: public static boolean isRouteLevelRequest(ActionRequestValue request) {
142: return EdenConstants.DOCUMENT_VERSION_ROUTE_LEVEL == request
143: .getDocVersion().intValue();
144: }
145:
146: /**
147: * Returns a list of RouteNodes in a flat list which is equivalent to the route level concept of
148: * Workflow <= version 2.0. If the document type is not route level compatible, then this method will throw an error.
149: */
150: public static List getRouteLevelCompatibleNodeList(
151: DocumentType documentType) {
152: if (!isRouteLevelCompatible(documentType)) {
153: throw new WorkflowRuntimeException(
154: "Attempting to invoke a 'route level' operation on a document which is not route level compatible.");
155: }
156: Process primaryProcess = documentType.getPrimaryProcess();
157: RouteNode routeNode = primaryProcess.getInitialRouteNode();
158: List nodes = new ArrayList();
159: int count = 0;
160: int maxCount = 100;
161: while (true) {
162: nodes.add(routeNode);
163: List nextNodes = routeNode.getNextNodes();
164: if (nextNodes.size() == 0) {
165: break;
166: }
167: if (nextNodes.size() > 1) {
168: throw new RuntimeException(
169: "Node has more than one next node! It is not route level compatible!"
170: + routeNode.getRouteNodeName());
171: }
172: if (count >= maxCount) {
173: throw new RuntimeException(
174: "A runaway loop was detected when attempting to create route level compatible node graph. documentType="
175: + documentType.getDocumentTypeId()
176: + "," + documentType.getName());
177: }
178: routeNode = (RouteNode) nextNodes.iterator().next();
179: }
180: return nodes;
181: }
182:
183: public static int getMaxRouteLevel(DocumentType documentType) {
184: return getRouteLevelCompatibleNodeList(documentType).size();
185: }
186: }
|