/******************************************************************************* * Copyright (c) 2006-2014 * Software Technology Group, Dresden University of Technology * DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026 * * 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: * Software Technology Group - TU Dresden, Germany; * DevBoost GmbH - Berlin, Germany * - initial API and implementation ******************************************************************************/ package org.emftext.language.mecore.resource.mecore.mopp; import java.io.IOException; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.emf.codegen.ecore.genmodel.GenModel; import org.eclipse.emf.common.util.BasicDiagnostic; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.common.util.DiagnosticException; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.common.util.WrappedException; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.Resource.Factory.Registry; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreValidator; import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl; import org.emftext.language.mecore.MPackage; import org.emftext.language.mecore.resource.mecore.IMecoreBuilder; import org.emftext.language.mecore.resource.mecore.MecoreEProblemType; import de.devboost.emftools.utils.SystemIndependentXMIResourceFactory; /** * The MecoreBuilder is invoked when .mecore files are saved. It converts the * MEcore models to Ecore models and saves them with the respective file * extension. The builder does also invoke the EcoreValidator to check whether * the produced Ecore models are valid. If problems are found they are mapped to * the corresponding elements of the MEcore model and problem markers are * created. */ public class MecoreBuilder implements IMecoreBuilder { public boolean isBuildingNeeded(URI uri) { return true; } public IStatus build(MecoreResource resource, IProgressMonitor monitor) { List<EObject> contents = resource.getContents(); // read existing .ecore file (if there is one) EPackage existingEPackage = null; URI ecoreURI = resource.getURI().trimFileExtension().trimFileExtension().appendFileExtension("ecore"); ResourceSet resourceSet = resource.getResourceSet(); replaceResourceFactories(resourceSet); Resource ecoreResource; try { ecoreResource = resourceSet.getResource(ecoreURI, true); } catch (WrappedException e) { // can't load resource, probably it does not exist ecoreResource = resourceSet.createResource(ecoreURI); } if (!ecoreResource.getErrors().isEmpty()) { // if the .ecore file has errors, we do not run the build, because it will // override the existing file return org.eclipse.core.runtime.Status.CANCEL_STATUS; } List<EObject> ecoreContents = ecoreResource.getContents(); if (!ecoreContents.isEmpty()) { EObject root = ecoreContents.get(0); if (root instanceof EPackage) { existingEPackage = (EPackage) root; } } MecoreWrapper wrapper = new MecoreWrapper(); for (EObject eObject : contents) { if (eObject instanceof MPackage) { MPackage mPackage = (MPackage) eObject; existingEPackage = wrapper.wrapMPackage(mPackage, existingEPackage); ecoreContents.add(existingEPackage); } } try { ecoreResource.save(null); } catch (IOException e) { MecorePlugin.logError("Can't save Ecore file.", e); return Status.CANCEL_STATUS; } reloadGeneratorModel(ecoreResource); runEcoreValidation(resource, ecoreResource, wrapper); return Status.OK_STATUS; } /** * We replace the resource factories for '.ecore' and '.genmodel' files to * make sure that these files are not saved using the default platform line * delimiter, but the Unix style line delimiter. This is required to avoid * having irrelevant changes in these files when saving them on machines * running different OS. */ private void replaceResourceFactories(ResourceSet resourceSet) { Registry resourceFactoryRegistry = resourceSet.getResourceFactoryRegistry(); Map<String, Object> extensionToFactoryMap = resourceFactoryRegistry.getExtensionToFactoryMap(); EcoreResourceFactoryImpl modifiedEcoreResourceFactory = new SystemIndependentXMIResourceFactory(); extensionToFactoryMap.put("ecore", modifiedEcoreResourceFactory); extensionToFactoryMap.put("genmodel", modifiedEcoreResourceFactory); } private void runEcoreValidation(MecoreResource mResource, Resource eResource, MecoreWrapper wrapper) { // do not validate if resource already contains errors (syntactical ones // or ones that are detected by the Mecore validation). if (!mResource.getErrors().isEmpty()) { return; } // run Ecore validation and map result back to .mecore file Map<Object, Object> context = new LinkedHashMap<Object, Object>(); TreeIterator<EObject> allContents = eResource.getAllContents(); while (allContents.hasNext()) { EObject next = (EObject) allContents.next(); BasicDiagnostic diagnostics = new BasicDiagnostic(); boolean result = EcoreValidator.INSTANCE.validate(next, diagnostics, context); if (!result) { processDiagnostics(mResource, wrapper, diagnostics); } } } /** * Reload the generator model (if one with the same name exists). * * @param ecoreResource the resource containing the Ecore model */ private void reloadGeneratorModel(Resource ecoreResource) { URI genModelURI = ecoreResource.getURI().trimFileExtension().appendFileExtension("genmodel"); ResourceSet resourceSet = ecoreResource.getResourceSet(); // check if generator model exists if (!resourceSet.getURIConverter().exists(genModelURI, null)) { return; } Resource genModelResource = resourceSet.getResource(genModelURI, true); if (genModelResource == null) { return; } List<EObject> genModelContents = genModelResource.getContents(); if (genModelContents.isEmpty()) { return; } for (EObject genModelObject : genModelContents) { if (genModelObject instanceof GenModel) { GenModel genModel = (GenModel) genModelObject; reloadGeneratorModel(genModel, resourceSet); } } } private void processDiagnostics(MecoreResource resource, MecoreWrapper wrapper, Diagnostic diagnostics) { String message = diagnostics.getMessage(); List<?> data = diagnostics.getData(); if (data != null) { for (Object object : data) { if (object instanceof EObject) { // find element in .mecore file that caused the problem EObject causingEObject = (EObject) object; EObject causingMObject = wrapper.getReverseMapping().get(causingEObject); if (causingMObject != null && causingMObject.eResource() != null) { attachProblemMarker(resource, diagnostics, message, causingMObject); } else { // attach problem to root object EList<EObject> contents = resource.getContents(); if (contents.size() > 0) { EObject root = contents.get(0); attachProblemMarker(resource, diagnostics, message, root); } } } } } for (Diagnostic child : diagnostics.getChildren()) { processDiagnostics(resource, wrapper, child); } } private void attachProblemMarker(MecoreResource resource, Diagnostic diagnostics, String message, EObject causingMObject) { // attach a problem marker if (diagnostics.getSeverity() == Diagnostic.ERROR) { resource.addError(message, MecoreEProblemType.BUILDER_ERROR, causingMObject); } else if (diagnostics.getSeverity() == Diagnostic.WARNING) { resource.addWarning(message, MecoreEProblemType.BUILDER_ERROR, causingMObject); } } private GenModel reloadGeneratorModel(GenModel genModel, ResourceSet rs) { if (Platform.isRunning()) { IWorkspace workspace = ResourcesPlugin.getWorkspace(); final URI genModelURI = genModel.eResource().getURI(); //only reload when we are working on the platform if(genModelURI.isPlatform()) { IResource member = workspace.getRoot().findMember(genModelURI.toPlatformString(true)); if (member instanceof IFile) { IFile file = (IFile) member; if (!file.isReadOnly()) { try { updateGenModel(genModel); Resource genModelResource = rs.getResource(genModelURI, true); return (GenModel) genModelResource.getContents().get(0); } catch (Exception e) { MecorePlugin.logError("Error while updating genmodel " + file, e); } } } } } return genModel; } private void updateGenModel(final GenModel genModel) throws Exception { final Resource genModelResource = genModel.eResource(); final boolean reconcileSucceeded = genModel.reconcile(); if (!reconcileSucceeded) { throw new RuntimeException("Reconciliation of genmodel failed."); } final Diagnostic diag = genModel.diagnose(); if (diag.getSeverity() != Diagnostic.OK) { throw new DiagnosticException(diag); } new Job("Saving genmodel after reconciling") { @Override protected IStatus run(IProgressMonitor monitor) { try { genModelResource.save(Collections.EMPTY_MAP); } catch (IOException e) { throw new RuntimeException(e); } return Status.OK_STATUS; } }.schedule(); } public IStatus handleDeletion(URI uri, IProgressMonitor monitor) { return Status.OK_STATUS; } }