| author | Christian W. Damus | 2012-03-12 11:58:03 (EDT) |
|---|---|---|
| committer | Kenn Hussey | 2012-03-12 11:58:03 (EDT) |
| commit | 8fac4dcf8c3970f1f1a270d706f8c127b421785f (patch) (side-by-side diff) | |
| tree | 2dd01860c7803bf4466fb39de3a94d2dc9e10945 | |
| parent | 16c3b4619945ffd2cf91d7d81e0b793ccefd3646 (diff) | |
| download | org.eclipse.uml2-8fac4dcf8c3970f1f1a270d706f8c127b421785f.zip org.eclipse.uml2-8fac4dcf8c3970f1f1a270d706f8c127b421785f.tar.gz org.eclipse.uml2-8fac4dcf8c3970f1f1a270d706f8c127b421785f.tar.bz2 | |
[373643] Adding dynamic provider for UML2 validation constraints.
13 files changed, 984 insertions, 8 deletions
diff --git a/plugins/org.eclipse.uml2.uml/META-INF/MANIFEST.MF b/plugins/org.eclipse.uml2.uml/META-INF/MANIFEST.MF index 9bb3fa6..e721e38 100644 --- a/plugins/org.eclipse.uml2.uml/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.uml2.uml/META-INF/MANIFEST.MF @@ -13,12 +13,14 @@ Export-Package: org.eclipse.uml2.uml, org.eclipse.uml2.uml.internal.operations;x-internal:=true, org.eclipse.uml2.uml.internal.resource;x-internal:=true, org.eclipse.uml2.uml.resource, - org.eclipse.uml2.uml.util + org.eclipse.uml2.uml.util, + org.eclipse.uml2.uml.validation Require-Bundle: org.eclipse.core.runtime, org.eclipse.emf.ecore;visibility:=reexport, org.eclipse.emf.ecore.xmi;visibility:=reexport, org.eclipse.uml2.common;visibility:=reexport, org.eclipse.uml2.types;visibility:=reexport, - org.eclipse.emf.mapping.ecore2xml + org.eclipse.emf.mapping.ecore2xml, + org.eclipse.emf.validation;resolution:=optional;visibility:=reexport Eclipse-LazyStart: true Bundle-ActivationPolicy: lazy diff --git a/plugins/org.eclipse.uml2.uml/plugin.properties b/plugins/org.eclipse.uml2.uml/plugin.properties index 1528680..e0baee3 100644 --- a/plugins/org.eclipse.uml2.uml/plugin.properties +++ b/plugins/org.eclipse.uml2.uml/plugin.properties @@ -8,6 +8,7 @@ # IBM - initial API and implementation # Kenn Hussey (Embarcadero Technologies) - 156879, 215488, 213218, 204200 # Kenn Hussey (CEA) - 327039, 351774, 373709 +# Christian W. Damus (CEA) - 373643 # # NLS_MESSAGEFORMAT_VAR @@ -247,3 +248,9 @@ _UI_AddVariableValueAction_SingleInputPin_diagnostic = Add variable value actio _UI_AssociationClass_CannotBeDefined_diagnostic = Association class ''{0}'' cannot be defined between itself and something else. _UI_AssociationClass_DisjointAttributesEnds_diagnostic = The owned attributes and owned ends of association class ''{0}'' are not disjoint. + +_UI_Validation_constraintName_ = {0}: {1} +_UI_Validation_constraintDesc = The modeled constraint ''{0}''. +_UI_Validation_violation_ = The constraint ''{0}'' is not satisfied. +_UI_Validation_runtimeError = Delegated validator operation failed with a run-time error: {0} +_UI_Validation_linkageError = Failed to invoke reflective constraint delegation: {0} diff --git a/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/DelegatingConstraintDescriptor.java b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/DelegatingConstraintDescriptor.java new file mode 100644 index 0000000..559df69 --- a/dev/null +++ b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/DelegatingConstraintDescriptor.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2012 CEA 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: + * Christian W. Damus (CEA) - initial API and implementation + */ +package org.eclipse.uml2.uml.validation; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.validation.model.ConstraintSeverity; +import org.eclipse.emf.validation.model.EvaluationMode; +import org.eclipse.emf.validation.service.AbstractConstraintDescriptor; +import org.eclipse.uml2.uml.UMLPlugin; + +/** + * Descriptor of a constraint that delegates to an {@link EPackage}'s + * constraints via its generated {@link EValidator}. + */ +class DelegatingConstraintDescriptor + extends AbstractConstraintDescriptor { + + private final String namespace; + + private final EClass target; + + private final String id; + + private final String name; + + /** + * Initializes me with the {@code EClass} that I constrain. + * + * @param a + * namespace in which to define the constraints (e.g., + * {@code "org.eclipse.uml2.uml"}) + * @param target + * my target model class + * @param name + * my name, from the model + */ + DelegatingConstraintDescriptor(String namespace, EClass target, String name) { + this.namespace = namespace; + this.target = target; + this.name = UMLPlugin.INSTANCE.getString( + "_UI_Validation_constraintName_", //$NON-NLS-1$ + new Object[]{target.getName(), name}); + + StringBuilder buf = new StringBuilder(); + buf.append(namespace); + buf.append('.').append(target.getEPackage().getName()).append('.') + .append(target.getName()); + buf.append('.').append(name); + this.id = buf.toString(); + } + + public String getName() { + return name; + } + + public String getId() { + return id; + } + + public String getPluginId() { + return namespace; + } + + public String getDescription() { + return UMLPlugin.INSTANCE.getString("_UI_Validation_constraintDesc_", //$NON-NLS-1$ + new Object[]{getName()}); + } + + public ConstraintSeverity getSeverity() { + // in the UML metamodel, constraints generally are warnings + return ConstraintSeverity.WARNING; + } + + public int getStatusCode() { + return 0; // constraints determine their own codes + } + + public EvaluationMode<?> getEvaluationMode() { + // it is not appropriate to try to invoke EValidator constraints in + // Batch mode + return EvaluationMode.BATCH; + } + + public boolean targetsTypeOf(EObject eObject) { + return target.isInstance(eObject); + } + + public boolean targetsEvent(Notification notification) { + // live mode is not supported + return false; + } + + public String getMessagePattern() { + return UMLPlugin.INSTANCE.getString("_UI_Validation_violation_", //$NON-NLS-1$ + new Object[]{getName()}); + } + + public String getBody() { + // delegated constraints have nobody (har, har) + return null; + } + + EClass getTarget() { + return target; + } +} diff --git a/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/DelegatingConstraintProvider.java b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/DelegatingConstraintProvider.java new file mode 100644 index 0000000..931b4f9 --- a/dev/null +++ b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/DelegatingConstraintProvider.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2012 CEA 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: + * Christian W. Damus (CEA) - initial API and implementation + */ +package org.eclipse.uml2.uml.validation; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.emf.common.util.DiagnosticChain; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EValidator; +import org.eclipse.emf.validation.model.Category; +import org.eclipse.emf.validation.model.CategoryManager; +import org.eclipse.emf.validation.model.IModelConstraint; +import org.eclipse.emf.validation.service.AbstractConstraintProvider; +import org.eclipse.emf.validation.service.ConstraintExistsException; +import org.eclipse.emf.validation.service.IConstraintDescriptor; +import org.eclipse.uml2.common.util.UML2Util; +import org.eclipse.uml2.uml.UMLPlugin; + +/** + * A provider of constraints that delegate to an {@link EPackage}'s constraints + * via its generated {@link EValidator}. + */ +public class DelegatingConstraintProvider + extends AbstractConstraintProvider { + + private static final String E_CATEGORY = "category"; //$NON-NLS-1$ + + private static final String A_PATH = "path"; //$NON-NLS-1$ + + private static final String E_EVALIDATOR_PROVIDER = "eValidatorProvider"; //$NON-NLS-1$ + + private static final String A_CLASS = "class"; //$NON-NLS-1$ + + /** + * Initializes me. + */ + public DelegatingConstraintProvider() { + super(); + } + + @Override + public void setInitializationData(IConfigurationElement config, + String propertyName, Object data) + throws CoreException { + + super.setInitializationData(config, propertyName, data); + + // first, grab the categories that I'll be assigning to my constraints + final Set<Category> categories = getCategories(config); + + // get an EValidator provider + IEValidatorProvider validatorProvider = getEValidatorProvider(config); + + // then find the constraints that I need to adapt + for (String next : getNamespaceUris()) { + // find the EValidator for this package + EPackage epackage = EPackage.Registry.INSTANCE.getEPackage(next); + + if (epackage == null) { + UMLPlugin.INSTANCE + .log(new Status( + IStatus.WARNING, + UMLPlugin.INSTANCE.getSymbolicName(), + "No such EPackage available for model validation: " + next)); //$NON-NLS-1$ + } else { + EValidator validator = validatorProvider + .getEValidator(epackage); + if (validator == null) { + UMLPlugin.INSTANCE + .log(new Status( + IStatus.WARNING, + UMLPlugin.INSTANCE.getSymbolicName(), + "No generated validator available for package: " + next)); //$NON-NLS-1$ + } else { + try { + Iterable<? extends IModelConstraint> constraints = createConstraints( + config.getNamespaceIdentifier(), epackage, + validator); + + if (!categories.isEmpty()) { + Category[] cats = categories + .toArray(new Category[categories.size()]); + for (IModelConstraint constraint : constraints) { + IConstraintDescriptor desc = constraint + .getDescriptor(); + for (int i = 0; i < cats.length; i++) { + desc.addCategory(cats[i]); + } + } + } + } catch (ConstraintExistsException e) { + throw new CoreException(new Status(IStatus.ERROR, + UMLPlugin.INSTANCE.getSymbolicName(), + "Failed to register model validation constraints.", //$NON-NLS-1$ + e)); + } + } + } + } + } + + private Set<Category> getCategories(IConfigurationElement config) { + final Set<Category> result = new java.util.HashSet<Category>(); + for (IConfigurationElement next : config.getChildren(E_CATEGORY)) { + String path = next.getAttribute(A_PATH); + + if (!UML2Util.isEmpty(path)) { + // if the category doesn't already exist, it is implicitly + // created, so + // we won't get a null category + result.add(CategoryManager.getInstance().getCategory(path)); + } + } + + return result; + } + + private IEValidatorProvider getEValidatorProvider( + IConfigurationElement config) { + + IEValidatorProvider result = null; + + IConfigurationElement[] vpConfig = config + .getChildren(E_EVALIDATOR_PROVIDER); + if (vpConfig.length > 0) { + Object ext; + try { + ext = vpConfig[0].createExecutableExtension(A_CLASS); + if (ext instanceof IEValidatorProvider) { + result = (IEValidatorProvider) ext; + } + } catch (CoreException e) { + UMLPlugin.INSTANCE.log(e.getStatus()); + } + } + + if (result == null) { + result = new IEValidatorProvider.Default(); + } + + return result; + } + + private Iterable<? extends IModelConstraint> createConstraints( + final String namespace, final EPackage epackage, + final EValidator validator) + throws ConstraintExistsException { + + final List<IModelConstraint> result = new java.util.ArrayList<IModelConstraint>(); + final Matcher m = Pattern.compile("validate\\w+_validate(\\w+)") //$NON-NLS-1$ + .matcher(""); //$NON-NLS-1$ + final Map<Class<?>, EClass> eclasses = new java.util.HashMap<Class<?>, EClass>(); + + for (Method next : validator.getClass().getDeclaredMethods()) { + if (Modifier.isPublic(next.getModifiers())) { + final Class<?>[] signature = next.getParameterTypes(); + + m.reset(next.getName()); + if (m.matches() + && isConstraintMethod(next.getReturnType(), signature)) { + EClass eclass = getEClass(eclasses, epackage, signature[0]); + + // constraint methods could exist for EDataTypes; this + // framework doesn't handle them + if (eclass != null) { + result.add(new DelegatingModelConstraint(namespace, + validator, eclass, next)); + } + } + } + } + + getConstraints().addAll(result); + registerConstraints(result); + + return result; + } + + private static boolean isConstraintMethod(Class<?> returnType, + Class<?>[] parameterTypes) { + + boolean result = false; + + if ((returnType == boolean.class) && (parameterTypes.length == 3)) { + result = EObject.class.isAssignableFrom(parameterTypes[0]) + && (parameterTypes[1] == DiagnosticChain.class) + && (parameterTypes[2] == Map.class); + } + + return result; + } + + /** + * Look up an {@link EClass} by instance-class, using a cache for + * performance of repeated queries. + * + * @param cache + * a cache of previous look-up results + * @param epackage + * the epackage in which to find the eclass + * @param interfaceType + * the Java interface type by which to look up the eclass + * + * @return the eclass, or {@code null} if not found + */ + private static EClass getEClass(Map<Class<?>, EClass> cache, + EPackage epackage, Class<?> interfaceType) { + + EClass result = cache.get(interfaceType); + + if (result == null) { + for (EClassifier next : epackage.getEClassifiers()) { + if ((next.getInstanceClass() == interfaceType) + && (next instanceof EClass)) { + result = (EClass) next; + cache.put(interfaceType, result); + break; + } + } + } + + return result; + } +} diff --git a/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/DelegatingModelConstraint.java b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/DelegatingModelConstraint.java new file mode 100644 index 0000000..deff6bf --- a/dev/null +++ b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/DelegatingModelConstraint.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2012 CEA 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: + * Christian W. Damus (CEA) - initial API and implementation + */ +package org.eclipse.uml2.uml.validation; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.emf.common.util.BasicDiagnostic; +import org.eclipse.emf.common.util.Diagnostic; +import org.eclipse.emf.common.util.DiagnosticChain; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EValidator; +import org.eclipse.emf.validation.IValidationContext; +import org.eclipse.emf.validation.model.ConstraintStatus; +import org.eclipse.emf.validation.model.IConstraintStatus; +import org.eclipse.emf.validation.model.IModelConstraint; +import org.eclipse.emf.validation.service.IConstraintDescriptor; +import org.eclipse.uml2.uml.UMLPlugin; + +/** + * Implementation of a constraint that delegates to an {@link EPackage}'s + * constraints via its generated {@link EValidator}. + */ +class DelegatingModelConstraint + implements IModelConstraint { + + private final IConstraintDescriptor descriptor; + + private final EValidator delegate; + + private final Method constraintMethod; + + /** + * Initializes me. + */ + DelegatingModelConstraint(String namespace, EValidator delegate, + EClass target, Method constraintMethod) { + // strip the type-qualifying part off of the validator method name + String name = constraintMethod.getName(); + String expectedPrefix = String.format("validate%s_validate", //$NON-NLS-1$ + target.getName()); + if (name.startsWith(expectedPrefix)) { + name = name.substring(expectedPrefix.length()); + } + + this.descriptor = new DelegatingConstraintDescriptor(namespace, target, + name); + this.delegate = delegate; + this.constraintMethod = constraintMethod; + } + + public final IConstraintDescriptor getDescriptor() { + return descriptor; + } + + public IStatus validate(IValidationContext ctx) { + IStatus result; + + final ContextAdapter ctxAdapter = ContextAdapter.getInstance(ctx); + final BasicDiagnostic diagnostics = ctxAdapter.getDiagnostics(); + final Map<Object, Object> contextMap = ctxAdapter.getContextMap(); + + try { + boolean isOK = (Boolean) constraintMethod.invoke(delegate, + ctx.getTarget(), diagnostics, contextMap); + + if (isOK) { + result = ctx.createSuccessStatus(); + } else { + final int count = diagnostics.getChildren().size(); + + switch (count) { + case 0 : + result = ctx.createFailureStatus(getDescriptor() + .getName()); + break; + case 1 : + result = toConstraintStatus(ctx, diagnostics + .getChildren().get(0)); + break; + default : + List<IStatus> statuses = new java.util.ArrayList<IStatus>( + diagnostics.getChildren().size()); + for (Diagnostic next : diagnostics.getChildren()) { + statuses.add(toConstraintStatus(ctx, next)); + } + result = ConstraintStatus.createMultiStatus(ctx, + statuses); + break; + } + } + } catch (InvocationTargetException e) { + // disable this constraint in future validation operations + ctx.disableCurrentConstraint(e.getTargetException()); + + result = ConstraintStatus.createStatus(ctx, ctx.getTarget(), ctx + .getResultLocus(), IStatus.WARNING, getDescriptor() + .getStatusCode(), UMLPlugin.INSTANCE + .getString("_UI_Validation_runtimeError"), //$NON-NLS-1$ + getDescriptor().getException().getMessage()); + } catch (Exception e) { + // disable this constraint in future validation operations + ctx.disableCurrentConstraint(e); + + result = ConstraintStatus.createStatus(ctx, ctx.getTarget(), ctx + .getResultLocus(), IStatus.WARNING, getDescriptor() + .getStatusCode(), UMLPlugin.INSTANCE + .getString("_UI_Validation_linkageError"), //$NON-NLS-1$ + e.getMessage()); + } + + return result; + } + + private static IConstraintStatus toConstraintStatus(IValidationContext ctx, + Diagnostic diagnostic) { + final EObject target = ctx.getTarget(); + + // collect the "result locus" on which to create problem markers, or + // whatever + List<EObject> resultLocus = new java.util.ArrayList<EObject>(3); + for (Object next : diagnostic.getData()) { + if ((next != target) && (next instanceof EObject)) { + resultLocus.add((EObject) next); + } + } + + return ConstraintStatus.createStatus(ctx, target, resultLocus, + diagnostic.getSeverity(), diagnostic.getCode(), + diagnostic.getMessage()); + } + + // + // Nested types + // + + /** + * A translation from the EMF Validation Framework's + * {@link IValidationContext}-based API to EMF's {@link DiagnosticChain}- + * and map-based API. A weak mapping ensures that the same adapters are + * reused as appropriate for any given instance of the validation context. + */ + private static final class ContextAdapter { + + private static final Map<IValidationContext, ContextAdapter> contextAdapters = new java.util.WeakHashMap<IValidationContext, ContextAdapter>(); + + private Map<Object, Object> contextMap = new java.util.HashMap<Object, Object>(); + + private BasicDiagnostic diagnostics; + + ContextAdapter() { + super(); + } + + static ContextAdapter getInstance(IValidationContext ctx) { + ContextAdapter result = contextAdapters.get(ctx); + + if (result == null) { + result = new ContextAdapter(); + contextAdapters.put(ctx, result); + } + + return result; + } + + Map<Object, Object> getContextMap() { + return contextMap; + } + + BasicDiagnostic getDiagnostics() { + if ((diagnostics != null) && !diagnostics.getChildren().isEmpty()) { + // self-destruct a diagnostic that previously had problems added + // to it + diagnostics = null; + } + + if (diagnostics == null) { + diagnostics = new BasicDiagnostic(); + } + + return diagnostics; + } + } +} diff --git a/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/IEValidatorProvider.java b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/IEValidatorProvider.java new file mode 100644 index 0000000..fb380c0 --- a/dev/null +++ b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/validation/IEValidatorProvider.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2012 CEA 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: + * Christian W. Damus (CEA) - initial API and implementation + */ +package org.eclipse.uml2.uml.validation; + +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EValidator; +import org.eclipse.uml2.uml.UMLPackage; +import org.eclipse.uml2.uml.util.UMLValidator; + +/** + * A protocol for package-to-validator mappings. + */ +public interface IEValidatorProvider { + + /** + * Obtains an appropriate {@link EValidator} for validation of instances of + * the specified {@code ePackage}. + * + * @param ePackage + * a package for which to obtain a validator + * + * @return a suitable validator, or {@code null} if none could be found + */ + EValidator getEValidator(EPackage ePackage); + + // + // Nested types + // + + /** + * The default {@link EValidator} provider uses the validator registry to + * look up whatever validator is there. + */ + class Default + implements IEValidatorProvider { + + public EValidator getEValidator(EPackage ePackage) { + return EValidator.Registry.INSTANCE.getEValidator(ePackage); + } + } + + /** + * The UML {@link EValidator} maps the UML package to the standard UML + * validator. For other packageos, uses the validator registry to look up + * whatever validator is there. + */ + class UML + extends Default { + + @Override + public EValidator getEValidator(EPackage ePackage) { + return (ePackage == UMLPackage.eINSTANCE) + ? UMLValidator.INSTANCE + : super.getEValidator(ePackage); + } + } +} diff --git a/tests/org.eclipse.uml2.uml.tests/META-INF/MANIFEST.MF b/tests/org.eclipse.uml2.uml.tests/META-INF/MANIFEST.MF index e400f1f..e11632d 100644 --- a/tests/org.eclipse.uml2.uml.tests/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.uml2.uml.tests/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName -Bundle-SymbolicName: org.eclipse.uml2.uml.tests; singleton:=true +Bundle-SymbolicName: org.eclipse.uml2.uml.tests;singleton:=true Bundle-Version: 4.0.0.qualifier Bundle-ClassPath: uml2.uml.tests.jar Bundle-Vendor: %providerName diff --git a/tests/org.eclipse.uml2.uml.tests/build.properties b/tests/org.eclipse.uml2.uml.tests/build.properties index 28b5baa..6fc17fc 100644 --- a/tests/org.eclipse.uml2.uml.tests/build.properties +++ b/tests/org.eclipse.uml2.uml.tests/build.properties @@ -1,4 +1,4 @@ -# Copyright (c) 2005, 2008 IBM Corporation, Embarcadero Technologies, and others. +# Copyright (c) 2005, 2012 IBM Corporation, Embarcadero Technologies, CEA, 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 @@ -7,13 +7,14 @@ # Contributors: # IBM - initial API and implementation # Kenn Hussey (Embarcadero Technologies) - 204200 +# Christian W. Damus (CEA) - 373643 # -# $Id: build.properties,v 1.5 2008/02/19 23:15:54 khussey Exp $ # NLS_MESSAGEFORMAT_VAR source.uml2.uml.tests.jar = src/ output.uml2.uml.tests.jar = bin/ bin.includes = about.html,\ + plugin.xml,\ plugin.properties,\ test.xml,\ uml2.uml.tests.jar,\ diff --git a/tests/org.eclipse.uml2.uml.tests/plugin.xml b/tests/org.eclipse.uml2.uml.tests/plugin.xml new file mode 100644 index 0000000..bdaf994 --- a/dev/null +++ b/tests/org.eclipse.uml2.uml.tests/plugin.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.4"?> + +<!-- + Copyright (c) 2012 CEA 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: + Christian W. Damus (CEA) - initial API and implementation + +--> + +<plugin> + <extension + point="org.eclipse.emf.validation.constraintProviders"> + <category + id="org.eclipse.uml2.uml.tests" + name="UML Test Constraints"> + </category> + <constraintProvider + cache="true" + class="org.eclipse.uml2.uml.validation.DelegatingConstraintProvider" + mode="Batch"> + <package + namespaceUri="http://www.eclipse.org/uml2/4.0.0/UML"> + </package> + <category path="org.eclipse.uml2.uml.tests"/> + </constraintProvider> + </extension> + <extension + point="org.eclipse.emf.validation.constraintBindings"> + <clientContext + id="org.eclipse.uml2.uml.tests.clientContext"> + <selector + class="org.eclipse.uml2.uml.validation.tests.TestClientContextSelector"> + </selector> + </clientContext> + <binding + context="org.eclipse.uml2.uml.tests.clientContext" + category="org.eclipse.uml2.uml.tests"> + </binding> + </extension> + +</plugin> diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/tests/UMLAllTests.java b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/tests/UMLAllTests.java index 0ef500d..57b9bb1 100644 --- a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/tests/UMLAllTests.java +++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/tests/UMLAllTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 IBM Corporation and others. + * Copyright (c) 2005, 2012 IBM Corporation, CEA, 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 @@ -7,11 +7,13 @@ * * Contributors: * IBM - initial API and implementation + * Christian W. Damus (CEA) - Bug 373643 * - * $Id: UMLAllTests.java,v 1.1 2006/02/21 16:32:24 khussey Exp $ */ package org.eclipse.uml2.uml.tests; +import org.eclipse.uml2.uml.validation.tests.UMLValidationTests; + import junit.framework.Test; import junit.framework.TestSuite; @@ -38,11 +40,12 @@ public class UMLAllTests /** * <!-- begin-user-doc --> * <!-- end-user-doc --> - * @generated + * @generated NOT */ public static Test suite() { TestSuite suite = new UMLAllTests("UML Tests"); //$NON-NLS-1$ suite.addTest(UMLTests.suite()); + suite.addTest(UMLValidationTests.suite()); return suite; } diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/validation/tests/DelegatingConstraintProviderTest.java b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/validation/tests/DelegatingConstraintProviderTest.java new file mode 100644 index 0000000..939d22b --- a/dev/null +++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/validation/tests/DelegatingConstraintProviderTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2012 CEA 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: + * Christian W. Damus (CEA) - initial API and implementation + */ +package org.eclipse.uml2.uml.validation.tests; + +import java.util.regex.Pattern; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.validation.model.Category; +import org.eclipse.emf.validation.model.CategoryManager; +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.uml2.uml.Actor; +import org.eclipse.uml2.uml.Component; +import org.eclipse.uml2.uml.Generalization; +import org.eclipse.uml2.uml.Package; +import org.eclipse.uml2.uml.UMLFactory; +import org.eclipse.uml2.uml.UMLPackage; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Test suite for the {@link DelegatingConstraintProvider} class and attendant + * classes. + */ +public class DelegatingConstraintProviderTest + extends TestCase { + + /** + * Initializes me with my name. + * + * @param name + * my name + */ + public DelegatingConstraintProviderTest(String name) { + super(name); + } + + public static Test suite() { + return new TestSuite(DelegatingConstraintProviderTest.class, + "Delegating constraint provider tests"); //$NON-NLS-1$ + } + + public void test_constraintsCategorized() { + Category testCategory = CategoryManager.getInstance().getCategory( + "org.eclipse.uml2.uml.tests"); //$NON-NLS-1$ + + boolean found = false; + for (IConstraintDescriptor next : testCategory.getConstraints()) { + if (next.getClass().getSimpleName() + .equals("DelegatingConstraintDescriptor")) { //$NON-NLS-1$ + found = true; + break; + } + } + + assertTrue( + "Didn't find any UML constraints in the test category", found); //$NON-NLS-1$ + } + + public void test_providedConstraintsRun() { + Package package_ = UMLFactory.eINSTANCE.createPackage(); + Component component = (Component) package_.createOwnedType( + "MyComponent", UMLPackage.Literals.COMPONENT); //$NON-NLS-1$ + Actor user = (Actor) package_.createOwnedType("User", //$NON-NLS-1$ + UMLPackage.Literals.ACTOR); + Generalization generalization = user.createGeneralization(component); + + // actor does not have a name. It must. Only check this constraint + IBatchValidator validator = ModelValidationService.getInstance() + .newValidator(EvaluationMode.BATCH); + validator.addConstraintFilter(new NameFilter("SpecializeType")); //$NON-NLS-1$ + + IStatus status = validator.validate(package_); + + assertTrue( + "Validation should not have passed.", status.getSeverity() >= IStatus.WARNING); //$NON-NLS-1$ + assertProblemOn(status, user); + + generalization.setGeneral((Actor) package_.createOwnedType("Sys Admin", //$NON-NLS-1$ + UMLPackage.Literals.ACTOR)); + status = validator.validate(package_); + + assertNoProblemOn(status, user); + } + + // + // Test framework + // + + @Override + protected void setUp() + throws Exception { + super.setUp(); + + TestClientContextSelector.turnOn(); + + // poke the framework to make sure that our provider loads its + // constraints + ModelValidationService.getInstance().newValidator(EvaluationMode.BATCH) + .validate(UMLFactory.eINSTANCE.createPackage()); + } + + @Override + protected void tearDown() + throws Exception { + TestClientContextSelector.turnOff(); + + super.tearDown(); + } + + IConstraintStatus findStatusOf(IStatus status, EObject object) { + IConstraintStatus result = null; + + if (status instanceof IConstraintStatus) { + IConstraintStatus candidate = (IConstraintStatus) status; + + if (candidate.getTarget() == object) { + result = candidate; + } + } + + if ((result == null) && status.isMultiStatus()) { + for (IStatus next : status.getChildren()) { + result = findStatusOf(next, object); + + if (result != null) { + break; + } + } + } + + return result; + } + + void assertProblemOn(IStatus status, EObject object) { + IConstraintStatus specific = findStatusOf(status, object); + + if (specific == null) { + fail("No status for object " + object); //$NON-NLS-1$ + } + + assertFalse("Status is not a problem", specific.isOK()); //$NON-NLS-1$ + } + + void assertNoProblemOn(IStatus status, EObject object) { + IConstraintStatus specific = findStatusOf(status, object); + + if (specific != null) { + assertTrue("Status is a problem", specific.isOK()); //$NON-NLS-1$ + } + } + + // + // Nested types + // + + static class NameFilter + implements IConstraintFilter { + + private final Pattern pattern; + + NameFilter(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + public boolean accept(IConstraintDescriptor constraint, EObject target) { + return pattern.matcher(constraint.getName()).find(); + } + } +} diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/validation/tests/TestClientContextSelector.java b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/validation/tests/TestClientContextSelector.java new file mode 100644 index 0000000..52c893b --- a/dev/null +++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/validation/tests/TestClientContextSelector.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 CEA 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: + * Christian W. Damus (CEA) - initial API and implementation + */ +package org.eclipse.uml2.uml.validation.tests; + +import org.eclipse.emf.validation.model.IClientSelector; +import org.eclipse.uml2.uml.Element; + +/** + * A client-context selector that selects all UML elements while a test is + * running. + */ +public class TestClientContextSelector + implements IClientSelector { + + private static boolean on; + + /** + * Initializes me. + */ + public TestClientContextSelector() { + super(); + } + + public boolean selects(Object object) { + return on && (object instanceof Element); + } + + static void turnOn() { + on = true; + } + + static void turnOff() { + on = false; + } +} diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/validation/tests/UMLValidationTests.java b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/validation/tests/UMLValidationTests.java new file mode 100644 index 0000000..c9c967b --- a/dev/null +++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/validation/tests/UMLValidationTests.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2012 CEA 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: + * Christian W. Damus (CEA) - initial API and implementation + */ +package org.eclipse.uml2.uml.validation.tests; + +import org.eclipse.core.runtime.Platform; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Test suite for the {@code org.eclipse.uml2.uml.validation} package. + */ +public class UMLValidationTests + extends TestSuite { + + /** + * Initializes me with my name. + * + * @param name + * my name + */ + public UMLValidationTests(String name) { + super(name); + } + + public static Test suite() { + TestSuite result; + + // these tests require an Eclipse instance + try { + if (new EclipseHelper().isEclipseRunning()) { + result = new UMLValidationTests( + "UML Validation Constraint Provider Tests"); //$NON-NLS-1$ + result.addTest(DelegatingConstraintProviderTest.suite()); + } else { + result = new TestSuite( + "<UML validation tests require Eclipse to be running>"); //$NON-NLS-1$ + } + } catch (LinkageError e) { + // no (or incomplete) Eclipse environment on the classpath + result = new TestSuite( + "<UML validation tests require an Eclipse instance>"); //$NON-NLS-1$ + } + + return result; + } + + // + // Nested types + // + + private static class EclipseHelper { + + boolean isEclipseRunning() { + return Platform.isRunning(); + } + } +}
\ No newline at end of file |

