/******************************************************************************* * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro * 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: * Gabor Bergmann - initial API and implementation *******************************************************************************/ package org.eclipse.incquery.runtime.internal; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.eclipse.incquery.patternlanguage.emf.validation.PatternSetValidationDiagnostics; import org.eclipse.incquery.patternlanguage.emf.validation.PatternSetValidator; import org.eclipse.incquery.patternlanguage.emf.validation.PatternValidationStatus; import org.eclipse.incquery.patternlanguage.helper.CorePatternLanguageHelper; import org.eclipse.incquery.patternlanguage.patternLanguage.Pattern; import com.google.inject.Injector; /** * Stateful sanitizer that maintains a set of admitted patterns. Patterns go through sanitization checks (validation + * name uniqueness) before they can be admitted. * * <p> * INVARIANTS: * <ul> * <li>the set of admitted patterns is closed with respect to references. * <li>the set of admitted patterns are free of errors. * <li>admitted patterns have unique qualified names. * </ul> * * @author Bergmann Gábor * */ public class PatternSanitizer { Set<Pattern> admittedPatterns = new HashSet<Pattern>(); Map<String, Pattern> patternsByName = new HashMap<String, Pattern>(); Logger logger; /** * Creates an instance of the stateful sanitizer. * * @param logger * where detected problems will be logged */ public PatternSanitizer(final Logger logger) { super(); this.logger = logger; } /** * Admits a new pattern, checking if it passes validation and name uniqueness checks. Referenced patterns likewise * go through the checks. Transactional semantics: will only admit any patterns if none of them have any errors. * * @param pattern * a pattern that should be validated. * @return false if the pattern was not possible to admit, true if it passed all validation checks (or was already * admitted before) */ public boolean admit(Pattern pattern) { return admit(Collections.singletonList(pattern)); } /** * Admits new patterns, checking whether they all pass validation and name uniqueness checks. Referenced patterns * likewise go through the checks. Transactional semantics: will only admit any patterns if none of them have any * errors. * * @param patterns * the collection of patterns that should be validated together. * @return false if the patterns were not possible to admit, true if they passed all validation checks (or were * already admitted before) */ public boolean admit(Collection<Pattern> patterns) { Set<Pattern> newPatterns = getAllReferencedUnvalidatedPatterns(patterns); if (newPatterns.isEmpty()) return true; // TODO validate(toBeValidated) as a group Set<Pattern> inadmissible = new HashSet<Pattern>(); Map<String, Pattern> newPatternsByName = new HashMap<String, Pattern>(); for (Pattern current : newPatterns) { if (current == null) { inadmissible.add(current); logger.error("Null pattern value"); } final String fullyQualifiedName = CorePatternLanguageHelper.getFullyQualifiedName(current); final boolean duplicate = patternsByName.containsKey(fullyQualifiedName) || newPatternsByName.containsKey(fullyQualifiedName); if (duplicate) { inadmissible.add(current); logger.error("Duplicate (qualified) name of pattern: " + fullyQualifiedName); } else { newPatternsByName.put(fullyQualifiedName, current); } final boolean validationPassed = true; // validator == null || validator.validate(current, errorOnlyLogger); if (!validationPassed) { inadmissible.add(current); } } Injector injector = XtextInjectorProvider.INSTANCE.getInjector(); PatternSetValidator validator = injector.getInstance(PatternSetValidator.class); PatternSetValidationDiagnostics validatorResult = validator.validate(patterns); validatorResult.logErrors(logger); boolean ok = inadmissible.isEmpty() && !validatorResult.getStatus().equals(PatternValidationStatus.ERROR); if (ok) { admittedPatterns.addAll(newPatterns); patternsByName.putAll(newPatternsByName); } return ok; } /** * Gathers all patterns that are not admitted yet, but are transitively referenced from the given patterns. */ protected Set<Pattern> getAllReferencedUnvalidatedPatterns(Collection<Pattern> patterns) { Set<Pattern> toBeValidated = new HashSet<Pattern>(); LinkedList<Pattern> unexplored = new LinkedList<Pattern>(); for (Pattern pattern : patterns) { if (!admittedPatterns.contains(pattern)) { toBeValidated.add(pattern); unexplored.add(pattern); } } while (!unexplored.isEmpty()) { Pattern current = unexplored.pollFirst(); final Set<Pattern> referencedPatterns = CorePatternLanguageHelper.getReferencedPatterns(current); for (Pattern referenced : referencedPatterns) { if (!admittedPatterns.contains(referenced) && !toBeValidated.contains(referenced)) { toBeValidated.add(referenced); unexplored.add(referenced); } } } return toBeValidated; } /** * Returns the set of patterns that have been admitted so far. * * @return the admitted patterns */ public Set<Pattern> getAdmittedPatterns() { return Collections.unmodifiableSet(admittedPatterns); } }