blob: daec0c7a6379461e0a3c42a93424e471126b092f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2017 EclipseSource Muenchen GmbH 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:
* Edgar Mueller - initial API and implementation
******************************************************************************/
package org.eclipse.emfforms.internal.common.prevalidation;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
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.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EValidator;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.ecore.util.EObjectValidator;
import org.eclipse.emf.ecore.util.EObjectValidator.DynamicEDataTypeValidator;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emfforms.common.Optional;
import org.eclipse.emfforms.spi.common.validation.IFeatureConstraint;
import org.eclipse.emfforms.spi.common.validation.PreSetValidationService;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
/**
* Implementation of the {@link PreSetValidationService}.
*
* @author emueller
*
*/
@Component(name = "PreSetValidationServiceImpl", service = PreSetValidationService.class)
public class PreSetValidationServiceImpl implements PreSetValidationService {
private static final String LOOSE_PATTERN_KEY = "loosePattern"; //$NON-NLS-1$
private static final String LOOSE_MIN_LENGTH = "looseMinLength"; //$NON-NLS-1$
private static final String MULTI_LITERAL_SEP = "|"; //$NON-NLS-1$
private static final String ESCAPED_MULTI_LITERAL_SEP = "\\|"; //$NON-NLS-1$
private Map<EDataType, Set<IFeatureConstraint>> extensions = new LinkedHashMap<EDataType, Set<IFeatureConstraint>>();
@Override
public Diagnostic validate(final EStructuralFeature eStructuralFeature, Object value) {
return validate(eStructuralFeature, value, null);
}
@Override
public Diagnostic validate(final EStructuralFeature eStructuralFeature, Object value, Map<Object, Object> context) {
return validate(
new PreSetValidator() {
@Override
public boolean validate(EDataType eDataType, Object value, DiagnosticChain diagnostics,
Map<Object, Object> context) {
EValidator validator = EValidator.Registry.INSTANCE
.getEValidator(eStructuralFeature.getEType().getEPackage());
if (validator == null) {
validator = new EObjectValidator();
}
return validator.validate(eDataType, value, diagnostics, context);
}
},
eStructuralFeature,
value,
context);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emfforms.spi.common.validation.PreSetValidationService#validateLoose(org.eclipse.emf.ecore.EStructuralFeature,
* java.lang.Object)
*/
@Override
public Diagnostic validateLoose(EStructuralFeature eStructuralFeature, Object value) {
final EClassifier eType = eStructuralFeature.getEType();
if (!EDataType.class.isInstance(eType) || EDataType.class.cast(eType).getEPackage() == EcorePackage.eINSTANCE) {
return new BasicDiagnostic();
}
return validate(
new DynamicLoosePatternEValidator(new LooseEValidator(), (EDataType) eType),
eStructuralFeature,
value,
null);
}
@SuppressWarnings("unchecked")
private Diagnostic validate(PreSetValidator validator, EStructuralFeature eStructuralFeature, Object value,
Map<Object, Object> context) {
final EClassifier eType = eStructuralFeature.getEType();
if (!EDataType.class.isInstance(eType) || EDataType.class.cast(eType).getEPackage() == EcorePackage.eINSTANCE) {
return new BasicDiagnostic();
}
final EDataType eDataType = EDataType.class.cast(eType);
final BasicDiagnostic diagnostics = Diagnostician.INSTANCE.createDefaultDiagnostic(eDataType, value);
// try to validate given value as enum literal
if (eDataType instanceof EEnum && value instanceof String) {
if (validateEEnum((EEnum) eDataType, (String) value)) {
return new BasicDiagnostic();
}
} else if (eDataType instanceof EEnum && value instanceof List<?>) {
try {
if (validateEEnum((EEnum) eDataType, (List<Enumerator>) value)) {
return new BasicDiagnostic();
}
} catch (final ClassCastException ex) {
// ignore and continue with regular validation
}
}
if (validator != null) {
validator.validate(eDataType, value, diagnostics, context);
}
final Set<IFeatureConstraint> featureConstraints = extensions.get(eType);
if (featureConstraints != null) {
for (final IFeatureConstraint constraint : featureConstraints) {
final Diagnostic result = constraint.validate(eStructuralFeature, value, context);
if (result.getSeverity() == Diagnostic.OK) {
continue;
}
diagnostics.add(result);
}
}
return diagnostics;
}
private static boolean validateEEnum(final EEnum eEnum, final String valueString) {
if (valueString.contains(MULTI_LITERAL_SEP)) {
boolean isValid = true;
final String[] literals = valueString.split(ESCAPED_MULTI_LITERAL_SEP);
for (final String literal : literals) {
isValid &= validateLiteral(eEnum, literal.trim());
}
return isValid;
}
return validateLiteral(eEnum, valueString);
}
private static boolean validateEEnum(final EEnum eEnum, final List<Enumerator> enumerators) {
boolean isValid = true;
for (final Enumerator enumerator : enumerators) {
isValid &= validateLiteral(eEnum, enumerator.getLiteral());
}
return isValid;
}
private static Optional<String> findLooseConstraint(EDataType eDataType, String looseConstrainKey) {
return Optional
.ofNullable(EcoreUtil.getAnnotation(eDataType, ExtendedMetaData.ANNOTATION_URI, looseConstrainKey));
}
private static boolean validateLiteral(EEnum eEnum, String literal) {
for (final EEnumLiteral enumLiteral : eEnum.getELiterals()) {
if (literal.equals(enumLiteral.getLiteral())) {
return true;
}
}
return false;
}
@Override
public void addConstraintValidator(EDataType eDataType, IFeatureConstraint extension) {
if (!extensions.containsKey(eDataType)) {
extensions.put(eDataType, new LinkedHashSet<IFeatureConstraint>());
}
extensions.get(eDataType).add(extension);
}
/**
* Called by the framework when the component gets activated.
*
* @param bundleContext The {@link BundleContext}
*/
@Activate
protected void activate(BundleContext bundleContext) {
extensions = new LinkedHashMap<EDataType, Set<IFeatureConstraint>>();
}
/**
* Called by the framework when the component gets deactivated.
*
* @param bundleContext The {@link BundleContext}
*/
@Deactivate
protected void deactivate(BundleContext bundleContext) {
extensions = null;
}
/**
* An {@link EObjectValidator} that considers loose constraints of any annotation details entry.
*
*/
class LooseEValidator extends EObjectValidator implements PreSetValidator {
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecore.util.EObjectValidator#validatePattern(org.eclipse.emf.ecore.EDataType,
* java.lang.Object, org.eclipse.emf.ecore.EValidator.PatternMatcher[][],
* org.eclipse.emf.common.util.DiagnosticChain, java.util.Map)
*/
@Override
protected boolean validatePattern(EDataType eDataType, Object value, PatternMatcher[][] patterns,
DiagnosticChain diagnostics, Map<Object, Object> context) {
final Optional<String> loosePattern = findLooseConstraint(eDataType, LOOSE_PATTERN_KEY);
if (loosePattern.isPresent()) {
return super.validatePattern(eDataType, value, new PatternMatcher[][] {
{
new PatternMatcher() {
@Override
public boolean matches(String value) {
final Pattern pattern = Pattern.compile(loosePattern.get());
final Matcher matcher = pattern.matcher(value);
return matcher.matches();
}
}
}
}, diagnostics, context);
}
return super.validatePattern(eDataType, value, patterns, diagnostics, context);
}
}
/**
* An {@link DynamicEDataTypeValidator} that considers loose constraints of any annotation details entry.
*
*/
class DynamicLoosePatternEValidator extends DynamicEDataTypeValidator implements PreSetValidator {
/**
* Constructor.
*
* @param eObjectValidator an instance of an {@link EObjectValidator}
* @param eDataType the {@link EDataType} to be validated
*/
DynamicLoosePatternEValidator(EObjectValidator eObjectValidator, EDataType eDataType) {
eObjectValidator.super(eDataType);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecore.util.EObjectValidator.DynamicEDataTypeValidator#validateSchemaConstraints(org.eclipse.emf.ecore.EDataType,
* java.lang.Object, org.eclipse.emf.common.util.DiagnosticChain, java.util.Map)
*/
@Override
protected boolean validateSchemaConstraints(EDataType eDataType, Object value, DiagnosticChain diagnostics,
Map<Object, Object> context) {
final Optional<String> looseMinLength = findLooseConstraint(eDataType, LOOSE_MIN_LENGTH);
if (looseMinLength.isPresent()) {
effectiveMinLength = Integer.parseInt(looseMinLength.get());
super.validateSchemaConstraints(eDataType, value, diagnostics, context);
}
return super.validateSchemaConstraints(eDataType, value, diagnostics, context);
}
}
}