/* * 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.refactor; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.teiid.core.designer.ModelerCoreException; import org.teiid.designer.core.ModelEditor; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.TransactionRunnable; import org.teiid.designer.core.builder.ModelBuildUtil; import org.teiid.designer.core.container.Container; import org.teiid.designer.core.refactor.RelatedResourceFinder.Relationship; import org.teiid.designer.core.transaction.UnitOfWork; import org.teiid.designer.core.workspace.ModelResource; /** * ObjectDeleteCommand * * @since 8.0 */ public class ObjectDeleteCommand implements RefactorCommand { private static final String PID = ModelerCore.PLUGIN_ID; /** IStatus code indicating that no target Resource has been set for this command before calling canExecute */ private static final int ERROR_MISSING_OBJECT = 3001; private static final int ERROR_MULTIPLE_FILES = 3002; private static final int ERROR_DELETE_NULL = 3003; private static final int EXCEPTION_CALCULATING_DEPENDENCIES = 3004; private static final int EXCEPTION_DURING_DELETE = 3007; private List<EObject> objectsToDelete; private String objectName; private boolean moreThanOneResource = false; private boolean deleteArrayContainsNull = false; private List<IStatus> problems = new ArrayList<IStatus>(); private Collection<IFile> relatedResources; /** * Construct an instance of ResourceRenameCommand. */ public ObjectDeleteCommand() { relatedResources = Collections.emptyList(); } /** * Add a problem to the problems list * * @param problem */ private void addProblem( IStatus problem ) { this.problems.add(problem); } /** * Set the objects to be deleted by this command * * @param objects * */ public void setObjectsToDelete( List<EObject> objects ) { this.objectsToDelete = objects; this.deleteArrayContainsNull = false; this.moreThanOneResource = false; if (objects != null && objects.size() > 0 && objects.get(0) != null) { // get the IResource from the first object and set it on the base class Resource firstResource = objects.get(0).eResource(); // all objects to delete must be in the same resource if (objects.size() > 1) { for (EObject eObject : objects) { if (eObject == null || eObject.eResource() == null) { deleteArrayContainsNull = true; break; } if (!eObject.eResource().equals(firstResource)) { moreThanOneResource = true; break; } } } } // set the object name for labels & messages if (objects.size() == 1) { this.objectName = ModelerCore.getModelEditor().getName(objects.get(0)); } else { this.objectName = ModelerCore.Util.getString("ObjectDeleteCommand.Number_of_objects", objects.size()); //$NON-NLS-1$ } } @Override public String getLabel() { return ModelerCore.Util.getString("ObjectDeleteCommand.delete_label", objectName); //$NON-NLS-1$ } @Override public String getDescription() { Object[] params = new Object[] {objectName}; return ModelerCore.Util.getString("ObjectDeleteCommand.delete_description", params); //$NON-NLS-1$ } @Override public boolean canRedo() { return false; } @Override public boolean canUndo() { return false; } @Override public IStatus canExecute() { IStatus status; String msg; if (this.objectsToDelete == null || this.objectsToDelete.size() == 0) { msg = ModelerCore.Util.getString("ObjectDeleteCommand.No_delete_target_selected"); //$NON-NLS-1$ status = new Status(IStatus.ERROR, PID, ERROR_MISSING_OBJECT, msg, null); return status; } if (this.moreThanOneResource) { msg = ModelerCore.Util.getString("ObjectDeleteCommand.Delete_from_more_than_one_file"); //$NON-NLS-1$ status = new Status(IStatus.ERROR, PID, ERROR_MULTIPLE_FILES, msg, null); return status; } if (this.deleteArrayContainsNull) { msg = ModelerCore.Util.getString("ObjectDeleteCommand.Delete_array_contains_null"); //$NON-NLS-1$ status = new Status(IStatus.ERROR, PID, ERROR_DELETE_NULL, msg, null); return status; } ModelResource modelResource = getModelResource(); if (modelResource == null || modelResource.isReadOnly()) { msg = ModelerCore.Util.getString("ObjectDeleteCommand.Selection_is_read_only"); //$NON-NLS-1$ status = new Status(IStatus.ERROR, PID, ERROR_READONLY_RESOURCE, msg, null); return status; } Object[] params = new Object[] {objectName}; msg = ModelerCore.Util.getString("ObjectDeleteCommand.Ready_to_delete", params); //$NON-NLS-1$ status = new Status(IStatus.OK, PID, CAN_EXECUTE, msg, null); return status; } /** * Get the model resource from the objects being * deleted. Since all objects are in the same resource, * returning the first object's resource is sufficient. */ private ModelResource getModelResource() { ModelEditor editor = ModelerCore.getModelEditor(); ModelResource modelResource = editor.findModelResource(objectsToDelete.get(0)); return modelResource; } /** * Get the resources dependent upon the objects being deleted * * @return collection */ @SuppressWarnings("unchecked") public Set<ModelResource> getDependentResources() { Set<ModelResource> result = new HashSet<ModelResource>(); Collection<Resource> emfResourceList = new HashSet<Resource>(); for (EObject object : objectsToDelete) { try { Collection<EObject> relatedList = ModelerCore.getModelEditor().findOtherObjectsToBeDeleted(object); relatedList = ModelerCore.getModelEditor().findExternalReferencesToObjectsBeingDeleted(object, relatedList); for (EObject dependent : relatedList) { Resource resource = dependent.eResource(); if (!emfResourceList.contains(resource)) { emfResourceList.add(resource); result.add(ModelerCore.getModelEditor().findModelResource(resource)); } } } catch (ModelerCoreException e) { String msg = ModelerCore.Util.getString("ObjectDeleteCommand.Error_attempting_calculate_dependencies", object); //$NON-NLS-1$ addProblem(new Status(IStatus.ERROR, PID, EXCEPTION_CALCULATING_DEPENDENCIES, msg, e)); } } return result; } /** * Check the dependent resources to determine if it is OK to delete the objects. * * TODO * Consider whether it is better to use eObjects to search as in {@link #getDependentResources()} * rather than this implementation which uses the first eObject's resource. * * @return value of severity of problems encountered. */ private int checkDependentResources() { RelatedResourceFinder finder = new RelatedResourceFinder(getModelResource().getResource()); // Determine dependent resource Collection<IFile> searchResults = finder.findRelatedResources(Relationship.DEPENDENT); ResourceStatusList statusList = new ResourceStatusList(searchResults); this.relatedResources = statusList.getResourceList(); this.problems.addAll(statusList.getProblems()); return statusList.getHighestSeverity(); } /** * Calls the appropriate validate method to re-index the related resources. */ private void validateDependentResources() { if (relatedResources.isEmpty()) return; TransactionRunnable runnable = new TransactionRunnable() { @Override public Object run( final UnitOfWork uow ) { Container cont = null; try { cont = ModelerCore.getModelContainer(); } catch (CoreException err) { String msg = ModelerCore.Util.getString("ObjectDeleteCommand.doGetContainerProblemMessage"); //$NON-NLS-1$ ModelerCore.Util.log(IStatus.ERROR, err, msg); } ModelBuildUtil.validateResources(null, relatedResources, cont, false); return null; } }; // Execute the validation within a transaction as this operation may open resources // and create new EObjects try { ModelerCore.getModelEditor().executeAsTransaction(runnable, "Updating ModelIndexes", false, false, this); //$NON-NLS-1$ } catch (CoreException err) { ModelerCore.Util.log(err); } } @Override public IStatus execute( IProgressMonitor monitor ) { problems.clear(); try { String msg = ModelerCore.Util.getString("ObjectDeleteCommand.Execution_complete"); //$NON-NLS-1$ IStatus result = new Status(IStatus.OK, PID, EXECUTE_SUCCEEDED, msg, null); // To check the dependent resources, the index files of all the model resources // in the workspace are to be searched. So, generate the index files. // result = buildIndexes(monitor); if (result.getSeverity() == IStatus.ERROR) { return result; } // check the dependent resources int severity = checkDependentResources(); // see if we should modify the resource if (severity >= IStatus.ERROR) { msg = ModelerCore.Util.getString("ObjectDeleteCommand.Dependent_resource_error"); //$NON-NLS-1$ return new Status(severity, PID, ERROR_READONLY_RESOURCE, msg, null); } // Delete the objects try { ModelerCore.getModelEditor().delete(objectsToDelete, monitor); } catch (ModelerCoreException e) { final Object[] params = new Object[] {objectName}; msg = ModelerCore.Util.getString("ObjectDeleteCommand.Error_attempting_to_delete", params); //$NON-NLS-1$ return new Status(IStatus.ERROR, PID, EXCEPTION_DURING_DELETE, msg, e); } validateDependentResources(); return result; } catch (Exception ex) { return new Status(IStatus.ERROR, PID, ex.getMessage(), ex); } finally { if (monitor != null) monitor.done(); } } @Override public IStatus undo( IProgressMonitor monitor ) { // do nothing - command is not undoable return Status.OK_STATUS; } @Override public IStatus redo( IProgressMonitor monitor ) { // do nothing - command is not redoable return Status.OK_STATUS; } @Override public Collection<Object> getResult() { return Collections.emptyList(); } @Override public Collection<Object> getAffectedObjects() { return Collections.emptyList(); } @Override public Collection<IStatus> getPostExecuteMessages() { return this.problems; } }