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