blob: ee1add6ce7b9b3f625b51c17593b97c9629eb16b [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.CopyOnWriteArraySet;
import java.util.stream.Collectors;
import org.eclipse.app4mc.validation.core.IProfile;
import org.eclipse.app4mc.validation.core.IValidation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EPackage;
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<>();
public ValidationAggregator(Class<? extends IProfile> profileClass) {
this(Collections.singletonList(profileClass));
}
public ValidationAggregator(final Collection<Class<? extends IProfile>> profileClasses) {
super();
addProfiles(profileClasses);
}
public void addProfiles(final Collection<Class<? extends IProfile>> profileClasses) {
if (profileClasses == null || profileClasses.isEmpty()) return;
Map<Class<? extends IValidation>, CachedValidator> newValidations = new HashMap<>();
// Store profiles in cache (without duplicates)
for (Class<? extends IProfile> pClass : profileClasses) {
if (pClass != null) {
if (!this.profileCache.containsKey(pClass)) {
CachedProfile profile = new CachedProfile(pClass);
this.profileCache.put(pClass, profile);
newValidations.putAll(profile.getAllValidations());
}
}
}
addValidations(newValidations);
}
public void addValidations(final Map<Class<? extends IValidation>, CachedValidator> map) {
if (map == null || map.isEmpty()) return;
// Store validations in cache (without duplicates)
for (Entry<Class<? extends IValidation>, CachedValidator> entry : map.entrySet()) {
Class<? extends IValidation> validatorClass = entry.getKey();
CachedValidator newValidator = entry.getValue();
CachedValidator previousValidator = this.validatorCache.get(validatorClass);
if (previousValidator == null) {
// insert new validator
this.validatorCache.put(validatorClass, newValidator);
// store affected Ecore package
EPackage ePackage = newValidator.getValidatorInstance().getEPackage();
boolean isNewPackage = this.ePackageSet.add(ePackage);
// store all concrete Ecore classes of packages
if (isNewPackage) {
for (final EClassifier classifier : ePackage.getEClassifiers()) {
if (classifier instanceof EClass) {
EClass eClass = (EClass) classifier;
if (!eClass.isAbstract() && !eClass.isInterface())
this.eClassSet.add(eClass);
}
}
}
} else if (previousValidator.getSeverity().ordinal() < newValidator.getSeverity().ordinal()) {
// insert validator with higher severity
this.validatorCache.put(validatorClass, newValidator);
}
}
rebuildValidationMaps();
}
private void rebuildValidationMaps() {
// Collect validations in Map 1
this.validationMap1.clear();
for (CachedValidator validator : this.validatorCache.values()) {
EClassifier eClassifier = validator.getTargetEClassifier();
this.validationMap1
.computeIfAbsent(eClassifier, k -> new ArrayList<CachedValidator>())
.add(validator);
}
// Reuse validations of Map1 in Map2
this.validationMap2.clear();
for (Entry<EClassifier, List<CachedValidator>> entry : validationMap1.entrySet()) {
addValidationsToMap2(entry.getKey(), entry.getValue());
}
}
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 : this.eClassSet) {
// Hint: Do not use eClass.isSuperTypeOf(potentialSubclass)
// because has a different result for EObject
// TODO check CustomProperty
if (eClass.getInstanceClass().isAssignableFrom(potentialSubclass.getInstanceClass())) {
basicAddValidationsToMap2(potentialSubclass, validatorList);
}
}
}
}
private void basicAddValidationsToMap2(final EClassifier eClassifier, final List<CachedValidator> validatorList) {
// only add entries for elements of the concerned packages
if (this.ePackageSet.contains(eClassifier.eContainer())) {
this.validationMap2
.computeIfAbsent(eClassifier, k -> new HashSet<CachedValidator>())
.addAll(validatorList);
}
}
// *** public getters ***
public List<Class<? extends IProfile>> getProfiles() {
return this.profileCache.values().stream().map(e -> e.getProfileClass()).collect(Collectors.toList());
}
public Set<EPackage> getEPackages() {
return Collections.unmodifiableSet(ePackageSet);
}
public Set<CachedValidator> getValidations(final EClass targetClass) {
Set<CachedValidator> result = validationMap2.get(targetClass);
if (result == null) {
return Collections.emptySet();
} else {
return Collections.unmodifiableSet(result);
}
}
public List<IValidation> getAllValidatorInstances() {
return this.validatorCache.values().stream().map(e -> e.getValidatorInstance()).collect(Collectors.toList());
}
public ConcurrentHashMap<EClassifier, CopyOnWriteArraySet<CachedValidator>> getConcurrentValidationMap() {
ConcurrentHashMap<EClassifier, CopyOnWriteArraySet<CachedValidator>> map = new ConcurrentHashMap<>();
for (Entry<EClassifier, Set<CachedValidator>> entry : validationMap2.entrySet()) {
map.put(entry.getKey(), new CopyOnWriteArraySet<CachedValidator>(entry.getValue()));
}
return map;
}
// *** Some helper methods
public void dumpValidationMap1(PrintStream stream) {
PrintStream out = (stream == null) ? System.out : stream;
List<EClassifier> classifiers = validationMap1.keySet().stream()
.sorted(Comparator.comparing(EClassifier::getName))
.collect(Collectors.toList());
for (EClassifier cl : classifiers) {
out.println("Validations for " + cl.getName() + ":");
for (CachedValidator vConf : validationMap1.get(cl)) {
out.println(" " + vConf.getSeverity() + " - " + vConf.getValidationID());
}
}
}
public void dumpValidationMap2(PrintStream stream) {
PrintStream out = (stream == null) ? System.out : stream;
List<EClassifier> classifiers = validationMap2.keySet().stream()
.sorted(Comparator.comparing(EClassifier::getName))
.collect(Collectors.toList());
for (EClassifier cl : classifiers) {
out.println("Validations for " + cl.getName() + ":");
for (CachedValidator vConf : validationMap2.get(cl)) {
out.println(" " + vConf.getSeverity() + " - " + vConf.getValidationID());
}
}
}
}