/******************************************************************************* * Copyright (c) 2007, 2008 Spring IDE Developers * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.webflow.core.internal.model; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.springframework.ide.eclipse.core.model.IModelElement; import org.springframework.ide.eclipse.core.model.IModelElementVisitor; import org.springframework.ide.eclipse.webflow.core.model.IAttribute; import org.springframework.ide.eclipse.webflow.core.model.ICloneableModelElement; import org.springframework.ide.eclipse.webflow.core.model.IDecisionState; import org.springframework.ide.eclipse.webflow.core.model.IExceptionHandler; import org.springframework.ide.eclipse.webflow.core.model.IGlobalTransitions; import org.springframework.ide.eclipse.webflow.core.model.IIf; import org.springframework.ide.eclipse.webflow.core.model.IIfTransition; import org.springframework.ide.eclipse.webflow.core.model.IImport; import org.springframework.ide.eclipse.webflow.core.model.IInlineFlowState; import org.springframework.ide.eclipse.webflow.core.model.IInputAttribute; import org.springframework.ide.eclipse.webflow.core.model.IInputMapper; import org.springframework.ide.eclipse.webflow.core.model.IOutputAttribute; import org.springframework.ide.eclipse.webflow.core.model.IOutputMapper; import org.springframework.ide.eclipse.webflow.core.model.IState; import org.springframework.ide.eclipse.webflow.core.model.IStateTransition; import org.springframework.ide.eclipse.webflow.core.model.ITransition; import org.springframework.ide.eclipse.webflow.core.model.ITransitionableFrom; import org.springframework.ide.eclipse.webflow.core.model.ITransitionableTo; import org.springframework.ide.eclipse.webflow.core.model.IVar; import org.springframework.ide.eclipse.webflow.core.model.IWebflowConfig; import org.springframework.ide.eclipse.webflow.core.model.IWebflowModelElement; import org.springframework.ide.eclipse.webflow.core.model.IWebflowState; import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** * @author Christian Dupuis * @since 2.0 */ @SuppressWarnings("restriction") public class WebflowState extends AbstractTransitionableFrom implements IWebflowState, ICloneableModelElement<IWebflowState> { private final IWebflowConfig webflowConfig; public WebflowState(IWebflowConfig webflowConfig) { this.webflowConfig = webflowConfig; } /** * The imports. */ private List<IImport> imports; /** * The input mapper. */ private IInputMapper inputMapper; /** * The output mapper. */ private IOutputMapper outputMapper; /** * The states. */ private List<IState> states; /** * The inline flows. */ private List<IInlineFlowState> inlineFlows; /** * The vars. */ private List<IVar> vars; private IGlobalTransitions globalTransition; private List<IInputAttribute> inputAttributes = null; private List<IOutputAttribute> outputAttributes = null; /** * @param node * @param parent */ @Override public void init(IDOMNode node, IWebflowModelElement parent) { super.init(node, parent); this.imports = new ArrayList<IImport>(); this.inlineFlows = new ArrayList<IInlineFlowState>(); this.states = new ArrayList<IState>(); this.vars = new ArrayList<IVar>(); this.inputAttributes = new ArrayList<IInputAttribute>(); this.outputAttributes = new ArrayList<IOutputAttribute>(); if (node != null) { NodeList children = node.getChildNodes(); if (children != null && children.getLength() > 0) { for (int i = 0; i < children.getLength(); i++) { IDOMNode child = (IDOMNode) children.item(i); if ("action-state".equals(child.getLocalName())) { ActionState state = new ActionState(); state.init(child, this); this.states.add(state); } else if ("view-state".equals(child.getLocalName())) { ViewState state = new ViewState(); state.init(child, this); this.states.add(state); } else if ("decision-state".equals(child.getLocalName())) { DecisionState state = new DecisionState(); state.init(child, this); this.states.add(state); } else if ("end-state".equals(child.getLocalName())) { EndState state = new EndState(); state.init(child, this); this.states.add(state); } else if ("subflow-state".equals(child.getLocalName())) { SubflowState state = new SubflowState(); state.init(child, this); this.states.add(state); } else if ("inline-flow".equals(child.getLocalName())) { InlineFlowState state = new InlineFlowState(); state.init(child, this); this.inlineFlows.add(state); } else if ("import".equals(child.getLocalName())) { Import im = new Import(); im.init(child, this); this.imports.add(im); } else if ("bean-import".equals(child.getLocalName())) { Import im = new Import(); im.init(child, this); this.imports.add(im); } else if ("var".equals(child.getLocalName())) { Variable var = new Variable(); var.init(child, this); this.vars.add(var); } else if ("start-actions".equals(child.getLocalName())) { this.entryActions = new EntryActions(); this.entryActions.init(child, this); } else if ("on-start".equals(child.getLocalName())) { this.entryActions = new EntryActions(); this.entryActions.init(child, this); } else if ("end-actions".equals(child.getLocalName())) { this.exitActions = new ExitActions(); this.exitActions.init(child, this); } else if ("on-end".equals(child.getLocalName())) { this.exitActions = new ExitActions(); this.exitActions.init(child, this); } else if ("input-mapper".equals(child.getLocalName())) { this.inputMapper = new InputMapper(); this.inputMapper.init(child, this); } else if ("output-mapper".equals(child.getLocalName())) { this.outputMapper = new OutputMapper(); this.outputMapper.init(child, this); } else if ("global-transitions".equals(child.getLocalName())) { this.globalTransition = new GlobalTransitions(); this.globalTransition.init(child, this); } else if ("input".equals(child.getLocalName())) { InputAttribute attr = new InputAttribute(); attr.init(child, this); this.inputAttributes.add(attr); } else if ("output".equals(child.getLocalName())) { OutputAttribute attr = new OutputAttribute(); attr.init(child, this); this.outputAttributes.add(attr); } } } // reconnect transistions for (IState state : this.states) { if (state instanceof ITransitionableFrom) { for (ITransition trans : ((ITransitionableFrom) state).getOutputTransitions()) { if (trans instanceof IStateTransition) { if (((IStateTransition) trans).getToState() != null) { ((IStateTransition) trans).getToState().getInputTransitions().add( trans); } } } if (state instanceof IDecisionState) { for (IIf i : ((IDecisionState) state).getIfs()) { if (i.getThenTransition() != null && i.getThenTransition().getToState() != null && !i.getThenTransition().getToState().getInputTransitions() .contains(i.getThenTransition())) { i.getThenTransition().getToState().getInputTransitions().add( i.getThenTransition()); } if (i.getElseTransition() != null && i.getElseTransition().getToState() != null && !i.getElseTransition().getToState().getInputTransitions() .contains(i.getElseTransition())) { i.getElseTransition().getToState().getInputTransitions().add( i.getElseTransition()); } } } } } } } /** * @param ip */ public void addImport(IImport ip) { if (!getImports().contains(ip)) { this.getImports().add(ip); WebflowModelXmlUtils.insertNode(ip.getNode(), node); super.firePropertyChange(ADD_CHILDREN, new Integer(this.getStates().indexOf(ip)), ip); } } /** * @param i * @param pm */ public void addImport(IImport pm, int i) { if (!getImports().contains(pm)) { int refIndex = i; if (i >= this.vars.size()) { refIndex = this.vars.size() - 1; } IVar refState = this.vars.get(refIndex); this.getImports().add(i, pm); WebflowModelXmlUtils.insertBefore(pm.getNode(), refState.getNode()); super.firePropertyChange(ADD_CHILDREN, new Integer(i), pm); } } /** * @param state */ public void addState(IState state) { if (!getStates().contains(state)) { // attach to xml after last state WebflowModelXmlUtils.insertNode(state.getNode(), node); this.states.add(state); super.firePropertyChange(ADD_CHILDREN, new Integer(this.states.indexOf(state)), state); // add possible dead transitions to new state if (state instanceof ITransitionableTo) { for (IState s : this.states) { if (s instanceof ITransitionableFrom) { for (ITransition trans : ((ITransitionableFrom) s).getOutputTransitions()) { if (trans instanceof IStateTransition) { IStateTransition stateTrans = (IStateTransition) trans; if (state.getId().equals(stateTrans.getToStateId())) { if (!((ITransitionableTo) state).getInputTransitions() .contains(trans)) { stateTrans.getFromState().fireStructureChange(OUTPUTS, stateTrans); ((ITransitionableTo) state).addInputTransition(trans); } } } } } if (s instanceof IDecisionState) { for (IIf i : ((IDecisionState) s).getIfs()) { if (i.getThenTransition() != null) { IIfTransition ifTrans = (IIfTransition) i.getThenTransition(); if (state.getId().equals(ifTrans.getToStateId())) { if (!((ITransitionableTo) state).getInputTransitions() .contains(ifTrans)) { ((IWebflowModelElement) ifTrans.getElementParent()) .fireStructureChange(OUTPUTS, ifTrans); ((ITransitionableTo) state).addInputTransition(ifTrans); } } } if (i.getElseTransition() != null) { IIfTransition ifTrans = (IIfTransition) i.getElseTransition(); if (state.getId().equals(ifTrans.getToStateId())) { if (!((ITransitionableTo) state).getInputTransitions() .contains(ifTrans)) { ((IWebflowModelElement) ifTrans.getElementParent()) .fireStructureChange(OUTPUTS, ifTrans); ((ITransitionableTo) state).addInputTransition(ifTrans); } } } } } } } } } /** * @param i * @param state */ public void addState(IState state, int i) { if (!getStates().contains(state)) { int refIndex = i; if (i >= this.states.size()) { refIndex = this.states.size() - 1; } IState refState = getStates().get(refIndex); this.states.add(i, state); WebflowModelXmlUtils.insertBefore(state.getNode(), refState.getNode()); super.firePropertyChange(ADD_CHILDREN, new Integer(i), state); } } /** * @param state */ public void addVar(IVar state) { if (!getVars().contains(state)) { this.getVars().add(state); WebflowModelXmlUtils.insertNode(state.getNode(), node); super.firePropertyChange(ADD_CHILDREN, new Integer(this.getVars().indexOf(state)), state); } } /** * @param i * @param state */ public void addVar(IVar state, int i) { if (!getVars().contains(state)) { int refIndex = i; if (i >= this.vars.size()) { refIndex = this.vars.size() - 1; } IVar refState = this.vars.get(refIndex); this.getVars().add(i, state); WebflowModelXmlUtils.insertBefore(state.getNode(), refState.getNode()); super.firePropertyChange(ADD_CHILDREN, new Integer(i), state); } } /** * @return */ public List<IImport> getImports() { return this.imports; } /** * @return */ public IInputMapper getInputMapper() { return inputMapper; } /** * @return */ public IOutputMapper getOutputMapper() { return outputMapper; } /** * @return */ public IState getStartState() { if (hasStartState()) { if (WebflowModelXmlUtils.isVersion1Flow(this)) { List<IDOMNode> nodes = getChildrenNodeByTagName("start-state"); IDOMNode node = nodes.get(0); return WebflowModelXmlUtils.getStateById(this, getAttribute(node, "idref")); } else { IState state = WebflowModelXmlUtils.getStateById(this, getAttribute(node, "start-state")); if (state != null) { return state; } else { return getStates().get(0); } } } return null; } /** * @return */ public List<IState> getStates() { return this.states; } /** * @return */ public List<IInlineFlowState> getInlineFlowStates() { return this.inlineFlows; } /** * @return */ public List<IVar> getVars() { return this.vars; } /** * @return */ public boolean hasStartState() { if (WebflowModelXmlUtils.isVersion1Flow(this)) { List<IDOMNode> nodes = getChildrenNodeByTagName("start-state"); return nodes != null && nodes.size() == 1; } else { return (getAttribute("start-state") != null || getStates().size() > 0); } } /** * @param state * @return */ public boolean isStartState(IState state) { return hasStartState() && state.getId() != null && state.equals(getStartState()); } /** * @param i * @param im */ public void moveImport(IImport im, int i) { // TODO Auto-generated method stub } /** * @param i * @param state */ public void moveState(IState state, int i) { if (!getStates().contains(state)) { int refIndex = i; if (i >= this.states.size()) { refIndex = this.states.size() - 1; } IState refState = getStates().get(refIndex); removeState(state); this.states.add(i, state); WebflowModelXmlUtils.insertBefore(state.getNode(), refState.getNode()); super.firePropertyChange(MOVE_CHILDREN, new Integer(i), state); } } /** * @param i * @param state */ public void moveVar(IVar state, int i) { // TODO Auto-generated method stub } /** * @param im */ public void removeImport(IImport im) { if (getImports().contains(im)) { this.getImports().remove(im); if (im.getNode().getParentNode() != null) { getNode().removeChild(im.getNode()); } super.fireStructureChange(REMOVE_CHILDREN, im); } } /** * @param state */ public void removeState(IState state) { if (getStates().contains(state)) { this.states.remove(state); if (state.getNode().getParentNode() != null) { getNode().removeChild(state.getNode()); } super.fireStructureChange(REMOVE_CHILDREN, state); } } /** * @param state */ public void removeVar(IVar state) { if (getVars().contains(state)) { this.getVars().remove(state); if (state.getNode().getParentNode() != null) { getNode().removeChild(state.getNode()); } super.fireStructureChange(REMOVE_CHILDREN, state); } } /** * @param state */ public void setStartState(IState state) { IState oldState = getStartState(); if (WebflowModelXmlUtils.isVersion1Flow(this)) { List<IDOMNode> nodes = getChildrenNodeByTagName("start-state"); if (nodes != null && nodes.size() > 0) { IDOMNode node = nodes.get(0); setAttribute(node, "idref", state.getId()); } else { Element startNode = getNode().getOwnerDocument().createElement("start-state"); WebflowModelXmlUtils.insertNode(startNode, node); setAttribute((IDOMNode) startNode, "idref", state.getId()); } } else { setAttribute("start-state", state.getId()); } // removeState(state); // addState(state, 0); if (oldState != null) { oldState.fireStructureChange(PROPS, oldState); } state.fireStructureChange(PROPS, state); } /** * @param parent */ public void createNew(IWebflowState parent) { IDOMNode node = (IDOMNode) parent.getNode().getOwnerDocument().createElement("flow"); init(node, parent); } /** * @return */ public IWebflowState cloneModelElement() { WebflowState state = new WebflowState(this.webflowConfig); state.init((IDOMNode) this.node.cloneNode(true), parent); return state; } /** * @param element */ public void applyCloneValues(IWebflowState element) { if (element != null) { if (this.node.getParentNode() != null) { this.parent.getNode().replaceChild(element.getNode(), this.node); } init(element.getNode(), parent); } } /** * @param outputMapper */ public void setOutputMapper(IOutputMapper outputMapper) { if (this.outputMapper != null) { getNode().removeChild(this.outputMapper.getNode()); } this.outputMapper = outputMapper; if (outputMapper != null) { WebflowModelXmlUtils.insertNode(outputMapper.getNode(), getNode()); } super.fireStructureChange(ADD_CHILDREN, outputMapper); } /** * @param inputMapper */ public void setInputMapper(IInputMapper inputMapper) { if (this.inputMapper != null) { getNode().removeChild(this.inputMapper.getNode()); } this.inputMapper = inputMapper; if (inputMapper != null) { WebflowModelXmlUtils.insertNode(inputMapper.getNode(), getNode()); } super.fireStructureChange(ADD_CHILDREN, inputMapper); } /** * @param state */ public void addInlineFlowState(IInlineFlowState state) { if (!this.inlineFlows.contains(state)) { // attach to xml after last state WebflowModelXmlUtils.insertNode(state.getNode(), node); this.inlineFlows.add(state); super.firePropertyChange(ADD_CHILDREN, new Integer(this.inlineFlows.indexOf(state)), state); } } /** * @param i * @param state */ public void addInlineFlowState(IInlineFlowState state, int i) { if (!this.inlineFlows.contains(state)) { int refIndex = i; if (i >= this.inlineFlows.size()) { refIndex = this.inlineFlows.size(); } IState refState = getStates().get(refIndex); this.inlineFlows.add(i, state); WebflowModelXmlUtils.insertBefore(state.getNode(), refState.getNode()); super.firePropertyChange(ADD_CHILDREN, new Integer(i), state); } } /** * @param i * @param state */ public void moveInlineFlowState(IInlineFlowState state, int i) { } /** * @param state */ public void removeInlineFlowState(IInlineFlowState state) { if (this.inlineFlows.contains(state)) { this.inlineFlows.remove(state); getNode().removeChild(state.getNode()); super.fireStructureChange(REMOVE_CHILDREN, state); } } public void accept(IModelElementVisitor visitor, IProgressMonitor monitor) { if (!monitor.isCanceled() && visitor.visit(this, monitor)) { for (IState state : getStates()) { if (monitor.isCanceled()) { return; } state.accept(visitor, monitor); } for (IImport state : getImports()) { if (monitor.isCanceled()) { return; } state.accept(visitor, monitor); } for (IInlineFlowState state : getInlineFlowStates()) { if (monitor.isCanceled()) { return; } state.accept(visitor, monitor); } for (IAttribute state : getAttributes()) { if (monitor.isCanceled()) { return; } state.accept(visitor, monitor); } for (IInputAttribute state : getInputAttributes()) { if (monitor.isCanceled()) { return; } state.accept(visitor, monitor); } for (IOutputAttribute state : getOutputAttributes()) { if (monitor.isCanceled()) { return; } state.accept(visitor, monitor); } for (IVar state : getVars()) { if (monitor.isCanceled()) { return; } state.accept(visitor, monitor); } for (IExceptionHandler state : getExceptionHandlers()) { if (monitor.isCanceled()) { return; } state.accept(visitor, monitor); } if (monitor.isCanceled()) { return; } if (getInputMapper() != null) { getInputMapper().accept(visitor, monitor); } if (getOutputMapper() != null) { getInputMapper().accept(visitor, monitor); } if (getEntryActions() != null) { getEntryActions().accept(visitor, monitor); } if (getExitActions() != null) { getExitActions().accept(visitor, monitor); } } } public IModelElement[] getElementChildren() { Set<IModelElement> children = new HashSet<IModelElement>(); children.addAll(getStates()); children.addAll(getImports()); children.addAll(getInlineFlowStates()); children.addAll(getAttributes()); children.addAll(getVars()); children.addAll(getInputAttributes()); children.addAll(getOutputAttributes()); children.addAll(getExceptionHandlers()); children.add(getInputMapper()); return children.toArray(new IModelElement[children.size()]); } public IGlobalTransitions getGlobalTransitions() { return this.globalTransition; } public void setGlobalTransitions(IGlobalTransitions inputMapper) { if (this.globalTransition != null) { getNode().removeChild(this.globalTransition.getNode()); } this.globalTransition = inputMapper; if (inputMapper != null) { WebflowModelXmlUtils.insertNode(inputMapper.getNode(), getNode()); } super.fireStructureChange(ADD_CHILDREN, inputMapper); } public IModelElement getElementParent() { return this.webflowConfig; } public String getAbstract() { return getAttribute("abstract"); } public void setAbstract(String abstrakt) { setAttribute("abstract", abstrakt); } public List<IInputAttribute> getInputAttributes() { return this.inputAttributes; } public void addInputAttribute(IInputAttribute action) { if (!this.inputAttributes.contains(action)) { WebflowModelXmlUtils.insertNode(action.getNode(), node); this.inputAttributes.add(action); super.firePropertyChange(ADD_CHILDREN, new Integer(this.inputAttributes.indexOf(action)), action); } } public void addInputAttribute(IInputAttribute action, int i) { if (!this.inputAttributes.contains(action)) { WebflowModelXmlUtils.insertNode(action.getNode(), node); this.inputAttributes.add(i, action); super.firePropertyChange(ADD_CHILDREN, new Integer(this.inputAttributes.indexOf(action)), action); } } public void removeAllInputAttribute() { for (IInputAttribute action : this.inputAttributes) { getNode().removeChild(action.getNode()); } this.inputAttributes = new ArrayList<IInputAttribute>(); } public void removeInputAttribute(IInputAttribute action) { if (this.inputAttributes.contains(action)) { this.inputAttributes.remove(action); getNode().removeChild(action.getNode()); super.fireStructureChange(REMOVE_CHILDREN, action); } } public List<IOutputAttribute> getOutputAttributes() { return this.outputAttributes; } public void addOutputAttribute(IOutputAttribute action) { if (!this.outputAttributes.contains(action)) { WebflowModelXmlUtils.insertNode(action.getNode(), node); this.outputAttributes.add(action); super.firePropertyChange(ADD_CHILDREN, new Integer(this.outputAttributes .indexOf(action)), action); } } public void addOutputAttribute(IOutputAttribute action, int i) { if (!this.outputAttributes.contains(action)) { WebflowModelXmlUtils.insertNode(action.getNode(), node); this.outputAttributes.add(i, action); super.firePropertyChange(ADD_CHILDREN, new Integer(this.outputAttributes .indexOf(action)), action); } } public void removeAllOutputAttribute() { for (IInputAttribute action : this.outputAttributes) { getNode().removeChild(action.getNode()); } this.outputAttributes = new ArrayList<IOutputAttribute>(); } public void removeOutputAttribute(IOutputAttribute action) { if (this.outputAttributes.contains(action)) { this.outputAttributes.remove(action); getNode().removeChild(action.getNode()); super.fireStructureChange(REMOVE_CHILDREN, action); } } public void addPersistenceContext() { if (!hasPersitenceContext()) { Element startNode = getNode().getOwnerDocument().createElement("persistence-context"); WebflowModelXmlUtils.insertNode(startNode, node); } } public boolean hasPersitenceContext() { List<IDOMNode> nodes = getChildrenNodeByTagName("persistence-context"); return nodes.size() > 0; } public void removePersistenceContext() { if (hasPersitenceContext()) { List<IDOMNode> nodes = getChildrenNodeByTagName("persistence-context"); for (IDOMNode node : nodes) { getNode().removeChild(node); } } } }