/** * Copyright (c) 2011 committers of YAKINDU and others. * 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: * committers of YAKINDU - initial API and implementation */ package org.yakindu.sct.generator.builder; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; 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.emf.common.util.URI; import org.eclipse.emf.common.util.WrappedException; import org.eclipse.emf.ecore.EObject; 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.emf.ecore.util.EcoreUtil; import org.eclipse.jface.preference.IPreferenceStore; import org.yakindu.sct.generator.core.GeneratorActivator; import org.yakindu.sct.model.sgen.GeneratorEntry; import org.yakindu.sct.model.sgen.GeneratorModel; import org.yakindu.sct.model.sgraph.Statechart; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; /** * * @author andreas muelder - Initial contribution and API * */ public class SCTBuilder extends IncrementalProjectBuilder { // TODO Remove dependency to fileextension and read referenced elements from // genmodels instead. private static final String SCT_FILE_EXTENSION = "sct"; private static final String SGEN_FILE_EXTENSION = "sgen"; public static final String BUILDER_ID = "org.yakindu.sct.builder.SCTBuilder"; private final class ElementRefGenerator implements Predicate<GeneratorEntry> { private final URI uri; private ElementRefGenerator(EObject eobject) { if (EcoreUtil.getURI(eobject) != null) { uri = EcoreUtil.getURI(eobject); } else { uri = null; } } public boolean apply(GeneratorEntry input) { return uri != null && input.getElementRef() != null && !input.getElementRef().eIsProxy() && uri.equals(EcoreUtil.getURI(input.getElementRef())); } } class DeltaVisitor implements IResourceDeltaVisitor { private Set<IResource> buildSgens = Sets.newHashSet(); public boolean visit(IResourceDelta delta) throws CoreException { IResource resource = delta.getResource(); switch (delta.getKind()) { case IResourceDelta.ADDED: // handle added resource doIt(resource, buildSgens); break; case IResourceDelta.REMOVED: // handle removed resource break; case IResourceDelta.CHANGED: // handle changed resource doIt(resource, buildSgens); break; } // return true to continue visiting children. return true; } } class SimpleResourceVisitor implements IResourceVisitor { private Set<IResource> buildSgens = Sets.newHashSet(); public boolean visit(IResource resource) { doIt(resource, buildSgens); // return true to continue visiting children. return true; } } @Override protected IProject[] build(int kind, @SuppressWarnings("rawtypes") Map args, IProgressMonitor monitor) throws CoreException { IPreferenceStore store = BuilderActivator.getDefault().getPreferenceStore(); boolean generateAutomatical = store.getBoolean(GeneratorActivator.PREF_GENERATE_AUTOMATICALLY); if (generateAutomatical) { if (kind == FULL_BUILD) { fullBuild(monitor); } else { IResourceDelta delta = getDelta(getProject()); if (delta == null) { fullBuild(monitor); } else { incrementalBuild(delta, monitor); } } } return null; } protected void fullBuild(final IProgressMonitor monitor) throws CoreException { try { getProject().accept(new SimpleResourceVisitor()); } catch (CoreException e) { } } protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException { delta.accept(new DeltaVisitor()); } /** * Build the Statecharts inside this sgen-file or find all sgen-files for * the statechart in the resource and build them. * * @param changedResource * Resource to check, if it can be build. * @param buildSgens * Contains a set of already build sgen files. Accepted * sgen-files are added inside this method. */ public void doIt(final IResource changedResource, final Set<IResource> buildSgens) { if (changedResource.getType() != IResource.FILE) { return; } if (SGEN_FILE_EXTENSION.equals(changedResource.getFileExtension()) && !buildSgens.contains(changedResource)) { if (hasError(changedResource)) { logGenmodelError(changedResource.getFullPath().toString()); } else { buildSgens.add(changedResource); executeGenmodelGenerator(changedResource); } } else if (SCT_FILE_EXTENSION.equals(changedResource.getFileExtension())) { // TODO rely on indexed genmodel and referenced objects uri final Statechart statechart = loadFromResource(changedResource); if (statechart == null) return; try { changedResource.getProject().accept(new IResourceVisitor() { public boolean visit(IResource resource) throws CoreException { if (IResource.FILE == resource.getType() && SGEN_FILE_EXTENSION.equals(resource.getFileExtension()) && !buildSgens.contains(resource) && isGenmodelForStatechart(resource, statechart)) { // TODO: would be good to filter the config for the // statechart so only the sct that changed is // build if (hasError(changedResource)) { logStatechartError(changedResource.getFullPath().toString()); } else { if (hasError(resource)) { logGenmodelError(resource.getFullPath().toString()); } else { buildSgens.add(resource); executeGenmodelGenerator(resource); } } } return true; } }); } catch (CoreException e) { e.printStackTrace(); } } } private boolean hasError(IResource resource) { IMarker[] findMarkers = null; try { findMarkers = resource.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE); for (IMarker iMarker : findMarkers) { Integer attribute = (Integer) iMarker.getAttribute(IMarker.SEVERITY); if (attribute.intValue() == IMarker.SEVERITY_ERROR) { return true; } } } catch (CoreException e) { e.printStackTrace(); } return false; } protected void executeGenmodelGenerator(IResource resource) { new EclipseContextGeneratorExecutorLookup().executeGenerator(resource.getProject().getFile(resource.getProjectRelativePath())); } protected void logGenmodelError(String resource) { Status status = new Status(Status.WARNING, BUILDER_ID, String.format("Cannot execute Genmodel %s. The file contains errors.", resource)); Platform.getLog(BuilderActivator.getDefault().getBundle()).log(status); } protected void logStatechartError(final String resource) { Status status = new Status(Status.WARNING, BUILDER_ID, String.format("Cannot generate Code for Statechart %s. The file contains errors.", resource)); Platform.getLog(BuilderActivator.getDefault().getBundle()).log(status); } private boolean isGenmodelForStatechart(IResource genmodelResource, final Statechart statechart) { GeneratorModel genModel = loadFromResource(genmodelResource); return genModel != null && !genModel.getEntries().isEmpty() && Iterables.any(genModel.getEntries(), new ElementRefGenerator(statechart)); } @SuppressWarnings("unchecked") private <TYPE extends EObject> TYPE loadFromResource(IResource res) { URI uri = URI.createPlatformResourceURI(res.getFullPath().toString(), true); ResourceSet set = new ResourceSetImpl(); Resource emfResource = null; try { emfResource = set.getResource(uri, true); } catch (WrappedException e) { Platform.getLog(BuilderActivator.getDefault().getBundle()).log(new Status(IStatus.WARNING, GeneratorActivator.PLUGIN_ID, "Resource " + uri + " can not be loaded by builder", e)); return null; } if (emfResource.getErrors().size() > 0 || emfResource.getContents().size() == 0) return null; return (TYPE) emfResource.getContents().get(0); } }