| /******************************************************************************* |
| * Copyright (c) 2005, 2014 IBM Corporation 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: |
| * 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 |
| */ |
| 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. |
| */ |
| 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 |
| */ |
| 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 |
| * <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. |
| * @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; |
| } |
| } |