blob: b7c264399d955c91dd0062cb14c4b221f5f6178f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 Mia-Software.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Bros (Mia-Software)
* Gregoire DUPE (Mia-Software)
*******************************************************************************/
package org.eclipse.gmt.modisco.infra.common.core.internal.validation;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
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.jobs.Job;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EValidator;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.gmt.modisco.infra.common.core.internal.CommonModiscoActivator;
import org.eclipse.gmt.modisco.infra.common.core.internal.Messages;
import org.eclipse.gmt.modisco.infra.common.core.logging.MoDiscoLogger;
import org.eclipse.osgi.util.NLS;
/**
* A job that queues requests for validation, executes the validation after a
* delay, optimizing duplicate validation requests away.
*/
public final class ValidationJob extends Job {
public static final String EMF_PROBLEM_MARKER = "org.eclipse.emf.ecore.diagnostic"; //$NON-NLS-1$
private static final long DELAY = 500;
private static ValidationJob instance;
private final LinkedList<ValidationJob.ValidationItem> queue = new LinkedList<ValidationJob.ValidationItem>();
public interface Callback {
void validated(final boolean result);
}
private ValidationJob() {
super(Messages.ValidationJob_validationJobName);
}
public static synchronized ValidationJob getInstance() {
if (ValidationJob.instance == null) {
ValidationJob.instance = new ValidationJob();
}
return ValidationJob.instance;
}
private static class ValidationItem {
private final Resource resource;
private final IFile file;
private final Callback callback;
public ValidationItem(final Resource resource, final IFile file, final Callback callback) {
this.resource = resource;
this.file = file;
this.callback = callback;
}
public Resource getResource() {
return this.resource;
}
public IResource getFile() {
return this.file;
}
public Callback getCallback() {
return this.callback;
}
@Override
public String toString() {
if (this.file != null) {
return this.file.toString();
}
return this.resource.toString();
}
}
/**
* @param resource
* the resource to validate
* @param file
* the corresponding file
* @param callback
* called when the asynchronous validation is finished (may be
* <code>null</code>)
*/
public void validateAsync(final Resource resource, final IFile file, final Callback callback) {
synchronized (this.queue) {
ListIterator<ValidationJob.ValidationItem> listIterator = this.queue.listIterator();
// remove potential duplicates from the queue
while (listIterator.hasNext()) {
ValidationItem item = listIterator.next();
if (resource != null && resource.equals(item.getResource())) {
listIterator.remove();
}
}
this.queue.addLast(new ValidationItem(resource, file, callback));
}
cancel();
setRule(ResourcesPlugin.getWorkspace().getRoot());
setPriority(Job.DECORATE);
schedule(ValidationJob.DELAY);
}
/**
* @param resource
* the resource to validate
* @param file
* the corresponding file
*/
public boolean validateSync(final Resource resource, final IFile file) {
synchronized (this.queue) {
ListIterator<ValidationJob.ValidationItem> listIterator = this.queue.listIterator();
// remove this element from the queue if it's there, since it is
// going to be handled right now
while (listIterator.hasNext()) {
ValidationItem item = listIterator.next();
if (resource != null && resource.equals(item.getResource())) {
listIterator.remove();
}
}
}
return validateItem(new ValidationItem(resource, file, null));
}
@Override
protected IStatus run(final IProgressMonitor monitor) {
while (true) {
ValidationItem validationItem;
synchronized (this.queue) {
if (this.queue.isEmpty()) {
break;
}
validationItem = this.queue.removeFirst();
}
boolean result = validateItem(validationItem);
Callback callback = validationItem.getCallback();
if (callback != null) {
callback.validated(result);
}
}
return Status.OK_STATUS;
}
private synchronized boolean validateItem(final ValidationItem validationItem) {
boolean result = true;
final List<Diagnostic> diagnostics = new ArrayList<Diagnostic>();
Resource resource = validationItem.getResource();
List<EObject> contents;
synchronized (resource) {
contents = new ArrayList<EObject>(resource.getContents());
}
for (EObject eObject : contents) {
Diagnostic diagnostic = Diagnostician.INSTANCE.validate(eObject);
if (diagnostic.getSeverity() != Diagnostic.OK) {
diagnostics.add(diagnostic);
result &= (diagnostic.getSeverity() != Diagnostic.ERROR);
}
}
if (validationItem.getFile() != null) {
ValidationJob.printWSProblems(validationItem.getFile(), diagnostics);
} else {
ValidationJob.printPluginProblem(validationItem.getResource(), diagnostics);
}
return result;
}
private static void printPluginProblem(final Resource resource,
final List<Diagnostic> diagnostics) {
for (Diagnostic diagnostic : diagnostics) {
ValidationJob.convertDiagnosticToLog(resource, diagnostic);
}
}
private static void printWSProblems(final IResource file, final List<Diagnostic> diagnostics) {
if (file == null) {
throw new IllegalArgumentException("null file"); //$NON-NLS-1$
}
WorkspaceJob job = new WorkspaceJob(NLS.bind(
Messages.ValidationJob_creatingValidationMarkersJobName, file.getFullPath())) {
@Override
public IStatus runInWorkspace(final IProgressMonitor monitor) {
if (file.exists()) {
try {
file.deleteMarkers(ValidationJob.EMF_PROBLEM_MARKER, true,
IResource.DEPTH_INFINITE);
} catch (CoreException e) {
MoDiscoLogger.logError(e,
"Failed to delete marker for: " + file.getFullPath(), //$NON-NLS-1$
CommonModiscoActivator.getDefault());
}
for (Diagnostic diagnostic : diagnostics) {
try {
ValidationJob.convertDiagnosticToMarker(file, diagnostic);
} catch (CoreException e) {
MoDiscoLogger.logError(e, null);
}
}
}
return Status.OK_STATUS;
}
};
job.setRule(file.getProject());
job.schedule();
}
/** Log messages corresponding to the given diagnostic and its children. */
static void convertDiagnosticToLog(final Resource resource, final Diagnostic diagnostic) {
if (diagnostic.getChildren().size() > 0) {
for (Diagnostic subDiagnostic : diagnostic.getChildren()) {
ValidationJob.convertDiagnosticToLog(resource, subDiagnostic);
}
} else {
String diagnosticMessage = "The following problem has been found while validating " //$NON-NLS-1$
+ resource.getURI().toString() + " : " + diagnostic.getMessage(); //$NON-NLS-1$
if (diagnostic.getSeverity() == Diagnostic.ERROR) {
MoDiscoLogger.logError(diagnosticMessage, CommonModiscoActivator.getDefault());
} else if (diagnostic.getSeverity() == Diagnostic.WARNING) {
MoDiscoLogger.logWarning(diagnosticMessage, CommonModiscoActivator.getDefault());
} else if (diagnostic.getSeverity() == Diagnostic.INFO) {
MoDiscoLogger.logInfo(diagnosticMessage, CommonModiscoActivator.getDefault());
}
}
}
/**
* Creates markers on the given file corresponding to the given diagnostic
* and its children.
*/
static void convertDiagnosticToMarker(final IResource file, final Diagnostic diagnostic)
throws CoreException {
if (diagnostic.getChildren().size() > 0) {
for (Diagnostic subDiagnostic : diagnostic.getChildren()) {
ValidationJob.convertDiagnosticToMarker(file, subDiagnostic);
}
} else {
IMarker marker = file.createMarker(ValidationJob.EMF_PROBLEM_MARKER);
marker.setAttribute(IMarker.MESSAGE, diagnostic.getMessage());
if (diagnostic.getSeverity() == Diagnostic.ERROR) {
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
} else if (diagnostic.getSeverity() == Diagnostic.WARNING) {
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING);
} else if (diagnostic.getSeverity() == Diagnostic.INFO) {
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);
}
marker.setAttribute(IMarker.LOCATION, diagnostic.getSource());
List<?> data = diagnostic.getData();
if (data.size() > 0) {
Object object = data.get(0);
if (object instanceof EObject) {
EObject target = (EObject) object;
marker.setAttribute(EValidator.URI_ATTRIBUTE, EcoreUtil.getURI(target)
.toString());
}
}
}
}
@Override
public boolean belongsTo(final Object family) {
return family == ResourcesPlugin.FAMILY_MANUAL_BUILD
|| family == ResourcesPlugin.FAMILY_AUTO_BUILD
|| family == ResourcesPlugin.FAMILY_MANUAL_REFRESH;
}
}