/*
* 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.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.provider.INotifyChangedListener;
import org.teiid.core.designer.ModelerCoreException;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.notification.util.NotificationUtilities;
import org.teiid.designer.core.transaction.SourcedNotification;
import org.teiid.designer.core.workspace.ModelMappingClassSets;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.core.workspace.ModelWorkspaceException;
import org.teiid.designer.mapping.PluginConstants;
import org.teiid.designer.mapping.factory.ModelMapperFactory;
import org.teiid.designer.mapping.factory.TreeMappingAdapter;
import org.teiid.designer.metamodels.transformation.InputBinding;
import org.teiid.designer.metamodels.transformation.MappingClassSet;
import org.teiid.designer.ui.undo.ModelerUndoManager;
/**
* MappingNotificationListener The notification listener is instantiated upon initialization of the plugin, and registered as a
* Notification listener. The listener responds to the following notifications XML Document Nodes o Need to reconcile the mapping
* class set and tree root mappings when we get a remove notification. (i.e. delete any unmapped mapping classes (modeler-side
* cleanup) This class provides a way for the mapping world to listen for any kind of notification which it may need to clean up
* stuff behind the scenes. In particular, when a document node is deleted, there may be mapping classes that hang around and mess
* up the model... Initial implementation 1/15/04 BML
*
* @since 8.0
*/
public class MappingNotificationListener implements INotifyChangedListener {
private static final String THIS_CLASS = "MappingNotificationListener"; //$NON-NLS-1$
private static final boolean NOT_SIGNIFICANT = false;
private static final boolean IS_UNDOABLE = true;
/**
* Construct an instance of TransformationNotificationListener.
*/
public MappingNotificationListener() {
super();
}
/* (non-Javadoc)
* @see org.eclipse.emf.edit.provider.INotifyChangedListener#notifyChanged(org.eclipse.emf.common.notify.Notification)
*/
@Override
public void notifyChanged( Notification notification ) {
if (notification instanceof SourcedNotification) {
if (isValidSource((SourcedNotification)notification) && shouldHandleNotification(notification)) {
boolean requiredStart = false;
boolean succeeded = false;
try {
// -------------------------------------------------
// Let's wrap this in a transaction!!!
// will result in only one transaction?
// -------------------------------------------------
requiredStart = ModelerCore.startTxn(NOT_SIGNIFICANT, IS_UNDOABLE, "Reconcile Mapping Class Set", this); //$NON-NLS-1$$
handleNotification(notification);
succeeded = true;
} catch (ModelerCoreException ex) {
PluginConstants.Util.log(IStatus.ERROR, ex, ex.getClass().getName() + ":" + THIS_CLASS + ".notifyModel()"); //$NON-NLS-1$ //$NON-NLS-2$
} finally {
if (requiredStart) {
if (succeeded) {
ModelerCore.commitTxn();
} else {
ModelerCore.rollbackTxn();
}
}
}
}
} else {
if (shouldHandleNotification(notification)) {
boolean requiredStart = false;
boolean succeeded = false;
try {
// -------------------------------------------------
// Let's wrap this in a transaction!!!
// will result in only one transaction?
// -------------------------------------------------
requiredStart = ModelerCore.startTxn(NOT_SIGNIFICANT, IS_UNDOABLE, "Reconcile Mapping Class Set", this); //$NON-NLS-1$$
handleNotification(notification);
succeeded = true;
} catch (ModelerCoreException ex) {
PluginConstants.Util.log(IStatus.ERROR, ex, ex.getClass().getName() + ":" + THIS_CLASS + ".notifyModel()"); //$NON-NLS-1$ //$NON-NLS-2$
} finally {
if (requiredStart) {
if (succeeded) {
ModelerCore.commitTxn();
} else {
ModelerCore.rollbackTxn();
}
}
}
}
}
}
/*
* Notifications handler. Gathers all like notifications and handles them together.
* Only the relevant notifications will be processed by this listener.
* @param notifications the collection of all notifications
*/
private void handleNotification( Notification notification ) throws ModelerCoreException {
if (notification instanceof SourcedNotification) {
boolean handled = false;
Object source = ((SourcedNotification)notification).getSource();
if (source == null || !source.equals(this)) {
Collection notifications = ((SourcedNotification)notification).getNotifications();
Iterator iter = notifications.iterator();
Notification nextNotification = null;
while (iter.hasNext() && !handled) {
nextNotification = (Notification)iter.next();
Object targetObject = ModelerCore.getModelEditor().getChangedObject(nextNotification);
if (targetObject != null && targetObject instanceof EObject) {
if (ModelMapperFactory.isXmlTreeNode((EObject)targetObject)) {
if (nextNotification.getEventType() == Notification.REMOVE
|| nextNotification.getEventType() == Notification.REMOVE_MANY) {
handleRemoveNotification((EObject)targetObject);
handled = true;
} else if (NotificationUtilities.isRemoved(nextNotification)) {
handleRemoveNotification((EObject)targetObject);
handled = true;
}
}
}
}
}
} else { // SINGLE NOTIFICATION
Object targetObject = ModelerCore.getModelEditor().getChangedObject(notification);
if (targetObject != null && targetObject instanceof EObject) {
if (ModelMapperFactory.isXmlTreeNode((EObject)targetObject)) {
if (notification.getEventType() == Notification.REMOVE
|| notification.getEventType() == Notification.REMOVE_MANY) {
handleRemoveNotification((EObject)targetObject);
}
}
}
}
}
/**
* Changed this method to isValidSource() so we could check if the source is the <code>ModelerUndoManager</code> this was
* causing additional work being done during the "Undo" that shouldn't have to be done. SEE Defect 18433
*
* @param sn
* @return
* @since 4.3
*/
private boolean isValidSource( SourcedNotification sn ) {
boolean valid = true;
Object source = sn.getSource();
if (source != null) {
if (source.equals(this) || source instanceof ModelerUndoManager) valid = false;
}
return valid;
}
private boolean shouldHandleNotification( Notification notification ) {
boolean shouldHandle = false;
if (notification instanceof SourcedNotification) {
Object source = ((SourcedNotification)notification).getSource();
if (source == null || !source.equals(this)) {
Collection notifications = ((SourcedNotification)notification).getNotifications();
Iterator iter = notifications.iterator();
Notification nextNotification = null;
while (iter.hasNext() && !shouldHandle) {
nextNotification = (Notification)iter.next();
Object targetObject = ModelerCore.getModelEditor().getChangedObject(nextNotification);
if (targetObject != null && targetObject instanceof EObject) {
if (ModelMapperFactory.isXmlTreeNode((EObject)targetObject)) {
if (nextNotification.getEventType() == Notification.REMOVE
|| nextNotification.getEventType() == Notification.REMOVE_MANY) {
shouldHandle = true;
} else if (NotificationUtilities.isRemoved(nextNotification)) {
shouldHandle = true;
}
}
}
}
}
} else { // SINGLE NOTIFICATION
Object targetObject = ModelerCore.getModelEditor().getChangedObject(notification);
if (targetObject != null && targetObject instanceof EObject) {
if (ModelMapperFactory.isXmlTreeNode((EObject)targetObject)) {
if (notification.getEventType() == Notification.REMOVE
|| notification.getEventType() == Notification.REMOVE_MANY) {
shouldHandle = true;
} else if (NotificationUtilities.isRemoved(notification)) {
shouldHandle = true;
}
}
}
}
return shouldHandle;
}
private void handleRemoveNotification( EObject targetEObject ) throws ModelerCoreException {
// Find it's tree root!!
EObject treeRoot = ModelMapperFactory.getTreeRoot(targetEObject);
if (treeRoot != null) {
// get the TreeMappingRoot??
// We have the tree root, now let's get two things, 1) Mapping Transformation Roots and 2) Mapping Class Set
TreeMappingAdapter tma = new TreeMappingAdapter(treeRoot);
List currentMappingClasses = tma.getAllMappingClasses();
List allStagingTables = tma.getAllStagingTables();
EObject document = tma.getDocument();
List mappingClasses = null;
if (document != null) {
MappingClassSet mcs = getMappingClassSet(treeRoot);
if (mcs != null) {
mappingClasses = new ArrayList(mcs.eContents());
}
} else {
// Had to add back in this case because if you delete the xml document4 root
// then there getDocument() returns null,when the tree root is really the document.
// get it???
MappingClassSet mcs = getMappingClassSet(treeRoot);
if (mcs != null) {
mappingClasses = new ArrayList(mcs.eContents());
}
}
if (mappingClasses != null && !mappingClasses.isEmpty()) {
Object nextObj = null;
Iterator iter = currentMappingClasses.iterator();
while (iter.hasNext()) {
nextObj = iter.next();
if (nextObj instanceof EObject) {
if (mappingClasses.contains(nextObj)) mappingClasses.remove(nextObj);
}
}
iter = allStagingTables.iterator();
while (iter.hasNext()) {
nextObj = iter.next();
if (nextObj instanceof EObject) {
if (mappingClasses.contains(nextObj)) mappingClasses.remove(nextObj);
}
}
if (!mappingClasses.isEmpty()) {
// Call delete now!! finally.
iter = mappingClasses.iterator();
while (iter.hasNext()) {
nextObj = iter.next();
if (nextObj instanceof EObject && !(nextObj instanceof InputBinding)) {
ModelerCore.getModelEditor().delete((EObject)nextObj);
}
}
}
}
}
}
private MappingClassSet getMappingClassSet( EObject treeRoot ) {
MappingClassSet mappingClassSet = null;
ModelResource mr = ModelerCore.getModelEditor().findModelResource(treeRoot);
if (mr != null) {
try {
ModelMappingClassSets sets = mr.getModelMappingClassSets();
List setList = sets.getMappingClassSets(treeRoot);
if (setList == null || setList.isEmpty()) {
mappingClassSet = sets.createNewMappingClassSet(treeRoot);
} else {
mappingClassSet = (MappingClassSet)setList.get(0);
}
} catch (ModelWorkspaceException e) {
PluginConstants.Util.log(e);
}
}
return mappingClassSet;
}
}