/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.mapping.ui.editor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.designer.diagram.ui.DiagramUiConstants;
import org.teiid.designer.mapping.factory.ITreeToRelationalMapper;
import org.teiid.designer.mapping.factory.MappingClassFactory;
import org.teiid.designer.mapping.factory.ModelMapperFactory;
import org.teiid.designer.mapping.factory.TreeMappingAdapter;
import org.teiid.designer.mapping.ui.PluginConstants;
import org.teiid.designer.mapping.ui.UiConstants;
import org.teiid.designer.mapping.ui.util.MappingUiUtil;
import org.teiid.designer.metamodels.transformation.MappingClass;
import org.teiid.designer.metamodels.transformation.MappingClassColumn;
import org.teiid.designer.metamodels.transformation.StagingTable;
import org.teiid.designer.metamodels.xml.XmlContainerNode;
import org.teiid.designer.ui.UiPlugin;
import org.teiid.designer.ui.actions.ModelerActionService;
import org.teiid.designer.ui.common.actions.ActionService;
/**
* The TreeViewer for the DocumentTreeEditor. It is the JFace version of DocumentJTree from Modeler 3.0
*
* @since 8.0
*/
public final class DocumentTreeViewer extends TreeViewer implements UiConstants {
private static final Color TEMP_TABLE_BACKGROUND = DiagramUiConstants.Colors.TEMP_GROUP_HEADER;
private static final Color CLEAR_BACKGROUND = ColorConstants.white;
private static final Color LOCATION_BACKGROUND = DiagramUiConstants.Colors.VIRTUAL_GROUP_HEADER;
private static final Color MAPPING_BACKGROUND = DiagramUiConstants.Colors.VIRTUAL_GROUP_BKGRND;
/** actions allowing user to directly expandAll or collapseAll the document tree */
private IAction collapseAllAction;
private IAction expandAllAction;
private MappingAdapterFilter mappingFilter;
private TreeMappingAdapter mappingAdapter;
private MappingClassFactory mcfFactory;
private int mappingType = PluginConstants.COARSE_MAPPING;
private EObject target;
private Collection selectedNodeList;
public DocumentTreeViewer( Composite parent ) {
super(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
createContextMenu();
}
private void createContextMenu() {
MenuManager menuMgr = new MenuManager();
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow( IMenuManager theMenuMgr ) {
fillContextMenu(theMenuMgr);
}
});
Tree tree = getTree();
tree.setMenu(menuMgr.createContextMenu(tree));
expandAllAction = new Action() {
@Override
public void run() {
myExpandAll();
}
};
expandAllAction.setToolTipText(UiConstants.Util.getString("DocumentTreeViewer.expandAllAction.tooltip")); //$NON-NLS-1$
expandAllAction.setText(UiConstants.Util.getString("DocumentTreeViewer.expandAllAction.text")); //$NON-NLS-1$
expandAllAction.setEnabled(true);
collapseAllAction = new Action() {
@Override
public void run() {
myCollapseAll();
}
};
collapseAllAction.setToolTipText(UiConstants.Util.getString("DocumentTreeViewer.collapseAllAction.tooltip")); //$NON-NLS-1$
collapseAllAction.setText(UiConstants.Util.getString("DocumentTreeViewer.collapseAllAction.text")); //$NON-NLS-1$
collapseAllAction.setEnabled(true);
}
void myExpandAll() {
// jh Defect 19925: Performance enhancement, turn off drawing during expandAll
getControl().setRedraw(false);
expandAll();
getControl().setRedraw(true);
// Let's make sure teh TreeExpansionMOnitor is updated...
getMappingAdapterFilter().getTreeExpansionMonitor().handleAllExpanded();
TreeExpansionEvent tee = new TreeExpansionEvent(getThis(), getTree().getItems()[0].getData());
fireTreeExpanded(tee);
// Reveal the first item in tree so it doesn't scroll to bottom
reveal(getTree().getItems()[0].getData());
}
void myCollapseAll() {
collapseAll();
// Let's make sure teh TreeExpansionMOnitor is updated...
getMappingAdapterFilter().getTreeExpansionMonitor().handleAllCollapsed();
fireTreeCollapsed(new TreeExpansionEvent(getThis(), getTree().getItems()[0].getData()));
}
private void createMappingFilter() {
// System.out.println("[DocumentTreeViewer.createMappingFilter]: Creating New Mapping Filter");
// jh Lyra enh: ISSUE: can the filter persist? Must it be recreated on each refresh???
if (mappingFilter != null) mappingFilter.dispose();
mappingFilter = null;
if (target != null) {
try {
mappingFilter = new MappingAdapterFilter(target, this, mappingAdapter);
// Let's cache the TreeMappingAdapter because it exists INDEPENDENT of the filter and does NOT change
mappingAdapter = mappingFilter.getMappingAdapter();
// jh Lyra enh: update the MappingAdapterFilter's idea of tree selection
mappingFilter.setSelectedNodes((List)selectedNodeList);
// jh Defect 21277: Locating MappingClassFactory here to keep it in sync with
// MappingAdapterFilter/TreeMappingAdapter. Create a new one
// whenever the mapping filter is recreated.
createMappingClassFactory(target);
} catch (Exception e) {
Util.log(e);
}
} else {
CoreArgCheck.isNotNull(target);
}
}
// jh Defect 21277: Locating MappingClassFactory here to keep it in sync with
// MappingAdapterFilter/TreeMappingAdapter.
public void createMappingClassFactory( EObject target ) {
ITreeToRelationalMapper ittrm = ModelMapperFactory.createModelMapper(target);
mcfFactory = new MappingClassFactory(ittrm, MappingUiUtil.getCurrentTreeMappingAdapter());
}
public MappingClassFactory getMappingClassFactory() {
return mcfFactory;
}
void fillContextMenu( IMenuManager theMenuMgr ) {
UiPlugin plugin = UiPlugin.getDefault();
ActionService actionService = plugin.getActionService(plugin.getCurrentWorkbenchWindow().getActivePage());
((ModelerActionService)actionService).contributeToContextMenu(theMenuMgr, null, getSelection());
theMenuMgr.add(collapseAllAction);
theMenuMgr.add(expandAllAction);
}
public void setXmlMappingTarget( EObject theTarget ) {
// see if target changed
if (target == null || target != theTarget) {
mappingAdapter = null;
}
target = theTarget;
if (target != null) {
/*
* jh Lyra ehn: perhaps this SHOULD be true, as it is only done once
*/
getMappingAdapterFilter(true);
}
}
public MappingAdapterFilter getMappingAdapterFilter() {
return getMappingAdapterFilter(false);
}
public MappingAdapterFilter getMappingAdapterFilter( boolean theRefreshFlag ) {
if (theRefreshFlag || mappingFilter == null) {
createMappingFilter();
}
return mappingFilter;
}
/**
* Highlight the appropriate mappingFilter locations for the specified collection of MappingClasses.
*/
public void setSelectedMapping( Collection mappingClasses ) {
ArrayList locations = new ArrayList();
ArrayList extent = new ArrayList();
for (Iterator iter = mappingClasses.iterator(); iter.hasNext();) {
Object nextObject = iter.next();
if (nextObject instanceof StagingTable) {
locations.add(mappingFilter.getLocation((StagingTable)nextObject));
} else if (nextObject instanceof MappingClass) {
locations.addAll(mappingFilter.getLocations((MappingClass)nextObject));
extent.addAll(mappingFilter.getCoarseMappingExtentNodes((MappingClass)nextObject));
} else if (nextObject instanceof MappingClassColumn) {
// Defect 22776 - were wrongly adding locations to the Extent list. needed to be the locations list
locations.addAll(mappingFilter.getLocations((MappingClassColumn)nextObject));
}
// remove all the locations out of the extent
extent.removeAll(locations);
}
if (locations.size() > 0) reveal(locations.get(0));
paintBackground(Collections.EMPTY_LIST, locations, extent);
}
/**
* Highlight the appropriate objects in the tree if they exist?.
*/
public void setSelectedNodes( Collection nodeList ) {
// System.out.println( "[DocumentTreeViewer.setSelectedNodes] TOP" );
selectedNodeList = nodeList;
// jh Lyra enh: update the MappingAdapterFilter's idea of tree selection
getMappingAdapterFilter().setSelectedNodes((List)nodeList);
ArrayList locations = new ArrayList();
for (Iterator iter = nodeList.iterator(); iter.hasNext();) {
Object nextObject = iter.next();
locations.add(nextObject);
}
boolean singleSelection = locations.size() == 1;
if (singleSelection) {
reveal(locations.get(0));
}
paintBackground(Collections.EMPTY_LIST, Collections.EMPTY_LIST, locations);
}
private void paintBackground( Collection tempTableLocations,
Collection mappingClassLocations,
Collection mappedNodes ) {
ArrayList nodes = new ArrayList();
buildTreeItemList(getTree().getItems(), nodes);
for (Iterator iter = nodes.iterator(); iter.hasNext();) {
TreeItem item = (TreeItem)iter.next();
Object o = item.getData();
if (mappingClassLocations.contains(o) && shouldHighlight(o)) {
highlightMappingClassLocation(item);
} else if (mappedNodes.contains(o) && shouldHighlight(o)) {
highlightAttributeLocation(item);
} else {
clearHighlight(item, tempTableLocations.contains(o));
}
}
}
private boolean shouldHighlight( Object oItem ) {
boolean bResult = false;
if (oItem instanceof EObject) {
EObject eo = (EObject)oItem;
// System.out.println("[DocumentTreeViewer.shouldHighlight] eo is: " + eo.toString() );
// one more rule, the supercedes the others:
MappingClass mc = mappingFilter.getMappingAdapter().getMappingClass(eo);
// we'll find a mapping class ONLY if this treenode is the mapping root element
if (mc != null) {
// System.out.println("[DocumentTreeViewer.shouldHighlight] eo is MC Root " );
bResult = true;
}
// jh Lyra enh: TODO do the same for Staging Tables...
StagingTable st = mappingFilter.getMappingAdapter().getStagingTableForRootTreeNode(eo);
// we'll find a staging table ONLY if this treenode is mapped to a staging table
// (a staging table can only be mapped to one element)
if (st != null) {
// System.out.println("[DocumentTreeViewer.shouldHighlight] eo is ST Root " );
bResult = true;
}
// if we have not found a reason to highlight yet, do the final tests
if (!bResult) {
// if this treenode is the mc's root, highlight it
if (eo instanceof XmlContainerNode) {
// System.out.println("[DocumentTreeViewer.shouldHighlight] eo failing because container, root, or has children "
// );
bResult = false;
} else {
// System.out.println("[DocumentTreeViewer.shouldHighlight] eo has passed all tests " );
bResult = true;
}
}
}
// System.out.println("[DocumentTreeViewer.shouldHighlight] BOT; about to return: " + bResult );
return bResult;
}
public void showNoneSelected() {
clearAllHilites();
setSelection(Collections.EMPTY_LIST);
}
public void clearAllHilites() {
ArrayList nodes = new ArrayList();
if (!getTree().isDisposed()) {
buildTreeItemList(getTree().getItems(), nodes);
for (Iterator iter = nodes.iterator(); iter.hasNext();) {
TreeItem item = (TreeItem)iter.next();
clearHighlight(item, false);
}
}
}
private void buildTreeItemList( TreeItem[] level,
List result ) {
for (int i = 0; i < level.length; ++i) {
result.add(level[i]);
TreeItem[] children = level[i].getItems();
if (children != null && children.length > 0) {
buildTreeItemList(children, result);
}
}
}
private void highlightMappingClassLocation( TreeItem row ) {
row.setBackground(LOCATION_BACKGROUND);
}
private void highlightAttributeLocation( TreeItem row ) {
row.setBackground(MAPPING_BACKGROUND);
}
private void clearHighlight( TreeItem row,
boolean hasTempTable ) {
if (hasTempTable) {
row.setBackground(TEMP_TABLE_BACKGROUND);
} else {
row.setBackground(CLEAR_BACKGROUND);
}
}
/**
* @return
*/
public int getMappingType() {
return mappingType;
}
/*
* Method required to supply this inside run() methods to wire up collapseAll and expandAll actions.
*/
private AbstractTreeViewer getThis() {
return this;
}
/**
* @param i
*/
public void setMappingType( int i ) {
mappingType = i;
}
/**
* @see org.eclipse.jface.viewers.StructuredViewer#handleLabelProviderChanged(org.eclipse.jface.viewers.LabelProviderChangedEvent)
* @since 5.0
*/
@Override
protected void handleLabelProviderChanged( LabelProviderChangedEvent theEvent ) {
// System.out.println("\n\n\n[DocumentTreeViewer.handleLabelProviderChanged] event: " + theEvent );
refresh();
}
}