| /******************************************************************************* |
| * Copyright (c) 2011, 2012 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| * |
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.web.core.internal; |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.wst.common.componentcore.ComponentCore; |
| import org.eclipse.wst.common.componentcore.ModuleCoreNature; |
| import org.eclipse.wst.common.componentcore.resources.IVirtualFolder; |
| import org.eclipse.wst.jsdt.core.IIncludePathAttribute; |
| import org.eclipse.wst.jsdt.core.IIncludePathEntry; |
| import org.eclipse.wst.jsdt.core.JavaScriptCore; |
| import org.eclipse.wst.jsdt.internal.core.ClasspathEntry; |
| import org.eclipse.wst.jsdt.internal.core.JavaProject; |
| import org.eclipse.wst.jsdt.internal.core.util.Messages; |
| import org.eclipse.wst.jsdt.web.core.internal.project.ModuleSourcePathProvider; |
| import org.eclipse.wst.sse.core.indexing.AbstractIndexManager; |
| |
| /** |
| * <p>This is an implementation of the {@link AbstractIndexManager} for the JavaScript Web core plugin.</p> |
| * |
| * <p>Current Uses: |
| * <ul> |
| * <li>listen for .project changes so that JavaScript class paths can be updated |
| * if the module core nature is added to a project</li> |
| * </ul></p> |
| * |
| * <p><b>NOTE:</b> If any other file resource change listening needs to take place in the future |
| * in this plugin it should be done here.</p> |
| */ |
| public class JSWebResourceEventManager extends AbstractIndexManager { |
| /** the singleton instance of the {@link JSWebResourceEventManager} */ |
| private static JSWebResourceEventManager INSTANCE; |
| |
| /** the name of the ".project" file where natures are stored */ |
| private static final String DOT_PROJECT_FILE_NAME = ".project"; //$NON-NLS-1$ |
| |
| /** the location to store state */ |
| private IPath fWorkingLocation; |
| |
| /** |
| * <p>Private constructor for the resource event manager</p> |
| */ |
| private JSWebResourceEventManager() { |
| super(Messages.build_analyzingDeltas, Messages.build_analyzingDeltas, |
| JsCoreMessages.model_initialization, Messages.manager_filesToIndex); |
| } |
| |
| /** |
| * @return the singleton instance of the {@link JSWebResourceEventManager} |
| */ |
| public static JSWebResourceEventManager getDefault() { |
| return INSTANCE != null ? INSTANCE : (INSTANCE = new JSWebResourceEventManager()); |
| } |
| |
| /** |
| * @see org.eclipse.wst.sse.core.indexing.AbstractIndexManager#isResourceToIndex(int, org.eclipse.core.runtime.IPath) |
| */ |
| protected boolean isResourceToIndex(int type, IPath path) { |
| return |
| type == IResource.ROOT || |
| type == IResource.PROJECT || |
| (type == IResource.FILE && DOT_PROJECT_FILE_NAME.equals(path.lastSegment())); |
| } |
| |
| /** |
| * @see org.eclipse.wst.sse.core.indexing.AbstractIndexManager#performAction(byte, byte, org.eclipse.core.resources.IResource, org.eclipse.core.runtime.IPath) |
| */ |
| protected void performAction(byte source, byte action, IResource resource, |
| IPath movePath) { |
| |
| switch(action) { |
| case(AbstractIndexManager.ACTION_ADD): { |
| if(resource.getName().equals(DOT_PROJECT_FILE_NAME)) { |
| updateClassPathEntries(resource.getProject()); |
| } |
| break; |
| } |
| } |
| |
| } |
| |
| /** |
| * @see org.eclipse.wst.sse.core.indexing.AbstractIndexManager#getWorkingLocation() |
| */ |
| protected IPath getWorkingLocation() { |
| if(this.fWorkingLocation == null) { |
| //create path to working area |
| IPath workingLocation = |
| JsCorePlugin.getDefault().getStateLocation().append("resourceEventManager"); //$NON-NLS-1$ |
| |
| // ensure that it exists on disk |
| File folder = new File(workingLocation.toOSString()); |
| if (!folder.isDirectory()) { |
| try { |
| folder.mkdir(); |
| } |
| catch (SecurityException e) { |
| Logger.logException(this.getName() + |
| ": Error while creating state location: " + folder + //$NON-NLS-1$ |
| " This renders the index manager irrevocably broken for this workspace session", //$NON-NLS-1$ |
| e); |
| } |
| } |
| |
| this.fWorkingLocation = workingLocation; |
| } |
| |
| return this.fWorkingLocation; |
| } |
| |
| /** |
| * <p>Updates the JavaScript class path entries for the given project if |
| * both the Module core and JavaScript natures are installed on that project.</p> |
| * |
| * @param project {@link IProject} to update the JavaScript class path entries for |
| */ |
| private static void updateClassPathEntries(IProject project) { |
| try { |
| /* |
| * if a JS project with Module Core nature, check if include path |
| * needs to be updated |
| */ |
| if (project.isAccessible() && project.hasNature(JavaScriptCore.NATURE_ID) && ModuleCoreNature.isFlexibleProject(project)) { |
| JavaProject jsProject = (JavaProject) JavaScriptCore.create(project); |
| |
| IIncludePathEntry[] oldEntries = jsProject.getRawIncludepath(); |
| List updatedEntries = new ArrayList(); |
| boolean updateIncludePath = false; |
| |
| for (int oldEntry = 0; oldEntry < oldEntries.length; ++oldEntry) { |
| IIncludePathAttribute[] entryAttributes = oldEntries[oldEntry].getExtraAttributes(); |
| |
| boolean isProvidedEntry = false; |
| for (int attribute = 0; attribute < entryAttributes.length; ++attribute) { |
| isProvidedEntry = entryAttributes[attribute].getName().equals(ModuleSourcePathProvider.PROVIDER_ATTRIBUTE_KEY_NAME) && entryAttributes[attribute].getValue().equals(ModuleSourcePathProvider.PROVIDER_ATTRIBUTE_KEY_VALUE); |
| updateIncludePath |= isProvidedEntry; |
| if (isProvidedEntry) { |
| /* |
| * create updated exclusion paths that are not |
| * relative to the parent entry |
| */ |
| IPath[] nonRelativeExclusionPaths = oldEntries[oldEntry].getExclusionPatterns(); |
| for (int i = 0; i < nonRelativeExclusionPaths.length; ++i) { |
| nonRelativeExclusionPaths[i] = oldEntries[oldEntry].getPath().append(nonRelativeExclusionPaths[i]); |
| } |
| |
| /* |
| * create updated inclusion paths that are not |
| * relative to the parent entry |
| */ |
| IPath[] nonRelativeInclusionPaths = oldEntries[oldEntry].getInclusionPatterns(); |
| for (int i = 0; i < nonRelativeInclusionPaths.length; ++i) { |
| nonRelativeInclusionPaths[i] = oldEntries[oldEntry].getPath().append(nonRelativeInclusionPaths[i]); |
| } |
| |
| IResource[] roots = getRoots(project); |
| IIncludePathEntry[] resolvedEntries = jsProject.getResolvedClasspath(); |
| for (int root = 0; root < roots.length; ++root) { |
| IPath rootPath = roots[root].getFullPath(); |
| |
| // make sure we do not create a source entry that conflicts with an existing library |
| boolean foundMatch = false; |
| for(int k = 0; k < resolvedEntries.length; k++) { |
| if(resolvedEntries[k].getPath().equals(rootPath)) { |
| foundMatch = true; |
| break; |
| } |
| } |
| if(foundMatch) |
| continue; |
| |
| /* |
| * find matching pre-existing exclusion |
| * patterns |
| */ |
| List exclusionPatterns = new ArrayList(); |
| for (int i = 0; i < nonRelativeExclusionPaths.length; ++i) { |
| IPath parentRelativeExclusionPattern = PathUtils.makePatternRelativeToParent(nonRelativeExclusionPaths[i], rootPath); |
| if (parentRelativeExclusionPattern != null) { |
| exclusionPatterns.add(parentRelativeExclusionPattern); |
| } |
| } |
| |
| /* |
| * find matching pre-existing inclusion |
| * patterns |
| */ |
| List inclusionPatterns = new ArrayList(); |
| for (int i = 0; i < nonRelativeInclusionPaths.length; ++i) { |
| IPath parentRelativeInclusionPattern = PathUtils.makePatternRelativeToParent(nonRelativeInclusionPaths[i], rootPath); |
| if (parentRelativeInclusionPattern != null) { |
| inclusionPatterns.add(parentRelativeInclusionPattern); |
| } |
| } |
| |
| // create new inclusion/exclusion rules |
| IPath[] exclusionPaths = exclusionPatterns.isEmpty() ? ClasspathEntry.EXCLUDE_NONE : (IPath[]) exclusionPatterns.toArray(new IPath[exclusionPatterns.size()]); |
| IPath[] inclusionPaths = inclusionPatterns.isEmpty() ? ClasspathEntry.INCLUDE_ALL : (IPath[]) inclusionPatterns.toArray(new IPath[inclusionPatterns.size()]); |
| |
| IIncludePathEntry newEntry = JavaScriptCore.newSourceEntry(rootPath, inclusionPaths, exclusionPaths, null); |
| updatedEntries.add(newEntry); |
| } |
| } |
| } |
| if (!isProvidedEntry) { |
| updatedEntries.add(oldEntries[oldEntry]); |
| } |
| } |
| |
| /* |
| * if found that a default source path was added, replace with |
| * module core determined path |
| */ |
| if (updateIncludePath) { |
| // commit the updated include path |
| jsProject.setRawIncludepath((IIncludePathEntry[]) updatedEntries.toArray(new IIncludePathEntry[updatedEntries.size()]), jsProject.getOutputLocation(), null); |
| } |
| } |
| } |
| catch (CoreException e) { |
| Logger.logException("Error while updating JavaScript includepath", e); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * <p>Uses module core to get the roots of the given project.</p> |
| * |
| * @param project find the module core roots for this {@link IProject} |
| * @return the module core roots for the given {@link IProject |
| */ |
| private static IResource[] getRoots(IProject project) { |
| IVirtualFolder root = ComponentCore.createFolder(project, Path.ROOT); |
| IResource[] underlyingResources = root.getUnderlyingResources(); |
| if (underlyingResources == null || underlyingResources.length == 0) { |
| underlyingResources = new IResource[]{root.getUnderlyingResource()}; |
| } |
| |
| return underlyingResources; |
| } |
| } |