/******************************************************************************* * Copyright (c) 2012, 2016, 2017 PDT Extension Group 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: * PDT Extension Group - initial API and implementation * Kaloyan Raev - [501269] externalize strings *******************************************************************************/ package org.eclipse.php.composer.core.buildpath; import java.util.*; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.IBuildpathAttribute; import org.eclipse.dltk.core.IBuildpathEntry; import org.eclipse.dltk.core.IScriptProject; import org.eclipse.dltk.internal.core.BuildpathEntry; import org.eclipse.php.composer.core.ComposerPlugin; import org.eclipse.php.composer.core.ComposerPluginConstants; import org.eclipse.php.composer.core.ComposerPreferenceConstants; import org.eclipse.php.composer.core.log.Logger; import org.eclipse.php.composer.core.resources.IComposerProject; import org.eclipse.wst.validation.ValidationFramework; 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"); //$NON-NLS-1$ return; } vendorPath = composerProject.getProject().getFullPath().append(composerProject.getVendorDir()); composerPath = vendorPath.append("composer"); //$NON-NLS-1$ IProject project = composerProject.getProject(); IScriptProject scriptProject = composerProject.getScriptProject(); BuildPathParser parser = new BuildPathParser(composerProject); TreeSet<BuildPathParser.BuildPathInfo> paths = parser.getPathsInfo(); // project prefs IEclipsePreferences prefs = ComposerPlugin.getDefault().getProjectPreferences(project); IPath[] inclusions; List<IPath> exs = new ArrayList<IPath>(); try { String encoded = prefs.get(ComposerPreferenceConstants.BUILDPATH_INCLUDES_EXCLUDES, ""); //$NON-NLS-1$ inclusions = scriptProject.decodeBuildpathEntry(encoded).getInclusionPatterns(); exs.addAll(Arrays.asList(scriptProject.decodeBuildpathEntry(encoded).getExclusionPatterns())); } catch (Exception e) { inclusions = new IPath[] {}; } // add includes for (IPath inclusion : inclusions) { paths.add(new BuildPathParser.BuildPathInfo(inclusion.toString(), BuildPathParser.BuildPathInfo.SOURCE)); } // clean up exclusion patterns: remove exact matches for (Iterator<IPath> eit = exs.iterator(); eit.hasNext();) { String exc = eit.next().removeTrailingSeparator().toString(); for (Iterator<BuildPathParser.BuildPathInfo> it = paths.iterator(); it.hasNext();) { BuildPathParser.BuildPathInfo info = it.next(); if (info.path.equals(exc)) { it.remove(); } } } exclusions = exs.toArray(new IPath[] {}); // clean build path List<IBuildpathEntry> buildPath = new ArrayList<IBuildpathEntry>( Arrays.asList(scriptProject.getRawBuildpath())); for (Iterator<IBuildpathEntry> it = buildPath.iterator(); it.hasNext();) { IBuildpathEntry entry = it.next(); if (entry.getEntryKind() == IBuildpathEntry.BPE_SOURCE && entry.getExtraAttribute(ComposerPluginConstants.BPE_ATTR_NAME) != null) { it.remove(); } } // sort paths for nesting detection Collections.sort(buildPath, new Comparator<IBuildpathEntry>() { @Override public int compare(IBuildpathEntry o1, IBuildpathEntry o2) { return o1.getPath().toString().compareTo(o2.getPath().toString()); } }); // Add new entries to buildpath. // NB: "paths" is a TreeSet that is already correctly sorted through // BuildPathInfo.compareTo(BuildPathInfo o) for (BuildPathParser.BuildPathInfo path : paths) { IPath entry = new Path(path.path); IFolder folder = project.getFolder(entry); if (folder != null && folder.exists()) { addPath(folder.getFullPath(), buildPath, path.type); } } DLTKCore.create(project).setRawBuildpath(buildPath.toArray(new IBuildpathEntry[0]), monitor); 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, int type) { // find parent IBuildpathEntry parent = null; int parentLength = 0; IPath entryPath; for (Iterator<IBuildpathEntry> it = entries.iterator(); it.hasNext();) { IBuildpathEntry entry = it.next(); if (entry.getEntryKind() != IBuildpathEntry.BPE_SOURCE) { continue; } if (entry.getPath().equals(path)) { it.remove(); continue; } entryPath = entry.getPath(); // user defined build path conflicted with composer entry if (entry.getExtraAttribute(ComposerPluginConstants.BPE_ATTR_NAME) == null) { if (path.isPrefixOf(entryPath) || entryPath.isPrefixOf(path)) { it.remove(); } continue; } 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); if (parent.getExtraAttribute(ComposerPluginConstants.BPE_ATTR_NAME) != null) { parent = DLTKCore.newSourceEntry(parent.getPath(), BuildpathEntry.INCLUDE_ALL, exclusions.toArray(new IPath[] {}), new IBuildpathAttribute[] { DLTKCore.newBuildpathAttribute(ComposerPluginConstants.BPE_ATTR_NAME, parent.getExtraAttribute(ComposerPluginConstants.BPE_ATTR_NAME)) }); entries.add(parent); } } // add own entry // leave vendor/composer untouched with exclusions if (vendorPath.equals(path) || composerPath.equals(path)) { entries.add(DLTKCore.newSourceEntry(path, BuildpathEntry.INCLUDE_ALL, BuildpathEntry.EXCLUDE_NONE, new IBuildpathAttribute[] { DLTKCore.newBuildpathAttribute(ComposerPluginConstants.BPE_ATTR_NAME, ComposerPluginConstants.BPE_ATTR_VENDOR) })); // 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("*")) { //$NON-NLS-1$ 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("*")) { //$NON-NLS-1$ ex.add(exclusion); } } entries.add(DLTKCore.newSourceEntry(path, BuildpathEntry.INCLUDE_ALL, ex.toArray(new IPath[] {}), new IBuildpathAttribute[] { DLTKCore.newBuildpathAttribute(ComposerPluginConstants.BPE_ATTR_NAME, getTypeName(type)) })); } } protected String getTypeName(int type) { switch (type) { case BuildPathParser.BuildPathInfo.SOURCE: return ComposerPluginConstants.BPE_ATTR_SOURCE; case BuildPathParser.BuildPathInfo.VENDOR: return ComposerPluginConstants.BPE_ATTR_VENDOR; } return null; } // 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); } } }