| /******************************************************************************* |
| * Copyright (c) 2002, 2006 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.core.internal.contentmodel.modelqueryimpl; |
| |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Vector; |
| |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration; |
| 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.CMGroup; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMNode; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMNodeList; |
| import org.eclipse.wst.xml.core.internal.contentmodel.internal.util.CMValidator; |
| import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery; |
| import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQueryAction; |
| 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; |
| |
| |
| /** |
| * |
| */ |
| public class ModelQueryActionHelper |
| { |
| protected ModelQueryImpl modelQuery; |
| |
| protected static class Action implements ModelQueryAction |
| { |
| public int kind; |
| public int startIndex; |
| public int endIndex; |
| public Node parent; |
| public CMNode cmNode; |
| public Object userData; |
| |
| public Action(int kind, Node parent, CMNode cmNode) |
| { |
| this.kind = kind; |
| this.parent = parent; |
| this.cmNode = cmNode; |
| } |
| |
| public Action(int kind, Node parent, CMNode cmNode, int startIndex, int endIndex) |
| { |
| this.kind = kind; |
| this.parent = parent; |
| this.cmNode = cmNode; |
| this.startIndex = startIndex; |
| this.endIndex = endIndex; |
| } |
| |
| public int getKind() |
| { |
| return kind; |
| } |
| |
| public int getStartIndex() |
| { |
| return startIndex; |
| } |
| |
| public int getEndIndex() |
| { |
| return endIndex; |
| } |
| |
| public Node getParent() |
| { |
| return parent; |
| } |
| |
| public CMNode getCMNode() |
| { |
| return cmNode; |
| } |
| |
| public Object getUserData() |
| { |
| return userData; |
| } |
| |
| public void setUserData(Object object) |
| { |
| userData = object; |
| } |
| |
| public void performAction() |
| { |
| } |
| } |
| |
| |
| public ModelQueryActionHelper(ModelQueryImpl modelQuery) |
| { |
| this.modelQuery = modelQuery; |
| } |
| |
| |
| public void getAllActions(Element parent, CMElementDeclaration ed, int validityChecking, List actionList) |
| { |
| } |
| |
| |
| // insert actions |
| // |
| public void getInsertActions(Element parent, CMElementDeclaration ed, int index, int includeOptions, int validityChecking, List actionList) |
| { |
| if ((includeOptions & ModelQuery.INCLUDE_ATTRIBUTES) != 0) |
| { |
| getInsertAttributeActions(parent, ed, validityChecking, actionList); |
| } |
| includeOptions &= ~ModelQuery.INCLUDE_ATTRIBUTES; |
| if ((includeOptions & ModelQuery.INCLUDE_CHILD_NODES) != 0) |
| { |
| if (index != -1) |
| { |
| getInsertChildNodeActionsAtIndex(parent, ed, index, includeOptions, validityChecking, actionList); |
| } |
| else |
| { |
| getInsertChildNodeActions(parent, ed, includeOptions, validityChecking, actionList); |
| } |
| } |
| } |
| |
| |
| |
| protected void getInsertAttributeActions(Element parent, CMElementDeclaration ed, int validityChecking, List actionList) |
| { |
| // get actions for each insertable attribute |
| // |
| List availableAttributeList = modelQuery.getAvailableContent(parent, ed, ModelQuery.INCLUDE_ATTRIBUTES); |
| |
| for (Iterator i = availableAttributeList.iterator(); i.hasNext(); ) |
| { |
| CMAttributeDeclaration ad = (CMAttributeDeclaration)i.next(); |
| if (modelQuery.canInsert(parent, ed, ad, 0, validityChecking)) |
| { |
| Action action = new Action(ModelQueryAction.INSERT, parent, ad); |
| actionList.add(action); |
| } |
| } |
| } |
| |
| |
| protected void getInsertChildNodeActionsAtIndex(Element parent, CMElementDeclaration ed, int index, int includeOptions, int validityChecking, List actionList) |
| { |
| // get actions for each insertable attribute |
| // |
| int size = parent.getChildNodes().getLength(); |
| if (index <= size) |
| { |
| List contentSpecificationList = modelQuery.getValidator().createContentSpecificationList(parent, ed); |
| List availableChildNodeList = modelQuery.getAvailableContent(parent, ed, includeOptions); |
| |
| boolean isSimpleChoice = isSimpleChoiceGroupContentModel(ed); |
| |
| for (Iterator i = availableChildNodeList.iterator(); i.hasNext(); ) |
| { |
| CMNode cmNode = (CMNode)i.next(); |
| if (isSimpleChoice || modelQuery.canInsert(parent, ed, cmNode, index, validityChecking, contentSpecificationList)) |
| { |
| Action action = new Action(ModelQueryAction.INSERT, parent, cmNode, index, index); |
| actionList.add(action); |
| } |
| } |
| } |
| } |
| |
| |
| protected boolean isSimpleChoiceGroupContentModel(CMElementDeclaration ed) |
| { |
| boolean result = false; |
| CMNode cmNode = ed.getContent(); |
| if (cmNode != null && cmNode.getNodeType() == CMNode.GROUP) |
| { |
| CMGroup cmGroup = (CMGroup)cmNode; |
| if (cmGroup.getOperator() == CMGroup.CHOICE && cmGroup.getMaxOccur() == -1) |
| { |
| result = true; |
| CMNodeList list = cmGroup.getChildNodes(); |
| for (int i = list.getLength() - 1; i >= 0; i--) |
| { |
| if (list.item(i).getNodeType() != CMNode.ELEMENT_DECLARATION) |
| { |
| result = false; |
| break; |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| |
| protected void getInsertChildNodeActions(Element parent, CMElementDeclaration ed, int includeOptions, int validityChecking, List actionList) |
| { |
| int size = parent.getChildNodes().getLength(); |
| List contentSpecificationList = modelQuery.getValidator().createContentSpecificationList(parent, ed); |
| List availableChildNodeList = modelQuery.getAvailableContent(parent, ed, includeOptions); |
| |
| boolean isSimpleChoice = isSimpleChoiceGroupContentModel(ed); |
| |
| for (Iterator iterator = availableChildNodeList.iterator(); iterator.hasNext(); ) |
| { |
| CMNode cmNode = (CMNode)iterator.next(); |
| for (int i = size; i >= 0; i--) |
| { |
| if (isSimpleChoice || modelQuery.canInsert(parent, ed, cmNode, i, validityChecking, contentSpecificationList)) |
| { |
| Action action = new Action(ModelQueryAction.INSERT, parent, cmNode, i, i); |
| actionList.add(action); |
| break; |
| } |
| } |
| } |
| } |
| |
| public void getInsertActions(Document parent, CMDocument cmDocument, int index, int includeOptions, int validityChecking, List actionList) |
| { |
| // get the root element and doctype index (if any) |
| // |
| int doctypeIndex = -1; |
| DocumentType doctype = null; |
| Element rootElement = null; |
| NodeList nodeList = parent.getChildNodes(); |
| int nodeListLength = nodeList.getLength(); |
| for (int i = 0; i < nodeListLength; i++) |
| { |
| Node childNode = nodeList.item(i); |
| if (childNode.getNodeType() == Node.ELEMENT_NODE) |
| { |
| rootElement = (Element)childNode; |
| break; |
| } |
| else if (childNode.getNodeType() == Node.DOCUMENT_TYPE_NODE) |
| { |
| doctype = (DocumentType)childNode; |
| doctypeIndex = i; |
| } |
| } |
| |
| // make sure that root elements are only added after the doctype (if any) |
| if (rootElement == null && index > doctypeIndex) |
| { |
| CMNamedNodeMap map = cmDocument.getElements(); |
| int mapLength = map.getLength(); |
| for (int i = 0; i < mapLength; i++) |
| { |
| CMNode cmNode = map.item(i); |
| |
| boolean canAdd = true; |
| if (validityChecking == ModelQuery.VALIDITY_STRICT) |
| { |
| canAdd = doctype == null || doctype.getName().equals(cmNode.getNodeName()); |
| } |
| |
| if (canAdd) |
| { |
| Action action = new Action(ModelQueryAction.INSERT, parent, cmNode, index, index); |
| actionList.add(action); |
| } |
| } |
| } |
| } |
| |
| |
| |
| public void getInsertChildNodeActionTable(Element parent, CMElementDeclaration ed, int validityChecking, Hashtable actionTable) |
| { |
| } |
| |
| |
| public void getReplaceActions(Element parent, CMElementDeclaration ed, int includeOptions, int validityChecking, List actionList) |
| { |
| CMValidator.MatchModelNode matchModelNode = modelQuery.getValidator().getMatchModel(ed, parent); |
| if (matchModelNode != null) |
| { |
| MatchModelVisitor visitor = new MatchModelVisitor(parent, actionList); |
| visitor.visitMatchModelNode(matchModelNode); |
| } |
| } |
| |
| public void getReplaceActions(Element parent, CMElementDeclaration ed, List selectedChildren, int includeOptions, int validityChecking, List actionList) |
| { |
| int[] range = getRange(parent, selectedChildren); |
| if (range != null) |
| { |
| if (isContiguous(parent, range, selectedChildren)) |
| { |
| List tempList = new Vector(); |
| getReplaceActions(parent, ed, includeOptions, validityChecking, tempList); |
| if ((includeOptions & ModelQuery.INCLUDE_ENCLOSING_REPLACE_ACTIONS) != 0) |
| { |
| removeActionsNotContainingRange(tempList, range[0], range[1]); |
| } |
| else |
| { |
| removeActionsNotMatchingRange(tempList, range[0], range[1]); |
| } |
| actionList.addAll(tempList); |
| } |
| } |
| |
| if (selectedChildren.size() == 1) |
| { |
| Node node = (Node)selectedChildren.get(0); |
| if (node.getNodeType() == Node.ELEMENT_NODE) |
| { |
| Element childElement = (Element)node; |
| CMNode childEd = modelQuery.getCMElementDeclaration(childElement); |
| if (childEd != null) |
| { |
| |
| CMNode childOrigin= modelQuery.getOrigin(childElement); |
| |
| CMNodeList cmNodeList = childOrigin != null ? |
| (CMNodeList)childOrigin.getProperty("SubstitutionGroup") : //$NON-NLS-1$ |
| (CMNodeList)childEd.getProperty("SubstitutionGroup"); //$NON-NLS-1$ |
| |
| if (cmNodeList != null && cmNodeList.getLength() > 1) |
| { |
| int replaceIndex = getIndex(parent, childElement); |
| String childEdName = childEd.getNodeName(); |
| for (int i = 0; i < cmNodeList.getLength(); i++) |
| { |
| CMNode substitution = cmNodeList.item(i); |
| if (!substitution.getNodeName().equals(childEdName) && !Boolean.TRUE.equals(substitution.getProperty("Abstract"))) //$NON-NLS-1$ |
| { |
| Action action = new Action(ModelQueryAction.REPLACE, parent, cmNodeList.item(i), replaceIndex, replaceIndex); |
| actionList.add(action); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| |
| // returns true if the selected nodes are contiguous |
| // |
| protected boolean isContiguous(Element parent, int[] range, List selectedNodeList) |
| { |
| boolean result = true; |
| NodeList nodeList = parent.getChildNodes(); |
| // issue: nodeListLength was never read, but in theory, |
| // nodelList.getLength() might cause some clearing of cached |
| // data, or something, so leaving in a potential meaningless call, for now. |
| //int nodeListLength = nodeList.getLength(); |
| nodeList.getLength(); |
| for (int i = range[0]; i < range[1]; i++) |
| { |
| Node node = nodeList.item(i); |
| if (!isWhitespaceNode(node) && !selectedNodeList.contains(node)) |
| { |
| result = false; |
| break; |
| } |
| } |
| return result; |
| } |
| |
| |
| protected int[] getRange(Element parent, List list) |
| { |
| int[] result = null; |
| int first = -1; |
| int last = -1; |
| |
| NodeList nodeList = parent.getChildNodes(); |
| int nodeListLength = nodeList.getLength(); |
| for (int i = 0; i < nodeListLength; i++) |
| { |
| Node node = nodeList.item(i); |
| if (list.contains(node)) |
| { |
| first = (first == -1) ? i : Math.min(first, i); |
| last = Math.max(last, i); |
| } |
| } |
| |
| if (first != -1 && last!= -1) |
| { |
| result = new int[2]; |
| result[0] = first; |
| result[1] = last; |
| } |
| return result; |
| } |
| |
| |
| protected boolean isWhitespaceNode(Node node) |
| { |
| return node.getNodeType() == Node.TEXT_NODE && |
| node.getNodeValue().trim().length() == 0; |
| } |
| |
| |
| protected 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; |
| } |
| |
| |
| protected boolean isActionContainingRange(ModelQueryAction action, int startIndex, int endIndex) |
| { |
| int actionStartIndex = action.getStartIndex(); |
| int actionEndIndex = action.getEndIndex(); |
| |
| return (actionStartIndex <= startIndex && |
| actionEndIndex >= endIndex); |
| } |
| |
| |
| protected boolean isActionMatchingRange(ModelQueryAction action, int startIndex, int endIndex) |
| { |
| int actionStartIndex = action.getStartIndex(); |
| int actionEndIndex = action.getEndIndex(); |
| return (actionStartIndex == startIndex && |
| actionEndIndex == endIndex); |
| } |
| |
| |
| protected void removeActionsNotContainingRange(List actionList, int startIndex, int endIndex) |
| { |
| for (int i = actionList.size() - 1; i >= 0; i--) |
| { |
| ModelQueryAction action = (ModelQueryAction)actionList.get(i); |
| if (!isActionContainingRange(action, startIndex, endIndex)) |
| { |
| actionList.remove(i); |
| } |
| } |
| } |
| |
| |
| protected void removeActionsNotMatchingRange(List actionList, int startIndex, int endIndex) |
| { |
| for (int i = actionList.size() - 1; i >= 0; i--) |
| { |
| ModelQueryAction action = (ModelQueryAction)actionList.get(i); |
| if (!isActionMatchingRange(action, startIndex, endIndex)) |
| { |
| actionList.remove(i); |
| } |
| } |
| } |
| |
| |
| public static class MatchModelVisitor |
| { |
| int indent; |
| int elementIndex; |
| Node parent; |
| List actionList; |
| |
| public MatchModelVisitor(Node parent, List actionList) |
| { |
| this.parent = parent; |
| this.actionList = actionList; |
| } |
| |
| public int indexOfNextElement(int start) |
| { |
| NodeList nodeList = parent.getChildNodes(); |
| int length = nodeList.getLength(); |
| int result = length; |
| for (int i = start; i < length; i++) |
| { |
| Node node = nodeList.item(i); |
| if (node.getNodeType() == Node.ELEMENT_NODE) |
| { |
| result = i; |
| break; |
| } |
| } |
| return result; |
| } |
| |
| public void visitMatchModelNode(CMValidator.MatchModelNode matchModelNode) |
| { |
| int startIndex = indexOfNextElement(elementIndex); |
| |
| //String cmNodeName = matchModelNode.cmNode != null ? matchModelNode.cmNode.getNodeName() : "null"; |
| //printIndented(indent, "+MatchModelNode : " + cmNodeName + " " + startIndex); |
| |
| indent += 2; |
| for (Iterator iterator = matchModelNode.children.iterator(); iterator.hasNext(); ) |
| { |
| CMValidator.MatchModelNode child = (CMValidator.MatchModelNode)iterator.next(); |
| visitMatchModelNode(child); |
| } |
| indent -= 2; |
| |
| if (matchModelNode.cmNode != null) |
| { |
| int nodeType = matchModelNode.cmNode.getNodeType(); |
| if (nodeType == CMNode.GROUP) |
| { |
| CMGroup group = (CMGroup)matchModelNode.cmNode; |
| if (group.getOperator() == CMGroup.CHOICE) |
| { |
| addReplaceActions(matchModelNode, group, startIndex, elementIndex - 1); |
| } |
| } |
| else if (nodeType == CMNode.ELEMENT_DECLARATION) |
| { |
| elementIndex = startIndex + 1; |
| } |
| //printIndented(indent, "-MatchModelNode : " + cmNodeName + " " + (elementIndex - 1)); |
| } |
| } |
| |
| public void addReplaceActions(CMValidator.MatchModelNode matchModelNode, CMGroup group, int startIndex, int endIndex) |
| { |
| CMNode excludeCMNode = null; |
| if (matchModelNode.children.size() > 0) |
| { |
| CMValidator.MatchModelNode child = (CMValidator.MatchModelNode)matchModelNode.children.get(0); |
| excludeCMNode = child.cmNode; |
| } |
| |
| CMNodeList nodeList = group.getChildNodes(); |
| int size = nodeList.getLength(); |
| for (int i = 0; i < size; i++) |
| { |
| CMNode alternative = nodeList.item(i); |
| if (alternative != excludeCMNode) |
| { |
| Action action = new Action(ModelQueryAction.REPLACE, parent, alternative, startIndex, endIndex); |
| actionList.add(action); |
| } |
| } |
| } |
| } |
| |
| //public static void printIndented(int indent, String string) |
| //{ |
| // for (int i = 0; i < indent; i++) |
| // { |
| // System.out.print(" "); |
| // } |
| // System.out.println(string); |
| //} |
| } |