/******************************************************************************* * Copyright (c) 2004-2011 Abel Hegedus 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: * Abel Hegedus - initial API and implementation *******************************************************************************/ package org.eclipse.incquery.runtime.extensibility; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.Platform; import org.eclipse.incquery.patternlanguage.helper.CorePatternLanguageHelper; import org.eclipse.incquery.patternlanguage.patternLanguage.Pattern; import org.eclipse.incquery.runtime.IExtensions; import org.eclipse.incquery.runtime.api.GenericMatcherFactory; import org.eclipse.incquery.runtime.api.IMatcherFactory; import org.eclipse.incquery.runtime.api.IPatternMatch; import org.eclipse.incquery.runtime.api.IncQueryEngine; import org.eclipse.incquery.runtime.api.IncQueryMatcher; /** * Registry for accessing matcher factory instances based on Pattern or pattern ID * * @author Abel Hegedus * */ public final class MatcherFactoryRegistry { private static final Map<String, IMatcherFactory<?>> MATCHER_FACTORIES = createMatcherFactories(); /** * Utility class constructor hidden */ private MatcherFactoryRegistry() { } private static Map<String, IMatcherFactory<?>> createMatcherFactories() { final Map<String, IMatcherFactory<?>> factories = new HashMap<String, IMatcherFactory<?>>(); initRegistry(factories); return factories; } // Does not use the field MATCHER_FACTORIES as it may still be uninitialized private static void initRegistry(Map<String, IMatcherFactory<?>> factories) { factories.clear(); IExtensionRegistry reg = Platform.getExtensionRegistry(); if (reg == null) { return; } IExtensionPoint poi = reg.getExtensionPoint(IExtensions.MATCHERFACTORY_EXTENSION_POINT_ID); if (poi != null) { IExtension[] exts = poi.getExtensions(); Set<String> duplicates = new HashSet<String>(); for (IExtension ext : exts) { IConfigurationElement[] els = ext.getConfigurationElements(); for (IConfigurationElement el : els) { if (el.getName().equals("matcher")) { prepareMatcherFactory(factories, duplicates, el); } else { IncQueryEngine.getDefaultLogger().error( "[MatcherFactoryRegistry] Unknown configuration element " + el.getName() + " in plugin.xml of " + el.getDeclaringExtension().getUniqueIdentifier()); } } } if (!duplicates.isEmpty()) { StringBuilder duplicateSB = new StringBuilder( "[MatcherFactoryRegistry] Trying to register patterns with the same FQN multiple times. Check your plug-in configuration!\n"); duplicateSB.append("The following pattern FQNs appeared multiple times:\n"); for (String fqn : duplicates) { duplicateSB.append(String.format("\t%s%n", fqn)); } IncQueryEngine.getDefaultLogger().warn(duplicateSB.toString()); } } } private static void prepareMatcherFactory(Map<String, IMatcherFactory<?>> factories, Set<String> duplicates, IConfigurationElement el) { try { String id = el.getAttribute("id"); @SuppressWarnings("unchecked") IMatcherFactoryProvider<IMatcherFactory<IncQueryMatcher<IPatternMatch>>> provider = (IMatcherFactoryProvider<IMatcherFactory<IncQueryMatcher<IPatternMatch>>>) el .createExecutableExtension("factoryProvider"); IMatcherFactory<IncQueryMatcher<IPatternMatch>> matcherFactory = provider.get(); String fullyQualifiedName = matcherFactory.getPatternFullyQualifiedName(); if (id.equals(fullyQualifiedName)) { if (factories.containsKey(fullyQualifiedName)) { duplicates.add(fullyQualifiedName); } else { factories.put(fullyQualifiedName, matcherFactory); } } else { throw new UnsupportedOperationException("Id attribute value " + id + " does not equal pattern FQN of factory " + fullyQualifiedName + " in plugin.xml of " + el.getDeclaringExtension().getUniqueIdentifier()); } } catch (Exception e) { IncQueryEngine.getDefaultLogger().error( "[MatcherFactoryRegistry] Exception during matcher factory registry initialization " + e.getMessage(), e); } } /** * Puts the factory in the registry, unless it already contains a factory for the given pattern FQN * * @param factory */ public static void registerMatcherFactory(IMatcherFactory<?> factory) { String qualifiedName = factory.getPatternFullyQualifiedName(); if (!MATCHER_FACTORIES.containsKey(qualifiedName)) { MATCHER_FACTORIES.put(qualifiedName, factory); } else { IncQueryEngine .getDefaultLogger() .warn(String .format("[MatcherFactoryRegistry] Trying to register duplicate FQN (%s). Check your plug-in configuration!", qualifiedName)); } } /** * @return a copy of the set of contributed matcher factories */ public static Set<IMatcherFactory<?>> getContributedMatcherFactories() { return new HashSet<IMatcherFactory<?>>(MATCHER_FACTORIES.values()); } /** * Returns the specific pattern matcher factory, if it is registered, null otherwise * * @param patternFqn * @return */ public static IMatcherFactory<?> getMatcherFactory(String patternFqn) { if (MATCHER_FACTORIES.containsKey(patternFqn)) { return MATCHER_FACTORIES.get(patternFqn); } return null; } /** * Returns the specific pattern matcher factory, if it is registered, null otherwise * * @param pattern * @return */ public static IMatcherFactory<?> getMatcherFactory(Pattern pattern) { String fullyQualifiedName = CorePatternLanguageHelper.getFullyQualifiedName(pattern); if (MATCHER_FACTORIES.containsKey(fullyQualifiedName)) { return MATCHER_FACTORIES.get(fullyQualifiedName); } return null; } /** * Returns a generic pattern matcher factory if a specific factory is not registered * * @param pattern * @return */ public static IMatcherFactory<?> getOrCreateMatcherFactory(Pattern pattern) { String fullyQualifiedName = CorePatternLanguageHelper.getFullyQualifiedName(pattern); if (MATCHER_FACTORIES.containsKey(fullyQualifiedName)) { return MATCHER_FACTORIES.get(fullyQualifiedName); } return new GenericMatcherFactory(pattern); } /** * Returns the set of matcher factories in a given package. Only matcher factories with the exact package fully * qualified name are returned. * * @param packageFQN * the fully qualified name of the package * @return the set of matcher factories inside the given package, empty set otherwise. */ public static Set<IMatcherFactory<?>> getPatternGroup(String packageFQN) { return getPatternGroupOrSubTree(packageFQN, false); } /** * Returns the set of matcher factories in a given package. Matcher factories with package names starting with the * given package are returned. * * @param packageFQN * the fully qualified name of the package * @return the set of matcher factories in the given package subtree, empty set otherwise. */ public static Set<IMatcherFactory<?>> getPatternSubTree(String packageFQN) { return getPatternGroupOrSubTree(packageFQN, true); } /** * Returns a pattern group for the given package * * @param packageFQN * the fully qualified name of the package * @param includeSubPackages * if true, the pattern is added if it is in the package hierarchy, if false, the pattern is added only * if it is in the given package * @return the matcher factories in the group */ private static Set<IMatcherFactory<?>> getPatternGroupOrSubTree(String packageFQN, boolean includeSubPackages) { Map<String, Set<IMatcherFactory<?>>> map = new HashMap<String, Set<IMatcherFactory<?>>>(); if (map.containsKey(packageFQN)) { return map.get(packageFQN); } else { Set<IMatcherFactory<?>> group = new HashSet<IMatcherFactory<?>>(); for (Entry<String, IMatcherFactory<?>> entry : MATCHER_FACTORIES.entrySet()) { addPatternToGroup(packageFQN, group, entry.getKey(), entry.getValue(), includeSubPackages); } if (group.size() > 0) { map.put(packageFQN, group); } return group; } } /** * Adds the factory to an existing group if the package of the factory's pattern matches the given package name. * * @param packageFQN * the fully qualified name of the package * @param group * the group to add the factory to * @param patternFQN * the fully qualified name of the pattern * @param factory * the matcher factory of the pattern * @param includeSubPackages * if true, the pattern is added if it is in the package hierarchy, if false, the pattern is added only * if it is in the given package */ private static void addPatternToGroup(String packageFQN, Set<IMatcherFactory<?>> group, String patternFQN, IMatcherFactory<?> factory, boolean includeSubPackages) { if (packageFQN.length() + 1 < patternFQN.length()) { if (includeSubPackages) { if (patternFQN.startsWith(packageFQN + '.')) { group.add(factory); } } else { String name = patternFQN.substring(patternFQN.lastIndexOf('.') + 1, patternFQN.length()); if (patternFQN.equals(packageFQN + '.' + name)) { group.add(factory); } } } } }