/*
* 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.core.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IResource;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
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.ModelResource;
import org.teiid.designer.core.workspace.ModelWorkspaceException;
import org.teiid.designer.core.workspace.ModelWorkspaceManager;
import org.teiid.designer.core.workspace.WorkspaceResourceFinderUtil;
import org.teiid.designer.metamodels.core.ModelAnnotation;
import org.teiid.designer.metamodels.diagram.Diagram;
import org.teiid.designer.metamodels.diagram.DiagramContainer;
import org.teiid.designer.metamodels.diagram.DiagramEntity;
/**
* This class provides notification analysis and bundling. It will categorize all notifications, collect target objects and
* filters out ignorable notifications.
*
* @since 8.0
*/
public class CoreModelObjectNotificationHelper {
private static final String NEW_LINE = "\n"; //$NON-NLS-1$
protected Notification primaryNotification = null;
protected HashSet addOrRemoveTargets = null;
protected HashSet changeTargets = null;
protected HashSet changedModels = null;
protected HashSet modifiedResources = null;
protected HashSet addedChildren;
protected boolean modelChildrenChanged = false;
protected List leftoverNotifications = null;
protected boolean handleNotification = false;
protected boolean isDiagramOnlyNotification = true;
/**
* @since 4.2
*/
public CoreModelObjectNotificationHelper( Notification notification ) {
super();
this.primaryNotification = notification;
init();
}
private void init() {
addOrRemoveTargets = new HashSet();
addedChildren = new HashSet();
changeTargets = new HashSet();
changedModels = new HashSet();
modifiedResources = new HashSet();
leftoverNotifications = new ArrayList();
setHandleNotification();
filterTargets();
}
private void setHandleNotification() {
handleNotification = true;
}
/**
* @return Returns the handleNotification.
* @since 4.2
*/
public boolean shouldHandleNotification() {
return this.handleNotification;
}
protected void filterTargets() {
addOrRemoveTargets.clear();
changeTargets.clear();
changedModels.clear();
modelChildrenChanged = false;
leftoverNotifications.clear();
if (primaryNotification instanceof SourcedNotification) {
// Walk through the notifications.
// and determine we need to reconcile target attributes (bail on check if already set)
// determine if any "source" tables need to be reconciled (don't bail on check if already set)
// Add all tables to reconcileTables list
Collection notifications = ((SourcedNotification)primaryNotification).getNotifications();
Iterator iter = notifications.iterator();
Notification notification = null;
while (iter.hasNext()) {
notification = (Notification)iter.next();
Object targetObject = ModelerCore.getModelEditor().getChangedObject(notification);
if (targetObject instanceof DiagramContainer || targetObject instanceof ModelAnnotation) {
leftoverNotifications.add(notification);
}
boolean targetIsResource = targetObject instanceof Resource;
EObject targetEObject = getEObjectTarget(notification);
if (targetEObject != null) {
addToModifiedResources(targetEObject);
if (NotificationUtilities.isAdded(notification)) {
handleAddOrRemove(notification, targetEObject);
EObject[] newValues = NotificationUtilities.getAddedChildren(notification);
if (newValues.length > 0) {
addedChildren.addAll(Arrays.asList(newValues));
// Check ignorable targets
checkNonDiagramRelatedChange(newValues, false);
} // endif
} else if (NotificationUtilities.isRemoved(notification)) {
handleAddOrRemove(notification, targetEObject);
// Check ignorable target
checkNonDiagramRelatedChange(targetEObject, false);
} else if (NotificationUtilities.isChanged(notification)) {
handleChanged(notification, targetEObject);
// Check ignorable target
checkNonDiagramRelatedChange(targetEObject, true);
}
} else if (targetIsResource) {
if (NotificationUtilities.isAdded(notification)) {
handleAddOrRemove(notification, targetEObject);
EObject[] newValues = NotificationUtilities.getAddedChildren(notification);
if (newValues.length > 0) {
addedChildren.addAll(Arrays.asList(newValues));
// Check ignorable targets
checkNonDiagramRelatedChange(newValues, false);
} // endif
} else if (NotificationUtilities.isRemoved(notification)) {
handleAddOrRemove(notification, targetEObject);
// Check ignorable target
checkNonDiagramRelatedChange(targetEObject, false);
} else if (NotificationUtilities.isChanged(notification)) {
handleChanged(notification, targetEObject);
// Check ignorable target
checkNonDiagramRelatedChange(targetEObject, true);
}
// call findIResource since this does NOT open the model
IResource resource = WorkspaceResourceFinderUtil.findIResource((Resource)targetObject);
if ((resource != null) && ModelWorkspaceManager.getModelWorkspaceManager().isModelOpen(resource)) {
modelChildrenChanged = true;
changedModels.add(resource);
addToModifiedResources(resource);
}
}
}
} else {
Object targetObject = ModelerCore.getModelEditor().getChangedObject(primaryNotification);
if (targetObject instanceof DiagramContainer || targetObject instanceof ModelAnnotation) {
leftoverNotifications.add(primaryNotification);
}
EObject targetEObject = getEObjectTarget(primaryNotification);
if (NotificationUtilities.isAdded(primaryNotification) || NotificationUtilities.isRemoved(primaryNotification)) {
if (targetEObject != null) {
addOrRemoveTargets.add(targetEObject);
// Check ignorable target
checkNonDiagramRelatedChange(targetEObject, false);
}
} else if (NotificationUtilities.isChanged(primaryNotification)) {
if (targetEObject != null) {
changeTargets.add(targetEObject);
// Check ignorable target
checkNonDiagramRelatedChange(targetEObject, true);
}
}
}
}
protected void addToModifiedResources( Object someObject ) {
IResource iResource = null;
try {
if (someObject instanceof EObject) {
ModelResource mr = ModelerCore.getModelEditor().findModelResource((EObject)someObject);
if (mr != null) {
iResource = mr.getUnderlyingResource();
}
} else if (someObject instanceof Resource) {
ModelResource mr = ModelerCore.getModelEditor().findModelResource((Resource)someObject);
if (mr != null) {
iResource = mr.getUnderlyingResource();
}
} else if (someObject instanceof IResource) {
iResource = (IResource)someObject;
} else if (someObject instanceof ModelResource) {
iResource = ((ModelResource)someObject).getUnderlyingResource();
}
} catch (ModelWorkspaceException error) {
}
if (iResource != null) {
modifiedResources.add(iResource);
}
}
/**
* @param notification
* @param targetEObject
*/
protected void handleAddOrRemove( Notification notification,
EObject targetEObject ) {
if (targetEObject != null) addOrRemoveTargets.add(targetEObject);
else leftoverNotifications.add(notification);
}
/**
* @param notification
* @param targetEObject
*/
protected void handleChanged( Notification notification,
EObject targetEObject ) {
if (targetEObject != null) changeTargets.add(targetEObject);
else leftoverNotifications.add(notification);
}
protected EObject getEObjectTarget( Notification notification ) {
Object targetObject = ModelerCore.getModelEditor().getChangedObject(notification);
if (targetObject instanceof EObject) return (EObject)targetObject;
return null;
}
/**
* @return List of the addOrRemoveTargets.
* @since 4.2
*/
public List getAddOrRemoveTargets() {
return new ArrayList(this.addOrRemoveTargets);
}
/**
* @return List of the changed targets
*/
public List getChangeTargets() {
return new ArrayList(this.changeTargets);
}
/**
* @return List of the changeModels.
* @since 4.2
*/
public List getChangeModels() {
return new ArrayList(this.changedModels);
}
/**
* @return Returns the movedChildrenChanged.
* @since 4.2
*/
public boolean getModelChildrenChanged() {
return modelChildrenChanged;
}
/**
* @return List of the leftoverNotifications.
* @since 4.2
*/
public List getLeftoverNotifications() {
return leftoverNotifications;
}
/**
* @return Set of the added children
*/
public Set getAddedChildren() {
return addedChildren;
}
/**
* @return List of the modified resources.
* @since 4.2
*/
public List getModifiedResources() {
return new ArrayList(this.modifiedResources);
}
/*
* Method which keeps track of whether or not any non-diagram objects are the "targets" for notifications
* This knowledge can be used by
* @param obj
* @param isSet
*/
private void checkNonDiagramRelatedChange( Object obj,
boolean isSet ) {
// Only do the check if still non-diagram notification
if (isDiagramOnlyNotification && obj != null) {
if (obj instanceof DiagramEntity) {
isDiagramOnlyNotification = true;
} else if (obj instanceof Diagram && isSet) {
// This case is for diagram name change notifications (i.e. Custom Diagrams)
isDiagramOnlyNotification = false;
} else if (obj instanceof List) {
for (Iterator iter = ((List)obj).iterator(); iter.hasNext();) {
Object nextObj = iter.next();
if (!(nextObj instanceof Diagram || nextObj instanceof DiagramEntity)) {
isDiagramOnlyNotification = false;
}
if (!isDiagramOnlyNotification) {
break;
}
}
}
}
}
/**
* Assesses whether or not all the changes from the notifications are ignorable.
*
* @return boolean true if ignorable, false if not
*/
public boolean allChangesAreIgnorable() {
if (isDiagramOnlyNotification) {
// Do a check to see if there are any added/removed/changed targets
if (addedChildren.isEmpty() && addOrRemoveTargets.isEmpty() && changeTargets.isEmpty()) {
return true;
}
return false;
}
return isDiagramOnlyNotification;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(this.getClass().getName() + NEW_LINE);
sb.append(" # added children = " + this.addedChildren.size() + NEW_LINE); //$NON-NLS-1$
sb.append(" # add/remove targets = " + this.addOrRemoveTargets.size() + NEW_LINE); //$NON-NLS-1$
sb.append(" # changed targets = " + this.changeTargets.size() + NEW_LINE); //$NON-NLS-1$
sb.append(" # changed models = " + this.changedModels.size() + NEW_LINE); //$NON-NLS-1$
sb.append(" # changed resources = " + this.modifiedResources.size() + NEW_LINE); //$NON-NLS-1$
sb.append(" Model Children Changed = " + this.modelChildrenChanged + NEW_LINE); //$NON-NLS-1$
sb.append(" All Changes to Ignorable= " + this.allChangesAreIgnorable() + NEW_LINE); //$NON-NLS-1$
return sb.toString();
}
}