| /** |
| ******************************************************************************** |
| * 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())); |
| } |
| } |
| |
| } |