/* * 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; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.emf.mapping.Mapping; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.designer.core.container.ContainerImpl; import org.teiid.designer.core.metamodel.aspect.AspectManager; import org.teiid.designer.core.metamodel.aspect.sql.SqlColumnAspect; import org.teiid.designer.core.search.ModelWorkspaceSearch; import org.teiid.designer.core.search.runtime.ReferencesRecord; import org.teiid.designer.core.util.ModelVisitor; import org.teiid.designer.metamodels.core.Annotation; import org.teiid.designer.metamodels.diagram.Diagram; import org.teiid.designer.metamodels.diagram.DiagramEntity; import org.teiid.designer.metamodels.transformation.InputBinding; import org.teiid.designer.metamodels.transformation.InputParameter; import org.teiid.designer.metamodels.transformation.InputSet; import org.teiid.designer.metamodels.transformation.MappingClass; import org.teiid.designer.metamodels.transformation.MappingClassColumn; import org.teiid.designer.metamodels.transformation.MappingClassSet; import org.teiid.designer.metamodels.transformation.SqlAlias; import org.teiid.designer.metamodels.transformation.TransformationMappingRoot; /** * This class finds related objects that should be deleted when the supplied object is deleted. No delete commands are added for * the deleted object nor any of it's contained objects (recursively). </p> * <p> * However, objects that are deleted (for which delete commands are created) include: * </p> * <p> * <ul> * <li>The {@link Annotation} for the deleted object;</li> * <li>All {@link DiagramEntity} instances in the same resource that reference the deleted object;</li> * <li>All {@link InputBinding} instances that reference the deleted object or objects under the deleted object;</li> * <li>All {@link TransformationMappingRoot} instances in the same resource whose {@link TransformationMappingRoot#getTarget() * target} is the deleted object or an object under the deleted object;</li> * <li>All {@link MappingClass} instances that map to the deleted object or objects under the deleted object; * <li>All {@link MappingClassSet} instances in the same resource whose {@link MappingClassSet#getTarget() target} is the deleted * object or an object under the deleted object;</li> * <li>All persistent or transient {@link Diagram} instances under the deleted object (i.e., whose {@link Diagram#getTarget() * target} is the deleted object or an object under the deleted object);</li> * <li>All {@link Mapping}s for the deleted object when the mapping does not have outputs or if the mapping is column to column * mapping and either the input or output column is deleted. * <li>All {@link SqlAlias}s whose target is the deleted object. * </ul> * </p> * * @see org.teiid.designer.core.ClearReferencesUponDelete * @see org.teiid.designer.core.ModelEditor#delete(EObject) * * @since 8.0 */ public class FindRelatedObjectsToDeleted implements ModelVisitor { private final List additionalDeleteCommands; private final EditingDomain editingDomain; private Collection deletedObjects; private LinkedList objectsForDeleteProcess; private final ModelWorkspaceSearch workspaceSearch; /** * Construct an instance of FindRelatedObjectsToDeleted. */ public FindRelatedObjectsToDeleted( final EObject deletedObject, final EditingDomain editingDomain, final LinkedList objectsForDeleteProcess, final Collection deletedObjects ) { this(deletedObject, editingDomain, objectsForDeleteProcess, deletedObjects, new ModelWorkspaceSearch()); } /** * Construct an instance of FindRelatedObjectsToDeleted. */ public FindRelatedObjectsToDeleted( final EObject deletedObject, final EditingDomain editingDomain, final LinkedList objectsForDeleteProcess, final Collection deletedObjects, final ModelWorkspaceSearch workspaceSearch ) { super(); CoreArgCheck.isNotNull(objectsForDeleteProcess); CoreArgCheck.isNotNull(editingDomain); CoreArgCheck.isNotNull(workspaceSearch); this.additionalDeleteCommands = new ArrayList(); this.editingDomain = editingDomain; this.objectsForDeleteProcess = objectsForDeleteProcess; this.deletedObjects = deletedObjects; if (this.deletedObjects == null) { this.deletedObjects = new HashSet(); } this.workspaceSearch = workspaceSearch; // Try to resolve to a EObject since not all EObjects have proxies // all the collections should have eitherdelegate to enable lookup if (deletedObject != null) { this.deletedObjects.add(deletedObject); // Compute the set of deleted objects ... for (final Iterator iter = deletedObject.eAllContents(); iter.hasNext();) { final Object child = iter.next(); if (child instanceof EObject) { this.deletedObjects.add(child); } } } } /** * @see org.teiid.designer.core.util.ModelVisitor#visit(org.eclipse.emf.ecore.EObject) * @since 4.2 */ @Override public boolean visit( EObject object ) { // clean up unidirectional references ModelEditorImpl modelEditor = (ModelEditorImpl)ModelerCore.getModelEditor(); // get objectID and find uni-directional references for that ID String objID = modelEditor.getSearchIndexObjectID(object); if (objID != null && workspaceSearch != null) { // unidirectional references for each object to clean up Collection refRecords = workspaceSearch.getUniDirectionalReferencesTo(objID); if (refRecords != null && !refRecords.isEmpty()) { for (final Iterator refIter = refRecords.iterator(); refIter.hasNext();) { ReferencesRecord refRecord = (ReferencesRecord)refIter.next(); String refUUID = refRecord.getUUID(); EObject refrencingObj = modelEditor.findObject(refUUID); if (refrencingObj != null) { try { if (refrencingObj.eIsProxy()) { final ContainerImpl cntr = (ContainerImpl)ModelerCore.getModelContainer(); refrencingObj = EcoreUtil.resolve(refrencingObj, cntr.getResourceSet()); } } catch (Exception err) { // Do nothing if we couldn't resolve the object, let the deleteAsNeeded proceed. } deleteObjectAsNeeded(refrencingObj); } } } } // this object has been processed for delet remove from list objectsForDeleteProcess.remove(object); return true; } /** * @see org.teiid.designer.core.util.ModelVisitor#visit(org.eclipse.emf.ecore.resource.Resource) * @since 4.2 */ @Override public boolean visit( Resource resource ) { return false; } /** * Delete the referencing eObject based on various rules. * * @param referencingObj * @since 4.2 */ public void deleteObjectAsNeeded( final EObject referencingObj ) { // These are ordered such that the types more likely to be found (because there may be more instances) // are nearer the top // if the parent is marked to be deleted, delete the child EObject parent = referencingObj.eContainer(); if (deletedObjects.contains(referencingObj) || (parent != null && deletedObjects.contains(parent))) { return; } if (referencingObj instanceof Annotation) { final Annotation annotation = (Annotation)referencingObj; final EObject target = annotation.getAnnotatedObject(); if (target == null || deletedObjects.contains(target)) { delete(referencingObj); } return; } if (referencingObj instanceof DiagramEntity) { final DiagramEntity entity = (DiagramEntity)referencingObj; final EObject target = entity.getModelObject(); if (deletedObjects.contains(target)) { delete(referencingObj); } return; } if (referencingObj instanceof InputBinding) { final InputBinding binding = (InputBinding)referencingObj; final InputParameter inputParam = binding.getInputParameter(); final InputSet inputSet = inputParam.getInputSet(); final MappingClassColumn mcCol = binding.getMappingClassColumn(); if (deletedObjects.contains(inputParam) || deletedObjects.contains(inputSet) || deletedObjects.contains(mcCol)) { delete(referencingObj); } return; } if (referencingObj instanceof TransformationMappingRoot) { final TransformationMappingRoot mappingRoot = (TransformationMappingRoot)referencingObj; final EObject target = mappingRoot.getTarget(); if (deletedObjects.contains(target)) { delete(referencingObj); return; } boolean doDelete = true; if (mappingRoot.getOutputs() != null && !mappingRoot.getOutputs().isEmpty()) { final Iterator outputs = mappingRoot.getOutputs().iterator(); while (outputs.hasNext() && doDelete) { Object next = outputs.next(); if (next instanceof MappingClass) { MappingClass mc = (MappingClass)next; if (!deletedObjects.contains(mc.getMappingClassSet())) { doDelete = false; } } else if (!deletedObjects.contains(next)) { doDelete = false; } } } if (doDelete) { delete(referencingObj); } return; } if (referencingObj instanceof MappingClassSet) { final MappingClassSet mcSet = (MappingClassSet)referencingObj; final EObject target = mcSet.getTarget(); if (deletedObjects.contains(target)) { delete(referencingObj); } return; } if (referencingObj instanceof Diagram) { final Diagram diagram = (Diagram)referencingObj; final EObject target = diagram.getTarget(); if (deletedObjects.contains(target)) { delete(referencingObj); } return; } if (referencingObj instanceof Mapping) { final Mapping mapping = (Mapping)referencingObj; final Collection outputs = mapping.getOutputs(); final Collection inputs = mapping.getInputs(); // if outputs are empty delete the mapping if (outputs.isEmpty()) { delete(referencingObj); return; } // check if this is X-1 mapping if (outputs.size() == 1) { EObject outputObj = (EObject)outputs.iterator().next(); // if the output is among deleted objects delete mapping if (deletedObjects.contains(outputObj)) { delete(referencingObj); return; } // check this is column - column mapping if (AspectManager.getSqlAspect(outputObj) instanceof SqlColumnAspect) { // no imputs delete the mapping if (inputs.isEmpty()) { delete(referencingObj); return; } // if the input is among deleted objects delete mapping Object inputObj = inputs.iterator().next(); if (deletedObjects.contains(inputObj)) { delete(referencingObj); return; } } } return; } if (referencingObj instanceof SqlAlias) { final SqlAlias sqlAlias = (SqlAlias)referencingObj; final EObject target = sqlAlias.getAliasedObject(); if (deletedObjects.contains(target)) { delete(referencingObj); } return; } return; } protected void delete( EObject eObject ) { // fix for 10856... ensure we don't try to delete same object twice if (deletedObjects.contains(eObject)) { return; } final Command command = ModelEditorImpl.createDeleteCommand(editingDomain, eObject); if (command != null) { this.additionalDeleteCommands.add(command); } // this object needs to further prcessed to check // if any of its references could be deleted, add to list this.objectsForDeleteProcess.addLast(eObject); // Compute the set of deleted objects ... for (final Iterator iter = eObject.eAllContents(); iter.hasNext();) { Object child = iter.next(); if (child instanceof EObject) { if (!deletedObjects.contains(child)) { // process child objects deletion this.objectsForDeleteProcess.addLast(child); deletedObjects.add(child); } } } // Add the deletedObject to the deletedObjects collection deletedObjects.add(eObject); } /** * @return */ public List getAdditionalDeleteCommands() { return additionalDeleteCommands; } public Collection getAllDeletedObjects() { return this.deletedObjects; } }