/******************************************************************************* * Copyright (c) 2010-2012, Zoltan Ujhelyi, Mark Czotter, 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, Mark Czotter - initial API and implementation *******************************************************************************/ package org.eclipse.incquery.tooling.core.generator.builder; import java.util.List; import org.apache.log4j.Logger; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IStorage; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.emf.codegen.ecore.genmodel.GenPackage; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.WrappedException; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.PackageImport; import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.PatternModel; import org.eclipse.incquery.patternlanguage.emf.helper.EMFPatternLanguageHelper; import org.eclipse.incquery.patternlanguage.helper.CorePatternLanguageHelper; import org.eclipse.incquery.patternlanguage.patternLanguage.CheckConstraint; import org.eclipse.incquery.patternlanguage.patternLanguage.Constraint; import org.eclipse.incquery.patternlanguage.patternLanguage.Pattern; import org.eclipse.incquery.patternlanguage.patternLanguage.PatternBody; import org.eclipse.incquery.runtime.util.CheckExpressionUtil; import org.eclipse.incquery.tooling.core.generator.ExtensionGenerator; import org.eclipse.incquery.tooling.core.generator.GenerateMatcherFactoryExtension; import org.eclipse.incquery.tooling.core.generator.GenerateXExpressionEvaluatorExtension; import org.eclipse.incquery.tooling.core.generator.builder.xmi.XmiModelSupport; import org.eclipse.incquery.tooling.core.generator.fragments.IGenerationFragment; import org.eclipse.incquery.tooling.core.generator.fragments.IGenerationFragmentProvider; import org.eclipse.incquery.tooling.core.generator.genmodel.IEiqGenmodelProvider; import org.eclipse.incquery.tooling.core.generator.util.EMFPatternLanguageJvmModelInferrerUtil; import org.eclipse.incquery.tooling.core.project.ProjectGenerationHelper; import org.eclipse.pde.core.plugin.IPluginExtension; import org.eclipse.xtext.builder.BuilderParticipant; import org.eclipse.xtext.builder.EclipseResourceFileSystemAccess2; import org.eclipse.xtext.generator.IGenerator; import org.eclipse.xtext.resource.IResourceDescription; import org.eclipse.xtext.resource.IResourceDescription.Delta; import org.eclipse.xtext.ui.resource.IStorage2UriMapper; import org.eclipse.xtext.util.Pair; import org.eclipse.xtext.xbase.XExpression; import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Injector; /** * @author Mark Czotter */ public class EMFPatternLanguageBuilderParticipant extends BuilderParticipant { @Inject private Injector injector; @Inject private IGenerator generator; @Inject private IGenerationFragmentProvider fragmentProvider; @Inject private EMFPatternLanguageJvmModelInferrerUtil util; @Inject private XmiModelSupport xmiModelSupport; @Inject private EnsurePluginSupport ensureSupport; @Inject private CleanSupport cleanSupport; @Inject private EclipseResourceSupport eclipseResourceSupport; @Inject private GenerateMatcherFactoryExtension matcherFactoryExtensionGenerator; @Inject private GenerateXExpressionEvaluatorExtension xExpressionEvaluatorExtensionGenerator; @Inject private IEiqGenmodelProvider genmodelProvider; @Inject private Logger logger; @Inject private IStorage2UriMapper storage2UriMapper; @Override public void build(final IBuildContext context, IProgressMonitor monitor) throws CoreException { if (!isEnabled(context)) { return; } final List<IResourceDescription.Delta> relevantDeltas = getRelevantDeltas(context); if (relevantDeltas.isEmpty()) { return; } // monitor handling if (monitor.isCanceled()) { throw new OperationCanceledException(); } SubMonitor progress = SubMonitor.convert(monitor, 5); final IProject modelProject = context.getBuiltProject(); modelProject.refreshLocal(IResource.DEPTH_INFINITE, progress.newChild(1)); if (context.getBuildType() == BuildType.CLEAN || context.getBuildType() == BuildType.RECOVERY) { cleanSupport.fullClean(context, progress.newChild(1)); // invoke clean build on main project src-gen super.build(context, progress.newChild(1)); if (context.getBuildType() == BuildType.CLEAN) { // work 2 unit if clean build is performed (xmi build, and // ensure) progress.worked(2); return; } } else { ensureSupport.clean(); cleanSupport.normalClean(context, relevantDeltas, progress.newChild(1)); } super.build(context, progress.newChild(1)); // normal cleanUp and codegen done on every delta, do XMI Model build xmiModelSupport.build(relevantDeltas.get(0), context, progress.newChild(1)); // normal code generation done, extensions, packages ready to add to the // plug-ins ensureSupport.ensure(modelProject, progress.newChild(1)); } @Override protected void handleChangedContents(Delta delta, IBuildContext context, EclipseResourceFileSystemAccess2 fileSystemAccess) throws CoreException { // TODO: we will run out of memory here if the number of deltas is large // enough Resource deltaResource = context.getResourceSet().getResource(delta.getUri(), true); if (shouldGenerate(deltaResource, context)) { try { // do inferred jvm model to code transformation generator.doGenerate(deltaResource, fileSystemAccess); doPostGenerate(deltaResource, context); } catch (RuntimeException e) { if (e.getCause() instanceof CoreException) { throw (CoreException) e.getCause(); } throw e; } } } /** * From all {@link Pattern} instance in the current deltaResource, computes various additions to the modelProject, * and executes the provided fragments. Various contribution: package export, MatcherFactory extension, validation * constraint stuff. * * * @param deltaResource * @param context * @throws CoreException */ private void doPostGenerate(Resource deltaResource, IBuildContext context) throws CoreException { final IProject project = context.getBuiltProject(); ExtensionGenerator extGenerator = new ExtensionGenerator(); extGenerator.setProject(project); calculateEMFModelProjects(deltaResource, project); TreeIterator<EObject> it = deltaResource.getAllContents(); while (it.hasNext()) { EObject obj = it.next(); if (obj instanceof Pattern && !CorePatternLanguageHelper.isPrivate((Pattern) obj)) { Pattern pattern = (Pattern) obj; Iterable<IPluginExtension> matcherFactoryExtensionContribution = matcherFactoryExtensionGenerator .extensionContribution(pattern, extGenerator); ensureSupport.appendAllExtension(project, matcherFactoryExtensionContribution); for (PatternBody patternBody : pattern.getBodies()) { for (Constraint constraint : patternBody.getConstraints()) { if (constraint instanceof CheckConstraint) { CheckConstraint checkConstraint = (CheckConstraint) constraint; XExpression xExpression = checkConstraint.getExpression(); String expressionID = CheckExpressionUtil.getExpressionUniqueID(pattern, xExpression); String expressionUniqueNameInPattern = CheckExpressionUtil .getExpressionUniqueNameInPattern(pattern, xExpression); Iterable<IPluginExtension> xExpressionEvaluatorExtensionContribution = xExpressionEvaluatorExtensionGenerator .extensionContribution(pattern, expressionID, expressionUniqueNameInPattern, extGenerator); ensureSupport.appendAllExtension(project, xExpressionEvaluatorExtensionContribution); } } } executeGeneratorFragments(context.getBuiltProject(), pattern); ensureSupport.exportPackage(project, util.getPackageName(pattern)); } } } private void calculateEMFModelProjects(Resource deltaResource, IProject project) { TreeIterator<EObject> it = deltaResource.getAllContents(); while (it.hasNext()) { EObject obj = it.next(); if (obj instanceof PatternModel) { PatternModel patternModel = (PatternModel) obj; for (PackageImport packageImport : EMFPatternLanguageHelper.getPackageImportsIterable(patternModel)) { GenPackage genPackage = genmodelProvider.findGenPackage(packageImport, packageImport.getEPackage()); if (genPackage != null) { String modelPluginID = genPackage.getGenModel().getModelPluginID(); if (modelPluginID != null && !modelPluginID.isEmpty()) { ensureSupport.addModelBundleId(project, modelPluginID); } } } it.prune(); } } } /** * Executes all {@link IGenerationFragment} provided for the current {@link Pattern}. * * @param modelProject * @param pattern * @throws CoreException */ private void executeGeneratorFragments(IProject modelProject, Pattern pattern) throws CoreException { for (IGenerationFragment fragment : fragmentProvider.getFragmentsForPattern(pattern)) { try { injector.injectMembers(fragment); executeGeneratorFragment(fragment, modelProject, pattern); } catch (Exception e) { String msg = String.format("Exception when executing generation for '%s' in fragment '%s'", CorePatternLanguageHelper.getFullyQualifiedName(pattern), fragment.getClass() .getCanonicalName()); logger.error(msg, e); } } } private void executeGeneratorFragment(IGenerationFragment fragment, IProject modelProject, Pattern pattern) throws CoreException { IProject targetProject = createOrGetTargetProject(modelProject, fragment); EclipseResourceFileSystemAccess2 fsa = eclipseResourceSupport.createProjectFileSystemAccess(targetProject); fragment.generateFiles(pattern, fsa); // Generating Eclipse extensions ExtensionGenerator exGenerator = new ExtensionGenerator(); exGenerator.setProject(targetProject); Iterable<IPluginExtension> extensionContribution = fragment.extensionContribution(pattern, exGenerator); // Gathering all registered extensions together to avoid unnecessary // plugin.xml modifications // Both for performance and for avoiding race conditions ensureSupport.appendAllExtension(targetProject, extensionContribution); } /** * Creates or finds {@link IProject} associated with the {@link IGenerationFragment}. If the project exist * dependencies ensured based on the {@link IGenerationFragment} contribution. If the project not exist, it will be * initialized. * * @param modelProject * @param fragment * @return * @throws CoreException */ private IProject createOrGetTargetProject(IProject modelProject, IGenerationFragment fragment) throws CoreException { String postfix = fragment.getProjectPostfix(); String modelProjectName = ProjectGenerationHelper.getBundleSymbolicName(modelProject); if (postfix == null || postfix.isEmpty()) { ProjectGenerationHelper.ensureBundleDependencies(modelProject, Lists.newArrayList(fragment.getProjectDependencies())); return modelProject; } else { List<String> dependencies = Lists.newArrayList(); dependencies.add(modelProjectName); dependencies.addAll(ensureSupport.getModelBundleDependencies(modelProject)); dependencies.addAll(Lists.newArrayList(fragment.getProjectDependencies())); IProject targetProject = fragmentProvider.getFragmentProject(modelProject, fragment); if (!targetProject.exists()) { ProjectGenerationHelper.initializePluginProject(targetProject, dependencies, fragment.getAdditionalBinIncludes()); } else { ProjectGenerationHelper.ensureBundleDependencies(targetProject, dependencies); } return targetProject; } } @Override protected boolean shouldGenerate(Resource resource, IBuildContext context) { try { Iterable<Pair<IStorage, IProject>> storages = storage2UriMapper.getStorages(resource.getURI()); for (Pair<IStorage, IProject> pair : storages) { if (pair.getFirst() instanceof IFile && pair.getSecond().equals(context.getBuiltProject())) { IFile file = (IFile) pair.getFirst(); return file.findMaxProblemSeverity("org.eclipse.xtext.ui.check", true, IResource.DEPTH_INFINITE) != IMarker.SEVERITY_ERROR; } } return false; } catch (CoreException exc) { throw new WrappedException(exc); } } }