/******************************************************************************* * Copyright (c) 2010 SAP AG. * 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: * Emil Simeonov - initial API and implementation. * Dimitar Donchev - initial API and implementation. * Dimitar Tenev - initial API and implementation. * Nevena Manova - initial API and implementation. * Georgi Konstantinov - initial API and implementation. *******************************************************************************/ package org.eclipse.wst.sse.sieditor.model.validation; 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.runtime.ILog; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EContentAdapter; import org.eclipse.emf.transaction.TransactionalEditingDomain.Lifecycle; import org.eclipse.emf.transaction.util.Adaptable; 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.IConstraintFilter; import org.eclipse.emf.validation.service.ILiveValidator; import org.eclipse.emf.validation.service.ModelValidationService; import org.eclipse.emf.workspace.WorkspaceEditingDomainFactory; import org.eclipse.swt.widgets.Display; import org.eclipse.wst.wsdl.Definition; import org.eclipse.wst.wsdl.util.WSDLResourceImpl; import org.eclipse.xsd.XSDConcreteComponent; import org.eclipse.xsd.XSDDiagnostic; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.util.XSDResourceImpl; import org.eclipse.wst.sse.sieditor.core.common.IDisposable; import org.eclipse.wst.sse.sieditor.core.common.Logger; import org.eclipse.wst.sse.sieditor.model.Activator; import org.eclipse.wst.sse.sieditor.model.api.IModelObject; import org.eclipse.wst.sse.sieditor.model.api.IModelRoot; import org.eclipse.wst.sse.sieditor.model.validation.impl.ValidationEvent; import org.eclipse.wst.sse.sieditor.model.validation.impl.ValidationStatus; import org.eclipse.wst.sse.sieditor.model.validation.impl.ValidationStatusRegistry; import org.eclipse.wst.sse.sieditor.model.validation.impl.XSDDiagnosticValidationStatus; public class ValidationService extends EContentAdapter implements IValidationService, IDisposable { public static final String OPTION_BATCH_VALIDTION = "batch_validation"; //$NON-NLS-1$ private final ValidationStatusRegistry registry; private final List<IModelAdapter> adapters; private final List<IValidationListener> listeners; protected ILiveValidator liveValidator; protected IBatchValidator batchValidator; private EditingDomainListener editingDomainListener; private ResourceSet resourceSet; private static final ILog logger = Activator.getDefault().getLog(); private final boolean logEnabled; public ValidationService(final ResourceSet resourceSet, final IModelRoot modelRoot) { super(); registry = new ValidationStatusRegistry(); adapters = new ArrayList<IModelAdapter>(); listeners = new ArrayList<IValidationListener>(); liveValidator = ModelValidationService.getInstance().newValidator(EvaluationMode.LIVE); liveValidator.setReportSuccesses(true); batchValidator = ModelValidationService.getInstance().newValidator(EvaluationMode.BATCH); batchValidator.setReportSuccesses(true); batchValidator.setIncludeLiveConstraints(true); update(resourceSet, modelRoot); logEnabled = System.getProperty("validationLogging") != null; //$NON-NLS-1$ } public void update(final ResourceSet resourceSet, final IModelRoot modelRoot) { detachFromCurrentResourceSet(); final Adaptable editingDomainAdaptable = (Adaptable) WorkspaceEditingDomainFactory.INSTANCE.getEditingDomain(resourceSet); final Lifecycle lifecycle = editingDomainAdaptable.getAdapter(Lifecycle.class); editingDomainListener = new EditingDomainListener(this); lifecycle.addTransactionalEditingDomainListener(editingDomainListener); resourceSet.eAdapters().add(this); this.resourceSet = resourceSet; } private void detachFromCurrentResourceSet() { if (resourceSet == null) { return; } final Adaptable editingDomainAdaptable = (Adaptable) WorkspaceEditingDomainFactory.INSTANCE.getEditingDomain(resourceSet); if (editingDomainAdaptable != null) { final Lifecycle lifecycle = editingDomainAdaptable.getAdapter(Lifecycle.class); lifecycle.removeTransactionalEditingDomainListener(editingDomainListener); resourceSet.eAdapters().remove(this); } } @Override public Set<IModelObject> validate(final Object source) { final EObject obj; if (source instanceof EObject) { obj = (EObject) source; } else { obj = adaptToEMFObject(source); } if (obj == null) { return Collections.emptySet(); } try { registry.clearSchemaOrDefinition(obj); final IStatus result = batchValidator.validate(obj); return processValidationResults(result, EvaluationMode.BATCH); } catch (final Exception e) { Logger.log(Activator.PLUGIN_ID, IStatus.WARNING, "Can not validate: " + obj.toString(), e); //$NON-NLS-1$ } return Collections.emptySet(); } public Set<IModelObject> validateAll(final Collection<?> sources) { final Set<IStatus> statuses = new HashSet<IStatus>(); for (final Object source : sources) { final EObject obj; if (source instanceof EObject) { obj = (EObject) source; } else { obj = adaptToEMFObject(source); } if (obj == null) { return Collections.emptySet(); } try { registry.clearSchemaOrDefinition(obj); statuses.add(batchValidator.validate(obj)); } catch (final Exception e) { Logger.log(Activator.PLUGIN_ID, IStatus.WARNING, "Can not validate: " + obj.toString(), e); //$NON-NLS-1$ } } return processValidationResults(new MultiStatus(Activator.PLUGIN_ID, IStatus.INFO, statuses.toArray(new IStatus[statuses .size()]), "This is a multistatus. Check children", null), EvaluationMode.BATCH); //$NON-NLS-1$ } protected boolean validateDescription(final EObject eObject) { final Resource resource = eObject.eResource(); if (resource != null && isWSDLResource(resource)) { validate(getDefinitionFromResource(resource)); return true; } return false; } protected boolean isWSDLResource(final Resource resource) { return resource instanceof WSDLResourceImpl; } protected Definition getDefinitionFromResource(final Resource resource) { return ((WSDLResourceImpl) resource).getDefinition(); } public void liveValidate(final Collection<Notification> notifications) { final Set<XSDSchema> schemas = new HashSet<XSDSchema>(); boolean descriptionValidated = false; boolean descriptionNeedsValidation = false; for (final Notification notification : notifications) { Object notifier = notification.getNotifier(); if (notifier instanceof WSDLResourceImpl) { notifier = ((WSDLResourceImpl) notifier).getDefinition(); } else if (notifier instanceof XSDResourceImpl) { notifier = ((XSDResourceImpl) notifier).getSchema(); } if (notifier instanceof XSDConcreteComponent) { final XSDSchema schema = ((XSDConcreteComponent) notifier).getSchema(); if (schemas.contains(schema) || schema == null) { continue; } if (schema != null) { registry.clearSchemaOrDefinition(schema); schemas.add(schema); } liveValidate(notification); descriptionNeedsValidation = true; } else if (!(notifier instanceof EObject)) { continue; } else { descriptionNeedsValidation = true; } if (!descriptionValidated && descriptionNeedsValidation) { descriptionValidated = validateDescription((EObject) notifier); } } } protected void liveValidate(final Notification notification) { processValidationResults(liveValidator.validate(notification), EvaluationMode.LIVE); } @Override public void notifyChanged(final Notification notification) { super.notifyChanged(notification); } public void addConstraintFilter(final IConstraintFilter constraintFilter) { liveValidator.addConstraintFilter(constraintFilter); batchValidator.addConstraintFilter(constraintFilter); } public void addModelAdapter(final IModelAdapter modelAdapter) { if (!adapters.contains(modelAdapter)) { adapters.add(modelAdapter); } } public void removeConstraintFilter(final IConstraintFilter constraintFilter) { liveValidator.removeConstraintFilter(constraintFilter); batchValidator.removeConstraintFilter(constraintFilter); } public void removeModelAdapter(final IModelAdapter modelAdapter) { adapters.remove(modelAdapter); } public void addValidationListener(final IValidationListener listener) { if (!listeners.contains(listener)) { listeners.add(listener); } } public List<IValidationListener> getValidationListeners() { return Collections.unmodifiableList(listeners); } public Collection<IModelAdapter> getModelAdapters() { return adapters; } public void removeValidationListener(final IValidationListener listener) { listeners.remove(listener); } public IValidationStatusProvider getValidationStatusProvider() { return registry; } private Set<IModelObject> processValidationResults(final IStatus status, final EvaluationMode<?> mode) { final List<IValidationStatus> validationStatusList = new ArrayList<IValidationStatus>(); processStatus(status, validationStatusList); if (validationStatusList != null && !validationStatusList.isEmpty()) { final Set<IModelObject> updatedModelObjects = registry.add(validationStatusList); notifyListeners(updatedModelObjects, mode); return updatedModelObjects; } return Collections.emptySet(); } private void processStatus(final IStatus status, final List<IValidationStatus> validationStatusList) { if (status.isMultiStatus()) { final IStatus[] children = status.getChildren(); for (final IStatus subStatus : children) { if (subStatus.isMultiStatus()) { processStatus(subStatus, validationStatusList); continue; } processSingleValidationStatus(subStatus, validationStatusList); } } else { processSingleValidationStatus(status, validationStatusList); } } private void processSingleValidationStatus(final IStatus status, final List<IValidationStatus> validationStatusList) { if (logEnabled && !status.isOK()) { logger.log(status); } if (!(status instanceof IConstraintStatus)) { return; } final EObject constraintStatusTarget = ((IConstraintStatus)status).getTarget(); final Collection<IModelObject> modelObjects = adaptToModelObject(constraintStatusTarget); for (final IModelObject modelObject : modelObjects) { if (constraintStatusTarget instanceof XSDDiagnostic && !status.isOK()) { validationStatusList.add(new XSDDiagnosticValidationStatus((IConstraintStatus)status, modelObject)); } else { validationStatusList.add(new ValidationStatus((IConstraintStatus)status, modelObject)); } } } private EObject adaptToEMFObject(final Object source) { for (final IModelAdapter modelAdapter : adapters) { final EObject eObject = modelAdapter.adapatToEMF(source); if (eObject != null) { return eObject; } } return null; } public Set<IModelObject> adaptToModelObject(final EObject source) { final Set<IModelObject> modelObjects = new HashSet<IModelObject>(); for (final IModelAdapter modelAdapter : adapters) { final IModelObject modelObject = (IModelObject) modelAdapter.adaptToModelObject(source); if (modelObject != null) { modelObjects.add(modelObject); } } return modelObjects; } private void notifyListeners(final Set<IModelObject> modelObjects, final EvaluationMode<?> mode) { final int intMode = (mode == EvaluationMode.LIVE) ? IValidationEvent.LIVE_MODE : IValidationEvent.BATCH_MODE; for (final IValidationListener listener : listeners) { final Set<Object> supportedObjects = new HashSet<Object>(); for (final Object modelObject : modelObjects) { if (listener.isSupportedModelObject(modelObject)) { supportedObjects.add(modelObject); } } final ValidationEvent event = new ValidationEvent(registry, supportedObjects, intMode); if (Display.getCurrent() != null) { listener.validationPerformed(event); } else { Display.getDefault().asyncExec(new Runnable() { public void run() { listener.validationPerformed(event); } }); } } } @Override public void doDispose() { detachFromCurrentResourceSet(); } }