blob: c0e8f4d1febfa33af7b47da26dd691e5e3cbffe4 [file] [log] [blame]
/*******************************************************************************
* 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();
}
}