| //------------------------------------------------------------------------------ |
| // Copyright (c) 2005, 2006 IBM Corporation 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: |
| // IBM Corporation - initial implementation |
| //------------------------------------------------------------------------------ |
| package org.eclipse.epf.validation; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.emf.common.notify.AdapterFactory; |
| import org.eclipse.emf.common.util.BasicDiagnostic; |
| import org.eclipse.emf.common.util.DiagnosticChain; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EDataType; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.util.EObjectValidator; |
| import org.eclipse.emf.edit.provider.AdapterFactoryTreeIterator; |
| import org.eclipse.emf.edit.provider.ITreeItemContentProvider; |
| import org.eclipse.emf.validation.model.Category; |
| 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.IConstraintDescriptor; |
| import org.eclipse.emf.validation.service.IConstraintFilter; |
| import org.eclipse.emf.validation.service.ModelValidationService; |
| import org.eclipse.epf.common.utils.StrUtil; |
| import org.eclipse.epf.library.edit.IAdapterFactoryProvider; |
| import org.eclipse.epf.library.edit.process.DescribableElementWrapperItemProvider; |
| import org.eclipse.epf.library.edit.process.IBSItemProvider; |
| import org.eclipse.epf.library.edit.util.TngUtil; |
| import org.eclipse.epf.library.edit.validation.DependencyChecker; |
| import org.eclipse.epf.library.edit.validation.DependencyValidationMgr; |
| import org.eclipse.epf.library.edit.validation.IValidator; |
| import org.eclipse.epf.library.edit.validation.IValidatorFactory; |
| import org.eclipse.epf.uma.BreakdownElement; |
| import org.eclipse.epf.uma.DescribableElement; |
| import org.eclipse.epf.uma.MethodElement; |
| import org.eclipse.epf.uma.MethodPlugin; |
| import org.eclipse.epf.uma.NamedElement; |
| import org.eclipse.epf.uma.Process; |
| import org.eclipse.epf.uma.ProcessComponent; |
| import org.eclipse.epf.uma.RoleDescriptor; |
| import org.eclipse.epf.uma.TeamProfile; |
| import org.eclipse.epf.uma.UmaPackage; |
| import org.eclipse.epf.uma.VariabilityElement; |
| import org.eclipse.epf.uma.WorkProductDescriptor; |
| import org.eclipse.epf.validation.constraints.LibraryTraversalStrategy; |
| import org.eclipse.epf.validation.util.ValidationStatus; |
| |
| /** |
| * An adapter that plugs the EMF Model Validation Service API into the |
| * {@link org.eclipse.emf.ecore.EValidator} API. |
| * |
| * @author Phong Nguyen Le |
| * @since 1.1 |
| */ |
| public class LibraryEValidator extends EObjectValidator { |
| private static final boolean DEBUG = Activator.getDefault().isDebugging(); |
| private static final Map featureToPropertyMap = createFeatureToAttributeMap(); |
| |
| public static final String CTX_ADAPTER_FACTORY_PROVIDER = "CTX_ADAPTER_FACTORY_PROVIDER"; //$NON-NLS-1$ |
| public static final String CTX_DEPENDENCY_VALIDATION_MGR = "CTX_DEPENDENCY_VALIDATION_MGR"; //$NON-NLS-1$ |
| public static final String CTX_CONSTRAINT_FILTER = "CTX_CONSTRAINT_FILTER"; //$NON-NLS-1$ |
| |
| public static final String CONSTRAINT_CATEGORY = "org.eclipse.epf.validation.library.category"; //$NON-NLS-1$ |
| |
| private static Map createFeatureToAttributeMap() { |
| Map map = new HashMap(); |
| map.put(UmaPackage.Literals.NAMED_ELEMENT__NAME, IBSItemProvider.COL_NAME); |
| map.put(UmaPackage.Literals.METHOD_ELEMENT__PRESENTATION_NAME, IBSItemProvider.COL_PRESENTATION_NAME); |
| return map; |
| } |
| |
| /** |
| * Model Validation Service interface for batch validation of EMF elements. |
| */ |
| private final IBatchValidator batchValidator; |
| |
| /** |
| * Initializes me. |
| */ |
| public LibraryEValidator() { |
| super(); |
| |
| batchValidator = (IBatchValidator) ModelValidationService.getInstance() |
| .newValidator(EvaluationMode.BATCH); |
| batchValidator.setIncludeLiveConstraints(true); |
| batchValidator.setReportSuccesses(false); |
| batchValidator.addConstraintFilter(new IConstraintFilter() { |
| |
| public boolean accept(IConstraintDescriptor constraint, |
| EObject target) { |
| Set<Category> categories = constraint.getCategories(); |
| if(categories != null && !categories.isEmpty()) { |
| for (Iterator<Category> iterator = categories.iterator(); iterator |
| .hasNext();) { |
| Category category = (Category) iterator.next(); |
| if(CONSTRAINT_CATEGORY.equals(category.getId())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| }); |
| } |
| |
| public boolean validate(EObject eObject, DiagnosticChain diagnostics, |
| Map context) { |
| return validate(eObject.eClass(), eObject, diagnostics, context); |
| } |
| |
| /** |
| * Implements validation by delegation to the EMF validation framework. |
| */ |
| public boolean validate(EClass eClass, EObject eObject, |
| DiagnosticChain diagnostics, Map context) { |
| // first, do whatever the basic EcoreValidator does |
| // super.validate(eClass, eObject, diagnostics, context); |
| |
| IStatus status = Status.OK_STATUS; |
| |
| boolean circularCheckOk = true; |
| DependencyValidationMgr mgr = (DependencyValidationMgr) context.get(CTX_DEPENDENCY_VALIDATION_MGR); |
| if (DependencyChecker.newCheck && mgr != null && |
| (eObject instanceof VariabilityElement || eObject instanceof MethodPlugin)) { |
| status = mgr.checkCircularDependnecy((MethodElement) eObject); |
| if(!status.isOK()) { |
| circularCheckOk = false; |
| String msg = status.getMessage(); |
| if(StrUtil.isBlank(msg)) { |
| //msg = NLS.bind(ValidationResources.circularDependency_error, ref.getEContainingClass().getName(), ref.getName()); |
| msg = ValidationResources.circularDependency_error; |
| } |
| appendDiagnostics(status, diagnostics); |
| } |
| } |
| |
| // no point in validating if we can't report results |
| if (diagnostics != null) { |
| // if EMF Mode Validation Service already covered the sub-tree, |
| // which it does for efficient computation and error reporting, |
| // then don't repeat (the Diagnostician does the recursion |
| // externally). If there is no context map, then we can't |
| // help it |
| if (!hasProcessed(eObject, context)) { |
| if (getTraversalType(context) == LibraryTraversalStrategy.DEEP) { |
| batchValidator |
| .setTraversalStrategy(LibraryTraversalStrategy.deepTraversalStrategy); |
| } |
| |
| IConstraintFilter filter = (IConstraintFilter) context.get(CTX_CONSTRAINT_FILTER); |
| if(filter != null) { |
| batchValidator.addConstraintFilter(filter); |
| } |
| try { |
| status = batchValidator.validate(eObject, |
| new NullProgressMonitor()); |
| } finally { |
| if (filter != null) { |
| batchValidator.removeConstraintFilter(filter); |
| } |
| } |
| |
| processed(eObject, context, status); |
| |
| appendDiagnostics(status, diagnostics); |
| } |
| } |
| |
| if (eObject instanceof ProcessComponent) { |
| Process proc = ((ProcessComponent) eObject).getProcess(); |
| if (proc != null) { |
| validateProcess(proc, diagnostics, context); |
| } |
| } |
| |
| return circularCheckOk && status.isOK(); |
| } |
| |
| /** |
| * @param proc |
| * @param diagnostics |
| * @param context |
| */ |
| public void validateProcess(Process proc, final DiagnosticChain diagnostics, |
| Map context) { |
| Object o = context.get(CTX_ADAPTER_FACTORY_PROVIDER); |
| if (!(o instanceof IAdapterFactoryProvider)) { |
| return; |
| } |
| |
| class FilteredAdapterFactoryTreeIterator extends AdapterFactoryTreeIterator { |
| |
| public FilteredAdapterFactoryTreeIterator(AdapterFactory adapterFactory, Object object) { |
| super(adapterFactory, object); |
| } |
| |
| protected boolean accept(Object child) { |
| return child instanceof NamedElement; |
| } |
| |
| /** |
| * Comment for <code>serialVersionUID</code> |
| */ |
| private static final long serialVersionUID = 0L; |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.emf.edit.provider.AdapterFactoryTreeIterator#getChildren(java.lang.Object) |
| */ |
| protected Iterator getChildren(Object o) { |
| ArrayList children = new ArrayList(); |
| for (Iterator iter = super.getChildren(o); iter.hasNext();) { |
| Object child = iter.next(); |
| if (child instanceof MethodElement |
| || (child instanceof DescribableElementWrapperItemProvider |
| && !((DescribableElementWrapperItemProvider) child).isReadOnly())) { |
| children.add(child); |
| child = TngUtil.unwrap(child); |
| } |
| if (accept(child)) { |
| try { |
| ArrayList features = new ArrayList(); |
| features.add(UmaPackage.Literals.NAMED_ELEMENT__NAME); |
| if(child instanceof DescribableElement) { |
| features.add(UmaPackage.Literals.METHOD_ELEMENT__PRESENTATION_NAME); |
| } |
| NamedElement e = (NamedElement) child; |
| if (e instanceof BreakdownElement) { |
| //continue; |
| } |
| if(DEBUG) { |
| System.out |
| .println("FilteredAdapterFactoryTreeIterator.getChildren(): validating '" + e.getName() + "'"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| for (Iterator iterator = features.iterator(); iterator |
| .hasNext();) { |
| EStructuralFeature feature = (EStructuralFeature) iterator.next(); |
| IValidator validator = IValidatorFactory.INSTANCE |
| .createValidator(o, null, |
| (EObject) child, feature, |
| adapterFactory); |
| Object itemProvider = adapterFactory.adapt(child, ITreeItemContentProvider.class); |
| String prop = (String) featureToPropertyMap.get(feature); |
| String name; |
| if(itemProvider instanceof IBSItemProvider && prop != null) { |
| name = ((IBSItemProvider)itemProvider).getAttribute(child, prop); |
| } |
| else { |
| name = (String) e.eGet(feature); |
| } |
| String msg = validator.isValid(name); |
| if (msg != null) { |
| appendDiagnostics(new ValidationStatus( |
| IStatus.ERROR, 0, msg, e, feature), |
| diagnostics); |
| } |
| } |
| } catch (Exception e) { |
| |
| } |
| } |
| } |
| return children.iterator(); |
| } |
| }; |
| IAdapterFactoryProvider provider = (IAdapterFactoryProvider) o; |
| Iterator iterator = new FilteredAdapterFactoryTreeIterator(provider |
| .getWBSAdapterFactory(), proc); |
| for (; iterator.hasNext(); iterator.next()) |
| ; |
| |
| iterator = new FilteredAdapterFactoryTreeIterator(provider.getTBSAdapterFactory(), proc) { |
| /** |
| * Comment for <code>serialVersionUID</code> |
| */ |
| private static final long serialVersionUID = 1L; |
| |
| /* (non-Javadoc) |
| * @see #accept(java.lang.Object) |
| */ |
| protected boolean accept(Object child) { |
| return child instanceof RoleDescriptor || child instanceof TeamProfile; |
| } |
| }; |
| for (; iterator.hasNext(); iterator.next()) |
| ; |
| |
| iterator = new FilteredAdapterFactoryTreeIterator(provider.getWPBSAdapterFactory(), proc) { |
| /** |
| * Comment for <code>serialVersionUID</code> |
| */ |
| private static final long serialVersionUID = 1L; |
| |
| /* (non-Javadoc) |
| * @see #accept(java.lang.Object) |
| */ |
| protected boolean accept(Object child) { |
| return child instanceof WorkProductDescriptor; |
| } |
| }; |
| for (; iterator.hasNext(); iterator.next()) |
| ; |
| } |
| |
| private static int getTraversalType(Map context) { |
| Object type = context.get(LibraryTraversalStrategy.TRAVERSAL_TYPE); |
| return (type instanceof Number && ((Number) type).intValue() == LibraryTraversalStrategy.DEEP) ? LibraryTraversalStrategy.DEEP |
| : LibraryTraversalStrategy.SHADOW; |
| } |
| |
| /** |
| * Direct validation of {@link EDataType}s is not supported by the EMF |
| * validation framework; they are validated indirectly via the |
| * {@link EObject}s that hold their values. |
| */ |
| public boolean validate(EDataType eDataType, Object value, |
| DiagnosticChain diagnostics, Map context) { |
| return super.validate(eDataType, value, diagnostics, context); |
| } |
| |
| /** |
| * If we have a context map, record this object's <code>status</code> in |
| * it so that we will know later that we have processed it and its sub-tree. |
| * |
| * @param eObject |
| * an element that we have validated |
| * @param context |
| * the context (may be <code>null</code>) |
| * @param status |
| * the element's validation status |
| */ |
| private void processed(EObject eObject, Map context, IStatus status) { |
| if (context != null) { |
| context.put(eObject, status); |
| } |
| } |
| |
| /** |
| * Determines whether we have processed this <code>eObject</code> before, |
| * by automatic recursion of the EMF Model Validation Service. This is only |
| * possible if we do, indeed, have a context. |
| * |
| * @param eObject |
| * an element to be validated (we hope not) |
| * @param context |
| * the context (may be <code>null</code>) |
| * @return <code>true</code> if the context is not <code>null</code> and |
| * the <code>eObject</code> or one of its containers has already |
| * been validated; <code>false</code>, otherwise |
| */ |
| private boolean hasProcessed(EObject eObject, Map context) { |
| boolean result = false; |
| |
| if (context != null) { |
| // this is O(NlogN) but there's no helping it |
| while (eObject != null) { |
| if (context.containsKey(eObject)) { |
| result = true; |
| eObject = null; |
| } else { |
| eObject = eObject.eContainer(); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Converts a status result from the EMF validation service to diagnostics. |
| * |
| * @param status |
| * the EMF validation service's status result |
| * @param diagnostics |
| * a diagnostic chain to accumulate results on |
| */ |
| private void appendDiagnostics(IStatus status, DiagnosticChain diagnostics) { |
| if (status.isMultiStatus()) { |
| IStatus[] children = status.getChildren(); |
| |
| for (int i = 0; i < children.length; i++) { |
| appendDiagnostics(children[i], diagnostics); |
| } |
| } else if (status instanceof IConstraintStatus) { |
| diagnostics.add(new BasicDiagnostic(status.getSeverity(), status |
| .getPlugin(), status.getCode(), status.getMessage(), |
| ((IConstraintStatus) status).getResultLocus().toArray())); |
| } else if (status instanceof org.eclipse.epf.library.edit.validation.ValidationStatus) { |
| diagnostics.add(new BasicDiagnostic(status.getSeverity(), status |
| .getPlugin(), status.getCode(), status.getMessage(), |
| new Object[] { ((org.eclipse.epf.library.edit.validation.ValidationStatus) status) |
| .getCheckedObject() })); |
| } else { |
| diagnostics.add(new BasicDiagnostic(status.getSeverity(), status |
| .getPlugin(), status.getCode(), status.getMessage(), |
| null)); |
| } |
| } |
| |
| } |