blob: 3ee46728b66c9d0e0fb367274945504bb9aac0ec [file] [log] [blame]
/*=============================================================================#
# 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);
}
}