blob: fd2da04ea27e3ed98ee01526972365f77589ad11 [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2008-2013 See4sys, itemis and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* See4sys - Initial API and implementation
* itemis - [418005] Add support for model files with multiple root elements
*
* </copyright>
*/
package org.eclipse.sphinx.emf.validation.ui.actions;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceRuleFactory;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.Assert;
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.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.MultiRule;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.provider.IWrapperItemProvider;
import org.eclipse.emf.edit.ui.EMFEditUIPlugin;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.sphinx.emf.util.EcorePlatformUtil;
import org.eclipse.sphinx.emf.validation.diagnostic.ExtendedDiagnostician;
import org.eclipse.sphinx.emf.validation.markers.ValidationMarkerManager;
import org.eclipse.sphinx.emf.validation.stats.ValidationPerformanceStats;
import org.eclipse.sphinx.emf.validation.ui.SphinxValidationUiActivator;
import org.eclipse.sphinx.emf.validation.ui.util.DiagnosticUI;
import org.eclipse.sphinx.emf.validation.ui.util.Messages;
import org.eclipse.sphinx.platform.util.ExtendedPlatform;
import org.eclipse.sphinx.platform.util.PlatformLogUtil;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.BaseSelectionListenerAction;
import org.eclipse.ui.actions.WorkspaceModifyDelegatingOperation;
/**
* Basic implementation of validate action. A <em>Validate</em> action is supposed to ask for the validation of a
* selected model, <em>i.e.</em> for verifying constraints that are applicable to the selected model's objects.
* <p>
* <table>
* <tr valign=top>
* <td><b>Note</b>&nbsp;&nbsp;</td>
* <td>Action's enablement is not computed by action itself. This is due to an optimization that has been made at the
* parent action provider level; indeed
* {@linkplain org.eclipse.sphinx.emf.validation.ui.actions.providers.BasicValidationActionProvider
* BasicValidationActionProvider} computes enablement (for all actions it owns) only once.</td>
* </tr>
* </table>
*/
public class BasicValidateAction extends BaseSelectionListenerAction {
private boolean displayBriefReport = false;
public BasicValidateAction() {
super(Messages._UI_Validate_menu_item);
setDescription(Messages._UI_Validate_simple_description);
}
@Override
public void run() {
final List<EObject> selectedModelObjects = getSelectedModelObjects();
final Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
IRunnableWithProgress runnableWithProgress = new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor progressMonitor) throws InvocationTargetException, InterruptedException {
try {
// FIXME Shouldn't this profiling be started and stopped inside 'asyncExec'?
ValidationPerformanceStats.INSTANCE.openContext("Validation of " + selectedModelObjects.get(0));
final List<Diagnostic> diagnostics = validateMulti(selectedModelObjects, progressMonitor);
shell.getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (progressMonitor.isCanceled()) {
handleDiagnostic(selectedModelObjects, Diagnostic.CANCEL_INSTANCE);
} else if (diagnostics != null) {
handleDiagnosticMulti(selectedModelObjects, diagnostics, displayBriefReport);
}
try {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
.showView("org.eclipse.sphinx.examples.validation.ui.views.validation"); //$NON-NLS-1$
} catch (PartInitException ex) {
// Fail silent
}
}
});
ValidationPerformanceStats.INSTANCE.closeAndLogCurrentContext();
} finally {
progressMonitor.done();
}
}
};
try {
// This runs the operation, and shows progress.
// (It appears to be a bad thing to fork this onto another thread.)
new ProgressMonitorDialog(shell).run(true, true, new WorkspaceModifyDelegatingOperation(runnableWithProgress));
} catch (Exception exception) {
EMFEditUIPlugin.INSTANCE.log(exception);
}
}
protected void handleDiagnostic(List<EObject> selectedModelObjects, Diagnostic diagnostic) {
handleDiagnosticMulti(selectedModelObjects, Collections.singletonList(diagnostic), displayBriefReport);
}
protected List<Diagnostic> validateMulti(List<EObject> selectedModelObjects, IProgressMonitor progressMonitor) {
ArrayList<Diagnostic> result = new ArrayList<Diagnostic>();
ExtendedDiagnostician diagnostician = new ExtendedDiagnostician();
if (selectedModelObjects.size() == 1) {
EObject eObject = selectedModelObjects.get(0);
if (eObject != null) {
int count = getNumberOfObject(eObject);
// ValidationPerformanceStats.INSTANCE.startEvent(enumerator, blameObject);
progressMonitor.beginTask("", count); //$NON-NLS-1$
progressMonitor.setTaskName(EMFEditUIPlugin.INSTANCE.getString("_UI_Validating_message", //$NON-NLS-1$
new Object[] { diagnostician.getObjectLabel(eObject) }));
ValidationPerformanceStats.INSTANCE.startNewEvent(ValidationPerformanceStats.ValidationEvent.EVENT_APPLY_CONSTRAINTS,
eObject.toString());
diagnostician.setProgressMonitor(progressMonitor);
result.add(diagnostician.validate(eObject));
ValidationPerformanceStats.INSTANCE.endEvent(ValidationPerformanceStats.ValidationEvent.EVENT_APPLY_CONSTRAINTS, eObject.toString());
diagnostician.setProgressMonitor(null);
progressMonitor.done();
}
return result;
}
else if (selectedModelObjects.size() > 1) {
int count = 0;
int[] subCount = new int[selectedModelObjects.size()];
int cptObject = 0;
for (EObject object : selectedModelObjects) {
subCount[cptObject] = getNumberOfObject(object);
count += subCount[cptObject++];
}
progressMonitor.beginTask("", count); //$NON-NLS-1$
progressMonitor.setTaskName(Messages._UI_progressBar_InitialMsg);
boolean isProgressMonitor = false;
diagnostician.setProgressMonitor(progressMonitor);
isProgressMonitor = true;
Diagnostic diag = null;
cptObject = 0;
int[] nbE = { 0, 0, 0 }; // Error, Warning, Info
final int ERRIdx = 0;
final int WARNIdx = 1;
final int INFOIdx = 2;
for (Object current : selectedModelObjects) {
if (progressMonitor.isCanceled()) {
break;
}
IProgressMonitor subMonitor = null;
if (isProgressMonitor) {
subMonitor = new SubProgressMonitor(progressMonitor, subCount[cptObject++]);
subMonitor.subTask(NLS.bind(Messages._UI_subValidationMonitorIntro, EcorePlatformUtil.getFile((EObject) current).getName()));
}
ValidationPerformanceStats.INSTANCE.startNewEvent(ValidationPerformanceStats.ValidationEvent.EVENT_APPLY_CONSTRAINTS,
current.toString());
diag = diagnostician.validate((EObject) current);
if (diag != null) {
result.add(diag);
for (Diagnostic c : diag.getChildren()) {
switch (c.getSeverity()) {
case Diagnostic.ERROR:
nbE[ERRIdx]++;
break;
case Diagnostic.WARNING:
nbE[WARNIdx]++;
break;
case Diagnostic.INFO:
nbE[INFOIdx]++;
break;
default: // do nothing
}
}
}
if (subMonitor != null) {
subMonitor.done();
}
ValidationPerformanceStats.INSTANCE.endEvent(ValidationPerformanceStats.ValidationEvent.EVENT_APPLY_CONSTRAINTS, current.toString());
progressMonitor.setTaskName(NLS.bind(Messages._UI_progressBarMulti_ErrWarnInfo, new Object[] { nbE[ERRIdx], nbE[WARNIdx],
nbE[INFOIdx] }));
}
diagnostician.setProgressMonitor(null);
progressMonitor.done();
return result;
}
PlatformLogUtil.logAsWarning(SphinxValidationUiActivator.getDefault(), new RuntimeException("Cannot perform validation on empty element selection.")); //$NON-NLS-1$
return null;
}
protected void handleDiagnosticMulti(final List<EObject> selectedModelObjects, final List<Diagnostic> diagnostics, boolean showBriefReport) {
Assert.isNotNull(diagnostics);
ValidationPerformanceStats.INSTANCE.startNewEvent(ValidationPerformanceStats.ValidationEvent.EVENT_UPDATE_PROBLEM_MARKERS, "UpdateMarkers");
// Optionally show validation results in a Pop-up window
if (showBriefReport) {
DiagnosticUI.showDiagnostic(diagnostics);
}
// On a second and, let's create markers
WorkspaceJob job = new WorkspaceJob(Messages._Job_HandleDiagnostic) {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
for (Diagnostic diag : diagnostics) {
ValidationMarkerManager.getInstance().handleDiagnostic(diag);
}
return Status.OK_STATUS;
}
};
ArrayList<ISchedulingRule> myRules = new ArrayList<ISchedulingRule>();
for (EObject eObject : selectedModelObjects) {
IResource r = EcorePlatformUtil.getFile(eObject);
if (r != null) {
IResourceRuleFactory ruleFactory = r.getWorkspace().getRuleFactory();
myRules.add(ruleFactory.modifyRule(r));
myRules.add(ruleFactory.createRule(r));
}
}
job.setRule(new MultiRule(myRules.toArray(new ISchedulingRule[myRules.size()])));
job.setPriority(Job.BUILD);
job.schedule();
ValidationPerformanceStats.INSTANCE.endEvent(ValidationPerformanceStats.ValidationEvent.EVENT_UPDATE_PROBLEM_MARKERS, "UpdateMarkers");
}
/**
* For progress bar, useful method which return number of Object to validate into model
*
* @param eObject
* @return number of Object which will be validate
*/
protected int getNumberOfObject(EObject eObject) {
int count = 0;
for (Iterator<?> i = eObject.eAllContents(); i.hasNext(); i.next()) {
++count;
}
return count;
}
/**
* Due to performance overhead, its just called before running the action to initialize the list of selected model
* objects.
*
* @param selection
* the current selection
*/
private List<EObject> getSelectedModelObjects() {
// Just retrieve the selection that has been given to this action by the parent action provider
IStructuredSelection selection = getStructuredSelection();
List<EObject> result = new ArrayList<EObject>();
List<IFile> files = new ArrayList<IFile>();
for (Object selectedObject : selection.toList()) {
if (selectedObject instanceof IProject) {
IProject project = (IProject) selectedObject;
if (project.isAccessible()) {
files.addAll(ExtendedPlatform.getAllFiles((IProject) selectedObject, true));
}
} else if (selectedObject instanceof IFolder) {
IFolder folder = (IFolder) selectedObject;
if (folder.isAccessible()) {
files.addAll(ExtendedPlatform.getAllFiles((IFolder) selectedObject));
}
} else if (selectedObject instanceof IFile) {
IFile file = (IFile) selectedObject;
if (file.isAccessible()) {
files.add((IFile) selectedObject);
}
} else if (selectedObject instanceof EObject) {
result.add((EObject) selectedObject);
} else if (selectedObject instanceof IWrapperItemProvider) {
Object object = AdapterFactoryEditingDomain.unwrap(selectedObject);
if (object instanceof EObject) {
result.add((EObject) object);
}
}
}
if (!files.isEmpty()) {
// If selected object is a file, get the mapped model root
for (IFile file : files) {
// Get model objects from workspace file
Resource resource = EcorePlatformUtil.getResource(file);
if (resource != null) {
result.addAll(resource.getContents());
}
}
}
return result;
}
}