package com.dubture.composer.core.buildpath;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.php.internal.core.buildpath.BuildPathUtils;
import org.eclipse.wst.validation.ValidationFramework;
import com.dubture.composer.core.ComposerPlugin;
import com.dubture.composer.core.ComposerPreferenceConstants;
import com.dubture.composer.core.log.Logger;
import com.dubture.composer.core.resources.IComposerProject;
@SuppressWarnings("restriction")
public class BuildPathManager {
private IComposerProject composerProject;
private IPath[] exclusions; // gnoar, shouldn't be a property, what did I thought? DI 'n stuff...
private IPath vendorPath;
private IPath composerPath;
public BuildPathManager(IComposerProject composerProject) {
this.composerProject = composerProject;
}
public void update() throws CoreException {
update(new NullProgressMonitor());
}
public void update(IProgressMonitor monitor) throws CoreException {
// check for valid composer json, stop processing when invalid
if (!composerProject.isValidComposerJson()) {
Logger.log(Logger.INFO, "Stop BuildPathManager, composer.json invalid");
return;
}
vendorPath = composerProject.getProject().getFullPath().append(composerProject.getVendorDir());
composerPath = vendorPath.append("composer");
IProject project = composerProject.getProject();
IScriptProject scriptProject = composerProject.getScriptProject();
BuildPathParser parser = new BuildPathParser(composerProject);
List<String> paths = parser.getPaths();
// project prefs
IEclipsePreferences prefs = ComposerPlugin.getDefault().getProjectPreferences(project);
IPath[] inclusions;
try {
String encoded = prefs.get(ComposerPreferenceConstants.BUILDPATH_INCLUDES_EXCLUDES, "");
exclusions = scriptProject.decodeBuildpathEntry(encoded).getExclusionPatterns();
inclusions = scriptProject.decodeBuildpathEntry(encoded).getInclusionPatterns();
} catch (Exception e) {
exclusions = new IPath[]{};
inclusions = new IPath[]{};
}
// add includes
for (IPath inclusion : inclusions) {
paths.add(inclusion.toString());
}
// clean up exclusion patterns: remove exact matches
List<IPath> exs = new ArrayList<IPath>();
for (IPath exclusion : exclusions) {
String exc = exclusion.removeTrailingSeparator().toString();
if (paths.contains(exc)) {
paths.remove(exc);
} else {
exs.add(exclusion);
}
}
exclusions = exs.toArray(new IPath[]{});
// clean build path
IBuildpathEntry[] rawBuildpath = scriptProject.getRawBuildpath();
for (IBuildpathEntry entry : rawBuildpath) {
if (entry.getEntryKind() == IBuildpathEntry.BPE_SOURCE) {
BuildPathUtils.removeEntryFromBuildPath(scriptProject, entry);
}
}
// sort paths for nesting detection
Collections.sort(paths);
// add new entries to buildpath
List<IBuildpathEntry> newEntries = new ArrayList<IBuildpathEntry>();
for (String path : paths) {
IPath entry = new Path(path);
IFolder folder = project.getFolder(entry);
if (folder != null && folder.exists()) {
addPath(folder.getFullPath(), newEntries);
}
}
if (newEntries.size() > 0) {
BuildPathUtils.addNonDupEntriesToBuildPath(scriptProject, newEntries);
}
IFolder folder = project.getFolder(new Path(composerProject.getVendorDir()));
if (folder != null && folder.exists()) {
if (!folder.isDerived()) {
folder.setDerived(true, monitor);
}
// disable validation in the vendor folder
ValidationFramework.getDefault().disableValidation(folder);
}
}
private void addPath(IPath path, List<IBuildpathEntry> entries) {
// find parent
IBuildpathEntry parent = null;
int parentLength = 0;
IPath entryPath;
for (IBuildpathEntry entry : entries) {
entryPath = entry.getPath();
if (entryPath.isPrefixOf(path)
&& (parent == null || (entryPath.toString().length() > parentLength))) {
parent = entry;
parentLength = parent.getPath().toString().length();
}
}
// add path as exclusion to found parent
if (parent != null) {
List<IPath> exclusions = new ArrayList<IPath>();
exclusions.addAll(Arrays.asList(parent.getExclusionPatterns()));
IPath diff = path.removeFirstSegments(path.matchingFirstSegments(parent.getPath()));
if (parent.getPath().equals(composerPath)) {
diff = diff.uptoSegment(1);
}
diff = diff.removeTrailingSeparator().addTrailingSeparator();
if (!exclusions.contains(diff)) {
exclusions.add(diff);
}
entries.remove(parent);
parent = DLTKCore.newSourceEntry(parent.getPath(), exclusions.toArray(new IPath[]{}));
entries.add(parent);
}
// add own entry
// leave vendor/composer untouched with exclusions
if (vendorPath.equals(path) || composerPath.equals(path)) {
entries.add(DLTKCore.newSourceEntry(path));
// add exclusions
} else {
List<IPath> ex = new ArrayList<IPath>();
// find the applying exclusion patterns for the new entry
for (IPath exclusion : exclusions) {
if (!exclusion.toString().startsWith("*")) {
exclusion = composerProject.getProject().getFullPath().append(exclusion);
}
// remove buildpath entries with exact exclusion matches
if (path.equals(exclusion)) {
return;
}
// if exclusion matches path, add the trailing path segments as exclusion
if (path.removeTrailingSeparator().isPrefixOf(exclusion)) {
ex.add(exclusion.removeFirstSegments(path.matchingFirstSegments(exclusion)));
}
// if exclusion starts with wildcard, add also
else if (exclusion.toString().startsWith("*")) {
ex.add(exclusion);
}
}
entries.add(DLTKCore.newSourceEntry(path, ex.toArray(new IPath[]{})));
}
}
// is this method necessary at all?
public static void setExclusionPattern(IScriptProject project, IBuildpathEntry entry) {
try {
String encoded = project.encodeBuildpathEntry(entry);
IEclipsePreferences prefs = ComposerPlugin.getDefault().getProjectPreferences(project.getProject());
prefs.put(ComposerPreferenceConstants.BUILDPATH_INCLUDES_EXCLUDES, encoded);
} catch (Exception e) {
Logger.logException(e);
}
}
}