blob: 9eef9a0c559c065f372ade1a6d2fb9d87ba19a88 [file] [log] [blame]
/*******************************************************************************
* 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);
}
}