/******************************************************************************* * 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.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.xml.core.internal.document.DOMModelImpl; import org.eclipse.wst.xml.core.internal.document.ElementImpl; import org.eclipse.wst.xml.core.internal.document.TextImpl; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.springframework.ide.eclipse.webflow.core.model.IState; import org.springframework.ide.eclipse.webflow.core.model.IWebflowModelElement; import org.springframework.ide.eclipse.webflow.core.model.IWebflowState; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; /** * @author Christian Dupuis * @since 2.0 */ @SuppressWarnings("restriction") public class WebflowModelXmlUtils { /** * The Constant PRIORITIES. */ private static final Map<String, Integer> PRIORITIES; static { PRIORITIES = new HashMap<String, Integer>(); // flow state PRIORITIES.put("flow.attribute", 1); PRIORITIES.put("flow.secured", 2); PRIORITIES.put("flow.persistence-context", 3); PRIORITIES.put("flow.var", 4); PRIORITIES.put("flow.input-mapper", 5); PRIORITIES.put("flow.input", 6); PRIORITIES.put("flow.start-actions", 7); PRIORITIES.put("flow.on-start", 8); PRIORITIES.put("flow.start-state", 9); PRIORITIES.put("flow.set", 10); PRIORITIES.put("flow.action-state", 11); PRIORITIES.put("flow.view-state", 12); PRIORITIES.put("flow.decision-state", 12); PRIORITIES.put("flow.subflow-state", 12); PRIORITIES.put("flow.end-state", 13); PRIORITIES.put("flow.global-transitions", 14); PRIORITIES.put("flow.end-actions", 15); PRIORITIES.put("flow.on-end", 16); PRIORITIES.put("flow.output-mapper", 17); PRIORITIES.put("flow.output", 18); PRIORITIES.put("flow.exception-handler", 19); PRIORITIES.put("flow.import", 20); PRIORITIES.put("flow.bean-import", 21); PRIORITIES.put("flow.inline-flow", 22); // action state PRIORITIES.put("action-state.attribute", 1); PRIORITIES.put("action-state.secured", 2); PRIORITIES.put("action-state.entry-actions", 3); PRIORITIES.put("action-state.on-entry", 4); PRIORITIES.put("action-state.action", 5); PRIORITIES.put("action-state.bean-action", 6); PRIORITIES.put("action-state.evaluate-action", 7); PRIORITIES.put("action-state.evaluate", 7); PRIORITIES.put("action-state.render", 7); PRIORITIES.put("action-state.set", 7); PRIORITIES.put("action-state.transition", 8); PRIORITIES.put("action-state.exit-actions", 9); PRIORITIES.put("action-state.on-exit", 10); PRIORITIES.put("action-state.exception-handler", 11); // view state PRIORITIES.put("view-state.attribute", 1); PRIORITIES.put("view-state.secured", 2); PRIORITIES.put("view-state.var", 3); PRIORITIES.put("view-state.entry-actions", 4); PRIORITIES.put("view-state.on-entry", 5); PRIORITIES.put("view-state.render-actions", 6); PRIORITIES.put("view-state.on-render", 7); PRIORITIES.put("view-state.transition", 8); PRIORITIES.put("view-state.exit-actions", 9); PRIORITIES.put("view-state.on-exit", 10); PRIORITIES.put("view-state.exception-handler", 12); // decision state PRIORITIES.put("decision-state.attribute", 1); PRIORITIES.put("decision-state.secured", 2); PRIORITIES.put("decision-state.entry-actions", 3); PRIORITIES.put("decision-state.on-entry", 4); PRIORITIES.put("decision-state.if", 5); PRIORITIES.put("decision-state.exit-actions", 6); PRIORITIES.put("decision-state.on-exit", 7); PRIORITIES.put("decision-state.exception-handler", 8); // sub flow state PRIORITIES.put("subflow-state.attribute", 1); PRIORITIES.put("subflow-state.secured", 2); PRIORITIES.put("subflow-state.entry-actions", 3); PRIORITIES.put("subflow-state.on-entry", 4); PRIORITIES.put("subflow-state.attribute-mapper", 5); PRIORITIES.put("subflow-state.input", 6); PRIORITIES.put("subflow-state.output", 7); PRIORITIES.put("subflow-state.transition", 8); PRIORITIES.put("subflow-state.exit-actions", 9); PRIORITIES.put("subflow-state.on-exit", 10); PRIORITIES.put("subflow-state.exception-handler", 11); // end state PRIORITIES.put("end-state.attribute", 1); PRIORITIES.put("end-state.secured", 2); PRIORITIES.put("end-state.entry-actions", 3); PRIORITIES.put("end-state.on-entry", 4); PRIORITIES.put("end-state.output-mapper", 5); PRIORITIES.put("end-state.output", 6); PRIORITIES.put("end-state.exception-handler", 7); // bean action PRIORITIES.put("bean-action.attribute", 1); PRIORITIES.put("bean-action.method-arguments", 2); PRIORITIES.put("bean-action.method-result", 3); // evaludate action PRIORITIES.put("evaluate-action.attribute", 1); PRIORITIES.put("evaluate-action.evaluation-result", 2); PRIORITIES.put("evaluate.attribute", 1); // input mapper PRIORITIES.put("input-mapper.input-attribute", 1); PRIORITIES.put("input-mapper.mapping", 2); // output mapper PRIORITIES.put("output-mapper.output-attribute", 1); PRIORITIES.put("output-mapper.mapping", 2); // action PRIORITIES.put("action.attribute", 1); // transition PRIORITIES.put("transition.attribute", 1); PRIORITIES.put("transition.secured", 2); PRIORITIES.put("transition.action", 3); PRIORITIES.put("transition.bean-action", 4); PRIORITIES.put("transition.evaluate-action", 5); PRIORITIES.put("transition.evaluate", 6); PRIORITIES.put("transition.render", 7); PRIORITIES.put("transition.set", 8); } /** * Gets the state by id. * @param webflowState the webflow state * @param id the id * @return the state by id */ public static IState getStateById(IWebflowState webflowState, String id) { if (webflowState.getStates() != null && webflowState.getStates().size() > 0) { for (IState state : webflowState.getStates()) { if (state.getId().equals(id)) { return state; } } } return null; } /** * Insert node. * @param parentNode the parent node * @param nodeToInsert the node to insert */ public static void insertNode(Node nodeToInsert, Node parentNode) { Node node = determineNodeToInsert(nodeToInsert, parentNode); if (node != null) { parentNode.insertBefore(nodeToInsert, node); } else { parentNode.appendChild(nodeToInsert); } if (parentNode instanceof ElementImpl) { ((ElementImpl) parentNode).setEmptyTag(false); } if (!"flow".equals(parentNode.getLocalName())) { // remove unrequired blank lines removeTextChildren(parentNode); } } /** * @param refNode * @param nodeToInsert */ public static void insertBefore(Node nodeToInsert, Node refNode) { Node parentNode = refNode.getParentNode(); if (parentNode != null) { parentNode.insertBefore(nodeToInsert, refNode); } if (parentNode instanceof ElementImpl) { ((ElementImpl) parentNode).setEmptyTag(false); } if (!"flow".equals(parentNode.getLocalName())) { // remove unrequired blank lines removeTextChildren(parentNode); } } /** * Removes the text children. * @param elem the elem */ public static void removeTextChildren(Node elem) { NodeList children = elem.getChildNodes(); List<Node> textElements = new ArrayList<Node>(); for (int j = 0; j < children.getLength(); j++) { Node nodetest = children.item(j); if (nodetest instanceof TextImpl) { textElements.add(nodetest); } else { removeTextChildren(nodetest); } } for (int k = 0; k < textElements.size(); k++) { elem.removeChild(textElements.get(k)); } elem.normalize(); } /** * Determine node to insert. * @param node the node * @param nodeToInsert the node to insert * @return the node */ public static Node determineNodeToInsert(Node nodeToInsert, Node node) { return determineNodeToInsert(nodeToInsert.getLocalName(), node); } /** * Determine node to insert. * @param elementName the element name * @param node the node * @return the node */ public static Node determineNodeToInsert(String elementName, Node node) { Node insertNode = null; Node lastChild = null; String key = node.getLocalName() + "." + elementName; if (!PRIORITIES.containsKey(key)) { return insertNode; } int prio = PRIORITIES.get(node.getLocalName() + "." + elementName); NodeList children = node.getChildNodes(); if (children != null && children.getLength() > 0) { for (int i = 0; i < children.getLength(); i++) { if (children.item(i).getLocalName() != null) { key = node.getLocalName() + "." + children.item(i).getLocalName(); if (PRIORITIES.containsKey(key)) { int p = PRIORITIES.get(key); if (prio < p) { insertNode = children.item(i); break; } else if (prio == p && children.item(i).getNextSibling() != null) { insertNode = children.item(i).getNextSibling(); break; } else { lastChild = children.item(i); } } } } } if (insertNode == null && lastChild != null) { if (lastChild.getNextSibling() != null) { insertNode = lastChild.getNextSibling(); } else { Text text = node.getOwnerDocument().createTextNode(""); insertNode = node.appendChild(text); } } return insertNode; } /** * Gets the states. * @param includeSelf the include self * @param state the state * @return the states */ public static List<IState> getStates(IWebflowModelElement state, boolean includeSelf) { List<IState> states = new ArrayList<IState>(); if (state instanceof IWebflowState) { states.addAll(((IWebflowState) state).getStates()); } else if (state.getElementParent() instanceof IWebflowState) { IWebflowState sws = (IWebflowState) state.getElementParent(); if (includeSelf) { states.addAll(sws.getStates()); } else { for (IState s : sws.getStates()) { if (!s.equals(state)) { states.add(s); } } } } return states; } public static Map<IDOMNode, Integer> getNodeLineNumbers(IDOMNode root, IDOMNode clone) { Map<IDOMNode, Integer> nodesToLineNumbers = new HashMap<IDOMNode, Integer>(); calculateNodeLineNumbers(root, clone, nodesToLineNumbers); return nodesToLineNumbers; } private static void calculateNodeLineNumbers(IDOMNode root, IDOMNode clone, Map<IDOMNode, Integer> nodesToLineNumbers) { if (root.getNodeType() == Node.ELEMENT_NODE) { int line = root.getStructuredDocument().getLineOfOffset(root.getStartOffset()) + 1; nodesToLineNumbers.put(clone, line); } NodeList rootChilds = root.getChildNodes(); NodeList cloneChilds = clone.getChildNodes(); for (int i = 0; i < rootChilds.getLength(); i++) { calculateNodeLineNumbers((IDOMNode) rootChilds.item(i), (IDOMNode) cloneChilds.item(i), nodesToLineNumbers); } } public static boolean isVersion1Flow(IWebflowModelElement element) { IStructuredModel model = null; try { model = StructuredModelManager.getModelManager().getExistingModelForRead( element.getElementResource()); if (model == null) { model = StructuredModelManager.getModelManager().getModelForRead( (IFile) element.getElementResource()); } if (model != null) { IDOMDocument document = ((DOMModelImpl) model).getDocument(); NamedNodeMap attributes = document.getDocumentElement().getAttributes(); IDOMAttr schemaLocationNode = (IDOMAttr) attributes.getNamedItemNS( "http://www.w3.org/2001/XMLSchema-instance", "schemaLocation"); String content = schemaLocationNode.getValue(); return !content.contains("spring-webflow-2.0.xsd"); } } catch (Exception e) { if (model != null) { model.releaseFromRead(); } } return false; } }