/*******************************************************************************
* Copyright (c) 2009 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
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.core.includepath;
import java.util.*;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.*;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.core.environment.EnvironmentPathUtils;
import org.eclipse.php.internal.core.buildpath.BuildPathUtils;
import org.eclipse.php.internal.core.language.LanguageModelInitializer;
import org.eclipse.php.internal.core.preferences.CorePreferencesSupport;
public class IncludePathManager {
private static final String PREF_KEY = "include_path"; //$NON-NLS-1$
private static final char PREF_SEP = (char) 5;
private static IncludePathManager instance = new IncludePathManager();
private boolean modifyingIncludePath;
private final Set<IIncludepathListener> listeners = new HashSet<IIncludepathListener>(4);
private IncludePathManager() {
DLTKCore.addElementChangedListener(new IElementChangedListener() {
public void elementChanged(ElementChangedEvent event) {
processChildren(event.getDelta());
}
private void processChildren(IModelElementDelta delta) {
if (modifyingIncludePath) {
return;
}
IModelElement element = delta.getElement();
try {
if ((delta.getFlags() & IModelElementDelta.F_BUILDPATH_CHANGED) != 0) {
IScriptProject scriptProject = element.getScriptProject();
IProject project = scriptProject.getProject();
IncludePath[] includePathEntries = getIncludePaths(project);
List<IncludePath> newEntries = new LinkedList<IncludePath>(Arrays.asList(includePathEntries));
IBuildpathEntry[] rawBuildpath = scriptProject.getRawBuildpath();
// This is a workaround to the lack of information about
// buildpath changes in delta:
boolean changed = false;
// Calculate added entries:
Set<IPath> addedModels = new HashSet<IPath>();
getAddedModels(delta, addedModels);
for (IBuildpathEntry entry : rawBuildpath) {
boolean added = false;
for (IncludePath includePath : includePathEntries) {
if (includePath.isBuildpath() && entry.equals(includePath.getEntry())
|| !addedModels.contains(entry.getPath())) {
added = false;
break;
}
}
if (added && isBuildpathAllowed(entry)) {
newEntries.add(new IncludePath(entry, scriptProject));
changed = true;
}
}
// Calculate removed entries:
List<IncludePath> entriesToRemove = new LinkedList<IncludePath>();
for (IncludePath includePath : includePathEntries) {
boolean removed = true;
for (IBuildpathEntry entry : rawBuildpath) {
if (includePath.isBuildpath()) {
if (entry.equals(includePath.getEntry())) {
removed = false;
break;
}
} else {
removed = false;
}
/*
* else { if
* (entry.getPath().isPrefixOf(((IResource)
* includePath.getEntry()).getFullPath())) {
* removed = false; break; } }
*/
}
if (removed) {
entriesToRemove.add(includePath);
}
}
for (IncludePath includePath : entriesToRemove) {
newEntries.remove(includePath);
changed = true;
}
if (changed) {
setIncludePath(project, newEntries.toArray(new IncludePath[newEntries.size()]));
}
} else {
IModelElementDelta[] children = delta.getAffectedChildren();
for (IModelElementDelta child : children) {
processChildren(child);
}
}
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
private void getAddedModels(IModelElementDelta delta, Set<IPath> addedModels) {
for (int i = 0; i < delta.getAddedChildren().length; i++) {
addedModels.add(delta.getAddedChildren()[i].getElement().getPath());
}
for (int i = 0; i < delta.getAffectedChildren().length; i++) {
getAddedModels(delta.getAffectedChildren()[i], addedModels);
}
}
});
}
public static IncludePathManager getInstance() {
return instance;
}
/**
* Read project's include path
*
* @param project
* @return ordered include path
*/
public IncludePath[] getIncludePaths(IProject project) {
List<IncludePath> includePathEntries = new LinkedList<IncludePath>();
IBuildpathEntry[] buildpath = null;
try {
buildpath = DLTKCore.create(project).getRawBuildpath();
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
String includePath = CorePreferencesSupport.getInstance().getProjectSpecificPreferencesValue(PREF_KEY, null,
project);
if (includePath != null) {
while (includePath != null) {
String path;
int i = includePath.indexOf(PREF_SEP);
if (i == -1) {
path = includePath;
includePath = null;
} else {
path = includePath.substring(0, i);
includePath = includePath.substring(i + 1);
}
if ((i = path.indexOf(';')) != -1) {
int kind = Integer.parseInt(path.substring(0, i));
path = path.substring(i + 1);
if (kind == 0) {
IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
if (resource != null) {
includePathEntries.add(new IncludePath(resource, project));
}
} else if (buildpath != null) {
for (IBuildpathEntry entry : buildpath) {
if (entry.getEntryKind() == kind) {
IPath localPath = EnvironmentPathUtils.getLocalPath(entry.getPath());
if (localPath.equals(new Path(path))) {
includePathEntries.add(new IncludePath(entry, project));
break;
}
}
}
}
}
}
} else if (buildpath != null) { // by default include path equals to the
// build path
for (IBuildpathEntry entry : buildpath) {
if (isBuildpathAllowed(entry)) {
includePathEntries.add(new IncludePath(entry, project));
}
}
}
return includePathEntries.toArray(new IncludePath[includePathEntries.size()]);
}
/**
* Sets project's include path
*
* @param project
* @param includePathEntries
* Ordered include path entries
*/
public void setIncludePath(final IProject project, IncludePath[] includePathEntries) {
final StringBuilder buf = new StringBuilder();
if (null == project || includePathEntries == null) {
return;
}
for (int i = 0; i < includePathEntries.length; ++i) {
IncludePath includePath = includePathEntries[i];
if (includePath.isBuildpath()) {
IBuildpathEntry entry = (IBuildpathEntry) includePath.getEntry();
IPath localPath = EnvironmentPathUtils.getLocalPath(entry.getPath());
buf.append(entry.getEntryKind()).append(';').append(localPath.toString());
} else {
IResource entry = (IResource) includePath.getEntry();
buf.append("0;").append(entry.getFullPath().toString()); //$NON-NLS-1$
}
if (i < includePathEntries.length - 1) {
buf.append(PREF_SEP);
}
}
modifyingIncludePath = true;
WorkspaceJob job = new WorkspaceJob("Modifying Include Path") { //$NON-NLS-1$
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
CorePreferencesSupport.getInstance().setProjectSpecificPreferencesValue(PREF_KEY, buf.toString(),
project);
modifyingIncludePath = false;
// TODO - should not be part of the current job
refresh(project);
return Status.OK_STATUS;
}
};
job.setRule(project.getWorkspace().getRoot());
job.setPriority(WorkspaceJob.INTERACTIVE);
job.schedule();
}
public static boolean isBuildpathAllowed(IBuildpathEntry entry) {
return ((entry.getEntryKind() == IBuildpathEntry.BPE_CONTAINER
&& !entry.getPath().toString().equals(LanguageModelInitializer.CONTAINER_PATH))
|| entry.getEntryKind() == IBuildpathEntry.BPE_LIBRARY
|| entry.getEntryKind() == IBuildpathEntry.BPE_PROJECT);
}
/**
* Removes the given entry from the include path (according to the path)
*
* @param scriptProject
* @param buildpathEntry
* @throws ModelException
*/
public void removeEntryFromIncludePath(IProject project, IBuildpathEntry buildpathEntry) throws ModelException {
IncludePath[] includePathEntries = getIncludePaths(project);
List<IncludePath> newIncludePathEntries = new ArrayList<IncludePath>();
// go over the entries and compare the path.
// if it is the same as the given entry, it won't be added to the list.
for (IncludePath entry : includePathEntries) {
Object includePathEntry = entry.getEntry();
IPath resourcePath = null;
if (includePathEntry instanceof IBuildpathEntry) {
IBuildpathEntry bpEntry = (IBuildpathEntry) includePathEntry;
resourcePath = bpEntry.getPath();
} else {
IResource resource = (IResource) includePathEntry;
resourcePath = resource.getFullPath();
}
if (resourcePath != null && !resourcePath.toString().equals(buildpathEntry.getPath().toString())) {
newIncludePathEntries.add(entry);
}
}
// update the include path for this project
setIncludePath(project, newIncludePathEntries.toArray(new IncludePath[newIncludePathEntries.size()]));
// if it's a library, remove it also from build path
IScriptProject scriptProject = DLTKCore.create(project);
if ((buildpathEntry.getEntryKind() == IBuildpathEntry.BPE_LIBRARY
|| buildpathEntry.getEntryKind() == IBuildpathEntry.BPE_CONTAINER
|| buildpathEntry.getEntryKind() == IBuildpathEntry.BPE_PROJECT)) {
BuildPathUtils.removeEntryFromBuildPath(scriptProject, buildpathEntry);
}
}
/**
* Adds the given entries to Include Path
*
* @param scriptProject
* @param entries
* @throws ModelException
*/
public void addEntriesToIncludePath(IProject project, List<IBuildpathEntry> entries) {
List<IncludePath> includePathEntries = new ArrayList<IncludePath>();
for (IBuildpathEntry buildpathEntry : entries) {
if (buildpathEntry.getEntryKind() == IBuildpathEntry.BPE_SOURCE) {
IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(buildpathEntry.getPath());
if (resource != null) {
includePathEntries.add(new IncludePath(resource, project));
}
} else {
includePathEntries.add(new IncludePath(buildpathEntry, project));
}
}
// update the include path for this project
setIncludePath(project, includePathEntries.toArray(new IncludePath[includePathEntries.size()]));
}
/**
* Appends the given entries to Include Path
*
* @param scriptProject
* @param entries
* @throws ModelException
*/
public void appendEntriesToIncludePath(IProject project, List<IBuildpathEntry> entries) {
List<IncludePath> includePathEntries = new ArrayList<IncludePath>();
for (IBuildpathEntry buildpathEntry : entries) {
if (buildpathEntry.getEntryKind() == IBuildpathEntry.BPE_SOURCE) {
IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(buildpathEntry.getPath());
if (resource != null) {
includePathEntries.add(new IncludePath(resource, project));
}
} else {
includePathEntries.add(new IncludePath(buildpathEntry, project));
}
}
includePathEntries.addAll(Arrays.asList(IncludePathManager.getInstance().getIncludePaths(project)));
// update the include path for this project
setIncludePath(project, includePathEntries.toArray(new IncludePath[includePathEntries.size()]));
}
/**
* Returns whether the given path is in the include definitions Meaning if
* one of the entries in the include path has the same path of this resource
*
* @param project
* @param resourcePath
* @return
*/
public static IPath isInIncludePath(IProject project, IPath entryPath) {
if (entryPath == null) {
return null;
}
IncludePathManager includepathManager = IncludePathManager.getInstance();
IncludePath[] includePathEntries = includepathManager.getIncludePaths(project);
// go over the entries and compare the path.
// checks if the path for one of the entries equals to the given one
for (IncludePath entry : includePathEntries) {
Object includePathEntry = entry.getEntry();
IPath resourcePath = null;
if (includePathEntry instanceof IBuildpathEntry) {
IBuildpathEntry bpEntry = (IBuildpathEntry) includePathEntry;
resourcePath = bpEntry.getPath();
} else {
IResource resource = (IResource) includePathEntry;
resourcePath = resource.getFullPath();
}
if (resourcePath != null && resourcePath.isPrefixOf(entryPath)) {
return resourcePath;
}
}
return null;
}
/**
* Adds the given listener to the list of Include path listeners
*
* @param listener
*/
public void registerIncludepathListener(IIncludepathListener listener) {
if (listener == null) {
throw new IllegalArgumentException("Error adding listener in IncludepathManager"); // $//$NON-NLS-1$
}
synchronized (this) {
listeners.add(listener);
}
}
/**
* Removes the given listener to the list of Include path listeners
*
* @param listener
*/
public void unregisterIncludepathListener(IIncludepathListener listener) {
if (listener == null) {
throw new IllegalArgumentException("Error adding listener in IncludepathManager"); // $//$NON-NLS-1$
}
synchronized (this) {
listeners.remove(listener);
}
}
/**
* Operates the refresh callback of the registered listeners
*
* @param project
*/
public synchronized void refresh(IProject project) {
IIncludepathListener[] array = null;
synchronized (this) {
array = listeners.toArray(new IIncludepathListener[listeners.size()]);
}
for (IIncludepathListener includepathListener : array) {
includepathListener.refresh(project);
}
}
}