| /******************************************************************************* |
| * Copyright (c) 2001, 2004 IBM Corporation and others. |
| * 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: |
| * IBM Corporation - initial API and implementation |
| * Jens Lukowski/Innoopract - initial renaming/restructuring |
| * |
| *******************************************************************************/ |
| package org.eclipse.wst.xml.ui.internal.actions; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Vector; |
| |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMDataType; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMNode; |
| import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery; |
| import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQueryAction; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.eclipse.wst.xml.ui.internal.XMLUIMessages; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.DocumentType; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.w3c.dom.ProcessingInstruction; |
| |
| public abstract class BaseNodeActionManager { |
| |
| |
| /** |
| * MyMenuManager |
| */ |
| public static class MyMenuManager extends MenuManager { |
| protected String title; |
| |
| public MyMenuManager(String s) { |
| super(s); |
| title = s; |
| } |
| |
| public boolean isEnabled() { |
| return !isEmpty(); |
| } |
| |
| public String toString() { |
| return title; |
| } |
| } |
| |
| public static DocumentType getDoctype(Node node) { |
| Document document = (node.getNodeType() == Node.DOCUMENT_NODE) ? (Document) node : node.getOwnerDocument(); |
| return document.getDoctype(); |
| } |
| |
| protected MenuBuilder menuBuilder = new MenuBuilder(); |
| protected IStructuredModel fModel; |
| protected ModelQuery modelQuery; |
| |
| protected BaseNodeActionManager(IStructuredModel model, ModelQuery modelQuery) { |
| this.fModel = model; |
| this.modelQuery = modelQuery; |
| } |
| |
| |
| protected void addActionHelper(IMenuManager menu, List modelQueryActionList) { |
| List actionList = new Vector(); |
| for (Iterator i = modelQueryActionList.iterator(); i.hasNext();) { |
| ModelQueryAction action = (ModelQueryAction) i.next(); |
| if (action.getCMNode() != null) { |
| int cmNodeType = action.getCMNode().getNodeType(); |
| if (action.getKind() == ModelQueryAction.INSERT) { |
| switch (cmNodeType) { |
| case CMNode.ATTRIBUTE_DECLARATION : { |
| actionList.add(createAddAttributeAction((Element) action.getParent(), (CMAttributeDeclaration) action.getCMNode())); |
| break; |
| } |
| case CMNode.ELEMENT_DECLARATION : { |
| actionList.add(createAddElementAction(action.getParent(), (CMElementDeclaration) action.getCMNode(), action.getStartIndex())); |
| break; |
| } |
| } |
| } else if (action.getKind() == ModelQueryAction.REPLACE) { |
| if (action.getParent() != null && action.getCMNode() != null) { |
| actionList.add(createReplaceAction(action.getParent(), action.getCMNode(), action.getStartIndex(), action.getEndIndex())); |
| } |
| } |
| } |
| } |
| menuBuilder.populateMenu(menu, actionList, false); |
| } |
| |
| protected void contributeAction(IMenuManager menu, Action action) { |
| if (action != null) { |
| menu.add(action); |
| } |
| } |
| |
| |
| public void contributeActions(IMenuManager menu, List selection) { |
| int editMode = modelQuery.getEditMode(); |
| int ic = ModelQuery.INCLUDE_CHILD_NODES; |
| int vc = (editMode == ModelQuery.EDIT_MODE_CONSTRAINED_STRICT) ? ModelQuery.VALIDITY_STRICT : ModelQuery.VALIDITY_NONE; |
| |
| List implicitlySelectedNodeList = null; |
| |
| if (selection.size() > 0) { |
| implicitlySelectedNodeList = getSelectedNodes(selection, true); |
| |
| // contribute delete actions |
| contributeDeleteActions(menu, implicitlySelectedNodeList, ic, vc); |
| } |
| |
| if (selection.size() == 1) { |
| Node node = (Node) selection.get(0); |
| |
| // contribute edit actions |
| contributeEditActions(menu, node); |
| |
| // contribute add child actions |
| contributeAddChildActions(menu, node, ic, vc); |
| |
| // contribute add before actions |
| contributeAddSiblingActions(menu, node, ic, vc); |
| } |
| |
| if (selection.size() > 0) { |
| // contribute replace actions |
| contributeReplaceActions(menu, implicitlySelectedNodeList, ic, vc); |
| } |
| |
| if (selection.size() == 0) { |
| Document document = ((IDOMModel) fModel).getDocument(); |
| contributeAddDocumentChildActions(menu, document, ic, vc); |
| contributeEditGrammarInformationActions(menu, document); |
| } |
| } |
| |
| |
| protected void contributeAddChildActions(IMenuManager menu, Node node, int ic, int vc) { |
| int nodeType = node.getNodeType(); |
| |
| if (nodeType == Node.ELEMENT_NODE) { |
| // 'Add Child...' and 'Add Attribute...' actions |
| // |
| Element element = (Element) node; |
| |
| IMenuManager addAttributeMenu = new MyMenuManager(XMLUIMessages._UI_MENU_ADD_ATTRIBUTE); //$NON-NLS-1$ |
| IMenuManager addChildMenu = new MyMenuManager(XMLUIMessages._UI_MENU_ADD_CHILD); //$NON-NLS-1$ |
| menu.add(addAttributeMenu); |
| menu.add(addChildMenu); |
| |
| CMElementDeclaration ed = modelQuery.getCMElementDeclaration(element); |
| if (ed != null) { |
| // add insert attribute actions |
| // |
| List modelQueryActionList = new ArrayList(); |
| modelQuery.getInsertActions(element, ed, -1, ModelQuery.INCLUDE_ATTRIBUTES, vc, modelQueryActionList); |
| addActionHelper(addAttributeMenu, modelQueryActionList); |
| |
| // add insert child node actions |
| // |
| modelQueryActionList = new ArrayList(); |
| modelQuery.getInsertActions(element, ed, -1, ic, vc, modelQueryActionList); |
| addActionHelper(addChildMenu, modelQueryActionList); |
| } |
| |
| // add PI and COMMENT |
| contributePIAndCommentActions(addChildMenu, element, ed, -1); |
| |
| // add PCDATA, CDATA_SECTION |
| contributeTextNodeActions(addChildMenu, element, ed, -1); |
| |
| // add NEW ELEMENT |
| contributeUnconstrainedAddElementAction(addChildMenu, element, ed, -1); |
| |
| // add ATTRIBUTE |
| contributeUnconstrainedAttributeActions(addAttributeMenu, element, ed); |
| } |
| } |
| |
| |
| protected void contributeAddDocumentChildActions(IMenuManager menu, Document document, int ic, int vc) { |
| IMenuManager addChildMenu = new MyMenuManager(XMLUIMessages._UI_MENU_ADD_CHILD); //$NON-NLS-1$ |
| menu.add(addChildMenu); |
| |
| // add PI and COMMENT |
| contributePIAndCommentActions(addChildMenu, document, -1); |
| |
| // add NEW ELEMENT |
| contributeUnconstrainedAddElementAction(addChildMenu, document, -1); |
| } |
| |
| |
| protected void contributeAddSiblingActions(IMenuManager menu, Node node, int ic, int vc) { |
| IMenuManager addBeforeMenu = new MyMenuManager(XMLUIMessages._UI_MENU_ADD_BEFORE); //$NON-NLS-1$ |
| IMenuManager addAfterMenu = new MyMenuManager(XMLUIMessages._UI_MENU_ADD_AFTER); //$NON-NLS-1$ |
| menu.add(addBeforeMenu); |
| menu.add(addAfterMenu); |
| |
| Node parentNode = node.getParentNode(); |
| if (parentNode != null) { |
| int index = getIndex(parentNode, node); |
| if (parentNode.getNodeType() == Node.ELEMENT_NODE) { |
| Element parentElement = (Element) parentNode; |
| CMElementDeclaration parentED = modelQuery.getCMElementDeclaration(parentElement); |
| if (parentED != null) { |
| // 'Add Before...' and 'Add After...' actions |
| // |
| List modelQueryActionList = new ArrayList(); |
| modelQuery.getInsertActions(parentElement, parentED, index, ic, vc, modelQueryActionList); |
| addActionHelper(addBeforeMenu, modelQueryActionList); |
| |
| modelQueryActionList = new ArrayList(); |
| modelQuery.getInsertActions(parentElement, parentED, index + 1, ic, vc, modelQueryActionList); |
| addActionHelper(addAfterMenu, modelQueryActionList); |
| } |
| |
| // add COMMENT and PI before and after |
| contributePIAndCommentActions(addBeforeMenu, parentElement, parentED, index); |
| contributePIAndCommentActions(addAfterMenu, parentElement, parentED, index + 1); |
| |
| // add PCDATA, CDATA_SECTION before and after |
| contributeTextNodeActions(addBeforeMenu, parentElement, parentED, index); |
| contributeTextNodeActions(addAfterMenu, parentElement, parentED, index + 1); |
| |
| // add NEW ELEMENT before and after |
| contributeUnconstrainedAddElementAction(addBeforeMenu, parentElement, parentED, index); |
| contributeUnconstrainedAddElementAction(addAfterMenu, parentElement, parentED, index + 1); |
| } else if (parentNode.getNodeType() == Node.DOCUMENT_NODE) { |
| Document document = (Document) parentNode; |
| CMDocument cmDocument = modelQuery.getCorrespondingCMDocument(parentNode); |
| if (cmDocument != null) { |
| // add possible root element insertions |
| // |
| List modelQueryActionList = new ArrayList(); |
| modelQuery.getInsertActions(document, cmDocument, index, ic, vc, modelQueryActionList); |
| addActionHelper(addAfterMenu, modelQueryActionList); |
| |
| modelQueryActionList = new ArrayList(); |
| modelQuery.getInsertActions(document, cmDocument, index + 1, ic, vc, modelQueryActionList); |
| addActionHelper(addAfterMenu, modelQueryActionList); |
| } |
| |
| // add COMMENT and PI before and after |
| contributePIAndCommentActions(addBeforeMenu, document, index); |
| contributePIAndCommentActions(addAfterMenu, document, index + 1); |
| |
| // add ELEMENT before and after |
| contributeUnconstrainedAddElementAction(addBeforeMenu, document, index); |
| contributeUnconstrainedAddElementAction(addAfterMenu, document, index + 1); |
| } |
| } |
| } |
| |
| protected void contributeDeleteActions(IMenuManager menu, List list, int ic, int vc) { |
| boolean canRemove = modelQuery.canRemove(list, vc); |
| |
| |
| // a delete action with an empty list will produce a disabled menu |
| // item |
| // |
| List resultList = canRemove ? list : Collections.EMPTY_LIST; |
| contributeAction(menu, createDeleteAction(resultList)); |
| } |
| |
| |
| protected void contributeEditActions(IMenuManager menu, Node node) { |
| contributeEditGrammarInformationActions(menu, node); |
| |
| if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { |
| contributeAction(menu, createEditProcessingInstructionAction((ProcessingInstruction) node)); |
| } else if (node.getNodeType() == Node.ATTRIBUTE_NODE) { |
| contributeAction(menu, createEditAttributeAction((Attr) node, null)); |
| } |
| } |
| |
| |
| protected void contributeEditGrammarInformationActions(IMenuManager menu, Node node) { |
| Document document = node.getNodeType() == Node.DOCUMENT_NODE ? (Document) node : node.getOwnerDocument(); |
| |
| DocumentType doctype = getDoctype(node); |
| if (doctype == null) { |
| contributeAction(menu, createAddDoctypeAction(document, -1)); |
| } |
| |
| if (node.getNodeType() == Node.DOCUMENT_TYPE_NODE) { |
| contributeAction(menu, createEditDoctypeAction((DocumentType) node)); |
| } |
| |
| if (doctype == null && getRootElement(document) != null) { |
| contributeAction(menu, createEditSchemaInfoAction(getRootElement(document))); |
| } |
| } |
| |
| protected void contributePIAndCommentActions(IMenuManager menu, Document document, int index) { |
| // test to make sure that the index isn't before the XML declaration |
| // |
| contributeAction(menu, createAddCommentAction(document, index)); |
| contributeAction(menu, createAddProcessingInstructionAction(document, index)); |
| } |
| |
| |
| protected void contributePIAndCommentActions(IMenuManager menu, Element parentElement, CMElementDeclaration parentEd, int index) { |
| if (parentEd == null || isCommentAllowed(parentEd)) { |
| contributeAction(menu, createAddCommentAction(parentElement, index)); |
| contributeAction(menu, createAddProcessingInstructionAction(parentElement, index)); |
| } |
| } |
| |
| |
| protected void contributeReplaceActions(IMenuManager menu, List selectedNodeList, int ic, int vc) { |
| // 'Replace With...' actions |
| // |
| IMenuManager replaceWithMenu = new MyMenuManager(XMLUIMessages._UI_MENU_REPLACE_WITH); //$NON-NLS-1$ |
| menu.add(replaceWithMenu); |
| |
| if (modelQuery.getEditMode() == ModelQuery.EDIT_MODE_CONSTRAINED_STRICT && selectedNodeList.size() > 0) { |
| Node node = (Node) selectedNodeList.get(0); |
| Node parentNode = node.getParentNode(); |
| if (parentNode != null && parentNode.getNodeType() == Node.ELEMENT_NODE) { |
| Element parentElement = (Element) parentNode; |
| CMElementDeclaration parentED = modelQuery.getCMElementDeclaration(parentElement); |
| if (parentED != null) { |
| List replaceActionList = new Vector(); |
| modelQuery.getReplaceActions(parentElement, parentED, selectedNodeList, ic, vc, replaceActionList); |
| addActionHelper(replaceWithMenu, replaceActionList); |
| } |
| } |
| } |
| } |
| |
| protected void contributeTextNodeActions(IMenuManager menu, Element parentElement, CMElementDeclaration parentEd, int index) { |
| if (parentEd == null || isTextAllowed(parentEd)) { |
| CMDataType dataType = parentEd != null ? parentEd.getDataType() : null; |
| contributeAction(menu, createAddPCDataAction(parentElement, dataType, index)); |
| contributeAction(menu, createAddCDataSectionAction(parentElement, index)); |
| } |
| } |
| |
| |
| protected void contributeUnconstrainedAddElementAction(IMenuManager menu, Document document, int index) { |
| if (isUnconstrainedActionAllowed()) { |
| if (getRootElement(document) == null) { |
| int xmlDeclarationIndex = -1; |
| int doctypeIndex = -1; |
| NodeList nodeList = document.getChildNodes(); |
| int nodeListLength = nodeList.getLength(); |
| for (int i = 0; i < nodeListLength; i++) { |
| Node node = nodeList.item(i); |
| int nodeType = node.getNodeType(); |
| if (nodeType == Node.DOCUMENT_TYPE_NODE) { |
| doctypeIndex = i; |
| break; |
| } else if (nodeType == Node.PROCESSING_INSTRUCTION_NODE) { |
| ProcessingInstruction pi = (ProcessingInstruction) node; |
| if (pi.getTarget().equalsIgnoreCase("xml") && xmlDeclarationIndex == -1) { //$NON-NLS-1$ |
| xmlDeclarationIndex = i; |
| } |
| } |
| } |
| |
| if ((xmlDeclarationIndex == -1 || index > xmlDeclarationIndex) && (doctypeIndex == -1 || index > doctypeIndex)) { |
| contributeAction(menu, createAddElementAction(document, null, index)); |
| } |
| } |
| } |
| } |
| |
| |
| protected void contributeUnconstrainedAddElementAction(IMenuManager menu, Element parentElement, CMElementDeclaration parentEd, int index) { |
| if (isUnconstrainedActionAllowed()) { |
| if (parentEd == null || parentEd.getProperty("isInferred") == Boolean.TRUE || (modelQuery.getEditMode() != ModelQuery.EDIT_MODE_CONSTRAINED_STRICT && isElementAllowed(parentEd))) { //$NON-NLS-1$ |
| contributeAction(menu, createAddElementAction(parentElement, null, index)); |
| } |
| } |
| } |
| |
| |
| protected void contributeUnconstrainedAttributeActions(IMenuManager menu, Element parentElement, CMElementDeclaration parentEd) { |
| if (isUnconstrainedActionAllowed()) { |
| if (parentEd == null || parentEd.getProperty("isInferred") == Boolean.TRUE || modelQuery.getEditMode() != ModelQuery.EDIT_MODE_CONSTRAINED_STRICT) { //$NON-NLS-1$ |
| contributeAction(menu, createAddAttributeAction(parentElement, null)); |
| } |
| } |
| } |
| |
| abstract protected Action createAddAttributeAction(Element parent, CMAttributeDeclaration ad); |
| |
| abstract protected Action createAddCDataSectionAction(Node parent, int index); |
| |
| abstract protected Action createAddCommentAction(Node parent, int index); |
| |
| abstract protected Action createAddDoctypeAction(Document parent, int index); |
| |
| abstract protected Action createAddElementAction(Node parent, CMElementDeclaration ed, int index); |
| |
| abstract protected Action createAddPCDataAction(Node parent, CMDataType dataType, int index); |
| |
| abstract protected Action createAddProcessingInstructionAction(Node parent, int index); |
| |
| abstract protected Action createAddSchemaInfoAction(Element element); |
| |
| abstract protected Action createDeleteAction(List selection); |
| |
| abstract protected Action createEditAttributeAction(Attr attribute, CMAttributeDeclaration ad); |
| |
| abstract protected Action createEditDoctypeAction(DocumentType doctype); |
| |
| abstract protected Action createEditProcessingInstructionAction(ProcessingInstruction pi); |
| |
| abstract protected Action createEditSchemaInfoAction(Element element); |
| |
| abstract protected Action createRenameAction(Node node); |
| |
| abstract protected Action createReplaceAction(Node parent, CMNode cmnode, int startIndex, int endIndex); |
| |
| |
| public int getIndex(Node parentNode, Node child) { |
| NodeList nodeList = parentNode.getChildNodes(); |
| int index = -1; |
| int size = nodeList.getLength(); |
| for (int i = 0; i < size; i++) { |
| if (nodeList.item(i) == child) { |
| index = i; |
| break; |
| } |
| } |
| return index; |
| } |
| |
| |
| public Node getRefChildNodeAtIndex(Node parent, int index) { |
| NodeList nodeList = parent.getChildNodes(); |
| Node refChild = (index >= 0 && index < nodeList.getLength()) ? nodeList.item(index) : null; |
| return refChild; |
| } |
| |
| |
| protected Element getRootElement(Document document) { |
| Element result = null; |
| NodeList nodeList = document.getChildNodes(); |
| int nodeListLength = nodeList.getLength(); |
| for (int i = 0; i < nodeListLength; i++) { |
| Node node = nodeList.item(i); |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| result = (Element) node; |
| break; |
| } |
| } |
| return result; |
| } |
| |
| |
| protected List getSelectedNodes(List list, boolean includeTextNodes) { |
| List result = new ArrayList(0); |
| for (Iterator i = list.iterator(); i.hasNext();) { |
| Object object = i.next(); |
| if (object instanceof Node) { |
| Node node = (Node) object; |
| if (node.getNodeType() == Node.TEXT_NODE) { |
| if (includeTextNodes) { |
| result.add(object); |
| } |
| } else { |
| result.add(node); |
| } |
| } |
| } |
| return result; |
| } |
| |
| |
| protected boolean isCommentAllowed(CMElementDeclaration parentEd) { |
| int contentType = parentEd.getContentType(); |
| return contentType == CMElementDeclaration.ELEMENT || contentType == CMElementDeclaration.MIXED || contentType == CMElementDeclaration.PCDATA || contentType == CMElementDeclaration.ANY; |
| } |
| |
| |
| protected boolean isElementAllowed(CMElementDeclaration parentEd) { |
| int contentType = parentEd.getContentType(); |
| return contentType == CMElementDeclaration.ELEMENT || contentType == CMElementDeclaration.MIXED || contentType == CMElementDeclaration.ANY; |
| } |
| |
| |
| protected boolean isTextAllowed(CMElementDeclaration parentEd) { |
| int contentType = parentEd.getContentType(); |
| return contentType == CMElementDeclaration.MIXED || contentType == CMElementDeclaration.PCDATA || contentType == CMElementDeclaration.ANY; |
| } |
| |
| |
| protected boolean isUnconstrainedActionAllowed() { |
| return true; |
| } |
| |
| |
| protected boolean isWhitespaceTextNode(Node node) { |
| return (node != null) && (node.getNodeType() == Node.TEXT_NODE) && (node.getNodeValue().trim().length() == 0); |
| } |
| } |