/* * <copyright> * * Copyright (c) 2005-2007 Sven Efftinge 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: * Sven Efftinge - Initial API and implementation * * </copyright> */ package org.eclipse.gmf.internal.xpand.build; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IFile; 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.OperationCanceledException; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.gmf.internal.xpand.RootManager; import org.eclipse.gmf.internal.xpand.expression.AnalysationIssue; import org.eclipse.gmf.internal.xpand.expression.ExecutionContext; import org.eclipse.gmf.internal.xpand.migration.Activator; import org.eclipse.gmf.internal.xpand.model.XpandExecutionContext; import org.eclipse.gmf.internal.xpand.model.XpandResource; import org.eclipse.gmf.internal.xpand.util.ContextFactory; import org.eclipse.gmf.internal.xpand.util.OawMarkerManager; import org.eclipse.gmf.internal.xpand.util.ParserException; import org.eclipse.gmf.internal.xpand.util.ParserException.ErrorLocationInfo; import org.eclipse.gmf.internal.xpand.xtend.ast.XtendResource; public class OawBuilder extends IncrementalProjectBuilder implements RootManager.IRootChangeListener { public static final String LEGACY_XPAND_PLUGIN_ID = "org.eclipse.gmf.xpand"; private RootManager myRootManager; private WorkspaceModelRegistry modelRegistry; private boolean myRootsChanged = true; // XXX again, using map as mere pairs private final Map<XtendResource, IFile> xtendResourcesToAnalyze = new HashMap<XtendResource, IFile>(); private final Map<XpandResource, IFile> xpandResourcesToAnalyze = new HashMap<XpandResource, IFile>(); public static final String getBUILDER_ID() { return LEGACY_XPAND_PLUGIN_ID + ".oawBuilder"; } @Override protected void startupOnInitialize() { super.startupOnInitialize(); myRootManager = Activator.getRootManager(getProject()); myRootManager.addRootChangeListener(this); modelRegistry = new WorkspaceModelRegistry(); Activator.registerModelSource(modelRegistry); } @Override protected IProject[] build(final int kind, final Map args, final IProgressMonitor monitor) throws CoreException { try { doBuild(kind, args, monitor); } catch (final Throwable e) { e.printStackTrace(); } // TODO to separate thread for (XtendResource r : xtendResourcesToAnalyze.keySet()) { final ExecutionContext ctx = ContextFactory.createXtendContext(getResourceManager(xtendResourcesToAnalyze.get(r))); final Set<AnalysationIssue> issues = new HashSet<AnalysationIssue>(); r.analyze(ctx, issues); updateMarkers(xtendResourcesToAnalyze.get(r), issues); } for (XpandResource r : xpandResourcesToAnalyze.keySet()) { final XpandExecutionContext ctx = ContextFactory.createXpandContext(getResourceManager(xpandResourcesToAnalyze.get(r))); final Set<AnalysationIssue> issues = new HashSet<AnalysationIssue>(); r.analyze(ctx, issues); updateMarkers(xpandResourcesToAnalyze.get(r), issues); } xtendResourcesToAnalyze.clear(); xpandResourcesToAnalyze.clear(); myRootsChanged = false; Set<IProject> referencedProjects = myRootManager.getReferencedProjects(); referencedProjects.remove(getProject()); return referencedProjects.toArray(new IProject[referencedProjects.size()]); } private void doBuild(int kind, Map<?, ?> args, IProgressMonitor monitor) throws CoreException { if ((kind == FULL_BUILD) || haveRootsChangedSinceLastBuild()) { fullBuild(monitor); } else { Set<IProject> referencedProjects = myRootManager.getReferencedProjects(); referencedProjects.remove(getProject()); Collection<IResourceDelta> deltas = new ArrayList<IResourceDelta>(referencedProjects.size()); IResourceDelta projectDelta = getDelta(getProject()); if (projectDelta == null) { fullBuild(monitor); return; } for (IProject next : referencedProjects) { final IResourceDelta delta = getDelta(next); if (delta == null) { fullBuild(monitor); return; } deltas.add(delta); } incrementalBuild(projectDelta, deltas, monitor); } } public void rootsChanged(RootManager rootManager) { myRootsChanged = true; } private boolean haveRootsChangedSinceLastBuild() { return myRootsChanged; } void reloadResource(final IFile resource) { getResourceManager(resource).forget(resource); if (!resource.exists()) { return; } try { if (isXpand(resource)) { XpandResource r = getResourceManager(resource).loadXpandResource(resource); if (r != null) { xpandResourcesToAnalyze.put(r, resource); } } else if (isXtend(resource)) { XtendResource r = getResourceManager(resource).loadXtendResource(resource); if (r != null) { xtendResourcesToAnalyze.put(r, resource); } } } catch (ParserException ex) { updateMarkers(resource, ex.getParsingErrors()); } catch (Exception ex) { Activator.logError(ex); // perhaps, depending on exception type (Core|IO) we can decide to keep old markers? OawMarkerManager.deleteMarkers(resource); OawMarkerManager.addErrorMarker(resource, ex.getMessage(), -1, -1); } } public void handleRemovement(final IFile resource) { OawMarkerManager.deleteMarkers(resource); getResourceManager(resource).forget(resource); } private WorkspaceResourceManager getResourceManager(IFile file) { WorkspaceResourceManager result = myRootManager.getResourceManager(file); assert result != null; return result; } protected void fullBuild(final IProgressMonitor monitor) throws CoreException { Set<IProject> referencedProjects = myRootManager.getReferencedProjects(); referencedProjects.add(getProject()); OawMarkerManager.deleteMarkers(getProject()); //to delete markers from obsolete roots. monitor.beginTask(null, 1 + referencedProjects.size()); try { for (IProject next : referencedProjects) { checkCanceled(monitor); next.accept(new XpandResourceVisitor(new SubProgressMonitor(monitor, 1))); } checkCanceled(monitor); modelRegistry.build(getProject(), new SubProgressMonitor(monitor, 1)); } finally { monitor.done(); } } protected void incrementalBuild(final IResourceDelta projectDelta, final Collection<IResourceDelta> referencedProjectDeltas, final IProgressMonitor monitor) throws CoreException { monitor.beginTask(null, 2 + referencedProjectDeltas.size()); try { for (IResourceDelta delta : referencedProjectDeltas) { checkCanceled(monitor); delta.accept(new XpandResourceVisitor(new SubProgressMonitor(monitor, 1))); } checkCanceled(monitor); projectDelta.accept(new XpandResourceVisitor(new SubProgressMonitor(monitor, 1))); checkCanceled(monitor); modelRegistry.build(getProject(), projectDelta, new SubProgressMonitor(monitor, 1)); } finally { monitor.done(); } } private void checkCanceled(final IProgressMonitor monitor) { if (monitor.isCanceled()) { throw new OperationCanceledException(); } } private static void updateMarkers(IFile resource, Set<AnalysationIssue> issues) { OawMarkerManager.deleteMarkers(resource); OawMarkerManager.addMarkers(resource, issues.toArray(new AnalysationIssue[issues.size()])); } private static void updateMarkers(IFile resource, ErrorLocationInfo[] parsingErrors) { OawMarkerManager.deleteMarkers(resource); OawMarkerManager.addMarkers(resource, parsingErrors); } private static boolean isXtend(final IFile resource) { return XtendResource.FILE_EXTENSION.equals(resource.getFileExtension()); } private static boolean isXpand(final IFile resource) { return XpandResource.TEMPLATE_EXTENSION.equals(resource.getFileExtension()); } private boolean isFileOfInterest(IFile file) { if (!isXpand(file) && !isXtend(file)) { return false; } if (getResourceManager(file) == null) { return false; } return true; } private class XpandResourceVisitor implements IResourceVisitor, IResourceDeltaVisitor { private final IProgressMonitor monitor; public XpandResourceVisitor(final IProgressMonitor monitor) { this.monitor = monitor; } public boolean visit(final IResource resource) { if (!resource.isDerived() && (resource instanceof IFile) && isFileOfInterest((IFile) resource)) { reloadResource((IFile) resource); } monitor.worked(1); return true; } public boolean visit(final IResourceDelta delta) throws CoreException { final IResource resource = delta.getResource(); if (resource.isDerived()) { return false; } if ((resource instanceof IFile)) { IFile file = (IFile) resource; if (!isFileOfInterest(file)) { return false; } switch (delta.getKind()) { case IResourceDelta.ADDED: reloadResource(file); break; case IResourceDelta.REMOVED: handleRemovement(file); break; case IResourceDelta.CHANGED: reloadResource(file); break; } } else if (resource instanceof IProject) { // forget about project in resource manager if (delta.getKind() == IResourceDelta.REMOVED) { System.err.println("Project removed:" + resource.getName()); } if (delta.getKind() == IResourceDelta.OPEN) { System.err.println("Project open:" + ((IProject) resource).isOpen()); } } monitor.worked(1); return true; } } }