| /******************************************************************************* |
| * Copyright (c) 2001, 2011 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.dtd.ui.views.contentoutline; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.jface.viewers.AbstractTreeViewer; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.StructuredViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.wst.dtd.core.internal.Attribute; |
| import org.eclipse.wst.dtd.core.internal.AttributeList; |
| import org.eclipse.wst.dtd.core.internal.Comment; |
| import org.eclipse.wst.dtd.core.internal.DTDFile; |
| import org.eclipse.wst.dtd.core.internal.DTDNode; |
| import org.eclipse.wst.dtd.core.internal.Element; |
| import org.eclipse.wst.dtd.core.internal.Entity; |
| import org.eclipse.wst.dtd.core.internal.NodeList; |
| import org.eclipse.wst.dtd.core.internal.Notation; |
| import org.eclipse.wst.dtd.core.internal.ParameterEntityReference; |
| import org.eclipse.wst.dtd.core.internal.document.DTDModelImpl; |
| import org.eclipse.wst.dtd.core.internal.event.IDTDFileListener; |
| import org.eclipse.wst.dtd.core.internal.event.NodesEvent; |
| |
| class DTDTreeContentProvider implements ITreeContentProvider, IDTDFileListener { |
| |
| private Object fInputObject; |
| protected Viewer fViewer; |
| |
| // A cached set of IndexedNodeLists, required for getParent to return the |
| // correct instances mapping to TreeItems |
| protected Object[] logicalNodeLists = null; |
| |
| private boolean showLogicalOrder = false; |
| |
| public DTDTreeContentProvider() { |
| super(); |
| } |
| |
| public void dispose() { |
| fViewer = null; |
| } |
| |
| private void expandToNode(DTDNode node) { |
| DTDFile dtdFile = node.getDTDFile(); |
| // first expand the root |
| AbstractTreeViewer viewer = (AbstractTreeViewer) fViewer; |
| viewer.expandToLevel(dtdFile, 1); |
| NodeList listToExpand = null; |
| if (node instanceof Element || node instanceof ParameterEntityReference) { |
| listToExpand = dtdFile.getElementsAndParameterEntityReferences(); |
| } |
| else if (node instanceof Notation) { |
| listToExpand = dtdFile.getNotations(); |
| } |
| else if (node instanceof Entity) { |
| listToExpand = dtdFile.getEntities(); |
| } |
| else if (node instanceof Comment) { |
| listToExpand = dtdFile.getComments(); |
| } |
| if (listToExpand != null) { |
| viewer.expandToLevel(listToExpand, 1); |
| } |
| viewer.expandToLevel(node, 0); |
| } |
| |
| public Object[] getChildren(Object parentElement) { |
| // return the lists of nodes when in logical order mode, all the Nodes |
| // otherwise |
| if (parentElement instanceof DTDFile) { |
| if (isShowLogicalOrder()) { |
| // return the visible node lists |
| if (logicalNodeLists == null) { |
| Iterator nodeLists = ((DTDFile) parentElement).getNodeLists().iterator(); |
| List visibleLists = new ArrayList(7); |
| while (nodeLists.hasNext()) { |
| NodeList list = (NodeList) nodeLists.next(); |
| if (isVisibleNodeList(list)) { |
| visibleLists.add(list); |
| } |
| } |
| logicalNodeLists = visibleLists.toArray(); |
| } |
| return logicalNodeLists; |
| } |
| else { |
| // return the visible nodes |
| List allNodes = ((DTDFile) parentElement).getNodes(); |
| List visibleNodes = new ArrayList(allNodes.size()); |
| for (int i = 0; i < allNodes.size(); i++) { |
| Object o = allNodes.get(i); |
| if (isVisibleNode(o)) { |
| visibleNodes.add(o); |
| } |
| } |
| return visibleNodes.toArray(); |
| } |
| } |
| else if (parentElement instanceof NodeList) { |
| return ((NodeList) parentElement).getNodes().toArray(); |
| } |
| else if (parentElement instanceof Element) { |
| // always group the attributes directly under the element |
| Object[] children = ((DTDNode) parentElement).getChildren(); |
| List attributes = ((Element) parentElement).getElementAttributes(); |
| Object[] logicalChildren = new Object[children.length + attributes.size()]; |
| int index = 0; |
| for (index = 0; index < children.length; index++) { |
| logicalChildren[index] = children[index]; |
| } |
| for (Iterator iter = attributes.iterator(); iter.hasNext();) { |
| logicalChildren[index++] = iter.next(); |
| } |
| return logicalChildren; |
| } |
| else if (parentElement instanceof DTDNode) { |
| return ((DTDNode) parentElement).getChildren(); |
| } |
| return Collections.EMPTY_LIST.toArray(); |
| } |
| |
| public Object[] getElements(java.lang.Object inputElement) { |
| Object[] elements = null; |
| // Always show the DTDFile "node" |
| if (inputElement instanceof DTDModelImpl) { |
| elements = new Object[]{((DTDModelImpl) inputElement).getDTDFile()}; |
| } |
| if (elements == null) { |
| elements = new Object[0]; |
| } |
| return elements; |
| } |
| |
| public Object getParent(Object element) { |
| List parents = getParents(element); |
| Object parent = null; |
| if(parents.size() > 0) { |
| parent = parents.get(0); |
| } |
| return parent; |
| } |
| |
| public boolean hasChildren(Object element) { |
| Object[] children = getChildren(element); |
| return children.length > 0; |
| } |
| |
| public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { |
| fViewer = viewer; |
| |
| if (fInputObject instanceof DTDModelImpl) { |
| ((DTDModelImpl) fInputObject).getDTDFile().removeDTDFileListener(this); |
| } |
| fInputObject = newInput; |
| if (fInputObject instanceof DTDModelImpl) { |
| ((DTDModelImpl) fInputObject).getDTDFile().addDTDFileListener(this); |
| } |
| } |
| |
| /** |
| * Get the value of showLogicalOrder. |
| * |
| * @return value of showLogicalOrder. |
| */ |
| public boolean isShowLogicalOrder() { |
| return showLogicalOrder; |
| } |
| |
| private boolean isVisibleNode(Object o) { |
| if (o instanceof AttributeList) { |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean isVisibleNodeList(NodeList nodeList) { |
| /* |
| * All NodesLists should be visible because you can momentarily have |
| * an ATTLIST (for example) without a corresponding ELEMENT |
| * declaration |
| */ |
| return true;// !nodeList.getListType().equals(DTDRegionTypes.ATTLIST_TAG); |
| } |
| |
| /** |
| * <p>If a node changed then refresh the tree for that node</p> |
| * |
| * @see org.eclipse.wst.dtd.core.internal.event.IDTDFileListener#nodeChanged(org.eclipse.wst.dtd.core.internal.DTDNode) |
| */ |
| public void nodeChanged(final DTDNode node) { |
| refreshTree(node); |
| } |
| |
| public void nodesAdded(NodesEvent event) { |
| if (fViewer instanceof AbstractTreeViewer) { |
| StructuredViewer viewer = (StructuredViewer) fViewer; |
| ISelection selection = viewer.getSelection(); |
| |
| Object firstObj = (!selection.isEmpty() && selection instanceof IStructuredSelection) ? ((IStructuredSelection) selection).getFirstElement() : null; |
| DTDNode oldSelectedNode = null; |
| if (firstObj instanceof DTDNode) { |
| oldSelectedNode = (DTDNode) firstObj; |
| } |
| |
| //update the tree |
| refreshTree(event); |
| |
| final List nodes = event.getNodes(); |
| if (!nodes.isEmpty()) { |
| final DTDNode node = (DTDNode) nodes.get(0); |
| if (oldSelectedNode == null || node.getStructuredDTDDocumentRegion() != oldSelectedNode.getStructuredDTDDocumentRegion() || node.getStartOffset() != oldSelectedNode.getStartOffset() || node.getEndOffset() != oldSelectedNode.getEndOffset()) { |
| expandToNode(node); |
| viewer.setSelection(new StructuredSelection(node)); |
| } |
| } |
| } |
| } |
| |
| public void nodesRemoved(NodesEvent event) { |
| if (fViewer instanceof AbstractTreeViewer) { |
| AbstractTreeViewer abstractTreeViewer = (AbstractTreeViewer) fViewer; |
| for (Iterator iter = event.getNodes().iterator(); iter.hasNext();) { |
| abstractTreeViewer.remove(iter.next()); |
| } |
| } |
| |
| //update the tree |
| refreshTree(event); |
| } |
| |
| /** |
| * Set the value of showLogicalOrder. |
| * |
| * @param value |
| * Value to assign to showLogicalOrder. |
| */ |
| public void setShowLogicalOrder(boolean value) { |
| this.showLogicalOrder = value; |
| if (!value) { |
| // if not using logical order, lose the cached lists |
| logicalNodeLists = null; |
| } |
| } |
| |
| /** |
| * <p>Used to update the tree after a node event such as a node added or removed.</p> |
| * @param event the {@link NodesEvent} that caused the tree to need updating |
| */ |
| private void refreshTree(NodesEvent event) { |
| for (Iterator it = event.getNodes().iterator(); it.hasNext();) { |
| Object node = it.next(); |
| this.refreshTree(node); |
| |
| } |
| } |
| |
| /** |
| * <p>Refreshes the tree from the parents of the given node.</p> |
| * @param node refresh the tree from the parents of this node |
| */ |
| private void refreshTree(Object node) { |
| List parents = getParents(node); |
| if(parents.size() > 0) { |
| for(int p = 0; p < parents.size(); ++p) { |
| final Object parent = parents.get(p); |
| |
| // Bug 111100 - If it is a top level node (ie. parent is a |
| // DTDFile), |
| // insert the node directly to improve performance |
| if (parent instanceof DTDFile) { |
| Object[] objs = getChildren(parent); |
| for (int i = 0; i < objs.length; i++) { |
| if (objs[i] == node) { |
| ((AbstractTreeViewer) fViewer).insert(parent, node, i); |
| break; |
| } |
| } |
| } |
| |
| this.refreshTreeNode(parent, true); |
| } |
| } |
| } |
| |
| /** |
| * @param element get the tree parents of this element |
| * @return {@link List} of parents of the given element |
| */ |
| private List getParents(Object element) { |
| List parents = new ArrayList(); |
| |
| Object parent = null; |
| if (element instanceof DTDNode) { |
| DTDNode node = (DTDNode) element; |
| if (element instanceof Attribute) { |
| parent = node.getParentNode(); |
| if (parent != null && parent instanceof AttributeList) { |
| parents.addAll(getElementParentsOfAttributeList((AttributeList)parent)); |
| } |
| } else if(element instanceof AttributeList) { |
| parents.addAll(getElementParentsOfAttributeList((AttributeList)element)); |
| } |
| |
| // if showing in the logical order, return the IndexedNodeList |
| // acting as a parent in the tree |
| if (isShowLogicalOrder()) { |
| Object[] indexedNodeLists = getChildren(((DTDModelImpl) fInputObject).getDTDFile()); |
| for (int i = 0; i < indexedNodeLists.length && parent == null; i++) { |
| if (indexedNodeLists[i] instanceof NodeList) { |
| if (((NodeList) indexedNodeLists[i]).getNodes().contains(element)) { |
| parents.add(indexedNodeLists[i]); |
| } |
| } |
| } |
| } |
| |
| //try and get the simple parent |
| parent = ((DTDNode) element).getParentNode(); |
| if(parent != null) { |
| parents.add(parent); |
| } |
| |
| //if no parents found must be new nodes so refresh from root |
| if(parents.size() == 0) { |
| parents.add(((DTDModelImpl) fInputObject).getDTDFile()); |
| } |
| }else if (element instanceof NodeList && fInputObject instanceof DTDModelImpl) { |
| parents.add(((DTDModelImpl) fInputObject).getDTDFile()); |
| } |
| |
| return parents; |
| } |
| |
| /** |
| * @param attList get the element parents of the given {@link AttributeList} |
| * @return the element parents of the given {@link AttributeList}, if no parents |
| * can be found the list contains the DTD file element |
| */ |
| private List getElementParentsOfAttributeList(AttributeList attList) { |
| List parents = new ArrayList(); |
| Iterator iterAttList = attList.getDTDFile().getNodes().iterator(); |
| while (iterAttList.hasNext()) { |
| DTDNode currentNode = (DTDNode) iterAttList.next(); |
| if (currentNode instanceof Element && |
| currentNode.getName().equals(attList.getName())) { |
| |
| parents.add(currentNode); |
| } |
| } |
| |
| if(parents.size() == 0) { |
| parents.add(((DTDModelImpl) fInputObject).getDTDFile()); |
| } |
| |
| return parents; |
| } |
| |
| /** |
| * <p>Executes a refresh on the {@link AbstractTreeViewer} for the given |
| * node</p> |
| * |
| * @param node refresh the tree for this node |
| * @param updateLabels <code>true</code> to update the labels on the tree, |
| * <code>false</code> otherwise. |
| */ |
| private void refreshTreeNode(final Object node, final boolean updateLabels) { |
| fViewer.getControl().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| if (!fViewer.getControl().isDisposed()) { |
| if(node != null) { |
| ((AbstractTreeViewer) fViewer).refresh(node, updateLabels); |
| } else { |
| ((AbstractTreeViewer) fViewer).refresh(updateLabels); |
| } |
| } |
| } |
| }); |
| } |
| } |