/* * Copyright 2009-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.codehaus.groovy.eclipse.dsl; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.codehaus.groovy.eclipse.GroovyLogManager; import org.codehaus.groovy.eclipse.TraceCategory; import org.codehaus.groovy.eclipse.dsl.inferencing.suggestions.SuggestionsLoader; import org.codehaus.groovy.eclipse.dsl.inferencing.suggestions.writer.SuggestionsFileProperties; import org.codehaus.groovy.eclipse.dsl.script.DSLDScriptExecutor; 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.IResourceRuleFactory; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.IStorage; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IJavaModelStatusConstants; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; public class RefreshDSLDJob extends Job { public class DSLDResourceVisitor implements IResourceVisitor { private static final String PLUGIN_DSLD_SUPPORT = "plugin_dsld_support"; private static final String GLOBAL_DSLD_SUPPORT = "global_dsld_support"; private final IProject project; private final Set<IStorage> dsldFiles; private final Set<String> alreadyAdded; public DSLDResourceVisitor(IProject project) { this.project = project; dsldFiles = new HashSet<IStorage>(); alreadyAdded = new HashSet<String>(); } public boolean visit(IResource resource) throws CoreException { // don't visit the output folders if (resource.isDerived()) { return false; } if (resource.getType() == IResource.FILE) { IFile file = (IFile) resource; if (!alreadyAdded.contains(file.getName()) && (isDSLD(file) || isSuggestionFile(file))) { alreadyAdded.add(file.getName()); dsldFiles.add(file); } else { if (alreadyAdded.contains(file.getName())) { GroovyDSLCoreActivator.logWarning("DSLD File " + file.getFullPath() + " already added, so skipping."); } } } return true; } public Set<IStorage> findFiles(IProgressMonitor monitor) { try { // first look for files in the project project.accept(this); // now look for files in class folders of the project findDSLDsInLibraries(monitor); } catch (CoreException e) { if (e.getStatus().getCode() == IResourceStatus.RESOURCE_NOT_FOUND) { // ignore. Resource was deleted } else { GroovyDSLCoreActivator.logException(e); } } return dsldFiles; } protected void findDSLDsInLibraries(IProgressMonitor monitor) throws JavaModelException { IJavaProject javaProject = JavaCore.create(project); IPackageFragmentRoot[] roots = getFragmentRoots(javaProject, monitor); for (IPackageFragmentRoot root : roots) { if (monitor.isCanceled()) { throw new OperationCanceledException(); } try { // GRECLIPSE-1458 must check source folders, but avoid source folders from same project if (root.getKind() == IPackageFragmentRoot.K_BINARY || isSourceFolderFromOtherProject(root)) { IPackageFragment frag = root.getPackageFragment("dsld"); if (frag.exists() || root.getElementName().equals(GLOBAL_DSLD_SUPPORT) || root.getElementName().equals(PLUGIN_DSLD_SUPPORT)) { IResource rootResource = root.getResource(); // // FIXADE start workaround for Bug 346928 // // in 3.6 and earlier, it was not possible to refresh scripts in external folders // // fixed in 3.7, consider removing when 3.6 is no longer supported. // if (rootResource == null && root instanceof ExternalPackageFragmentRoot) { // // external source roots return null for getResource, but do have a resource // rootResource = ((ExternalPackageFragmentRoot) root).resource(); // } // if (rootResource != null) { // try { // rootResource.refreshLocal(IResource.DEPTH_INFINITE, monitor); // root.close(); // root.open(monitor); // if (monitor.isCanceled()) { // throw new OperationCanceledException(); // } // if (!root.exists() || !frag.exists()) { // // must check a second time for existence because the close and re-opening of the root may // // have changed things // continue; // } // } catch (CoreException e) { // GroovyDSLCoreActivator.logException(e); // } // } // // FIXADE end workaround if (rootResource instanceof IFolder && ((IFolder) rootResource).getFolder("dsld").exists()) { IFolder dsldFolder = ((IFolder) rootResource).getFolder("dsld"); for (IResource resource : dsldFolder.members()) { if (resource.getType() == IResource.FILE && !alreadyAdded.contains(resource.getName()) && isDSLD((IFile) resource)) { alreadyAdded.add(resource.getName()); dsldFiles.add((IStorage) resource); } else { if (alreadyAdded.contains(resource.getName())) { GroovyLogManager.manager.log(TraceCategory.DSL, "DSLD File " + resource.getFullPath() + " already added, so skipping."); } } } } else { Object[] resources = frag.getNonJavaResources(); // make sure we don't add files with the same names. // this ensures that a dsld file that is coming from 2 different places is // not added twice. for (Object resource : resources) { if (resource instanceof IStorage) { IStorage file = (IStorage) resource; if (!alreadyAdded.contains(file.getName()) && isDSLD(file)) { alreadyAdded.add(file.getName()); dsldFiles.add(file); } else { if (alreadyAdded.contains(file.getName())) { GroovyLogManager.manager.log(TraceCategory.DSL, "DSLD File " + file.getFullPath() + " already added, so skipping."); } } } } } } } } catch (CoreException e) { switch (e.getStatus().getCode()) { case IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST: case IJavaModelStatusConstants.ELEMENT_NOT_ON_CLASSPATH: break; default: GroovyDSLCoreActivator.logException(e); } } } } private boolean isSourceFolderFromOtherProject(IPackageFragmentRoot root) { if (root.isReadOnly()) { // not source folder return false; } IResource resource = root.getResource(); if (resource == null) { return false; } if (resource.getProject().equals(project)) { return false; } return true; } /** * Get all package fragment roots in a safe way so that concurrent modifications aren't thrown * See http://jira.codehaus.org/browse/GRECLIPSE-1284 */ private IPackageFragmentRoot[] getFragmentRoots(final IJavaProject javaProject, IProgressMonitor monitor) throws JavaModelException { final IPackageFragmentRoot[][] roots = new IPackageFragmentRoot[1][]; try { ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { roots[0] = javaProject.getAllPackageFragmentRoots(); } }, getSchedulingRule(), IWorkspace.AVOID_UPDATE, monitor); } catch (CoreException e) { if (e.getStatus().getCode() == IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST) { // ignore...project was deleted } else { GroovyDSLCoreActivator.logException(e); } } return roots[0] != null ? roots[0] : new IPackageFragmentRoot[0]; } private ISchedulingRule getSchedulingRule() { IResourceRuleFactory ruleFactory = ResourcesPlugin.getWorkspace().getRuleFactory(); // FIXADE Arrrrgh...we need to grab a build rule here. Looks like a classpath container refresh // will grab the rule of projects that are contained in the container. // return new MultiRule(new ISchedulingRule[] { // // use project modification rule as this is needed to create the .classpath file if it doesn't exist yet, or to update project references // ruleFactory.modifyRule(this.project.getProject()), // // // and external project modification rule in case the external folders are modified // ruleFactory.modifyRule(JavaModelManager.getExternalManager().getExternalFoldersProject()) // }); return ruleFactory.buildRule(); } } private final List<IProject> projects; private DSLDStoreManager contextStoreManager = GroovyDSLCoreActivator.getDefault().getContextStoreManager(); /** * Deprecated. Use {@link DSLDStoreManager#initialize(IProject, boolean)} * instead. This new method allows for the initialization of a store synchronously * or asynchronously. */ @Deprecated public RefreshDSLDJob(IProject project) { this(Collections.singletonList(project)); } /** * Deprecated. Use {@link DSLDStoreManager#initialize(List, boolean)} * instead. This new method allows for the initialization of a store synchronously * or asynchronously. */ @Deprecated public RefreshDSLDJob(List<IProject> projects) { super("Refresh DSLD scripts"); this.projects = contextStoreManager.addInProgress(projects); } protected boolean isDSLD(IStorage file) { return isFile(file, "dsld"); } protected boolean isSuggestionFile(IStorage file) { return isFile(file, SuggestionsFileProperties.FILE_TYPE); } protected boolean isFile(IStorage file, String extension) { if (file instanceof IFile) { IFile iFile = (IFile) file; return !iFile.isDerived() && extension.equals(iFile.getFileExtension()); } else { String name = file.getName(); return name != null && name.endsWith(extension); } } @Override public IStatus run(IProgressMonitor monitor) { try { if (GroovyDSLCoreActivator.getDefault().isDSLDDisabled()) { if (GroovyLogManager.manager.hasLoggers()) { GroovyLogManager.manager.log(TraceCategory.DSL, "DSLD support is currently disabled, so not refreshing DSLDs."); } return Status.OK_STATUS; } SubMonitor submon = SubMonitor.convert(monitor); submon.beginTask("Refresh DSLD scripts", projects.size() * 9); List<IStatus> errorStatuses = new ArrayList<IStatus>(); for (IProject project : projects) { IStatus res = Status.OK_STATUS; try { res = refreshProject(project, submon.newChild(9)); } finally { contextStoreManager.removeInProgress(project); } if (!res.isOK()) { errorStatuses.add(res); } else if (res == Status.CANCEL_STATUS) { return res; } } submon.done(); if (errorStatuses.isEmpty()) { return Status.OK_STATUS; } else { MultiStatus multi = new MultiStatus(GroovyDSLCoreActivator.PLUGIN_ID, 0, "Error refreshing DSLDs.", null); for (IStatus error : errorStatuses) { multi.add(error); } return multi; } } finally { // in case the job was exited early, ensure all projects have their initialization stage removed for (IProject project : projects) { contextStoreManager.removeInProgress(project); } } } private IStatus refreshProject(IProject project, IProgressMonitor monitor) { String event = null; if (GroovyLogManager.manager.hasLoggers()) { GroovyLogManager.manager.log(TraceCategory.DSL, "Refreshing inferencing scripts for " + project.getName()); event = "Refreshing inferencing scripts: " + project.getName(); GroovyLogManager.manager.logStart(event); } monitor.beginTask("Refreshing DSLD files for project " + project.getName(), 9); if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } monitor.worked(1); // purge existing if (GroovyLogManager.manager.hasLoggers()) { GroovyLogManager.manager.log(TraceCategory.DSL, "Purging old state"); } DSLDStore store = GroovyDSLCoreActivator.getDefault().getContextStoreManager().getDSLDStore(project); store.purgeAll(); if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } monitor.worked(1); // find dslds if (GroovyLogManager.manager.hasLoggers()) { GroovyLogManager.manager.log(TraceCategory.DSL, "Finding inferencing DSL scripts"); } Set<IStorage> findDSLDFiles = new DSLDResourceVisitor(project).findFiles(monitor); if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } monitor.worked(1); // now add the rest for (IStorage file : findDSLDFiles) { if (GroovyLogManager.manager.hasLoggers()) { GroovyLogManager.manager.log(TraceCategory.DSL, "Processing " + file.getName() + " in project " + project.getName()); } monitor.subTask("Processing " + file.getName() + " in project " + project.getName()); if (isDSLD(file)) { DSLDScriptExecutor executor = new DSLDScriptExecutor(JavaCore.create(project)); executor.executeScript(file); } else if (isSuggestionFile(file)) { new SuggestionsLoader((IFile)file).loadExistingSuggestions(); } if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } } monitor.worked(6); monitor.done(); if (event != null) { GroovyLogManager.manager.logEnd(event, TraceCategory.DSL); } return Status.OK_STATUS; } @Override public boolean belongsTo(Object family) { return family == RefreshDSLDJob.class; } }