/* * #%~ * org.overture.ide.core * %% * Copyright (C) 2008 - 2014 Overture * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #~% */ package org.overture.ide.internal.core; import java.util.ArrayList; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Vector; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.jobs.Job; import org.overture.ide.core.IVdmModel; import org.overture.ide.core.VdmCore; import org.overture.ide.core.resources.IVdmProject; import org.overture.ide.core.resources.IVdmSourceUnit; import org.overture.ide.core.resources.VdmSourceUnit; import org.overture.ide.internal.core.ast.VdmModelManager; import org.overture.ide.internal.core.resources.VdmProject; public class ResourceManager implements IResourceChangeListener { Map<String, IVdmProject> projects = new Hashtable<String, IVdmProject>(); Map<IFile, IVdmSourceUnit> vdmSourceUnits = new Hashtable<IFile, IVdmSourceUnit>(); Queue<IVdmProject> lastAccessed = new LinkedList<IVdmProject>(); ArrayList<IProject> projectsToRebuild = new ArrayList<IProject>(); /** * A handle to the unique Singleton instance. */ static private volatile ResourceManager _instance = null; /** * @return The unique instance of this class. */ static public ResourceManager getInstance() { if (null == _instance) { _instance = new ResourceManager(); } return _instance; } public IVdmSourceUnit getVdmSourceUnit(IFile file) { if (file == null) { return null; } if (vdmSourceUnits.containsKey(file)) { return vdmSourceUnits.get(file); } else { if (VdmProject.isVdmProject(file.getProject())) { IVdmProject project = VdmProject.createProject(file.getProject()); try { if (!file.isSynchronized(IResource.DEPTH_ONE)) { file.refreshLocal(IResource.DEPTH_ONE, null); } // if (file.getContentDescription() != null // && // project.getContentTypeIds().contains(file.getContentDescription().getContentType().getId())) if (project.isModelFile(file)) { IVdmSourceUnit unit = createSourceUnit(file, project); return unit; } } catch (CoreException e) { if (VdmCore.DEBUG) { e.printStackTrace(); } } } else System.err.println("project is not vdm complient"); } return null; } private IVdmSourceUnit createSourceUnit(IFile file, IVdmProject project) { IVdmModel model = project.getModel(); model.addVdmSourceUnit(new VdmSourceUnit(project, file)); IVdmSourceUnit unit = model.getVdmSourceUnit(file); vdmSourceUnits.put(file, unit); return unit; } /*** * Recursive search of a project for files based on the content type * * @param project * the project to search * @param resource * the resource currently selected to be searched * @param contentTypeId * a possibly null content type id, if null it is just checked that a content type exist for the file * @return a list of IFiles * @throws CoreException */ public List<IVdmSourceUnit> getFiles(IVdmProject project, IResource resource, IContentType contentTypeId) throws CoreException { List<IVdmSourceUnit> list = new Vector<IVdmSourceUnit>(); if (resource instanceof IFolder) { if (resource.getLocation().lastSegment().startsWith("."))// skip return list; // . folders like.svn for (IResource res : ((IFolder) resource).members(IContainer.INCLUDE_PHANTOMS | IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS)) { list.addAll(getFiles(project, res, contentTypeId)); } } // check if it is a IFile and that there exists a known content type for // this file and the project else if (resource instanceof IFile) { // if (contentTypeId != null // && contentTypeId.isAssociatedWith(resource.toString()) && // !resource.getName().startsWith("~")) if (project.isModelFile((IFile) resource)) { list.add(getVdmSourceUnit((IFile) resource)); } } return list; } public boolean hasProject(IProject project) { return projects.containsKey(project.getName()); } public IVdmProject getProject(IProject project) { return projects.get(project.getName()); } public synchronized IVdmProject addProject(IVdmProject project) { if (projects.containsKey(project.getName())) return projects.get(project.getName()); else { IVdmProject p = getLeastAccessed(); if (p != null) { lastAccessed.remove(p); lastAccessed.add(project); } try { projects.put(project.getName(), project); VdmModelManager.getInstance().createModel(project); // System.out.println("Creating project: " + project.getName()); project.getSpecFiles(); return project; } catch (CoreException e) { if (VdmCore.DEBUG) { e.printStackTrace(); } return null; } } } private IVdmProject getLeastAccessed() { return lastAccessed.poll(); } public void resourceChanged(IResourceChangeEvent event) { try { IResource res = event.getResource(); switch (event.getType()) { case IResourceChangeEvent.PRE_DELETE: case IResourceChangeEvent.PRE_CLOSE: remove(res); break; case IResourceChangeEvent.PRE_BUILD: break; case IResourceChangeEvent.POST_CHANGE: event.getDelta().accept(new DeltaPrinter()); break; default: break; } // rebuilds projects added to the projectsToRebuild // during the exploration of the delta rebuildProjects(); } catch (CoreException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private synchronized void remove(IResource res) { // VDM Project is closing if (res instanceof IProject && hasProject((IProject) res)) { if (projects.containsKey(res.getName())) { projects.remove(res.getName()); } } else if (res instanceof IFile) { if (vdmSourceUnits.containsKey(res)) { final IVdmProject vdmProject = vdmSourceUnits.get(res).getProject(); vdmProject.getModel().remove(vdmSourceUnits.get(res)); vdmSourceUnits.remove(res); IProject p = res.getProject(); synchronized (projectsToRebuild) { if (!projectsToRebuild.contains(p)) { projectsToRebuild.add(p); } } } } else if (res instanceof IFolder) { // no special action } else { // System.err.println("Resource not handled in remove: " + res); } } class DeltaPrinter implements IResourceDeltaVisitor { public boolean visit(IResourceDelta delta) { IResource res = delta.getResource(); switch (delta.getKind()) { case IResourceDelta.ADDED: // System.out.print("Resource "); // System.out.print(res.getFullPath()); // System.out.println(" was added."); add(res); break; case IResourceDelta.REMOVED: remove(res); // if (res instanceof IFile) // { // if (isProjectBuildConttent((IFile) res)) // { // IProject p = res.getProject(); // synchronized (projectsToRebuild) // { // if (!projectsToRebuild.contains(p)) // { // projectsToRebuild.add(p); // } // } // } // // } break; case IResourceDelta.CHANGED: try { for (IResourceDelta resourceDelta : delta.getAffectedChildren()) { resourceDelta.accept(new DeltaPrinter()); } } catch (CoreException e) { // TODO Auto-generated catch block e.printStackTrace(); } break; } return true; // visit the children } private void add(final IResource res) { if (res instanceof IProject && VdmProject.isVdmProject((IProject) res)) { addBuilderProject((IProject) res);// , false); } else if (res instanceof IFile) { IFile file = (IFile) res; //FIXME use new method isProjectBuildConttent(file) if (VdmProject.isVdmProject(file.getProject())) { // Add the VDM builder to the project if missing and the // project have the correct nature addBuilderProject(res.getProject()); // Call getVdmSourceUnit to associate the the IFile to the // project if the project contains the content type IVdmProject project = (IVdmProject) res.getProject().getAdapter(IVdmProject.class); Assert.isNotNull(project, "Project in ResourceManager is null for file: " + file); IContentType contentTypeId = null; try { if (file.getContentDescription() != null) { contentTypeId = file.getContentDescription().getContentType(); } } catch (CoreException e) { } if (project.getContentTypeIds().contains(contentTypeId)) { getVdmSourceUnit(file); } } } } } private List<IProject> addBuilders = new Vector<IProject>(); private boolean addBuilderThreadRunning = false; private synchronized IProject getAddBuidlerProject() { if (addBuilders.size() > 0) { IProject p = addBuilders.get(0); addBuilders.remove(p); return p; } return null; } private synchronized void addBuilderProject(IProject project) { if (!addBuilders.contains(project)) { addBuilders.add(project); if (!addBuilderThreadRunning) { System.out.println("starting add builder thread"); new AddBuilderThread().start(); addBuilderThreadRunning = true; } } } /** * Sync existing IVdmSource files with the build path of the project. This is used when the build path changed and * the project should be updated. This method removed old IVdmSource files which no longer is withing the build * path. * * @param project * @throws CoreException */ public synchronized void syncBuildPath(IVdmProject project) throws CoreException { List<IVdmSourceUnit> syncedVdmSourceUnits = project.getSpecFiles(); List<IFile> removedFiles = new Vector<IFile>(); IProject p = (IProject) project.getAdapter(IProject.class); for (IFile file : vdmSourceUnits.keySet()) { if (file.getProject().equals(p) && !syncedVdmSourceUnits.contains(vdmSourceUnits.get(file))) { removedFiles.add(file); System.out.println("Found an existing file removed from build path: " + file); } } // remove the old files for (IFile iFile : removedFiles) { remove(iFile); } } private class AddBuilderThread extends Thread { @Override public void run() { try { while (ResourcesPlugin.getWorkspace().isTreeLocked()) { try { Thread.sleep(500); } catch (InterruptedException e) { } } ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { IProject p = null; while (addBuilders.size() > 0) { p = getAddBuidlerProject(); if (p == null) { addBuilderThreadRunning = false; System.out.println("ended add builder thread"); return; } System.out.println("Adding builder for: " + p); IVdmProject project = VdmProject.createProject(p); Assert.isNotNull(project, "VDM Project creation faild for project: " + p); try { if (!project.hasBuilder()) { project.setBuilder(project.getLanguageVersion()); } project.getSpecFiles();// sync with content type // files } catch (CoreException e1) { e1.printStackTrace(); } } System.out.println("DONE adding"); } }, null); } catch (CoreException e) { VdmCore.log("Error in ResourceManager: AddBuilderThread", e); } } } private void rebuildProjects() { synchronized (projectsToRebuild) { for (int i = 0; i < projectsToRebuild.size(); i++) { IProject p = projectsToRebuild.get(i); IVdmProject vdmProject = (IVdmProject) p.getAdapter(IVdmProject.class); if (vdmProject != null) { scheduleBuilder(vdmProject); } } projectsToRebuild.clear(); } } private void scheduleBuilder(IVdmProject project) { final IVdmProject vdmProject = project; Job job = new Job("Refresh resources") { @Override protected IStatus run(IProgressMonitor monitor) { try { vdmProject.getModel().setIsTypeChecked(false); vdmProject.typeCheck(true, monitor); } catch (CoreException e) { VdmCore.log("Faild to auto build project", e); } return Status.OK_STATUS; } }; job.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule()); job.schedule(); } }