/*
* 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.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.TreeItem;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.mapping.factory.IMappableTree;
import org.teiid.designer.mapping.factory.ITreeToRelationalMapper;
import org.teiid.designer.mapping.factory.ModelMapperFactory;
import org.teiid.designer.mapping.factory.TreeMappingAdapter;
import org.teiid.designer.mapping.ui.diagram.MappingDiagramUtil;
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.transformation.impl.MappingClassImpl;
import org.teiid.designer.metamodels.xml.XmlAttribute;
import org.teiid.designer.metamodels.xml.XmlContainerNode;
import org.teiid.designer.metamodels.xml.XmlElement;
import org.teiid.designer.metamodels.xml.XmlRoot;
import org.teiid.designer.ui.common.tree.TreeExpansionMonitor;
import org.teiid.designer.ui.common.tree.TreeNodeMap;
/**
* MappingAdapterFilter is a specialization for working on an XmlMapping through a TreeViewer, such that the only objects found
* are those that are visible in the tree.
*
* @since 8.0
*/
public class MappingAdapterFilter {
private int rowHeight;
private TreeViewer treeViewer;
private TreeExpansionMonitor treeExpansionMonitor;
private TreeMappingAdapter mappingAdapter;
private ITreeToRelationalMapper mapper;
private List lstNodes = Collections.EMPTY_LIST;
/**
* Construct an instance of MappingAdapterFilter.
*/
public MappingAdapterFilter( EObject target,
TreeViewer treeViewer,
TreeMappingAdapter theMappingAdapter ) {
this.treeViewer = treeViewer;
rowHeight = treeViewer.getTree().getItemHeight();
treeExpansionMonitor = new TreeExpansionMonitor(treeViewer);
this.mappingAdapter = theMappingAdapter;
if (mappingAdapter == null) {
this.mappingAdapter = new TreeMappingAdapter(target);
}
mapper = ModelMapperFactory.createModelMapper(target);
CoreArgCheck.isNotNull(mapper);
}
public void setTreeExpansionMonitorStale() {
treeExpansionMonitor.setIsStale(true);
}
/**
* Obtain the TreeMappingAdapter that this object is using.
*
* @return
*/
public TreeMappingAdapter getMappingAdapter() {
return mappingAdapter;
}
/**
* Obtain the IMappableTree that this object is using.
*
* @return
*/
public IMappableTree getMappableTree() {
return mapper.getMappableTree();
}
public ITreeToRelationalMapper getMapper() {
return mapper;
}
private MappingDiagramBehavior getBehavior() {
return MappingDiagramUtil.getCurrentMappingDiagramBehavior();
}
/**
* Generate an ordered list of coarse MappingExtent instances for the specified MappingClass.
*
* @param theMappingClass
* @return
*/
public List getCoarseMappingExtents( IProgressMonitor monitor ) {
boolean bPopulateDiagramFromTreeSelection = getBehavior().getPopulateDiagramFromTreeSelectionState();
// System.out.println("[MappingAdapterFilter.getCoarseMappingExtents] Retrieved bPopulateDiagramFromTreeSelection: " +
// bPopulateDiagramFromTreeSelection );
if (bPopulateDiagramFromTreeSelection && getSelectedNodes() != null && getSelectedNodes().size() > 0
&& getMappingClassesImpliedBySelection().size() > 0) {
return getCoarseMappingExtentsInSelectionDrivenStyle(monitor, getSelectedNodes());
}
// default to the normal, full diagram
return getCoarseMappingExtentsForFullDiagram(monitor);
}
/**
* Generate an ordered list of coarse MappingExtent instances for the specified MappingClass.
*
* @param theMappingClass
* @return
*/
public List getCoarseMappingExtentsInSelectionDrivenStyle( IProgressMonitor monitor,
List lstSelectedNodes ) {
// System.out.println("[MappingAdapterFilter.getCoarseMappingExtentsInSelectionDrivenStyle] TOP");
boolean showProgress = (monitor != null);
int rowHeight = this.getTreeViewer().getTree().getItemHeight();
List totalExtentList = new ArrayList();
if (showProgress) {
monitor.subTask("Getting mapping classes from XmlFilter"); //$NON-NLS-1$
}
// get the visible mapping classes
List visibleMappingClasses = this.getMappedClassifiersForSelectionDrivenDiagram();
/*
* jh: old version is driven by MCs; this new one will be driven by walking
* the visible nodes of the tree.
*/
List visibleTreeNodes = this.getTreeExpansionMonitor().getVisibleTreeItems();
TreeItem nextTreeItem = null;
Iterator iter = visibleTreeNodes.iterator();
int iTreeNodesCnt = visibleTreeNodes.size();
int iTreeNodes = 0;
String pMessage = null;
MappingClass mcCurrentMappingClass = null;
MappingExtent meCurrentExtent = null;
/*
* Special requirements for this method:
* 1. As we process nodes, build up a collection of Mapping Classes that the
* tree selection will cause us to display in this partial diagram...
* Or better yet, do it in advance.
*/
HashMap hmMappingClassesToDisplay = getMappingClassesImpliedBySelection();
while (iter.hasNext()) {
if (showProgress) {
if (iTreeNodes % 10 == 0) {
pMessage = "Getting extents for tree location " + iTreeNodes + " of " + iTreeNodesCnt; //$NON-NLS-1$ //$NON-NLS-2$
monitor.subTask(pMessage);
}
}
nextTreeItem = (TreeItem)iter.next();
// get EObject for this TreeItem, so we'll have both
EObject eoNext = (EObject)nextTreeItem.getData();
// ==========================================
// case: XmlContainer
// ==========================================
if (eoNext instanceof XmlContainerNode || eoNext instanceof XmlRoot || isUnexpandedElementWithChildren(eoNext, this)) {
/*
* One more jh Lyra enh: If user selects a Mapping Class Root, even if it is a container that is
* not really a Mapping Class column, we MUST include that Mapping Class
* in the diagram, and exclude others.
* 2. Ok, since the special selection-driven version of the diagram will
* NOT be showing SummaryExtents (verify with John V.), then
* we should probably write a special version of THIS method
* (getCoarseMappingExtents()) dedicated to that situation...
*
* isNodeAMappingClassRoot( EObject eo )
*/
// Note: Container nodes get special decoration; see xxxLabelProvider.
// Note2: But do NOT do it if it is an element
boolean bExpanded = this.getTreeViewer().getExpandedState(eoNext);
if (bExpanded) {
// if Expanded, leave a gap
// null out the ongoing element extent, if any, because a gap should interrupt it
meCurrentExtent = null;
} else {
if (getSelectedNodes().contains(eoNext) && isNodeAMappingClassRoot(eoNext)) {
/*
* jhTODO Ok, it is selected AND it is a MappingClassRoot. What do we do with it????
*/
}
// get the collection of MCs that elements in this closed branch map to...
/*
* per the 'Barry Case', we now are going to report the number of columns in
* all of the hidden MCs, rather than the count of MCs.
*
* So the structure returned from this call should itself be a map whose
* key is the MC and whose value is the total column count that is hidden
* in this branch FOR THAT MC.
*
*/
HashMap hmapMappingClasses = this.getMappingClassesInBranch(eoNext);
// System.out.println("[MappingAdapterFilter.getCoarseMappingExtents] hmapMappingClasses has: " +
// hmapMappingClasses.size() );
if (!hmapMappingClasses.isEmpty()) {
EObject eoMappingRef = null; // ??
int iRow = iTreeNodes; // right?
/*
* jh Defect 22768 (22566): Special case...if Root is also a mapped element,
* use normal MappingExtent, otherwise use the SummaryExtent.
*/
MappingExtent extent = null;
if (eoNext instanceof XmlRoot) {
eoMappingRef = (MappingClassImpl)hmapMappingClasses.keySet().iterator().next();
extent = new MappingExtent(iRow * rowHeight, rowHeight, eoMappingRef, eoNext);
} else {
extent = new SummaryExtent(iRow * rowHeight, rowHeight, eoMappingRef, eoNext);
((SummaryExtent)extent).setMappingClasses(hmapMappingClasses);
boolean bSomeAreVisible = someMappingClassesAreVisible(hmapMappingClasses, visibleMappingClasses);
((SummaryExtent)extent).setSomeMappingClassesAreVisible(bSomeAreVisible);
}
// test this col to see if mapped
boolean bMapped = (getMappingAdapter().getMappingClassColumn(eoNext) != null);
// test this col to see if MUST be mapped
boolean bThisNodeIsRequired = isMappingRequiredForNode(eoNext);
if (bThisNodeIsRequired) {
// if required, set 'completely mapped' to the current mapped state
extent.setCompletelyMapped(bMapped);
extent.setMappingRequired(true);
} else {
// if not required, set 'completely mapped' to bMapped
// jh Defect 21988: was 'true', changing to bMapped:
extent.setCompletelyMapped(bMapped);
extent.setMappingRequired(false);
}
extent.setPathToDocumentRoot(this.getMapper().getPathInDocument(eoNext));
totalExtentList.add(extent);
// null out the ongoing element extent, if any, because a Summary Extent should interrupt it
meCurrentExtent = null;
}
}
} else
// ==========================================
// case: XmlElement OR XmlAttribute
// Note: we must test for this LAST, because everything is an XmlElement,
// including containers and roots.
// Perhaps we can discover a property in it that will tell us it
// that it cannot have children, and is 'elemental'
//
// Do XmlAttributes have any special requirements?
// ==========================================
if (eoNext instanceof XmlElement || eoNext instanceof XmlAttribute) {
/*
* if an extent is in progress for the same MC, add this row to it;
* otherwise, start a new extent
*/
MappingClass mc = this.getMappingAdapter().getMappingClassForTreeNode(eoNext);
// System.out.println("[MappingAdapterFilter.getCoarseMappingExtentsForFullDiagram] Matching TreeNode: " + eoNext
// + " to MC: " + mc );
// if we get an mc (Mapping Class) this col is capable of being mapped
if (mc != null) {
if (hmMappingClassesToDisplay.get(mc) != null) {
// test this col to see if mapped
boolean bMapped = (getMappingAdapter().getMappingClassColumn(eoNext) != null);
// test this col to see if MUST be mapped
boolean bThisNodeIsRequired = isMappingRequiredForNode(eoNext);
// see if this is a continuation of the current extent
if (meCurrentExtent != null && mc == mcCurrentMappingClass) {
// override the existing 'mapped' state only if this new col is unmapped AND required
if (!bMapped && bThisNodeIsRequired) {
meCurrentExtent.setCompletelyMapped(bMapped);
meCurrentExtent.setMappingRequired(true);
}
meCurrentExtent.increaseHeight(rowHeight);
} else {
// start a new extent
int iRow = iTreeNodes;
meCurrentExtent = new MappingExtent(iRow * rowHeight, rowHeight, mc, eoNext);
meCurrentExtent.setPathToDocumentRoot(this.getMapper().getPathInDocument(eoNext));
// 'completely mapped': Any extent containing a 'must-map but unmapped' column should
// be colored red, so setCompletelyMapped will be false in that case.
// All other cases should be orange, so set 'completely mapped' to true in all other cases
if (bThisNodeIsRequired) {
// if required, set 'completely mapped' to the current mapped state
meCurrentExtent.setCompletelyMapped(bMapped);
meCurrentExtent.setMappingRequired(true);
} else {
// if not required, set 'completely mapped' to true
meCurrentExtent.setCompletelyMapped(true);
meCurrentExtent.setMappingRequired(false);
}
totalExtentList.add(meCurrentExtent);
mcCurrentMappingClass = mc;
}
} else {
// This MC is not in the selected list, so close out the current mc extent
// null out the ongoing element extent, if any, because any non-mapped element
// should interrupt it and leave a gap
meCurrentExtent = null;
}
} else {
// null out the ongoing element extent, if any, because any non-mapped element
// should interrupt it and leave a gap
meCurrentExtent = null;
}
}
// ==========================================
// case: any node, mapped to a StagingTable
// ==========================================
// Last step for this node: handle a treenode mapped to a staging Table
StagingTable st = this.getMappingAdapter().getStagingTable(eoNext);
if (st != null) {
/*
* I am not sure how to generate the Staging Table extent, or where.
* Can a treenode be both a Staging Table anchor point, and the locus of
* an ordinary element bound Extent? Yes it can.
*
* First cut: Why not just use the existing process to create Staging table extents?
* We don't really have any new rules for Staging Tables...
*/
// System.out.println("[MappingDiagramModelFactory.getAllCoarseExtentsForLyra] This IS a Staging Table: " + st );
// jhTODO Experiment: Do not show Staging Tables at all in 'selection driven' diagram
// OR, only show Staging Tables that tie to cols that belong to the MC we are showing.
// totalExtentList.add( this.getExtent( st ) );
}
iTreeNodes++;
}
return totalExtentList;
}
private HashMap getMappingClassesImpliedBySelection() {
List lstSelectedTreeNodes = this.getSelectedNodes();
HashMap hmMappingClassesToDisplay = new HashMap();
Iterator itNodes = lstSelectedTreeNodes.iterator();
EObject eoTemp = null;
while (itNodes.hasNext()) {
eoTemp = (EObject)itNodes.next();
MappingClassColumn mccol = getMappingAdapter().getMappingClassColumn(eoTemp);
if (mccol != null) {
MappingClass mc = mccol.getMappingClass();
if (mc != null) {
hmMappingClassesToDisplay.put(mc, "x"); //$NON-NLS-1$
}
} else if (isNodeAMappingClassRoot(eoTemp)) {
MappingClass mc = getMappingAdapter().getMappingClass(eoTemp);
if (mc != null) {
hmMappingClassesToDisplay.put(mc, "x"); //$NON-NLS-1$
}
}
}
return hmMappingClassesToDisplay;
}
/**
* Generate an ordered list of coarse MappingExtent instances for the specified MappingClass.
*
* @param theMappingClass
* @return
*/
public List getCoarseMappingExtentsForFullDiagram( IProgressMonitor monitor ) {
// System.out.println("[MappingAdapterFilter.getCoarseMappingExtentsForFullDiagram] TOP");
boolean showProgress = (monitor != null);
int rowHeight = this.getTreeViewer().getTree().getItemHeight();
List totalExtentList = new ArrayList();
if (showProgress) {
monitor.subTask("Getting mapping classes from XmlFilter"); //$NON-NLS-1$
}
// get the visible mapping classes
List visibleMappingClasses = this.getMappedClassifiersForFullDiagram();
/*
* jh: old version is driven by MCs; this new one will be driven by walking
* the visible nodes of the tree.
*/
List visibleTreeNodes = this.getTreeExpansionMonitor().getVisibleTreeItems();
TreeItem nextTreeItem = null;
Iterator iter = visibleTreeNodes.iterator();
int iTreeNodesCnt = visibleTreeNodes.size();
int iTreeNodes = 0;
String pMessage = null;
MappingClass mcCurrentMappingClass = null;
MappingExtent meCurrentExtent = null;
while (iter.hasNext()) {
if (showProgress) {
if (iTreeNodes % 10 == 0) {
pMessage = "Getting extents for tree location " + iTreeNodes + " of " + iTreeNodesCnt; //$NON-NLS-1$ //$NON-NLS-2$
monitor.subTask(pMessage);
}
}
nextTreeItem = (TreeItem)iter.next();
// get EObject for this TreeItem, so we'll have both
EObject eoNext = (EObject)nextTreeItem.getData();
// ==========================================
// case: XmlContainer
// ==========================================
if (eoNext instanceof XmlContainerNode || eoNext instanceof XmlRoot || isUnexpandedElementWithChildren(eoNext, this)) {
// Note: Container nodes get special decoration; see xxxLabelProvider.
// Note2: But do NOT do it if it is an element
boolean bExpanded = this.getTreeViewer().getExpandedState(eoNext);
if (bExpanded) {
// if Expanded, leave a gap
// null out the ongoing element extent, if any, because a gap should interrupt it
meCurrentExtent = null;
} else {
// get the collection of MCs that elements in this closed branch map to...
/*
* per the 'Barry Case', we now are going to report the number of columns in
* all of the hidden MCs, rather than the count of MCs.
*
* So the structure returned from this call should itself be a map whose
* key is the MC and whose value is the total column count that is hidden
* in this branch FOR THAT MC.
*
*/
HashMap hmapMappingClasses = this.getMappingClassesInBranch(eoNext);
// System.out.println("[MappingAdapterFilter.getCoarseMappingExtents] hmapMappingClasses has: " +
// hmapMappingClasses.size() );
if (!hmapMappingClasses.isEmpty()) {
EObject eoMappingRef = null;
int iRow = iTreeNodes;
/*
* jh Defect 22768 (22566): Special case...if Root is also a mapped element,
* use normal MappingExtent, otherwise use the SummaryExtent.
*/
MappingExtent extent = null;
if (eoNext instanceof XmlRoot) {
Object nextKey = hmapMappingClasses.keySet().iterator().next();
if (nextKey instanceof MappingClassImpl) {
eoMappingRef = (MappingClassImpl)nextKey;
}
extent = new MappingExtent(iRow * rowHeight, rowHeight, eoMappingRef, eoNext);
} else {
extent = new SummaryExtent(iRow * rowHeight, rowHeight, eoMappingRef, eoNext);
((SummaryExtent)extent).setMappingClasses(hmapMappingClasses);
boolean bSomeAreVisible = someMappingClassesAreVisible(hmapMappingClasses, visibleMappingClasses);
((SummaryExtent)extent).setSomeMappingClassesAreVisible(bSomeAreVisible);
}
// test this col to see if mapped
boolean bMapped = (getMappingAdapter().getMappingClassColumn(eoNext) != null);
// test this col to see if MUST be mapped
boolean bThisNodeIsRequired = isMappingRequiredForNode(eoNext);
if (bThisNodeIsRequired) {
// if required, set 'completely mapped' to the current mapped state
extent.setCompletelyMapped(bMapped);
extent.setMappingRequired(true);
} else {
// if not required, set 'completely mapped' to bMapped
// jh Defect 21988: was 'true', changing to bMapped:
extent.setCompletelyMapped(bMapped);
extent.setMappingRequired(false);
}
extent.setPathToDocumentRoot(this.getMapper().getPathInDocument(eoNext));
totalExtentList.add(extent);
// null out the ongoing element extent, if any, because a Summary Extent should interrupt it
meCurrentExtent = null;
}
}
} else
// ==========================================
// case: XmlElement OR XmlAttribute
// Note: we must test for this LAST, because everything is an XmlElement,
// including containers and roots.
// Perhaps we can discover a property in it that will tell us it
// that it cannot have children, and is 'elemental'
//
// Do XmlAttributes have any special requirements?
// ==========================================
if (eoNext instanceof XmlElement || eoNext instanceof XmlAttribute) {
/*
* if an extent is in progress for the same MC, add this row to it;
* otherwise, start a new extent
* TODO: make similar changes in the 'SelectionDriven' get extents method...!
*/
MappingClass mc = this.getMappingAdapter().getMappingClassForTreeNode(eoNext);
// System.out.println("[MappingAdapterFilter.getCoarseMappingExtentsForFullDiagram] Matching TreeNode: " + eoNext
// + " to MC: " + mc );
// if we get an mc (Mapping Class) this col is capable of being mapped
if (mc != null) {
// test this col to see if mapped
boolean bMapped = (getMappingAdapter().getMappingClassColumn(eoNext) != null);
// test this col to see if MUST be mapped
boolean bThisNodeIsRequired = isMappingRequiredForNode(eoNext);
// see if this is a continuation of the current extent
if (meCurrentExtent != null && mc == mcCurrentMappingClass) {
// override the existing 'mapped' state only if this new col is unmapped AND required
if (!bMapped && bThisNodeIsRequired) {
meCurrentExtent.setCompletelyMapped(bMapped);
meCurrentExtent.setMappingRequired(true);
}
meCurrentExtent.increaseHeight(rowHeight);
} else {
// start a new extent
int iRow = iTreeNodes;
meCurrentExtent = new MappingExtent(iRow * rowHeight, rowHeight, mc, eoNext);
meCurrentExtent.setPathToDocumentRoot(this.getMapper().getPathInDocument(eoNext));
// 'completely mapped': Any extent containing a 'must-map but unmapped' column should
// be colored red, so setCompletelyMapped will be false in that case.
// All other cases should be orange, so set 'completely mapped' to true in all other cases
if (bThisNodeIsRequired) {
// if required, set 'completely mapped' to the current mapped state
meCurrentExtent.setCompletelyMapped(bMapped);
meCurrentExtent.setMappingRequired(true);
} else {
// if not required, set 'completely mapped' to bMapped
// jh Defect 21988: was 'true', changing to bMapped:
meCurrentExtent.setCompletelyMapped(bMapped);
meCurrentExtent.setMappingRequired(false);
}
totalExtentList.add(meCurrentExtent);
mcCurrentMappingClass = mc;
}
} else {
// otherwise it is NOT mappable:
// null out the ongoing element extent, if any, because any un-mappable element
// should interrupt it and leave a gap
meCurrentExtent = null;
}
}
// ==========================================
// case: any node, mapped to a StagingTable
// ==========================================
// Last step for this node: handle a treenode mapped to a staging Table
StagingTable st = this.getMappingAdapter().getStagingTable(eoNext);
if (st != null) {
/*
* I am not sure how to generate the Staging Table extent, or where.
* Can a treenode be both a Staging Table anchor point, and the locus of
* an ordinary element bound Extent? Yes it can.
*
* First cut: Why not just use the existing process to create Staging table extents?
* We don't really have any new rules for Staging Tables...
*/
// System.out.println("[MappingDiagramModelFactory.getAllCoarseExtentsForLyra] This IS a Staging Table: " + st );
totalExtentList.add(this.getExtent(st));
}
iTreeNodes++;
}
return totalExtentList;
}
private boolean isMappingRequiredForNode( EObject eoNode ) {
if (eoNode instanceof XmlElement) {
// Defect 22500 - somehow this changed elsewhere. Now we need to check if NO Xsd Component, then return false
if (((XmlElement)eoNode).getXsdComponent() == null) {
return false;
}
return (((XmlElement)eoNode).getMinOccurs() > 0);
}
if (eoNode instanceof XmlAttribute) {
return (((XmlAttribute)eoNode).getMinOccurs() > 0);
}
return false;
}
private boolean isUnexpandedElementWithChildren( EObject eo,
MappingAdapterFilter xmlFilter ) {
/*
* jh Lyra enh: This method gets the Summary Extent to show up for unexpanded elements
* that have children, but when it is working we don't get any normal
* Element extents on the other elements. Could it be that this logic
* is not discriminating enough, and ALL elements wind up passing this test,
* but only the elements that have MCs under them get the Summary Extent?????
* Yes! Fixed.
*/
boolean bExpanded = xmlFilter.getTreeViewer().getExpandedState(eo);
if (!bExpanded) {
DocumentContentProvider dcpContentProvider = (DocumentContentProvider)xmlFilter.getTreeViewer().getContentProvider();
Object[] children = dcpContentProvider.getChildren(eo);
if (children.length > 0) {
// if it is not expanded, but has children, return true
return true;
}
}
// else return false
return false;
}
private boolean someMappingClassesAreVisible( HashMap hmapMappingClasses,
List lstVisibleMappingClasses ) {
Iterator itMCs = hmapMappingClasses.keySet().iterator();
while (itMCs.hasNext()) {
MappingClass mcTemp = (MappingClass)itMCs.next();
if (lstVisibleMappingClasses.contains(mcTemp)) {
return true;
}
}
return false;
}
private boolean isNodeAMappingClassRoot( EObject eo ) {
/*
* jh Lyra enh: Determine if this node is the anchor of a Mapping Class.
* If so, add the MC's name to the text of this node.
*/
TreeMappingAdapter tmaMappingAdapter = getMappingAdapter();
// we'll find a mapping class ONLY if this treenode is the mapping root element
MappingClass mc = tmaMappingAdapter.getMappingClass(eo);
if (mc != null) {
return true;
}
return false;
}
/**
* Obtain a MappingExtent for the specified StagingTable.
*
* @param theStagingTable
* @return
*/
public MappingExtent getExtent( StagingTable theStagingTable ) {
// System.out.println("[MappingAdapterFilter.getExtent] TOP; theStagingTable: " + theStagingTable );
EObject documentLocation = mappingAdapter.getStagingTableOutputLocation(theStagingTable);
int index = treeExpansionMonitor.getVisibleObjects().indexOf(documentLocation);
// System.out.println("[MappingAdapterFilter.getExtent] index of Staging Tables doc location: " + index );
MappingExtent extent = new MappingExtent(index * rowHeight, rowHeight, theStagingTable, documentLocation);
extent.setPathToDocumentRoot(mapper.getPathInDocument(documentLocation));
return extent;
}
/**
* Generate an ordered list of detailed MappingExtents for the specified MappingClass.
*
* @param theMappingClass
* @return
*/
public List getDetailedMappingExtents( MappingClass theMappingClass ) {
// System.out.println("[MappingAdapterFilter.getDetailedMappingExtents] TOP");
CoreArgCheck.isNotNull(theMappingClass);
// the detailed mapping extents are gathered the same way the Coarse extents are, except
// one is built for every row (tree node) that is visible and mappable.
List allNodes = getCoarseMappingExtentNodes(theMappingClass);
List mappableNodes = new ArrayList(allNodes.size());
for (Iterator iter = allNodes.iterator(); iter.hasNext();) {
EObject node = (EObject)iter.next();
if (isMappable(node)) {
mappableNodes.add(node);
}
}
// jh PERFORMANCE - make visibleNodes a Map: key=node; value=row number
TreeNodeMap tnmVisibleNodes = treeExpansionMonitor.getVisibleObjectsAsMap();
List result = new ArrayList(mappableNodes.size());
for (Iterator iter = mappableNodes.iterator(); iter.hasNext();) {
EObject node = (EObject)iter.next();
// if ( node instanceof XmlElementImpl ) {
// // XmlElementImpl xeiNode = (XmlElementImpl)node;
// // System.out.println("[MappingAdapterFilter.getDetailedMappingExtents] processing mappableNodes- Node: " +
// xeiNode.getName() );
// }
// else
// if ( node instanceof XmlAttributeImpl ) {
// // XmlAttributeImpl xaiNode = (XmlAttributeImpl)node;
// // System.out.println("[MappingAdapterFilter.getDetailedMappingExtents] processing mappableNodes- Node: " +
// xaiNode.getName() );
// }
// else {
// // System.out.println("[MappingAdapterFilter.getDetailedMappingExtents] processing mappableNodes- Node: " + node );
// }
// create a MappingExtent for this attributeMapping
int row = tnmVisibleNodes.indexOf(node);
// jh Defect 22585: research: is this MappingExtent being created with a
// null MappingClassColumn?????
MappingClassColumn column = mappingAdapter.getMappingClassColumn(node, theMappingClass);
MappingExtent extent = new MappingExtent(row * rowHeight, rowHeight, column, node);
// System.out.println("{MappingAdapterFilter.getDetailedMappingExtents] new extent.documentNodeRef: " +
// extent.documentNodeRef );
if (column == null) {
// System.out.println("{MappingAdapterFilter.getDetailedMappingExtents] new extent's mapping class column is NULL "
// );
}
extent.setPathToDocumentRoot(mapper.getPathInDocument(node));
extent.setXsdQualifiedName(mapper.getXsdQualifiedName(node));
extent.setXsdTargetNamespace(mapper.getXsdTargetNamespace(node));
// jh Defect 21988: Must also set 'completelyMapped' and 'mappingRequired' here:
extent.setMappingRequired(isMappingRequiredForNode(node));
result.add(extent);
}
// now add extents for any staging tables at or above the mapping class location
List mcLocations = mappingAdapter.getMappingClassOutputLocations(theMappingClass);
for (Iterator stIter = mappingAdapter.getAllStagingTables().iterator(); stIter.hasNext();) {
StagingTable st = (StagingTable)stIter.next();
// System.out.println("[MappingAdapterFilter.getDetailedMappingExtents] Staging Table: " + st );
EObject stLocation = mappingAdapter.getStagingTableOutputLocation(st);
// see if the staging table location is visible - if not, nothing to return
if (tnmVisibleNodes.contains(stLocation)) {
for (Iterator mcIter = mcLocations.iterator(); mcIter.hasNext();) {
EObject mcLocation = (EObject)mcIter.next();
if (stLocation.equals(mcLocation)) {
result.add(getExtent(st));
break;
} else if (this.mapper.getMappableTree().isAncestorOf(stLocation, mcLocation)) {
result.add(getExtent(st));
}
}
}
}
// System.out.println("[MappingAdapterFilter.getDetailedMappingExtents] BOT");
return result;
}
/**
* Obtain an ordered list of all locations visible in the TreeViewer that are in the extent of the specified MappingClass.
*
* @param theMappingClass
* @return
*/
public List getCoarseMappingExtentNodes( MappingClass theMappingClass ) {
// get all locations for this mapping class
List locations = mappingAdapter.getMappingClassOutputLocations(theMappingClass);
List extentNodes = new ArrayList();
List columnLocations = getColumnLocations(theMappingClass);
// get all the visible tree nodes
List visibleTreeNodes = new ArrayList(treeExpansionMonitor.getVisibleObjects());
HashMap visibleTNMap = new HashMap();
Iterator iter = visibleTreeNodes.iterator();
while (iter.hasNext()) {
visibleTNMap.put(iter.next(), "x"); //$NON-NLS-1$
}
if (!locations.isEmpty()) {
// build a list of all mapping class locations for use in calculating extents
List mappingClassLocations = new ArrayList();
for (Iterator mcIter = mappingAdapter.getAllMappingClasses().iterator(); mcIter.hasNext();) {
mappingClassLocations.addAll(mappingAdapter.getMappingClassOutputLocations((MappingClass)mcIter.next()));
}
for (int size = locations.size(), i = 0; i < size; i++) {
// iterate over every visible location node
if (visibleTNMap.get(locations.get(i)) != null) { // visibleTreeNodes.contains(locations.get(i)) ) {
// add the location to the collection of extent nodes
extentNodes.add(locations.get(i));
// recurse down this location and collect up the extent nodes
extentNodes.addAll(gatherCoarseExtentNodes((EObject)locations.get(i),
columnLocations,
mappingClassLocations,
visibleTNMap));
}
}
}
// get the extent nodes in order by calling retainAll on the visible nodes
visibleTreeNodes.retainAll(extentNodes);
// return the retained visible nodes
return visibleTreeNodes;
}
/**
* Recursive method used by getCoarseExtentNodes to walk down a branch of the tree and find all visible nodes in the extent.
*
* @param visibleNode the branch node that this method will look beneath
* @param columnLocations a Collection of tree nodes that should automatically be added in the result
* @param mappingClassLocations a Collection of mapping class locations. Any node inside this collection should not be added
* to the result.
* @param visibleTreeNodes a Collection of all tree nodes that are visible in the tree. Nodes that are not in this Collection
* should not be added to the result.
* @return
*/
private List gatherCoarseExtentNodes( EObject visibleNode,
Collection columnLocations,
Collection mappingClassLocations,
HashMap visibleTreeNodes ) {
ArrayList result = new ArrayList();
for (Iterator childIter = mapper.getMappableTree().getChildren(visibleNode).iterator(); childIter.hasNext();) {
EObject node = (EObject)childIter.next();
// first, make sure the child is visible
if (visibleTreeNodes.get(node) != null) {
// next, check to see if this node is mapped into the MappingClass by checking columnLocations
if (columnLocations.contains(node)) {
// if so, then this node is in the extent
result.add(node);
// recurse down this node's children
result.addAll(gatherCoarseExtentNodes(node, columnLocations, mappingClassLocations, visibleTreeNodes));
} else {
// see if there is a mapping class located at this node
if (mappingClassLocations.contains(node)) {
// stop; this node is in another extent. do not check this node's children.
} else {
// this node is in the extent
result.add(node);
// recurse down this node's children
result.addAll(gatherCoarseExtentNodes(node, columnLocations, mappingClassLocations, visibleTreeNodes));
}
}
}
}
return result;
}
/**
* return a list of all location mapped to the attributes of the specified MappingClass
*
* @param theMappingClass
* @return
*/
public List getColumnLocations( MappingClass theMappingClass ) {
List result = new ArrayList();
for (Iterator iter = theMappingClass.getColumns().iterator(); iter.hasNext();) {
result.addAll(mappingAdapter.getMappingClassColumnOutputLocations((MappingClassColumn)iter.next()));
}
return result;
}
public List getLocations( MappingClass theMappingClass ) {
return mappingAdapter.getMappingClassOutputLocations(theMappingClass);
}
public EObject getLocation( StagingTable theStagingTable ) {
return mappingAdapter.getStagingTableOutputLocation(theStagingTable);
}
public List getLocations( MappingClassColumn theMappingClassColumn ) {
return mappingAdapter.getMappingClassColumnOutputLocations(theMappingClassColumn);
}
public int getLevel( MappingClass theMappingClass ) {
int result = 0;
List locations = getLocations(theMappingClass);
if (locations != null && locations.size() > 0) {
EObject location = (EObject)locations.iterator().next();
result = ModelerCore.getModelEditor().getModelRelativePath(location).segmentCount() - 2;
}
return result;
}
public int getLevel( StagingTable theStagingTable ) {
int result = 0;
EObject location = getLocation(theStagingTable);
if (location != null) {
result = ModelerCore.getModelEditor().getModelRelativePath(location).segmentCount() - 2;
}
return result;
}
public List getMappedClassifiers() {
boolean bPopulateDiagramFromTreeSelection = getBehavior().getPopulateDiagramFromTreeSelectionState();
// System.out.println("[MappingAdapterFilter.getMappedClassifiers] Retrieved bPopulateDiagramFromTreeSelection: " +
// bPopulateDiagramFromTreeSelection );
if (bPopulateDiagramFromTreeSelection && getSelectedNodes() != null && getSelectedNodes().size() == 1) {
// && getMappingClassesImpliedBySelection().size() > 0 ) {
return getMappedClassifiersForSelectionDrivenDiagram();
}
// default to the normal, full diagram
return getMappedClassifiersForFullDiagram();
}
/**
* Obtain all {@link MappingClass}es visible in the filter. This collection will also include any visible {@link StagingTable}
* s.
*
* @return list of visible <code>MappingClass</code>es and <code>StagingTable</code>s
*/
private List getMappedClassifiersForSelectionDrivenDiagram() {
/*
* jh Lyra enh: We seem to be doing this redundantly. Isn't there some way to return the result without
* reconstructing it under some circumstances?????
*/
List result = new ArrayList();
/*
* jh Lyra enh: the call: "TreeViewerUtil.getVisibleObjects(treeViewer)"
* has a problem for Lyra, which is that it does not return
* nodes which are visible but NOT expanded. For example,
* if a sequence is a child of an expanded sequence, but
* is not expanded itself, it will not be returned.
* For Lyra, this is bad, because we are tree-driven and
* must have the complete picture of which nodes are VISIBLE.
*/
// this call should work fine:
// List visibleNodes = treeExpansionMonitor.getVisibleObjects();
List mappingClassObjects = new ArrayList(mappingAdapter.getAllMappingClasses());
mappingClassObjects.addAll(mappingAdapter.getAllStagingTables());
// boolean bShowAllMappingClasses
// = editor.getDisplayAllMappingClasses();
// System.out.println("[MappingAdapterFilter.getMappedClassifiers] Show all Mapping Classes boolean: " +
// bShowAllMappingClasses );
for (Iterator mcIter = mappingClassObjects.iterator(); mcIter.hasNext();) {
MappingClass mappingClass = (MappingClass)mcIter.next();
// List locations = null;
//
// // StagingTable is a subclass of MappingClass
// if (mappingClass instanceof StagingTable) {
// // System.out.println("[MappingAdapterFilter.getMappedClassifiers] Found a Staging Table: " + mappingClass );
// locations = Collections.singletonList(mappingAdapter.getLocation((StagingTable)mappingClass));
// } else {
// // System.out.println("[MappingAdapterFilter.getMappedClassifiers] Found a Mapping Class: " + mappingClass );
// locations = mappingAdapter.getLocations(mappingClass);
// }
HashMap hmMappingClassesToDisplay = getMappingClassesImpliedBySelection();
if (hmMappingClassesToDisplay.get(mappingClass) != null) {
// System.out.println("[MappingAdapterFilter.getMappedClassifiersForSelectionDrivenDiagram] About to add to output: "
// + mappingClass );
result.add(mappingClass);
break;
}
}
return result;
}
/**
* Obtain all {@link MappingClass}es visible in the filter. This collection will also include any visible {@link StagingTable}
* s.
*
* @return list of visible <code>MappingClass</code>es and <code>StagingTable</code>s
*/
public List getMappedClassifiersForFullDiagram() {
/*
* jh Lyra enh: We seem to be doing this redundantly. Isn't there some way to return the result without
* reconstructing it under some circumstances?????
*/
List result = new ArrayList();
/*
* jh Lyra enh: the call: "TreeViewerUtil.getVisibleObjects(treeViewer)"
* has a problem for Lyra, which is that it does not return
* nodes which are visible but NOT expanded. For example,
* if a sequence is a child of an expanded sequence, but
* is not expanded itself, it will not be returned.
* For Lyra, this is bad, because we are tree-driven and
* must have the complete picture of which nodes are VISIBLE.
*/
// this call should work fine:
// List visibleNodes = treeExpansionMonitor.getVisibleObjects();
TreeNodeMap tnmVisibleNodes = treeExpansionMonitor.getVisibleObjectsAsMap();
List mappingClassObjects = new ArrayList(mappingAdapter.getAllMappingClasses());
mappingClassObjects.addAll(mappingAdapter.getAllStagingTables());
boolean bShowAllMappingClasses = getBehavior().getDisplayAllMappingClasses();
// System.out.println("[MappingAdapterFilter.getMappedClassifiers] Show all Mapping Classes boolean: " +
// bShowAllMappingClasses );
for (Iterator mcIter = mappingClassObjects.iterator(); mcIter.hasNext();) {
MappingClass mappingClass = (MappingClass)mcIter.next();
List locations = null;
// StagingTable is a subclass of MappingClass
if (mappingClass instanceof StagingTable) {
// System.out.println("[MappingAdapterFilter.getMappedClassifiers] Found a Staging Table: " + mappingClass );
locations = Collections.singletonList(mappingAdapter.getStagingTableOutputLocation((StagingTable)mappingClass));
} else {
// System.out.println("[MappingAdapterFilter.getMappedClassifiers] Found a Mapping Class: " + mappingClass );
locations = mappingAdapter.getMappingClassOutputLocations(mappingClass);
}
for (Iterator locIter = locations.iterator(); locIter.hasNext();) {
Object node = locIter.next();
if (bShowAllMappingClasses || tnmVisibleNodes.contains(node)) {
// System.out.println("[MappingAdapterFilter.getMappedClassifiers] About to add to output: " + mappingClass );
result.add(mappingClass);
break;
}
}
}
return result;
}
private boolean isMappable( EObject theTreeNode ) {
return mapper.isMappable(theTreeNode);
}
public int getNumberVisibleNodes() {
if (treeExpansionMonitor != null) return treeExpansionMonitor.getVisibleObjects().size();
return 0;
}
public void dispose() {
treeExpansionMonitor.dispose();
}
public TreeExpansionMonitor getTreeExpansionMonitor() {
return this.treeExpansionMonitor;
}
public TreeViewer getTreeViewer() {
return this.treeViewer;
}
public HashMap getMappingClassesInBranch( EObject eo ) {
HashMap hmap = new HashMap();
DocumentContentProvider dcpContentProvider = (DocumentContentProvider)getTreeViewer().getContentProvider();
hmap = internalGetMappingClassesInBranch(dcpContentProvider, dcpContentProvider.getChildren(eo), hmap);
/*
* jh Defect 22768 (22566): Handle the special case in which eo:
* 1) Is the XmlRootImpl
* 2) Has no children
* 3) But as an element itself, maps to a mapping class
* --> put eo itself into an object array and call
* internalGetMappingClassesInBranch() with it.
*/
if (hmap.isEmpty() && eo instanceof XmlRoot) {
Object[] children = {eo};
hmap = internalGetMappingClassesInBranch(dcpContentProvider, children, hmap);
}
return hmap;
}
private HashMap internalGetMappingClassesInBranch( DocumentContentProvider dcpContentProvider,
Object[] eoChildren,
HashMap hmap ) {
/* jh Lyra enh:
* It is possible to get dupes, so correct for that.
* jhTODO
* Barry's case: To do this, create a new hashmap in the Mapping Adapter that maps ALL
* mapped columns to their mapping classes. Then we will use it here
* to discover hidden nodes that map to VISIBLE mapping classes, and add
* those MCs to the result array in this method.
*/
for (int i = 0; i < eoChildren.length; i++) {
EObject eoTemp = (EObject)eoChildren[i];
if (eoTemp != null) {
// System.out.println("[MappingAdapterFilter.internalGetMappingnClassesInBranch] processing node: " + eoTemp );
// see if this tree EObject maps to a MappingClass; if so, capture the Mapping Class
MappingClassColumn mccol = getMappingAdapter().getMappingClassColumn(eoTemp);
if (mccol != null) {
MappingClass mc = mccol.getMappingClass();
if (mc != null) {
Integer ICount = (Integer)hmap.get(mc);
if (ICount != null) {
// if the MC is already in the map, bump up its count by 1
// System.out.println("[MappingAdapterFilter.internalGetMappingnClassesInBranch] About to add mc: " +
// mc.getName() );
hmap.put(mc, new Integer(ICount.intValue() + 1));
} else {
// Otherwise if it is new, add it to the map
hmap.put(mc, new Integer(1));
}
}
} else {
/*
* this treenode is not mapped, but we should only count it
* IF it is mappable, i.e., not a sequence or other container.
*/
// count this node toward a total of mappable + unmapped nodes for this branch
if (isMappable(eoTemp)) {
Integer ICount = (Integer)hmap.get(SummaryExtent.UNMAPPED_MAPPABLE_COLUMN_COUNT);
if (ICount != null) {
// System.out.println("\n[MappingAdapterFilter.internalGetMappingnClassesInBranch] About to save new Unmapped cnt: "
// + (ICount.intValue() + 1) );
// System.out.println("\n[MappingAdapterFilter.internalGetMappingnClassesInBranch] And the unmapped node is: "
// + eoTemp );
// if the MC is already in the map, bump up its count by 1
hmap.put(SummaryExtent.UNMAPPED_MAPPABLE_COLUMN_COUNT, new Integer(ICount.intValue() + 1));
} else {
// Otherwise if it is new, add it to the map
// System.out.println("\n[MappingAdapterFilter.internalGetMappingnClassesInBranch] About to save new Unmapped cnt: 1 "
// );
// System.out.println("\n[MappingAdapterFilter.internalGetMappingnClassesInBranch] And the unmapped node is: "
// + eoTemp );
hmap.put(SummaryExtent.UNMAPPED_MAPPABLE_COLUMN_COUNT, new Integer(1));
}
}
}
}
// if it has children, process the children recursively
Object[] eoNextChildren = dcpContentProvider.getChildren(eoTemp);
if (eoNextChildren.length > 0) {
internalGetMappingClassesInBranch(dcpContentProvider, eoNextChildren, hmap);
}
}
return hmap;
}
public void setSelectedNodes( List lstNodes ) {
// System.out.println("[MappingAdapterFilter.setSelectedNodes");
this.lstNodes = lstNodes;
}
public List getSelectedNodes() {
return this.lstNodes;
}
}