/******************************************************************************* * Copyright (c) 2004-2011 Abel Hegedus, 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: * Abel Hegedus - initial API and implementation * Istvan Rath - refactorings to accommodate to generic/patternspecific API differences *******************************************************************************/ package hu.bme.mit.incquery.application; import java.util.Collection; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EcoreFactory; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.incquery.patternlanguage.emf.EMFPatternLanguageStandaloneSetup; import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.PatternModel; import org.eclipse.incquery.patternlanguage.emf.specification.SpecificationBuilder; import org.eclipse.incquery.patternlanguage.helper.CorePatternLanguageHelper; import org.eclipse.incquery.patternlanguage.patternLanguage.Pattern; import org.eclipse.incquery.runtime.api.AdvancedIncQueryEngine; import org.eclipse.incquery.runtime.api.IMatchProcessor; import org.eclipse.incquery.runtime.api.IMatchUpdateListener; import org.eclipse.incquery.runtime.api.IPatternMatch; import org.eclipse.incquery.runtime.api.IQuerySpecification; import org.eclipse.incquery.runtime.api.IncQueryMatcher; import org.eclipse.incquery.runtime.api.IncQueryModelUpdateListener; import org.eclipse.incquery.runtime.exception.IncQueryException; import org.eclipse.incquery.runtime.extensibility.QuerySpecificationRegistry; import org.eclipse.incquery.runtime.rete.misc.DeltaMonitor; /** * @author Abel Hegedus * @author Istvan Rath * */ public class IncQueryHeadlessAdvanced { protected Resource loadModel(URI fileURI) { // Loads the resource ResourceSet resourceSet = new ResourceSetImpl(); Resource resource = resourceSet.getResource(fileURI, true); return resource; } protected void prettyPrintMatches(StringBuilder results, Collection<? extends IPatternMatch> matches) { for (IPatternMatch match : matches) { results.append(match.prettyPrint()+"; "); } if(matches.size() == 0) { results.append("Empty match set"); } results.append("\n"); } protected void performModelModification(Resource res) { // somewhat brittle code, assumes there is a root EPackage in the Resource EPackage rootPackage = (EPackage)res.getContents().get(0); // add a new EPackage EPackage newPackage = EcoreFactory.eINSTANCE.createEPackage(); newPackage.setName("NewPackage"); rootPackage.getESubpackages().add(newPackage); } /** * Returns the match set for patternFQN over the model in modelPath in pretty printed form * * @param modelPath * @param patternFQN * @return */ public String executeDemo_GenericAPI_LoadFromEIQ(URI modelURI, URI fileURI, String patternFQN) { final StringBuilder results = new StringBuilder(); Resource resource = loadModel(modelURI); if (resource != null) { try { // get all matches of the pattern // create an *unmanaged* engine to ensure that noone else is going // to use our engine AdvancedIncQueryEngine engine = AdvancedIncQueryEngine.createUnmanagedEngine(resource); // instantiate a pattern matcher through the registry, by only knowing its FQN // assuming that there is a pattern definition registered matching 'patternFQN' Pattern p = null; // Initializing Xtext-based resource parser new EMFPatternLanguageStandaloneSetup().createInjectorAndDoEMFRegistration(); //Loading pattern resource from file ResourceSet resourceSet = new ResourceSetImpl(); Resource patternResource = resourceSet.getResource(fileURI, true); // navigate to the pattern definition that we want if (patternResource != null) { if (patternResource.getErrors().size() == 0 && patternResource.getContents().size() >= 1) { EObject topElement = patternResource.getContents().get(0); if (topElement instanceof PatternModel) { for (Pattern _p : ((PatternModel) topElement).getPatterns()) { if (patternFQN.equals(CorePatternLanguageHelper.getFullyQualifiedName(_p))) { p = _p; break; } } } } } if (p == null) { throw new RuntimeException(String.format("Pattern %s not found", patternFQN)); } SpecificationBuilder builder = new SpecificationBuilder(); final IQuerySpecification<? extends IncQueryMatcher<? extends IPatternMatch>> specification = builder.getOrCreateSpecification(p); QuerySpecificationRegistry.registerQuerySpecification(specification); // Initialize matcher from specification IncQueryMatcher<? extends IPatternMatch> matcher = engine.getMatcher(specification); if (matcher!=null) { Collection<? extends IPatternMatch> matches = matcher.getAllMatches(); prettyPrintMatches(results, matches); } // wipe the engine engine.wipe(); // after a wipe, new patterns can be rebuilt with much less overhead than // complete traversal (as the base indexes will be kept) // completely dispose of the engine once's it is not needed engine.dispose(); resource.unload(); } catch (IncQueryException e) { e.printStackTrace(); results.append(e.getMessage()); } } else { results.append("Resource not found"); } return results.toString(); } // incrementally track changes public String executeTrackChangesDemo_Advanced(URI modelURI, String patternFQN) { final StringBuilder results = new StringBuilder(); Resource resource = loadModel(modelURI); if (resource != null) { try { // initialization // phase 1: (managed) IncQueryEngine AdvancedIncQueryEngine engine = AdvancedIncQueryEngine.createUnmanagedEngine(resource); // phase 2: pattern matcher for packages IncQueryMatcher<? extends IPatternMatch> matcher = QuerySpecificationRegistry.getQuerySpecification(patternFQN).getMatcher(engine); matcher.forEachMatch(new IMatchProcessor<IPatternMatch>() { @Override public void process(IPatternMatch match) { results.append("\tMatch before modification: " + match.prettyPrint()+"\n"); } }); // phase 3: prepare for advanced change processing changeProcessing_lowlevel(results, matcher, engine); changeProcessing_deltaMonitor(results, matcher, engine); // phase 4: modify model, change processor will update results accordingly performModelModification(resource); } catch (IncQueryException e) { e.printStackTrace(); results.append(e.getMessage()); } } else { results.append("Resource not found"); } return results.toString(); } private void changeProcessing_lowlevel(final StringBuilder results, IncQueryMatcher<? extends IPatternMatch> matcher, AdvancedIncQueryEngine engine) { // (+) these update callbacks are called whenever there is an actual change in the // result set of the pattern you are interested in. Hence, they are called fewer times // than the "afterUpdates" option, giving better performance. // (-) the downside is that the callbacks are *not* guaranteed to be called in a consistent // state (i.e. when the update propagation is settled), hence // * you must not invoke pattern matching and model manipulation _inside_ the callback method // * the callbacks might encounter "hazards", i.e. when an appearance is followed immediately by a disappearance. engine.addMatchUpdateListener(matcher, new IMatchUpdateListener<IPatternMatch>() { @Override public void notifyDisappearance(IPatternMatch match) { // left empty } @Override public void notifyAppearance(IPatternMatch match) { results.append("\tNew match found by changeset low level callback: " + match.prettyPrint()+"\n"); } }, false); } @Deprecated private void changeProcessing_deltaMonitor(final StringBuilder results, IncQueryMatcher<? extends IPatternMatch> matcher, AdvancedIncQueryEngine engine) { final DeltaMonitor<? extends IPatternMatch> dm = matcher.newDeltaMonitor(false); // (+) these updates are guaranteed to be called in a *consistent* state, // i.e. when the pattern matcher is guaranteed to be consistent with the model // anything can be written into the callback method // (-) the downside is that the callback is called after *every* update // that propagates through the matcher, i.e. also when the updates do not actually // influence the result set you are interested in. Hence, the performance is somewhat // lower than for the "lowlevel" option. engine.addModelUpdateListener(new IncQueryModelUpdateListener() { @Override public void notifyChanged(ChangeLevel changeLevel) { for (IPatternMatch newMatch : dm.matchFoundEvents) { results.append("\tNew match found by changeset delta monitor: " + newMatch.prettyPrint()+"\n"); } for (IPatternMatch lostMatch : dm.matchLostEvents) { // left empty } dm.clear(); } @Override public ChangeLevel getLevel() { return ChangeLevel.MATCHSET; } }); } }