| /*=============================================================================# |
| # Copyright (c) 2010, 2019 IBM Corporation and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 |
| # |
| # Contributors: |
| # IBM Corporation - org.eclipse.jdt: initial API and implementation |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.ltk.buildpaths.core; |
| |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.osgi.util.NLS; |
| |
| import org.eclipse.statet.jcommons.collections.ImList; |
| |
| import org.eclipse.statet.internal.ltk.buildpaths.core.Messages; |
| |
| |
| public abstract class BuildpathInitializer { |
| |
| |
| /** |
| * Status code indicating that an attribute is not supported. |
| */ |
| public static final int NOT_SUPPORTED= 1 << 16; |
| |
| /** |
| * Status code indicating that an attribute is not modifiable. |
| */ |
| public static final int READ_ONLY= 1 << 17; |
| |
| |
| /** |
| * Status constant indicating that a buildpath entry was invalid |
| */ |
| public static final int INVALID_BUILDPATH= 964; |
| |
| /** |
| * Status constant indicating that a naming collision would occur |
| * if the operation proceeded. |
| */ |
| public static final int NAME_COLLISION= 977; |
| |
| |
| private static final IStatus VERIFIED_OK= new Status(IStatus.OK, LTKBuildpathsCore.BUNDLE_ID, 0, "OK", null); //$NON-NLS-1$ |
| |
| |
| public boolean canUpdateContainer(final IPath path, final IProject project) { |
| return false; |
| } |
| |
| public IStatus getAttributeStatus(final IPath path, final IProject project, final String key) { |
| return null; |
| } |
| |
| |
| public IResource getWorkspaceTarget(final IPath path) { |
| if (path == null || path.getDevice() != null) { |
| return null; |
| } |
| final IWorkspace workspace= ResourcesPlugin.getWorkspace(); |
| if (workspace == null) { |
| return null; |
| } |
| return workspace.getRoot().findMember(path); |
| } |
| |
| protected String getEntryPathMsg(final IProject project, final IPath path) { |
| return (project.getName().equals(path.segment(0))) ? |
| path.removeFirstSegments(1).makeRelative().toString() : |
| path.toString(); |
| } |
| |
| |
| /** |
| * Validate a given buildpath and output location for a project, using the following rules: |
| * <ul> |
| * <li>Buildpath entries cannot collide with each other; that is, all entry paths must be unique. |
| * <li>The project output location path cannot be null, must be absolute and located inside the project. |
| * <li>Specific output locations (specified on source entries) can be null, if not they must be located inside the project, |
| * <li>A project entry cannot refer to itself directly (that is, a project cannot prerequisite itself). |
| * <li>Buildpath entries or output locations cannot coincidate or be nested in each other, except for the following scenario listed below: |
| * <ul><li>A source folder can coincidate with its own output location, in which case this output can then contain library archives. |
| * However, a specific output location cannot coincidate with any library or a distinct source folder than the one referring to it. </li> |
| * <li>A source/library folder can be nested in any source folder as long as the nested folder is excluded from the enclosing one. </li> |
| * <li>An output location can be nested in a source folder, if the source folder coincidates with the project itself, or if the output |
| * location is excluded from the source folder. </li> |
| * </ul> |
| * </ul> |
| * |
| * Note that the buildpath entries are not validated automatically. Only bound variables or containers are considered |
| * in the checking process (this allows to perform a consistency check on a buildpath which has references to |
| * yet non existing projects, folders, ...). |
| * <p> |
| * This validation is intended to anticipate buildpath issues prior to assigning it to a project. In particular, it will automatically |
| * be performed during the buildpath setting operation (if validation fails, the buildpath setting will not complete). |
| * <p> |
| * |
| * @param project the given java project |
| * @param rawBuildpath a given buildpath |
| * @param projectOutputLocation a given output location |
| * @return a status object with code <code>IStatus.OK</code> if |
| * the given buildpath and output location are compatible, otherwise a status |
| * object indicating what is wrong with the buildpath or output location |
| */ |
| public IStatus validateBuildpath(final IProject project, final List<IBuildpathElement> rawBuildpath) { |
| // tolerate null path, it will be reset to default |
| if (rawBuildpath == null) { |
| return VERIFIED_OK; |
| } |
| |
| // check duplicate entries on raw buildpath only |
| { final Set<IPath> pathes= new HashSet<>(rawBuildpath.size()); |
| for (final IBuildpathElement element : rawBuildpath) { |
| final IPath path= element.getPath(); |
| if (!pathes.add(path)){ |
| return createErrorStatus(NAME_COLLISION, NLS.bind( |
| Messages.BuildpathStatus_DuplicateEntryPath_message, |
| getEntryPathMsg(project, path), project.getName() )); |
| } |
| } |
| } |
| |
| for (final IBuildpathElement element : rawBuildpath) { |
| final IStatus status= validateBuildpathEntry(project, element); |
| if (status.getSeverity() == IStatus.ERROR) { |
| return status; |
| } |
| } |
| |
| return VERIFIED_OK; |
| } |
| |
| protected IStatus validateBuildpathEntry(final IProject project, final IBuildpathElement entry) { |
| final IPath path= entry.getPath(); |
| |
| // Build some common strings for status message |
| final String projectName= project.getName(); |
| |
| switch (entry.getTypeName()) { |
| case IBuildpathElement.SOURCE: |
| if (!path.isAbsolute() || path.isEmpty()) { |
| return createErrorStatus(INVALID_BUILDPATH, NLS.bind( |
| Messages.BuildpathStatus_Entry_IllegalSourceFolderPath_message, |
| getEntryPathMsg(project, path), projectName )); |
| } |
| { final IPath projectPath= project.getProject().getFullPath(); |
| if (!projectPath.isPrefixOf(path) || getWorkspaceTarget(path) == null) { |
| return createErrorStatus(INVALID_BUILDPATH, NLS.bind( |
| Messages.BuildpathStatus_Entry_UnboundSourceFolder_message, |
| getEntryPathMsg(project, path), projectName )); |
| } |
| } |
| break; |
| } |
| |
| // Validate extra attributes |
| final ImList<IBuildpathAttribute> extraAttributes= entry.getExtraAttributes(); |
| if (extraAttributes != null) { |
| final int length= extraAttributes.size(); |
| final Set<String> set= new HashSet<>(length); |
| for (final IBuildpathAttribute attribute : extraAttributes) { |
| if (!set.add(attribute.getName())) { |
| return createErrorStatus(NAME_COLLISION, NLS.bind( |
| Messages.BuildpathStatus_Entry_DuplicateExtraAttribute_message, new String[] { |
| attribute.getName(), |
| getEntryPathMsg(project, path), |
| projectName |
| })); |
| } |
| } |
| } |
| |
| return VERIFIED_OK; |
| } |
| |
| protected IStatus createErrorStatus(final int code, final String message) { |
| return new Status(IStatus.ERROR, LTKBuildpathsCore.BUNDLE_ID, code, message, null); |
| } |
| |
| } |