/************************************************************************************* * Copyright (c) 2012 Red Hat, Inc. and others. * 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: * JBoss by Red Hat - Initial implementation. ************************************************************************************/ package org.eclipse.bpmn2.modeler.core.validation; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.eclipse.bpmn2.modeler.core.Activator; import org.eclipse.bpmn2.modeler.core.adapters.ExtendedPropertiesAdapter; import org.eclipse.bpmn2.modeler.core.builder.BPMN2Nature; import org.eclipse.bpmn2.modeler.core.model.Bpmn2ModelerResourceImpl; import org.eclipse.bpmn2.modeler.core.model.Bpmn2ModelerResourceSetImpl; import org.eclipse.bpmn2.modeler.core.model.ProxyURIConverterImplExtension; import org.eclipse.bpmn2.modeler.core.preferences.Bpmn2Preferences; import org.eclipse.bpmn2.modeler.core.runtime.TargetRuntime; import org.eclipse.bpmn2.modeler.core.runtime.TargetRuntimeAdapter; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectNature; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.content.IContentDescription; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EValidator; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.impl.ENotificationImpl; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.validation.marker.MarkerUtil; import org.eclipse.emf.validation.model.EvaluationMode; import org.eclipse.emf.validation.model.IConstraintStatus; import org.eclipse.emf.validation.service.IBatchValidator; import org.eclipse.emf.validation.service.IValidator; import org.eclipse.emf.validation.service.ModelValidationService; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.wst.validation.AbstractValidator; import org.eclipse.wst.validation.ValidationEvent; import org.eclipse.wst.validation.ValidationFramework; import org.eclipse.wst.validation.ValidationResult; import org.eclipse.wst.validation.ValidationState; import org.eclipse.wst.validation.Validator; import org.eclipse.wst.validation.ValidatorMessage; public class BPMN2ProjectValidator extends AbstractValidator { @Override public synchronized ValidationResult validate(ValidationEvent event, ValidationState state, IProgressMonitor monitor) { IResource file = event.getResource(); if ((event.getKind() & IResourceDelta.REMOVED) != 0 || file.isDerived(IResource.CHECK_ANCESTORS) || !(file instanceof IFile)) { return new ValidationResult(); } ValidationResult result = null; IFile modelFile = (IFile) file; try { modelFile.deleteMarkers(null, true, IProject.DEPTH_INFINITE); TargetRuntime runtime = TargetRuntime.getRuntime(new FileEditorInput(modelFile)); Bpmn2ModelerResourceSetImpl rs = new Bpmn2ModelerResourceSetImpl(); runtime.registerExtensionResourceFactory(rs); URI modelUri = URI.createPlatformResourceURI(modelFile.getFullPath().toString(), true); rs.setURIConverter(new ProxyURIConverterImplExtension(modelUri)); Map<Object,Object> options = new HashMap<Object,Object>(); options.put(Bpmn2ModelerResourceSetImpl.OPTION_PROGRESS_MONITOR, monitor); rs.setLoadOptions(options); Resource resource = rs.createResource(modelUri, Bpmn2ModelerResourceImpl.BPMN2_CONTENT_TYPE_ID); TargetRuntimeAdapter.adapt(resource, runtime); resource.load(null); result = new ValidationResult(); if (resource.getContents().isEmpty()) { ValidatorMessage message = ValidatorMessage.create(Messages.BPMN2ProjectValidator_Invalid_File, modelFile); message.setType(runtime.getProblemMarkerId()); result.add(message); } else { IBatchValidator validator = ModelValidationService.getInstance().newValidator(EvaluationMode.BATCH); processStatus(validator.validate(resource.getContents(), monitor), modelFile, result, runtime); } } catch (CoreException e1) { e1.printStackTrace(); } catch (IOException e2) { e2.printStackTrace(); } return result; } public static void validate(IResource resource, IProgressMonitor monitor) { if (isBPMN2File(resource)) { Validator[] validators = ValidationFramework.getDefault().getValidatorsFor(resource); for (Validator v : validators) { if (BPMN2ProjectValidator.class.getName().equals(v.getValidatorClassname())) { v.validate(resource, IResourceDelta.CHANGED, null, monitor); break; } } } } public static void validate(IResourceDelta delta, IProgressMonitor monitor) { IResource resource = delta.getResource(); if (isBPMN2File(resource)) { Validator[] validators = ValidationFramework.getDefault().getValidatorsFor(resource); for (Validator v : validators) { if (BPMN2ProjectValidator.class.getName().equals(v.getValidatorClassname())) { v.validate(resource, delta.getKind(), null, monitor); break; } } } } /** * Perform a "Live" validation of the given model object. Errors resulting * from a Live validation should be cause to consider the model as being * corrupt and should not allow the Resource to be saved in this corrupt * state, until the issues are resolved. * * @param object the model object to validate. * @return an IStatus object indicating the result of the validation. A * status severity of ERROR or higher should be considered reasons * for believing the model is corrupt. */ public static IStatus validateLive(EObject object) { IValidator<Notification> validator = ModelValidationService.getInstance().newValidator(EvaluationMode.LIVE); Notification n = new ENotificationImpl((InternalEObject) object, 0, null, null, null, false); return validator.validate(n); } public static boolean isBPMN2File(IResource resource) { if (resource instanceof IFile) { try { IFile file = (IFile) resource; IContentDescription cd = file.getContentDescription(); if (cd!=null) { return Bpmn2ModelerResourceImpl.BPMN2_CONTENT_TYPE_ID.equals( cd.getContentType().getId()); } String ext = file.getFileExtension(); if ("bpmn".equals(ext) || "bpmn2".equals(ext)) //$NON-NLS-1$ //$NON-NLS-2$ return true; } catch (Exception e) { } } return false; } public static boolean validateOnSave(Resource resource, IProgressMonitor monitor) { boolean needValidation = false; URI uri = resource.getURI(); if (uri.isPlatformResource()) { String pathString = uri.toPlatformString(true); IPath path = Path.fromOSString(pathString); IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path); IProject project = file.getProject(); if (project!=null) { try { // The BPMN2 Project Nature will allow the editor to dynamically // reload configuration files (a.k.a. extensions) from ".bpmn2config" IProjectNature bpmn2Nature = project.getNature(BPMN2Nature.NATURE_ID); Bpmn2Preferences preferences = Bpmn2Preferences.getInstance(project); if (bpmn2Nature==null) { if (preferences.getCheckProjectNature()) { Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); String title = Messages.BPMN2ProjectValidator_Title; String message = NLS.bind( Messages.BPMN2ProjectValidator_No_BPMN2_Project_Nature, project.getName() ); MessageDialogWithToggle result = MessageDialogWithToggle.open( MessageDialog.QUESTION, shell, title, message, Messages.BPMN2ProjectValidator_Dont_Ask_Again, // toggle message false, // toggle state null, // pref store null, // pref key SWT.NONE); if (result.getReturnCode() == IDialogConstants.YES_ID) { BPMN2Nature.setBPMN2Nature(project, true); needValidation = true; } if (result.getToggleState()) { // don't ask again preferences.setCheckProjectNature(false); } } } // The WST Validation Builder is required to do BPMN2 model validation boolean hasWSTBuilder = BPMN2Nature.hasBuilder(project, BPMN2Nature.WST_VALIDATION_BUILDER_ID); if (!hasWSTBuilder) { // if (preferences.getCheckProjectNature()) { Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); String title = Messages.BPMN2ProjectValidator_Title; String message = NLS.bind( Messages.BPMN2ProjectValidator_No_WST_Project_Builder, project.getName() ); boolean result = MessageDialog.open( MessageDialog.QUESTION, shell, title, message, SWT.NONE); if (result) { BPMN2Nature.setBPMN2Nature(project, true); needValidation = true; } } } } catch (CoreException e) { e.printStackTrace(); } } } return needValidation; } public void processStatus(IStatus status, IResource resource, ValidationResult result, TargetRuntime runtime) { if (status.isMultiStatus()) { for (IStatus child : status.getChildren()) { processStatus(child, resource, result, runtime); } } else if (!status.isOK()) { result.add(createValidationMessage(status, resource, runtime)); } } public ValidatorMessage createValidationMessage(IStatus status, IResource resource, TargetRuntime runtime) { ValidatorMessage message = ValidatorMessage.create(status.getMessage(), resource); switch (status.getSeverity()) { case IStatus.INFO: message.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO); break; case IStatus.WARNING: message.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING); break; case IStatus.ERROR: case IStatus.CANCEL: message.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); break; } if (status instanceof IConstraintStatus) { IConstraintStatus ics = (IConstraintStatus) status; EObject object = ics.getTarget(); ExtendedPropertiesAdapter adapter = ExtendedPropertiesAdapter.adapt(object); if (adapter!=null) { Object lineNumber = adapter.getProperty(ExtendedPropertiesAdapter.LINE_NUMBER); if (lineNumber!=null) { message.setAttribute(IMarker.LINE_NUMBER, lineNumber); } } message.setAttribute(EValidator.URI_ATTRIBUTE, EcoreUtil.getURI(object).toString()); message.setAttribute(MarkerUtil.RULE_ATTRIBUTE, ics.getConstraint().getDescriptor().getId()); if (ics.getResultLocus().size() > 0) { StringBuffer relatedUris = new StringBuffer(); for (EObject eobject : ics.getResultLocus()) { relatedUris.append(EcoreUtil.getURI(eobject).toString()).append(" "); //$NON-NLS-1$ } relatedUris.deleteCharAt(relatedUris.length() - 1); String uris = relatedUris.toString(); message.setAttribute(EValidator.RELATED_URIS_ATTRIBUTE, uris); } } message.setType(runtime.getProblemMarkerId()); return message; } @Override public void clean(IProject project, ValidationState state, IProgressMonitor monitor) { super.clean(project, state, monitor); try { project.deleteMarkers(null, false, IProject.DEPTH_INFINITE); } catch (CoreException e) { Activator.getDefault().getLog().log(e.getStatus()); } } }