001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
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: */
018:
019: /* $Id: WorkflowBuilder.java 565260 2007-08-13 07:55:41Z nettings $ */
020:
021: package org.apache.lenya.workflow.impl;
022:
023: import java.io.File;
024:
025: import org.apache.avalon.framework.container.ContainerUtil;
026: import org.apache.avalon.framework.logger.AbstractLogEnabled;
027: import org.apache.avalon.framework.logger.Logger;
028: import org.apache.lenya.workflow.Condition;
029: import org.apache.lenya.workflow.Workflow;
030: import org.apache.lenya.workflow.WorkflowException;
031: import org.apache.lenya.xml.DocumentHelper;
032: import org.w3c.dom.Document;
033: import org.w3c.dom.Element;
034: import org.w3c.dom.NodeList;
035:
036: /**
037: * Utility class to build a workflow schema from a file.
038: */
039: public class WorkflowBuilder extends AbstractLogEnabled {
040:
041: protected static final String STATE_ELEMENT = "state";
042: protected static final String TRANSITION_ELEMENT = "transition";
043: protected static final String EVENT_ELEMENT = "event";
044: protected static final String CONDITION_ELEMENT = "condition";
045: //protected static final String ACTION_ELEMENT = "action";
046: protected static final String ID_ATTRIBUTE = "id";
047: protected static final String INITIAL_ATTRIBUTE = "initial";
048: protected static final String SOURCE_ATTRIBUTE = "source";
049: protected static final String DESTINATION_ATTRIBUTE = "destination";
050: protected static final String CLASS_ATTRIBUTE = "class";
051: protected static final String VARIABLE_ELEMENT = "variable";
052: protected static final String ASSIGNMENT_ELEMENT = "assign";
053: protected static final String VARIABLE_ATTRIBUTE = "variable";
054: protected static final String VALUE_ATTRIBUTE = "value";
055: protected static final String NAME_ATTRIBUTE = "name";
056: protected static final String SYNCHRONIZED_ATTRIBUTE = "synchronized";
057:
058: private ConditionFactory conditionFactory = null;
059:
060: /**
061: * Ctor.
062: * @param logger The logger to use.
063: */
064: public WorkflowBuilder(Logger logger) {
065: ContainerUtil.enableLogging(this , logger);
066: }
067:
068: /**
069: * Builds a workflow schema from a file.
070: * @param name The workflow name.
071: * @param file The file.
072: * @return A workflow schema implementation.
073: * @throws WorkflowException if the file does not represent a valid workflow
074: * schema.
075: */
076: public WorkflowImpl buildWorkflow(String name, File file)
077: throws WorkflowException {
078: WorkflowImpl workflow;
079:
080: try {
081: Document document = DocumentHelper.readDocument(file);
082: workflow = buildWorkflow(name, document);
083: } catch (Exception e) {
084: throw new WorkflowException(e);
085: }
086:
087: return workflow;
088: }
089:
090: /**
091: * Builds a workflow object from an XML document.
092: * @param name The workflow name.
093: * @param document The XML document.
094: * @return A workflow implementation.
095: * @throws WorkflowException when something went wrong.
096: */
097: protected WorkflowImpl buildWorkflow(String name, Document document)
098: throws WorkflowException {
099:
100: Element root = document.getDocumentElement();
101: String initialState = null;
102:
103: // load states
104: NodeList stateElements = root.getElementsByTagNameNS(
105: Workflow.NAMESPACE, STATE_ELEMENT);
106:
107: for (int i = 0; i < stateElements.getLength(); i++) {
108: Element element = (Element) stateElements.item(i);
109: String state = buildState(element);
110:
111: if (isInitialStateElement(element)) {
112: initialState = state;
113: }
114: }
115:
116: WorkflowImpl workflow = new WorkflowImpl(name, initialState);
117:
118: // load variables
119: NodeList variableElements = root.getElementsByTagNameNS(
120: Workflow.NAMESPACE, VARIABLE_ELEMENT);
121:
122: for (int i = 0; i < variableElements.getLength(); i++) {
123: Element element = (Element) variableElements.item(i);
124: BooleanVariableImpl variable = buildVariable(element);
125: workflow.addVariable(variable);
126: }
127:
128: // load events
129: NodeList eventElements = root.getElementsByTagNameNS(
130: Workflow.NAMESPACE, EVENT_ELEMENT);
131:
132: for (int i = 0; i < eventElements.getLength(); i++) {
133: String event = buildEvent((Element) eventElements.item(i));
134: workflow.addEvent(event);
135: }
136:
137: // load transitions
138: NodeList transitionElements = root.getElementsByTagNameNS(
139: Workflow.NAMESPACE, TRANSITION_ELEMENT);
140:
141: for (int i = 0; i < transitionElements.getLength(); i++) {
142: TransitionImpl transition = buildTransition((Element) transitionElements
143: .item(i));
144: workflow.addTransition(transition);
145: }
146:
147: return workflow;
148: }
149:
150: /**
151: * Checks if a state element contains the initial state.
152: * @param element An XML element.
153: * @return A boolean value.
154: */
155: protected boolean isInitialStateElement(Element element) {
156: String initialAttribute = element
157: .getAttribute(INITIAL_ATTRIBUTE);
158:
159: return (initialAttribute != null)
160: && (initialAttribute.equals("yes") || initialAttribute
161: .equals("true"));
162: }
163:
164: /**
165: * Builds a state from an XML element.
166: * @param element An XML element.
167: * @return A state.
168: */
169: protected String buildState(Element element) {
170: return element.getAttribute(ID_ATTRIBUTE);
171: }
172:
173: /**
174: * Builds a transition from an XML element.
175: * @param element An XML element.
176: * @return A transition.
177: * @throws WorkflowException when something went wrong.
178: */
179: protected TransitionImpl buildTransition(Element element)
180: throws WorkflowException {
181:
182: if (getLogger().isDebugEnabled()) {
183: getLogger().debug("Building transition");
184: }
185:
186: String source = element.getAttribute(SOURCE_ATTRIBUTE);
187: String destination = element
188: .getAttribute(DESTINATION_ATTRIBUTE);
189:
190: TransitionImpl transition = new TransitionImpl(source,
191: destination);
192: ContainerUtil.enableLogging(transition, getLogger());
193:
194: // set event
195: Element eventElement = (Element) element
196: .getElementsByTagNameNS(Workflow.NAMESPACE,
197: EVENT_ELEMENT).item(0);
198: String event = eventElement.getAttribute(ID_ATTRIBUTE);
199: transition.setEvent(event);
200:
201: if (getLogger().isDebugEnabled()) {
202: getLogger().debug(" Event: [" + event + "]");
203: }
204:
205: // load conditions
206: NodeList conditionElements = element.getElementsByTagNameNS(
207: Workflow.NAMESPACE, CONDITION_ELEMENT);
208:
209: for (int i = 0; i < conditionElements.getLength(); i++) {
210: Condition condition = buildCondition((Element) conditionElements
211: .item(i));
212: transition.addCondition(condition);
213: }
214:
215: // load assignments
216: NodeList assignmentElements = element.getElementsByTagNameNS(
217: Workflow.NAMESPACE, ASSIGNMENT_ELEMENT);
218:
219: for (int i = 0; i < assignmentElements.getLength(); i++) {
220: BooleanVariableAssignmentImpl action = buildAssignment((Element) assignmentElements
221: .item(i));
222: transition.addAction(action);
223: }
224:
225: // load actions
226: /*
227: * NodeList actionElements = element
228: * .getElementsByTagNameNS(Workflow.NAMESPACE, ACTION_ELEMENT);
229: *
230: * for (int i = 0; i < actionElements.getLength(); i++) { Action action =
231: * buildAction((Element) actionElements.item(i));
232: * transition.addAction(action); }
233: */
234:
235: // set synchronization
236: /* FIXME: this is not used in the default publication, and is not currently accepted by the workflow xml validation. what does it do? */
237: if (element.hasAttribute(SYNCHRONIZED_ATTRIBUTE)) {
238: Boolean isSynchronized = Boolean.valueOf(element
239: .getAttribute(SYNCHRONIZED_ATTRIBUTE));
240: transition.setSynchronized(isSynchronized.booleanValue());
241: }
242:
243: return transition;
244: }
245:
246: /**
247: * Builds an event from an XML element.
248: * @param element An XML element.
249: * @return An event.
250: */
251: protected String buildEvent(Element element) {
252: return element.getAttribute(ID_ATTRIBUTE);
253: }
254:
255: /**
256: * Builds a condition from an XML element.
257: * @param element An XML element.
258: * @return A condition.
259: * @throws WorkflowException when something went wrong.
260: */
261: protected Condition buildCondition(Element element)
262: throws WorkflowException {
263: String className = element.getAttribute(CLASS_ATTRIBUTE);
264: String expression = DocumentHelper
265: .getSimpleElementText(element);
266: if (this .conditionFactory == null) {
267: this .conditionFactory = new ConditionFactory(getLogger());
268: }
269: Condition condition = this .conditionFactory.createCondition(
270: className, expression);
271:
272: return condition;
273: }
274:
275: /**
276: * Builds a boolean variable from an XML element.
277: * @param element An XML element.
278: * @return A boolean variable.
279: */
280: protected BooleanVariableImpl buildVariable(Element element) {
281: String name = element.getAttribute(NAME_ATTRIBUTE);
282: String value = element.getAttribute(VALUE_ATTRIBUTE);
283:
284: return new BooleanVariableImpl(name, Boolean.getBoolean(value));
285: }
286:
287: /**
288: * Builds an assignment object from an XML element.
289: * @param element An XML element.
290: * @return An assignment object.
291: * @throws WorkflowException when something went wrong.
292: */
293: protected BooleanVariableAssignmentImpl buildAssignment(
294: Element element) throws WorkflowException {
295: String variableName = element.getAttribute(VARIABLE_ATTRIBUTE);
296:
297: String valueString = element.getAttribute(VALUE_ATTRIBUTE);
298: boolean value = Boolean.valueOf(valueString).booleanValue();
299:
300: return new BooleanVariableAssignmentImpl(variableName, value);
301: }
302: }
|