Copyright (c) 2005, 2014 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation Serge Beauchamp (Freescale Semiconductor) - [229633] Project Path Variable Support
/******************************************************************************* * Copyright (c) 2005, 2014 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Serge Beauchamp (Freescale Semiconductor) - [229633] Project Path Variable Support *******************************************************************************/
package org.eclipse.core.internal.resources; import java.net.URI; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.internal.utils.FileUtil; import org.eclipse.core.internal.utils.Messages; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.osgi.util.NLS;
This class implements the various path, URI, and name validation methods in the workspace API
/** * This class implements the various path, URI, and name validation methods * in the workspace API */
public class LocationValidator { private final Workspace workspace; public LocationValidator(Workspace workspace) { this.workspace = workspace; }
Returns a string representation of a URI suitable for displaying to an end user.
/** * Returns a string representation of a URI suitable for displaying to an end user. */
private String toString(URI uri) { try { return EFS.getStore(uri).toString(); } catch (CoreException e) { //there is no store defined, so the best we can do is the URI toString. return uri.toString(); } }
Check that the location is absolute
/** * Check that the location is absolute */
private IStatus validateAbsolute(URI location, boolean error) { if (!location.isAbsolute()) { String message; String schemeSpecificPart = location.getSchemeSpecificPart(); if (schemeSpecificPart == null || schemeSpecificPart.isEmpty()) { message = Messages.links_noPath; } else { IPath pathPart = new Path(schemeSpecificPart); if (pathPart.segmentCount() > 0) message = NLS.bind(Messages.pathvar_undefined, location.toString(), pathPart.segment(0)); else message = Messages.links_noPath; } int code = error ? IResourceStatus.VARIABLE_NOT_DEFINED : IResourceStatus.VARIABLE_NOT_DEFINED_WARNING; return new ResourceStatus(code, null, message); } return Status.OK_STATUS; } /* (non-Javadoc) * @see IWorkspace#validateLinkLocation(IResource, IPath) */ public IStatus validateLinkLocation(IResource resource, IPath unresolvedLocation) { IPath location = resource.getPathVariableManager().resolvePath(unresolvedLocation); if (location.isEmpty()) return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), Messages.links_noPath); //check that the location is absolute if (!location.isAbsolute()) { //we know there is at least one segment, because of previous isEmpty check String message = NLS.bind(Messages.pathvar_undefined, location.toOSString(), location.segment(0)); return new ResourceStatus(IResourceStatus.VARIABLE_NOT_DEFINED_WARNING, resource.getFullPath(), message); } //if the location doesn't have a device, see if the OS will assign one if (location.getDevice() == null) location = new Path(location.toFile().getAbsolutePath()); return validateLinkLocationURI(resource, URIUtil.toURI(location)); } public IStatus validateLinkLocationURI(IResource resource, URI unresolvedLocation) { String schemeSpecificPart = unresolvedLocation.getSchemeSpecificPart(); if (schemeSpecificPart == null || schemeSpecificPart.isEmpty()) { return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), Messages.links_noPath); } String message; //check if resource linking is disabled if (ResourcesPlugin.getPlugin().getPluginPreferences().getBoolean(ResourcesPlugin.PREF_DISABLE_LINKING)) { message = NLS.bind(Messages.links_workspaceVeto, resource.getName()); return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), message); } //check that the resource is the right type int type = resource.getType(); if (type != IResource.FOLDER && type != IResource.FILE) { message = NLS.bind(Messages.links_notFileFolder, resource.getName()); return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), message); } IContainer parent = resource.getParent(); if (!parent.isAccessible()) { message = NLS.bind(Messages.links_parentNotAccessible, resource.getFullPath()); return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), message); } URI location = resource.getPathVariableManager().resolveURI(unresolvedLocation); //check nature veto String[] natureIds = ((Project) resource.getProject()).internalGetDescription().getNatureIds(); IStatus result = workspace.getNatureManager().validateLinkCreation(natureIds); if (!result.isOK()) return result; //check team provider veto if (resource.getType() == IResource.FILE) result = workspace.getTeamHook().validateCreateLink((IFile) resource, IResource.NONE, location); else result = workspace.getTeamHook().validateCreateLink((IFolder) resource, IResource.NONE, location); if (!result.isOK()) return result; //check the standard path name restrictions result = validateSegments(location); if (!result.isOK()) return result; //check if the location is based on an undefined variable result = validateAbsolute(location, false); if (!result.isOK()) return result; // test if the given location overlaps the platform metadata location URI testLocation = workspace.getMetaArea().getLocation().toFile().toURI(); if (FileUtil.isOverlapping(location, testLocation)) { message = NLS.bind(Messages.links_invalidLocation, toString(location)); return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), message); } //test if the given path overlaps the location of the given project testLocation = resource.getProject().getLocationURI(); if (testLocation != null && FileUtil.isPrefixOf(location, testLocation)) { message = NLS.bind(Messages.links_locationOverlapsProject, toString(location)); return new ResourceStatus(IResourceStatus.INVALID_VALUE, resource.getFullPath(), message); } //warnings (all errors must be checked before all warnings) // Iterate over each known project and ensure that the location does not // conflict with any project locations or linked resource locations for (IProject project : workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN)) { // since we are iterating over the project in the workspace, we // know that they have been created before and must have a description IProjectDescription desc = ((Project) project).internalGetDescription(); testLocation = desc.getLocationURI(); if (testLocation != null && FileUtil.isOverlapping(location, testLocation)) { message = NLS.bind(Messages.links_overlappingResource, toString(location)); return new ResourceStatus(IResourceStatus.OVERLAPPING_LOCATION, resource.getFullPath(), message); } //iterate over linked resources and check for overlap if (!project.isOpen()) continue; IResource[] children = null; try { children = project.members(); } catch (CoreException e) { //ignore projects that cannot be accessed } if (children == null) continue; for (IResource child : children) { if (child.isLinked()) { testLocation = child.getLocationURI(); if (testLocation != null && FileUtil.isOverlapping(location, testLocation)) { message = NLS.bind(Messages.links_overlappingResource, toString(location)); return new ResourceStatus(IResourceStatus.OVERLAPPING_LOCATION, resource.getFullPath(), message); } } } } return Status.OK_STATUS; } /* (non-Javadoc) * @see IWorkspace#validateName(String, int) */ public IStatus validateName(String segment, int type) { String message; /* segment must not be null */ if (segment == null) { message = Messages.resources_nameNull; return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } // cannot be an empty string if (segment.length() == 0) { message = Messages.resources_nameEmpty; return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } /* test invalid characters */ char[] chars = OS.INVALID_RESOURCE_CHARACTERS; for (char c : chars) if (segment.indexOf(c) != -1) { message = NLS.bind(Messages.resources_invalidCharInName, String.valueOf(c), segment); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } /* test invalid OS names */ if (!OS.isNameValid(segment)) { message = NLS.bind(Messages.resources_invalidName, segment); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } return Status.OK_STATUS; }
Validates that the given workspace path is valid for the given type. If lastSegmentOnly is true, it is assumed that all segments except the last one have previously been validated. This is an optimization for validating a leaf resource when it is known that the parent exists (and thus its parent path must already be valid).
/** * Validates that the given workspace path is valid for the given type. If * <code>lastSegmentOnly</code> is true, it is assumed that all segments except * the last one have previously been validated. This is an optimization for validating * a leaf resource when it is known that the parent exists (and thus its parent path * must already be valid). */
public IStatus validatePath(IPath path, int type, boolean lastSegmentOnly) { String message; /* path must not be null */ if (path == null) { message = Messages.resources_pathNull; return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } /* path must not have a device separator */ if (path.getDevice() != null) { message = NLS.bind(Messages.resources_invalidCharInPath, String.valueOf(IPath.DEVICE_SEPARATOR), path); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } /* path must not be the root path */ if (path.isRoot()) { message = Messages.resources_invalidRoot; return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } /* path must be absolute */ if (!path.isAbsolute()) { message = NLS.bind(Messages.resources_mustBeAbsolute, path); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } /* validate segments */ int numberOfSegments = path.segmentCount(); if ((type & IResource.PROJECT) != 0) { if (numberOfSegments == ICoreConstants.PROJECT_SEGMENT_LENGTH) { return validateName(path.segment(0), IResource.PROJECT); } else if (type == IResource.PROJECT) { message = NLS.bind(Messages.resources_projectPath, path); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } } if ((type & (IResource.FILE | IResource.FOLDER)) != 0) { if (numberOfSegments < ICoreConstants.MINIMUM_FILE_SEGMENT_LENGTH) { message = NLS.bind(Messages.resources_resourcePath, path); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } int fileFolderType = type &= ~IResource.PROJECT; int segmentCount = path.segmentCount(); if (lastSegmentOnly) return validateName(path.segment(segmentCount - 1), fileFolderType); IStatus status = validateName(path.segment(0), IResource.PROJECT); if (!status.isOK()) return status; // ignore first segment (the project) for (int i = 1; i < segmentCount; i++) { status = validateName(path.segment(i), fileFolderType); if (!status.isOK()) return status; } return Status.OK_STATUS; } message = NLS.bind(Messages.resources_invalidPath, path); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } /* (non-Javadoc) * @see IWorkspace#validatePath(String, int) */ public IStatus validatePath(String path, int type) { /* path must not be null */ if (path == null) { String message = Messages.resources_pathNull; return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } return validatePath(Path.fromOSString(path), type, false); } public IStatus validateProjectLocation(IProject context, IPath unresolvedLocation) { if (unresolvedLocation == null) return validateProjectLocationURI(context, null); IPath location; if (context != null) location = context.getPathVariableManager().resolvePath(unresolvedLocation); else location = workspace.getPathVariableManager().resolvePath(unresolvedLocation); //check that the location is absolute if (!location.isAbsolute()) { String message; if (location.segmentCount() > 0) message = NLS.bind(Messages.pathvar_undefined, location.toString(), location.segment(0)); else message = Messages.links_noPath; return new ResourceStatus(IResourceStatus.VARIABLE_NOT_DEFINED, null, message); } return validateProjectLocationURI(context, URIUtil.toURI(location)); } /* (non-Javadoc) * @see IWorkspace#validateProjectLocationURI(IProject, URI) */ public IStatus validateProjectLocationURI(IProject context, URI unresolvedLocation) { if (context == null && unresolvedLocation == null) throw new IllegalArgumentException("Either a project or a location must be provided"); //$NON-NLS-1$ // Checks if the new location overlaps the workspace metadata location boolean isMetadataLocation = false; if (unresolvedLocation != null) { if (URIUtil.equals(unresolvedLocation, URIUtil.toURI(Platform.getLocation().addTrailingSeparator().append(LocalMetaArea.F_METADATA)))) { isMetadataLocation = true; } } else if (context != null && context.getName().equals(LocalMetaArea.F_METADATA)) { isMetadataLocation = true; } String message; if (isMetadataLocation) { message = NLS.bind(Messages.resources_invalidPath, toString(URIUtil.toURI(Platform.getLocation().addTrailingSeparator().append(LocalMetaArea.F_METADATA)))); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } // the default is ok for all other projects if (unresolvedLocation == null) return Status.OK_STATUS; URI location; if (context != null) location = context.getPathVariableManager().resolveURI(unresolvedLocation); else location = workspace.getPathVariableManager().resolveURI(unresolvedLocation); //check the standard path name restrictions IStatus result = validateSegments(location); if (!result.isOK()) return result; result = validateAbsolute(location, true); if (!result.isOK()) return result; //check that the URI has a legal scheme try { EFS.getFileSystem(location.getScheme()); } catch (CoreException e) { return e.getStatus(); } //overlaps with default location can only occur with file URIs if (location.getScheme().equals(EFS.SCHEME_FILE)) { IPath locationPath = URIUtil.toPath(location); // test if the given location overlaps the default default location IPath defaultDefaultLocation = workspace.getRoot().getLocation(); if (FileUtil.isPrefixOf(locationPath, defaultDefaultLocation)) { message = NLS.bind(Messages.resources_overlapWorkspace, toString(location), defaultDefaultLocation.toOSString()); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } // Test if the given location is the default location for any potential project except // the one being created. IPath parentPath = locationPath.removeLastSegments(1); if (FileUtil.isPrefixOf(parentPath, defaultDefaultLocation) && FileUtil.isPrefixOf(defaultDefaultLocation, parentPath) && (context == null || !locationPath.equals(defaultDefaultLocation.append(context.getName())))) { message = NLS.bind(Messages.resources_overlapProject, toString(location), locationPath.lastSegment()); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } } // Iterate over each known project and ensure that the location does not // conflict with any of their already defined locations. IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN); for (IProject project : projects) { URI testLocation = project.getLocationURI(); if (context != null && project.equals(context)) { //tolerate locations being the same if this is the project being tested if (URIUtil.equals(testLocation, location)) continue; //a project cannot be moved inside of its current location if (!FileUtil.isPrefixOf(testLocation, location)) continue; } else if (!URIUtil.equals(testLocation, location)) { // a project cannot have the same location as another existing project continue; } //in all other cases there is illegal overlap message = NLS.bind(Messages.resources_overlapProject, toString(location), project.getName()); return new ResourceStatus(IResourceStatus.INVALID_VALUE, null, message); } //if this project exists and has linked resources, the project location cannot overlap //the locations of any linked resources in that project if (context != null && context.exists() && context.isOpen()) { IResource[] children = null; try { children = context.members(); } catch (CoreException e) { //ignore projects that cannot be accessed } if (children != null) { for (IResource child : children) { if (child.isLinked()) { URI testLocation = child.getLocationURI(); if (testLocation != null && FileUtil.isPrefixOf(testLocation, location)) { message = NLS.bind(Messages.links_locationOverlapsLink, toString(location)); return new ResourceStatus(IResourceStatus.OVERLAPPING_LOCATION, context.getFullPath(), message); } } } } } return Status.OK_STATUS; }
Validates the standard path name restrictions on the segments of the provided URI.
Params:
  • location – The URI to validate
Returns:A status indicating if the segments of the provided URI are valid
/** * Validates the standard path name restrictions on the segments of the provided URI. * @param location The URI to validate * @return A status indicating if the segments of the provided URI are valid */
private IStatus validateSegments(URI location) { if (EFS.SCHEME_FILE.equals(location.getScheme())) { IPath pathPart = new Path(location.getSchemeSpecificPart()); int segmentCount = pathPart.segmentCount(); for (int i = 0; i < segmentCount; i++) { IStatus result = validateName(pathPart.segment(i), IResource.PROJECT); if (!result.isOK()) return result; } } return Status.OK_STATUS; } }