blob: eab4a56951cfcabb940b318a7ac1cb099346dad6 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2019 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Robert Bosch GmbH - initial API and implementation
********************************************************************************
*/
package org.eclipse.app4mc.validation.util;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;
import org.eclipse.app4mc.validation.core.IProfile;
import org.eclipse.app4mc.validation.core.IValidation;
import org.eclipse.app4mc.validation.core.Severity;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcorePackage;
public class ValidationAggregator {
private final Map<Class<? extends IProfile>, CachedProfile> profileCache = new HashMap<>();
private final Map<Class<? extends IValidation>, CachedValidator> validatorCache = new HashMap<>();
private final Set<EPackage> ePackageSet = new HashSet<>();
private final Set<EClass> eClassSet = new HashSet<>();
private final Map<EClassifier, List<CachedValidator>> validationMap1 = new HashMap<>();
private final Map<EClassifier, Set<CachedValidator>> validationMap2 = new HashMap<>();
private boolean rebuildValidationMaps = false;
public ValidationAggregator() {
super();
}
public void addProfiles(final Collection<Class<? extends IProfile>> profileClasses) {
if (profileClasses == null || profileClasses.isEmpty()) return;
// store profiles in cache
for (Class<? extends IProfile> pClass : profileClasses) {
addProfile(pClass);
}
}
public void addProfile(final Class<? extends IProfile> profileClass) {
if (profileClass == null) return;
if (!profileCache.containsKey(profileClass)) {
// create new profile and add to cache
CachedProfile profile = new CachedProfile(profileClass);
profileCache.put(profileClass, profile);
// add validations of profile to cache
addValidations(profile.getAllValidations());
}
}
public void addValidations(final Map<Class<? extends IValidation>, Severity> map) {
if (map == null || map.isEmpty()) return;
for (Entry<Class<? extends IValidation>, Severity> entry : map.entrySet()) {
addValidation(entry.getKey(), entry.getValue());
}
}
public void addValidation(final Class<? extends IValidation> validatorClass, Severity validatorSeverity) {
if (validatorClass == null) return;
CachedValidator cachedValidator = validatorCache.get(validatorClass);
if (cachedValidator == null) {
// create new validator and add to cache
CachedValidator newValidator = new CachedValidator(validatorClass, validatorSeverity);
validatorCache.put(validatorClass, newValidator);
rebuildValidationMaps = true;
// store affected Ecore package and classes
EPackage ePackage = newValidator.getValidatorInstance().getEPackage();
if (ePackage != null && !ePackageSet.contains(ePackage)) {
ePackageSet.add(ePackage);
extendClassSet(ePackage);
}
} else {
// check existing validator and set higher severity
if (cachedValidator.getSeverity().ordinal() < validatorSeverity.ordinal()) {
cachedValidator.setSeverity(validatorSeverity);
}
}
}
private void extendClassSet(EPackage ePackage) {
// store all concrete Ecore classes of ePackage
for (final EClassifier classifier : ePackage.getEClassifiers()) {
if (classifier instanceof EClass) {
EClass eClass = (EClass) classifier;
if (!eClass.isAbstract() && !eClass.isInterface())
eClassSet.add(eClass);
}
}
}
private void rebuildValidationMaps() {
// Collect validations in Map 1
validationMap1.clear();
for (CachedValidator validator : validatorCache.values()) {
EClassifier eClassifier = validator.getTargetEClassifier();
validationMap1
.computeIfAbsent(eClassifier, k -> new ArrayList<>())
.add(validator);
}
// Reuse validations of Map1 in Map2
validationMap2.clear();
for (Entry<EClassifier, List<CachedValidator>> entry : validationMap1.entrySet()) {
addValidationsToMap2(entry.getKey(), entry.getValue());
}
rebuildValidationMaps = false;
}
private void addValidationsToMap2(final EClassifier eClassifier, final List<CachedValidator> validatorList) {
if (eClassifier instanceof EDataType) {
// Add entry for data type
basicAddValidationsToMap2(eClassifier, validatorList);
} else if (eClassifier instanceof EClass) {
// Add entry for concrete target class
EClass eClass = (EClass) eClassifier;
if (!eClass.isAbstract() && !eClass.isInterface()) {
basicAddValidationsToMap2(eClass, validatorList);
}
// Add entries for sub classes of target class
for (EClass potentialSubclass : eClassSet) {
// Hint: special check for EObject is required
if (eClass.equals(EcorePackage.eINSTANCE.getEObject()) || eClass.isSuperTypeOf(potentialSubclass)) {
basicAddValidationsToMap2(potentialSubclass, validatorList);
}
}
}
}
private void basicAddValidationsToMap2(final EClassifier eClassifier, final List<CachedValidator> validatorList) {
// only add entries for elements of the concerned packages
if (ePackageSet.contains(eClassifier.eContainer())) {
validationMap2
.computeIfAbsent(eClassifier, k -> new HashSet<>())
.addAll(validatorList);
}
}
// *** public getters ***
public List<Class<? extends IProfile>> getProfiles() {
return profileCache.values().stream().map(CachedProfile::getProfileClass).collect(Collectors.toList());
}
public Set<EPackage> getEPackages() {
return Collections.unmodifiableSet(ePackageSet);
}
public Set<CachedValidator> getValidations(final EClass targetClass) {
if (rebuildValidationMaps) rebuildValidationMaps();
Set<CachedValidator> result = validationMap2.get(targetClass);
if (result == null) {
return Collections.emptySet();
} else {
return Collections.unmodifiableSet(result);
}
}
public ConcurrentMap<EClassifier, CopyOnWriteArraySet<CachedValidator>> getConcurrentValidationMap() {
if (rebuildValidationMaps) rebuildValidationMaps();
ConcurrentHashMap<EClassifier, CopyOnWriteArraySet<CachedValidator>> map = new ConcurrentHashMap<>();
for (Entry<EClassifier, Set<CachedValidator>> entry : validationMap2.entrySet()) {
map.put(entry.getKey(), new CopyOnWriteArraySet<>(entry.getValue()));
}
return map;
}
// *** Some helper methods
public void dumpValidationMap1(PrintStream out) {
if (out == null) return;
if (rebuildValidationMaps) rebuildValidationMaps();
List<EClassifier> classifiers = validationMap1.keySet().stream()
.sorted(Comparator.comparing(EClassifier::getName))
.collect(Collectors.toList());
for (EClassifier cl : classifiers) {
out.println(cl.getName() + ":");
validationMap1.get(cl).stream()
.sorted(Comparator.comparing(CachedValidator::getValidationID))
.forEachOrdered(cv -> out.println(" " + cv.getValidationID() + " -> " + cv.getSeverity()));
}
}
public void dumpValidationMap2(PrintStream out) {
if (out == null) return;
if (rebuildValidationMaps) rebuildValidationMaps();
List<EClassifier> classifiers = validationMap2.keySet().stream()
.sorted(Comparator.comparing(EClassifier::getName))
.collect(Collectors.toList());
for (EClassifier cl : classifiers) {
out.println(cl.getName() + ":");
validationMap2.get(cl).stream()
.sorted(Comparator.comparing(CachedValidator::getValidationID))
.forEachOrdered(cv -> out.println(" " + cv.getValidationID() + " -> " + cv.getSeverity()));
}
}
}