/******************************************************************************* * Copyright (c) 2010-2012, Zoltan Ujhelyi, Tamas Szabo, Istvan Rath 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: * Zoltan Ujhelyi, Tamas Szabo - initial API and implementation *******************************************************************************/ package org.eclipse.incquery.tooling.ui.queryexplorer.util; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; 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.ILog; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EValidator; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.incquery.databinding.runtime.adapter.DatabindingAdapter; import org.eclipse.incquery.databinding.runtime.adapter.GenericDatabindingAdapter; import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.PatternModel; import org.eclipse.incquery.patternlanguage.helper.CorePatternLanguageHelper; import org.eclipse.incquery.patternlanguage.patternLanguage.Annotation; import org.eclipse.incquery.patternlanguage.patternLanguage.AnnotationParameter; import org.eclipse.incquery.patternlanguage.patternLanguage.Pattern; import org.eclipse.incquery.patternlanguage.patternLanguage.ValueReference; import org.eclipse.incquery.patternlanguage.patternLanguage.Variable; import org.eclipse.incquery.patternlanguage.patternLanguage.impl.BoolValueImpl; import org.eclipse.incquery.patternlanguage.patternLanguage.impl.StringValueImpl; import org.eclipse.incquery.runtime.api.IMatcherFactory; import org.eclipse.incquery.runtime.api.IPatternMatch; import org.eclipse.incquery.runtime.api.IncQueryMatcher; import org.eclipse.incquery.runtime.extensibility.MatcherFactoryRegistry; import org.eclipse.incquery.tooling.ui.IncQueryGUIPlugin; import org.eclipse.incquery.tooling.ui.queryexplorer.content.matcher.MatcherTreeViewerRootKey; import org.eclipse.incquery.tooling.ui.queryexplorer.content.matcher.ObservablePatternMatcherRoot; import org.eclipse.xtext.ui.resource.IResourceSetProvider; import com.google.inject.Inject; import com.google.inject.Singleton; /** * The util contains several useful methods for the databinding operations. * * @author Tamas Szabo * */ @Singleton public class DatabindingUtil { /** * */ private static final String DATABINDING_EXTENSION = "org.eclipse.incquery.databinding.runtime.databinding"; private static Map<URI, AdapterFactoryLabelProvider> registeredItemProviders = new HashMap<URI, AdapterFactoryLabelProvider>(); private static Map<URI, IConfigurationElement> uriConfElementMap = null; private static ILog logger = IncQueryGUIPlugin.getDefault().getLog(); private static Map<String, IMarker> orderByPatternMarkers = new HashMap<String, IMarker>(); private static List<Pattern> generatedPatterns; private static Map<Pattern, IMatcherFactory<?>> generatedMatcherFactories; public static final String QUERY_EXPLORER_ANNOTATION = "QueryExplorer"; public static final String PATTERNUI_ANNOTATION = "PatternUI"; public static final String ORDERBY_ANNOTATION = "OrderBy"; @Inject private IResourceSetProvider resSetProvider; /** * Creates a marker with a warning for the given pattern. The marker's message will be set to the given message * parameter. * * @param patternFqn * the fully qualified name of the pattern * @param message * the warning message for the marker */ public static void addOrderByPatternWarning(String patternFqn, String message) { if (orderByPatternMarkers.get(patternFqn) == null) { Pattern pattern = PatternRegistry.getInstance().getPatternByFqn(patternFqn); URI uri = pattern.eResource().getURI(); String platformString = uri.toPlatformString(true); IResource markerLoc = ResourcesPlugin.getWorkspace().getRoot().findMember(platformString); try { IMarker marker = markerLoc.createMarker(EValidator.MARKER); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING); marker.setAttribute(IMarker.TRANSIENT, true); marker.setAttribute(IMarker.LOCATION, pattern.getName()); marker.setAttribute(EValidator.URI_ATTRIBUTE, EcoreUtil.getURI(pattern).toString()); marker.setAttribute(IMarker.MESSAGE, message); orderByPatternMarkers.put(patternFqn, marker); } catch (CoreException e) { logger.log(new Status(IStatus.ERROR, IncQueryGUIPlugin.PLUGIN_ID, "Marker could not be created for pattern: " + patternFqn, e)); } } } private static Map<Pattern, IMatcherFactory<?>> collectGeneratedMatcherFactories() { Map<Pattern, IMatcherFactory<?>> factories = new HashMap<Pattern, IMatcherFactory<?>>(); for (IMatcherFactory<?> factory : MatcherFactoryRegistry.getContributedMatcherFactories()) { Pattern pattern = factory.getPattern(); Boolean annotationValue = getValueOfQueryExplorerAnnotation(pattern); if (annotationValue != null && annotationValue) { factories.put(pattern, factory); } } return factories; } public static Boolean getValueOfQueryExplorerAnnotation(Pattern pattern) { Annotation annotation = CorePatternLanguageHelper.getFirstAnnotationByName(pattern, QUERY_EXPLORER_ANNOTATION); if (annotation == null) { return null; } else { for (AnnotationParameter ap : annotation.getParameters()) { if (ap.getName().equalsIgnoreCase("display")) { return Boolean.valueOf(((BoolValueImpl) ap.getValue()).isValue()); } } return Boolean.TRUE; } } public static synchronized Collection<IMatcherFactory<?>> getGeneratedMatcherFactories() { if (generatedMatcherFactories == null) { generatedMatcherFactories = collectGeneratedMatcherFactories(); } return Collections.unmodifiableCollection(generatedMatcherFactories.values()); } public static synchronized List<Pattern> getGeneratedPatterns() { if (generatedPatterns == null) { generatedPatterns = collectGeneratedPatterns(); } return Collections.unmodifiableList(generatedPatterns); } private static List<Pattern> collectGeneratedPatterns() { List<Pattern> patterns = new ArrayList<Pattern>(); for (IMatcherFactory<?> factory : getGeneratedMatcherFactories()) { patterns.add(factory.getPattern()); } return patterns; } /** * Removes the marker for the given pattern if it is present. * * @param patternFqn * the fully qualified name of the pattern */ public static void removeOrderByPatternWarning(String patternFqn) { IMarker marker = orderByPatternMarkers.remove(patternFqn); if (marker != null) { try { marker.delete(); } catch (CoreException e) { logger.log(new Status(IStatus.ERROR, IncQueryGUIPlugin.PLUGIN_ID, "Marker could not be deleted: " + marker.toString(), e)); } } } /** * Returns the {@link AdapterFactoryLabelProvider} instance for the given uri. * * @param uri * the uri * @return the {@link AdapterFactoryLabelProvider} instance */ public synchronized static AdapterFactoryLabelProvider getAdapterFactoryLabelProvider(URI uri) { if (uriConfElementMap == null) { uriConfElementMap = collectItemProviders(); } AdapterFactoryLabelProvider af = registeredItemProviders.get(uri); if (af != null) { return af; } else { IConfigurationElement ce = uriConfElementMap.get(uri); try { if (ce != null) { Object obj = ce.createExecutableExtension("class"); AdapterFactoryLabelProvider lp = new AdapterFactoryLabelProvider((AdapterFactory) obj); registeredItemProviders.put(uri, lp); return lp; } } catch (CoreException e) { logger.log(new Status(IStatus.ERROR, IncQueryGUIPlugin.PLUGIN_ID, "AdapterFactory could not be created for uri: " + uri.toString(), e)); } return null; } } private static Map<URI, IConfigurationElement> collectItemProviders() { Map<URI, IConfigurationElement> result = new HashMap<URI, IConfigurationElement>(); try { IExtensionRegistry reg = Platform.getExtensionRegistry(); IExtensionPoint ep = reg.getExtensionPoint("org.eclipse.emf.edit.itemProviderAdapterFactories"); for (IExtension e : ep.getExtensions()) { for (IConfigurationElement ce : e.getConfigurationElements()) { if (ce.getName().matches("factory")) { URI uri = URI.createURI(ce.getAttribute("uri")); result.put(uri, ce); } } } } catch (Exception e) { logger.log(new Status(IStatus.ERROR, IncQueryGUIPlugin.PLUGIN_ID, "Collecting item providers failed.", e)); } return result; } /** * Returns a text message for a generated, not filtered matcher about the current match size. * * @param matcher * @param matchesSize * @param patternFqn * @return */ public static String getMessage(IncQueryMatcher<? extends IPatternMatch> matcher, int matchesSize, String patternFqn) { return getMessage(matcher, matchesSize, patternFqn, true, false, null); } /** * Returns a text message about the matches size for the given matcher. * * @param matcher * the {@link IncQueryMatcher} instance * @param matchesSize * the size of the matchset * @param patternFqn * the pattern fqn * @param isGenerated * true, if the matcher is generated, false if generic * @param isFiltered * true, if the matcher is filtered, false otherwise * @return the label associated to the matcher */ public static String getMessage(IncQueryMatcher<? extends IPatternMatch> matcher, int matchesSize, String patternFqn, boolean isGenerated, boolean isFiltered, String exceptionMessage) { String isGeneratedString = isGenerated ? " (Generated)" : " (Runtime)"; if (matcher == null) { return String.format("%s - %s (see Error Log)", patternFqn, exceptionMessage); } else { String matchString; switch (matchesSize) { case 0: matchString = "No matches"; break; case 1: matchString = "1 match"; break; default: matchString = String.format("%d matches", matchesSize); } String filtered = isFiltered ? " - Filtered" : ""; return String.format("%s - %s %s %s", matcher.getPatternName(), matchString, filtered, isGeneratedString); } } /** * Get the value of the PatternUI annotation's message attribute for the pattern which name is patternName. * * @param patternName * the name of the pattern * @return the content of the message attribute */ public static String getMessage(IPatternMatch match, boolean generatedMatcher) { if (generatedMatcher) { return getMessageForGeneratedMatcher(match); } else { return getMessageForGenericMatcher(match); } } private static String getMessageForGeneratedMatcher(IPatternMatch match) { String patternName = match.patternName(); try { IExtensionRegistry reg = Platform.getExtensionRegistry(); IExtensionPoint ep = reg.getExtensionPoint(DATABINDING_EXTENSION); for (IExtension e : ep.getExtensions()) { for (IConfigurationElement ce : e.getConfigurationElements()) { String[] tokens = patternName.split("\\."); String pattern = tokens[tokens.length - 1]; if (ce.getName().equals("databinding") && ce.getAttribute("patternName").equalsIgnoreCase(pattern)) { return ce.getAttribute("message"); } } } } catch (Exception e) { logger.log(new Status(IStatus.ERROR, IncQueryGUIPlugin.PLUGIN_ID, "Message could not be retrieved for generated matcher.", e)); } return null; } private static String getMessageForGenericMatcher(IPatternMatch match) { String patternName = match.patternName(); Pattern pattern = null; // find PatternUI annotation for (Pattern p : PatternRegistry.getInstance().getPatterns()) { if (CorePatternLanguageHelper.getFullyQualifiedName(p).matches(patternName)) { pattern = p; Annotation annotation = CorePatternLanguageHelper .getFirstAnnotationByName(p, QUERY_EXPLORER_ANNOTATION); if (annotation == null) { // Try with deprecated PatternUI annotation annotation = CorePatternLanguageHelper.getFirstAnnotationByName(p, PATTERNUI_ANNOTATION); } if (annotation != null) { for (AnnotationParameter ap : annotation.getParameters()) { if (ap.getName().matches("message")) { ValueReference valRef = ap.getValue(); if (valRef instanceof StringValueImpl) { return ((StringValueImpl) valRef).getValue(); } } } } } } // PatternUI annotation was not found if (pattern != null) { StringBuilder message = new StringBuilder(); if (pattern.getParameters().size() == 0) { message.append("(Match)"); } else { int i = 0; for (Variable v : pattern.getParameters()) { if (i > 0) { message.append(", "); } // message += v.getName()+"=$"+v.getName()+"$"; message.append(String.format("%s=$%s$", v.getName(), v.getName())); i++; } } return message.toString(); } return null; } /** * Get the DatabindingAdapter generated for the pattern whose name is patternName * * @param patternName * the name of the pattern * @return an instance of the DatabindingAdapter class generated for the pattern */ public static DatabindingAdapter<IPatternMatch> getDatabindingAdapter(String patternName, boolean generatedMatcher) { if (generatedMatcher) { return getDatabindingAdapterForGeneratedMatcher(patternName); } else { return getDatabindingAdapterForGenericMatcher(patternName); } } @SuppressWarnings("unchecked") private static DatabindingAdapter<IPatternMatch> getDatabindingAdapterForGeneratedMatcher(String patternName) { try { IExtensionRegistry reg = Platform.getExtensionRegistry(); IExtensionPoint ep = reg.getExtensionPoint(DATABINDING_EXTENSION); for (IExtension e : ep.getExtensions()) { for (IConfigurationElement ce : e.getConfigurationElements()) { String[] tokens = patternName.split("\\."); String pattern = tokens[tokens.length - 1]; if (ce.getName().equals("databinding") && ce.getAttribute("patternName").equalsIgnoreCase(pattern)) { Object obj = ce.createExecutableExtension("class"); if (obj instanceof DatabindingAdapter) { return (DatabindingAdapter<IPatternMatch>) obj; } } } } } catch (Exception e) { logger.log(new Status(IStatus.ERROR, IncQueryGUIPlugin.PLUGIN_ID, "Could not find DatabindableMatcher for pattern named: " + patternName, e)); } return null; } private static DatabindingAdapter<IPatternMatch> getDatabindingAdapterForGenericMatcher(String patternName) { Pattern pattern = PatternRegistry.getInstance().getPatternByFqn(patternName); GenericDatabindingAdapter adapter = new GenericDatabindingAdapter(pattern); return adapter; } /** * Returns the generated matcher factory for the given generated pattern. * * @param pattern * the pattern instance * @return the matcher factory for the given pattern */ public static IMatcherFactory<?> getMatcherFactoryForGeneratedPattern(Pattern pattern) { return generatedMatcherFactories.get(pattern); } /** * Create a PatternMatcher root for the given key element. * * @param key * the key element (editorpart + notifier) * @return the PatternMatcherRoot element */ public static ObservablePatternMatcherRoot createPatternMatcherRoot(MatcherTreeViewerRootKey key) { ObservablePatternMatcherRoot root = new ObservablePatternMatcherRoot(key); List<Pattern> activePatterns = PatternRegistry.getInstance().getActivePatterns(); // runtime & generated matchers root.registerPattern(activePatterns.toArray(new Pattern[activePatterns.size()])); return root; } /** * Parses the given .eiq file into a {@link PatternModel}. * * @param file * the .eiq file instance * @return the parsed pattern model */ public PatternModel parseEPM(IFile file) { if (file == null) { return null; } ResourceSet resourceSet = resSetProvider.get(file.getProject()); URI fileURI = URI.createPlatformResourceURI(file.getFullPath().toString(), false); Resource resource = resourceSet.getResource(fileURI, true); if (resource != null) { if (resource.getErrors().size() > 0) { return null; } if (resource.getContents().size() >= 1) { EObject topElement = resource.getContents().get(0); return topElement instanceof PatternModel ? (PatternModel) topElement : null; } } return null; } }